From f519a04fd95c7dba349a9a1b4275ef55e8e55990 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/GlusterServersClient.java | 2 +- .../storage/management/client/UsersClient.java | 24 ++- .../storage/management/client/VolumesClient.java | 38 +++-- .../management/core/constants/RESTConstants.java | 3 + .../storage/management/core/model/Volume.java | 3 +- .../storage/management/core/utils/GlusterUtil.java | 173 ++++++++++++++++----- .../storage/management/gui/login/LoginDialog.java | 2 +- .../storage/management/gui/utils/GUIHelper.java | 16 +- .../gui/views/details/AbstractDisksPage.java | 8 +- .../gui/views/details/VolumeOptionsPage.java | 124 ++++++++++++++- .../server/resources/GlusterServersResource.java | 8 +- .../server/resources/VolumesResource.java | 85 ++++++---- .../management/server/utils/ServerUtil.java | 52 +++++-- 15 files changed, 413 insertions(+), 138 deletions(-) (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/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..a479e1f7 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,11 +60,30 @@ 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 List getVolumeOptionsDefaults() { + String responseStr = (String)fetchSubResource( + RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS, String.class); + System.out.println(responseStr); + VolumeOptionInfoListResponse response = (VolumeOptionInfoListResponse) fetchSubResource( RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS, VolumeOptionInfoListResponse.class); return response.getOptions(); @@ -79,7 +91,7 @@ public class VolumesClient extends AbstractClient { 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 +106,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/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/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..30406e27 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,7 +193,7 @@ 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(); 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/VolumeOptionsPage.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeOptionsPage.java index baa14a59..cd425dc2 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,16 +18,28 @@ *******************************************************************************/ 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.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.graphics.Cursor; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; @@ -38,6 +50,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 +62,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 @@ -72,6 +88,7 @@ public class VolumeOptionsPage extends Composite { public VolumeOptionsPage(final Composite parent, int style, Volume volume) { this(parent, style); + this.volume = volume; tableViewer.setInput(volume.getOptions().entrySet().toArray()); @@ -98,26 +115,117 @@ 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); + + tableColumnLayout.setColumnData(createKeyColumn(), defaultColumnLayoutData); + tableColumnLayout.setColumnData(createValueColumn(), defaultColumnLayoutData); + + return tableColumnLayout; + } + + 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 TableViewer createDiskTableViewer(Composite parent) { - TableViewer tableViewer = CheckboxTableViewer.newCheckList(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.MULTI); + 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(); + } + }); + return keyColumn.getColumn(); + } + + private void createDiskTableViewer(Composite parent) { + tableViewer = CheckboxTableViewer.newCheckList(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.MULTI); // 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 +237,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 c9f1c41af770a75160dd5f7dca572f6dedc8afec Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 8 Apr 2011 17:37:13 +0530 Subject: Enhanced create volume directory functionalities. Added DiskUtils.py file Bug fixed in XmlHandler.py file Moved stripEmptyLines function into Common.py file. --- .../src/common/Common.py | 9 + .../src/common/DiskUtils.py | 192 +++++++++++++++++++++ .../src/nodes/CreateVolumeExportDirectory.py | 65 ++++--- .../src/nodes/XmlHandler.py | 2 +- 4 files changed, 244 insertions(+), 24 deletions(-) create mode 100644 src/com.gluster.storage.management.server.scripts/src/common/DiskUtils.py mode change 100644 => 100755 src/com.gluster.storage.management.server.scripts/src/nodes/CreateVolumeExportDirectory.py (limited to 'src') diff --git a/src/com.gluster.storage.management.server.scripts/src/common/Common.py b/src/com.gluster.storage.management.server.scripts/src/common/Common.py index 60f200fe..99c2f440 100644 --- a/src/com.gluster.storage.management.server.scripts/src/common/Common.py +++ b/src/com.gluster.storage.management.server.scripts/src/common/Common.py @@ -32,3 +32,12 @@ def log(priority, message=None): else: syslog.syslog(logPriority, logMessage) return + + +def stripEmptyLines(content): + ret = "" + for line in content.split("\n"): + if line.strip() != "": + ret += line + return ret + diff --git a/src/com.gluster.storage.management.server.scripts/src/common/DiskUtils.py b/src/com.gluster.storage.management.server.scripts/src/common/DiskUtils.py new file mode 100644 index 00000000..bde12500 --- /dev/null +++ b/src/com.gluster.storage.management.server.scripts/src/common/DiskUtils.py @@ -0,0 +1,192 @@ +# Copyright (c) 2010 Gluster, Inc. +# This file is part of Gluster Storage Platform. +# +# Gluster Storage Platform is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 of +# the License, or (at your option) any later version. +# +# Gluster Storage Platform is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see +# . + +import os +import glob +import dbus + +import Globals +from Utils import * + +ONE_MB_SIZE = 1048576 + + +def _stripDev(device): + if isString(device) and device.startswith("/dev/"): + return device[5:] + return device + + +def _addDev(deviceName): + if isString(deviceName) and not deviceName.startswith("/dev/"): + return "/dev/" + deviceName + return deviceName + + +def getDeviceName(device): + if type(device) == type([]): + nameList = [] + for d in device: + nameList.append(_stripDev(d)) + return nameList + return _stripDev(device) + + +def getDevice(deviceName): + if isString(deviceName): + return _addDev(deviceName) + if type(deviceName) == type([]): + nameList = [] + for d in deviceName: + nameList.append(_addDev(d)) + return nameList + return _addDev(deviceName) + + +def getDiskPartitionByUuid(uuid): + uuidFile = "/dev/disk/by-uuid/%s" % uuid + if os.path.exists(uuidFile): + return getDeviceName(os.path.realpath(uuidFile)) + return None + + +def getUuidByDiskPartition(device): + for uuidFile in glob.glob("/dev/disk/by-uuid/*"): + if os.path.realpath(uuidFile) == device: + return os.path.basename(uuidFile) + return None + + +def getDiskPartitionUuid(partition): + log("WARNING: getDiskPartitionUuid() is deprecated by getUuidByDiskPartition()") + return getUuidByDiskPartition(partition) + + +def getDiskPartitionByLabel(label): + ## TODO: Finding needs to be enhanced + labelFile = "/dev/disk/by-label/%s" % label + if os.path.exists(labelFile): + return getDeviceName(os.path.realpath(labelFile)) + return None + + +def getDeviceByLabel(label): + log("WARNING: getDeviceByLabel() is deprecated by getDiskPartitionByLabel()") + return getDiskPartitionByLabel(label) + + +def getDiskPartitionLabel(device): + rv = runCommandFG(["sudo", "e2label", device], stdout=True) + if rv["Status"] == 0: + return rv["Stdout"].strip() + return False + + +def getRootPartition(fsTabFile=Globals.FSTAB_FILE): + fsTabEntryList = readFsTab(fsTabFile) + for fsTabEntry in fsTabEntryList: + if fsTabEntry["MountPoint"] == "/": + if fsTabEntry["Device"].startswith("UUID="): + return getDiskPartitionByUuid(fsTabEntry["Device"].split("UUID=")[-1]) + if fsTabEntry["Device"].startswith("LABEL="): + return getDiskPartitionByLabel(fsTabEntry["Device"].split("LABEL=")[-1]) + return getDeviceName(fsTabEntry["Device"]) + return None + + +def getOsDisk(): + log("WARNING: getOsDisk() is deprecated by getRootPartition()") + return getRootPartition() + + +def getDiskList(diskDeviceList=None): + diskDeviceList = getDevice(diskDeviceList) + if isString(diskDeviceList): + diskDeviceList = [diskDeviceList] + + dbusSystemBus = dbus.SystemBus() + halObj = dbusSystemBus.get_object("org.freedesktop.Hal", + "/org/freedesktop/Hal/Manager") + halManager = dbus.Interface(halObj, "org.freedesktop.Hal.Manager") + storageUdiList = halManager.FindDeviceByCapability("storage") + + diskList = [] + for udi in storageUdiList: + halDeviceObj = dbusSystemBus.get_object("org.freedesktop.Hal", udi) + halDevice = dbus.Interface(halDeviceObj, + "org.freedesktop.Hal.Device") + if halDevice.GetProperty("storage.drive_type") == "cdrom" or \ + halDevice.GetProperty("block.is_volume"): + continue + + disk = {} + disk["Device"] = str(halDevice.GetProperty('block.device')) + if diskDeviceList and disk["Device"] not in diskDeviceList: + continue + disk["Description"] = str(halDevice.GetProperty('storage.vendor')) + " " + str(halDevice.GetProperty('storage.model')) + if halDevice.GetProperty('storage.removable'): + disk["Size"] = long(halDevice.GetProperty('storage.removable.media_size')) + else: + disk["Size"] = long(halDevice.GetProperty('storage.size')) + disk["Interface"] = str(halDevice.GetProperty('storage.bus')) + disk["DriveType"] = str(halDevice.GetProperty('storage.drive_type')) + partitionList = [] + partitionUdiList = halManager.FindDeviceStringMatch("info.parent", udi) + for partitionUdi in partitionUdiList: + partitionHalDeviceObj = dbusSystemBus.get_object("org.freedesktop.Hal", + partitionUdi) + partitionHalDevice = dbus.Interface(partitionHalDeviceObj, + "org.freedesktop.Hal.Device") + if not partitionHalDevice.GetProperty("block.is_volume"): + continue + partition = {} + partition["Device"] = str(partitionHalDevice.GetProperty('block.device')) + partition["Uuid"] = str(partitionHalDevice.GetProperty('volume.uuid')) + partition["Size"] = long(partitionHalDevice.GetProperty('volume.size')) + partition["Fstype"] = str(partitionHalDevice.GetProperty('volume.fstype')) + partition["Fsversion"] = str(partitionHalDevice.GetProperty('volume.fsversion')) + partition["Label"] = str(partitionHalDevice.GetProperty('volume.label')) + partition["Used"] = 0L + if partitionHalDevice.GetProperty("volume.is_mounted"): + rv = runCommandFG(["df", str(partitionHalDevice.GetProperty('volume.mount_point'))], stdout=True) + if rv["Status"] == 0: + try: + partition["Used"] = long(rv["Stdout"].split("\n")[1].split()[2]) + except IndexError: + pass + except ValueError: + pass + partitionList.append(partition) + disk["Partitions"] = partitionList + diskList.append(disk) + return diskList + + +def getMountPointByUuid(partitionUuid): + # check uuid in etc/fstab + try: + fstabEntries = open(Globals.FSTAB_FILE).readlines() + except IOError: + fstabEntries = [] + found = False + for entry in fstabEntries: + entry = entry.strip() + if not entry: + continue + if entry.split()[0] == "UUID=" + partitionUuid: + return entry.split()[1] + return None diff --git a/src/com.gluster.storage.management.server.scripts/src/nodes/CreateVolumeExportDirectory.py b/src/com.gluster.storage.management.server.scripts/src/nodes/CreateVolumeExportDirectory.py old mode 100644 new mode 100755 index 611d9695..59dc3c19 --- a/src/com.gluster.storage.management.server.scripts/src/nodes/CreateVolumeExportDirectory.py +++ b/src/com.gluster.storage.management.server.scripts/src/nodes/CreateVolumeExportDirectory.py @@ -16,36 +16,55 @@ # along with this program. If not, see # . import os +import sys +import syslog from XmlHandler import ResponseXml -from optparse import OptionParser +import DiskUtils import Utils +import Common -def stripEmptyLines(content): - ret = "" - for line in content.split("\n"): - if line.strip() != "": - ret += line - return ret - -def createDirectory(disk, volumename): - dirname = "/export" - if not os.path.isdir(dirname) or not os.path.isdir(disk): - rs = ResponseXml() - rs.appendTagRoute("code", 1) - rs.appendTagRoute("message", "Disk is not mounted properly") +def createDirectory(disk, volumeName): + + # Retrieving disk uuid + diskUuid = DiskUtils.getUuidByDiskPartition(DiskUtils.getDevice(disk)) + + rs = ResponseXml() + if not diskUuid: + Common.log(syslog.LOG_ERR, "failed to find disk:%s uuid" % disk) + rs.appendTagRoute("status.code", "-1") + rs.appendTagRoute("status.message", "Error: Unable to find disk uuid") + return rs.toprettyxml() + + # Retrieving disk mount point using disk uuid + diskMountPoint = DiskUtils.getMountPointByUuid(diskUuid) + if not os.path.exists(diskMountPoint): + Common.log(syslog.LOG_ERR, "failed to retrieve disk:%s mount point" % disk) + rs.appendTagRoute("status.code", "-1") + rs.appendTagRoute("status.message", "Error: Failed to retrieve disk details") return rs.toprettyxml() - - - if not os.path.isdir(dirname + "/" + disk + "/" + volumename + "/"): - command = "mkdir " + volumename; + + # creating volume directory under disk mount point + volumeDirectory = "%s/%s" % (diskMountPoint, volumeName) + if not os.path.exists(volumeDirectory): + command = ["sudo", "mkdir", volumeDirectory] rv = Utils.runCommandFG(command, stdout=True, root=True) - message = stripEmptyLines(rv["Stdout"]) + message = Common.stripEmptyLines(rv["Stdout"]) if rv["Stderr"]: - message += "Error: [" + stripEmptyLines(rv["Stderr"]) + "]" - rs = ResponseXml() + error = Common.stripEmptyLines(rv["Stderr"]) + message += "Error: [%s]" % (error) + Common.log(syslog.LOG_ERR, "failed to create volume directory %s, %s" % (volumeDirectory, error) rs.appendTagRoute("status.code", rv["Status"]) rs.appendTagRoute("status.message", message) return rs.toprettyxml() -def main(disk, volumename): - return createDirectory(disk, volumename) \ No newline at end of file +def main(): + if len(sys.argv) != 3: + print >> sys.stderr, "usage: %s " % sys.argv[0] + sys.exit(-1) + + disk = sys.argv[1] + volumeName = sys.argv[2] + print createDirectory(disk, volumeName) + sys.exit(0) + +main() diff --git a/src/com.gluster.storage.management.server.scripts/src/nodes/XmlHandler.py b/src/com.gluster.storage.management.server.scripts/src/nodes/XmlHandler.py index d5a1fe19..72164ffb 100644 --- a/src/com.gluster.storage.management.server.scripts/src/nodes/XmlHandler.py +++ b/src/com.gluster.storage.management.server.scripts/src/nodes/XmlHandler.py @@ -343,4 +343,4 @@ def test(): networkInterfaces.appendChild(networkTag) print rs.toprettyxml() -test() +#test() -- cgit From a74db3659cb4102cb654ae83f5bd710b751acd46 Mon Sep 17 00:00:00 2001 From: Dhandapani Date: Mon, 11 Apr 2011 10:40:26 +0530 Subject: Create Reexport directory on remote nodes Clean up the created directories in case of failure --- .../storage/management/core/utils/GlusterUtil.java | 6 +- .../META-INF/MANIFEST.MF | 3 +- .../management/gui/dialogs/CreateVolumePage1.java | 11 ++- .../server/resources/VolumesResource.java | 85 ++++++++++++++-------- 4 files changed, 67 insertions(+), 38 deletions(-) (limited to 'src') 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 35bba55d..38de196a 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 @@ -178,11 +178,9 @@ public class GlusterUtil { 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); + return new Status(result); } private List prepareVolumeCreateCommand(Volume volume, List bricks, int count, String volumeType, @@ -202,7 +200,7 @@ public class GlusterUtil { return command; } - private Status createOptions(Volume volume) { + public Status createOptions(Volume volume) { Map options = volume.getOptions(); if (options != null) { for (Entry option : options.entrySet()) { 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 2e59c854..13fb07dc 100644 --- a/src/com.gluster.storage.management.gui/META-INF/MANIFEST.MF +++ b/src/com.gluster.storage.management.gui/META-INF/MANIFEST.MF @@ -21,7 +21,8 @@ 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.core.resources, + org.eclipse.equinox.common.source;bundle-version="3.6.0" 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/dialogs/CreateVolumePage1.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/CreateVolumePage1.java index b09bbb44..e880df36 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/CreateVolumePage1.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/CreateVolumePage1.java @@ -234,6 +234,7 @@ public class CreateVolumePage1 extends WizardPage { new Label(container, SWT.NONE); btnNfs = new Button(container, SWT.CHECK); + btnNfs.setEnabled(false); btnNfs.setSelection(true); btnNfs.setText("NFS"); @@ -251,7 +252,7 @@ public class CreateVolumePage1 extends WizardPage { new Label(container, SWT.NONE); Label lblAccessControlInfo = new Label(container, SWT.TOP); lblAccessControlInfo.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1)); - lblAccessControlInfo.setText("(Comma separated list of IP addresses)"); + lblAccessControlInfo.setText("(Comma separated list of IP addresses/Hostname)"); Label lblStartVolume = new Label(container, SWT.NONE); lblStartVolume.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); @@ -270,9 +271,11 @@ public class CreateVolumePage1 extends WizardPage { volume.setTransportType(TRANSPORT_TYPE.ETHERNET); Set nasProtocols = new HashSet(); nasProtocols.add(NAS_PROTOCOL.GLUSTERFS); - if(btnNfs.getSelection()) { - nasProtocols.add(NAS_PROTOCOL.NFS); - } + nasProtocols.add(NAS_PROTOCOL.NFS); + +// if(btnNfs.getSelection()) { +// nasProtocols.add(NAS_PROTOCOL.NFS); +// } volume.setAccessControlList(txtAccessControl.getText()); 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 49fb1e0d..c7184b59 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 @@ -29,6 +29,7 @@ import static com.gluster.storage.management.core.constants.RESTConstants.SUBRES import static com.gluster.storage.management.core.constants.RESTConstants.SUBRESOURCE_OPTIONS; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javax.ws.rs.Consumes; @@ -55,8 +56,9 @@ import com.sun.jersey.spi.resource.Singleton; @Singleton @Path(RESOURCE_PATH_VOLUMES) public class VolumesResource { - private static final String SCRIPT_NAME = "preVolumeCreate.py"; - + private static final String PREPARE_BRICK_SCRIPT = "preVolumeCreate.py"; + private static final String VOLUME_DIRECTORY_CLEANUP_SCRIPT = "cleanupVolumeDirectoryCreate.py"; + @InjectParam private static ServerUtil serverUtil; private final GlusterUtil glusterUtil = new GlusterUtil(); @@ -79,24 +81,20 @@ public class VolumesResource { @Consumes(MediaType.TEXT_XML) @Produces(MediaType.TEXT_XML) public Status createVolume(Volume volume) { - //Create the directories for the volume - List bricks = new ArrayList(); - for(String disk : volume.getDisks()) { - - String brickNotation = prepareBrick(volume, disk); - if (brickNotation != null) { - bricks.add(brickNotation); - } else { - 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() + "]"); + // Create the directories for the volume + List disks = volume.getDisks(); + Status result = createDirectory(disks, volume.getName()); + if (result.isSuccess()) { + List bricks = Arrays.asList(result.getMessage().split(", ")); + + result = glusterUtil.createVolume(volume, bricks); + if (!result.isSuccess()) { + // Perform cleanup on all nodes before returning + cleanupDirectory(disks, volume.getName(), disks.size()); + return result; } } - - return glusterUtil.createVolume(volume, bricks); + return glusterUtil.createOptions(volume); } @GET @@ -105,7 +103,7 @@ public class VolumesResource { public Volume getVolume(@PathParam(PATH_PARAM_VOLUME_NAME) String volumeName) { return glusterUtil.getVolume(volumeName); } - + @PUT @Path("{" + PATH_PARAM_VOLUME_NAME + "}") @Produces(MediaType.TEXT_XML) @@ -129,7 +127,7 @@ public class VolumesResource { @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) @@ -145,19 +143,48 @@ public class VolumesResource { // whenever such a CLI command is made available in GlusterFS return new VolumeOptionInfoListResponse(Status.STATUS_SUCCESS, volumeOptionsDefaults.getDefaults()); } - - private String prepareBrick(Volume vol, String disk) { + + private Status prepareBrick(String disk, String volumeName) { String serverName = disk.split(":")[0]; String diskName = disk.split(":")[1]; - Status result = (Status)serverUtil.executeOnServer(true, serverName, SCRIPT_NAME + " " + vol.getName() + " " + diskName, Status.class); - - if(result.isSuccess()) { - return result.getMessage(); - } else { - return null; + return (Status) serverUtil.executeOnServer(true, serverName, PREPARE_BRICK_SCRIPT + " " + volumeName + " " + + diskName, Status.class); + } + + @SuppressWarnings("null") + private Status createDirectory(List disks, String volumeName) { + List brickNotation = null; + for (int i = 0; i < disks.size(); i++) { + Status result = prepareBrick(disks.get(i), volumeName); + if (result.isSuccess()) { + brickNotation.add(result.getMessage()); + } else { + cleanupDirectory(disks, volumeName, i); + return new Status(Status.STATUS_CODE_FAILURE, "Error while preparing disk [" + disks.get(i) + + "] for volume [" + volumeName + "]"); + } + } + return new Status(Status.STATUS_CODE_SUCCESS, brickNotation.toString()); + + } + + private Status cleanupDirectory(List disks, String volumeName, int maxIndex) { + String serverName, diskName, diskInfo[]; + Status result; + for (int i = 0; i < maxIndex; i++) { + // TODO: Call to delete the volume directory + diskInfo = disks.get(i).split(":"); + serverName = diskInfo[0]; + diskName = diskInfo[1]; + result = (Status) serverUtil.executeOnServer(true, serverName, VOLUME_DIRECTORY_CLEANUP_SCRIPT + " " + volumeName + " " + + diskName, Status.class); + if (!result.isSuccess()) { + return result; + } } + return new Status(Status.STATUS_CODE_SUCCESS, "Directories cleanedup..."); } - + public static void main(String[] args) { VolumesResource vr = new VolumesResource(); VolumeListResponse response = vr.getAllVolumes(); -- cgit