diff options
| author | Shireesh Anjal <shireesh@gluster.com> | 2011-04-07 21:38:32 +0530 |
|---|---|---|
| committer | Shireesh Anjal <shireesh@gluster.com> | 2011-04-11 13:51:51 +0530 |
| commit | 49f755f886ce0b99c7e0f61d71eaee814ce5ae66 (patch) | |
| tree | 4ce870a23a4ca3fda9f007ee6f2aa09310c3897b /src | |
| parent | 9461e6090694b777b9ac9ceae77de5e9e4df1b7d (diff) | |
Story#15 - Volume options
Diffstat (limited to 'src')
22 files changed, 716 insertions, 160 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 12fbd354..807e32a3 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 @@ -129,6 +129,17 @@ public abstract class AbstractClient { }
/**
+ * Submits given Form using PUT method to the given sub-resource and returns the object received as response
+ * @param subResourceName Name of the sub-resource to which the request is to be posted
+ * @param responseClass Class of the object expected as response
+ * @return Object of given class received as response
+ */
+ protected Object putRequest(String subResourceName, Class responseClass) {
+ return resource.path(subResourceName).type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
+ .header("Authorization", authHeader).accept(MediaType.TEXT_XML).put(responseClass);
+ }
+
+ /**
* 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
diff --git a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/DiscoveredServersClient.java b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/DiscoveredServersClient.java index 84074115..6a22bf5f 100644 --- a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/DiscoveredServersClient.java +++ b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/DiscoveredServersClient.java @@ -64,7 +64,7 @@ public class DiscoveredServersClient extends AbstractClient { public static void main(String[] args) { UsersClient usersClient = new UsersClient(); - if (usersClient.authenticate("gluster", "gluster")) { + if (usersClient.authenticate("gluster", "gluster").isSuccess()) { DiscoveredServersClient serverResource = new DiscoveredServersClient("localhost", usersClient.getSecurityToken()); List<String> discoveredServerNames = serverResource.getDiscoveredServerNames(); 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 0f932df0..a8134c7d 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 @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.Map; import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; import com.gluster.storage.management.core.model.Cluster; @@ -41,9 +42,10 @@ import com.gluster.storage.management.core.model.Volume; import com.gluster.storage.management.core.model.Volume.TRANSPORT_TYPE; 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.VolumeOptionInfo; import com.gluster.storage.management.core.response.RunningTaskListResponse; import com.gluster.storage.management.core.response.VolumeListResponse; -import com.gluster.storage.management.client.VolumesClient; +import com.gluster.storage.management.core.response.VolumeOptionInfoListResponse; public class GlusterDataModelManager { // private Server discoveredServer1, discoveredServer2, discoveredServer3, @@ -57,6 +59,7 @@ public class GlusterDataModelManager { private String securityToken; private String serverName; private List<ClusterListener> listeners = new ArrayList<ClusterListener>(); + private List<VolumeOptionInfo> volumeOptionsDefaults; private GlusterDataModelManager() { } @@ -130,12 +133,20 @@ public class GlusterDataModelManager { createDummyLogMessages(); initializeRunningTasks(cluster); - initializeAlerts(cluster); + initializeVolumeOptionsDefaults(); model.addCluster(cluster); } + private void initializeVolumeOptionsDefaults() { + VolumeOptionInfoListResponse response = new VolumesClient(getSecurityToken()).getVolumeOptionsDefaults(); + if(!response.getStatus().isSuccess()) { + throw new GlusterRuntimeException("Error fetching volume option defaults: [" + response.getStatus().getMessage() + "]"); + } + this.volumeOptionsDefaults = response.getOptions(); + } + private void addVolumeOptions() { for (Volume vol : new Volume[] { volume1, volume2, volume3, volume4, volume5 }) { for (int i = 1; i <= 5; i++) { @@ -376,6 +387,13 @@ public class GlusterDataModelManager { listener.volumeChanged(volume, new Event(EVENT_TYPE.VOLUME_STATUS_CHANGED, newStatus)); } } + + public void resetVolumeOptions(Volume volume) { + volume.getOptions().clear(); + for (ClusterListener listener : listeners) { + listener.volumeChanged(volume, new Event(EVENT_TYPE.VOLUME_OPTIONS_RESET, null)); + } + } public void addVolume(Volume volume) { Cluster cluster = model.getCluster(); @@ -385,4 +403,8 @@ public class GlusterDataModelManager { listener.volumeCreated(volume); } } + + public List<VolumeOptionInfo> getVolumeOptionsDefaults() { + return volumeOptionsDefaults; + } } diff --git a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/GlusterServersClient.java b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/GlusterServersClient.java index dfee988c..8ae64016 100644 --- a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/GlusterServersClient.java +++ b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/GlusterServersClient.java @@ -63,7 +63,7 @@ public class GlusterServersClient extends AbstractClient { public static void main(String[] args) { UsersClient usersClient = new UsersClient(); - if (usersClient.authenticate("gluster", "gluster")) { + if (usersClient.authenticate("gluster", "gluster").isSuccess()) { GlusterServersClient serverResource = new GlusterServersClient(usersClient.getSecurityToken()); List<GlusterServer> glusterServers = serverResource.getServers(); diff --git a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/UsersClient.java b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/UsersClient.java index 8d7a52fc..0f2b5f86 100644 --- a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/UsersClient.java +++ b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/UsersClient.java @@ -19,6 +19,7 @@ package com.gluster.storage.management.client; import com.gluster.storage.management.core.model.Status; +import com.sun.jersey.api.client.UniformInterfaceException; import com.sun.jersey.api.representation.Form; import com.sun.jersey.core.util.Base64; @@ -26,6 +27,7 @@ public class UsersClient extends AbstractClient { private static final String RESOURCE_NAME = "users"; private static final String FORM_PARAM_OLD_PASSWORD = "oldpassword"; private static final String FORM_PARAM_NEW_PASSWORD = "newpassword"; + private static final int HTTP_STATUS_UNAUTHORIZED = 401; private String generateSecurityToken(String user, String password) { return new String(Base64.encode(user + ":" + password)); @@ -35,20 +37,26 @@ public class UsersClient extends AbstractClient { super(); } - public boolean authenticate(String user, String password) { + public Status authenticate(String user, String password) { setSecurityToken(generateSecurityToken(user, password)); try { Status authStatus = (Status) fetchSubResource(user, Status.class); - if (authStatus.isSuccess()) { - return true; + if(!authStatus.isSuccess()) { + // authentication failed. clear security token. + setSecurityToken(null); } + return authStatus; } catch (Exception e) { - e.printStackTrace(); + if (e instanceof UniformInterfaceException + && ((UniformInterfaceException) e).getResponse().getStatus() == HTTP_STATUS_UNAUTHORIZED) { + // authentication failed. clear security token. + setSecurityToken(null); + return new Status(Status.STATUS_CODE_FAILURE, "Invalid user id or password!"); + } else { + return new Status(Status.STATUS_CODE_FAILURE, "Exception during authentication: [" + e.getMessage() + + "]"); + } } - - // If we reach here, it means authentication failed. Clear security token and return false. - setSecurityToken(null); - return false; } public boolean changePassword(String user, String oldPassword, String newPassword) { 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 ab462abc..faa4bf71 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 @@ -26,7 +26,6 @@ import com.gluster.storage.management.core.constants.RESTConstants; import com.gluster.storage.management.core.model.Status; import com.gluster.storage.management.core.model.Volume; import com.gluster.storage.management.core.model.VolumeOptionInfo; -import com.gluster.storage.management.core.response.GenericResponse; import com.gluster.storage.management.core.response.VolumeListResponse; import com.gluster.storage.management.core.response.VolumeOptionInfoListResponse; import com.sun.jersey.api.representation.Form; @@ -42,14 +41,8 @@ public class VolumesClient extends AbstractClient { return RESTConstants.RESOURCE_PATH_VOLUMES; } - @SuppressWarnings("unchecked") public Status createVolume(Volume volume) { - GenericResponse<String> createVolumeResponse = (GenericResponse<String>) postObject(GenericResponse.class, volume); - - if (!createVolumeResponse.getStatus().isSuccess()) { - return (Status) createVolumeResponse.getStatus(); - } - return (Status) createVolumeResponse.getStatus(); + return (Status) postObject(Status.class, volume); } private Status performOperation(String volumeName, String operation) { @@ -67,19 +60,36 @@ public class VolumesClient extends AbstractClient { return performOperation(volumeName, RESTConstants.FORM_PARAM_VALUE_STOP); } + public Status setVolumeOption(String volume, String key, String value) { + Form form = new Form(); + form.add(RESTConstants.FORM_PARAM_OPTION_KEY, key); + form.add(RESTConstants.FORM_PARAM_OPTION_VALUE, value); + return (Status)postRequest(volume + "/" + RESTConstants.SUBRESOURCE_OPTIONS, Status.class, form); + } + + public Status resetVolumeOptions(String volume) { + return (Status)putRequest(volume, Status.class); + } + public VolumeListResponse getAllVolumes() { return (VolumeListResponse) fetchResource(VolumeListResponse.class); } + + public Volume getVolume(String volumeName) { + return (Volume) fetchSubResource(volumeName, Volume.class); + } + + public VolumeOptionInfoListResponse getVolumeOptionsDefaults() { + String responseStr = (String) fetchSubResource(RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS, String.class); + System.out.println(responseStr); - public List<VolumeOptionInfo> getVolumeOptionsDefaults() { - VolumeOptionInfoListResponse response = (VolumeOptionInfoListResponse) fetchSubResource( - RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS, VolumeOptionInfoListResponse.class); - return response.getOptions(); + return ((VolumeOptionInfoListResponse) fetchSubResource(RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS, + VolumeOptionInfoListResponse.class)); } public static void main(String[] args) { UsersClient usersClient = new UsersClient(); - if (usersClient.authenticate("gluster", "gluster")) { + if (usersClient.authenticate("gluster", "gluster").isSuccess()) { VolumesClient client = new VolumesClient(usersClient.getSecurityToken()); // List<Disk> disks = new ArrayList<Disk>(); // Disk diskElement = new Disk(); @@ -94,9 +104,11 @@ public class VolumesClient extends AbstractClient { // 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()); - } +// 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()); } } } 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 47697eb9..b5b51cfd 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 @@ -29,8 +29,11 @@ public class RESTConstants { public static final String FORM_PARAM_OPERATION = "operation"; public static final String FORM_PARAM_VALUE_START = "start"; public static final String FORM_PARAM_VALUE_STOP = "stop"; + public static final String FORM_PARAM_OPTION_KEY = "key"; + public static final String FORM_PARAM_OPTION_VALUE = "value"; public static final String PATH_PARAM_VOLUME_NAME = "volumeName"; public static final String SUBRESOURCE_DEFAULT_OPTIONS = "defaultoptions"; + public static final String SUBRESOURCE_OPTIONS = "options"; // Running tasks resource public static final String RESOURCE_PATH_RUNNING_TASKS = "/cluster/runningtasks"; diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Event.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Event.java index 65501a2b..bac86a2e 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Event.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Event.java @@ -24,7 +24,8 @@ public class Event { DISK_REMOVED, NETWORK_INTERFACE_ADDED, NETWORK_INTERFACE_REMOVED, - VOLUME_STATUS_CHANGED + VOLUME_STATUS_CHANGED, + VOLUME_OPTIONS_RESET } private EVENT_TYPE eventType; 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 968611ec..baa3edb9 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 @@ -174,12 +174,13 @@ public class Volume extends Entity { return protocolsStr; } + @XmlTransient public String getAccessControlList() { return options.get(OPTION_AUTH_ALLOW); } public void setAccessControlList(String accessControlList) { - this.accessControlList = accessControlList; + setOption(OPTION_AUTH_ALLOW, accessControlList); } public Map<String, String> getOptions() { diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterUtil.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterUtil.java index 300f79ef..35bba55d 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterUtil.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterUtil.java @@ -22,18 +22,21 @@ package com.gluster.storage.management.core.utils; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import com.gluster.storage.management.core.constants.CoreConstants; import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; -import com.gluster.storage.management.core.model.Disk; import com.gluster.storage.management.core.model.GlusterServer; import com.gluster.storage.management.core.model.GlusterServer.SERVER_STATUS; +import com.gluster.storage.management.core.model.Status; import com.gluster.storage.management.core.model.Volume; import com.gluster.storage.management.core.model.Volume.TRANSPORT_TYPE; import com.gluster.storage.management.core.model.Volume.VOLUME_STATUS; import com.gluster.storage.management.core.model.Volume.VOLUME_TYPE; public class GlusterUtil { + private static final String glusterFSminVersion = "3.1"; private static final String HOSTNAME_PFX = "Hostname:"; private static final String UUID_PFX = "Uuid:"; @@ -140,19 +143,23 @@ public class GlusterUtil { return output; } - public ProcessResult addServer(String serverName) { - return processUtil.executeCommand("gluster", "peer", "probe", serverName); + public Status addServer(String serverName) { + return new Status(processUtil.executeCommand("gluster", "peer", "probe", serverName)); } - public ProcessResult startVolume(String volumeName) { - return processUtil.executeCommand("gluster", "volume", "start", volumeName); + public Status startVolume(String volumeName) { + return new Status(processUtil.executeCommand("gluster", "volume", "start", volumeName)); } - public ProcessResult stopVolume(String volumeName) { - return processUtil.executeCommand("gluster", "--mode=script", "volume", "stop", volumeName); + public Status stopVolume(String volumeName) { + return new Status(processUtil.executeCommand("gluster", "--mode=script", "volume", "stop", volumeName)); } - public ProcessResult createVolume(Volume volume, List<String> bricks) { + public Status resetOptions(String volumeName) { + return new Status(processUtil.executeCommand("gluster", "volume", "reset", volumeName)); + } + + public Status createVolume(Volume volume, List<String> bricks) { int count = 1; // replica or stripe count String volumeType = null; VOLUME_TYPE volType = volume.getVolumeType(); @@ -168,6 +175,18 @@ public class GlusterUtil { TRANSPORT_TYPE transportType = volume.getTransportType(); transportTypeStr = (transportType == TRANSPORT_TYPE.ETHERNET) ? "tcp" : "rdma"; + List<String> command = prepareVolumeCreateCommand(volume, bricks, count, volumeType, transportTypeStr); + ProcessResult result = processUtil.executeCommand(command); + if(!result.isSuccess()) { + // TODO: Perform cleanup on all nodes before returning + return new Status(result); + } + + return createOptions(volume); + } + + private List<String> prepareVolumeCreateCommand(Volume volume, List<String> bricks, int count, String volumeType, + String transportTypeStr) { List<String> command = new ArrayList<String>(); command.add("gluster"); command.add("volume"); @@ -180,22 +199,43 @@ public class GlusterUtil { command.add("transport"); command.add(transportTypeStr); command.addAll(bricks); - return processUtil.executeCommand(command); + return command; } - public ProcessResult setOption(List<String> command) { - return processUtil.executeCommand(command); + private Status createOptions(Volume volume) { + Map<String, String> options = volume.getOptions(); + 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); + if (!status.isSuccess()) { + return status; + } + } + } + return Status.STATUS_SUCCESS; } - public ProcessResult setVolumeAccessControl(Volume volume) { + public Status setOption(String volumeName, String key, String value) { List<String> command = new ArrayList<String>(); command.add("gluster"); command.add("volume"); command.add("set"); - command.add(volume.getName()); - command.add("auth.allow"); - command.add(volume.getAccessControlList()); - return setOption(command); + command.add(volumeName); + command.add(key); + command.add(value); + + return new Status(processUtil.executeCommand(command)); + } + + private String getVolumeInfo(String volumeName) { + ProcessResult result = new ProcessUtil().executeCommand("gluster", "volume", "info", volumeName); + if (!result.isSuccess()) { + throw new GlusterRuntimeException("Command [gluster volume info] failed with error: [" + + result.getExitValue() + "][" + result.getOutput() + "]"); + } + return result.getOutput(); } private String getVolumeInfo() { @@ -206,10 +246,75 @@ public class GlusterUtil { } return result.getOutput(); } + + private boolean readVolumeType(Volume volume, String line) { + String volumeType = extractToken(line, VOLUME_TYPE_PFX); + if (volumeType != null) { + volume.setVolumeType((volumeType.equals("Distribute")) ? VOLUME_TYPE.PLAIN_DISTRIBUTE + : VOLUME_TYPE.DISTRIBUTED_MIRROR); // TODO: for Stripe + return true; + } + return false; + } + + private boolean readVolumeStatus(Volume volume, String line) { + String volumeStatus = extractToken(line, VOLUME_STATUS_PFX); + if (volumeStatus != null) { + volume.setStatus(volumeStatus.equals("Started") ? VOLUME_STATUS.ONLINE : VOLUME_STATUS.OFFLINE); + return true; + } + return false; + } + + private boolean readTransportType(Volume volume, String line) { + String transportType = extractToken(line, VOLUME_TRANSPORT_TYPE_PFX); + if (transportType != null) { + volume.setTransportType(transportType.equals("tcp") ? TRANSPORT_TYPE.ETHERNET + : TRANSPORT_TYPE.INFINIBAND); + return true; + } + return false; + } + + private boolean readBrick(Volume volume, String line) { + if (line.matches("Brick[0-9]+:.*")) { + // line: "Brick1: server1:/export/md0/volume-name" + volume.addDisk(line.split(":")[2].trim().split("/")[2].trim()); + return true; + } + return false; + } + + private boolean readBrickGroup(String line) { + return extractToken(line, VOLUME_BRICKS_GROUP_PFX) != null; + } + + private boolean readOptionReconfigGroup(String line) { + return extractToken(line, VOLUME_OPTIONS_RECONFIG_PFX) != null; + } + + private boolean readOption(Volume volume, String line) { + if (line.matches("^[^:]*:[^:]*$")) { + String[] parts = line.split(":"); + volume.setOption(parts[0].trim(), parts[1].trim()); + return true; + } + return false; + } + + public Volume getVolume(String volumeName) { + List<Volume> volumes = parseVolumeInfo(getVolumeInfo(volumeName)); + if(volumes.size() > 0) { + return volumes.get(0); + } + return null; + } public List<Volume> getAllVolumes() { - String volumeInfoText = getVolumeInfo(); + return parseVolumeInfo(getVolumeInfo()); + } + private List<Volume> parseVolumeInfo(String volumeInfoText) { List<Volume> volumes = new ArrayList<Volume>(); boolean isBricksGroupFound = false; boolean isOptionReconfigFound = false; @@ -230,50 +335,34 @@ public class GlusterUtil { continue; } - String volumeType = extractToken(line, VOLUME_TYPE_PFX); - if (volumeType != null) { - volume.setVolumeType((volumeType == "Distribute") ? VOLUME_TYPE.PLAIN_DISTRIBUTE - : VOLUME_TYPE.DISTRIBUTED_MIRROR); // TODO: for Stripe + if (readVolumeType(volume, line)) continue; - } - - String volumeStatus = extractToken(line, VOLUME_STATUS_PFX); - if (volumeStatus != null) { - volume.setStatus(volumeStatus.equals("Started") ? VOLUME_STATUS.ONLINE : VOLUME_STATUS.OFFLINE); + if (readVolumeStatus(volume, line)) continue; - } - - String transportType = extractToken(line, VOLUME_TRANSPORT_TYPE_PFX); - if (transportType != null) { - volume.setTransportType(transportType.equals("tcp") ? TRANSPORT_TYPE.ETHERNET - : TRANSPORT_TYPE.INFINIBAND); + if(readTransportType(volume, line)) continue; - } - - if (extractToken(line, VOLUME_BRICKS_GROUP_PFX) != null) { + + if (readBrickGroup(line)) { isBricksGroupFound = true; continue; } if (isBricksGroupFound) { - if (line.matches("Brick[0-9]+:.*")) { - // line: "Brick1: server1:/export/md0/volume-name" - volume.addDisk(line.split(":")[2].trim().split("/")[2].trim()); + if (readBrick(volume, line)) { continue; } else { isBricksGroupFound = false; } } - if (extractToken(line, VOLUME_OPTIONS_RECONFIG_PFX) != null) { + if (readOptionReconfigGroup(line)) { isOptionReconfigFound = true; continue; } if (isOptionReconfigFound) { - if (line.matches("^[^:]*:[^:]*$")) { - String[] parts = line.split(":"); - volume.setOption(parts[0].trim(), parts[1].trim()); + if(readOption(volume, line)) { + continue; } else { isOptionReconfigFound = false; } diff --git a/src/com.gluster.storage.management.gui/icons/reset-options.png b/src/com.gluster.storage.management.gui/icons/reset-options.png Binary files differnew file mode 100644 index 00000000..7b93eb05 --- /dev/null +++ b/src/com.gluster.storage.management.gui/icons/reset-options.png diff --git a/src/com.gluster.storage.management.gui/plugin.xml b/src/com.gluster.storage.management.gui/plugin.xml index f5ab3db7..97a60ad1 100644 --- a/src/com.gluster.storage.management.gui/plugin.xml +++ b/src/com.gluster.storage.management.gui/plugin.xml @@ -223,6 +223,12 @@ </command> <command categoryId="com.gluster.storage.management.gui.category" + description="Reset all options of a Volume" + id="com.gluster.storage.management.gui.commands.ResetVolumeOptions" + name="Reset Options"> + </command> + <command + categoryId="com.gluster.storage.management.gui.category" description="Rebalance Volume" id="com.gluster.storage.management.gui.commands.RebalanceVolume" name="Rebalance Volume"> @@ -320,6 +326,11 @@ id="com.gluster.storage.management.gui.KeyConfig" name="Gluster"> </scheme> + <key + commandId="com.gluster.storage.management.gui.commands.ResetVolumeOptions" + schemeId="com.gluster.storage.management.gui.KeyConfig" + sequence="CTRL+SHIFT+O"> + </key> </extension> <extension id="product" @@ -504,6 +515,22 @@ </action> <action allowLabelUpdate="false" + class="com.gluster.storage.management.gui.actions.ResetVolumeOptionsAction" + definitionId="com.gluster.storage.management.gui.commands.ResetVolumeOptions" + icon="icons/reset-options.png" + id="com.gluster.storage.management.gui.actions.ResetVolumeOptionsAction" + label="Reset &Options" + menubarPath="com.gluster.storage.management.gui.menu.volume/volume" + mode="FORCE_TEXT" + pulldown="false" + retarget="false" + state="false" + style="push" + toolbarPath="Normal" + tooltip="Reset all options of the volume"> + </action> + <action + allowLabelUpdate="false" class="com.gluster.storage.management.gui.actions.RebalanceVolumeAction" definitionId="com.gluster.storage.management.gui.commands.RebalanceVolume" icon="icons/volume-rebalance.png" diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/ResetVolumeOptionsAction.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/ResetVolumeOptionsAction.java new file mode 100644 index 00000000..9f77fb27 --- /dev/null +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/ResetVolumeOptionsAction.java @@ -0,0 +1,64 @@ +package com.gluster.storage.management.gui.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.IWorkbenchWindowActionDelegate; + +import com.gluster.storage.management.client.GlusterDataModelManager; +import com.gluster.storage.management.client.VolumesClient; +import com.gluster.storage.management.core.model.Status; +import com.gluster.storage.management.core.model.Volume; +import com.gluster.storage.management.core.model.Volume.VOLUME_STATUS; + +public class ResetVolumeOptionsAction extends AbstractActionDelegate { + private Volume volume; + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + + @Override + public void dispose() { + // TODO Auto-generated method stub + + } + + @Override + protected void performAction(IAction action) { + final String actionDesc = action.getDescription(); + + boolean confirmed = showConfirmDialog(actionDesc, + "Are you sure you want to reset all options of the volume [" + volume.getName() + "] ?"); + if (!confirmed) { + return; + } + + final Status status = resetVolumeOptions(); + if (status.isSuccess()) { + showInfoDialog(actionDesc, "Volume options for [" + volume.getName() + "] reset successfully!"); + modelManager.updateVolumeStatus(volume, VOLUME_STATUS.OFFLINE); + } else { + showErrorDialog(actionDesc, "Volume options for [" + volume.getName() + "] could not be reset! Error: [" + status + + "]"); + } + } + + private Status resetVolumeOptions() { + return new VolumesClient(modelManager.getSecurityToken()).resetVolumeOptions(volume.getName()); + } + + /* + * (non-Javadoc) + * + * @see + * com.gluster.storage.management.gui.actions.AbstractActionDelegate#selectionChanged(org.eclipse.jface.action.IAction + * , org.eclipse.jface.viewers.ISelection) + */ + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + + if (selectedEntity instanceof Volume) { + volume = (Volume) selectedEntity; + action.setEnabled(volume.getOptions().size() > 0); + } + } +} diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/login/LoginDialog.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/login/LoginDialog.java index a1be243d..b955f0e3 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/login/LoginDialog.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/login/LoginDialog.java @@ -193,16 +193,15 @@ public class LoginDialog extends Dialog { String password = connectionDetails.getPassword(); UsersClient usersClient = new UsersClient(); - if (usersClient.authenticate(user, password)) { + if (usersClient.authenticate(user, password).isSuccess()) { try { GlusterDataModelManager.getInstance().initializeModel(usersClient.getSecurityToken()); super.okPressed(); - } catch (GlusterRuntimeException e) { + } catch (Exception e) { setReturnCode(RETURN_CODE_ERROR); MessageDialog.openError(getShell(), "Initialization Error", e.getMessage()); close(); } - } else { MessageDialog.openError(getShell(), "Authentication Failed", "Invalid User ID or password"); } diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/utils/GUIHelper.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/utils/GUIHelper.java index ccd5d8ec..9f5fdfb7 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/utils/GUIHelper.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/utils/GUIHelper.java @@ -36,7 +36,6 @@ import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; @@ -57,6 +56,7 @@ import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IViewReference; import org.eclipse.ui.IWorkbenchSite; +import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.forms.events.ExpansionAdapter; import org.eclipse.ui.forms.events.ExpansionEvent; @@ -64,9 +64,9 @@ import org.eclipse.ui.forms.widgets.ColumnLayout; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.forms.widgets.Section; +import org.eclipse.ui.progress.IProgressConstants; -import com.gluster.storage.management.core.model.Entity; -import com.gluster.storage.management.core.model.EntityGroup; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; import com.gluster.storage.management.gui.IImageKeys; import com.gluster.storage.management.gui.views.NavigationView; @@ -360,4 +360,14 @@ public class GUIHelper { } return null; } + + public void showProgressView() { + try { + PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage() + .showView(IProgressConstants.PROGRESS_VIEW_ID); + } catch (PartInitException e) { + e.printStackTrace(); + throw new GlusterRuntimeException("Could not open the progress view!", e); + } + } } diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/AbstractDisksPage.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/AbstractDisksPage.java index e00fe3f8..593f7ba1 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/AbstractDisksPage.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/AbstractDisksPage.java @@ -276,13 +276,7 @@ public abstract class AbstractDisksPage extends Composite implements IEntityList @Override public void linkActivated(HyperlinkEvent e) { updateStatus(DISK_STATUS.INITIALIZING, true); - - try { - site.getWorkbenchWindow().getActivePage().showView(IProgressConstants.PROGRESS_VIEW_ID); - } catch (PartInitException e1) { - e1.printStackTrace(); - throw new GlusterRuntimeException("Could not open the progress view!", e1); - } + guiHelper.showProgressView(); new InitializeDiskJob(disk).schedule(); } diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionKeyEditingSupport.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionKeyEditingSupport.java new file mode 100644 index 00000000..56f25997 --- /dev/null +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionKeyEditingSupport.java @@ -0,0 +1,86 @@ +/** + * + */ +package com.gluster.storage.management.gui.views.details; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ColumnViewer; +import org.eclipse.jface.viewers.ComboBoxCellEditor; +import org.eclipse.jface.viewers.EditingSupport; +import org.eclipse.swt.widgets.Composite; + +import com.gluster.storage.management.client.GlusterDataModelManager; +import com.gluster.storage.management.core.model.Volume; +import com.gluster.storage.management.core.model.VolumeOptionInfo; + +/** + * Editing support for the "value" column in volume options table viewer. + */ +public class OptionKeyEditingSupport extends EditingSupport { + private CellEditor cellEditor; + private Volume volume; + private List<VolumeOptionInfo> defaults = GlusterDataModelManager.getInstance().getVolumeOptionsDefaults(); + private String[] allowedKeys; + + public OptionKeyEditingSupport(ColumnViewer viewer, Volume volume) { + super(viewer); + this.volume = volume; + allowedKeys = getAllowedKeys(); + this.cellEditor = new ComboBoxCellEditor((Composite) viewer.getControl(), allowedKeys); + } + + /** + * @return array of option keys that are not already set on the volume + */ + private String[] getAllowedKeys() { + ArrayList<String> keys = new ArrayList<String>(); + Map<String, String> volumeOptions = volume.getOptions(); + for(VolumeOptionInfo optionInfo : defaults) { + String optionName = optionInfo.getName(); + if(!volumeOptions.containsKey(optionName)) { + keys.add(optionName); + } + } + return keys.toArray(new String[0]); + } + + @SuppressWarnings("unchecked") + @Override + protected void setValue(final Object element, final Object value) { + Integer newValue = (Integer)value; + String newKey = allowedKeys[newValue]; + + if (((Entry<String, String>)element).getKey().equals(newKey)) { + // selected value is same as old one. + return; + } + + // value has changed. set volume option at back-end and update model accordingly + volume.getOptions().remove(""); + volume.setOption(newKey, ""); + getViewer().refresh(); + } + + @SuppressWarnings("unchecked") + @Override + protected Object getValue(Object element) { + return cellEditor.getValue(); + } + + @Override + protected CellEditor getCellEditor(Object element) { + return cellEditor; + } + + @SuppressWarnings("unchecked") + @Override + protected boolean canEdit(Object element) { + Entry<String, String> entry = (Entry<String, String>)element; + return (entry.getKey().isEmpty() || entry.getValue().isEmpty()); + } +} diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionValueEditingSupport.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionValueEditingSupport.java new file mode 100644 index 00000000..f975f1ff --- /dev/null +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionValueEditingSupport.java @@ -0,0 +1,103 @@ +/** + * + */ +package com.gluster.storage.management.gui.views.details; + +import java.util.List; +import java.util.Map.Entry; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ColumnViewer; +import org.eclipse.jface.viewers.EditingSupport; +import org.eclipse.jface.viewers.TextCellEditor; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; + +import com.gluster.storage.management.client.GlusterDataModelManager; +import com.gluster.storage.management.client.VolumesClient; +import com.gluster.storage.management.core.model.Status; +import com.gluster.storage.management.core.model.Volume; +import com.gluster.storage.management.core.model.VolumeOptionInfo; + +/** + * Editing support for the "value" column in volume options table viewer. + */ +public class OptionValueEditingSupport extends EditingSupport { + private CellEditor cellEditor; + private Volume volume; + private List<VolumeOptionInfo> defaults = GlusterDataModelManager.getInstance().getVolumeOptionsDefaults(); + + public OptionValueEditingSupport(ColumnViewer viewer, Volume volume) { + super(viewer); + this.volume = volume; + this.cellEditor = new TextCellEditor((Composite) viewer.getControl()); + } + + @SuppressWarnings("unchecked") + @Override + protected void setValue(final Object element, final Object value) { + final Entry<String, String> entry = (Entry<String, String>) element; + + // It is not allowed to change value to empty string + if(((String)value).isEmpty()) { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Set Volume Option", + "Option value can't be empty! Please enter a valid value."); + cellEditor.setFocus(); + return; + } + + if (entry.getValue().equals(value)) { + // value is same as that present in the model. return without doing anything. + return; + } + + // value has changed. set volume option at back-end and update model accordingly + BusyIndicator.showWhile(Display.getDefault(), new Runnable() { + + @Override + public void run() { + VolumesClient client = new VolumesClient(GlusterDataModelManager.getInstance().getSecurityToken()); + Status status = client.setVolumeOption(volume.getName(), entry.getKey(), (String) value); + if (status.isSuccess()) { + volume.setOption(entry.getKey(), (String) value); + } else { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Set Volume Option", + status.getMessage()); + } + getViewer().update(entry, null); + } + }); + } + + /** + * @param key Key whose default value is to be fetched + * @return Default value of the volume option with given key + */ + private String getDefaultValue(String key) { + for(VolumeOptionInfo optionInfo : defaults) { + if(optionInfo.getName().equals(key)) { + return optionInfo.getDefaultValue(); + } + } + return ""; + } + + @SuppressWarnings("unchecked") + @Override + protected Object getValue(Object element) { + Entry<String, String> entry = (Entry<String, String>) element; + return entry.getValue().isEmpty() ? getDefaultValue(entry.getKey()) : entry.getValue(); + } + + @Override + protected CellEditor getCellEditor(Object element) { + return cellEditor; + } + + @Override + protected boolean canEdit(Object element) { + return true; + } +} diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeOptionsPage.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeOptionsPage.java index baa14a59..349c638a 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeOptionsPage.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeOptionsPage.java @@ -18,19 +18,36 @@ *******************************************************************************/ package com.gluster.storage.management.gui.views.details; +import java.util.Map.Entry; + +import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ColumnLayoutData; +import org.eclipse.jface.viewers.ColumnViewer; import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.EditingSupport; +import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Table; @@ -38,6 +55,9 @@ import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.forms.widgets.FormToolkit; +import com.gluster.storage.management.client.GlusterDataModelManager; +import com.gluster.storage.management.client.VolumesClient; +import com.gluster.storage.management.core.model.Status; import com.gluster.storage.management.core.model.Volume; import com.gluster.storage.management.gui.VolumeOptionsTableLabelProvider; import com.gluster.storage.management.gui.utils.GUIHelper; @@ -47,6 +67,7 @@ public class VolumeOptionsPage extends Composite { private final FormToolkit toolkit = new FormToolkit(Display.getCurrent()); private TableViewer tableViewer; private GUIHelper guiHelper = GUIHelper.getInstance(); + private Volume volume; public enum OPTIONS_TABLE_COLUMN_INDICES { OPTION_KEY, OPTION_VALUE @@ -54,29 +75,55 @@ public class VolumeOptionsPage extends Composite { private static final String[] OPTIONS_TABLE_COLUMN_NAMES = new String[] { "Option Key", "Option Value" }; - public VolumeOptionsPage(Composite parent, int style) { + public VolumeOptionsPage(final Composite parent, int style, Volume volume) { super(parent, style); - addDisposeListener(new DisposeListener() { - public void widgetDisposed(DisposeEvent e) { - toolkit.dispose(); - } - }); - + + this.volume = volume; toolkit.adapt(this); toolkit.paintBordersFor(this); setupPageLayout(); Text filterText = guiHelper.createFilterText(toolkit, this); setupDiskTableViewer(filterText); - } - - public VolumeOptionsPage(final Composite parent, int style, Volume volume) { - this(parent, style); + + createAddButton(); - tableViewer.setInput(volume.getOptions().entrySet().toArray()); + tableViewer.setInput(volume.getOptions().entrySet()); parent.layout(); // Important - this actually paints the table + registerListeners(parent); + } + + private void createAddButton() { + Button addButton = toolkit.createButton(this, "&Add", SWT.FLAT); + addButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + volume.setOption("", ""); + + tableViewer.refresh(); + tableViewer.setSelection(new StructuredSelection(getEntry(""))); + } + + private Entry getEntry(String key) { + for(Entry entry : volume.getOptions().entrySet()) { + if(entry.getKey().equals(key)) { + return entry; + } + } + return null; + } + }); + } + + private void registerListeners(final Composite parent) { + addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + toolkit.dispose(); + } + }); + /** * Ideally not required. However the table viewer is not getting laid out properly on performing * "maximize + restore" So this is a hack to make sure that the table is laid out again on re-size of the window @@ -88,7 +135,6 @@ public class VolumeOptionsPage extends Composite { parent.layout(); } }); - } private void setupPageLayout() { @@ -98,26 +144,69 @@ public class VolumeOptionsPage extends Composite { setLayout(layout); } - private void setupDiskTable(Composite parent, Table table) { + private void setupDiskTable(Composite parent) { + Table table = tableViewer.getTable(); table.setHeaderVisible(true); table.setLinesVisible(false); - TableColumnLayout tableColumnLayout = guiHelper.createTableColumnLayout(table, OPTIONS_TABLE_COLUMN_NAMES); + TableColumnLayout tableColumnLayout = createTableColumnLayout(); parent.setLayout(tableColumnLayout); setColumnProperties(table, OPTIONS_TABLE_COLUMN_INDICES.OPTION_KEY, SWT.CENTER, 100); setColumnProperties(table, OPTIONS_TABLE_COLUMN_INDICES.OPTION_VALUE, SWT.CENTER, 100); } + + private TableColumnLayout createTableColumnLayout() { + TableColumnLayout tableColumnLayout = new TableColumnLayout(); + ColumnLayoutData defaultColumnLayoutData = new ColumnWeightData(100); - private TableViewer createDiskTableViewer(Composite parent) { - TableViewer tableViewer = CheckboxTableViewer.newCheckList(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.MULTI); + tableColumnLayout.setColumnData(createKeyColumn(), defaultColumnLayoutData); + tableColumnLayout.setColumnData(createValueColumn(), defaultColumnLayoutData); + + return tableColumnLayout; + } + + private TableColumn createValueColumn() { + TableViewerColumn valueColumn = new TableViewerColumn(tableViewer, SWT.NONE); + valueColumn.getColumn() + .setText(OPTIONS_TABLE_COLUMN_NAMES[OPTIONS_TABLE_COLUMN_INDICES.OPTION_VALUE.ordinal()]); + valueColumn.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + return ((Entry<String, String>) element).getValue(); + } + }); + + // User can edit value of a volume option + valueColumn.setEditingSupport(new OptionValueEditingSupport(valueColumn.getViewer(), volume)); + + return valueColumn.getColumn(); + } + + private TableColumn createKeyColumn() { + TableViewerColumn keyColumn = new TableViewerColumn(tableViewer, SWT.NONE); + keyColumn.getColumn().setText(OPTIONS_TABLE_COLUMN_NAMES[OPTIONS_TABLE_COLUMN_INDICES.OPTION_KEY.ordinal()]); + keyColumn.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + return ((Entry<String, String>) element).getKey(); + } + }); + + // Editing support required when adding new key + keyColumn.setEditingSupport(new OptionKeyEditingSupport(keyColumn.getViewer(), volume)); + + return keyColumn.getColumn(); + } + + private void createDiskTableViewer(Composite parent) { + //tableViewer = CheckboxTableViewer.newCheckList(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.MULTI); + tableViewer = new TableViewer(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.SINGLE); // TableViewer tableViewer = new TableViewer(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.MULTI); tableViewer.setLabelProvider(new VolumeOptionsTableLabelProvider()); tableViewer.setContentProvider(new ArrayContentProvider()); - setupDiskTable(parent, tableViewer.getTable()); - - return tableViewer; + setupDiskTable(parent); } private Composite createTableViewerComposite() { @@ -129,7 +218,7 @@ public class VolumeOptionsPage extends Composite { private void setupDiskTableViewer(final Text filterText) { Composite tableViewerComposite = createTableViewerComposite(); - tableViewer = createDiskTableViewer(tableViewerComposite); + createDiskTableViewer(tableViewerComposite); // Create a case insensitive filter for the table viewer using the filter text field guiHelper.createFilter(tableViewer, filterText, false); } diff --git a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/GlusterServersResource.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/GlusterServersResource.java index e8adceb7..42f7760e 100644 --- a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/GlusterServersResource.java +++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/GlusterServersResource.java @@ -94,12 +94,10 @@ public class GlusterServersResource extends AbstractServersResource { @POST @Produces(MediaType.TEXT_XML) public GlusterServerResponse addServer(@FormParam("serverName") String serverName) { - ProcessResult result = glusterUtil.addServer(serverName); + Status status = glusterUtil.addServer(serverName); - if (!result.isSuccess()) { - Status failure = new Status(Status.STATUS_CODE_FAILURE, "Add server [" + serverName + "] failed: [" + result.getExitValue() - + "][" + result.getOutput() + "]"); - return new GlusterServerResponse(failure, null); + if (!status.isSuccess()) { + return new GlusterServerResponse(status, null); } return new GlusterServerResponse(Status.STATUS_SUCCESS, getGlusterServer(serverName)); } 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 22ef2462..49fb1e0d 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 @@ -26,6 +26,7 @@ import static com.gluster.storage.management.core.constants.RESTConstants.FORM_P import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_VOLUME_NAME; import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_VOLUMES; import static com.gluster.storage.management.core.constants.RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS; +import static com.gluster.storage.management.core.constants.RESTConstants.SUBRESOURCE_OPTIONS; import java.util.ArrayList; import java.util.List; @@ -40,15 +41,12 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; -import org.springframework.beans.factory.annotation.Autowired; - +import com.gluster.storage.management.core.constants.RESTConstants; 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.response.VolumeListResponse; import com.gluster.storage.management.core.response.VolumeOptionInfoListResponse; import com.gluster.storage.management.core.utils.GlusterUtil; -import com.gluster.storage.management.core.utils.ProcessResult; import com.gluster.storage.management.server.constants.VolumeOptionsDefaults; import com.gluster.storage.management.server.utils.ServerUtil; import com.sun.jersey.api.core.InjectParam; @@ -57,9 +55,9 @@ import com.sun.jersey.spi.resource.Singleton; @Singleton @Path(RESOURCE_PATH_VOLUMES) public class VolumesResource { - private static final String SCRIPT_NAME = "CreateVolumeExportDirectory.py"; + private static final String SCRIPT_NAME = "preVolumeCreate.py"; - @Autowired + @InjectParam private static ServerUtil serverUtil; private final GlusterUtil glusterUtil = new GlusterUtil(); @@ -80,30 +78,34 @@ public class VolumesResource { @POST @Consumes(MediaType.TEXT_XML) @Produces(MediaType.TEXT_XML) - public GenericResponse<String> createVolume(Volume volume) { + public Status createVolume(Volume volume) { //Create the directories for the volume List<String> bricks = new ArrayList<String>(); for(String disk : volume.getDisks()) { - String brickNotation = getBrickNotation(volume, disk); + String brickNotation = prepareBrick(volume, disk); if (brickNotation != null) { bricks.add(brickNotation); } else { - return new GenericResponse<String>(Status.STATUS_FAILURE, "Disk is not mounted properly. Pls mount the disk."); + int failedIndex = volume.getDisks().indexOf(disk); + // TODO: Perform cleanup on all previously prepared bricks + // i.e. those disks with index < failedIndex + + return new Status(Status.STATUS_CODE_FAILURE, "Error while preparing disk [" + disk + "] for volume [" + + volume.getName() + "]"); } } - ProcessResult response = glusterUtil.createVolume(volume, bricks); - if (!response.isSuccess()) { - return new GenericResponse<String>(Status.STATUS_FAILURE, "Volume creation failed: [" - + response.getOutput() + "]"); - } - - response = glusterUtil.setVolumeAccessControl(volume); - - return new GenericResponse<String>(Status.STATUS_SUCCESS, response.getOutput()); + return glusterUtil.createVolume(volume, bricks); } + @GET + @Path("{" + PATH_PARAM_VOLUME_NAME + "}") + @Produces(MediaType.TEXT_XML) + public Volume getVolume(@PathParam(PATH_PARAM_VOLUME_NAME) String volumeName) { + return glusterUtil.getVolume(volumeName); + } + @PUT @Path("{" + PATH_PARAM_VOLUME_NAME + "}") @Produces(MediaType.TEXT_XML) @@ -111,40 +113,57 @@ public class VolumesResource { @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName) { if (operation.equals(FORM_PARAM_VALUE_START)) { - return new Status(glusterUtil.startVolume(volumeName)); + return glusterUtil.startVolume(volumeName); } if (operation.equals(FORM_PARAM_VALUE_STOP)) { - return new Status(glusterUtil.stopVolume(volumeName)); + return glusterUtil.stopVolume(volumeName); } return new Status(Status.STATUS_CODE_FAILURE, "Invalid operation code [" + operation + "]"); } + @POST + @Path("{" + PATH_PARAM_VOLUME_NAME + " }/" + SUBRESOURCE_OPTIONS) + @Produces(MediaType.TEXT_XML) + public Status setOption(@PathParam(PATH_PARAM_VOLUME_NAME) String volumeName, + @FormParam(RESTConstants.FORM_PARAM_OPTION_KEY) String key, + @FormParam(RESTConstants.FORM_PARAM_OPTION_VALUE) String value) { + return glusterUtil.setOption(volumeName, key, value); + } + + @PUT + @Path("{" + PATH_PARAM_VOLUME_NAME + " }/" + SUBRESOURCE_OPTIONS) + @Produces(MediaType.TEXT_XML) + public Status resetOptions(@PathParam(PATH_PARAM_VOLUME_NAME) String volumeName) { + return glusterUtil.resetOptions(volumeName); + } + @GET @Path(SUBRESOURCE_DEFAULT_OPTIONS) @Produces(MediaType.TEXT_XML) public VolumeOptionInfoListResponse getDefaultOptions() { - // TODO: Fetch all volume options with their default values from - // GlusterFS + // 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 String getBrickNotation(Volume vol, String disk) { + private String prepareBrick(Volume vol, String disk) { String serverName = disk.split(":")[0]; - String exportDirectory = disk.split(":")[1]; - Status result = serverUtil.executeOnServer(true, serverName, "python " + SCRIPT_NAME +" " + exportDirectory + " " + vol.getName()); + String diskName = disk.split(":")[1]; + Status result = (Status)serverUtil.executeOnServer(true, serverName, SCRIPT_NAME + " " + vol.getName() + " " + diskName, Status.class); - if(result.getCode() == 0) { - String dirName = "/export/" + disk + "/" + vol.getName() ; - return serverName + ":" + dirName; + if(result.isSuccess()) { + return result.getMessage(); } else { return null; } - } - public static void main(String args[]) { - // Disk disk = null; - serverUtil.executeOnServer(true, "localhost", "CreateVolumeExportDirectory.py md0 testvol"); + public static void main(String[] args) { + VolumesResource vr = new VolumesResource(); + VolumeListResponse response = vr.getAllVolumes(); + for (Volume volume : response.getVolumes()) { + System.out.println("\nName:" + volume.getName() + "\nType: " + volume.getVolumeTypeStr() + "\nStatus: " + + volume.getStatusStr()); + } } -}
\ No newline at end of file +} diff --git a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/ServerUtil.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/ServerUtil.java index b2264ffa..645b7991 100644 --- a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/ServerUtil.java +++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/ServerUtil.java @@ -22,7 +22,6 @@ package com.gluster.storage.management.server.utils; import java.io.BufferedReader; import java.io.ByteArrayInputStream; -import java.io.FileReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetAddress; @@ -32,7 +31,7 @@ import java.util.List; import javax.servlet.ServletContext; import javax.xml.bind.JAXBContext; -import javax.xml.bind.Marshaller; +import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import org.springframework.beans.factory.annotation.Autowired; @@ -42,9 +41,7 @@ import com.gluster.storage.management.core.constants.CoreConstants; import com.gluster.storage.management.core.model.Status; import com.gluster.storage.management.core.utils.ProcessResult; import com.gluster.storage.management.core.utils.ProcessUtil; -import com.sun.jersey.spi.resource.Singleton; -@Singleton @Component public class ServerUtil { @Autowired @@ -73,9 +70,11 @@ public class ServerUtil { * @param runInForeground * @param serverName * @param commandWithArgs + * @param expectedClass Class of the object expected from script execution * @return Response from remote execution of the command */ - public Status executeOnServer(boolean runInForeground, String serverName, String commandWithArgs) { + public Object executeOnServer(boolean runInForeground, String serverName, String commandWithArgs, Class expectedClass) { + StringBuffer output = new StringBuffer(); try { InetAddress address = InetAddress.getByName(serverName); Socket connection = new Socket(address, 50000); @@ -86,7 +85,6 @@ public class ServerUtil { writer.println(commandWithArgs); writer.println(); // empty line means end of request - StringBuffer output = new StringBuffer(); String line; while (!(line = reader.readLine()).trim().isEmpty()) { output.append(line + CoreConstants.NEWLINE); @@ -94,23 +92,45 @@ public class ServerUtil { connection.close(); System.out.println("The ouput string is : " + output.toString()); - // create JAXB context and instantiate marshaller - JAXBContext context = JAXBContext.newInstance(Status.class); - Unmarshaller um = context.createUnmarshaller(); - Status result = (Status) um.unmarshal(new ByteArrayInputStream(output.toString().getBytes())); + return unmarshal(expectedClass, output.toString(), expectedClass != Status.class); + } catch(Exception e) { + // any other exception means unexpected error. return status with error from exception. + return new Status(Status.STATUS_CODE_FAILURE, "Error during remote execution: [" + e.getMessage() + "]"); + } + } - return result; + /** + * Unmarshals given input string into object of given class + * + * @param expectedClass + * Class whose object is expected + * @param input + * Input string + * @param tryStatusOnFailure + * If true, and if the unmarshalling fails for given class, another unmarshalling will be attempted with + * class Status. If that also fails, a status object with exception message is created and returned. + * @return Object of given expected class, or a status object in case first unmarshalling fails. + */ + private Object unmarshal(Class expectedClass, String input, boolean tryStatusOnFailure) { + try { + // create JAXB context and instantiate marshaller + JAXBContext context = JAXBContext.newInstance(expectedClass); + Unmarshaller um = context.createUnmarshaller(); + return um.unmarshal(new ByteArrayInputStream(input.getBytes())); + } catch (JAXBException e) { + if(tryStatusOnFailure) { + // unmarshalling failed. try to unmarshal a Status object + return unmarshal(Status.class, input, false); + } - // return new ProcessResult( 0, output.toString()); - } catch (Exception e) { - e.printStackTrace(); + return new Status(Status.STATUS_CODE_FAILURE, "Error during unmarshalling string [" + input + + "] for class [" + expectedClass.getName() + ": [" + e.getMessage() + "]"); } - return null; } public static void main(String args[]) { // CreateVolumeExportDirectory.py md0 testvol - System.out.println(new ServerUtil().executeOnServer(true, "localhost", "python CreateVolumeExportDirectory.py md0 testvol").getMessage()); + System.out.println(new ServerUtil().executeOnServer(true, "localhost", "python CreateVolumeExportDirectory.py md0 testvol", Status.class)); } }
\ No newline at end of file |
