From 49f755f886ce0b99c7e0f61d71eaee814ce5ae66 Mon Sep 17 00:00:00 2001 From: Shireesh Anjal Date: Thu, 7 Apr 2011 21:38:32 +0530 Subject: Story#15 - Volume options --- .../storage/management/client/AbstractClient.java | 11 ++ .../management/client/DiscoveredServersClient.java | 2 +- .../management/client/GlusterDataModelManager.java | 26 +++- .../management/client/GlusterServersClient.java | 2 +- .../storage/management/client/UsersClient.java | 24 ++- .../storage/management/client/VolumesClient.java | 44 ++++-- .../management/core/constants/RESTConstants.java | 3 + .../storage/management/core/model/Event.java | 3 +- .../storage/management/core/model/Volume.java | 3 +- .../storage/management/core/utils/GlusterUtil.java | 173 ++++++++++++++++----- .../icons/reset-options.png | Bin 0 -> 916 bytes src/com.gluster.storage.management.gui/plugin.xml | 27 ++++ .../gui/actions/ResetVolumeOptionsAction.java | 64 ++++++++ .../storage/management/gui/login/LoginDialog.java | 5 +- .../storage/management/gui/utils/GUIHelper.java | 16 +- .../gui/views/details/AbstractDisksPage.java | 8 +- .../gui/views/details/OptionKeyEditingSupport.java | 86 ++++++++++ .../views/details/OptionValueEditingSupport.java | 103 ++++++++++++ .../gui/views/details/VolumeOptionsPage.java | 131 +++++++++++++--- .../server/resources/GlusterServersResource.java | 8 +- .../server/resources/VolumesResource.java | 85 ++++++---- .../management/server/utils/ServerUtil.java | 52 +++++-- 22 files changed, 716 insertions(+), 160 deletions(-) create mode 100644 src/com.gluster.storage.management.gui/icons/reset-options.png create mode 100644 src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/ResetVolumeOptionsAction.java create mode 100644 src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionKeyEditingSupport.java create mode 100644 src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionValueEditingSupport.java (limited to 'src') 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 @@ -128,6 +128,17 @@ public abstract class AbstractClient { .header("Authorization", authHeader).accept(MediaType.TEXT_XML).put(responseClass, form); } + /** + * 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 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 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 listeners = new ArrayList(); + private List 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 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 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 createVolumeResponse = (GenericResponse) 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 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 disks = new ArrayList(); // 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 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 bricks) { + public Status resetOptions(String volumeName) { + return new Status(processUtil.executeCommand("gluster", "volume", "reset", volumeName)); + } + + public Status createVolume(Volume volume, List 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 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 prepareVolumeCreateCommand(Volume volume, List bricks, int count, String volumeType, + String transportTypeStr) { List command = new ArrayList(); 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 command) { - return processUtil.executeCommand(command); + private Status createOptions(Volume volume) { + Map options = volume.getOptions(); + if (options != null) { + for (Entry 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 command = new ArrayList(); 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 volumes = parseVolumeInfo(getVolumeInfo(volumeName)); + if(volumes.size() > 0) { + return volumes.get(0); + } + return null; + } public List getAllVolumes() { - String volumeInfoText = getVolumeInfo(); + return parseVolumeInfo(getVolumeInfo()); + } + private List parseVolumeInfo(String volumeInfoText) { List volumes = new ArrayList(); 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 new file mode 100644 index 00000000..7b93eb05 Binary files /dev/null and b/src/com.gluster.storage.management.gui/icons/reset-options.png differ 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 @@ -221,6 +221,12 @@ id="com.gluster.storage.management.gui.commands.EditVolume" name="Edit Volume"> + + + + + + 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 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 keys = new ArrayList(); + Map 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)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 entry = (Entry)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 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 entry = (Entry) 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 entry = (Entry) 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) 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) 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 createVolume(Volume volume) { + public Status createVolume(Volume volume) { //Create the directories for the volume List bricks = new ArrayList(); 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(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(Status.STATUS_FAILURE, "Volume creation failed: [" - + response.getOutput() + "]"); - } - - response = glusterUtil.setVolumeAccessControl(volume); - - return new GenericResponse(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 -- cgit From 793358f8e1068c944a15d6e16b90dcb4cc1c2950 Mon Sep 17 00:00:00 2001 From: Shireesh Anjal Date: Tue, 12 Apr 2011 12:02:26 +0530 Subject: Story#15 - Volume options --- .../storage/management/client/VolumesClient.java | 5 +- .../META-INF/MANIFEST.MF | 3 +- .../gui/actions/ResetVolumeOptionsAction.java | 2 +- .../gui/views/details/VolumeOptionsPage.java | 99 ++++------------------ .../server/resources/VolumesResource.java | 7 -- 5 files changed, 19 insertions(+), 97 deletions(-) (limited to 'src') 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 faa4bf71..1d9ddbff 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 @@ -68,7 +68,7 @@ public class VolumesClient extends AbstractClient { } public Status resetVolumeOptions(String volume) { - return (Status)putRequest(volume, Status.class); + return (Status)putRequest(volume + "/" + RESTConstants.SUBRESOURCE_OPTIONS, Status.class); } public VolumeListResponse getAllVolumes() { @@ -80,9 +80,6 @@ public class VolumesClient extends AbstractClient { } public VolumeOptionInfoListResponse getVolumeOptionsDefaults() { - String responseStr = (String) fetchSubResource(RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS, String.class); - System.out.println(responseStr); - return ((VolumeOptionInfoListResponse) fetchSubResource(RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS, VolumeOptionInfoListResponse.class)); } diff --git a/src/com.gluster.storage.management.gui/META-INF/MANIFEST.MF b/src/com.gluster.storage.management.gui/META-INF/MANIFEST.MF index 13fb07dc..2e59c854 100644 --- a/src/com.gluster.storage.management.gui/META-INF/MANIFEST.MF +++ b/src/com.gluster.storage.management.gui/META-INF/MANIFEST.MF @@ -21,8 +21,7 @@ Require-Bundle: org.eclipse.ui;bundle-version="3.6.1", org.eclipse.birt.chart.device.swt;bundle-version="2.6.1", com.ibm.icu;bundle-version="4.2.1", com.richclientgui.rcptoolbox;bundle-version="1.0.5", - org.eclipse.core.resources, - org.eclipse.equinox.common.source;bundle-version="3.6.0" + org.eclipse.core.resources Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . 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 index 9f77fb27..7fd77ea8 100644 --- 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 @@ -34,7 +34,7 @@ public class ResetVolumeOptionsAction extends AbstractActionDelegate { final Status status = resetVolumeOptions(); if (status.isSuccess()) { showInfoDialog(actionDesc, "Volume options for [" + volume.getName() + "] reset successfully!"); - modelManager.updateVolumeStatus(volume, VOLUME_STATUS.OFFLINE); + modelManager.resetVolumeOptions(volume); } else { showErrorDialog(actionDesc, "Volume options for [" + volume.getName() + "] could not be reset! Error: [" + status + "]"); 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 2ab83789..5a1a41e9 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 @@ -20,30 +20,21 @@ 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; @@ -56,8 +47,9 @@ 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.DefaultClusterListener; +import com.gluster.storage.management.core.model.Event; +import com.gluster.storage.management.core.model.Event.EVENT_TYPE; import com.gluster.storage.management.core.model.Volume; import com.gluster.storage.management.gui.VolumeOptionsTableLabelProvider; import com.gluster.storage.management.gui.utils.GUIHelper; @@ -135,6 +127,19 @@ public class VolumeOptionsPage extends Composite { parent.layout(); } }); + + GlusterDataModelManager.getInstance().addClusterListener(new DefaultClusterListener() { + @Override + public void volumeChanged(Volume volume, Event event) { + super.volumeChanged(volume, event); + if(event.getEventType() == EVENT_TYPE.VOLUME_OPTIONS_RESET) { + if(!tableViewer.getControl().isDisposed()) { + tableViewer.refresh(); + } + } + } + }); + } private void setupPageLayout() { @@ -183,76 +188,6 @@ public class VolumeOptionsPage extends Composite { return valueColumn.getColumn(); } - private class OptionValueEditingSupport extends EditingSupport { - private CellEditor cellEditor; - - public OptionValueEditingSupport(ColumnViewer viewer) { - super(viewer); - cellEditor = new TextCellEditor((Composite) viewer.getControl()); - } - - @Override - protected void setValue(final Object element, final Object value) { - final Entry entry = (Entry)element; - if(entry.getValue().equals(value)) { - // value is same as that present in the model. return without doing anything. - return; - } - - final Cursor oldCursor = getViewer().getControl().getCursor(); - //getViewer().getControl().setCursor(new Cursor(Display.getDefault(), SWT.CURSOR_WAIT)); - // value has changed. set volume option at back-end and update model accordingly - BusyIndicator.showWhile(getDisplay(), 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(getShell(), "Set Volume Option", status.getMessage()); - } - getViewer().update(entry, null); - //getViewer().refresh(); - //getViewer().getControl().setCursor(oldCursor); - } - }); - } - - @Override - protected Object getValue(Object element) { - return ((Entry) element).getValue(); - } - - @Override - protected CellEditor getCellEditor(Object element) { - return cellEditor; - } - - @Override - protected boolean canEdit(Object element) { - return true; - } - } - - 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) element).getValue(); - } - }); - - // User can edit value of a volume option - valueColumn.setEditingSupport(new OptionValueEditingSupport(valueColumn.getViewer())); - - 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()]); @@ -270,9 +205,7 @@ public class VolumeOptionsPage extends Composite { } 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()); 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 06406434..4fe06cc9 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 @@ -105,13 +105,6 @@ public class VolumesResource { return glusterUtil.getVolume(volumeName); } - @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) -- cgit From 491325d013f1b1b5ac033eac4ff8b1d565e669e8 Mon Sep 17 00:00:00 2001 From: Selvam Date: Fri, 8 Apr 2011 11:39:32 +0530 Subject: Volume property page - access control update --- .../storage/management/core/model/Volume.java | 2 +- .../storage/management/core/utils/GlusterUtil.java | 3 +- .../management/core/utils/ValidationUtil.java | 68 ++++++++++++++++++++++ .../management/gui/views/VolumeSummaryView.java | 46 +++++++++++---- 4 files changed, 107 insertions(+), 12 deletions(-) create mode 100644 src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/ValidationUtil.java (limited to 'src') 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 baa3edb9..daa96cd4 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 @@ -49,7 +49,7 @@ public class Volume extends Entity { GLUSTERFS, NFS }; - private static final String OPTION_AUTH_ALLOW = "auth.allow:"; + private static final String OPTION_AUTH_ALLOW = "auth.allow"; private static final String[] VOLUME_TYPE_STR = new String[] { "Plain Distribute", "Distributed Mirror", "Distributed Stripe" }; 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 d1533f25..5ed83810 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 @@ -49,6 +49,7 @@ public class GlusterUtil { private static final String VOLUME_TRANSPORT_TYPE_PFX = "Transport-type:"; private static final String VOLUME_BRICKS_GROUP_PFX = "Bricks"; private static final String VOLUME_OPTIONS_RECONFIG_PFX = "Options Reconfigured"; + private static final String VOLUME_OPTION_AUTH_ALLOW = "auth.allow:"; private static final ProcessUtil processUtil = new ProcessUtil(); @@ -324,7 +325,7 @@ public class GlusterUtil { String volumeName = extractToken(line, VOLUME_NAME_PFX); if (volumeName != null) { if (volume != null) { - // add the previously read volume to volume list + volumes.add(volume); } diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/ValidationUtil.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/ValidationUtil.java new file mode 100644 index 00000000..76dc748e --- /dev/null +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/ValidationUtil.java @@ -0,0 +1,68 @@ +package com.gluster.storage.management.core.utils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ValidationUtil { + + // Access control may contains IP with wild card(*), hostname and/or multiple ip/hostnames + public static boolean isValidAccessControl(String ac) { + String access[] = ac.split(","); + boolean isValidAccessControl = true; + for (int i = 0; i < access.length && isValidAccessControl; i++) { + isValidAccessControl = (isValidIpWithWC(access[i]) || isValidHostName(access[i])); + } + return isValidAccessControl; + } + + public static boolean isValidIpWithWC(String ip) { + String ipAddress[] = ip.split("\\."); + boolean isValid = true; + + if (ip.equals("0.0.0.0") || ip.equals("255.255.255.255")) { // Invalidate the special ip's + isValid = false; + } + + for (int i = 0; i < ipAddress.length && isValid; i++) { + if (ipAddress[i].equals("*")) { + isValid = (i == ipAddress.length - 1) ? isValid : false; + } else { + isValid = isValidIpQuad(ipAddress[i]); + } + } + return isValid; + } + + public static boolean isValidIp(String ip) { + String ipAddress[] = ip.split("\\."); + boolean isValid = true; + + if (ip.equals("0.0.0.0") || ip.equals("255.255.255.255")) { // Invalidate the special ip's + isValid = false; + } + + for (int i = 0; i < ipAddress.length && isValid; i++) { + isValid = isValidIpQuad(ipAddress[i]); + } + return isValid; + } + + public static boolean isValidIpQuad(String ipQuad) { + Pattern pattern = Pattern.compile("([01]?\\d\\d?|2[0-4]\\d|25[0-5])"); + return pattern.matcher(ipQuad).matches(); + } + + public static boolean isValidHostName(String hostName) { + Pattern pattern = Pattern + .compile("^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\\-]*[A-Za-z0-9])$"); + return pattern.matcher(hostName).matches(); + } + + public static void main(String[] argv) { + String ip = "0.0.0.0"; + System.out.println("Is valid ip (" + ip + ")? " + isValidIp(ip)); + String hostName = "Selvam-sd.com"; + System.out.println(isValidHostName(hostName)); + } + +} diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumeSummaryView.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumeSummaryView.java index 798c2a40..291c494a 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumeSummaryView.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumeSummaryView.java @@ -1,5 +1,9 @@ package com.gluster.storage.management.gui.views; +import java.util.Map; + +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CLabel; import org.eclipse.swt.layout.FillLayout; @@ -17,13 +21,16 @@ import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.part.ViewPart; import com.gluster.storage.management.client.GlusterDataModelManager; +import com.gluster.storage.management.client.VolumesClient; import com.gluster.storage.management.core.model.DefaultClusterListener; import com.gluster.storage.management.core.model.Event; import com.gluster.storage.management.core.model.Event.EVENT_TYPE; +import com.gluster.storage.management.core.model.Status; import com.gluster.storage.management.core.model.Volume; import com.gluster.storage.management.core.model.Volume.NAS_PROTOCOL; import com.gluster.storage.management.core.model.Volume.VOLUME_TYPE; import com.gluster.storage.management.core.utils.NumberUtil; +import com.gluster.storage.management.core.utils.ValidationUtil; import com.gluster.storage.management.gui.IImageKeys; import com.gluster.storage.management.gui.toolbar.GlusterToolbarManager; import com.gluster.storage.management.gui.utils.GUIHelper; @@ -44,12 +51,12 @@ public class VolumeSummaryView extends ViewPart { } createSections(parent); - + // Refresh the navigation tree whenever there is a change to the data model volumeChangedListener = new DefaultClusterListener() { @Override public void volumeChanged(Volume volume, Event event) { - if(event.getEventType() == EVENT_TYPE.VOLUME_STATUS_CHANGED) { + if (event.getEventType() == EVENT_TYPE.VOLUME_STATUS_CHANGED) { updateVolumeStatusLabel(); new GlusterToolbarManager(getSite().getWorkbenchWindow()).updateToolbar(volume); } @@ -57,8 +64,10 @@ public class VolumeSummaryView extends ViewPart { }; GlusterDataModelManager.getInstance().addClusterListener(volumeChangedListener); } - - /* (non-Javadoc) + + /* + * (non-Javadoc) + * * @see org.eclipse.ui.part.WorkbenchPart#dispose() */ @Override @@ -131,12 +140,28 @@ public class VolumeSummaryView extends ViewPart { final Hyperlink changeLink = toolkit.createHyperlink(section, "change", SWT.NONE); changeLink.addHyperlinkListener(new HyperlinkAdapter() { + @SuppressWarnings("static-access") private void finishEdit() { - // TODO: Update value to back-end - // TODO: Validation of entered text - volume.setAccessControlList(accessControlText.getText()); - accessControlText.setEnabled(false); - changeLink.setText("change"); + + if (new ValidationUtil().isValidAccessControl(accessControlText.getText())) { + Status status = (new VolumesClient(GlusterDataModelManager.getInstance().getSecurityToken())) + .setVolumeOption(volume.getName(), "auth.allow", accessControlText.getText()); + if (status.isSuccess()) { + volume.setAccessControlList(accessControlText.getText()); + accessControlText.setEnabled(false); + changeLink.setText("change"); + MessageDialog.openInformation(Display.getDefault().getActiveShell(), "Access control", + status.getMessage()); + } else { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Access control", + status.getMessage()); + } + + } else { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Access control", + "Invalid IP / Host name "); + } + } private void startEdit() { @@ -167,7 +192,8 @@ public class VolumeSummaryView extends ViewPart { final Button nfsCheckBox = createCheckbox(nasProtocolsComposite, "NFS", volume.getNASProtocols().contains(NAS_PROTOCOL.NFS)); - createChangeLinkForNASProtocol(section, nfsCheckBox); + toolkit.createLabel(section, "", SWT.NONE); // dummy + // createChangeLinkForNASProtocol(section, nfsCheckBox); } private Button createCheckbox(Composite parent, String label, boolean selected) { -- cgit From 7cb60df3b751c43b30093aac10a170b9df26d44c Mon Sep 17 00:00:00 2001 From: Selvam Date: Fri, 8 Apr 2011 11:39:32 +0530 Subject: Volume property page - access control update --- .../storage/management/core/model/Volume.java | 2 +- .../storage/management/core/utils/GlusterUtil.java | 2 +- .../management/core/utils/ValidationUtil.java | 68 ++++++++++++++++++++++ .../management/gui/views/VolumeSummaryView.java | 48 +++++++++++---- 4 files changed, 108 insertions(+), 12 deletions(-) create mode 100644 src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/ValidationUtil.java (limited to 'src') 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 baa3edb9..daa96cd4 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 @@ -49,7 +49,7 @@ public class Volume extends Entity { GLUSTERFS, NFS }; - private static final String OPTION_AUTH_ALLOW = "auth.allow:"; + private static final String OPTION_AUTH_ALLOW = "auth.allow"; private static final String[] VOLUME_TYPE_STR = new String[] { "Plain Distribute", "Distributed Mirror", "Distributed Stripe" }; 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 d1533f25..fb7261ed 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 @@ -324,7 +324,7 @@ public class GlusterUtil { String volumeName = extractToken(line, VOLUME_NAME_PFX); if (volumeName != null) { if (volume != null) { - // add the previously read volume to volume list + volumes.add(volume); } diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/ValidationUtil.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/ValidationUtil.java new file mode 100644 index 00000000..76dc748e --- /dev/null +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/ValidationUtil.java @@ -0,0 +1,68 @@ +package com.gluster.storage.management.core.utils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ValidationUtil { + + // Access control may contains IP with wild card(*), hostname and/or multiple ip/hostnames + public static boolean isValidAccessControl(String ac) { + String access[] = ac.split(","); + boolean isValidAccessControl = true; + for (int i = 0; i < access.length && isValidAccessControl; i++) { + isValidAccessControl = (isValidIpWithWC(access[i]) || isValidHostName(access[i])); + } + return isValidAccessControl; + } + + public static boolean isValidIpWithWC(String ip) { + String ipAddress[] = ip.split("\\."); + boolean isValid = true; + + if (ip.equals("0.0.0.0") || ip.equals("255.255.255.255")) { // Invalidate the special ip's + isValid = false; + } + + for (int i = 0; i < ipAddress.length && isValid; i++) { + if (ipAddress[i].equals("*")) { + isValid = (i == ipAddress.length - 1) ? isValid : false; + } else { + isValid = isValidIpQuad(ipAddress[i]); + } + } + return isValid; + } + + public static boolean isValidIp(String ip) { + String ipAddress[] = ip.split("\\."); + boolean isValid = true; + + if (ip.equals("0.0.0.0") || ip.equals("255.255.255.255")) { // Invalidate the special ip's + isValid = false; + } + + for (int i = 0; i < ipAddress.length && isValid; i++) { + isValid = isValidIpQuad(ipAddress[i]); + } + return isValid; + } + + public static boolean isValidIpQuad(String ipQuad) { + Pattern pattern = Pattern.compile("([01]?\\d\\d?|2[0-4]\\d|25[0-5])"); + return pattern.matcher(ipQuad).matches(); + } + + public static boolean isValidHostName(String hostName) { + Pattern pattern = Pattern + .compile("^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\\-]*[A-Za-z0-9])$"); + return pattern.matcher(hostName).matches(); + } + + public static void main(String[] argv) { + String ip = "0.0.0.0"; + System.out.println("Is valid ip (" + ip + ")? " + isValidIp(ip)); + String hostName = "Selvam-sd.com"; + System.out.println(isValidHostName(hostName)); + } + +} diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumeSummaryView.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumeSummaryView.java index 798c2a40..fe583a67 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumeSummaryView.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumeSummaryView.java @@ -1,5 +1,9 @@ package com.gluster.storage.management.gui.views; +import java.util.Map; + +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CLabel; import org.eclipse.swt.layout.FillLayout; @@ -17,13 +21,16 @@ import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.part.ViewPart; import com.gluster.storage.management.client.GlusterDataModelManager; +import com.gluster.storage.management.client.VolumesClient; import com.gluster.storage.management.core.model.DefaultClusterListener; import com.gluster.storage.management.core.model.Event; import com.gluster.storage.management.core.model.Event.EVENT_TYPE; +import com.gluster.storage.management.core.model.Status; import com.gluster.storage.management.core.model.Volume; import com.gluster.storage.management.core.model.Volume.NAS_PROTOCOL; import com.gluster.storage.management.core.model.Volume.VOLUME_TYPE; import com.gluster.storage.management.core.utils.NumberUtil; +import com.gluster.storage.management.core.utils.ValidationUtil; import com.gluster.storage.management.gui.IImageKeys; import com.gluster.storage.management.gui.toolbar.GlusterToolbarManager; import com.gluster.storage.management.gui.utils.GUIHelper; @@ -36,6 +43,8 @@ public class VolumeSummaryView extends ViewPart { private Volume volume; private CLabel lblStatusValue; private DefaultClusterListener volumeChangedListener; + + private static final String VOLUME_OPTION_AUTH_ALLOW = "auth.allow"; @Override public void createPartControl(Composite parent) { @@ -44,12 +53,12 @@ public class VolumeSummaryView extends ViewPart { } createSections(parent); - + // Refresh the navigation tree whenever there is a change to the data model volumeChangedListener = new DefaultClusterListener() { @Override public void volumeChanged(Volume volume, Event event) { - if(event.getEventType() == EVENT_TYPE.VOLUME_STATUS_CHANGED) { + if (event.getEventType() == EVENT_TYPE.VOLUME_STATUS_CHANGED) { updateVolumeStatusLabel(); new GlusterToolbarManager(getSite().getWorkbenchWindow()).updateToolbar(volume); } @@ -57,8 +66,10 @@ public class VolumeSummaryView extends ViewPart { }; GlusterDataModelManager.getInstance().addClusterListener(volumeChangedListener); } - - /* (non-Javadoc) + + /* + * (non-Javadoc) + * * @see org.eclipse.ui.part.WorkbenchPart#dispose() */ @Override @@ -131,12 +142,28 @@ public class VolumeSummaryView extends ViewPart { final Hyperlink changeLink = toolkit.createHyperlink(section, "change", SWT.NONE); changeLink.addHyperlinkListener(new HyperlinkAdapter() { + @SuppressWarnings("static-access") private void finishEdit() { - // TODO: Update value to back-end - // TODO: Validation of entered text - volume.setAccessControlList(accessControlText.getText()); - accessControlText.setEnabled(false); - changeLink.setText("change"); + + if (new ValidationUtil().isValidAccessControl(accessControlText.getText())) { + Status status = (new VolumesClient(GlusterDataModelManager.getInstance().getSecurityToken())) + .setVolumeOption(volume.getName(), VOLUME_OPTION_AUTH_ALLOW, accessControlText.getText()); + if (status.isSuccess()) { + volume.setAccessControlList(accessControlText.getText()); + accessControlText.setEnabled(false); + changeLink.setText("change"); + MessageDialog.openInformation(Display.getDefault().getActiveShell(), "Access control", + status.getMessage()); + } else { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Access control", + status.getMessage()); + } + + } else { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Access control", + "Invalid IP / Host name "); + } + } private void startEdit() { @@ -167,7 +194,8 @@ public class VolumeSummaryView extends ViewPart { final Button nfsCheckBox = createCheckbox(nasProtocolsComposite, "NFS", volume.getNASProtocols().contains(NAS_PROTOCOL.NFS)); - createChangeLinkForNASProtocol(section, nfsCheckBox); + toolkit.createLabel(section, "", SWT.NONE); // dummy + // createChangeLinkForNASProtocol(section, nfsCheckBox); } private Button createCheckbox(Composite parent, String label, boolean selected) { -- cgit