diff options
| author | Shireesh Anjal <shireesh@gluster.com> | 2011-06-21 21:17:09 +0530 |
|---|---|---|
| committer | Shireesh Anjal <shireesh@gluster.com> | 2011-06-22 18:33:12 +0530 |
| commit | 5bb44641bf7b40f048e71281885efea68a3fbcbd (patch) | |
| tree | 77819aab61ec66e0f7a798c186c2170aeaa11e62 /src | |
| parent | 8c5c224a98f194402f1ce413d6d1dd59946e2d52 (diff) | |
REST API enhancements
Diffstat (limited to 'src')
18 files changed, 804 insertions, 432 deletions
diff --git a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java index f38c1a5f..9d9d6c5f 100644 --- a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java +++ b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java @@ -20,11 +20,13 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap;
import com.gluster.storage.management.client.utils.ClientUtil;
+import com.gluster.storage.management.core.constants.RESTConstants;
import com.gluster.storage.management.core.exceptions.GlusterRuntimeException;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.WebResource.Builder;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.representation.Form;
import com.sun.jersey.client.urlconnection.HTTPSProperties;
@@ -121,8 +123,12 @@ public abstract class AbstractClient { */
@SuppressWarnings({ "unchecked", "rawtypes" })
private Object fetchResource(WebResource res, MultivaluedMap<String, String> queryParams, Class responseClass) {
- return res.queryParams(queryParams).header(HTTP_HEADER_AUTH, authHeader).accept(MediaType.APPLICATION_XML)
- .get(responseClass);
+ try {
+ return res.path("." + RESTConstants.FORMAT_XML).queryParams(queryParams)
+ .header(HTTP_HEADER_AUTH, authHeader).accept(MediaType.APPLICATION_XML).get(responseClass);
+ } catch(UniformInterfaceException e) {
+ throw new GlusterRuntimeException(e.getResponse().getEntity(String.class));
+ }
}
protected void downloadResource(WebResource res, String filePath) {
@@ -212,6 +218,29 @@ public abstract class AbstractClient { return fetchResource(resource.path(subResourceName), queryParams, responseClass);
}
+ private void postRequest(WebResource resource, Form form) {
+ try {
+ prepareFormRequestBuilder(resource).post(form);
+ } catch (UniformInterfaceException e) {
+ throw new GlusterRuntimeException(e.getResponse().getEntity(String.class));
+ }
+ }
+
+ /**
+ * Submits given object to the resource and returns the object received as response
+ *
+ * @param responseClass
+ * Class of the object expected as response
+ * @param requestObject
+ * the Object to be submitted
+ * @return Object of given class received as response
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected Object postObject(Class responseClass, Object requestObject) {
+ return resource.type(MediaType.APPLICATION_XML).header(HTTP_HEADER_AUTH, authHeader)
+ .accept(MediaType.APPLICATION_XML).post(responseClass, requestObject);
+ }
+
/**
* Submits given Form using POST method to the resource and returns the object received as response
*
@@ -219,12 +248,7 @@ public abstract class AbstractClient { * Form to be submitted
*/
protected void postRequest(Form form) {
- try {
- resource.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE).header(HTTP_HEADER_AUTH, authHeader)
- .accept(MediaType.APPLICATION_XML).post(form);
- } catch (UniformInterfaceException e) {
- throw new GlusterRuntimeException(e.getResponse().getEntity(String.class));
- }
+ postRequest(resource, form);
}
/**
@@ -236,14 +260,22 @@ public abstract class AbstractClient { * Form to be submitted
*/
protected void postRequest(String subResourceName, Form form) {
+ postRequest(resource.path(subResourceName), form);
+ }
+
+ private void putRequest(WebResource resource, Form form) {
try {
- resource.path(subResourceName).type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
- .header(HTTP_HEADER_AUTH, authHeader).accept(MediaType.APPLICATION_XML).post(form);
+ prepareFormRequestBuilder(resource).put(form);
} catch (UniformInterfaceException e) {
throw new GlusterRuntimeException(e.getResponse().getEntity(String.class));
}
}
+ public Builder prepareFormRequestBuilder(WebResource resource) {
+ return resource.path("." + RESTConstants.FORMAT_XML).type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
+ .header(HTTP_HEADER_AUTH, authHeader).accept(MediaType.APPLICATION_XML);
+ }
+
/**
* Submits given Form using PUT method to the given sub-resource and returns the object received as response
*
@@ -253,12 +285,7 @@ public abstract class AbstractClient { * Form to be submitted
*/
protected void putRequest(String subResourceName, Form form) {
- try {
- resource.path(subResourceName).type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
- .header(HTTP_HEADER_AUTH, authHeader).accept(MediaType.APPLICATION_XML).put(form);
- } catch (UniformInterfaceException e) {
- throw new GlusterRuntimeException(e.getResponse().getEntity(String.class));
- }
+ putRequest(resource.path(subResourceName), form);
}
/**
@@ -268,12 +295,7 @@ public abstract class AbstractClient { * Form to be submitted
*/
protected void putRequest(Form form) {
- try {
- resource.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE).header(HTTP_HEADER_AUTH, authHeader)
- .accept(MediaType.APPLICATION_XML).put(form);
- } catch(UniformInterfaceException e) {
- throw new GlusterRuntimeException(e.getResponse().getEntity(String.class));
- }
+ putRequest(resource, form);
}
/**
@@ -284,38 +306,27 @@ public abstract class AbstractClient { */
protected void putRequest(String subResourceName) {
try {
- resource.path(subResourceName).type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
- .header(HTTP_HEADER_AUTH, authHeader).accept(MediaType.APPLICATION_XML).put();
+ prepareFormRequestBuilder(resource.path(subResourceName)).put();
} catch (UniformInterfaceException e) {
throw new GlusterRuntimeException(e.getResponse().getEntity(String.class));
}
}
- /**
- * Submits given object to the resource and returns the object received as response
- *
- * @param responseClass
- * Class of the object expected as response
- * @param requestObject
- * the Object to be submitted
- * @return Object of given class received as response
- */
- @SuppressWarnings({ "unchecked", "rawtypes" })
- protected Object postObject(Class responseClass, Object requestObject) {
- return resource.type(MediaType.APPLICATION_XML).header(HTTP_HEADER_AUTH, authHeader)
- .accept(MediaType.APPLICATION_XML).post(responseClass, requestObject);
+ private void deleteResource(WebResource resource, MultivaluedMap<String, String> queryParams) {
+ try {
+ resource.path("." + RESTConstants.FORMAT_XML).queryParams(queryParams).header(HTTP_HEADER_AUTH, authHeader)
+ .delete();
+ } catch (UniformInterfaceException e) {
+ throw new GlusterRuntimeException(e.getResponse().getEntity(String.class));
+ }
}
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- protected Object deleteResource(Class responseClass, MultivaluedMap<String, String> queryParams) {
- return resource.queryParams(queryParams).header(HTTP_HEADER_AUTH, authHeader).delete(responseClass);
+
+ protected void deleteResource(MultivaluedMap<String, String> queryParams) {
+ deleteResource(resource, queryParams);
}
- @SuppressWarnings({ "unchecked", "rawtypes" })
- protected Object deleteSubResource(String subResourceName, Class responseClass,
- MultivaluedMap<String, String> queryParams) {
- return resource.path(subResourceName).queryParams(queryParams).header(HTTP_HEADER_AUTH, authHeader)
- .delete(responseClass);
+ protected void deleteSubResource(String subResourceName, MultivaluedMap<String, String> queryParams) {
+ deleteResource(resource.path(subResourceName), queryParams);
}
protected void deleteSubResource(String subResourceName) {
diff --git a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/GlusterDataModelManager.java b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/GlusterDataModelManager.java index fe8b3048..3a33e4e4 100644 --- a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/GlusterDataModelManager.java +++ b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/GlusterDataModelManager.java @@ -33,6 +33,7 @@ import com.gluster.storage.management.core.model.Event.EVENT_TYPE; import com.gluster.storage.management.core.model.GlusterDataModel; import com.gluster.storage.management.core.model.GlusterServer; import com.gluster.storage.management.core.model.Server; +import com.gluster.storage.management.core.model.Status; import com.gluster.storage.management.core.model.TaskInfo; import com.gluster.storage.management.core.model.Volume; import com.gluster.storage.management.core.model.Volume.TRANSPORT_TYPE; @@ -110,7 +111,9 @@ public class GlusterDataModelManager { private void initializeAutoDiscoveredServers(Cluster cluster) { ServerListResponse discoveredServerListResponse = new DiscoveredServersClient(securityToken) .getDiscoveredServerDetails(); - if (!discoveredServerListResponse.getStatus().isSuccess()) { + Status status = discoveredServerListResponse.getStatus(); + if (!status.isSuccess() && !status.isPartSuccess()) { + // TODO: Find a way to show warning in case of part success throw new GlusterRuntimeException(discoveredServerListResponse.getStatus().getMessage()); } cluster.setAutoDiscoveredServers(discoveredServerListResponse.getData()); @@ -118,20 +121,11 @@ public class GlusterDataModelManager { private void initializeVolumes(Cluster cluster) { VolumesClient volumeClient = new VolumesClient(); - VolumeListResponse response = volumeClient.getAllVolumes(); - if (!response.getStatus().isSuccess()) { - throw new GlusterRuntimeException("Error fetching volume list: [" + response.getStatus() + "]"); - } - cluster.setVolumes(response.getVolumes()); + cluster.setVolumes(volumeClient.getAllVolumes()); } private void initializeVolumeOptionsDefaults() { - VolumeOptionInfoListResponse response = new VolumesClient().getVolumeOptionsDefaults(); - if (!response.getStatus().isSuccess()) { - throw new GlusterRuntimeException("Error fetching volume option defaults: [" - + response.getStatus().getMessage() + "]"); - } - this.volumeOptionsDefaults = response.getOptions(); + this.volumeOptionsDefaults = new VolumesClient().getVolumeOptionsDefaults(); } public void initializeTasks(Cluster cluster) { diff --git a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/TasksClient.java b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/TasksClient.java index 54ba07e7..b5ee1d1c 100644 --- a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/TasksClient.java +++ b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/TasksClient.java @@ -87,10 +87,10 @@ public class TasksClient extends AbstractClient { putRequest(taskId, form); } - public TaskResponse deleteTask(String taskId) { + public void deleteTask(String taskId) { MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl(); queryParams.add(RESTConstants.FORM_PARAM_OPERATION, RESTConstants.TASK_DELETE); - return (TaskResponse) deleteSubResource(taskId, TaskResponse.class, queryParams); + deleteSubResource(taskId, queryParams); } } diff --git a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/VolumesClient.java b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/VolumesClient.java index 24ba119f..9a1eb261 100644 --- a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/VolumesClient.java +++ b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/VolumesClient.java @@ -31,7 +31,8 @@ import com.gluster.storage.management.core.constants.RESTConstants; import com.gluster.storage.management.core.model.Brick; import com.gluster.storage.management.core.model.Status; import com.gluster.storage.management.core.model.Volume; -import com.gluster.storage.management.core.response.GenericResponse; +import com.gluster.storage.management.core.model.VolumeLogMessage; +import com.gluster.storage.management.core.model.VolumeOptionInfo; import com.gluster.storage.management.core.response.LogMessageListResponse; import com.gluster.storage.management.core.response.VolumeListResponse; import com.gluster.storage.management.core.response.VolumeOptionInfoListResponse; @@ -77,6 +78,17 @@ public class VolumesClient extends AbstractClient { public void stopVolume(String volumeName) { performOperation(volumeName, RESTConstants.TASK_STOP); } + + public boolean volumeExists(String volumeName) { + try { + // TODO: instead of fetching full volume name, fetch list of volumes and check if + // it contains our volume name + getVolume(volumeName); + return true; + } catch(Exception e) { + return false; + } + } public void setVolumeOption(String volume, String key, String value) { Form form = new Form(); @@ -89,23 +101,22 @@ public class VolumesClient extends AbstractClient { putRequest(volume + "/" + RESTConstants.RESOURCE_OPTIONS); } - public VolumeListResponse getAllVolumes() { - return (VolumeListResponse) fetchResource(VolumeListResponse.class); + public List<Volume> getAllVolumes() { + return ((VolumeListResponse) fetchResource(VolumeListResponse.class)).getVolumes(); } - @SuppressWarnings("unchecked") - public GenericResponse<Volume> getVolume(String volumeName) { - return (GenericResponse<Volume>)fetchSubResource(volumeName, GenericResponse.class); + public Volume getVolume(String volumeName) { + return (Volume)fetchSubResource(volumeName, Volume.class); } - public Status deleteVolume(Volume volume, boolean deleteOption) { + public void deleteVolume(Volume volume, boolean deleteOption) { MultivaluedMap<String, String> queryParams = prepareDeleteVolumeQueryParams(deleteOption); - return (Status) deleteSubResource(volume.getName(), Status.class, queryParams); + deleteSubResource(volume.getName(), queryParams); } - public VolumeOptionInfoListResponse getVolumeOptionsDefaults() { + public List<VolumeOptionInfo> getVolumeOptionsDefaults() { return ((VolumeOptionInfoListResponse) fetchSubResource(RESTConstants.RESOURCE_DEFAULT_OPTIONS, - VolumeOptionInfoListResponse.class)); + VolumeOptionInfoListResponse.class)).getOptions(); } public void addBricks(String volumeName, List<String> brickList) { @@ -134,23 +145,23 @@ public class VolumesClient extends AbstractClient { * Number of most recent log messages to be fetched (from each disk) * @return Log Message List response received from the Gluster Management Server. */ - public LogMessageListResponse getLogs(String volumeName, String brickName, String severity, Date fromTimestamp, + public List<VolumeLogMessage> getLogs(String volumeName, String brickName, String severity, Date fromTimestamp, Date toTimestamp, int messageCount) { MultivaluedMap<String, String> queryParams = prepareGetLogQueryParams(brickName, severity, fromTimestamp, toTimestamp, messageCount); - return (LogMessageListResponse) fetchSubResource(volumeName + "/" + RESTConstants.RESOURCE_LOGS, - queryParams, LogMessageListResponse.class); + return ((LogMessageListResponse) fetchSubResource(volumeName + "/" + RESTConstants.RESOURCE_LOGS, + queryParams, LogMessageListResponse.class)).getLogMessages(); } public void downloadLogs(String volumeName, String filePath) { downloadSubResource(volumeName + "/" + RESTConstants.RESOURCE_LOGS + "/" + RESTConstants.RESOURCE_DOWNLOAD, filePath); } - public Status removeBricks(String volumeName, List<Brick> BrickList, boolean deleteOption) { + public void removeBricks(String volumeName, List<Brick> BrickList, boolean deleteOption) { String bricks = StringUtil.ListToString(GlusterCoreUtil.getQualifiedBrickList(BrickList), ","); MultivaluedMap<String, String> queryParams = prepareRemoveBrickQueryParams(volumeName, bricks, deleteOption); - return (Status) deleteSubResource(volumeName + "/" + RESTConstants.RESOURCE_BRICKS, Status.class, queryParams); + deleteSubResource(volumeName + "/" + RESTConstants.RESOURCE_BRICKS, queryParams); } private MultivaluedMap<String, String> prepareRemoveBrickQueryParams(String volumeName, String bricks, @@ -199,41 +210,15 @@ public class VolumesClient extends AbstractClient { form.add(RESTConstants.FORM_PARAM_OPERATION, RESTConstants.TASK_START); form.add(RESTConstants.FORM_PARAM_AUTO_COMMIT, autoCommit); - putRequest( volumeName + "/" + RESTConstants.RESOURCE_BRICKS, form); + putRequest(volumeName + "/" + RESTConstants.RESOURCE_BRICKS, form); } public static void main(String[] args) { UsersClient usersClient = new UsersClient(); if (usersClient.authenticate("gluster", "gluster").isSuccess()) { VolumesClient client = new VolumesClient(usersClient.getSecurityToken()); -// List<Disk> disks = new ArrayList<Disk>(); -// Disk diskElement = new Disk(); -// diskElement.setName("sda1"); -// diskElement.setStatus(DISK_STATUS.READY); -// disks.add(diskElement); -// diskElement.setName("sda2"); -// diskElement.setStatus(DISK_STATUS.READY); -// disks.add(diskElement); -// -// Volume vol = new Volume("vol1", null, Volume.VOLUME_TYPE.PLAIN_DISTRIBUTE, Volume.TRANSPORT_TYPE.ETHERNET, -// Volume.VOLUME_STATUS.ONLINE); -// // vol.setDisks(disks); -// System.out.println(client.createVolume(vol)); -// for (VolumeOptionInfo option : client.getVolumeOptionsDefaults()) { -// System.out.println(option.getName() + "-" + option.getDescription() + "-" + option.getDefaultValue()); -// } -// System.out.println(client.getVolume("Volume3").getOptions()); -// System.out.println(client.setVolumeOption("Volume3", "network.frame-timeout", "600").getMessage()); -// List<Disk> disks = new ArrayList<Disk>(); -// Disk disk = new Disk(); -// disk.setServerName("server1"); -// disk.setName("sda"); -// disk.setStatus(DISK_STATUS.READY); -// disks.add(disk); -// -// Status status = client.addDisks("Volume3", disks); -// System.out.println(status.getMessage()); - client.downloadLogs("vol1", "/tmp/temp1.tar.gz"); + System.out.println(client.getAllVolumes()); +// client.downloadLogs("vol1", "/tmp/temp1.tar.gz"); } } } diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/RESTConstants.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/RESTConstants.java index a082d49b..a2d222a8 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/RESTConstants.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/RESTConstants.java @@ -47,6 +47,16 @@ public class RESTConstants { public static final String TASK_STATUS = "status"; public static final String TASK_DELETE = "delete"; + public static final String FORM_PARAM_VOLUME_NAME = "name"; + public static final String FORM_PARAM_VOLUME_TYPE = "volumeType"; + public static final String FORM_PARAM_TRANSPORT_TYPE = "transportType"; + public static final String FORM_PARAM_REPLICA_COUNT = "replicaCount"; + public static final String FORM_PARAM_STRIPE_COUNT = "stripeCount"; + public static final String FORM_PARAM_BRICKS = "bricks"; + public static final String FORM_PARAM_ACCESS_PROTOCOLS = "accessProtocols"; + public static final String FORM_PARAM_VOLUME_OPTIONS = "options"; + + public static final String FORM_PARAM_CLUSTER_NAME = "clusterName"; public static final String FORM_PARAM_SERVER_NAME = "serverName"; public static final String FORM_PARAM_DISKS = "disks"; @@ -56,9 +66,9 @@ public class RESTConstants { public static final String FORM_PARAM_OPTION_VALUE = "value"; public static final String FORM_PARAM_SOURCE = "source"; public static final String FORM_PARAM_TARGET = "target"; - public static final String FORM_PARAM_BRICKS = "bricks"; public static final String FORM_PARAM_AUTO_COMMIT = "autoCommit"; + public static final String PATH_PARAM_FORMAT = "format"; public static final String PATH_PARAM_VOLUME_NAME = "volumeName"; public static final String PATH_PARAM_CLUSTER_NAME = "clusterName"; public static final String PATH_PARAM_SERVER_NAME = "serverName"; @@ -75,4 +85,7 @@ public class RESTConstants { public static final String QUERY_PARAM_TO_TIMESTAMP = "toTimestamp"; public static final String QUERY_PARAM_DOWNLOAD = "download"; public static final String QUERY_PARAM_SERVER_NAME = "serverName"; + + public static final String FORMAT_XML = "xml"; + public static final String FORMAT_JSON = "json"; } diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Volume.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Volume.java index 399192f4..1d5b2fec 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Volume.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Volume.java @@ -88,6 +88,14 @@ public class Volume extends Entity { public static String getVolumeTypeStr(VOLUME_TYPE volumeType) { return VOLUME_TYPE_STR[volumeType.ordinal()]; } + + public static VOLUME_TYPE getVolumeTypeByStr(String volumeTypeStr) { + return VOLUME_TYPE.valueOf(volumeTypeStr); + } + + public static TRANSPORT_TYPE getTransportTypeByStr(String transportTypeStr) { + return TRANSPORT_TYPE.valueOf(transportTypeStr); + } public String getTransportTypeStr() { return TRANSPORT_TYPE_STR[getTransportType().ordinal()]; diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/LogMessageListResponse.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/LogMessageListResponse.java index be374e8b..c1f879bb 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/LogMessageListResponse.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/LogMessageListResponse.java @@ -7,25 +7,27 @@ import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; -import com.gluster.storage.management.core.model.VolumeLogMessage; import com.gluster.storage.management.core.model.Status; +import com.gluster.storage.management.core.model.VolumeLogMessage; -@XmlRootElement(name = "response") +@XmlRootElement(name = "logMessages") public class LogMessageListResponse extends AbstractResponse { private List<VolumeLogMessage> logMessages = new ArrayList<VolumeLogMessage>(); public LogMessageListResponse() { } + public LogMessageListResponse(List<VolumeLogMessage> logMessages) { + setLogMessages(logMessages); + } + public LogMessageListResponse(Status status, List<VolumeLogMessage> logMessages) { setStatus(status); setLogMessages(logMessages); } - @XmlElementWrapper(name = "logMessages") @XmlElement(name = "logMessage", type = VolumeLogMessage.class) public List<VolumeLogMessage> getLogMessages() { return logMessages; diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/TaskIdResponse.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/TaskIdResponse.java new file mode 100644 index 00000000..f2610104 --- /dev/null +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/TaskIdResponse.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com> + * This file is part of Gluster Management Console. + * + * Gluster Management Console is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Gluster Management Console is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License + * for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see + * <http://www.gnu.org/licenses/>. + *******************************************************************************/ +package com.gluster.storage.management.core.response; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + */ +@XmlRootElement(name="task") +public class TaskIdResponse { + private String id; + + public TaskIdResponse() { + } + + public TaskIdResponse(String id) { + setId(id); + } + + public void setId(String id) { + this.id = id; + } + + @XmlElement(name="id") + public String getId() { + return id; + } +} diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/VolumeListResponse.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/VolumeListResponse.java index 97085603..6518f0d5 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/VolumeListResponse.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/VolumeListResponse.java @@ -4,27 +4,22 @@ import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlTransient; -import com.gluster.storage.management.core.model.Status; import com.gluster.storage.management.core.model.Volume; -@XmlRootElement(name = "response") -public class VolumeListResponse extends AbstractResponse { +@XmlRootElement(name = "volumes") +public class VolumeListResponse { private List<Volume> volumes = new ArrayList<Volume>(); public VolumeListResponse() { } - public VolumeListResponse(Status status, List<Volume> volumes) { - setStatus(status); + public VolumeListResponse(List<Volume> volumes) { setVolumes(volumes); } - @XmlElementWrapper(name = "volumes") @XmlElement(name = "volume", type = Volume.class) public List<Volume> getVolumes() { return this.volumes; @@ -33,10 +28,4 @@ public class VolumeListResponse extends AbstractResponse { public void setVolumes(List<Volume> volumes) { this.volumes = volumes; } - - @Override - @XmlTransient - public Object getData() { - return this.volumes; - } } diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/VolumeOptionInfoListResponse.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/VolumeOptionInfoListResponse.java index 629ccbee..affe1e00 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/VolumeOptionInfoListResponse.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/VolumeOptionInfoListResponse.java @@ -22,47 +22,28 @@ import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlTransient; import com.gluster.storage.management.core.model.Status; import com.gluster.storage.management.core.model.VolumeOptionInfo; -@XmlRootElement(name = "response") -public class VolumeOptionInfoListResponse extends AbstractResponse { +@XmlRootElement(name = "options") +public class VolumeOptionInfoListResponse { private List<VolumeOptionInfo> options = new ArrayList<VolumeOptionInfo>(); public VolumeOptionInfoListResponse() { } public VolumeOptionInfoListResponse(Status status, List<VolumeOptionInfo> options) { - setStatus(status); setOptions(options); } - @XmlElementWrapper(name = "volumeOptionDefaults") - @XmlElement(name = "volumeOption", type=VolumeOptionInfo.class) + @XmlElement(name = "option", type=VolumeOptionInfo.class) public List<VolumeOptionInfo> getOptions() { return options; } - /** - * @param options - * the options to set - */ public void setOptions(List<VolumeOptionInfo> options) { this.options = options; } - - /* - * (non-Javadoc) - * - * @see com.gluster.storage.management.core.model.Response#getData() - */ - @Override - @XmlTransient - public List<VolumeOptionInfo> getData() { - return getOptions(); - } } diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterCoreUtil.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterCoreUtil.java index c238cad7..14bad4fd 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterCoreUtil.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterCoreUtil.java @@ -21,7 +21,9 @@ package com.gluster.storage.management.core.utils; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import com.gluster.storage.management.core.model.Brick; import com.gluster.storage.management.core.model.Disk; @@ -44,4 +46,50 @@ public class GlusterCoreUtil { } return qualifiedBricks; } + + /** + * Extracts a list from a string by splitting it on given delimiter + * @param input the input string + * @return A {@link List} of extracted tokens + */ + public List<String> extractList(String input, String delim) { + String[] arr = input.split(delim); + List<String> output = new ArrayList<String>(); + for(String str : arr) { + String brick = str.trim(); + if(!brick.isEmpty()) { + output.add(brick); + } + } + return null; + } + + /** + * Extracts a map from a string by splitting it on the given primary and secondary delimiter. e.g. The input string + * <i>k1=v1,k2=v2,k3=v3</i> will yield the following map:<br> + * k1 -> v1<br> + * k2 -> v2<br> + * k3 -> v3<br> + * where <b>,</b> is the primary delimiter and <b>=</b> is the secondary delimiter. + * + * @param input + * @param majorDelim + * @param minorDelim + * @return Map of key value pairs + */ + public Map<String, String> extractMap(String input, String majorDelim, String minorDelim) { + String[] arr = input.split(majorDelim); + Map<String, String> output = new LinkedHashMap<String, String>(); + for(String str : arr) { + String[] elements = str.split(minorDelim); + if(elements.length == 2) { + String key = elements[0].trim(); + String value = elements[1].trim(); + if(!key.isEmpty() && !value.isEmpty()) { + output.put(key, value); + } + } + } + return output; + } } diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/DeleteVolumeAction.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/DeleteVolumeAction.java index acfbdd28..eca8e789 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/DeleteVolumeAction.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/DeleteVolumeAction.java @@ -77,17 +77,22 @@ public class DeleteVolumeAction extends AbstractActionDelegate { confirmDelete = true; } - status = client.deleteVolume(volume, confirmDelete); - if (status.isSuccess()) { + try { + client.deleteVolume(volume, confirmDelete); showInfoDialog(actionDesc, "Volume [" + volume.getName() + "] deleted successfully!"); modelManager.deleteVolume(volume); - } else { - if (status.isPartSuccess()) { - showWarningDialog(actionDesc, "Volume deleted, but following error(s) occured: " + status); - modelManager.deleteVolume(volume); - } else { + } catch(Exception e) { + showErrorDialog(actionDesc, e.getMessage()); + + // there is a possibility that the error was in post-delete operation, which means + // volume was deleted, but some other error happened. check if this is the case, + // and if so, update the model manager + if(client.volumeExists(volume.getName())) { showErrorDialog(actionDesc, "Volume [" + volume.getName() + "] could not be deleted! Error: [" - + status + "]"); + + e.getMessage() + "]"); + } else { + modelManager.deleteVolume(volume); + showWarningDialog(actionDesc, "Volume deleted, but following error(s) occured: " + e.getMessage()); } } } diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/RemoveDiskAction.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/RemoveDiskAction.java index ffe2469d..0cad5945 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/RemoveDiskAction.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/RemoveDiskAction.java @@ -53,10 +53,8 @@ public class RemoveDiskAction extends AbstractActionDelegate { BusyIndicator.showWhile(Display.getDefault(), new Runnable() { public void run() { VolumesClient client = new VolumesClient(); - Status status = client.removeBricks(volume.getName(), bricks, confirmDelete); - - if (status.isSuccess()) { - + try { + client.removeBricks(volume.getName(), bricks, confirmDelete); // Remove the bricks from the volume object for (Brick brick : bricks) { volume.removeBrick(brick); @@ -66,13 +64,12 @@ public class RemoveDiskAction extends AbstractActionDelegate { showInfoDialog(actionDesc, "Volume [" + volume.getName() + "] bricks(s) removed successfully!"); - } else { + } catch (Exception e) { showErrorDialog(actionDesc, "Volume [" + volume.getName() - + "] bricks(s) could not be removed! Error: [" + status + "]"); + + "] bricks(s) could not be removed! Error: [" + e.getMessage() + "]"); } } }); - } }); diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/pages/VolumeLogsPage.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/pages/VolumeLogsPage.java index ab1b353d..9bff0213 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/pages/VolumeLogsPage.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/pages/VolumeLogsPage.java @@ -185,16 +185,15 @@ public class VolumeLogsPage extends Composite { return; } - LogMessageListResponse response = client.getLogs(volume.getName(), bricksCombo.getText(), - severityCombo.getText(), fromTimestamp, toTimestamp, Integer.parseInt(lineCountText.getText())); - Status status = response.getStatus(); - if (status.isSuccess()) { - List<VolumeLogMessage> logMessages = response.getLogMessages(); + try { + List<VolumeLogMessage> logMessages = client.getLogs(volume.getName(), bricksCombo.getText(), + severityCombo.getText(), fromTimestamp, toTimestamp, + Integer.parseInt(lineCountText.getText())); tableViewer.setInput(logMessages.toArray(new VolumeLogMessage[0])); tableViewer.refresh(); - } else { - MessageDialog.openError(getShell(), "Volume Logs", "Error while fetching volume logs: [" + status - + "]"); + } catch (Exception ex) { + MessageDialog.openError(getShell(), "Volume Logs", + "Error while fetching volume logs: [" + ex.getMessage() + "]"); } } }); diff --git a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/AbstractResource.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/AbstractResource.java index 2fcf3875..d96ea5f5 100644 --- a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/AbstractResource.java +++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/AbstractResource.java @@ -19,12 +19,21 @@ package com.gluster.storage.management.server.resources; import java.net.URI; +import java.util.ArrayList; import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.StreamingOutput; import javax.ws.rs.core.UriInfo; +import com.gluster.storage.management.core.constants.RESTConstants; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.core.model.Volume; +import com.gluster.storage.management.core.response.VolumeListResponse; + /** * */ @@ -74,7 +83,7 @@ public class AbstractResource { * @return the {@link Response} object */ protected Response errorResponse(String errMessage) { - return Response.serverError().entity(errMessage).build(); + return Response.serverError().type(MediaType.TEXT_HTML).entity(errMessage).build(); } /** @@ -86,6 +95,33 @@ public class AbstractResource { * @return the {@link Response} object */ protected Response badRequestResponse(String errMessage) { - return Response.status(Status.BAD_REQUEST).entity(errMessage).build(); + return Response.status(Status.BAD_REQUEST).type(MediaType.TEXT_HTML).entity(errMessage).build(); + } + + /** + * Creates an OK response and sets the entity in the response body. + * + * @param entity + * Entity to be set in the response body + * @param mediaType + * Media type to be set on the response + * @return the {@link Response} object + */ + protected Response okResponse(Object entity, String mediaType) { + return Response.ok(entity).type(mediaType).build(); + } + + /** + * Creates a streaming output response and sets the given streaming output in the response. Typically used for + * "download" requests + * + * @param entity + * Entity to be set in the response body + * @param mediaType + * Media type to be set on the response + * @return the {@link Response} object + */ + protected Response streamingOutputResponse(StreamingOutput output) { + return Response.ok(output).type(MediaType.APPLICATION_OCTET_STREAM).build(); } } diff --git a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/DiscoveredServersResource.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/DiscoveredServersResource.java index 740f7cfa..4359b7d5 100644 --- a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/DiscoveredServersResource.java +++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/DiscoveredServersResource.java @@ -37,12 +37,14 @@ import com.gluster.storage.management.core.model.Status; import com.gluster.storage.management.core.response.GenericResponse; import com.gluster.storage.management.core.response.ServerListResponse; import com.gluster.storage.management.core.response.StringListResponse; + +import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_FORMAT; import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_DISCOVERED_SERVERS; import com.sun.jersey.spi.resource.Singleton; @Component @Singleton -@Path(RESOURCE_PATH_DISCOVERED_SERVERS) +@Path(RESOURCE_PATH_DISCOVERED_SERVERS + ".{" + PATH_PARAM_FORMAT + "}") public class DiscoveredServersResource extends AbstractServersResource { private List<String> discoveredServerNames = new ArrayList<String>(); diff --git a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/VolumesResource.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/VolumesResource.java index 71dc3579..369ef349 100644 --- a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/VolumesResource.java +++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/VolumesResource.java @@ -20,13 +20,20 @@ */ package com.gluster.storage.management.server.resources; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_ACCESS_PROTOCOLS; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_AUTO_COMMIT; import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_BRICKS; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_CLUSTER_NAME; import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_OPERATION; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_REPLICA_COUNT; import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_SOURCE; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_STRIPE_COUNT; import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_TARGET; -import static com.gluster.storage.management.core.constants.RESTConstants.TASK_START; -import static com.gluster.storage.management.core.constants.RESTConstants.TASK_STOP; -import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_AUTO_COMMIT; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_TRANSPORT_TYPE; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_VOLUME_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_VOLUME_OPTIONS; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_VOLUME_TYPE; +import static com.gluster.storage.management.core.constants.RESTConstants.*; import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_CLUSTER_NAME; import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_VOLUME_NAME; import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_BRICKS; @@ -45,6 +52,8 @@ import static com.gluster.storage.management.core.constants.RESTConstants.RESOUR import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_OPTIONS; import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_CLUSTERS; import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_VOLUMES; +import static com.gluster.storage.management.core.constants.RESTConstants.TASK_START; +import static com.gluster.storage.management.core.constants.RESTConstants.TASK_STOP; import java.io.File; import java.io.IOException; @@ -56,7 +65,6 @@ import java.util.Comparator; import java.util.Date; import java.util.List; -import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.FormParam; import javax.ws.rs.GET; @@ -68,6 +76,7 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; import com.gluster.storage.management.core.constants.CoreConstants; @@ -77,9 +86,8 @@ import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; import com.gluster.storage.management.core.model.Brick; import com.gluster.storage.management.core.model.GlusterServer; import com.gluster.storage.management.core.model.Status; -import com.gluster.storage.management.core.model.Task.TASK_TYPE; -import com.gluster.storage.management.core.model.TaskInfo; import com.gluster.storage.management.core.model.Volume; +import com.gluster.storage.management.core.model.Volume.VOLUME_TYPE; import com.gluster.storage.management.core.model.VolumeLogMessage; import com.gluster.storage.management.core.response.GenericResponse; import com.gluster.storage.management.core.response.LogMessageListResponse; @@ -93,7 +101,6 @@ import com.gluster.storage.management.core.utils.ProcessUtil; import com.gluster.storage.management.server.constants.VolumeOptionsDefaults; import com.gluster.storage.management.server.data.ClusterInfo; import com.gluster.storage.management.server.services.ClusterService; -import com.gluster.storage.management.server.tasks.MigrateDiskTask; import com.gluster.storage.management.server.utils.GlusterUtil; import com.gluster.storage.management.server.utils.ServerUtil; import com.sun.jersey.api.core.InjectParam; @@ -101,7 +108,7 @@ import com.sun.jersey.spi.resource.Singleton; @Singleton @Path(RESOURCE_PATH_CLUSTERS + "/{" + PATH_PARAM_CLUSTER_NAME + "}/" + RESOURCE_VOLUMES) -public class VolumesResource { +public class VolumesResource extends AbstractResource { private static final String PREPARE_BRICK_SCRIPT = "create_volume_directory.py"; private static final String VOLUME_DIRECTORY_CLEANUP_SCRIPT = "clear_volume_directory.py"; private static final String VOLUME_BRICK_LOG_SCRIPT = "get_volume_brick_log.py"; @@ -122,101 +129,215 @@ public class VolumesResource { private VolumeOptionsDefaults volumeOptionsDefaults; @GET - @Produces(MediaType.APPLICATION_XML) - public VolumeListResponse getAllVolumes(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName) { + @Produces({MediaType.APPLICATION_XML}) + public Response getVolumesXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName) { + return getVolumes(clusterName, MediaType.APPLICATION_XML); + } + + @GET + @Produces({MediaType.APPLICATION_JSON}) + public Response getVolumesJSON(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName) { + return getVolumes(clusterName, MediaType.APPLICATION_JSON); + } + + public Response getVolumes(String clusterName, String mediaType) { + if (clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_CLUSTER_NAME + "] is missing in request!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return badRequestResponse("Cluster [" + clusterName + "] not found!"); + } + + return okResponse(getVolumes(clusterName), mediaType); + } + + public VolumeListResponse getVolumes(String clusterName) { GlusterServer onlineServer = glusterServersResource.getOnlineServer(clusterName); if (onlineServer == null) { - return new VolumeListResponse(Status.STATUS_SUCCESS, new ArrayList<Volume>()); + return new VolumeListResponse(new ArrayList<Volume>()); } try { - return new VolumeListResponse(Status.STATUS_SUCCESS, glusterUtil.getAllVolumes(onlineServer.getName())); + return new VolumeListResponse(glusterUtil.getAllVolumes(onlineServer.getName())); } catch (ConnectionException e) { // online server has gone offline! try with a different one. onlineServer = glusterServersResource.getNewOnlineServer(clusterName); if (onlineServer == null) { - return new VolumeListResponse(Status.STATUS_SUCCESS, new ArrayList<Volume>()); + return new VolumeListResponse(new ArrayList<Volume>()); } - return new VolumeListResponse(Status.STATUS_SUCCESS, glusterUtil.getAllVolumes(onlineServer.getName())); + return new VolumeListResponse(glusterUtil.getAllVolumes(onlineServer.getName())); } } @POST - @Consumes(MediaType.APPLICATION_XML) @Produces(MediaType.APPLICATION_XML) - public Status createVolume(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, Volume volume) { - List<String> brickDirectories = GlusterCoreUtil.getQualifiedBrickList(volume.getBricks()); + public Response createVolume(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, @FormParam(FORM_PARAM_VOLUME_NAME) String volumeName, + @FormParam(FORM_PARAM_VOLUME_TYPE) String volumeType, @FormParam(FORM_PARAM_TRANSPORT_TYPE) String transportType, + @FormParam(FORM_PARAM_REPLICA_COUNT) Integer replicaCount, @FormParam(FORM_PARAM_STRIPE_COUNT) Integer stripeCount, + @FormParam(FORM_PARAM_BRICKS) String bricks, @FormParam(FORM_PARAM_ACCESS_PROTOCOLS) String accessProtocols, + @FormParam(FORM_PARAM_VOLUME_OPTIONS) String options) { + String missingParam = checkMissingParamsForCreateVolume(clusterName, volumeName, volumeType, transportType, replicaCount, stripeCount, bricks, accessProtocols, options); + if(missingParam != null) { + return badRequestResponse("Parameter [" + missingParam + "] is missing in request!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return badRequestResponse("Cluster [" + clusterName + "] not found!"); + } + + if (volumeType.equals(VOLUME_TYPE.DISTRIBUTED_MIRROR) && replicaCount <= 0) { + return badRequestResponse("Replica count must be a positive integer"); + } + + if (volumeType.equals(VOLUME_TYPE.DISTRIBUTED_STRIPE) && stripeCount <= 0) { + return badRequestResponse("Stripe count must be a positive integer"); + } GlusterServer onlineServer = glusterServersResource.getOnlineServer(clusterName); if (onlineServer == null) { - return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName + "]"); + return errorResponse("No online servers found in cluster [" + clusterName + "]"); } - Status status = null; try { - status = glusterUtil.createVolume(volume, brickDirectories, onlineServer.getName()); + glusterUtil.createVolume(onlineServer.getName(), volumeName, volumeType, transportType, replicaCount, stripeCount, bricks, accessProtocols, options); + return createdResponse(volumeName); } catch (ConnectionException e) { // online server has gone offline! try with a different one. onlineServer = glusterServersResource.getNewOnlineServer(clusterName); if (onlineServer == null) { - return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName - + "]"); + return errorResponse("No online servers found in cluster [" + clusterName + "]"); + } + + try { + glusterUtil.createVolume(onlineServer.getName(), volumeName, volumeType, transportType, replicaCount, stripeCount, bricks, accessProtocols, options); + return createdResponse(volumeName); + } catch(Exception e1) { + return errorResponse(e1.getMessage()); } - status = glusterUtil.createVolume(volume, brickDirectories, onlineServer.getName()); } - return status; } - @SuppressWarnings("rawtypes") + /** + * Returns name of the missing parameter if any. If all parameters are present, + * @param clusterName + * @param volumeName + * @param volumeType + * @param transportType + * @param replicaCount + * @param stripeCount + * @param bricks + * @param accessProtocols + * @param options + * @return + */ + private String checkMissingParamsForCreateVolume(String clusterName, String volumeName, String volumeType, + String transportType, Integer replicaCount, Integer stripeCount, String bricks, String accessProtocols, + String options) { + + return (clusterName == null || clusterName.isEmpty()) ? FORM_PARAM_CLUSTER_NAME : + (volumeName == null || volumeName.isEmpty()) ? FORM_PARAM_VOLUME_NAME : + (volumeType == null || volumeType.isEmpty()) ? FORM_PARAM_VOLUME_TYPE : + (transportType == null || transportType.isEmpty()) ? FORM_PARAM_TRANSPORT_TYPE : + (replicaCount == null) ? FORM_PARAM_REPLICA_COUNT : + (stripeCount == null) ? FORM_PARAM_STRIPE_COUNT : + (bricks == null || bricks.isEmpty()) ? FORM_PARAM_BRICKS : + (accessProtocols == null || accessProtocols.isEmpty()) ? FORM_PARAM_ACCESS_PROTOCOLS : + (options == null || options.isEmpty()) ? FORM_PARAM_VOLUME_OPTIONS : + null; + } + @GET @Path("{" + PATH_PARAM_VOLUME_NAME + "}") @Produces(MediaType.APPLICATION_XML) - public GenericResponse getVolume(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, + public Response getVolumeXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName) { + return getVolume(clusterName, volumeName, MediaType.APPLICATION_XML); + } + + @GET + @Path("{" + PATH_PARAM_VOLUME_NAME + "}") + @Produces(MediaType.APPLICATION_JSON) + public Response getVolumeJSON(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, + @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName) { + return getVolume(clusterName, volumeName, MediaType.APPLICATION_JSON); + } + + private Response getVolume(String clusterName, String volumeName, String mediaType) { + Volume volume = null; + + if (clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_CLUSTER_NAME + "] is missing in request!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return badRequestResponse("Cluster [" + clusterName + "] not found!"); + } + + try { + volume = getVolume(clusterName, volumeName); + return okResponse(volume, mediaType); + } catch(Exception e) { + return errorResponse(e.getMessage()); + } + } + + private Volume getVolume(String clusterName, String volumeName) { + Volume volume; GlusterServer onlineServer = glusterServersResource.getOnlineServer(clusterName); if (onlineServer == null) { - return new GenericResponse<Volume>(new Status(Status.STATUS_CODE_FAILURE, - "No online servers found in cluster [" + clusterName + "]"), null); + throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]"); } try { - return new GenericResponse<Volume>(Status.STATUS_SUCCESS, glusterUtil.getVolume(volumeName, - onlineServer.getName())); + volume = glusterUtil.getVolume(volumeName, onlineServer.getName()); } catch (ConnectionException e) { // online server has gone offline! try with a different one. onlineServer = glusterServersResource.getNewOnlineServer(clusterName); if (onlineServer == null) { - return new GenericResponse<Volume>(new Status(Status.STATUS_CODE_FAILURE, - "No online servers found in cluster [" + clusterName + "]"), null); + throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]"); } - return new GenericResponse<Volume>(Status.STATUS_SUCCESS, glusterUtil.getVolume(volumeName, - onlineServer.getName())); + volume = glusterUtil.getVolume(volumeName, onlineServer.getName()); } + return volume; } @PUT @Path("{" + PATH_PARAM_VOLUME_NAME + "}") - @Produces(MediaType.APPLICATION_XML) - public Status performOperation(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, + public Response performOperation(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName, @FormParam(FORM_PARAM_OPERATION) String operation) { + if (clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_CLUSTER_NAME + "] is missing in request!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return badRequestResponse("Cluster [" + clusterName + "] not found!"); + } + GlusterServer onlineServer = glusterServersResource.getOnlineServer(clusterName); if (onlineServer == null) { - return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName + "]"); + return errorResponse("No online servers found in cluster [" + clusterName + "]"); } try { - return performOperation(volumeName, operation, onlineServer); + performOperation(volumeName, operation, onlineServer); } catch (ConnectionException e) { // online server has gone offline! try with a different one. onlineServer = glusterServersResource.getNewOnlineServer(clusterName); if (onlineServer == null) { - return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName - + "]"); + return errorResponse("No online servers found in cluster [" + clusterName + "]"); + } + + try { + performOperation(volumeName, operation, onlineServer); + } catch(Exception e1) { + // TODO: Log the exception + return errorResponse(e1.getMessage()); } - return performOperation(volumeName, operation, onlineServer); } + return noContentResponse(); } private Status performOperation(String volumeName, String operation, GlusterServer onlineServer) { @@ -231,79 +352,121 @@ public class VolumesResource { @DELETE @Path("{" + PATH_PARAM_VOLUME_NAME + "}") - @Produces(MediaType.APPLICATION_XML) - public Status deleteVolume(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, + public Response deleteVolume(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName, @QueryParam(QUERY_PARAM_DELETE_OPTION) boolean deleteFlag) { - GlusterServer onlineServer = glusterServersResource.getOnlineServer(clusterName); - if (onlineServer == null) { - return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName + "]"); + if (clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_CLUSTER_NAME + "] is missing in request!"); } + if (clusterService.getCluster(clusterName) == null) { + return badRequestResponse("Cluster [" + clusterName + "] not found!"); + } + Volume volume = null; try { - volume = glusterUtil.getVolume(volumeName, onlineServer.getName()); - } catch (ConnectionException e) { - // online server has gone offline! try with a different one. - onlineServer = glusterServersResource.getNewOnlineServer(clusterName); - if (onlineServer == null) { - return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName - + "]"); - } - volume = glusterUtil.getVolume(volumeName, onlineServer.getName()); + volume = getVolume(clusterName, volumeName); + } catch (Exception e) { + // TODO: Log the exception + return errorResponse(e.getMessage()); } + List<Brick> bricks = volume.getBricks(); - Status status = glusterUtil.deleteVolume(volumeName, onlineServer.getName()); - - if (status.isSuccess()) { - Status postDeleteStatus = postDelete(volumeName, bricks, deleteFlag); - - if (!postDeleteStatus.isSuccess()) { - status.setCode(Status.STATUS_CODE_PART_SUCCESS); - status.setMessage("Error in post-delete operation: " + postDeleteStatus); - } + Status status = glusterUtil.deleteVolume(volumeName, glusterServersResource.getOnlineServer(clusterName) + .getName()); + if(!status.isSuccess()) { + return errorResponse("Couldn't delete volume [" + volumeName + "]. Error: " + status); } - return status; + + try { + postDelete(volumeName, bricks, deleteFlag); + } catch(Exception e) { + return errorResponse("Volume [" + volumeName + + "] deleted from cluster, however following errors happened: " + CoreConstants.NEWLINE + + e.getMessage()); + } + + return noContentResponse(); } @DELETE @Path("{" + QUERY_PARAM_VOLUME_NAME + "}/" + RESOURCE_BRICKS) - @Produces(MediaType.APPLICATION_XML) - public Status removeBricks(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, + public Response removeBricks(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, @PathParam(QUERY_PARAM_VOLUME_NAME) String volumeName, @QueryParam(QUERY_PARAM_BRICKS) String bricks, @QueryParam(QUERY_PARAM_DELETE_OPTION) boolean deleteFlag) { List<String> brickList = Arrays.asList(bricks.split(",")); // Convert from comma separated string (query // parameter) + if (clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_CLUSTER_NAME + "] is missing in request!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return badRequestResponse("Cluster [" + clusterName + "] not found!"); + } GlusterServer onlineServer = glusterServersResource.getOnlineServer(clusterName); if (onlineServer == null) { - return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName + "]"); + return errorResponse("No online servers found in cluster [" + clusterName + "]"); } - Status status = null; try { - status = glusterUtil.removeBricks(volumeName, brickList, onlineServer.getName()); + removeBricks(clusterName, volumeName, brickList, onlineServer); + } catch(Exception e) { + return errorResponse(e.getMessage()); + } + + try { + cleanupDirectories(brickList, volumeName, brickList.size(), deleteFlag); + } catch(Exception e) { + // append cleanup error to prepare brick error + return errorResponse(e.getMessage()); + } + + return noContentResponse(); + } + + public void removeBricks(String clusterName, String volumeName, List<String> brickList, GlusterServer onlineServer) { + try { + glusterUtil.removeBricks(volumeName, brickList, onlineServer.getName()); } catch (ConnectionException e) { // online server has gone offline! try with a different one. onlineServer = glusterServersResource.getNewOnlineServer(clusterName); if (onlineServer == null) { - return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName - + "]"); + throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]"); } - status = glusterUtil.removeBricks(volumeName, brickList, onlineServer.getName()); + glusterUtil.removeBricks(volumeName, brickList, onlineServer.getName()); } + } - if (status.isSuccess()) { - Status cleanupStatus = cleanupDirectories(brickList, volumeName, brickList.size(), deleteFlag); - if (!cleanupStatus.isSuccess()) { - // append cleanup error to prepare brick error - status.setMessage(status.getMessage() + CoreConstants.NEWLINE + cleanupStatus.getMessage()); + @SuppressWarnings("rawtypes") + private void cleanupDirectories(List<String> bricks, String volumeName, int maxIndex, boolean deleteFlag) { + Status result; + String errors = ""; + for (int i = 0; i < maxIndex; i++) { + String[] brickInfo = bricks.get(i).split(":"); + String serverName = brickInfo[0]; + String brickDirectory = brickInfo[1]; + + String mountPoint = brickDirectory.substring(0, brickDirectory.lastIndexOf("/")); + Object response = serverUtil.executeOnServer(true, serverName, VOLUME_DIRECTORY_CLEANUP_SCRIPT + " " + + mountPoint + " " + volumeName + " " + (deleteFlag ? "-d" : ""), GenericResponse.class); + if (response instanceof GenericResponse) { + result = ((GenericResponse) response).getStatus(); + if (!result.isSuccess()) { + errors += "[" + mountPoint + "][" + volumeName + "] => " + result + + CoreConstants.NEWLINE; + } + } else { + Status errStatus = (Status) response; + errors += "[" + mountPoint + "][" + volumeName + "] => " + errStatus + CoreConstants.NEWLINE; } } - return status; + if(!errors.trim().isEmpty()) { + throw new GlusterRuntimeException("Volume directory cleanup errors: " + errors.trim()); + } } - private Status postDelete(String volumeName, List<Brick> bricks, boolean deleteFlag) { + private void postDelete(String volumeName, List<Brick> bricks, boolean deleteFlag) { Status result; for (Brick brick : bricks) { String brickDirectory = brick.getBrickDirectory(); @@ -312,93 +475,122 @@ public class VolumesResource { result = (Status) serverUtil.executeOnServer(true, brick.getServerName(), VOLUME_DIRECTORY_CLEANUP_SCRIPT + " " + mountPoint + " " + volumeName + (deleteFlag ? " -d" : ""), Status.class); if (!result.isSuccess()) { - return result; + throw new GlusterRuntimeException("Error in post-delete operation of volume [" + volumeName + "]: " + + result); } } - return new Status(Status.STATUS_CODE_SUCCESS, "Post volume delete operation successfully initiated"); } @POST @Path("{" + PATH_PARAM_VOLUME_NAME + " }/" + RESOURCE_OPTIONS) - @Produces(MediaType.APPLICATION_XML) - public Status setOption(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, + public Response setOption(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName, @FormParam(RESTConstants.FORM_PARAM_OPTION_KEY) String key, @FormParam(RESTConstants.FORM_PARAM_OPTION_VALUE) String value) { + if (clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_CLUSTER_NAME + "] is missing in request!"); + } + + if(volumeName == null || volumeName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_VOLUME_NAME + "] is missing in request!"); + } + + if(key == null || key.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_OPTION_KEY + "] is missing in request!"); + } + + if(value == null || value.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_OPTION_VALUE + "] is missing in request!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return badRequestResponse("Cluster [" + clusterName + "] not found!"); + } + GlusterServer onlineServer = glusterServersResource.getOnlineServer(clusterName); if (onlineServer == null) { - return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName + "]"); + return errorResponse("No online servers found in cluster [" + clusterName + "]"); } - + try { - return glusterUtil.setOption(volumeName, key, value, onlineServer.getName()); + glusterUtil.setOption(volumeName, key, value, onlineServer.getName()); } catch (ConnectionException e) { // online server has gone offline! try with a different one. onlineServer = glusterServersResource.getNewOnlineServer(clusterName); if (onlineServer == null) { - return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName - + "]"); + return errorResponse("No online servers found in cluster [" + clusterName + "]"); + } + + try { + glusterUtil.setOption(volumeName, key, value, onlineServer.getName()); + } catch(Exception e1) { + return errorResponse(e1.getMessage()); } - return glusterUtil.setOption(volumeName, key, value, onlineServer.getName()); + } catch(Exception e) { + return errorResponse(e.getMessage()); } + + return noContentResponse(); } @PUT @Path("{" + PATH_PARAM_VOLUME_NAME + " }/" + RESOURCE_OPTIONS) - @Produces(MediaType.APPLICATION_XML) - public Status resetOptions(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, + public Response resetOptions(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName) { + if (clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_CLUSTER_NAME + "] is missing in request!"); + } + + if(volumeName == null || volumeName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_VOLUME_NAME + "] is missing in request!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return badRequestResponse("Cluster [" + clusterName + "] not found!"); + } + GlusterServer onlineServer = glusterServersResource.getOnlineServer(clusterName); if (onlineServer == null) { - return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName + "]"); + return errorResponse("No online servers found in cluster [" + clusterName + "]"); } try { - return glusterUtil.resetOptions(volumeName, onlineServer.getName()); + glusterUtil.resetOptions(volumeName, onlineServer.getName()); } catch (ConnectionException e) { // online server has gone offline! try with a different one. onlineServer = glusterServersResource.getNewOnlineServer(clusterName); if (onlineServer == null) { - return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName - + "]"); + return errorResponse("No online servers found in cluster [" + clusterName + "]"); } - return glusterUtil.resetOptions(volumeName, onlineServer.getName()); + + try { + glusterUtil.resetOptions(volumeName, onlineServer.getName()); + } catch(Exception e1) { + return errorResponse(e1.getMessage()); + } + } catch(Exception e) { + return errorResponse(e.getMessage()); } + + return noContentResponse(); } @GET @Path(RESOURCE_DEFAULT_OPTIONS) @Produces(MediaType.APPLICATION_XML) - public VolumeOptionInfoListResponse getDefaultOptions(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName) { + public VolumeOptionInfoListResponse getDefaultOptionsXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName) { // TODO: Fetch all volume options with their default values from GlusterFS // whenever such a CLI command is made available in GlusterFS return new VolumeOptionInfoListResponse(Status.STATUS_SUCCESS, volumeOptionsDefaults.getDefaults()); } - @SuppressWarnings("rawtypes") - private Status cleanupDirectories(List<String> bricks, String volumeName, int maxIndex, boolean deleteFlag) { - Status result; - for (int i = 0; i < maxIndex; i++) { - String[] brickInfo = bricks.get(i).split(":"); - String serverName = brickInfo[0]; - String brickDirectory = brickInfo[1]; - - String mountPoint = brickDirectory.substring(0, brickDirectory.lastIndexOf("/")); - Object response = serverUtil.executeOnServer(true, serverName, VOLUME_DIRECTORY_CLEANUP_SCRIPT + " " - + mountPoint + " " + volumeName + " " + (deleteFlag ? "-d" : ""), GenericResponse.class); - if (response instanceof GenericResponse) { - result = ((GenericResponse) response).getStatus(); - if (!result.isSuccess()) { - // TODO: append error and continue with cleaning up of other directories - return result; - } - } else { - // TODO: append error and continue with cleaning up of other directories - // In case of script execution failure, a Status object will be returned. - return (Status) response; - } - } - return new Status(Status.STATUS_CODE_SUCCESS, "Directories cleaned up successfully!"); + @GET + @Path(RESOURCE_DEFAULT_OPTIONS) + @Produces(MediaType.APPLICATION_JSON) + public VolumeOptionInfoListResponse getDefaultOptionsJSON(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName) { + // TODO: Fetch all volume options with their default values from GlusterFS + // whenever such a CLI command is made available in GlusterFS + return new VolumeOptionInfoListResponse(Status.STATUS_SUCCESS, volumeOptionsDefaults.getDefaults()); } private List<VolumeLogMessage> getBrickLogs(Volume volume, Brick brick, Integer lineCount) @@ -436,34 +628,38 @@ public class VolumesResource { @GET @Produces(MediaType.APPLICATION_OCTET_STREAM) @Path("{" + PATH_PARAM_VOLUME_NAME + "}/" + RESOURCE_LOGS + "/" + RESOURCE_DOWNLOAD) - public StreamingOutput downloadLogs(@PathParam(PATH_PARAM_CLUSTER_NAME) final String clusterName, + public Response downloadLogs(@PathParam(PATH_PARAM_CLUSTER_NAME) final String clusterName, @PathParam(PATH_PARAM_VOLUME_NAME) final String volumeName) { - final ClusterInfo cluster = clusterService.getCluster(clusterName); - if (cluster == null) { - throw new GlusterRuntimeException("Cluster [" + clusterName + "] doesn't exist!"); + if (clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_CLUSTER_NAME + "] is missing in request!"); } - - final Volume volume = (Volume) getVolume(clusterName, volumeName).getData(); - if (volume == null) { - throw new GlusterRuntimeException("Volume [" + volumeName + "] doesn't exist in cluster [" + clusterName - + "]!"); + + if (clusterService.getCluster(clusterName) == null) { + return badRequestResponse("Cluster [" + clusterName + "] not found!"); } - - return new StreamingOutput() { - - @Override - public void write(OutputStream output) throws IOException, WebApplicationException { - try { - File archiveFile = new File(downloadLogs(volume)); - output.write(FileUtil.readFileAsByteArray(archiveFile)); - archiveFile.delete(); - } catch (Exception e) { - // TODO: Log the exception - e.printStackTrace(); - throw new GlusterRuntimeException("Exception while downloading/archiving volume log files!", e); + + try { + final Volume volume = getVolume(clusterName, volumeName); + StreamingOutput output = new StreamingOutput() { + + @Override + public void write(OutputStream output) throws IOException, WebApplicationException { + try { + File archiveFile = new File(downloadLogs(volume)); + output.write(FileUtil.readFileAsByteArray(archiveFile)); + archiveFile.delete(); + } catch (Exception e) { + // TODO: Log the exception + e.printStackTrace(); + String errMsg = "Exception while downloading/archiving volume log files : " + e.getMessage(); + output.write(errMsg.getBytes()); + } } - } - }; + }; + return streamingOutputResponse(output); + } catch(Exception e) { + return errorResponse("Volume [" + volumeName + "] doesn't exist in cluster [" + clusterName + "]!"); + } } private String downloadLogs(Volume volume) { @@ -498,45 +694,66 @@ public class VolumesResource { @GET @Path("{" + PATH_PARAM_VOLUME_NAME + "}/" + RESOURCE_LOGS) - public LogMessageListResponse getLogs(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, + @Produces(MediaType.APPLICATION_XML) + public Response getLogsXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName, @QueryParam(QUERY_PARAM_BRICK_NAME) String brickName, @QueryParam(QUERY_PARAM_LOG_SEVERITY) String severity, @QueryParam(QUERY_PARAM_FROM_TIMESTAMP) String fromTimestamp, @QueryParam(QUERY_PARAM_TO_TIMESTAMP) String toTimestamp, @QueryParam(QUERY_PARAM_LINE_COUNT) Integer lineCount, @QueryParam(QUERY_PARAM_DOWNLOAD) Boolean download) { - List<VolumeLogMessage> logMessages = null; + return getLogs(clusterName, volumeName, brickName, severity, fromTimestamp, toTimestamp, lineCount, MediaType.APPLICATION_XML); + } - ClusterInfo cluster = clusterService.getCluster(clusterName); - if (cluster == null) { - return new LogMessageListResponse(new Status(Status.STATUS_CODE_FAILURE, "Cluster [" + clusterName - + "] doesn't exist!"), null); + @GET + @Path("{" + PATH_PARAM_VOLUME_NAME + "}/" + RESOURCE_LOGS) + @Produces(MediaType.APPLICATION_JSON) + public Response getLogsJSON(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, + @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName, @QueryParam(QUERY_PARAM_BRICK_NAME) String brickName, + @QueryParam(QUERY_PARAM_LOG_SEVERITY) String severity, + @QueryParam(QUERY_PARAM_FROM_TIMESTAMP) String fromTimestamp, + @QueryParam(QUERY_PARAM_TO_TIMESTAMP) String toTimestamp, + @QueryParam(QUERY_PARAM_LINE_COUNT) Integer lineCount, @QueryParam(QUERY_PARAM_DOWNLOAD) Boolean download) { + return getLogs(clusterName, volumeName, brickName, severity, fromTimestamp, toTimestamp, lineCount, MediaType.APPLICATION_JSON); + } + + public Response getLogs(String clusterName, String volumeName, String brickName, String severity, + String fromTimestamp, String toTimestamp, Integer lineCount, String mediaType) { + if (clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_CLUSTER_NAME + "] is missing in request!"); + } + + if (volumeName == null || volumeName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_VOLUME_NAME + "] is missing in request!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return badRequestResponse("Cluster [" + clusterName + "] not found!"); } + List<VolumeLogMessage> logMessages = null; + Volume volume = null; try { - Volume volume = (Volume) getVolume(clusterName, volumeName).getData(); - if (volume == null) { - return new LogMessageListResponse(new Status(Status.STATUS_CODE_FAILURE, "Volume [" + volumeName - + "] doesn't exist in cluster [" + clusterName + "]!"), null); - } + volume = (Volume) getVolume(clusterName, volumeName); + } catch(Exception e) { + return errorResponse(e.getMessage()); + } - if (brickName == null || brickName.isEmpty() || brickName.equals(CoreConstants.ALL)) { - logMessages = getLogsForAllBricks(volume, lineCount); - } else { - // fetch logs for given brick of the volume - for (Brick brick : volume.getBricks()) { - if (brick.getQualifiedName().equals(brickName)) { - logMessages = getBrickLogs(volume, brick, lineCount); - break; - } + if (brickName == null || brickName.isEmpty() || brickName.equals(CoreConstants.ALL)) { + logMessages = getLogsForAllBricks(volume, lineCount); + } else { + // fetch logs for given brick of the volume + for (Brick brick : volume.getBricks()) { + if (brick.getQualifiedName().equals(brickName)) { + logMessages = getBrickLogs(volume, brick, lineCount); + break; } } - } catch (Exception e) { - return new LogMessageListResponse(new Status(e), null); } filterLogsBySeverity(logMessages, severity); filterLogsByTime(logMessages, fromTimestamp, toTimestamp); - return new LogMessageListResponse(Status.STATUS_SUCCESS, logMessages); + + return okResponse(new LogMessageListResponse(logMessages), mediaType); } private void filterLogsByTime(List<VolumeLogMessage> logMessages, String fromTimestamp, String toTimestamp) { @@ -600,71 +817,87 @@ public class VolumesResource { @POST @Path("{" + QUERY_PARAM_VOLUME_NAME + "}/" + RESOURCE_BRICKS) - public Status addBricks(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, + public Response addBricks(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, @PathParam(QUERY_PARAM_VOLUME_NAME) String volumeName, @FormParam(FORM_PARAM_BRICKS) String bricks) { + if (clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_CLUSTER_NAME + "] is missing in request!"); + } + + if (volumeName == null || volumeName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_VOLUME_NAME + "] is missing in request!"); + } + + if (bricks == null || bricks.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_BRICKS + "] is missing in request!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return badRequestResponse("Cluster [" + clusterName + "] not found!"); + } + GlusterServer onlineServer = glusterServersResource.getOnlineServer(clusterName); if (onlineServer == null) { - return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName + "]"); + return errorResponse("No online servers found in cluster [" + clusterName + "]"); } try { - return glusterUtil.addBricks(volumeName, Arrays.asList(bricks.split(",")), onlineServer.getName()); + glusterUtil.addBricks(volumeName, Arrays.asList(bricks.split(",")), onlineServer.getName()); } catch (ConnectionException e) { // online server has gone offline! try with a different one. onlineServer = glusterServersResource.getNewOnlineServer(clusterName); if (onlineServer == null) { - return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName - + "]"); + return errorResponse("No online servers found in cluster [" + clusterName + "]"); } - return glusterUtil.addBricks(volumeName, Arrays.asList(bricks.split(",")), onlineServer.getName()); - } - } - -// @PUT -// @Path("{" + QUERY_PARAM_VOLUME_NAME + "}/" + RESOURCE_BRICKS) -// public Status replaceBrick(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, -// @PathParam(QUERY_PARAM_VOLUME_NAME) String volumeName, @FormParam(FORM_PARAM_SOURCE) String diskFrom, -// @FormParam(FORM_PARAM_TARGET) String diskTo, @FormParam(FORM_PARAM_OPERATION) String operation) { -// GlusterServer onlineServer = glusterServersResource.getOnlineServer(clusterName); -// if (onlineServer == null) { -// return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName + "]"); -// } -// -// try { -// -// return glusterUtil.migrateDisk(volumeName, diskFrom, diskTo, "start", onlineServer.getName()); -// } catch (ConnectionException e) { -// // online server has gone offline! try with a different one. -// onlineServer = glusterServersResource.getNewOnlineServer(clusterName); -// if (onlineServer == null) { -// return new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" + clusterName -// + "]"); -// } -// return glusterUtil.migrateDisk(volumeName, diskFrom, diskTo, operation, onlineServer.getName()); -// } -// } - + + try { + glusterUtil.addBricks(volumeName, Arrays.asList(bricks.split(",")), onlineServer.getName()); + } catch(Exception e1) { + return errorResponse(e1.getMessage()); + } + } catch(Exception e1) { + return errorResponse(e1.getMessage()); + } + + return noContentResponse(); + } + @PUT @Path("{" + PATH_PARAM_VOLUME_NAME + "}/" + RESOURCE_BRICKS) - public TaskResponse migrateBrick(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, + public Response migrateBrick(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName, @FormParam(FORM_PARAM_SOURCE) String fromBrick, @FormParam(FORM_PARAM_TARGET) String toBrick, @FormParam(FORM_PARAM_AUTO_COMMIT) Boolean autoCommit) { - TaskResponse taskResponse = new TaskResponse(); + if (clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_CLUSTER_NAME + "] is missing in request!"); + } + + if (volumeName == null || volumeName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_VOLUME_NAME + "] is missing in request!"); + } + GlusterServer onlineServer = glusterServersResource.getOnlineServer(clusterName); if (onlineServer == null) { - taskResponse.setData(null); - taskResponse.setStatus(new Status(Status.STATUS_CODE_FAILURE, "No online servers found in cluster [" - + clusterName + "]")); - return taskResponse; + return errorResponse("No online servers found in cluster [" + clusterName + "]"); } - + + String taskId = null; try { - return glusterUtil.migrateBrick(volumeName, fromBrick, toBrick, "start", autoCommit, onlineServer.getName()); + taskId = glusterUtil.migrateBrickStart(volumeName, fromBrick, toBrick, autoCommit, onlineServer.getName()); } catch (ConnectionException e) { + // online server has gone offline! try with a different one. onlineServer = glusterServersResource.getNewOnlineServer(clusterName); - return glusterUtil.migrateBrick(volumeName, fromBrick, toBrick, "start", autoCommit, onlineServer.getName()); + + try { + taskId = glusterUtil.migrateBrickStart(volumeName, fromBrick, toBrick, autoCommit, onlineServer.getName()); + } catch(Exception e1) { + return errorResponse(e1.getMessage()); + } + } catch(Exception e1) { + return errorResponse(e1.getMessage()); } + + return noContentResponse(uriInfo.getBaseUri() + "/" + RESTConstants.RESOURCE_PATH_CLUSTERS + "/" + clusterName + + "/" + RESOURCE_TASKS + "/" + taskId); } public static void main(String[] args) throws ClassNotFoundException { @@ -690,8 +923,6 @@ public class VolumesResource { // System.out.println("Code : " + status.getCode()); // System.out.println("Message " + status.getMessage()); - Status status1 = vr.removeBricks("testCluster", "test", "192.168.1.210:sdb", true); - System.out.println("Code : " + status1.getCode()); - System.out.println("Message " + status1.getMessage()); + vr.removeBricks("testCluster", "test", "192.168.1.210:sdb", true); } } diff --git a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/GlusterUtil.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/GlusterUtil.java index 6b4bdb35..3472745e 100644 --- a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/GlusterUtil.java +++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/GlusterUtil.java @@ -23,12 +23,14 @@ package com.gluster.storage.management.server.utils; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.gluster.storage.management.core.constants.CoreConstants; +import com.gluster.storage.management.core.constants.RESTConstants; import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; import com.gluster.storage.management.core.model.Brick; import com.gluster.storage.management.core.model.Brick.BRICK_STATUS; @@ -43,6 +45,7 @@ import com.gluster.storage.management.core.model.Volume.VOLUME_STATUS; import com.gluster.storage.management.core.model.Volume.VOLUME_TYPE; import com.gluster.storage.management.core.model.VolumeOptions; import com.gluster.storage.management.core.response.TaskResponse; +import com.gluster.storage.management.core.utils.GlusterCoreUtil; import com.gluster.storage.management.core.utils.ProcessResult; import com.gluster.storage.management.server.resources.TasksResource; import com.gluster.storage.management.server.tasks.MigrateDiskTask; @@ -68,6 +71,8 @@ public class GlusterUtil { private static final String VOLUME_LOG_LOCATION_PFX = "log file location:"; private static final String VOLUME_TYPE_DISTRIBUTE = "Distribute"; private static final String VOLUME_TYPE_REPLICATE = "Replicate"; + + private static final GlusterCoreUtil glusterCoreUtil = new GlusterCoreUtil(); @Autowired private SshUtil sshUtil; @@ -203,41 +208,52 @@ public class GlusterUtil { return new Status(sshUtil.executeRemote(knownServer, "gluster --mode=script volume stop " + volumeName)); } - public Status resetOptions(String volumeName, String knownServer) { - return new Status(sshUtil.executeRemote(knownServer, "gluster volume reset " + volumeName)); + public void resetOptions(String volumeName, String knownServer) { + ProcessResult result = sshUtil.executeRemote(knownServer, "gluster volume reset " + volumeName); + if(!result.isSuccess()) { + throw new GlusterRuntimeException("Couldn't reset options for volume [" + volumeName + "]! Error: " + + result); + } } - public Status createVolume(Volume volume, List<String> brickDirectories, String knownServer) { + public void createVolume(String knownServer, String volumeName, String volumeTypeStr, String transportTypeStr, + Integer replicaCount, Integer stripeCount, String bricks, String accessProtocols, String options) { + int count = 1; // replica or stripe count - String volumeType = null; - VOLUME_TYPE volType = volume.getVolumeType(); + + VOLUME_TYPE volType = Volume.getVolumeTypeByStr(volumeTypeStr); + String volTypeArg = null; if (volType == VOLUME_TYPE.DISTRIBUTED_MIRROR) { - volumeType = "replica"; - count = volume.getReplicaCount(); + volTypeArg = "replica"; + count = replicaCount; } else if (volType == VOLUME_TYPE.DISTRIBUTED_STRIPE) { - volumeType = "stripe"; - count = volume.getStripeCount(); + volTypeArg = "stripe"; + count = stripeCount; } - String transportTypeStr = null; - TRANSPORT_TYPE transportType = volume.getTransportType(); - transportTypeStr = (transportType == TRANSPORT_TYPE.ETHERNET) ? "tcp" : "rdma"; - String command = prepareVolumeCreateCommand(volume, brickDirectories, count, volumeType, transportTypeStr); + String transportTypeArg = null; + TRANSPORT_TYPE transportType = Volume.getTransportTypeByStr(transportTypeStr); + transportTypeArg = (transportType == TRANSPORT_TYPE.ETHERNET) ? "tcp" : "rdma"; + + String command = prepareVolumeCreateCommand(volumeName, glusterCoreUtil.extractList(bricks, ","), count, + volTypeArg, transportTypeArg); ProcessResult result = sshUtil.executeRemote(knownServer, command); if (!result.isSuccess()) { - return new Status(result); + throw new GlusterRuntimeException("Error in creating volume [" + volumeName + "]: " + result); } - Status status = createOptions(volume, knownServer); - if (!status.isSuccess()) { // Return partial success if set volume option failed. - status.setCode(Status.STATUS_CODE_PART_SUCCESS); - status.setMessage("Error while setting volume options: " + status); + + try { + createOptions(volumeName, glusterCoreUtil.extractMap(options, ",", "="), knownServer); + } catch(Exception e) { + throw new GlusterRuntimeException( + "Volume created successfully, however following errors occurred while setting options: " + + CoreConstants.NEWLINE + e.getMessage()); } - return status; } - private String prepareVolumeCreateCommand(Volume volume, List<String> brickDirectories, int count, + private String prepareVolumeCreateCommand(String volumeName, List<String> brickDirectories, int count, String volumeType, String transportTypeStr) { - StringBuilder command = new StringBuilder("gluster volume create " + volume.getName() + " "); + StringBuilder command = new StringBuilder("gluster volume create " + volumeName + " "); if (volumeType != null) { command.append(volumeType + " " + count + " "); } @@ -248,24 +264,34 @@ public class GlusterUtil { return command.toString(); } - public Status createOptions(Volume volume, String knownServer) { - VolumeOptions options = volume.getOptions(); + public void createOptions(String volumeName, Map<String, String> options, String knownServer) { + String errors = ""; if (options != null) { for (Entry<String, String> option : options.entrySet()) { String key = option.getKey(); String value = option.getValue(); - Status status = setOption(volume.getName(), key, value, knownServer); - if (!status.isSuccess()) { - return status; + + try { + setOption(volumeName, key, value, knownServer); + } catch(Exception e) { + // append error + errors += e.getMessage() + CoreConstants.NEWLINE; } } } - return Status.STATUS_SUCCESS; + if (!errors.trim().isEmpty()) { + throw new GlusterRuntimeException("Errors while setting option(s) on volume [" + volumeName + "] : " + + errors.trim()); + } } - public Status setOption(String volumeName, String key, String value, String knownServer) { - return new Status(sshUtil.executeRemote(knownServer, "gluster volume set " + volumeName + " " + key + " " - + "\"" + value + "\"")); + public void setOption(String volumeName, String key, String value, String knownServer) { + ProcessResult result = sshUtil.executeRemote(knownServer, "gluster volume set " + volumeName + " " + key + " " + + "\"" + value + "\""); + if (!result.isSuccess()) { + throw new GlusterRuntimeException("Volume [" + volumeName + "] set [" + key + "=" + value + "] => " + + result); + } } public Status deleteVolume(String volumeName, String knownServer) { @@ -275,8 +301,8 @@ public class GlusterUtil { private String getVolumeInfo(String volumeName, String knownServer) { ProcessResult result = sshUtil.executeRemote(knownServer, "gluster volume info " + volumeName); if (!result.isSuccess()) { - throw new GlusterRuntimeException("Command [gluster volume info] failed on [" + knownServer - + "] with error: " + result); + throw new GlusterRuntimeException("Command [gluster volume info " + volumeName + "] failed on [" + + knownServer + "] with error: " + result); } return result.getOutput(); } @@ -393,11 +419,7 @@ public class GlusterUtil { } public Volume getVolume(String volumeName, String knownServer) { - List<Volume> volumes = parseVolumeInfo(getVolumeInfo(volumeName, knownServer)); - if (volumes.size() > 0) { - return volumes.get(0); - } - return null; + return parseVolumeInfo(getVolumeInfo(volumeName, knownServer)).get(0); } public List<Volume> getAllVolumes(String knownServer) { @@ -467,12 +489,17 @@ public class GlusterUtil { return volumes; } - public Status addBricks(String volumeName, List<String> bricks, String knownServer) { + public void addBricks(String volumeName, List<String> bricks, String knownServer) { StringBuilder command = new StringBuilder("gluster volume add-brick " + volumeName); for (String brickDir : bricks) { command.append(" " + brickDir); } - return new Status(sshUtil.executeRemote(knownServer, command.toString())); + + ProcessResult result = sshUtil.executeRemote(knownServer, command.toString()); + if(!result.isSuccess()) { + throw new GlusterRuntimeException("Error in volume [" + volumeName + "] add-brick [" + bricks + "]: " + + result); + } } public String getLogLocation(String volumeName, String brickName, String knownServer) { @@ -501,19 +528,18 @@ public class GlusterUtil { } - public TaskResponse migrateBrick(String volumeName, String fromBrick, String toBrick, String operation, - Boolean autoCommit, String knownServer) { - TaskResponse taskResponse = new TaskResponse(); + public String migrateBrickStart(String volumeName, String fromBrick, String toBrick, Boolean autoCommit, + String knownServer) { MigrateDiskTask migrateDiskTask = new MigrateDiskTask(TASK_TYPE.BRICK_MIGRATE, volumeName, fromBrick, toBrick); migrateDiskTask.setOnlineServer(knownServer); migrateDiskTask.setAutoCommit(autoCommit); + TaskInfo taskInfo = migrateDiskTask.start(); if (taskInfo.isSuccess()) { taskResource.addTask(migrateDiskTask); } - taskResponse.setData(taskInfo); - taskResponse.setStatus(new Status(Status.STATUS_CODE_SUCCESS, "")); - return taskResponse; + + return taskInfo.getId(); } public Status removeBricks(String volumeName, List<String> bricks, String knownServer) { @@ -534,7 +560,6 @@ public class GlusterUtil { List<String> disks = new ArrayList<String>(); disks.add("server1:sda"); disks.add("server1:sdb"); - Status status = new GlusterUtil().addBricks("Volume3", disks, "localhost"); - System.out.println(status); + new GlusterUtil().addBricks("Volume3", disks, "localhost"); } } |
