summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java80
-rw-r--r--src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/DiscoveredServersClient.java4
-rw-r--r--src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/GlusterDataModelManager.java125
-rw-r--r--src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/VolumesClient.java124
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/CoreConstants.java4
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/GlusterConstants.java35
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/RESTConstants.java19
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Alert.java2
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Cluster.java13
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/ClusterListener.java2
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/DefaultClusterListener.java5
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Event.java4
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/GlusterDummyModel.java2
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/LogMessage.java27
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Status.java21
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Volume.java38
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/adapters/VolumeLogDateAdapter.java53
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/LogMessageListResponse.java42
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/VolumeListResponse.java4
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/DateUtil.java88
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterCoreUtil.java38
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/StringUtil.java30
-rw-r--r--src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/ValidationUtil.java80
-rw-r--r--src/com.gluster.storage.management.gui.feature/feature.xml7
-rw-r--r--src/com.gluster.storage.management.gui/META-INF/MANIFEST.MF3
-rw-r--r--src/com.gluster.storage.management.gui/icons/reset-options.pngbin0 -> 916 bytes
-rw-r--r--src/com.gluster.storage.management.gui/plugin.xml80
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/Application.java10
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/ApplicationWorkbenchWindowAdvisor.java2
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/VolumeLogTableLabelProvider.java10
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/AbstractActionDelegate.java2
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/AddDiskAction.java84
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/CreateVolumeAction.java2
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/DeleteVolumeAction.java67
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/IActionConstants.java (renamed from src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/IActionSetIDs.java)5
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/RemoveDiskAction.java65
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/ResetVolumeOptionsAction.java64
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/AddDiskPage.java165
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/AddDiskWizard.java92
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/CreateVolumePage1.java379
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/CreateVolumeWizard.java37
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/DisksSelectionPage.java (renamed from src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/CreateVolumeDisksPage.java)9
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/SelectDisksDialog.java28
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/login/LoginDialog.java5
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/toolbar/GlusterToolbarManager.java16
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/utils/GUIHelper.java26
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/ClusterSummaryView.java39
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/NavigationView.java8
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumeSummaryView.java237
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumesSummaryView.java6
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/AbstractDisksPage.java3
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionKeyEditingSupport.java125
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionValueEditingSupport.java115
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeLogsPage.java340
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeOptionsPage.java262
-rw-r--r--src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/tabcreators/VolumeTabCreator.java2
-rw-r--r--src/com.gluster.storage.management.server.scripts/src/common/Common.py9
-rw-r--r--src/com.gluster.storage.management.server.scripts/src/common/DiskUtils.py226
-rw-r--r--src/com.gluster.storage.management.server.scripts/src/common/Utils.py5
-rw-r--r--src/com.gluster.storage.management.server.scripts/src/nodes/CreateVolumeExportDirectory.py51
-rw-r--r--src/com.gluster.storage.management.server.scripts/src/nodes/GlusterdUtils.py250
-rw-r--r--src/com.gluster.storage.management.server.scripts/src/nodes/VolumeUtils.py610
-rw-r--r--src/com.gluster.storage.management.server.scripts/src/nodes/XmlHandler.py2
-rwxr-xr-xsrc/com.gluster.storage.management.server.scripts/src/nodes/clear_volume_directory.py101
-rwxr-xr-xsrc/com.gluster.storage.management.server.scripts/src/nodes/create_volume_directory.py85
-rwxr-xr-xsrc/com.gluster.storage.management.server.scripts/src/nodes/get_disk_mount_point.py64
-rwxr-xr-xsrc/com.gluster.storage.management.server.scripts/src/nodes/get_disk_name_by_path.py69
-rwxr-xr-xsrc/com.gluster.storage.management.server.scripts/src/nodes/get_volume_brick_log.py109
-rwxr-xr-xsrc/com.gluster.storage.management.server.scripts/src/nodes/get_volume_log.py132
-rw-r--r--src/com.gluster.storage.management.server.scripts/src/nodes/multicast_response.py19
-rw-r--r--src/com.gluster.storage.management.server/WebContent/WEB-INF/web.xml4
-rw-r--r--src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/constants/VolumeOptionsDefaults.java4
-rw-r--r--src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/filters/AuditFilter.java31
-rw-r--r--src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/filters/GlusterResourceFilterFactory.java31
-rw-r--r--src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/AlertsResource.java2
-rw-r--r--src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/GlusterServersResource.java2
-rw-r--r--src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/VolumesResource.java353
-rw-r--r--src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/GlusterUtil.java (renamed from src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterUtil.java)158
-rw-r--r--src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/ServerUtil.java14
79 files changed, 4799 insertions, 667 deletions
diff --git a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java
index 807e32a3..a4df0e58 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
@@ -6,6 +6,7 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import com.gluster.storage.management.client.utils.ClientUtil;
+import com.gluster.storage.management.core.constants.RESTConstants;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.DefaultClientConfig;
@@ -19,12 +20,12 @@ public abstract class AbstractClient {
protected WebResource resource;
private String securityToken;
private String authHeader;
-
+
public AbstractClient() {
URI baseURI = new ClientUtil().getServerBaseURI();
resource = Client.create(new DefaultClientConfig()).resource(baseURI).path(getResourceName());
}
-
+
public AbstractClient(String securityToken) {
this();
setSecurityToken(securityToken);
@@ -92,9 +93,30 @@ public abstract class AbstractClient {
}
/**
+ * Fetches the resource whose name is arrived at by appending the "subResourceName" parameter to the default
+ * resource (the one returned by {@link AbstractClient#getResourceName()})
+ *
+ * @param subResourceName
+ * Name of the sub-resource
+ * @param queryParams
+ * Query parameters to be sent for the GET request
+ * @param responseClass
+ * Expected class of the response
+ * @return Object of responseClass received as a result of the GET request on the sub-resource
+ */
+ @SuppressWarnings("rawtypes")
+ protected Object fetchSubResource(String subResourceName, MultivaluedMap<String, String> queryParams,
+ Class responseClass) {
+ return fetchResource(resource.path(subResourceName), queryParams, responseClass);
+ }
+
+ /**
* Submits given Form using POST method to the resource and returns the object received as response
- * @param responseClass Class of the object expected as response
- * @param form Form to be submitted
+ *
+ * @param responseClass
+ * Class of the object expected as response
+ * @param form
+ * Form to be submitted
* @return Object of given class received as response
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -105,9 +127,13 @@ public abstract class AbstractClient {
/**
* Submits given Form using POST 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
- * @param form Form to be submitted
+ *
+ * @param subResourceName
+ * Name of the sub-resource to which the request is to be posted
+ * @param responseClass
+ * Class of the object expected as response
+ * @param form
+ * Form to be submitted
* @return Object of given class received as response
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@@ -118,9 +144,13 @@ public abstract class AbstractClient {
/**
* Submits given Form using PUT method to the given sub-resource and returns the object received as response
- * @param subResourceName Name of the sub-resource to which the request is to be posted
- * @param responseClass Class of the object expected as response
- * @param form Form to be submitted
+ *
+ * @param subResourceName
+ * Name of the sub-resource to which the request is to be posted
+ * @param responseClass
+ * Class of the object expected as response
+ * @param form
+ * Form to be submitted
* @return Object of given class received as response
*/
protected Object putRequest(String subResourceName, Class responseClass, Form form) {
@@ -130,8 +160,11 @@ public abstract class AbstractClient {
/**
* Submits given Form using PUT method to the given sub-resource and returns the object received as response
- * @param subResourceName Name of the sub-resource to which the request is to be posted
- * @param responseClass Class of the object expected as response
+ *
+ * @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) {
@@ -141,8 +174,11 @@ public abstract class AbstractClient {
/**
* Submits given object to the resource and returns the object received as response
- * @param responseClass Class of the object expected as response
- * @param requestObject the Object to be submitted
+ *
+ * @param responseClass
+ * Class of the object expected as response
+ * @param requestObject
+ * the Object to be submitted
* @return Object of given class received as response
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@@ -151,6 +187,19 @@ public abstract class AbstractClient {
.post(responseClass, requestObject);
}
+ @SuppressWarnings("unchecked")
+ protected Object deleteResource(Class responseClass, Form form) {
+ return resource.header(HTTP_HEADER_AUTH, authHeader).delete(responseClass);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected Object deleteSubResource(String subResourceName, Class responseClass, String volumeName,
+ String deleteOption) {
+ return resource.path(subResourceName).queryParam(RESTConstants.QUERY_PARAM_VOLUME_NAME, volumeName)
+ .queryParam(RESTConstants.QUERY_PARAM_DELETE_OPTION, deleteOption).header(HTTP_HEADER_AUTH, authHeader)
+ .delete(responseClass);
+ }
+
public abstract String getResourceName();
/**
@@ -161,7 +210,8 @@ public abstract class AbstractClient {
}
/**
- * @param securityToken the securityToken to set
+ * @param securityToken
+ * the securityToken to set
*/
protected void setSecurityToken(String securityToken) {
this.securityToken = securityToken;
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 6a22bf5f..c7ea7507 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
@@ -41,17 +41,21 @@ public class DiscoveredServersClient extends AbstractClient {
return RESOURCE_NAME;
}
+ @SuppressWarnings("rawtypes")
private Object getDiscoveredServers(Boolean getDetails, Class responseClass) {
MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
queryParams.putSingle("details", getDetails.toString());
+ //System.out.println((String) fetchResource(queryParams, String.class));
return ((Response) fetchResource(queryParams, responseClass)).getData();
}
+ @SuppressWarnings("unchecked")
public List<String> getDiscoveredServerNames() {
return (List<String>) getDiscoveredServers(Boolean.FALSE, StringListResponse.class);
}
+ @SuppressWarnings("unchecked")
public List<Server> getDiscoveredServerDetails() {
return (List<Server>) getDiscoveredServers(Boolean.TRUE, ServerListResponse.class);
}
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..41d76e58 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.Entry;
import com.gluster.storage.management.core.exceptions.GlusterRuntimeException;
import com.gluster.storage.management.core.model.Cluster;
@@ -41,9 +42,10 @@ import com.gluster.storage.management.core.model.Volume;
import com.gluster.storage.management.core.model.Volume.TRANSPORT_TYPE;
import com.gluster.storage.management.core.model.Volume.VOLUME_STATUS;
import com.gluster.storage.management.core.model.Volume.VOLUME_TYPE;
+import com.gluster.storage.management.core.model.VolumeOptionInfo;
import com.gluster.storage.management.core.response.RunningTaskListResponse;
import com.gluster.storage.management.core.response.VolumeListResponse;
-import com.gluster.storage.management.client.VolumesClient;
+import com.gluster.storage.management.core.response.VolumeOptionInfoListResponse;
public class GlusterDataModelManager {
// private Server discoveredServer1, discoveredServer2, discoveredServer3,
@@ -57,6 +59,7 @@ public class GlusterDataModelManager {
private String securityToken;
private String serverName;
private List<ClusterListener> listeners = new ArrayList<ClusterListener>();
+ private List<VolumeOptionInfo> volumeOptionsDefaults;
private GlusterDataModelManager() {
}
@@ -114,13 +117,9 @@ public class GlusterDataModelManager {
setSecurityToken(securityToken);
Cluster cluster = new Cluster("Home", model);
- VolumesClient volumeClient = new VolumesClient(securityToken);
initializeGlusterServers(cluster);
-
- // initializeVolumes(cluster);
- VolumeListResponse response = volumeClient.getAllVolumes();
- cluster.setVolumes(response.getVolumes());
+ initializeVolumes(cluster);
initializeAutoDiscoveredServers(cluster);
initializeDisks();
@@ -130,12 +129,21 @@ 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++) {
@@ -155,26 +163,12 @@ public class GlusterDataModelManager {
}
private void initializeVolumes(Cluster cluster) {
- List<Volume> volumes = new ArrayList<Volume>();
-
- volume1 = addVolume(volumes, "Volume1", cluster, VOLUME_TYPE.PLAIN_DISTRIBUTE, TRANSPORT_TYPE.ETHERNET,
- VOLUME_STATUS.ONLINE);
-
- volume2 = addVolume(volumes, "Volume2", cluster, VOLUME_TYPE.PLAIN_DISTRIBUTE, TRANSPORT_TYPE.ETHERNET,
- VOLUME_STATUS.ONLINE);
-
- volume3 = addVolume(volumes, "Volume3", cluster, VOLUME_TYPE.DISTRIBUTED_MIRROR, TRANSPORT_TYPE.ETHERNET,
- VOLUME_STATUS.OFFLINE);
- volume3.setReplicaCount(2);
-
- volume4 = addVolume(volumes, "Volume4", cluster, VOLUME_TYPE.PLAIN_DISTRIBUTE, TRANSPORT_TYPE.ETHERNET,
- VOLUME_STATUS.ONLINE);
-
- volume5 = addVolume(volumes, "Volume5", cluster, VOLUME_TYPE.DISTRIBUTED_STRIPE, TRANSPORT_TYPE.INFINIBAND,
- VOLUME_STATUS.OFFLINE);
- volume5.setStripeCount(3);
-
- cluster.setVolumes(volumes);
+ VolumesClient volumeClient = new VolumesClient(securityToken);
+ VolumeListResponse response = volumeClient.getAllVolumes();
+ if (!response.getStatus().isSuccess()) {
+ throw new GlusterRuntimeException("Error fetching volume list: [" + response.getStatus() + "]");
+ }
+ cluster.setVolumes(response.getVolumes());
}
private void initializeDisks() {
@@ -186,13 +180,8 @@ public class GlusterDataModelManager {
s2dc = new Disk(server2, "sdc", 200d, -1d, DISK_STATUS.UNINITIALIZED);
s2dd = new Disk(server2, "sdd", 200d, 124.89, DISK_STATUS.READY);
- s3da = new Disk(server3, "NA", -1d, -1d, DISK_STATUS.OFFLINE); // disk
- // name
- // unavailable
- // since
- // server
- // is
- // offline
+ // disk name unavailable since server is offline
+ s3da = new Disk(server3, "NA", -1d, -1d, DISK_STATUS.OFFLINE);
s4da = new Disk(server4, "sda", 100d, 85.39, DISK_STATUS.READY);
@@ -250,7 +239,7 @@ public class GlusterDataModelManager {
private void addMessages(List<LogMessage> messages, Disk disk, String severity, int count) {
for (int i = 1; i <= count; i++) {
String message = severity + "message" + i;
- messages.add(new LogMessage(new Date(), disk, severity, message));
+ messages.add(new LogMessage(new Date(), disk.getQualifiedName(), severity, message));
}
}
@@ -294,7 +283,7 @@ public class GlusterDataModelManager {
List<Disk> allDisks = getReadyDisksOfAllServers();
String brickInfo[] = volumeDisk.split(":");
for (Disk disk : allDisks) {
- if (disk.getServerName() == brickInfo[0] && disk.getName() == brickInfo[1]) {
+ if (disk.getServerName().equals(brickInfo[0]) && disk.getName().equals(brickInfo[1])) {
return disk;
}
}
@@ -305,8 +294,8 @@ public class GlusterDataModelManager {
/*
* TODO: review the logic
*
- * List<Disk> disks = new ArrayList<Disk>(); for (Disk disk :
- * volume.getDisks()) { if (disk.isReady()) { disks.add(disk); } }
+ * List<Disk> disks = new ArrayList<Disk>(); for (Disk disk : volume.getDisks()) { if (disk.isReady()) {
+ * disks.add(disk); } }
*/
Disk disk = null;
List<Disk> volumeDisks = new ArrayList<Disk>();
@@ -370,6 +359,15 @@ public class GlusterDataModelManager {
}
}
+ public void deleteVolume(Volume volume) {
+ Cluster cluster = model.getCluster();
+ cluster.deleteVolume(volume);
+
+ for (ClusterListener listener : listeners) {
+ listener.volumeDeleted(volume);
+ }
+ }
+
public void updateVolumeStatus(Volume volume, VOLUME_STATUS newStatus) {
volume.setStatus(newStatus);
for (ClusterListener listener : listeners) {
@@ -377,6 +375,20 @@ public class GlusterDataModelManager {
}
}
+ 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 setVolumeOption(Volume volume, Entry<String, String> entry) {
+ volume.setOption(entry.getKey(), (String) entry.getValue());
+ for (ClusterListener listener : listeners) {
+ listener.volumeChanged(volume, new Event(EVENT_TYPE.VOLUME_OPTION_SET, entry));
+ }
+ }
+
public void addVolume(Volume volume) {
Cluster cluster = model.getCluster();
cluster.addVolume(volume);
@@ -385,4 +397,41 @@ public class GlusterDataModelManager {
listener.volumeCreated(volume);
}
}
+
+ public List<VolumeOptionInfo> getVolumeOptionsDefaults() {
+ return volumeOptionsDefaults;
+ }
+
+ public VolumeOptionInfo getVolumeOptionInfo(String optionKey) {
+ for (VolumeOptionInfo info : volumeOptionsDefaults) {
+ if (info.getName().equals(optionKey)) {
+ return info;
+ }
+ }
+ throw new GlusterRuntimeException("Invalid option key [" + optionKey
+ + "] passed to GlusterDataModelManager#getVolumeOptionInfo");
+ }
+
+ public String getVolumeOptionDefaultValue(String optionKey) {
+ return getVolumeOptionInfo(optionKey).getDefaultValue();
+ }
+
+ public String getVolumeOptionDesc(String optionKey) {
+ return getVolumeOptionInfo(optionKey).getDescription();
+ }
+
+ public void setAccessControlList(Volume volume, String accessControlList) {
+ volume.setAccessControlList(accessControlList);
+ setVolumeOption(volume, getOptionEntry(volume, Volume.OPTION_AUTH_ALLOW));
+ }
+
+ private Entry<String, String> getOptionEntry(Volume volume, String optionKey) {
+ for (Entry entry : volume.getOptions().entrySet()) {
+ if (entry.getKey().equals(optionKey)) {
+ return entry;
+ }
+ }
+ throw new GlusterRuntimeException("Couldn't find entry for option [" + optionKey + "] on volume ["
+ + volume.getName());
+ }
}
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 a479e1f7..33791a50 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
@@ -20,18 +20,28 @@
*/
package com.gluster.storage.management.client;
+import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
+import javax.ws.rs.core.MultivaluedMap;
+import com.gluster.storage.management.core.constants.CoreConstants;
+import com.gluster.storage.management.core.constants.GlusterConstants;
import com.gluster.storage.management.core.constants.RESTConstants;
+import com.gluster.storage.management.core.model.Disk;
+import com.gluster.storage.management.core.model.Disk.DISK_STATUS;
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.LogMessageListResponse;
import com.gluster.storage.management.core.response.VolumeListResponse;
import com.gluster.storage.management.core.response.VolumeOptionInfoListResponse;
+import com.gluster.storage.management.core.utils.DateUtil;
+import com.gluster.storage.management.core.utils.GlusterCoreUtil;
+import com.gluster.storage.management.core.utils.StringUtil;
import com.sun.jersey.api.representation.Form;
+import com.sun.jersey.core.util.MultivaluedMapImpl;
public class VolumesClient extends AbstractClient {
-
public VolumesClient(String securityToken) {
super(securityToken);
}
@@ -44,14 +54,14 @@ public class VolumesClient extends AbstractClient {
public Status createVolume(Volume volume) {
return (Status) postObject(Status.class, volume);
}
-
+
private Status performOperation(String volumeName, String operation) {
Form form = new Form();
form.add(RESTConstants.FORM_PARAM_OPERATION, operation);
-
- return (Status)putRequest(volumeName, Status.class, form);
+
+ return (Status) putRequest(volumeName, Status.class, form);
}
-
+
public Status startVolume(String volumeName) {
return performOperation(volumeName, RESTConstants.FORM_PARAM_VALUE_START);
}
@@ -59,34 +69,97 @@ public class VolumesClient extends AbstractClient {
public Status stopVolume(String volumeName) {
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);
+ return (Status) postRequest(volume + "/" + RESTConstants.SUBRESOURCE_OPTIONS, Status.class, form);
}
-
+
public Status resetVolumeOptions(String volume) {
- return (Status)putRequest(volume, Status.class);
+ return (Status) putRequest(volume + "/" + RESTConstants.SUBRESOURCE_OPTIONS, Status.class);
}
-
+
public VolumeListResponse getAllVolumes() {
return (VolumeListResponse) fetchResource(VolumeListResponse.class);
}
-
+
public Volume getVolume(String volumeName) {
return (Volume) fetchSubResource(volumeName, Volume.class);
}
- public List<VolumeOptionInfo> getVolumeOptionsDefaults() {
- String responseStr = (String)fetchSubResource(
- RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS, String.class);
- System.out.println(responseStr);
+ public Status deleteVolume(Volume volume, String deleteOption) {
+ return (Status) deleteSubResource(volume.getName(), Status.class, volume.getName(), deleteOption);
+ }
+
+ public VolumeOptionInfoListResponse getVolumeOptionsDefaults() {
+ return ((VolumeOptionInfoListResponse) fetchSubResource(RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS,
+ VolumeOptionInfoListResponse.class));
+ }
+
+ public Status addDisks(String volumeName, List<Disk> diskList) {
+ String disks = StringUtil.ListToString( GlusterCoreUtil.getQualifiedDiskNames(diskList), ",");
+ Form form = new Form();
+ form.add(RESTConstants.QUERY_PARAM_DISKS, disks);
+ return (Status) postRequest(volumeName + "/" + RESTConstants.SUBRESOURCE_DISKS, Status.class, form);
+ }
+
+ public Status addDisks(String volumeName, String disks) {
+ Form form = new Form();
+ form.add(RESTConstants.QUERY_PARAM_DISKS, disks);
+ return (Status) postRequest(volumeName + "/" + RESTConstants.SUBRESOURCE_DISKS, Status.class, form);
+ }
+
+ /**
+ * Fetches volume logs for the given volume based on given filter criteria
+ *
+ * @param volumeName
+ * Name of volume whose logs are to be fetched
+ * @param diskName
+ * Name of the disk whose logs are to be fetched. Pass ALL to fetch log messages from all disks of the
+ * volume.
+ * @param severity
+ * Log severity {@link GlusterConstants#VOLUME_LOG_LEVELS_ARR}. Pass ALL to fetch log messages of all
+ * severity levels.
+ * @param fromTimestamp
+ * From timestamp. Pass null if this filter is not required.
+ * @param toTimestamp
+ * To timestamp. Pass null if this filter is not required.
+ * @param messageCount
+ * Number of most recent log messages to be fetched (from each disk)
+ * @return Log Message List response received from the Gluster Management Server.
+ */
+ public LogMessageListResponse getLogs(String volumeName, String diskName, String severity, Date fromTimestamp, Date toTimestamp, int messageCount) {
+ MultivaluedMap<String, String> queryParams = prepareGetLogQueryParams(diskName, severity, fromTimestamp,
+ toTimestamp, messageCount);
+
+ return (LogMessageListResponse) fetchSubResource(volumeName + "/" + RESTConstants.SUBRESOURCE_LOGS,
+ queryParams, LogMessageListResponse.class);
+ }
+
+ private MultivaluedMap<String, String> prepareGetLogQueryParams(String diskName, String severity,
+ Date fromTimestamp, Date toTimestamp, int messageCount) {
+ MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
+ queryParams.add(RESTConstants.QUERY_PARAM_LINE_COUNT, "" + messageCount);
+ if(!diskName.equals(CoreConstants.ALL)) {
+ queryParams.add(RESTConstants.QUERY_PARAM_DISK_NAME, diskName);
+ }
+
+ if (!severity.equals(CoreConstants.ALL)) {
+ queryParams.add(RESTConstants.QUERY_PARAM_LOG_SEVERITY, severity);
+ }
- VolumeOptionInfoListResponse response = (VolumeOptionInfoListResponse) fetchSubResource(
- RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS, VolumeOptionInfoListResponse.class);
- return response.getOptions();
+ if (fromTimestamp != null) {
+ queryParams.add(RESTConstants.QUERY_PARAM_FROM_TIMESTAMP,
+ DateUtil.dateToString(fromTimestamp, CoreConstants.DATE_WITH_TIME_FORMAT));
+ }
+
+ if (toTimestamp != null) {
+ queryParams.add(RESTConstants.QUERY_PARAM_TO_TIMESTAMP,
+ DateUtil.dateToString(toTimestamp, CoreConstants.DATE_WITH_TIME_FORMAT));
+ }
+ return queryParams;
}
public static void main(String[] args) {
@@ -109,8 +182,17 @@ public class VolumesClient extends AbstractClient {
// 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());
+// System.out.println(client.getVolume("Volume3").getOptions());
+// System.out.println(client.setVolumeOption("Volume3", "network.frame-timeout", "600").getMessage());
+ List<Disk> disks = new ArrayList<Disk>();
+ Disk disk = new Disk();
+ disk.setServerName("server1");
+ disk.setName("sda");
+ disk.setStatus(DISK_STATUS.READY);
+ disks.add(disk);
+
+ Status status = client.addDisks("Volume3", disks);
+ System.out.println(status.getMessage());
}
}
}
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/CoreConstants.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/CoreConstants.java
index 177334bd..8a1e1f37 100644
--- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/CoreConstants.java
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/CoreConstants.java
@@ -26,4 +26,8 @@ public class CoreConstants {
public static final String NEWLINE = System.getProperty("line.separator");
public static final String FILE_SEPARATOR = System.getProperty("file.separator");
public static final String ENCODING_UTF8 = "UTF-8";
+ public static final String ALL = "ALL";
+ public static final String DATE_WITH_TIME_FORMAT = "MM/dd/yyyy HH:mm:ss";
+ public static final String PURE_DATE_FORMAT = "MM/dd/yyyy";
+ public static final String PURE_TIME_FORMAT = "HH:mm:ss.SSS";
}
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/GlusterConstants.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/GlusterConstants.java
new file mode 100644
index 00000000..064037ec
--- /dev/null
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/GlusterConstants.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com>
+ * This file is part of Gluster Management Console.
+ *
+ * Gluster Management Console is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Gluster Management Console is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *******************************************************************************/
+package com.gluster.storage.management.core.constants;
+
+import java.util.List;
+
+import com.gluster.storage.management.core.utils.StringUtil;
+
+/**
+ *
+ */
+public class GlusterConstants {
+ public enum VOLUME_LOG_LEVELS {
+ EMERGENCY, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG, TRACE
+ };
+
+ public static final List<String> VOLUME_LOG_LEVELS_ARR = StringUtil.enumToArray(VOLUME_LOG_LEVELS.values());
+
+}
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 b5b51cfd..159c408e 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
@@ -26,15 +26,30 @@ package com.gluster.storage.management.core.constants;
public class RESTConstants {
// Volumes Resource
public static final String RESOURCE_PATH_VOLUMES = "/cluster/volumes";
+ public static final String SUBRESOURCE_DEFAULT_OPTIONS = "defaultoptions";
+ public static final String SUBRESOURCE_OPTIONS = "options";
+ public static final String SUBRESOURCE_LOGS = "logs";
+ public static final String SUBRESOURCE_DISKS = "disks";
+
+
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";
+ public static final String FORM_PARAM_DELETE_OPTION = "value";
+ public static final String QUERY_PARAM_DISK_NAME = "diskName";
+ public static final String QUERY_PARAM_DISKS = "disks";
+ public static final String QUERY_PARAM_LINE_COUNT = "lineCount";
+ public static final String QUERY_PARAM_VOLUME_NAME = "volumeName";
+ public static final String QUERY_PARAM_DELETE_OPTION = "deleteOption";
+ public static final String QUERY_PARAM_LOG_SEVERITY = "severity";
+ public static final String QUERY_PARAM_FROM_TIMESTAMP = "fromTimestamp";
+ public static final String QUERY_PARAM_TO_TIMESTAMP = "toTimestamp";
+
// Running tasks resource
public static final String RESOURCE_PATH_RUNNING_TASKS = "/cluster/runningtasks";
public static final String RESOURCE_PATH_ALERTS = "/cluster/alerts";
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Alert.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Alert.java
index 66107734..c0077a30 100644
--- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Alert.java
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Alert.java
@@ -14,7 +14,7 @@ public class Alert {
protected String id;
protected ALERT_TYPES type;
- protected String reference;
+ protected String reference; // [for server- "Server", for Disk- "Server:disk", for volume- "Volume:Server:disk"]
protected String message;
public String getAlertType(ALERT_TYPES alertType) {
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Cluster.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Cluster.java
index 1af57266..8fa247a1 100644
--- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Cluster.java
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Cluster.java
@@ -54,6 +54,10 @@ public class Cluster extends Entity {
discoveredServers.remove(server);
}
+ public void deleteVolume(Volume volume) {
+ volumes.remove(volume);
+ }
+
public void setServers(List<GlusterServer> servers) {
this.servers = servers;
children.add(new EntityGroup<GlusterServer>("Servers", GlusterServer.class, this, servers));
@@ -67,6 +71,15 @@ public class Cluster extends Entity {
this.discoveredServers = autoDiscoveredServers;
children.add(new EntityGroup<Server>("Discovered Servers", Server.class, this, autoDiscoveredServers));
}
+
+ public EntityGroup<Server> getAutoDiscoveredServersEntityGroup() {
+ for(Entity entity : getChildren()) {
+ if(entity instanceof EntityGroup && ((EntityGroup)entity).getEntityType() == Server.class) {
+ return (EntityGroup<Server>)entity;
+ }
+ }
+ return null;
+ }
public List<Volume> getVolumes() {
return volumes;
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/ClusterListener.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/ClusterListener.java
index ce2752a4..f96116ed 100644
--- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/ClusterListener.java
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/ClusterListener.java
@@ -40,4 +40,6 @@ public interface ClusterListener {
public void volumeChanged(Volume volume, Event event);
public void volumeCreated(Volume volume);
+
+ public void volumeDeleted(Volume volume);
}
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/DefaultClusterListener.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/DefaultClusterListener.java
index 1a39a014..e226d51b 100644
--- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/DefaultClusterListener.java
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/DefaultClusterListener.java
@@ -69,6 +69,11 @@ public class DefaultClusterListener implements ClusterListener {
clusterChanged();
}
+ @Override
+ public void volumeDeleted(Volume volume) {
+ clusterChanged();
+ }
+
/**
* This method is called by every other event method. Thus, if a view/listener is interested in performing the same
* task on any change happening in the cluster data model, it can simply override this method and implement the
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..22b938d2 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,9 @@ public class Event {
DISK_REMOVED,
NETWORK_INTERFACE_ADDED,
NETWORK_INTERFACE_REMOVED,
- VOLUME_STATUS_CHANGED
+ VOLUME_STATUS_CHANGED,
+ VOLUME_OPTIONS_RESET,
+ VOLUME_OPTION_SET
}
private EVENT_TYPE eventType;
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/GlusterDummyModel.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/GlusterDummyModel.java
index f25999f0..09137014 100644
--- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/GlusterDummyModel.java
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/GlusterDummyModel.java
@@ -218,7 +218,7 @@ public class GlusterDummyModel {
private void addMessages(List<LogMessage> messages, Disk disk, String severity, int count) {
for (int i = 1; i <= count; i++) {
String message = severity + "message" + i;
- messages.add(new LogMessage(new Date(), disk, severity, message));
+ messages.add(new LogMessage(new Date(), disk.getQualifiedName(), severity, message));
}
}
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/LogMessage.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/LogMessage.java
index cc3aa043..4f6347dc 100644
--- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/LogMessage.java
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/LogMessage.java
@@ -20,14 +20,29 @@ package com.gluster.storage.management.core.model;
import java.util.Date;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import com.gluster.storage.management.core.model.adapters.VolumeLogDateAdapter;
import com.gluster.storage.management.core.utils.StringUtil;
+@XmlRootElement
public class LogMessage implements Filterable {
private Date timestamp;
- private Disk disk;
+ private String disk;
private String severity;
private String message;
+ public LogMessage() {
+ }
+
+ public LogMessage(String logMessage) {
+ // TODO: Parse the log message and extract fields
+ }
+
+ @XmlElement(name = "timestamp", required = true)
+ @XmlJavaTypeAdapter(VolumeLogDateAdapter.class)
public Date getTimestamp() {
return timestamp;
}
@@ -36,11 +51,11 @@ public class LogMessage implements Filterable {
this.timestamp = timestamp;
}
- public Disk getDisk() {
+ public String getDisk() {
return disk;
}
- public void setDisk(Disk disk) {
+ public void setDisk(String disk) {
this.disk = disk;
}
@@ -60,7 +75,7 @@ public class LogMessage implements Filterable {
this.message = message;
}
- public LogMessage(Date timestamp, Disk disk, String severity, String message) {
+ public LogMessage(Date timestamp, String disk, String severity, String message) {
setTimestamp(timestamp);
setDisk(disk);
setSeverity(severity);
@@ -69,7 +84,7 @@ public class LogMessage implements Filterable {
@Override
public boolean filter(String filterString, boolean caseSensitive) {
- return StringUtil.filterString(getSeverity() + getTimestamp() + getDisk().getServerName()
- + getDisk().getQualifiedName() + getMessage(), filterString, caseSensitive);
+ return StringUtil.filterString(getSeverity() + getTimestamp() + getDisk() + getMessage(), filterString,
+ caseSensitive);
}
}
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Status.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Status.java
index c5fdb246..95075f78 100644
--- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Status.java
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Status.java
@@ -24,14 +24,16 @@ import com.gluster.storage.management.core.utils.ProcessResult;
@XmlRootElement(name = "status")
public class Status {
+ // TODO: Convert the status codes to an enumeration
public static final int STATUS_CODE_SUCCESS = 0;
public static final int STATUS_CODE_FAILURE = 1;
- public static final int STATUS_CODE_RUNNING = 2;
- public static final int STATUS_CODE_PAUSE = 3;
- public static final int STATUS_CODE_WARNING = 4;
+ public static final int STATUS_CODE_PART_SUCCESS = 2;
+ public static final int STATUS_CODE_RUNNING = 3;
+ public static final int STATUS_CODE_PAUSE = 4;
+ public static final int STATUS_CODE_WARNING = 5;
public static final Status STATUS_SUCCESS = new Status(STATUS_CODE_SUCCESS, "Success");
public static final Status STATUS_FAILURE = new Status(STATUS_CODE_FAILURE, "Failure");
-
+
// public static final Status
private Integer code;
@@ -43,6 +45,10 @@ public class Status {
public boolean isSuccess() {
return code == STATUS_CODE_SUCCESS;
}
+
+ public boolean isPartSuccess() {
+ return code == STATUS_CODE_PART_SUCCESS;
+ }
public Status(Integer code, String message) {
this.code = code;
@@ -53,6 +59,11 @@ public class Status {
this.code = result.getExitValue();
this.message = result.getOutput();
}
+
+ public Status(Exception e) {
+ this.code = STATUS_CODE_FAILURE;
+ this.message = e.getMessage();
+ }
@XmlElement(name = "code", type = Integer.class)
public Integer getCode() {
@@ -74,6 +85,6 @@ public class Status {
@Override
public String toString() {
- return (isSuccess() ? "Success" : "Failure [" + getCode() + "]") + ": " + getMessage();
+ return isSuccess() ? "Success" : "[" + getCode() + "][" + getMessage() + "]";
}
} \ No newline at end of file
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..41ed5a25 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,11 @@ public class Volume extends Entity {
GLUSTERFS, NFS
};
- private static final String OPTION_AUTH_ALLOW = "auth.allow:";
+
+ public static final int DEFAULT_REPLICA_COUNT = 2;
+ public static final int DEFAULT_STRIPE_COUNT = 4;
+
+ public static final String OPTION_AUTH_ALLOW = "auth.allow";
private static final String[] VOLUME_TYPE_STR = new String[] { "Plain Distribute", "Distributed Mirror",
"Distributed Stripe" };
@@ -67,15 +71,14 @@ public class Volume extends Entity {
private double totalDiskSpace = 0;
private List<String> disks = new ArrayList<String>();
+ private List<String> bricks = new ArrayList<String>();
public Volume() {
}
- // GlusterFS export is always enabled
- private Set<NAS_PROTOCOL> nasProtocols = new LinkedHashSet<NAS_PROTOCOL>(
- Arrays.asList(new NAS_PROTOCOL[] { NAS_PROTOCOL.GLUSTERFS }));
-
- private String accessControlList = "*";
+ // GlusterFS and NFS export is always enabled
+ private Set<NAS_PROTOCOL> nasProtocols = new LinkedHashSet<NAS_PROTOCOL>(Arrays.asList(new NAS_PROTOCOL[] {
+ NAS_PROTOCOL.GLUSTERFS, NAS_PROTOCOL.NFS }));
public String getVolumeTypeStr() {
return getVolumeTypeStr(getVolumeType());
@@ -106,9 +109,9 @@ public class Volume extends Entity {
// TODO find a way to get the replica / strip count
if (volumeType == VOLUME_TYPE.DISTRIBUTED_STRIPE) {
setReplicaCount(0);
- setStripeCount(3);
+ setStripeCount(DEFAULT_STRIPE_COUNT);
} else if (volumeType == VOLUME_TYPE.DISTRIBUTED_MIRROR) {
- setReplicaCount(2);
+ setReplicaCount(DEFAULT_REPLICA_COUNT);
setStripeCount(0);
} else {
setReplicaCount(0);
@@ -204,9 +207,6 @@ public class Volume extends Entity {
}
public void addDisk(String disk) {
- // if (disks.add(disk) && disk.getStatus() != DISK_STATUS.OFFLINE) {
- // totalDiskSpace += disk.getSpace();
- // }
disks.add(disk);
}
@@ -217,9 +217,7 @@ public class Volume extends Entity {
}
public void removeDisk(String disk) {
- // if (disks.remove(disk)) {
- // totalDiskSpace -= disk.getSpace();
- // }
+ disks.remove(disk);
}
public void removeAllDisks() {
@@ -227,6 +225,18 @@ public class Volume extends Entity {
totalDiskSpace = 0;
}
+ public void addBrick(String brick) {
+ bricks.add(brick);
+ }
+
+ public void removeBrick(String brick) {
+ bricks.remove(brick);
+ }
+
+ public List<String> getBricks() {
+ return bricks;
+ }
+
public void setDisks(List<String> disks) {
removeAllDisks();
addDisks(disks);
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/adapters/VolumeLogDateAdapter.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/adapters/VolumeLogDateAdapter.java
new file mode 100644
index 00000000..fac208ad
--- /dev/null
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/adapters/VolumeLogDateAdapter.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com>
+ * This file is part of Gluster Management Console.
+ *
+ * Gluster Management Console is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Gluster Management Console is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *******************************************************************************/
+
+package com.gluster.storage.management.core.model.adapters;
+
+import java.util.Date;
+
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+
+import com.gluster.storage.management.core.utils.DateUtil;
+
+/**
+ * Adapter class used for converting timestamp from Gluster volume log files to Date object.
+ */
+public class VolumeLogDateAdapter extends XmlAdapter<String, Date> {
+ private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
+
+ /* (non-Javadoc)
+ * @see javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal(java.lang.Object)
+ */
+ @Override
+ public Date unmarshal(String input) {
+ input = input.trim();
+ if(input.length() > DATE_FORMAT.length()) {
+ input = input.substring(0, DATE_FORMAT.length());
+ }
+ return DateUtil.stringToDate(input, DATE_FORMAT);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.xml.bind.annotation.adapters.XmlAdapter#marshal(java.lang.Object)
+ */
+ @Override
+ public String marshal(Date input) {
+ return DateUtil.dateToString(input, DATE_FORMAT);
+ }
+}
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/LogMessageListResponse.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/LogMessageListResponse.java
new file mode 100644
index 00000000..191334d3
--- /dev/null
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/LogMessageListResponse.java
@@ -0,0 +1,42 @@
+/**
+ *
+ */
+package com.gluster.storage.management.core.response;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import com.gluster.storage.management.core.model.LogMessage;
+import com.gluster.storage.management.core.model.Status;
+
+@XmlRootElement(name = "response")
+public class LogMessageListResponse extends AbstractResponse {
+ private List<LogMessage> logMessages = new ArrayList<LogMessage>();
+
+ public LogMessageListResponse() {
+ }
+
+ public LogMessageListResponse(Status status, List<LogMessage> logMessages) {
+ setStatus(status);
+ setLogMessages(logMessages);
+ }
+
+ @XmlElementWrapper(name = "logMessages")
+ @XmlElement(name = "logMessage", type = LogMessage.class)
+ public List<LogMessage> getLogMessages() {
+ return logMessages;
+ }
+
+ public void setLogMessages(List<LogMessage> logMessages) {
+ this.logMessages = logMessages;
+ }
+
+ @Override
+ public Object getData() {
+ return getLogMessages();
+ }
+}
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/VolumeListResponse.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/VolumeListResponse.java
index fc1c9a6c..97085603 100644
--- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/VolumeListResponse.java
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/response/VolumeListResponse.java
@@ -30,10 +30,6 @@ public class VolumeListResponse extends AbstractResponse {
return this.volumes;
}
- /**
- * @param volumes
- * volumes to set
- */
public void setVolumes(List<Volume> volumes) {
this.volumes = volumes;
}
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/DateUtil.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/DateUtil.java
index 1b284cb8..8677fecd 100644
--- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/DateUtil.java
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/DateUtil.java
@@ -18,18 +18,94 @@
*******************************************************************************/
package com.gluster.storage.management.core.utils;
-import java.text.DateFormat;
+import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
+import com.gluster.storage.management.core.constants.CoreConstants;
+import com.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+
public class DateUtil {
+
+ /**
+ * Formats given date in pure date format (without time component) using default format
+ * {@link CoreConstants#PURE_DATE_FORMAT}
+ *
+ * @param inputDate
+ * Date to be formatted
+ * @return Formatted String representation of the given date
+ */
public static final String formatDate(Date inputDate) {
- DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
- return formatter.format(inputDate);
+ return dateToString(inputDate, CoreConstants.PURE_DATE_FORMAT);
}
-
+
+ /**
+ * Formats given date in pure time format (without date component) using default format
+ * {@link CoreConstants#PURE_TIME_FORMAT}
+ *
+ * @param inputDate
+ * Date to be formatted
+ * @return Formatted String representation of the given date
+ */
public static final String formatTime(Date inputDate) {
- DateFormat formatter = new SimpleDateFormat("HH:mm:ss z");
- return formatter.format(inputDate);
+ return dateToString(inputDate, CoreConstants.PURE_TIME_FORMAT);
+ }
+
+ /**
+ * Converts given date object to string by formatting it in given format
+ *
+ * @param date
+ * Date to be formatted
+ * @param dateFormat
+ * Date format
+ * @return String representation of the given Date
+ */
+ public static final String dateToString(Date date, String dateFormat) {
+ SimpleDateFormat dateFormatter = new SimpleDateFormat(dateFormat);
+ return dateFormatter.format(date);
+ }
+
+ /**
+ * Converts given date object to string by formatting it using default date format
+ * {@link CoreConstants#DATE_WITH_TIME_FORMAT}
+ *
+ * @param date
+ * Date to be formatted
+ * @param dateFormat
+ * Date format
+ * @return String representation of the given Date
+ */
+ public static final String dateToString(Date date) {
+ return dateToString(date, CoreConstants.DATE_WITH_TIME_FORMAT);
+ }
+
+ /**
+ * Converts given string to date using the given date format
+ *
+ * @param input
+ * Input string
+ * @param dateFormat
+ * The date format to be used
+ * @return Date object
+ */
+ public static final Date stringToDate(String input, String dateFormat) {
+ try {
+ SimpleDateFormat dateFormatter = new SimpleDateFormat(dateFormat);
+ return dateFormatter.parse(input);
+ } catch (ParseException e) {
+ throw new GlusterRuntimeException("Error trying to parse string [" + input + "] in to date using format ["
+ + dateFormat + "]", e);
+ }
+ }
+
+ /**
+ * Converts given string to date using the default date format {@link CoreConstants#DATE_WITH_TIME_FORMAT}
+ *
+ * @param input
+ * Input string
+ * @return Date object
+ */
+ public static final Date stringToDate(String input) {
+ return stringToDate(input, CoreConstants.DATE_WITH_TIME_FORMAT);
}
}
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterCoreUtil.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterCoreUtil.java
new file mode 100644
index 00000000..9e3084fb
--- /dev/null
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterCoreUtil.java
@@ -0,0 +1,38 @@
+/**
+ * GlusterCoreUtil.java
+ *
+ * Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com>
+ * This file is part of Gluster Management Console.
+ *
+ * Gluster Management Console is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Gluster Management Console is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+package com.gluster.storage.management.core.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.gluster.storage.management.core.model.Disk;
+
+
+public class GlusterCoreUtil {
+ // Convert from Disk list to Qualified disk name list
+ public static final List<String> getQualifiedDiskNames(List<Disk> diskList) {
+ List<String> qualifiedDiskNames = new ArrayList<String>();
+ for (Disk disk : diskList) {
+ qualifiedDiskNames.add(disk.getQualifiedName());
+ }
+ return qualifiedDiskNames;
+ }
+}
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/StringUtil.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/StringUtil.java
index 45f4c436..29026410 100644
--- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/StringUtil.java
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/StringUtil.java
@@ -18,15 +18,33 @@
*******************************************************************************/
package com.gluster.storage.management.core.utils;
+import java.util.ArrayList;
+import java.util.List;
+
public class StringUtil {
- public static boolean filterString(String sourceString,
- String filterString, boolean caseSensitive) {
- return caseSensitive ? sourceString.contains(filterString)
- : sourceString.toLowerCase().contains(
- filterString.toLowerCase());
+ public static boolean filterString(String sourceString, String filterString, boolean caseSensitive) {
+ return caseSensitive ? sourceString.contains(filterString) : sourceString.toLowerCase().contains(
+ filterString.toLowerCase());
}
-
+
public static String removeSpaces(String str) {
return str.replaceAll("\\s+", "");
}
+
+ public static String ListToString(List<String> list, String delimiter) {
+ StringBuilder output = new StringBuilder();
+ for(String element : list) {
+ output.append(element).append(delimiter);
+ }
+ String outputStr = output.toString();
+ return outputStr.substring(0, outputStr.length() - (delimiter.length()+1));
+ }
+
+ public static <T extends Enum<T>> List<String> enumToArray(T[] values) {
+ List<String> enumAsArray = new ArrayList<String>();
+ for(T value : values) {
+ enumAsArray.add(value.toString());
+ }
+ return enumAsArray;
+ }
}
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..b1ceb478
--- /dev/null
+++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/ValidationUtil.java
@@ -0,0 +1,80 @@
+package com.gluster.storage.management.core.utils;
+
+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;
+ }
+
+ int iterator=ipAddress.length-1;
+
+ if (ipAddress.length <= 4 && ipAddress[ipAddress.length - 1].equals("*")) {
+ iterator = ipAddress.length - 2;
+ } else if (ipAddress.length < 4 || ipAddress.length > 4 ){
+ isValid = false;
+ iterator = ipAddress.length - 1;
+ }
+
+ for (int i = 0; i <= iterator && 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;
+ }
+ if (ipAddress.length < 4) {
+ isValid = false;
+ }
+ for (int i = 0; i < ipAddress.length && isValid; i++) {
+ isValid = isValidIpQuad(ipAddress[i]);
+ }
+ return isValid;
+ }
+
+ private 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 = "myhost.q";
+ // System.out.println(isValidHostName(hostName));
+ // System.out.println(isValidHostName(hostName));
+ System.out.println(isValidAccessControl(hostName));
+ }
+
+}
diff --git a/src/com.gluster.storage.management.gui.feature/feature.xml b/src/com.gluster.storage.management.gui.feature/feature.xml
index 2448b3cc..7f99803e 100644
--- a/src/com.gluster.storage.management.gui.feature/feature.xml
+++ b/src/com.gluster.storage.management.gui.feature/feature.xml
@@ -772,4 +772,11 @@
fragment="true"
unpack="false"/>
+ <plugin
+ id="org.apache.commons.lang"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
</feature>
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..076a2805 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.apache.commons.lang;bundle-version="2.3.0"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
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
--- /dev/null
+++ b/src/com.gluster.storage.management.gui/icons/reset-options.png
Binary files differ
diff --git a/src/com.gluster.storage.management.gui/plugin.xml b/src/com.gluster.storage.management.gui/plugin.xml
index f5ab3db7..b208fc24 100644
--- a/src/com.gluster.storage.management.gui/plugin.xml
+++ b/src/com.gluster.storage.management.gui/plugin.xml
@@ -223,6 +223,12 @@
</command>
<command
categoryId="com.gluster.storage.management.gui.category"
+ description="Reset all options of a Volume"
+ id="com.gluster.storage.management.gui.commands.ResetVolumeOptions"
+ name="Reset Options">
+ </command>
+ <command
+ categoryId="com.gluster.storage.management.gui.category"
description="Rebalance Volume"
id="com.gluster.storage.management.gui.commands.RebalanceVolume"
name="Rebalance Volume">
@@ -245,6 +251,17 @@
id="com.gluster.storage.management.gui.commands.MigrateDisk"
name="Migrate Disk">
</command>
+ <command
+ categoryId="com.gluster.storage.management.gui.category"
+ description="Remove Disk"
+ id="com.gluster.storage.management.gui.commands.RemoveDisk"
+ name="Remove Disk">
+ </command>
+ <command
+ description="Add Disk"
+ id="com.gluster.storage.management.gui.commands.AddDisk"
+ name="Add Disk">
+ </command>
</extension>
<extension
point="org.eclipse.ui.bindings">
@@ -320,6 +337,11 @@
id="com.gluster.storage.management.gui.KeyConfig"
name="Gluster">
</scheme>
+ <key
+ commandId="com.gluster.storage.management.gui.commands.ResetVolumeOptions"
+ schemeId="com.gluster.storage.management.gui.KeyConfig"
+ sequence="CTRL+SHIFT+O">
+ </key>
</extension>
<extension
id="product"
@@ -480,11 +502,11 @@
style="push"
toolbarPath="Normal"
tooltip="Migrate Disk">
- <enablement>
- <objectClass
- name="com.gluster.storage.management.core.model.Disk">
- </objectClass>
- </enablement>
+ <enablement>
+ <objectClass
+ name="com.gluster.storage.management.core.model.Disk">
+ </objectClass>
+ </enablement>
</action>
<action
allowLabelUpdate="false"
@@ -504,6 +526,22 @@
</action>
<action
allowLabelUpdate="false"
+ class="com.gluster.storage.management.gui.actions.ResetVolumeOptionsAction"
+ definitionId="com.gluster.storage.management.gui.commands.ResetVolumeOptions"
+ icon="icons/reset-options.png"
+ id="com.gluster.storage.management.gui.actions.ResetVolumeOptionsAction"
+ label="Reset &amp;Options"
+ menubarPath="com.gluster.storage.management.gui.menu.volume/volume"
+ mode="FORCE_TEXT"
+ pulldown="false"
+ retarget="false"
+ state="false"
+ style="push"
+ toolbarPath="Normal"
+ tooltip="Reset all options of the volume">
+ </action>
+ <action
+ allowLabelUpdate="false"
class="com.gluster.storage.management.gui.actions.RebalanceVolumeAction"
definitionId="com.gluster.storage.management.gui.commands.RebalanceVolume"
icon="icons/volume-rebalance.png"
@@ -550,6 +588,38 @@
toolbarPath="Normal"
tooltip="Start Volume">
</action>
+ <action
+ allowLabelUpdate="false"
+ class="com.gluster.storage.management.gui.actions.RemoveDiskAction"
+ definitionId="com.gluster.storage.management.gui.commands.RemoveDisk"
+ icon="icons/disk.png"
+ id="com.gluster.storage.management.gui.actions.RemoveDiskAction"
+ label="Remove Disk"
+ menubarPath="com.gluster.storage.management.gui.menu.volume/volume"
+ mode="FORCE_TEXT"
+ pulldown="false"
+ retarget="false"
+ state="false"
+ style="push"
+ toolbarPath="Normal"
+ tooltip="Remove Disk from Volume">
+ </action>
+ <action
+ allowLabelUpdate="false"
+ class="com.gluster.storage.management.gui.actions.AddDiskAction"
+ definitionId="com.gluster.storage.management.gui.commands.AddDisk"
+ icon="icons/disk.png"
+ id="com.gluster.storage.management.gui.actions.AddDiskAction"
+ label="&amp;Add Disk"
+ menubarPath="com.gluster.storage.management.gui.menu.volume/volume"
+ mode="FORCE_TEXT"
+ pulldown="false"
+ retarget="false"
+ state="false"
+ style="push"
+ toolbarPath="Normal"
+ tooltip="Add Disk to Volume">
+ </action>
<menu
id="com.gluster.storage.management.gui.menu.volume"
label="&amp;Gluster"
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/Application.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/Application.java
index 50dbd314..47f2cfd9 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/Application.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/Application.java
@@ -25,6 +25,7 @@ import java.util.List;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
+import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Display;
@@ -43,6 +44,7 @@ public class Application implements IApplication {
public static final String PLUGIN_ID = "com.gluster.storage.management.gui";
private static Application instance;
private List<IEntityListener> entityListeners = Collections.synchronizedList(new ArrayList<IEntityListener>());
+ private IStatusLineManager statusLineManager;
public Application() {
instance = this;
@@ -52,6 +54,14 @@ public class Application implements IApplication {
return instance;
}
+ public IStatusLineManager getStatusLineManager() {
+ return statusLineManager;
+ }
+
+ public void setStatusLineManager(IStatusLineManager statusLineManager) {
+ this.statusLineManager = statusLineManager;
+ }
+
private boolean login() {
LoginDialog loginDialog = new LoginDialog(new Shell(Display.getDefault()));
return (loginDialog.open() == Window.OK);
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/ApplicationWorkbenchWindowAdvisor.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/ApplicationWorkbenchWindowAdvisor.java
index 722821f7..05d7443f 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/ApplicationWorkbenchWindowAdvisor.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/ApplicationWorkbenchWindowAdvisor.java
@@ -56,5 +56,7 @@ public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor {
public void postWindowCreate() {
super.postWindowCreate();
guiHelper.centerShellInScreen(getWindowConfigurer().getWindow().getShell());
+ Application.getApplication().setStatusLineManager(
+ getWindowConfigurer().getActionBarConfigurer().getStatusLineManager());
}
}
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/VolumeLogTableLabelProvider.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/VolumeLogTableLabelProvider.java
index 399cdc65..9858a25b 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/VolumeLogTableLabelProvider.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/VolumeLogTableLabelProvider.java
@@ -19,19 +19,11 @@
package com.gluster.storage.management.gui;
-import com.gluster.storage.management.core.model.Disk;
import com.gluster.storage.management.core.model.LogMessage;
import com.gluster.storage.management.core.utils.DateUtil;
-import com.gluster.storage.management.gui.utils.GUIHelper;
import com.gluster.storage.management.gui.views.details.VolumeLogsPage.LOG_TABLE_COLUMN_INDICES;
public class VolumeLogTableLabelProvider extends TableLabelProviderAdapter {
- private GUIHelper guiHelper = GUIHelper.getInstance();
-
- private String getFormattedDiskName(Disk disk) {
- return disk.getServerName() + ":" + disk.getName();
- }
-
@Override
public String getColumnText(Object element, int columnIndex) {
if (!(element instanceof LogMessage)) {
@@ -41,7 +33,7 @@ public class VolumeLogTableLabelProvider extends TableLabelProviderAdapter {
LogMessage logMessage = (LogMessage) element;
return (columnIndex == LOG_TABLE_COLUMN_INDICES.DATE.ordinal() ? DateUtil.formatDate(logMessage.getTimestamp())
: columnIndex == LOG_TABLE_COLUMN_INDICES.TIME.ordinal() ? DateUtil.formatTime(logMessage.getTimestamp())
- : columnIndex == LOG_TABLE_COLUMN_INDICES.DISK.ordinal() ? getFormattedDiskName(logMessage.getDisk())
+ : columnIndex == LOG_TABLE_COLUMN_INDICES.DISK.ordinal() ? logMessage.getDisk()
: columnIndex == LOG_TABLE_COLUMN_INDICES.SEVERITY.ordinal() ? "" + logMessage.getSeverity()
: columnIndex == LOG_TABLE_COLUMN_INDICES.MESSAGE.ordinal() ? logMessage.getMessage() : "Invalid");
}
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/AbstractActionDelegate.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/AbstractActionDelegate.java
index adc98f98..1c5a3c72 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/AbstractActionDelegate.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/AbstractActionDelegate.java
@@ -73,7 +73,7 @@ public abstract class AbstractActionDelegate implements IWorkbenchWindowActionDe
this.window = window;
}
- private Shell getShell() {
+ protected Shell getShell() {
if(window == null) {
return Display.getDefault().getActiveShell();
}
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/AddDiskAction.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/AddDiskAction.java
new file mode 100644
index 00000000..cc57c541
--- /dev/null
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/AddDiskAction.java
@@ -0,0 +1,84 @@
+/**
+ * AddDiskAction.java
+ *
+ * Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com>
+ * This file is part of Gluster Management Console.
+ *
+ * Gluster Management Console is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Gluster Management Console is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+package com.gluster.storage.management.gui.actions;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.widgets.Display;
+
+import com.gluster.storage.management.client.GlusterDataModelManager;
+import com.gluster.storage.management.core.model.Volume;
+import com.gluster.storage.management.gui.dialogs.AddDiskWizard;
+
+public class AddDiskAction extends AbstractActionDelegate {
+ private Volume volume;
+ private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance();
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#dispose()
+ */
+ @Override
+ public void dispose() {
+ window = null;
+ }
+
+ /* (non-Javadoc)
+ * @see com.gluster.storage.management.gui.actions.AbstractActionDelegate#performAction(org.eclipse.jface.action.IAction)
+ */
+ @Override
+ protected void performAction(IAction action) {
+ //TODO: open a dialog box
+ // MessageDialog.openInformation(getShell(), "Action captured", action.getDescription() + "\n" + volume.getName());
+ Display.getDefault().asyncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ AddDiskWizard wizard = new AddDiskWizard(volume); // Also add single page
+
+ WizardDialog dialog = new WizardDialog(getShell(), wizard);
+ dialog.create();
+ dialog.getShell().setSize(1024, 600);
+ dialog.open();
+ }
+ });
+ }
+
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.gluster.storage.management.gui.actions.AbstractActionDelegate#selectionChanged(org.eclipse.jface.action.IAction
+ * , org.eclipse.jface.viewers.ISelection)
+ */
+ @Override
+ public void selectionChanged(IAction action, ISelection selection) {
+ super.selectionChanged(action, selection);
+
+ if (selectedEntity instanceof Volume) {
+ this.volume = (Volume) selectedEntity;
+ // action.setEnabled(volume.getStatus() == VOLUME_STATUS.ONLINE);
+ }
+ }
+
+}
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/CreateVolumeAction.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/CreateVolumeAction.java
index 9ec500bc..7f06bc96 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/CreateVolumeAction.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/CreateVolumeAction.java
@@ -36,7 +36,7 @@ public class CreateVolumeAction extends AbstractActionDelegate {
public void run() {
CreateVolumeWizard wizard = new CreateVolumeWizard();
- WizardDialog dialog = new WizardDialog(window.getShell(), wizard);
+ WizardDialog dialog = new WizardDialog(getShell(), wizard);
dialog.create();
dialog.getShell().setSize(500, 550);
dialog.open();
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/DeleteVolumeAction.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/DeleteVolumeAction.java
index 82ac1663..d5501082 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/DeleteVolumeAction.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/DeleteVolumeAction.java
@@ -19,15 +19,80 @@
package com.gluster.storage.management.gui.actions;
import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ISelection;
+
+import com.gluster.storage.management.client.GlusterDataModelManager;
+import com.gluster.storage.management.client.VolumesClient;
+import com.gluster.storage.management.core.model.Status;
+import com.gluster.storage.management.core.model.Volume;
+import com.gluster.storage.management.core.model.Volume.VOLUME_STATUS;
+import com.gluster.storage.management.gui.IImageKeys;
+import com.gluster.storage.management.gui.utils.GUIHelper;
public class DeleteVolumeAction extends AbstractActionDelegate {
+ private Volume volume;
+ private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance();
+
@Override
protected void performAction(IAction action) {
- System.out.println("Running [" + this.getClass().getSimpleName() + "]");
+ final String actionDesc = action.getDescription();
+
+ String warningMessage;
+ if (volume.getStatus() == VOLUME_STATUS.OFFLINE) {
+ warningMessage = "Are you sure to delete the Volume[" + volume.getName() + "] ?";
+ } else {
+ warningMessage = "Volume [" + volume.getName() + "] is online, \nAre you sure to continue?";
+ }
+
+ Integer deleteOption = new MessageDialog(getShell(), "Delete Volume", GUIHelper.getInstance().getImage(
+ IImageKeys.VOLUME), warningMessage, MessageDialog.QUESTION, new String[] { "Cancel",
+ "Delete volume and it's data", "Delete volume, keep back-up of data" }, 2).open();
+ if (deleteOption == 0) {
+ return;
+ }
+
+ VolumesClient client = new VolumesClient(modelManager.getSecurityToken());
+
+ Status status;
+ if (volume.getStatus() == VOLUME_STATUS.ONLINE) { // To stop the volume service, if running
+ status = client.stopVolume(volume.getName());
+ if (!status.isSuccess()) {
+ showErrorDialog(actionDesc, "Volume [" + volume.getName() + "] could not be stopped! Error: [" + status
+ + "]");
+ return;
+ }
+ }
+ String confirmDelete = "";
+ if (deleteOption == 1) {
+ confirmDelete = "-d";
+ }
+
+ status = client.deleteVolume(volume, confirmDelete);
+ if (status.isSuccess()) {
+ showInfoDialog(actionDesc, "Volume [" + volume.getName() + "] deleted successfully!");
+ modelManager.deleteVolume(volume);
+ } else {
+ if (status.isPartSuccess()) {
+ showWarningDialog(actionDesc, "Volume deleted, but following error(s) occured: " + status);
+ modelManager.deleteVolume(volume);
+ } else {
+ showErrorDialog(actionDesc,
+ "Volume [" + volume.getName() + "] could not be deleted! Error: [" + status + "]");
+ }
+ }
}
@Override
public void dispose() {
System.out.println("Disposing [" + this.getClass().getSimpleName() + "]");
}
+
+ @Override
+ public void selectionChanged(IAction action, ISelection selection) {
+ super.selectionChanged(action, selection);
+ if (selectedEntity instanceof Volume) {
+ volume = (Volume) selectedEntity;
+ }
+ }
}
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/IActionSetIDs.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/IActionConstants.java
index 6c54bae8..2f696709 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/IActionSetIDs.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/IActionConstants.java
@@ -18,7 +18,7 @@
*******************************************************************************/
package com.gluster.storage.management.gui.actions;
-public interface IActionSetIDs {
+public interface IActionConstants {
public static final String ACTION_SET_CLUSTER = "com.gluster.storage.management.gui.actionsets.gluster";
public static final String ACTION_SET_VOLUMES = "com.gluster.storage.management.gui.actionsets.volumes";
public static final String ACTION_SET_VOLUME = "com.gluster.storage.management.gui.actionsets.volume";
@@ -30,4 +30,7 @@ public interface IActionSetIDs {
public static final String ACTION_SET_DISCOVERED_SERVER = "com.gluster.storage.management.gui.actionsets.serverdiscovered";
public static final String ACTION_SET_EDIT = "com.gluster.storage.management.gui.actionsets.edit";
+
+ public static final String COMMAND_CREATE_VOLUME = "com.gluster.storage.management.gui.commands.CreateVolume";
+ public static final String COMMAND_ADD_SERVER = "com.gluster.storage.management.gui.commands.AddServer";
}
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/RemoveDiskAction.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/RemoveDiskAction.java
new file mode 100644
index 00000000..19cf84f7
--- /dev/null
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/RemoveDiskAction.java
@@ -0,0 +1,65 @@
+package com.gluster.storage.management.gui.actions;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IWorkbenchPart;
+
+import com.gluster.storage.management.client.GlusterDataModelManager;
+import com.gluster.storage.management.client.VolumesClient;
+import com.gluster.storage.management.core.model.Disk;
+import com.gluster.storage.management.core.model.Entity;
+import com.gluster.storage.management.core.model.Volume;
+import com.gluster.storage.management.gui.utils.GUIHelper;
+import com.gluster.storage.management.gui.views.VolumeDisksView;
+
+public class RemoveDiskAction extends AbstractActionDelegate {
+ private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance();
+ private GUIHelper guiHelper = GUIHelper.getInstance();
+ private List<Disk> disks;
+
+ @Override
+ protected void performAction(IAction action) {
+ VolumesClient client = new VolumesClient(modelManager.getSecurityToken());
+ // final Status status = client.removeDisk();
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ @Override
+ public void selectionChanged(IAction action, ISelection selection) {
+ super.selectionChanged(action, selection);
+
+ action.setEnabled(false);
+ Volume selectedVolume = (Volume)guiHelper.getSelectedEntity(window, Volume.class);
+ if (selectedVolume != null) {
+ // a volume is selected on navigation tree. Let's check if the currently open view is volume disks view
+ IWorkbenchPart view = guiHelper.getActiveView();
+ if(view instanceof VolumeDisksView) {
+ // volume disks view is open. check if any disk is selected
+ disks = getSelectedDisks(selection);
+ action.setEnabled(disks.size() > 0);
+ }
+ }
+ }
+
+ private List<Disk> getSelectedDisks(ISelection selection) {
+ List<Disk> selectedDisks = new ArrayList<Disk>();
+ if (selection instanceof IStructuredSelection) {
+ Iterator<Object> iter = ((IStructuredSelection) selection).iterator();
+ while (iter.hasNext()) {
+ Object selectedObj = iter.next();
+ if (selectedObj instanceof Disk) {
+ selectedDisks.add((Disk)selectedObj);
+ }
+ }
+ }
+ return selectedDisks;
+ }
+} \ No newline at end of file
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/ResetVolumeOptionsAction.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/ResetVolumeOptionsAction.java
new file mode 100644
index 00000000..7fd77ea8
--- /dev/null
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/ResetVolumeOptionsAction.java
@@ -0,0 +1,64 @@
+package com.gluster.storage.management.gui.actions;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+import com.gluster.storage.management.client.GlusterDataModelManager;
+import com.gluster.storage.management.client.VolumesClient;
+import com.gluster.storage.management.core.model.Status;
+import com.gluster.storage.management.core.model.Volume;
+import com.gluster.storage.management.core.model.Volume.VOLUME_STATUS;
+
+public class ResetVolumeOptionsAction extends AbstractActionDelegate {
+ private Volume volume;
+ private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance();
+
+ @Override
+ public void dispose() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ protected void performAction(IAction action) {
+ final String actionDesc = action.getDescription();
+
+ boolean confirmed = showConfirmDialog(actionDesc,
+ "Are you sure you want to reset all options of the volume [" + volume.getName() + "] ?");
+ if (!confirmed) {
+ return;
+ }
+
+ final Status status = resetVolumeOptions();
+ if (status.isSuccess()) {
+ showInfoDialog(actionDesc, "Volume options for [" + volume.getName() + "] reset successfully!");
+ modelManager.resetVolumeOptions(volume);
+ } else {
+ showErrorDialog(actionDesc, "Volume options for [" + volume.getName() + "] could not be reset! Error: [" + status
+ + "]");
+ }
+ }
+
+ private Status resetVolumeOptions() {
+ return new VolumesClient(modelManager.getSecurityToken()).resetVolumeOptions(volume.getName());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.gluster.storage.management.gui.actions.AbstractActionDelegate#selectionChanged(org.eclipse.jface.action.IAction
+ * , org.eclipse.jface.viewers.ISelection)
+ */
+ @Override
+ public void selectionChanged(IAction action, ISelection selection) {
+ super.selectionChanged(action, selection);
+
+ if (selectedEntity instanceof Volume) {
+ volume = (Volume) selectedEntity;
+ action.setEnabled(volume.getOptions().size() > 0);
+ }
+ }
+}
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/AddDiskPage.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/AddDiskPage.java
new file mode 100644
index 00000000..7eb107c0
--- /dev/null
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/AddDiskPage.java
@@ -0,0 +1,165 @@
+/**
+ * AddDiskPage.java
+ *
+ * Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com>
+ * This file is part of Gluster Management Console.
+ *
+ * Gluster Management Console is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Gluster Management Console is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+package com.gluster.storage.management.gui.dialogs;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+
+import com.gluster.storage.management.client.GlusterDataModelManager;
+import com.gluster.storage.management.core.model.Disk;
+import com.gluster.storage.management.core.model.Volume;
+import com.gluster.storage.management.core.model.Volume.VOLUME_TYPE;
+import com.richclientgui.toolbox.duallists.DualListComposite.ListContentChangedListener;
+import com.richclientgui.toolbox.duallists.IRemovableContentProvider;
+
+/**
+ * @author root
+ *
+ */
+public class AddDiskPage extends WizardPage {
+ private List<Disk> availableDisks = new ArrayList<Disk>();
+ private List<Disk> selectedDisks = new ArrayList<Disk>();
+ private Volume volume = null;
+ private DisksSelectionPage page = null;
+
+
+ public static final String PAGE_NAME = "add.disk.volume.page";
+
+ /**
+ * @param pageName
+ */
+ protected AddDiskPage(Volume volume) {
+ super(PAGE_NAME);
+ this.volume = volume;
+ setTitle("Add Disk");
+
+ String description = "Add disks to the Volume by choosing disks from the cluster servers.\n";
+ if ( volume.getVolumeType() == VOLUME_TYPE.DISTRIBUTED_MIRROR) {
+ description += "(Disk selection should be multiples of " + volume.getReplicaCount() + ")";
+ } else if (volume.getVolumeType() == VOLUME_TYPE.DISTRIBUTED_STRIPE) {
+ description += "(Disk selection should be multiples of " + volume.getStripeCount() + ")";
+ }
+ setDescription(description);
+
+ availableDisks = getAvailableDisks(volume);
+
+ setPageComplete(false);
+ setErrorMessage("Please select disks to be added to the volume.");
+ }
+
+
+ private boolean isDiskUsed(Volume volume, Disk disk){
+ for (String volumeDisk : volume.getDisks()) { // expected form of volumeDisk is "server:diskName"
+ if ( disk.getQualifiedName().equals(volumeDisk)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected List<Disk> getAvailableDisks(Volume volume) {
+ List<Disk> availableDisks = new ArrayList<Disk>();
+ for (Disk disk : GlusterDataModelManager.getInstance().getReadyDisksOfAllServers()) {
+ if ( ! isDiskUsed(volume, disk) ) {
+ availableDisks.add(disk);
+ }
+ }
+ return availableDisks;
+ }
+
+
+ public List<Disk> getChosenDisks( ) {
+ return page.getChosenDisks();
+ }
+
+ private boolean isValidDiskSelection(int diskCount) {
+ if ( diskCount == 0) {
+ return false;
+ }
+ switch (volume.getVolumeType()) {
+ case DISTRIBUTED_MIRROR:
+ return (diskCount % volume.getReplicaCount() == 0);
+ case DISTRIBUTED_STRIPE:
+ return (diskCount % volume.getStripeCount() == 0);
+ }
+ return true;
+ }
+
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ public void createControl(Composite parent) {
+ getShell().setText("Add Disk");
+ List<Disk> chosenDisks = new ArrayList<Disk>(); // or volume.getDisks();
+
+ page = new DisksSelectionPage(parent, SWT.NONE, availableDisks, chosenDisks);
+ page.addDiskSelectionListener(new ListContentChangedListener<Disk>() {
+ @Override
+ public void listContentChanged(IRemovableContentProvider<Disk> contentProvider) {
+ List<Disk> newChosenDisks = page.getChosenDisks();
+
+ // validate chosen disks
+ if(isValidDiskSelection(newChosenDisks.size())) {
+ clearError();
+ } else {
+ setError();
+ }
+ }
+ });
+ setControl(page);
+ }
+
+ private void setError() {
+ String errorMessage = null;
+ if ( volume.getVolumeType() == VOLUME_TYPE.PLAIN_DISTRIBUTE) {
+ errorMessage = "Please select at least one disk!";
+ } else if( volume.getVolumeType() == VOLUME_TYPE.DISTRIBUTED_MIRROR) {
+ errorMessage = "Please select disks in multiples of " + volume.getReplicaCount();
+ } else {
+ errorMessage = "Please select disks in multiples of " + volume.getStripeCount();
+ }
+
+ setPageComplete(false);
+ setErrorMessage(errorMessage);
+ }
+
+ private void clearError() {
+ setErrorMessage(null);
+ setPageComplete(true);
+ }
+
+ public DisksSelectionPage getDialogPage() {
+ return this.page;
+ }
+
+ public void setPageComplete() {
+
+ }
+
+}
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/AddDiskWizard.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/AddDiskWizard.java
new file mode 100644
index 00000000..e9608e38
--- /dev/null
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/AddDiskWizard.java
@@ -0,0 +1,92 @@
+/**
+ * AddDiskWizard.java
+ *
+ * Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com>
+ * This file is part of Gluster Management Console.
+ *
+ * Gluster Management Console is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Gluster Management Console is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+package com.gluster.storage.management.gui.dialogs;
+
+
+import java.util.List;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.wizard.Wizard;
+
+import com.gluster.storage.management.client.GlusterDataModelManager;
+import com.gluster.storage.management.client.VolumesClient;
+import com.gluster.storage.management.core.model.Disk;
+import com.gluster.storage.management.core.model.Status;
+import com.gluster.storage.management.core.model.Volume;
+import com.gluster.storage.management.core.utils.GlusterCoreUtil;
+
+
+/**
+ *
+ */
+public class AddDiskWizard extends Wizard {
+ private AddDiskPage page;
+ private Volume volume;
+
+
+ public AddDiskWizard(Volume volume) {
+ setWindowTitle("Gluster Management Console - Add disk");
+ setHelpAvailable(false); // TODO: Introduce wizard help
+ this.volume = volume;
+ }
+
+ public void addPages() {
+ page = new AddDiskPage(volume);
+ addPage(page);
+ }
+
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish() {
+ List<Disk> disks = page.getChosenDisks();
+ VolumesClient volumeClient = new VolumesClient(GlusterDataModelManager.getInstance().getSecurityToken());
+ try {
+ Status status = volumeClient.addDisks(volume.getName(), disks);
+ if (!status.isSuccess()) {
+ MessageDialog.openError(getShell(), "Add disk(s) to Volume", status.getMessage());
+ return status.isSuccess();
+ } else {
+ volume.addDisks(GlusterCoreUtil.getQualifiedDiskNames(disks));
+ MessageDialog.openInformation(getShell(), "Add disk(s) to Volume", "Disk(s) are successfully added to "
+ + volume.getName());
+ return status.isSuccess();
+ }
+ } catch (Exception e) {
+ MessageDialog.openError(getShell(), "Add disk(s) to Volume", e.getMessage());
+ return false;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.wizard.Wizard#canFinish()
+ */
+ @Override
+ public boolean canFinish() {
+ return super.canFinish() && page.isPageComplete();
+ }
+}
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..e319dcca 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
@@ -25,7 +25,6 @@ import java.util.Set;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.window.Window;
@@ -51,6 +50,7 @@ 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.TRANSPORT_TYPE;
import com.gluster.storage.management.core.model.Volume.VOLUME_TYPE;
+import com.gluster.storage.management.core.utils.ValidationUtil;
public class CreateVolumePage1 extends WizardPage {
public static final String PAGE_NAME = "create.volume.page.1";
@@ -62,7 +62,6 @@ public class CreateVolumePage1 extends WizardPage {
private Button btnNfs;
private Button btnStartVolume;
private Link linkCustomize;
- private ValidationListener valListener = new ValidationListener();
/**
* Create the wizard.
@@ -85,123 +84,102 @@ public class CreateVolumePage1 extends WizardPage {
return disks;
}
- private class ValidationListener implements ModifyListener {
- /* (non-Javadoc)
- * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
- */
- @Override
- public void modifyText(ModifyEvent e) {
- String volumeName = txtName.getText().trim();
- String accessControl = txtAccessControl.getText().trim();
- String volumeNameToken = "^[a-zA-Z][a-zA-Z0-9\\-]*";
-
-
- setErrorMessage(null);
- setPageComplete(true);
-
- if(volumeName.length() == 0) {
- setPageComplete(false);
- setErrorMessage("Please enter Volume Name");
- }
-
- if (!volumeName.matches(volumeNameToken)) {
- setPageComplete(false);
- setErrorMessage("Please enter valid Volume Name");
- }
-
- if(accessControl.length() == 0) {
- setPageComplete(false);
- setErrorMessage("Please enter Access Control");
- }
-
-
- // acl validation
- String[] aclList = accessControl.split(",");
- for (String ip : aclList) {
- if (!isValidIP(ip)) {
- setPageComplete(false);
- setErrorMessage("Please enter valid access control list");
- }
- }
-
- }
-
- private Boolean isValidIP(String ip) {
- // String pattern = "^.[0-9]{1,3}/..[0-9]{1,3}/..[0-9]{1,3}/..[0-9]{1,3}";
- String pattern = "^.[0-9]{1,3}/.";
- if (ip == "*") {
- return true;
- }
- String[] ipQuads = ip.split(".");
- for (String quad : ipQuads) {
- if (!quad.matches(pattern)) {
- return false;
- }
- }
- return true;
-
- }
- }
-
/**
* Create contents of the wizard.
* @param parent
*/
public void createControl(Composite parent) {
-
setPageComplete(false);
- Composite container = new Composite(parent, SWT.NULL);
-
- setControl(container);
- GridLayout gl_container = new GridLayout(2, false);
- gl_container.verticalSpacing = 10;
- gl_container.marginHeight = 10;
- gl_container.marginLeft = 20;
- gl_container.horizontalSpacing = 10;
- container.setLayout(gl_container);
+ Composite container = createContainer(parent);
- new Label(container, SWT.NONE);
- new Label(container, SWT.NONE);
+ createEmptyRow(container);
- Label lblName = new Label(container, SWT.NONE);
- lblName.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
- lblName.setText("Name: ");
+ createNameLabel(container);
+ createNameText(container);
- txtName = new Text(container, SWT.BORDER);
- GridData txtNameData = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1);
- txtNameData.widthHint = 300;
- txtName.setLayoutData(txtNameData);
- txtName.addModifyListener(valListener);
+ createTypeLabel(container);
+ createTypeCombo(container);
- Label lblType = new Label(container, SWT.NONE);
- lblType.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
- lblType.setText("Type: ");
+ createTransportTypeLabel(container);
+ createTransportTypeValueLabel(container);
- typeComboViewer = new ComboViewer(container, SWT.READ_ONLY);
- Combo typeCombo = typeComboViewer.getCombo();
- GridData typeComboData = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1);
- typeCombo.setLayoutData(typeComboData);
- typeComboViewer.setContentProvider(new ArrayContentProvider());
- typeComboViewer.setInput(Volume.VOLUME_TYPE.values());
- typeCombo.select(VOLUME_TYPE.PLAIN_DISTRIBUTE.ordinal()); // default type = Plain Distribute
- typeComboViewer.setLabelProvider(new LabelProvider() {
- @Override
- public String getText(Object element) {
- VOLUME_TYPE volumeType = (VOLUME_TYPE)element;
- return Volume.getVolumeTypeStr(volumeType);
- }
- });
+ createDisksLabel(container);
+ createDisksCustomizeLink(container);
- Label lblTransportType = new Label(container, SWT.NONE);
- lblTransportType.setText("Transport Type: ");
+ createNasProtocolLabel(container);
+ createNasProtocolCheckboxes(container);
- Label lblEthernet = new Label(container, SWT.NONE);
- lblEthernet.setText("Ethernet");
+ createAccessControlLabel(container);
+ createAccessControlText(container);
+
+ createEmptyLabel(container);
+ createAccessControlInfoLabel(container);
- Label lblDisks = new Label(container, SWT.RIGHT);
- lblDisks.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
- lblDisks.setText("Disks: ");
+ createStartVolumeLabel(container);
+ createStartVolumeCheckbox(container);
+ }
+
+ private void createStartVolumeCheckbox(Composite container) {
+ btnStartVolume = new Button(container, SWT.CHECK);
+ btnStartVolume.setSelection(true);
+ }
+
+ private void createStartVolumeLabel(Composite container) {
+ Label lblStartVolume = new Label(container, SWT.NONE);
+ lblStartVolume.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ lblStartVolume.setText("Start Volume: ");
+ }
+
+ private void createAccessControlInfoLabel(Composite container) {
+ 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/hostnames)");
+ }
+
+ private void createEmptyLabel(Composite container) {
+ new Label(container, SWT.NONE);
+ }
+
+ private void createAccessControlText(Composite container) {
+ txtAccessControl = new Text(container, SWT.BORDER);
+ txtAccessControl.setText("*");
+ GridData accessControlData = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1);
+ accessControlData.widthHint = 300;
+ txtAccessControl.setLayoutData(accessControlData);
+ txtAccessControl.addModifyListener(new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ validateForm();
+ }
+ });
+ }
+
+ private void createAccessControlLabel(Composite container) {
+ Label lblAccessControl = new Label(container, SWT.NONE);
+ lblAccessControl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ lblAccessControl.setText("Access Control: ");
+ }
+
+ private void createNasProtocolCheckboxes(Composite container) {
+ Button btnGluster = new Button(container, SWT.CHECK);
+ btnGluster.setEnabled(false);
+ btnGluster.setSelection(true);
+ btnGluster.setText("Gluster");
+ createEmptyLabel(container);
+ btnNfs = new Button(container, SWT.CHECK);
+ btnNfs.setEnabled(false);
+ btnNfs.setSelection(true);
+ btnNfs.setText("NFS");
+ }
+
+ private void createNasProtocolLabel(Composite container) {
+ Label lblNasProtocol = new Label(container, SWT.RIGHT);
+ lblNasProtocol.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ lblNasProtocol.setText("NAS Protocol: ");
+ }
+
+ private void createDisksCustomizeLink(Composite container) {
linkCustomize = new Link(container, SWT.UNDERLINE_LINK);
linkCustomize.setText("All Disk(s) (<a>customize</a>)");
linkCustomize.addListener (SWT.Selection, new Listener () {
@@ -222,43 +200,82 @@ public class CreateVolumePage1 extends WizardPage {
});
}
});
-
- Label lblNasProtocol = new Label(container, SWT.RIGHT);
- lblNasProtocol.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
- lblNasProtocol.setText("NAS Protocol: ");
-
- Button btnGluster = new Button(container, SWT.CHECK);
- btnGluster.setEnabled(false);
- btnGluster.setSelection(true);
- btnGluster.setText("Gluster");
- new Label(container, SWT.NONE);
-
- btnNfs = new Button(container, SWT.CHECK);
- btnNfs.setSelection(true);
- btnNfs.setText("NFS");
-
- Label lblAccessControl = new Label(container, SWT.NONE);
- lblAccessControl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
- lblAccessControl.setText("Access Control: ");
-
- txtAccessControl = new Text(container, SWT.BORDER);
- txtAccessControl.setText("*");
- GridData accessControlData = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1);
- accessControlData.widthHint = 300;
- txtAccessControl.setLayoutData(accessControlData);
- txtAccessControl.addModifyListener(valListener);
+ }
- 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)");
-
- Label lblStartVolume = new Label(container, SWT.NONE);
- lblStartVolume.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
- lblStartVolume.setText("Start Volume: ");
-
- btnStartVolume = new Button(container, SWT.CHECK);
- btnStartVolume.setSelection(true);
+ private void createDisksLabel(Composite container) {
+ Label lblDisks = new Label(container, SWT.RIGHT);
+ lblDisks.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ lblDisks.setText("Disks: ");
+ }
+
+ private void createTransportTypeValueLabel(Composite container) {
+ Label lblEthernet = new Label(container, SWT.NONE);
+ lblEthernet.setText("Ethernet");
+ }
+
+ private void createTransportTypeLabel(Composite container) {
+ Label lblTransportType = new Label(container, SWT.NONE);
+ lblTransportType.setText("Transport Type: ");
+ }
+
+ private void createTypeCombo(Composite container) {
+ typeComboViewer = new ComboViewer(container, SWT.READ_ONLY);
+ Combo typeCombo = typeComboViewer.getCombo();
+ GridData typeComboData = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1);
+ typeCombo.setLayoutData(typeComboData);
+ typeComboViewer.setContentProvider(new ArrayContentProvider());
+ typeComboViewer.setInput(Volume.VOLUME_TYPE.values());
+ typeCombo.select(VOLUME_TYPE.PLAIN_DISTRIBUTE.ordinal()); // default type = Plain Distribute
+ typeComboViewer.setLabelProvider(new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ VOLUME_TYPE volumeType = (VOLUME_TYPE)element;
+ return Volume.getVolumeTypeStr(volumeType);
+ }
+ });
+ }
+
+ private void createTypeLabel(Composite container) {
+ Label lblType = new Label(container, SWT.NONE);
+ lblType.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ lblType.setText("Type: ");
+ }
+
+ private void createNameText(Composite container) {
+ txtName = new Text(container, SWT.BORDER);
+ GridData txtNameData = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1);
+ txtNameData.widthHint = 300;
+ txtName.setLayoutData(txtNameData);
+ txtName.addModifyListener(new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ validateForm();
+ }
+ });
+ }
+
+ private void createNameLabel(Composite container) {
+ Label lblName = new Label(container, SWT.NONE);
+ lblName.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ lblName.setText("Name: ");
+ }
+
+ private void createEmptyRow(Composite container) {
+ createEmptyLabel(container);
+ createEmptyLabel(container);
+ }
+
+ private Composite createContainer(Composite parent) {
+ Composite container = new Composite(parent, SWT.NULL);
+ setControl(container);
+
+ GridLayout gl_container = new GridLayout(2, false);
+ gl_container.verticalSpacing = 10;
+ gl_container.marginHeight = 10;
+ gl_container.marginLeft = 20;
+ gl_container.horizontalSpacing = 10;
+ container.setLayout(gl_container);
+ return container;
}
public Volume getVolume() {
@@ -270,28 +287,86 @@ public class CreateVolumePage1 extends WizardPage {
volume.setTransportType(TRANSPORT_TYPE.ETHERNET);
Set<NAS_PROTOCOL> nasProtocols = new HashSet<Volume.NAS_PROTOCOL>();
nasProtocols.add(NAS_PROTOCOL.GLUSTERFS);
- if(btnNfs.getSelection()) {
- nasProtocols.add(NAS_PROTOCOL.NFS);
- }
+ nasProtocols.add(NAS_PROTOCOL.NFS);
volume.setAccessControlList(txtAccessControl.getText());
return volume;
}
- public Boolean getStartVolumeRequest() {
+ public Boolean startVolumeAfterCreation() {
return btnStartVolume.getSelection();
}
- public Boolean isValidCreateVolumeForm() {
- IStructuredSelection selection = (IStructuredSelection)typeComboViewer.getSelection();
- if (selection.getFirstElement().equals(VOLUME_TYPE.DISTRIBUTED_MIRROR) && ((int)volume.getDisks().size()) % 2 != 0 ) {
- setErrorMessage("Mirror type volume requires disk in multiples of two");
- return false;
- } else if(selection.getFirstElement().equals(VOLUME_TYPE.DISTRIBUTED_STRIPE) && ((int)volume.getDisks().size()) % 4 != 0) {
- setErrorMessage("Stripe type volume requires disk in multiples of four");
- return false;
+ public Boolean volumeExists(String volumeName) {
+ List<Volume> volumes = GlusterDataModelManager.getInstance().getModel().getCluster().getVolumes();
+ for (Volume volume : volumes) {
+ if (volume.getName().equals(volumeName)) {
+ setErrorMessage("Volume name already exists.");
+ return false;
+ }
}
return true;
}
+
+ private void validateForm() {
+ clearErrors();
+ validateVolumeName();
+ validateAccessControl();
+ validateDisks();
+ }
+
+ private void validateDisks() {
+ int diskCount = volume.getDisks().size();
+
+ if(diskCount < 1) {
+ setError("At least one disk must be selected!");
+ }
+
+ VOLUME_TYPE volumeType = (VOLUME_TYPE) ((IStructuredSelection) typeComboViewer
+ .getSelection()).getFirstElement();
+ if (volumeType == VOLUME_TYPE.DISTRIBUTED_MIRROR && diskCount % 2 != 0) {
+ setError("Mirror type volume requires disks in multiples of two");
+ } else if (volumeType == VOLUME_TYPE.DISTRIBUTED_STRIPE && diskCount % 4 != 0) {
+ setError("Stripe type volume requires disks in multiples of four");
+ }
+ }
+
+ private void validateAccessControl() {
+ String accessControl = txtAccessControl.getText().trim();
+ if (accessControl.length() == 0) {
+ setError("Please enter Access Control");
+ return;
+ }
+
+ if (!ValidationUtil.isValidAccessControl(accessControl)) {
+ setError("Access control list must be a comma separated list of IP addresses/Host names. Please enter a valid value!");
+ }
+ }
+
+ private void validateVolumeName() {
+ String volumeName = txtName.getText().trim();
+ String volumeNameToken = "^[a-zA-Z][a-zA-Z0-9\\-]*";
+ if(volumeName.length() == 0) {
+ setError("Please enter Volume Name");
+ }
+
+ if (!volumeName.matches(volumeNameToken)) {
+ setError("Please enter valid Volume Name");
+ }
+
+ if(!volumeExists(volumeName)) {
+ setError("Volume [" + volumeName + "] already exists!");
+ }
+ }
+
+ private void clearErrors() {
+ setErrorMessage(null);
+ setPageComplete(true);
+ }
+
+ private void setError(String errorMsg) {
+ setPageComplete(false);
+ setErrorMessage(errorMsg);
+ }
}
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/CreateVolumeWizard.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/CreateVolumeWizard.java
index 36755bec..66c26a89 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/CreateVolumeWizard.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/CreateVolumeWizard.java
@@ -38,33 +38,46 @@ public class CreateVolumeWizard extends Wizard {
public void addPages() {
addPage(new CreateVolumePage1());
}
-
+
@Override
public boolean performFinish() {
+ String dialogTitle = "Create Volume";
CreateVolumePage1 page = (CreateVolumePage1) getPage(CreateVolumePage1.PAGE_NAME);
- if (!page.isValidCreateVolumeForm()) {
- return false;
- }
-
+
Volume newVolume = page.getVolume();
GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance();
VolumesClient volumesClient = new VolumesClient(modelManager.getSecurityToken());
Status status = volumesClient.createVolume(newVolume);
-
+
if (status.isSuccess()) {
+ String message = "Volume created successfully!";
+ boolean warning = false;
newVolume.setStatus(VOLUME_STATUS.OFFLINE);
- if (page.getStartVolumeRequest()) {
+ if (page.startVolumeAfterCreation()) {
Status volumeStartStatus = volumesClient.startVolume(newVolume.getName());
if (volumeStartStatus.isSuccess()) {
newVolume.setStatus(VOLUME_STATUS.ONLINE);
+ message = "Volume created and started successfully!";
+ } else {
+ message = "Volume created successfuly, but couldn't be started. Error: " + volumeStartStatus;
+ warning = true;
}
}
- //update the model
- modelManager.addVolume(newVolume);
- MessageDialog.openInformation(getShell(), "Create Volume", "Volume created successfully and configuration added!");
+
+ // update the model
+ modelManager.addVolume(newVolume);
+ if (warning) {
+ MessageDialog.openWarning(getShell(), dialogTitle, message);
+ } else {
+ MessageDialog.openInformation(getShell(), dialogTitle, message);
+ }
} else {
- MessageDialog.openError(getShell(), "Create Volume", "Volume creation failed! [" + status.getCode() + "]["
- + status.getMessage() + "]");
+ if (status.isPartSuccess()) {
+ MessageDialog.openWarning(getShell(), dialogTitle, "Volume created, but following error(s) occured: "
+ + status);
+ } else {
+ MessageDialog.openError(getShell(), dialogTitle, "Volume creation failed! " + status);
+ }
}
return true;
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/CreateVolumeDisksPage.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/DisksSelectionPage.java
index c78601d9..e50e81a7 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/CreateVolumeDisksPage.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/DisksSelectionPage.java
@@ -19,7 +19,6 @@
package com.gluster.storage.management.gui.dialogs;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import org.eclipse.jface.viewers.ITableLabelProvider;
@@ -48,7 +47,7 @@ import com.richclientgui.toolbox.duallists.IRemovableContentProvider;
import com.richclientgui.toolbox.duallists.RemovableContentProvider;
import com.richclientgui.toolbox.duallists.TableColumnData;
-public class CreateVolumeDisksPage extends Composite {
+public class DisksSelectionPage extends Composite {
private enum DISK_TABLE_COLUMN_INDICES {
SERVER, DISK, SPACE, SPACE_USED
}
@@ -67,13 +66,17 @@ public class CreateVolumeDisksPage extends Composite {
private Button btnDown;
- public CreateVolumeDisksPage(final Composite parent, int style, List<Disk> allDisks, List<Disk> selectedDisks) {
+ public DisksSelectionPage(final Composite parent, int style, List<Disk> allDisks, List<Disk> selectedDisks) {
super(parent, style);
createPage(allDisks, selectedDisks);
parent.layout();
}
+
+ public void addDiskSelectionListener(ListContentChangedListener<Disk> listener) {
+ dualTableViewer.addChosenListChangedSelectionListener(listener);
+ }
private TableLabelProviderAdapter getDiskLabelProvider() {
return new TableLabelProviderAdapter() {
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/SelectDisksDialog.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/SelectDisksDialog.java
index 0ec19d5b..08a35357 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/SelectDisksDialog.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/dialogs/SelectDisksDialog.java
@@ -24,7 +24,6 @@ import java.util.List;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.osgi.framework.internal.core.Msg;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
@@ -33,13 +32,11 @@ import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
-import com.gluster.storage.management.client.GlusterDataModelManager;
import com.gluster.storage.management.core.model.Disk;
-import com.gluster.storage.management.core.model.Volume;
public class SelectDisksDialog extends Dialog {
- private CreateVolumeDisksPage disksPage;
+ private DisksSelectionPage disksPage;
private List<Disk> allDisks;
private List<Disk> selectedDisks;
@@ -58,9 +55,9 @@ public class SelectDisksDialog extends Dialog {
private List<Disk> getSelectedDisks(List<Disk> allDisks, List<String> selectedDisks) {
List<Disk> disks = new ArrayList<Disk>();
for (String selectedDisk : selectedDisks) {
- for (Disk disk : disks) {
+ for (Disk disk : allDisks) {
String brick[] = selectedDisk.split(":");
- if (disk.getServerName() == brick[0] && disk.getName() == brick[1]) {
+ if (disk.getServerName().equals(brick[0]) && disk.getName().equals(brick[1])) {
disks.add(disk);
}
}
@@ -78,14 +75,12 @@ public class SelectDisksDialog extends Dialog {
Composite container = new Composite(parent, SWT.NONE);
GridLayout containerLayout = new GridLayout(2, false);
container.setLayout(containerLayout);
- GridData containerLayoutData = new GridData(SWT.FILL, SWT.FILL, true,
- true);
+ GridData containerLayoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
container.setLayoutData(containerLayoutData);
getShell().setText("Create Volume - Select Disks");
- disksPage = new CreateVolumeDisksPage(container, SWT.NONE, allDisks,
- selectedDisks);
+ disksPage = new DisksSelectionPage(container, SWT.NONE, allDisks, selectedDisks);
return container;
}
@@ -97,10 +92,8 @@ public class SelectDisksDialog extends Dialog {
*/
@Override
protected void createButtonsForButtonBar(Composite parent) {
- createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
- true);
- createButton(parent, IDialogConstants.CANCEL_ID,
- IDialogConstants.CANCEL_LABEL, false);
+ createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
+ createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
}
/**
@@ -112,15 +105,14 @@ public class SelectDisksDialog extends Dialog {
}
@Override
- protected void cancelPressed() {
+ protected void cancelPressed() { System.out.println("Test");
super.cancelPressed();
}
@Override
protected void okPressed() {
if (this.getSelectedDisks().size() == 0) {
- MessageDialog.openError(getShell(), "Select Disk(s)",
- "Please select atlease one disk");
+ MessageDialog.openError(getShell(), "Select Disk(s)", "Please select atlease one disk");
} else {
super.okPressed();
}
@@ -129,7 +121,7 @@ public class SelectDisksDialog extends Dialog {
public List<Disk> getSelectedDisks() {
return disksPage.getChosenDisks();
}
-
+
public List<String> getSelectedBricks() {
return disksPage.getChosenBricks();
}
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 30406e27..c6ffa8d5 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
@@ -41,8 +41,6 @@ import org.eclipse.swt.widgets.Text;
import com.gluster.storage.management.client.GlusterDataModelManager;
import com.gluster.storage.management.client.UsersClient;
-import com.gluster.storage.management.client.constants.ClientConstants;
-import com.gluster.storage.management.core.exceptions.GlusterRuntimeException;
import com.gluster.storage.management.core.model.ConnectionDetails;
import com.gluster.storage.management.gui.IImageKeys;
import com.gluster.storage.management.gui.utils.GUIHelper;
@@ -197,12 +195,11 @@ public class LoginDialog extends Dialog {
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/toolbar/GlusterToolbarManager.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/toolbar/GlusterToolbarManager.java
index a98c7862..cef0bf5d 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/toolbar/GlusterToolbarManager.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/toolbar/GlusterToolbarManager.java
@@ -30,7 +30,7 @@ import com.gluster.storage.management.core.model.EntityGroup;
import com.gluster.storage.management.core.model.GlusterServer;
import com.gluster.storage.management.core.model.Server;
import com.gluster.storage.management.core.model.Volume;
-import com.gluster.storage.management.gui.actions.IActionSetIDs;
+import com.gluster.storage.management.gui.actions.IActionConstants;
public class GlusterToolbarManager implements ToolbarManager {
private enum ENTITY_TYPE {
@@ -53,13 +53,13 @@ public class GlusterToolbarManager implements ToolbarManager {
private Map<ENTITY_TYPE, String> createActionSetMap() {
Map<ENTITY_TYPE, String> actionSetMap = new HashMap<GlusterToolbarManager.ENTITY_TYPE, String>();
- actionSetMap.put(ENTITY_TYPE.CLUSTER, IActionSetIDs.ACTION_SET_CLUSTER);
- actionSetMap.put(ENTITY_TYPE.VOLUMES, IActionSetIDs.ACTION_SET_VOLUMES);
- actionSetMap.put(ENTITY_TYPE.VOLUME, IActionSetIDs.ACTION_SET_VOLUME);
- actionSetMap.put(ENTITY_TYPE.GLUSTER_SERVERS, IActionSetIDs.ACTION_SET_GLUSTER_SERVERS);
- actionSetMap.put(ENTITY_TYPE.GLUSTER_SERVER, IActionSetIDs.ACTION_SET_GLUSTER_SERVER);
- actionSetMap.put(ENTITY_TYPE.DISCOVERED_SERVERS, IActionSetIDs.ACTION_SET_DISCOVERED_SERVERS);
- actionSetMap.put(ENTITY_TYPE.DISCOVERED_SERVER, IActionSetIDs.ACTION_SET_DISCOVERED_SERVER);
+ actionSetMap.put(ENTITY_TYPE.CLUSTER, IActionConstants.ACTION_SET_CLUSTER);
+ actionSetMap.put(ENTITY_TYPE.VOLUMES, IActionConstants.ACTION_SET_VOLUMES);
+ actionSetMap.put(ENTITY_TYPE.VOLUME, IActionConstants.ACTION_SET_VOLUME);
+ actionSetMap.put(ENTITY_TYPE.GLUSTER_SERVERS, IActionConstants.ACTION_SET_GLUSTER_SERVERS);
+ actionSetMap.put(ENTITY_TYPE.GLUSTER_SERVER, IActionConstants.ACTION_SET_GLUSTER_SERVER);
+ actionSetMap.put(ENTITY_TYPE.DISCOVERED_SERVERS, IActionConstants.ACTION_SET_DISCOVERED_SERVERS);
+ actionSetMap.put(ENTITY_TYPE.DISCOVERED_SERVER, IActionConstants.ACTION_SET_DISCOVERED_SERVER);
return actionSetMap;
}
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 9f5fdfb7..89c5a78e 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
@@ -18,7 +18,9 @@
*******************************************************************************/
package com.gluster.storage.management.gui.utils;
+import java.util.ArrayList;
import java.util.Iterator;
+import java.util.List;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
@@ -55,7 +57,9 @@ import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
+import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchSite;
+import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.events.ExpansionAdapter;
@@ -67,6 +71,9 @@ import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.progress.IProgressConstants;
import com.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import com.gluster.storage.management.core.model.Disk;
+import com.gluster.storage.management.core.model.Entity;
+import com.gluster.storage.management.gui.Application;
import com.gluster.storage.management.gui.IImageKeys;
import com.gluster.storage.management.gui.views.NavigationView;
@@ -250,6 +257,10 @@ public class GUIHelper {
}
return null;
}
+
+ public IWorkbenchPart getActiveView() {
+ return PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart();
+ }
public ControlDecoration createErrorDecoration(Control control) {
ControlDecoration passwordErrorDecoration = new ControlDecoration(control, SWT.LEFT | SWT.TOP);
@@ -348,7 +359,11 @@ public class GUIHelper {
* @return The selected object of given type if found, else null
*/
public Object getSelectedEntity(IWorkbenchSite site, Class expectedType) {
- ISelection selection = site.getWorkbenchWindow().getSelectionService().getSelection(NavigationView.ID);
+ return getSelectedEntity(site.getWorkbenchWindow(), expectedType);
+ }
+
+ public Object getSelectedEntity(IWorkbenchWindow window, Class expectedType) {
+ ISelection selection = window.getSelectionService().getSelection(NavigationView.ID);
if (selection instanceof IStructuredSelection) {
Iterator<Object> iter = ((IStructuredSelection) selection).iterator();
while (iter.hasNext()) {
@@ -360,6 +375,7 @@ public class GUIHelper {
}
return null;
}
+
public void showProgressView() {
try {
@@ -370,4 +386,12 @@ public class GUIHelper {
throw new GlusterRuntimeException("Could not open the progress view!", e);
}
}
+
+ public void setStatusMessage(String message) {
+ Application.getApplication().getStatusLineManager().setMessage(message);
+ }
+
+ public void clearStatusMessage() {
+ Application.getApplication().getStatusLineManager().setMessage(null);
+ }
}
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/ClusterSummaryView.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/ClusterSummaryView.java
index b2d2b93a..079d6bcf 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/ClusterSummaryView.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/ClusterSummaryView.java
@@ -25,18 +25,24 @@ import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ImageHyperlink;
import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.part.ViewPart;
import com.gluster.storage.management.client.GlusterDataModelManager;
import com.gluster.storage.management.core.model.Cluster;
+import com.gluster.storage.management.core.model.EntityGroup;
+import com.gluster.storage.management.core.model.GlusterDataModel;
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.Server;
import com.gluster.storage.management.core.model.Volume;
import com.gluster.storage.management.core.model.Volume.VOLUME_STATUS;
import com.gluster.storage.management.gui.IImageKeys;
+import com.gluster.storage.management.gui.actions.IActionConstants;
import com.gluster.storage.management.gui.utils.GUIHelper;
import com.gluster.storage.management.gui.views.details.tabcreators.PieChartViewerComposite;
@@ -50,6 +56,7 @@ public class ClusterSummaryView extends ViewPart {
private final FormToolkit toolkit = new FormToolkit(Display.getCurrent());
private ScrolledForm form;
private Cluster cluster;
+ private GlusterDataModel model = GlusterDataModelManager.getInstance().getModel();
/*
* (non-Javadoc)
@@ -59,10 +66,9 @@ public class ClusterSummaryView extends ViewPart {
@Override
public void createPartControl(Composite parent) {
if (cluster == null) {
- //cluster = (Cluster)guiHelper.getSelectedEntity(getSite(), Cluster.class);
- cluster = (Cluster)GlusterDataModelManager.getInstance().getModel().getChildren().get(0);
+ cluster = model.getCluster();
}
-
+
createSections(parent);
}
@@ -75,7 +81,7 @@ public class ClusterSummaryView extends ViewPart {
}
return count;
}
-
+
private int getServerCountByStatus(Cluster cluster, SERVER_STATUS status) {
int count = 0;
for (GlusterServer server : cluster.getServers()) {
@@ -105,12 +111,13 @@ public class ClusterSummaryView extends ViewPart {
private void createStatusChart(FormToolkit toolkit, Composite section, Double[] values) {
String[] categories = new String[] { "Online", "Offline" };
- PieChartViewerComposite chartViewerComposite = new PieChartViewerComposite(section, SWT.NONE, categories, values);
+ PieChartViewerComposite chartViewerComposite = new PieChartViewerComposite(section, SWT.NONE, categories,
+ values);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
data.widthHint = 250;
data.heightHint = 250;
- chartViewerComposite.setLayoutData(data);
+ chartViewerComposite.setLayoutData(data);
}
private void createActionsSection() {
@@ -120,14 +127,30 @@ public class ClusterSummaryView extends ViewPart {
imageHyperlink.setText("Create Volume");
imageHyperlink.setImage(guiHelper.getImage(IImageKeys.CREATE_VOLUME_BIG));
imageHyperlink.addHyperlinkListener(new HyperlinkAdapter() {
- // TODO: Override appropriate method and handle hyperlink event
+ @Override
+ public void linkActivated(HyperlinkEvent e) {
+ IHandlerService hs = (IHandlerService) getSite().getService(IHandlerService.class);
+ try {
+ hs.executeCommand(IActionConstants.COMMAND_CREATE_VOLUME, null);
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ }
});
imageHyperlink = toolkit.createImageHyperlink(section, SWT.NONE);
imageHyperlink.setText("Add Server(s)");
imageHyperlink.setImage(guiHelper.getImage(IImageKeys.ADD_SERVER_BIG));
imageHyperlink.addHyperlinkListener(new HyperlinkAdapter() {
- // TODO: Override appropriate method and handle hyperlink event
+ @Override
+ public void linkActivated(HyperlinkEvent e) {
+ // Open the "discovered servers" view by selecting the corresponding entity in the navigation view
+ EntityGroup<Server> autoDiscoveredServersEntityGroup = GlusterDataModelManager.getInstance().getModel()
+ .getCluster().getAutoDiscoveredServersEntityGroup();
+
+ NavigationView navigationView = (NavigationView) guiHelper.getView(NavigationView.ID);
+ navigationView.selectEntity(autoDiscoveredServersEntityGroup);
+ }
});
}
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/NavigationView.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/NavigationView.java
index 4de5b61a..aede70c5 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/NavigationView.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/NavigationView.java
@@ -90,13 +90,7 @@ public class NavigationView extends ViewPart implements ISelectionListener {
@Override
public void volumeChanged(Volume volume, Event event) {
super.volumeChanged(volume, event);
- selectEntity(volume); // this makes sure that the toolbar buttons get updated accoring to new status
- }
-
- @Override
- public void volumeCreated(Volume volume) {
- super.volumeCreated(volume);
- selectEntity(volume);
+ selectEntity(volume); // this makes sure that the toolbar buttons get updated according to new status
}
});
}
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..1c9577ac 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,7 +1,17 @@
package com.gluster.storage.management.gui.views;
+import java.util.List;
+import java.util.Map.Entry;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
@@ -17,13 +27,17 @@ 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.Alert;
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;
@@ -31,11 +45,17 @@ import com.gluster.storage.management.gui.utils.GUIHelper;
public class VolumeSummaryView extends ViewPart {
public static final String ID = VolumeSummaryView.class.getName();
private static final GUIHelper guiHelper = GUIHelper.getInstance();
+
private final FormToolkit toolkit = new FormToolkit(Display.getCurrent());
private ScrolledForm form;
private Volume volume;
private CLabel lblStatusValue;
private DefaultClusterListener volumeChangedListener;
+ private Hyperlink changeLink;
+ private Text accessControlText;
+ private ControlDecoration errDecoration;
+ private Composite parent;
+ private static final String COURIER_FONT = "Courier";
@Override
public void createPartControl(Composite parent) {
@@ -43,22 +63,35 @@ public class VolumeSummaryView extends ViewPart {
volume = (Volume) guiHelper.getSelectedEntity(getSite(), Volume.class);
}
- createSections(parent);
-
+ this.parent = parent;
+ createSections();
+
// Refresh the navigation tree whenever there is a change to the data model
volumeChangedListener = new DefaultClusterListener() {
+ @SuppressWarnings("unchecked")
@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);
+ } else if (event.getEventType() == EVENT_TYPE.VOLUME_OPTION_SET) {
+ Entry<String, String> option = (Entry<String, String>) event.getEventData();
+ if (option.getKey().equals(Volume.OPTION_AUTH_ALLOW)) {
+ // access control option value has changed. update the text field with new value.
+ populateAccessControlText();
+ }
+ } else if (event.getEventType() == EVENT_TYPE.VOLUME_OPTIONS_RESET) {
+ // all volume options reset. populate access control text with default value.
+ populateAccessControlText();
}
}
};
GlusterDataModelManager.getInstance().addClusterListener(volumeChangedListener);
}
-
- /* (non-Javadoc)
+
+ /*
+ * (non-Javadoc)
+ *
* @see org.eclipse.ui.part.WorkbenchPart#dispose()
*/
@Override
@@ -67,7 +100,7 @@ public class VolumeSummaryView extends ViewPart {
GlusterDataModelManager.getInstance().removeClusterListener(volumeChangedListener);
}
- private void createSections(Composite parent) {
+ private void createSections() {
form = guiHelper.setupForm(parent, toolkit, "Volume Properties [" + volume.getName() + "]");
createVolumePropertiesSection();
@@ -78,13 +111,69 @@ public class VolumeSummaryView extends ViewPart {
}
private void createVolumeAlertsSection() {
- Composite section = guiHelper.createSection(form, toolkit, "Alerts", null, 3, false);
- toolkit.createLabel(section, "Volume related alerts will be displayed here");
+ Composite section = guiHelper.createSection(form, toolkit, "Alerts", null, 1, false);
+ List<Alert> alerts = GlusterDataModelManager.getInstance().getModel().getCluster().getAlerts();
+
+ for (int i = 0; i < alerts.size(); i++) {
+ if (alerts.get(i).getType() == Alert.ALERT_TYPES.OFFLINE_VOLUME_DISKS_ALERT
+ && alerts.get(i).getReference().split(":")[0].trim().equals(volume.getName())) {
+ addAlertLabel(section, alerts.get(i));
+ }
+ }
+ }
+
+ private void addAlertLabel(Composite section, Alert alert) {
+ CLabel lblAlert = new CLabel(section, SWT.NONE);
+ lblAlert.setImage(guiHelper.getImage(IImageKeys.DISK_OFFLINE));
+ lblAlert.setText(alert.getMessage());
+ lblAlert.redraw();
+ }
+
+ private Label setLabelStyle(Label label, String fontName, int size, int style) {
+ Font font = new Font(Display.getCurrent(), new FontData(fontName, size, style));
+ label.setFont(font);
+ return label;
}
private void createVolumeMountingInfoSection() {
- Composite section = guiHelper.createSection(form, toolkit, "Mounting Information", null, 3, false);
- toolkit.createLabel(section, "Information about mounting the\nvolume will be printed here");
+ String glusterFs = "Gluster:";
+ String nfs = "NFS:";
+ String glusterFsSyntax = "mount -t glusterfs <SERVER-NAME>:/<VOLUME-NAME> <MOUNT-POINT>";
+ String nfsSyntax = "mount -t nfs <SERVER-NAME>:/nfs/<VOLUME-NAME> <MOUNT-POINT>";
+ String info = "<SERVER-NAME> - Any server name in the storage cloud";
+ String volumeName = volume.getName().trim();
+ String serverName = volume.getDisks().get(0).split(":")[0].trim(); // disk if the form of: "server:disk"
+
+ Composite section = guiHelper.createSection(form, toolkit, "Mounting Information", null, 2, false);
+
+ Label lbl = toolkit.createLabel(section, "Syntax");
+ final int defaultFontSize = lbl.getFont().getFontData()[0].getHeight();
+ final String defaultFontName = lbl.getFont().getFontData()[0].name;
+
+ setLabelStyle(lbl, defaultFontName, defaultFontSize, SWT.BOLD);
+ toolkit.createLabel(section, "");
+
+ setLabelStyle(toolkit.createLabel(section, glusterFs), defaultFontName, defaultFontSize, SWT.NORMAL);
+ setLabelStyle(toolkit.createLabel(section, glusterFsSyntax, SWT.NONE), COURIER_FONT, 10, SWT.NONE);
+
+ // TODO: Check required if nfs is optional
+ setLabelStyle(toolkit.createLabel(section, nfs), defaultFontName, defaultFontSize, SWT.NORMAL);
+ setLabelStyle(toolkit.createLabel(section, nfsSyntax, SWT.NONE), COURIER_FONT, 10, SWT.NONE);
+
+ toolkit.createLabel(section, "");
+ setLabelStyle(toolkit.createLabel(section, info), defaultFontName, (defaultFontSize - 1), SWT.NONE);
+
+ setLabelStyle(toolkit.createLabel(section, "Example"), defaultFontName, defaultFontSize, SWT.BOLD);
+ toolkit.createLabel(section, "");
+
+ setLabelStyle(toolkit.createLabel(section, glusterFs), defaultFontName, defaultFontSize, SWT.NORMAL);
+ setLabelStyle(toolkit.createLabel(section, "#mount -t glusterfs " + serverName + ":/" + volumeName + " /mnt"),
+ COURIER_FONT, 10, SWT.NONE);
+
+ // TODO: Check required if nfs is optional
+ setLabelStyle(toolkit.createLabel(section, nfs), defaultFontName, defaultFontSize, SWT.NORMAL);
+ setLabelStyle(toolkit.createLabel(section, "#mount -t nfs " + serverName + ":/" + volumeName + " /mnt"),
+ COURIER_FONT, 10, SWT.NONE);
}
/**
@@ -114,33 +203,47 @@ public class VolumeSummaryView extends ViewPart {
private GridData createDefaultLayoutData() {
GridData layoutData = new GridData();
- layoutData.minimumWidth = 150;
- layoutData.widthHint = 150;
+ layoutData.minimumWidth = 300;
+ layoutData.widthHint = 300;
return layoutData;
}
private void createAccessControlField(Composite section) {
toolkit.createLabel(section, "Access Control: ", SWT.NONE);
- Text accessControlText = toolkit.createText(section, volume.getAccessControlList());
+ accessControlText = toolkit.createText(section, volume.getAccessControlList());
+
+ populateAccessControlText();
+ addKeyListerForAccessControl();
accessControlText.setLayoutData(createDefaultLayoutData());
accessControlText.setEnabled(false);
- createChangeLinkForAccessControl(section, accessControlText);
+ createChangeLinkForAccessControl(section);
+
+ // error decoration used while validating the access control text
+ errDecoration = guiHelper.createErrorDecoration(accessControlText);
+ errDecoration.hide();
+ createAccessControlInfoLabel(section); // info text
+ }
+
+ private void createAccessControlInfoLabel(Composite section) {
+ toolkit.createLabel(section, "", SWT.NONE);
+ Label accessControlInfoLabel = toolkit.createLabel(section, "(Comma separated list of IP addresses/hostnames)");
+ GridData data = new GridData(SWT.LEFT, SWT.CENTER, true, false);
+ data.horizontalSpan = 2;
+ accessControlInfoLabel.setLayoutData(data);
}
- private void createChangeLinkForAccessControl(Composite section, final Text accessControlText) {
- final Hyperlink changeLink = toolkit.createHyperlink(section, "change", SWT.NONE);
+ private void createChangeLinkForAccessControl(Composite section) {
+ changeLink = toolkit.createHyperlink(section, "change", SWT.NONE);
changeLink.addHyperlinkListener(new HyperlinkAdapter() {
private void finishEdit() {
- // TODO: Update value to back-end
- // TODO: Validation of entered text
- volume.setAccessControlList(accessControlText.getText());
- accessControlText.setEnabled(false);
- changeLink.setText("change");
+ saveAccessControlList();
}
private void startEdit() {
accessControlText.setEnabled(true);
+ accessControlText.setFocus();
+ accessControlText.selectAll();
changeLink.setText("update");
}
@@ -157,6 +260,71 @@ public class VolumeSummaryView extends ViewPart {
});
}
+ private void saveAccessControlList() {
+ final String newACL = accessControlText.getText();
+
+ guiHelper.setStatusMessage("Setting access control list to [" + newACL + "]...");
+ parent.update();
+
+ if (newACL.equals(volume.getAccessControlList())) {
+ accessControlText.setEnabled(false);
+ changeLink.setText("change");
+ } else if (ValidationUtil.isValidAccessControl(newACL)) {
+ BusyIndicator.showWhile(Display.getDefault(), new Runnable() {
+ @Override
+ public void run() {
+ Status status = (new VolumesClient(GlusterDataModelManager.getInstance().getSecurityToken()))
+ .setVolumeOption(volume.getName(), Volume.OPTION_AUTH_ALLOW, newACL);
+
+ if (status.isSuccess()) {
+ accessControlText.setEnabled(false);
+ changeLink.setText("change");
+
+ GlusterDataModelManager.getInstance().setAccessControlList(volume, newACL);
+ } else {
+ MessageDialog.openError(Display.getDefault().getActiveShell(), "Access control",
+ status.getMessage());
+ }
+ }
+ });
+ } else {
+ MessageDialog.openError(Display.getDefault().getActiveShell(), "Access control", "Invalid IP / Host name ");
+ }
+ guiHelper.clearStatusMessage();
+ parent.update();
+ }
+
+ private void addKeyListerForAccessControl() {
+ accessControlText.addKeyListener(new KeyAdapter() {
+ public void keyReleased(KeyEvent key) {
+ switch (key.keyCode) {
+ case SWT.ESC:
+ // Reset to default
+ populateAccessControlText();
+ changeLink.setText("change");
+ accessControlText.setEnabled(false);
+ break;
+ case 13:
+ // User has pressed enter. Save the new value
+ saveAccessControlList();
+ break;
+ }
+
+ validateAccessControlList();
+ }
+ });
+ }
+
+ private void populateAccessControlText() {
+ String accessControlList = volume.getAccessControlList();
+ if (accessControlList == null) {
+ // if not set, show default value
+ accessControlList = GlusterDataModelManager.getInstance().getVolumeOptionDefaultValue(
+ Volume.OPTION_AUTH_ALLOW);
+ }
+ accessControlText.setText(accessControlList);
+ }
+
private void createNASProtocolField(Composite section) {
toolkit.createLabel(section, "NAS Protocols: ", SWT.NONE);
@@ -167,7 +335,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) {
@@ -178,8 +347,8 @@ public class VolumeSummaryView extends ViewPart {
}
private void createChangeLinkForNASProtocol(Composite section, final Button nfsCheckBox) {
- final Hyperlink changeLink = toolkit.createHyperlink(section, "change", SWT.NONE);
- changeLink.addHyperlinkListener(new HyperlinkAdapter() {
+ final Hyperlink nasChangeLink = toolkit.createHyperlink(section, "change", SWT.NONE);
+ nasChangeLink.addHyperlinkListener(new HyperlinkAdapter() {
private void finishEdit() {
// TODO: Update value to back-end
@@ -189,12 +358,12 @@ public class VolumeSummaryView extends ViewPart {
volume.disableNFS();
}
nfsCheckBox.setEnabled(false);
- changeLink.setText("change");
+ nasChangeLink.setText("change");
}
private void startEdit() {
nfsCheckBox.setEnabled(true);
- changeLink.setText("update");
+ nasChangeLink.setText("update");
}
@Override
@@ -267,4 +436,20 @@ public class VolumeSummaryView extends ViewPart {
public void setFocus() {
form.setFocus();
}
+
+ private void validateAccessControlList() {
+ errDecoration.hide();
+
+ if (accessControlText.getText().length() == 0) {
+ errDecoration.setDescriptionText("Access control list cannot be empty!");
+ errDecoration.show();
+ return;
+ }
+
+ if (!ValidationUtil.isValidAccessControl(accessControlText.getText())) {
+ errDecoration
+ .setDescriptionText("Access control list must be a comma separated list of IP addresses/Host names. Please enter a valid value!");
+ errDecoration.show();
+ }
+ }
}
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumesSummaryView.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumesSummaryView.java
index 8c39fbeb..b929a2b2 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumesSummaryView.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumesSummaryView.java
@@ -33,7 +33,6 @@ import org.eclipse.ui.part.ViewPart;
import com.gluster.storage.management.core.model.Alert;
import com.gluster.storage.management.core.model.EntityGroup;
-import com.gluster.storage.management.core.model.Cluster;
import com.gluster.storage.management.client.GlusterDataModelManager;
import com.gluster.storage.management.core.model.RunningTask;
import com.gluster.storage.management.core.model.Volume;
@@ -96,12 +95,11 @@ public class VolumesSummaryView extends ViewPart {
}
private void addAlertLabel(Composite section, Alert alert) {
- if (alert.getType() == Alert.ALERT_TYPES.DISK_USAGE_ALERT
- || alert.getType() == Alert.ALERT_TYPES.OFFLINE_VOLUME_DISKS_ALERT) {
+ if (alert.getType() == Alert.ALERT_TYPES.OFFLINE_VOLUME_DISKS_ALERT) {
CLabel lblAlert = new CLabel(section, SWT.NONE);
- lblAlert.setText(alert.getMessage());
lblAlert.setImage((alert.getType() == Alert.ALERT_TYPES.DISK_USAGE_ALERT) ? guiHelper
.getImage(IImageKeys.LOW_DISK_SPACE) : guiHelper.getImage(IImageKeys.DISK_OFFLINE));
+ lblAlert.setText(alert.getMessage());
lblAlert.redraw();
}
}
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 593f7ba1..6b516019 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
@@ -94,6 +94,7 @@ public abstract class AbstractDisksPage extends Composite implements IEntityList
setupPageLayout();
Text filterText = guiHelper.createFilterText(toolkit, this);
setupDiskTableViewer(createTableViewerComposite(), filterText);
+ site.setSelectionProvider(tableViewer);
tableViewer.setInput(disks);
setupStatusCellEditor(); // creates hyperlinks for "unitialized" disks
@@ -231,7 +232,7 @@ public abstract class AbstractDisksPage extends Composite implements IEntityList
}
private TableViewer createDiskTableViewer(Composite parent) {
- tableViewer = CheckboxTableViewer.newCheckList(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.MULTI);
+ tableViewer = CheckboxTableViewer.newCheckList(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.MULTI );
tableViewer.setLabelProvider(getTableLabelProvider());
tableViewer.setContentProvider(new ArrayContentProvider());
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..27dc8d4b
--- /dev/null
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionKeyEditingSupport.java
@@ -0,0 +1,125 @@
+/**
+ *
+ */
+package com.gluster.storage.management.gui.views.details;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+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.SWT;
+import org.eclipse.swt.widgets.Composite;
+
+import com.gluster.storage.management.client.GlusterDataModelManager;
+import com.gluster.storage.management.core.model.Volume;
+import com.gluster.storage.management.core.model.VolumeOptionInfo;
+
+/**
+ * Editing support for the "value" column in volume options table viewer.
+ */
+public class OptionKeyEditingSupport extends EditingSupport {
+ private CellEditor cellEditor;
+ private Volume volume;
+ private List<VolumeOptionInfo> defaults = GlusterDataModelManager.getInstance().getVolumeOptionsDefaults();
+ private String[] allowedKeys;
+ private ColumnViewer viewer;
+
+ public OptionKeyEditingSupport(ColumnViewer viewer, Volume volume) {
+ super(viewer);
+ this.volume = volume;
+ this.viewer = viewer;
+ }
+
+ /**
+ * @return array of option keys that are not already set on the volume
+ */
+ private String[] getAllowedKeys() {
+ ArrayList<String> keys = new ArrayList<String>();
+ Map<String, String> volumeOptions = volume.getOptions();
+ for(VolumeOptionInfo optionInfo : defaults) {
+ String optionName = optionInfo.getName();
+ if(!volumeOptions.containsKey(optionName) || volumeOptions.get(optionName).isEmpty()) {
+ // key not set => available for setting
+ // value not set => this is the row being edited
+ keys.add(optionName);
+ }
+ }
+ return keys.toArray(new String[0]);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void setValue(final Object element, final Object value) {
+ Entry<String, String> oldEntry = (Entry<String, String>)element;
+ Integer newValue = (Integer)value;
+ String newKey = allowedKeys[newValue];
+
+ if (((Entry<String, String>)element).getKey().equals(newKey)) {
+ // selected value is same as old one.
+ return;
+ }
+
+ // value has changed. set new value and refresh the viewer.
+ volume.getOptions().remove(oldEntry.getKey());
+ volume.setOption(newKey, "");
+ getViewer().refresh();
+ }
+
+ @Override
+ protected Object getValue(Object element) {
+ Entry<String, String> entryBeingAdded = getEntryBeingAdded();
+ if(entryBeingAdded == null) {
+ return cellEditor.getValue();
+ }
+
+ if(entryBeingAdded.getKey().isEmpty()) {
+ // editing just about to start. set first element as default.
+ return 0;
+ }
+
+ return getIndexOfEntry(entryBeingAdded);
+ }
+
+ @Override
+ protected CellEditor getCellEditor(Object element) {
+ allowedKeys = getAllowedKeys();
+ cellEditor = new ComboBoxCellEditor((Composite) viewer.getControl(), allowedKeys, SWT.READ_ONLY);
+ return cellEditor;
+ }
+
+ private int getIndexOfEntry(Entry<String, String> entryBeingAdded) {
+ for(int index = 0; index < allowedKeys.length; index++) {
+ if(allowedKeys[index].equals(entryBeingAdded.getKey())) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ protected Entry<String, String> getEntryBeingAdded() {
+ Entry<String, String> entryBeingAdded = null;
+ Iterator<Entry<String, String>> iter = volume.getOptions().entrySet().iterator();
+ while(iter.hasNext()) {
+ Entry<String, String> nextEntry = iter.next();
+ if(!iter.hasNext() && nextEntry.getValue().isEmpty()) {
+ // it's the LAST entry, and it's value is empty.
+ // means this is a new row being added in the table viewer.
+ entryBeingAdded = nextEntry;
+ }
+ }
+ return entryBeingAdded;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected boolean canEdit(Object element) {
+ Entry<String, String> entry = (Entry<String, String>)element;
+ return (entry.getKey().isEmpty() || entry.getValue().isEmpty());
+ }
+}
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionValueEditingSupport.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionValueEditingSupport.java
new file mode 100644
index 00000000..af1ef949
--- /dev/null
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionValueEditingSupport.java
@@ -0,0 +1,115 @@
+/**
+ *
+ */
+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;
+import com.gluster.storage.management.gui.utils.GUIHelper;
+
+/**
+ * Editing support for the "value" column in volume options table viewer.
+ */
+public class OptionValueEditingSupport extends EditingSupport {
+ private CellEditor cellEditor;
+ private Volume volume;
+ private List<VolumeOptionInfo> defaults = GlusterDataModelManager.getInstance().getVolumeOptionsDefaults();
+ private GUIHelper guiHelper = GUIHelper.getInstance();
+
+ public OptionValueEditingSupport(ColumnViewer viewer, Volume volume) {
+ super(viewer);
+ this.volume = volume;
+ this.cellEditor = new TextCellEditor((Composite) viewer.getControl());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void setValue(final Object element, final Object value) {
+ final Entry<String, String> entry = (Entry<String, String>) element;
+ final String optionKey = entry.getKey();
+ final String optionValue = (String)value;
+ final String oldValue = entry.getValue();
+
+ // It is not allowed to change value to empty string
+ if(optionValue.isEmpty()) {
+ MessageDialog.openError(Display.getDefault().getActiveShell(), "Set Volume Option",
+ "Option value can't be empty! Please enter a valid value.");
+ cellEditor.setFocus();
+ return;
+ }
+
+ if (oldValue.equals(optionValue)) {
+ // 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
+ guiHelper.setStatusMessage("Setting option [" + optionKey + " = " + optionValue + "]...");
+ getViewer().getControl().update();
+
+ 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()) {
+ entry.setValue((String)value);
+ GlusterDataModelManager.getInstance().setVolumeOption(volume, entry);
+ } else {
+ MessageDialog.openError(Display.getDefault().getActiveShell(), "Set Volume Option",
+ status.getMessage());
+ }
+ getViewer().update(entry, null);
+ }
+ });
+
+ guiHelper.clearStatusMessage();
+ getViewer().getControl().update();
+ }
+
+ /**
+ * @param key Key whose default value is to be fetched
+ * @return Default value of the volume option with given key
+ */
+ private String getDefaultValue(String key) {
+ for(VolumeOptionInfo optionInfo : defaults) {
+ if(optionInfo.getName().equals(key)) {
+ return optionInfo.getDefaultValue();
+ }
+ }
+ return "";
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Object getValue(Object element) {
+ Entry<String, String> entry = (Entry<String, String>) element;
+ return entry.getValue().isEmpty() ? getDefaultValue(entry.getKey()) : entry.getValue();
+ }
+
+ @Override
+ protected CellEditor getCellEditor(Object element) {
+ return cellEditor;
+ }
+
+ @Override
+ protected boolean canEdit(Object element) {
+ return true;
+ }
+}
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeLogsPage.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeLogsPage.java
index d435201e..df9f8533 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeLogsPage.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeLogsPage.java
@@ -18,6 +18,11 @@
*******************************************************************************/
package com.gluster.storage.management.gui.views.details;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
@@ -25,6 +30,8 @@ import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
@@ -39,8 +46,15 @@ import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.widgets.FormToolkit;
-import com.gluster.storage.management.core.model.GlusterDummyModel;
+import com.gluster.storage.management.client.GlusterDataModelManager;
+import com.gluster.storage.management.client.VolumesClient;
+import com.gluster.storage.management.core.constants.CoreConstants;
+import com.gluster.storage.management.core.constants.GlusterConstants;
+import com.gluster.storage.management.core.constants.GlusterConstants.VOLUME_LOG_LEVELS;
+import com.gluster.storage.management.core.model.LogMessage;
+import com.gluster.storage.management.core.model.Status;
import com.gluster.storage.management.core.model.Volume;
+import com.gluster.storage.management.core.response.LogMessageListResponse;
import com.gluster.storage.management.gui.VolumeLogTableLabelProvider;
import com.gluster.storage.management.gui.utils.GUIHelper;
@@ -48,20 +62,35 @@ public class VolumeLogsPage extends Composite {
private final FormToolkit toolkit = new FormToolkit(Display.getCurrent());
private final GUIHelper guiHelper = GUIHelper.getInstance();
- private Text text;
+ private Text filterText;
+ private Text lineCountText;
+ private Volume volume;
+
public enum LOG_TABLE_COLUMN_INDICES {
DATE, TIME, DISK, SEVERITY, MESSAGE
};
private static final String[] LOG_TABLE_COLUMN_NAMES = new String[] { "Date", "Time", "Disk", "Severity", "Message" };
+ private TableViewer tableViewer;
+ private Combo disksCombo;
+ private Combo severityCombo;
+ private DateTime fromDate;
+ private DateTime fromTime;
+ private DateTime toDate;
+ private DateTime toTime;
+ private Button fromCheckbox;
+ private Button toCheckbox;
/**
- * Create the composite.
+ * Create the volume logs page
* @param parent
* @param style
+ * @param volume Volume for which the logs page is to be created
*/
public VolumeLogsPage(Composite parent, int style, Volume volume) {
super(parent, style);
+ this.volume = volume;
+
addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
toolkit.dispose();
@@ -70,89 +99,257 @@ public class VolumeLogsPage extends Composite {
toolkit.adapt(this);
toolkit.paintBordersFor(this);
- setLayout(new GridLayout(1, false));
- GridData layoutData = new GridData();
- layoutData.grabExcessHorizontalSpace = true;
- layoutData.grabExcessVerticalSpace = true;
- //layoutData.verticalIndent = 10;
- setLayoutData(layoutData);
+ configureLayout();
Composite composite = toolkit.createComposite(this, SWT.NONE);
toolkit.paintBordersFor(composite);
- Label lblScanLast = toolkit.createLabel(composite, "Scan last", SWT.NONE);
- lblScanLast.setBounds(0, 15, 80, 20);
+ createLineCountLabel(composite);
+ createLineCountText(composite);
- text = toolkit.createText(composite, "100", SWT.NONE);
- text.setBounds(85, 15, 60, 20);
+ createDiskLabel(composite);
+ createDisksCombo(composite);
- Label lblMessagesAndFilter = toolkit.createLabel(composite, "messages, and filter on disk", SWT.NONE);
- lblMessagesAndFilter.setBounds(160, 15, 200, 20);
+ createSeverityLabel(composite);
+ createSeverityCombo(composite);
- Combo combo = new Combo(composite, SWT.NONE);
- combo.setBounds(365, 15, 100, 20);
- combo.setItems(new String[] {"ALL", "sda", "sdb", "sdc", "sdd"});
- toolkit.adapt(combo);
- toolkit.paintBordersFor(combo);
- combo.select(0);
+ createFromDateLabel(composite);
+ createFromDateField(composite);
+ createFromTimeField(composite);
+ createFromCheckbox(composite);
- Label lblSeverity = toolkit.createLabel(composite, "Severity", SWT.NONE);
- lblSeverity.setBounds(480, 15, 70, 20);
+ createToDateLabel(composite);
+ createToDateField(composite);
+ createToTimeField(composite);
+ createToCheckbox(composite);
- Combo combo_1 = new Combo(composite, SWT.NONE);
- combo_1.setBounds(555, 15, 110, 20);
- combo_1.setItems(new String[] {"ALL", "SEVERE", "WARNING", "DEBUG", "INFO"});
- toolkit.adapt(combo_1);
- toolkit.paintBordersFor(combo_1);
- combo_1.select(1);
+ createSearchButton(composite);
- Label lblFrom = toolkit.createLabel(composite, "from", SWT.NONE);
- lblFrom.setBounds(0, 60, 40, 20);
+ createSeparator(composite);
- DateTime dateTime = new DateTime(composite, SWT.BORDER | SWT.DROP_DOWN);
- dateTime.setBounds(45, 60, 120, 20);
- toolkit.adapt(dateTime);
- toolkit.paintBordersFor(dateTime);
+ createFilterLabel(composite);
+ createFilterText(composite);
- DateTime dateTime_1 = new DateTime(composite, SWT.BORDER | SWT.TIME);
- dateTime_1.setBounds(171, 60, 120, 20);
- toolkit.adapt(dateTime_1);
- toolkit.paintBordersFor(dateTime_1);
+ createLogTableViewer();
+ }
+
+ private void createLogTableViewer() {
+ Composite tableViewerComposite = createTableViewerComposite();
- Label lblTo = toolkit.createLabel(composite, "To", SWT.NONE);
- lblTo.setBounds(329, 60, 26, 20);
+ tableViewer = new TableViewer(tableViewerComposite, SWT.FLAT | SWT.FULL_SELECTION | SWT.MULTI);
+ tableViewer.setLabelProvider(new VolumeLogTableLabelProvider());
+ tableViewer.setContentProvider(new ArrayContentProvider());
+
+ setupLogsTable(tableViewerComposite, tableViewer.getTable());
+ guiHelper.createFilter(tableViewer, filterText, false);
+ }
+
+ private void createFilterText(Composite composite) {
+ filterText = guiHelper.createFilterText(toolkit, composite);
+ filterText.setBounds(90, 105, 250, 20);
+ }
+
+ private void createFilterLabel(Composite composite) {
+ Label lblFilterString = toolkit.createLabel(composite, "Filter String", SWT.LEFT);
+ lblFilterString.setBounds(0, 105, 85, 20);
+ }
+
+ private void createSeparator(Composite composite) {
+ Label separator = toolkit.createLabel(composite, "", SWT.SEPARATOR | SWT.HORIZONTAL | SWT.FILL);
+ separator.setBounds(0, 95, 680, 2);
+ }
+
+ private void createSearchButton(Composite composite) {
+ Button btnGo = toolkit.createButton(composite, "&Go", SWT.NONE);
+ btnGo.setBounds(615, 55, 50, 30);
+ btnGo.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ VolumesClient client = new VolumesClient(GlusterDataModelManager.getInstance().getSecurityToken());
+
+ Date fromTimestamp = null;
+ Date toTimestamp = null;
+
+ if(fromCheckbox.getSelection()) {
+ fromTimestamp = extractTimestamp(fromDate, fromTime);
+ }
+
+ if(toCheckbox.getSelection()) {
+ toTimestamp = extractTimestamp(toDate, toTime);
+ }
+
+ if (!validateTimeRange(fromTimestamp, toTimestamp)) {
+ return;
+ }
+
+ LogMessageListResponse response = client.getLogs(volume.getName(), disksCombo.getText(),
+ severityCombo.getText(), fromTimestamp, toTimestamp, Integer.parseInt(lineCountText.getText()));
+ Status status = response.getStatus();
+ if(status.isSuccess()) {
+ List<LogMessage> logMessages = response.getLogMessages();
+ tableViewer.setInput(logMessages.toArray(new LogMessage[0]));
+ tableViewer.refresh();
+ } else {
+ MessageDialog.openError(getShell(), "Volume Logs", "Error while fetching volume logs: [" + status
+ + "]");
+ }
+ }
+ });
+ }
+
+ protected boolean validateTimeRange(Date fromTimestamp, Date toTimestamp) {
+ if(fromTimestamp == null && toTimestamp == null) {
+ // no time range selected. nothing to validate.
+ return true;
+ }
- DateTime dateTime_2 = new DateTime(composite, SWT.BORDER | SWT.DROP_DOWN);
- dateTime_2.setBounds(355, 60, 120, 20);
- toolkit.adapt(dateTime_2);
- toolkit.paintBordersFor(dateTime_2);
+ Calendar calendar = Calendar.getInstance();
+ Date now = calendar.getTime();
+ if(fromTimestamp != null && fromTimestamp.after(now)) {
+ MessageDialog.openError(getShell(), "Volume Logs", "From time can't be greater than current time!");
+ return false;
+ }
- DateTime dateTime_3 = new DateTime(composite, SWT.BORDER | SWT.TIME);
- dateTime_3.setBounds(480, 60, 120, 20);
- toolkit.adapt(dateTime_3);
- toolkit.paintBordersFor(dateTime_3);
+ if(toTimestamp != null) {
+ if (toTimestamp.after(now)) {
+ MessageDialog.openError(getShell(), "Volume Logs", "To time can't be greater than current time!");
+ return false;
+ }
+
+ if(fromTimestamp.after(toTimestamp)) {
+ MessageDialog.openError(getShell(), "Volume Logs", "From time can't be greater than To time!");
+ return false;
+ }
+ }
- Button btngo = toolkit.createButton(composite, "&Go", SWT.NONE);
- btngo.setBounds(605, 55, 60, 30);
+ return true;
+ }
+
+ private void createToCheckbox(Composite composite) {
+ toCheckbox = toolkit.createButton(composite, null, SWT.CHECK);
+ toCheckbox.setBounds(320, 60, 15, 20);
+ toCheckbox.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (toCheckbox.getSelection()) {
+ toDate.setEnabled(true);
+ toTime.setEnabled(true);
+ } else {
+ toDate.setEnabled(false);
+ toTime.setEnabled(false);
+ }
+ }
+ });
+ }
+
+ private void createToTimeField(Composite composite) {
+ toTime = new DateTime(composite, SWT.BORDER | SWT.TIME);
+ toTime.setBounds(490, 60, 120, 20);
+ toTime.setEnabled(false);
+ toolkit.adapt(toTime);
+ toolkit.paintBordersFor(toTime);
+ }
+
+ private void createToDateField(Composite composite) {
+ toDate = new DateTime(composite, SWT.BORDER | SWT.DROP_DOWN);
+ toDate.setBounds(365, 60, 120, 20);
+ toDate.setEnabled(false);
+ toolkit.adapt(toDate);
+ toolkit.paintBordersFor(toDate);
+ }
+
+ private void createToDateLabel(Composite composite) {
+ Label lblTo = toolkit.createLabel(composite, "To", SWT.NONE);
+ lblTo.setBounds(340, 60, 25, 20);
+ }
+
+ private void createFromCheckbox(Composite composite) {
+ fromCheckbox = toolkit.createButton(composite, null, SWT.CHECK);
+ fromCheckbox.setBounds(0, 60, 15, 20);
+ fromCheckbox.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (fromCheckbox.getSelection()) {
+ fromDate.setEnabled(true);
+ fromTime.setEnabled(true);
+ } else {
+ fromDate.setEnabled(false);
+ fromTime.setEnabled(false);
+ }
+ }
+ });
+ }
+
+ private void createFromTimeField(Composite composite) {
+ fromTime = new DateTime(composite, SWT.BORDER | SWT.TIME);
+ fromTime.setBounds(190, 60, 120, 20);
+ fromTime.setEnabled(false);
+ toolkit.adapt(fromTime);
+ toolkit.paintBordersFor(fromTime);
+ }
+
+ private void createFromDateField(Composite composite) {
+ fromDate = new DateTime(composite, SWT.BORDER | SWT.DROP_DOWN);
+ fromDate.setBounds(60, 60, 120, 20);
+ fromDate.setEnabled(false);
+ toolkit.adapt(fromDate);
+ toolkit.paintBordersFor(fromDate);
+ }
+
+ private void createFromDateLabel(Composite composite) {
+ Label lblFrom = toolkit.createLabel(composite, "from", SWT.NONE);
+ lblFrom.setBounds(20, 60, 40, 20);
+ }
+
+ private void createSeverityCombo(Composite composite) {
+ severityCombo = new Combo(composite, SWT.READ_ONLY);
+ severityCombo.setBounds(555, 15, 110, 20);
- Label separator = toolkit.createLabel(composite, "", SWT.SEPARATOR | SWT.HORIZONTAL | SWT.FILL);
- separator.setBounds(0, 95, 680, 2);
+ severityCombo.setItems(GlusterConstants.VOLUME_LOG_LEVELS_ARR.toArray(new String[0]));
+ severityCombo.select(VOLUME_LOG_LEVELS.ERROR.ordinal());
+ severityCombo.add(CoreConstants.ALL, 0);
- Label lblFilterString = toolkit.createLabel(composite, "Filter String", SWT.LEFT);
- lblFilterString.setBounds(0, 105, 85, 20);
+ toolkit.adapt(severityCombo);
+ toolkit.paintBordersFor(severityCombo);
+ }
- text = guiHelper.createFilterText(toolkit, composite);
- text.setBounds(90, 105, 250, 20);
+ private void createSeverityLabel(Composite composite) {
+ Label lblSeverity = toolkit.createLabel(composite, "Severity", SWT.NONE);
+ lblSeverity.setBounds(480, 15, 70, 20);
+ }
- Composite tableViewerComposite = createTableViewerComposite();
-
- TableViewer tableViewer = new TableViewer(tableViewerComposite, SWT.FLAT | SWT.FULL_SELECTION | SWT.MULTI);
- tableViewer.setLabelProvider(new VolumeLogTableLabelProvider());
- tableViewer.setContentProvider(new ArrayContentProvider());
+ private void createDisksCombo(Composite composite) {
+ disksCombo = new Combo(composite, SWT.READ_ONLY);
+ disksCombo.setBounds(365, 15, 100, 20);
+ disksCombo.setItems(volume.getDisks().toArray(new String[0]));
+ disksCombo.add(CoreConstants.ALL, 0);
+ toolkit.adapt(disksCombo);
+ toolkit.paintBordersFor(disksCombo);
+ disksCombo.select(0);
+ }
- setupLogsTable(tableViewerComposite, tableViewer.getTable());
- guiHelper.createFilter(tableViewer, text, false);
- tableViewer.setInput(GlusterDummyModel.getDummyLogMessages().toArray());
+ private void createDiskLabel(Composite composite) {
+ Label lblMessagesAndFilter = toolkit.createLabel(composite, "messages, and filter on disk", SWT.NONE);
+ lblMessagesAndFilter.setBounds(160, 15, 200, 20);
+ }
+
+ private void createLineCountText(Composite composite) {
+ lineCountText = toolkit.createText(composite, "100", SWT.NONE);
+ lineCountText.setBounds(85, 15, 60, 20);
+ }
+
+ private void createLineCountLabel(Composite composite) {
+ Label lblScanLast = toolkit.createLabel(composite, "Scan last", SWT.NONE);
+ lblScanLast.setBounds(0, 15, 80, 20);
+ }
+
+ private void configureLayout() {
+ setLayout(new GridLayout(1, false));
+ GridData layoutData = new GridData();
+ layoutData.grabExcessHorizontalSpace = true;
+ layoutData.grabExcessVerticalSpace = true;
+ //layoutData.verticalIndent = 10;
+ setLayoutData(layoutData);
}
private Composite createTableViewerComposite() {
@@ -193,4 +390,17 @@ public class VolumeLogsPage extends Composite {
TableColumnLayout tableColumnLayout = (TableColumnLayout) table.getParent().getLayout();
tableColumnLayout.setColumnData(column, new ColumnWeightData(weight));
}
+
+ private Date extractTimestamp(DateTime date, DateTime time) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setLenient(false);
+ calendar.set(Calendar.DAY_OF_MONTH, date.getDay());
+ // in Calendar class, month starts with zero i.e. Jan = 0
+ calendar.set(Calendar.MONTH, date.getMonth());
+ calendar.set(Calendar.YEAR, date.getYear());
+ calendar.set(Calendar.HOUR_OF_DAY, time.getHours());
+ calendar.set(Calendar.MINUTE, time.getMinutes());
+ calendar.set(Calendar.SECOND, time.getSeconds());
+ return calendar.getTime();
+ }
}
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 cd425dc2..c0a69693 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,29 +20,29 @@ package com.gluster.storage.management.gui.views.details;
import java.util.Map.Entry;
-import org.eclipse.jface.dialogs.MessageDialog;
+import org.apache.commons.lang.WordUtils;
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.ColumnViewerToolTipSupport;
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.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
-import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
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;
@@ -51,9 +51,12 @@ 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.constants.CoreConstants;
+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.core.model.VolumeOptionInfo;
import com.gluster.storage.management.gui.VolumeOptionsTableLabelProvider;
import com.gluster.storage.management.gui.utils.GUIHelper;
@@ -63,36 +66,91 @@ public class VolumeOptionsPage extends Composite {
private TableViewer tableViewer;
private GUIHelper guiHelper = GUIHelper.getInstance();
private Volume volume;
+ private DefaultClusterListener clusterListener;
+ private Text filterText;
public enum OPTIONS_TABLE_COLUMN_INDICES {
OPTION_KEY, OPTION_VALUE
};
private static final String[] OPTIONS_TABLE_COLUMN_NAMES = new String[] { "Option Key", "Option Value" };
+ private Button addButton;
+ private TableViewerColumn keyColumn;
+ private OptionKeyEditingSupport keyEditingSupport;
- 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);
+ filterText = guiHelper.createFilterText(toolkit, this);
+ setupOptionsTableViewer(filterText);
+
+ createAddButton();
+
+ tableViewer.setInput(volume.getOptions().entrySet());
+
+ parent.layout(); // Important - this actually paints the table
+ registerListeners(parent);
}
- public VolumeOptionsPage(final Composite parent, int style, Volume volume) {
- this(parent, style);
- this.volume = volume;
+ private void createAddButton() {
+ addButton = toolkit.createButton(this, "&Add", SWT.FLAT);
+ addButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ // add an empty option to be filled up by user
+ volume.setOption("", "");
+
+ tableViewer.refresh();
+ tableViewer.setSelection(new StructuredSelection(getEntry("")));
+ keyColumn.getViewer().editElement(getEntry(""), 0); // edit newly created entry
+
+ // disable the add button AND search filter textbox till user fills up the new option
+ addButton.setEnabled(false);
+ filterText.setEnabled(false);
+ }
- tableViewer.setInput(volume.getOptions().entrySet().toArray());
+ private Entry<String, String> getEntry(String key) {
+ for(Entry<String, String> entry : volume.getOptions().entrySet()) {
+ if(entry.getKey().equals(key)) {
+ return entry;
+ }
+ }
+ return null;
+ }
+ });
- parent.layout(); // Important - this actually paints the table
+ // Make sure that add button is enabled only when search filter textbox is empty
+ filterText.addModifyListener(new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ if(filterText.getText().length() > 0) {
+ addButton.setEnabled(false);
+ } else {
+ addButton.setEnabled(true);
+ }
+ }
+ });
+ }
+
+ private void registerListeners(final Composite parent) {
+ addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ if(!addButton.isEnabled()) {
+ // user has selected key, but not added value. Since this is not a valid entry,
+ // remove the last option (without value) from the volume
+ volume.getOptions().remove(keyEditingSupport.getEntryBeingAdded().getKey());
+ }
+
+ GlusterDataModelManager.getInstance().removeClusterListener(clusterListener);
+ toolkit.dispose();
+ }
+ });
/**
* Ideally not required. However the table viewer is not getting laid out properly on performing
@@ -105,7 +163,62 @@ public class VolumeOptionsPage extends Composite {
parent.layout();
}
});
+
+ parent.addDisposeListener(new DisposeListener() {
+
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ if(!addButton.isEnabled()) {
+ // user has selected key, but not added value. Since this is not a valid entry,
+ // remove the last option (without value) from the volume
+ Entry<String, String> entryBeingAdded = keyEditingSupport.getEntryBeingAdded();
+ volume.getOptions().remove(entryBeingAdded.getKey());
+ }
+ }
+ });
+
+ clusterListener = new DefaultClusterListener() {
+ @SuppressWarnings("unchecked")
+ @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();
+ }
+ }
+
+ if(event.getEventType() == EVENT_TYPE.VOLUME_OPTION_SET) {
+ Entry<String, String> eventEntry = (Entry<String, String>)event.getEventData();
+ if (isNewOption(volume, eventEntry.getKey())) {
+ // option has been set successfully by the user. re-enable the add button and search filter textbox
+ addButton.setEnabled(true);
+ filterText.setEnabled(true);
+ }
+
+ if(tableViewer.getTable().getItemCount() < volume.getOptions().size()) {
+ // new volume set from outside this page. refresh the viewer.
+ tableViewer.refresh();
+ } else {
+ // existing volume option value changed. update that element.
+ tableViewer.update(eventEntry, null);
+ }
+ }
+ }
+
+ private boolean isNewOption(Volume volume, String optionKey) {
+ if(filterText.getText().length() > 0) {
+ // user has been filtering the contents. adding new option is allowed only when contents are NOT
+ // filtered. Thus it's impossible that this is a newly added option
+ return false;
+ }
+
+ // if this is the last option in the volume options, it must be the new option
+ return optionKey.equals(volume.getOptions().keySet().toArray()[volume.getOptions().size()-1]);
+ }
+ };
+ GlusterDataModelManager.getInstance().addClusterListener(clusterListener);
}
private void setupPageLayout() {
@@ -115,7 +228,7 @@ public class VolumeOptionsPage extends Composite {
setLayout(layout);
}
- private void setupDiskTable(Composite parent) {
+ private void setupOptionsTable(Composite parent) {
Table table = tableViewer.getTable();
table.setHeaderVisible(true);
table.setLinesVisible(false);
@@ -137,64 +250,12 @@ public class VolumeOptionsPage extends Composite {
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<String, String> entry = (Entry<String, String>)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<String, String>) 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() {
+ @SuppressWarnings("unchecked")
@Override
public String getText(Object element) {
return ((Entry<String, String>) element).getValue();
@@ -202,30 +263,50 @@ public class VolumeOptionsPage extends Composite {
});
// User can edit value of a volume option
- valueColumn.setEditingSupport(new OptionValueEditingSupport(valueColumn.getViewer()));
+ valueColumn.setEditingSupport(new OptionValueEditingSupport(valueColumn.getViewer(), volume));
return valueColumn.getColumn();
}
private TableColumn createKeyColumn() {
- TableViewerColumn keyColumn = new TableViewerColumn(tableViewer, SWT.NONE);
+ keyColumn = new TableViewerColumn(tableViewer, SWT.NONE);
keyColumn.getColumn().setText(OPTIONS_TABLE_COLUMN_NAMES[OPTIONS_TABLE_COLUMN_INDICES.OPTION_KEY.ordinal()]);
keyColumn.setLabelProvider(new ColumnLabelProvider() {
+ @SuppressWarnings("unchecked")
@Override
public String getText(Object element) {
return ((Entry<String, String>) element).getKey();
}
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public String getToolTipText(Object element) {
+ String key = ((Entry<String, String>) element).getKey();
+ if(key.isEmpty()) {
+ return "Click to select a volume option key";
+ }
+
+ VolumeOptionInfo optionInfo = GlusterDataModelManager.getInstance().getVolumeOptionInfo(key);
+ // Wrap the description before adding to tooltip so that long descriptions are displayed properly
+ return WordUtils.wrap(optionInfo.getDescription(), 60) + CoreConstants.NEWLINE + "Default value: "
+ + optionInfo.getDefaultValue();
+ }
});
+
+ // Editing support required when adding new key
+ keyEditingSupport = new OptionKeyEditingSupport(keyColumn.getViewer(), volume);
+ keyColumn.setEditingSupport(keyEditingSupport);
+
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);
+ private void createOptionsTableViewer(Composite parent) {
+ tableViewer = new TableViewer(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.SINGLE);
tableViewer.setLabelProvider(new VolumeOptionsTableLabelProvider());
tableViewer.setContentProvider(new ArrayContentProvider());
+ tableViewer.getTable().setLinesVisible(true);
- setupDiskTable(parent);
+ setupOptionsTable(parent);
}
private Composite createTableViewerComposite() {
@@ -235,22 +316,15 @@ public class VolumeOptionsPage extends Composite {
return tableViewerComposite;
}
- private void setupDiskTableViewer(final Text filterText) {
+ private void setupOptionsTableViewer(final Text filterText) {
Composite tableViewerComposite = createTableViewerComposite();
- createDiskTableViewer(tableViewerComposite);
+ createOptionsTableViewer(tableViewerComposite);
+ ColumnViewerToolTipSupport.enableFor(tableViewer);
// Create a case insensitive filter for the table viewer using the filter text field
guiHelper.createFilter(tableViewer, filterText, false);
}
- /**
- * Sets properties for alignment and weight of given column of given table
- *
- * @param table
- * @param columnIndex
- * @param alignment
- * @param weight
- */
- public void setColumnProperties(Table table, OPTIONS_TABLE_COLUMN_INDICES columnIndex, int alignment, int weight) {
+ private void setColumnProperties(Table table, OPTIONS_TABLE_COLUMN_INDICES columnIndex, int alignment, int weight) {
TableColumn column = table.getColumn(columnIndex.ordinal());
column.setAlignment(alignment);
diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/tabcreators/VolumeTabCreator.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/tabcreators/VolumeTabCreator.java
index 7f78829e..6913e211 100644
--- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/tabcreators/VolumeTabCreator.java
+++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/tabcreators/VolumeTabCreator.java
@@ -255,7 +255,7 @@ public class VolumeTabCreator implements TabCreator {
private void createVolumeOptionsTab(Volume volume, TabFolder tabFolder, FormToolkit toolkit) {
Composite volumeTab = guiHelper.createTab(tabFolder, "Options", IImageKeys.VOLUME);
- VolumeOptionsPage page = new VolumeOptionsPage(volumeTab, SWT.NONE, volume);
+ //VolumeOptionsPage page = new VolumeOptionsPage(volumeTab, SWT.NONE, volume);
volumeTab.layout(); // IMP: lays out the form properly
}
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..0e42bba2
--- /dev/null
+++ b/src/com.gluster.storage.management.server.scripts/src/common/DiskUtils.py
@@ -0,0 +1,226 @@
+# Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
+# 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
+# <http://www.gnu.org/licenses/>.
+
+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 readFsTab(fsTabFile=Globals.FSTAB_FILE):
+ try:
+ fsTabfp = open(fsTabFile)
+ except IOError, e:
+ Utils.log("readFsTab(): " + str(e))
+ return None
+
+ fsTabEntryList = []
+ for line in fsTabfp:
+ tokens = line.strip().split()
+ if not tokens or tokens[0].startswith('#'):
+ continue
+ fsTabEntry = {}
+ fsTabEntry["Device"] = None
+ fsTabEntry["MountPoint"] = None
+ fsTabEntry["FsType"] = None
+ fsTabEntry["Options"] = None
+ fsTabEntry["DumpOption"] = 0
+ fsTabEntry["fsckOrder"] = 0
+ try:
+ fsTabEntry["Device"] = tokens[0]
+ fsTabEntry["MountPoint"] = tokens[1]
+ fsTabEntry["FsType"] = tokens[2]
+ fsTabEntry["Options"] = tokens[3]
+ fsTabEntry["DumpOption"] = tokens[4]
+ fsTabEntry["fsckOrder"] = tokens[5]
+ except IndexError:
+ pass
+ if fsTabEntry["Device"] and fsTabEntry["MountPoint"] and fsTabEntry["FsType"] and fsTabEntry["Options"]:
+ fsTabEntryList.append(fsTabEntry)
+
+ fsTabfp.close()
+ return fsTabEntryList
+
+
+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/common/Utils.py b/src/com.gluster.storage.management.server.scripts/src/common/Utils.py
index c605eecd..5140b641 100644
--- a/src/com.gluster.storage.management.server.scripts/src/common/Utils.py
+++ b/src/com.gluster.storage.management.server.scripts/src/common/Utils.py
@@ -34,7 +34,7 @@ import urllib
import Globals
import Protocol
-
+from Common import *
RUN_COMMAND_ERROR = -1024
LOG_SYSLOG = 1
@@ -117,8 +117,7 @@ def openLog(fileName=None):
return False
return True
-
-def log(priority, message=None):
+def record(priority, message=None):
global LOG_FILE_OBJ
global SYSLOG_REQUIRED
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
deleted file mode 100644
index 611d9695..00000000
--- a/src/com.gluster.storage.management.server.scripts/src/nodes/CreateVolumeExportDirectory.py
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/python
-# Copyright (C) 2010 Gluster, Inc. <http://www.gluster.com>
-# 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
-# <http://www.gnu.org/licenses/>.
-import os
-from XmlHandler import ResponseXml
-from optparse import OptionParser
-import Utils
-
-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")
- return rs.toprettyxml()
-
-
- if not os.path.isdir(dirname + "/" + disk + "/" + volumename + "/"):
- command = "mkdir " + volumename;
- rv = Utils.runCommandFG(command, stdout=True, root=True)
- message = stripEmptyLines(rv["Stdout"])
- if rv["Stderr"]:
- message += "Error: [" + stripEmptyLines(rv["Stderr"]) + "]"
- rs = ResponseXml()
- 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
diff --git a/src/com.gluster.storage.management.server.scripts/src/nodes/GlusterdUtils.py b/src/com.gluster.storage.management.server.scripts/src/nodes/GlusterdUtils.py
new file mode 100644
index 00000000..7c0e899c
--- /dev/null
+++ b/src/com.gluster.storage.management.server.scripts/src/nodes/GlusterdUtils.py
@@ -0,0 +1,250 @@
+# Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
+# 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
+# <http://www.gnu.org/licenses/>.
+
+import os
+import Utils
+
+import ServerUtils
+
+
+def getGlusterVolumeInfo(volumeName=None):
+ volumeNameList = None
+ if Utils.isString(volumeName):
+ volumeNameList = [volumeName]
+ if type(volumeName) == type([]):
+ volumeNameList = volumeName
+
+ status = Utils.runCommand("gluster volume info", output=True, root=True)
+ if status["Status"] != 0:
+ Utils.log("Failed to execute 'gluster volume info' command")
+ return None
+
+ volumeInfoDict = {}
+ volumeInfo = {}
+ volumeName = None
+ brickList = []
+ for line in status['Stdout'].split("\n"):
+ if not line:
+ if volumeName and volumeInfo:
+ volumeInfo["Bricks"] = brickList
+ volumeInfoDict[volumeName] = volumeInfo
+ volumeInfo = {}
+ volumeName = None
+ brickList = []
+ continue
+
+ tokens = line.split(":")
+ if tokens[0].strip().upper() == "BRICKS":
+ continue
+ elif tokens[0].strip().upper() == "VOLUME NAME":
+ volumeName = tokens[1].strip()
+ volumeInfo["VolumeName"] = volumeName
+ elif tokens[0].strip().upper() == "TYPE":
+ volumeInfo["VolumeType"] = tokens[1].strip()
+ elif tokens[0].strip().upper() == "STATUS":
+ volumeInfo["VolumeStatus"] = tokens[1].strip()
+ elif tokens[0].strip().upper() == "TRANSPORT-TYPE":
+ volumeInfo["TransportType"] = tokens[1].strip()
+ elif tokens[0].strip().upper().startswith("BRICK"):
+ brickList.append(":".join(tokens[1:]).strip())
+
+ if volumeName and volumeInfo:
+ volumeInfoDict[volumeName] = volumeInfo
+
+ if not volumeNameList:
+ return volumeInfoDict
+
+ # remove unwanted volume info
+ for volumeName in list(set(volumeInfoDict.keys()) - set(volumeNameList)):
+ del volumeInfoDict[volumeName]
+
+ return volumeInfoDict
+
+
+def isVolumeRunning(volumeName):
+ if not volumeName:
+ return False
+ volumeInfo = getGlusterVolumeInfo(volumeName)
+ if not volumeInfo:
+ return False
+ status = volumeInfo[volumeName]["VolumeStatus"]
+ if not status:
+ return False
+ if status.upper() == "STARTED":
+ return True
+ return False
+
+
+def isVolumeExist(volumeName):
+ if not volumeName:
+ return False
+ if getGlusterVolumeInfo(volumeName):
+ return True
+ return False
+
+
+def peerProbe(serverName):
+ command = "gluster peer probe %s" % serverName
+ status = Utils.runCommand(command, output=True, root=True)
+ if status["Status"] == 0:
+ return True
+ Utils.log("command [%s] failed with [%d:%s]" % (command, status["Status"], os.strerror(status["Status"])))
+ return False
+
+
+def setAuthAllow(volumeName, authList, includeServers=True):
+ if not (volumeName and authList):
+ return False
+ vacl = []
+ if includeServers:
+ for serverName in ServerUtils.getAllServerList():
+ vacl += ServerUtils.getServerIpList(serverName)
+ vacl += authList
+
+ command = "gluster volume set %s auth.allow %s" % (volumeName, ",".join(list(set(vacl))))
+ status = Utils.runCommand(command, output=True, root=True)
+ if status["Status"] == 0:
+ return True
+ Utils.log("command [%s] failed with [%d:%s]" % (command, status["Status"], os.strerror(status["Status"])))
+ return False
+
+
+def volumeCreate(volumeName, volumeType, transportTypeList, brickList):
+ command = "gluster volume create %s" % volumeName
+
+ if volumeType.upper() == "MIRROR":
+ command += " replica 2"
+ elif volumeType.upper() == "STRIPE":
+ command += " stripe 4"
+
+ if "RDMA" in transportTypeList:
+ command += " transport rdma"
+
+ command += " " + " ".join(brickList)
+
+ status = Utils.runCommand(command, output=True, root=True)
+ if status["Status"] == 0:
+ return True
+ Utils.log("command [%s] failed with [%d:%s]" % (command, status["Status"], os.strerror(status["Status"])))
+ return False
+
+
+def volumeDelete(volumeName):
+ command = "gluster --mode=script volume delete %s" % volumeName
+ status = Utils.runCommand(command, output=True, root=True)
+ if status["Status"] == 0:
+ return True
+ Utils.log("command [%s] failed with [%d:%s]" % (command, status["Status"], os.strerror(status["Status"])))
+ return False
+
+
+def volumeLogFileName(volumeName, brick, logDir):
+ command = "gluster volume log filename %s %s %s" % (volumeName, brick, logDir)
+ status = Utils.runCommand(command, output=True, root=True)
+ if status["Status"] == 0:
+ return True
+ Utils.log("command [%s] failed with [%d:%s]" % (command, status["Status"], os.strerror(status["Status"])))
+ return False
+
+
+def startVolumeMigration(volumeName, sourcePath, destinationPath):
+ command = "gluster volume replace-brick %s %s %s start" % (volumeName, sourcePath, destinationPath)
+ status = Utils.runCommand(command, output=True, root=True)
+ if status["Status"] == 0:
+ lines = status["Stdout"].split("\n")
+ if lines[0].split()[-1] == "successfully":
+ return True
+ Utils.log("command [%s] failed with [%d:%s]" % (command, status["Status"], os.strerror(status["Status"])))
+ return False
+
+
+def stopVolumeMigration(volumeName, sourcePath, destinationPath):
+ command = "gluster volume replace-brick %s %s %s abort" % (volumeName, sourcePath, destinationPath)
+ status = Utils.runCommand(command, output=True, root=True)
+ if status["Status"] == 0:
+ lines = status["Stdout"].split("\n")
+ if lines[0].split()[-1] == "successful":
+ return True
+ Utils.log("command [%s] failed with [%d:%s]" % (command, status["Status"], os.strerror(status["Status"])))
+ return False
+
+
+def commitVolumeMigration(volumeName, sourcePath, destinationPath):
+ command = "gluster volume replace-brick %s %s %s commit" % (volumeName, sourcePath, destinationPath)
+ status = Utils.runCommand(command, output=True, root=True)
+ if status["Status"] == 0:
+ lines = status["Stdout"].split("\n")
+ if lines[0].split()[-1] == "successful":
+ return True
+ Utils.log("command [%s] failed with [%d:%s]" % (command, status["Status"], os.strerror(status["Status"])))
+ return False
+
+
+def getMigrationStatus(volumeName, sourcePath, destinationPath):
+ command = "gluster volume replace-brick %s %s %s status" % (volumeName, sourcePath, destinationPath)
+ status = Utils.runCommand(command, output=True, root=True)
+ if status['Status'] == 0 and status['Stdout']:
+ lines = status["Stdout"].split("\n")
+ if "Current file" in lines[0]:
+ return "started"
+ if "Migration complete" in lines[0]:
+ return "completed"
+ Utils.log("command [%s] returns unknown status:%s" % (command, lines[0]))
+ return "failed"
+ #if status['Status'] == 0 and status['Stdout']:
+ # for line in status['Stdout'].split('\n'):
+ # words = line.split()
+ # if words and words[0].upper() == "STATUS:":
+ # return " ".join(words[1:]).upper()
+ Utils.log("command [%s] failed with [%d:%s]" % (command, status["Status"], os.strerror(status["Status"])))
+ return None
+
+
+def volumeRebalanceStart(volumeName):
+ command = "gluster volume rebalance %s start" % volumeName
+ status = Utils.runCommand(command, output=True, root=True)
+ if status["Status"] == 0:
+ lines = status["Stdout"].split("\n")
+ if lines[0].split()[-1] == "successful":
+ return True
+ Utils.log("command [%s] failed with [%d:%s]" % (command, status["Status"], os.strerror(status["Status"])))
+ return False
+
+
+def volumeRebalanceStop(volumeName):
+ command = "gluster volume rebalance %s stop" % volumeName
+ status = Utils.runCommand(command, output=True, root=True)
+ if status["Status"] == 0:
+ lines = status["Stdout"].split("\n")
+ if lines[0].split()[0] == "stopped":
+ return True
+ Utils.log("command [%s] failed with [%d:%s]" % (command, status["Status"], os.strerror(status["Status"])))
+ return False
+
+
+def volumeRebalanceStatus(volumeName):
+ command = "gluster volume rebalance %s status" % volumeName
+ status = Utils.runCommand(command, output=True, root=True)
+ if status["Status"] == 0:
+ lines = status["Stdout"].split("\n")
+ if "rebalance not started" in lines[0]:
+ return "not started"
+ if "rebalance completed" in lines[0]:
+ return "completed"
+ return "running"
+ Utils.log("command [%s] failed with [%d:%s]" % (command, status["Status"], os.strerror(status["Status"])))
+ return False
diff --git a/src/com.gluster.storage.management.server.scripts/src/nodes/VolumeUtils.py b/src/com.gluster.storage.management.server.scripts/src/nodes/VolumeUtils.py
new file mode 100644
index 00000000..a19ccd62
--- /dev/null
+++ b/src/com.gluster.storage.management.server.scripts/src/nodes/VolumeUtils.py
@@ -0,0 +1,610 @@
+# Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
+# 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
+# <http://www.gnu.org/licenses/>.
+
+import os
+import glob
+import tempfile
+from operator import itemgetter
+import Globals
+from Protocol import *
+from Utils import *
+from DiskUtils import *
+from ServerUtils import *
+import GlusterdUtils as Glusterd
+
+
+def isVolumeExist(volumeName):
+ volumeDom = XDOM()
+ return volumeDom.parseFile("%s/%s.xml" % (Globals.VOLUME_CONF_DIR, volumeName)) and \
+ Glusterd.isVolumeExist(volumeName)
+
+
+def getVolumeUuid(volumeName):
+ fileName = "%s/%s.xml" % (Globals.VOLUME_CONF_DIR, volumeName)
+ volumeDom = XDOM()
+ if not volumeDom.parseFile(fileName):
+ log("Failed to parse volume configuration file %s of %s" % (fileName, volumeName))
+ return None
+ return volumeDom.getTextByTagRoute("uuid")
+
+
+def readVolumeSmbConfFile(fileName=Globals.VOLUME_SMBCONF_FILE):
+ entryList = []
+ try:
+ fp = open(fileName)
+ for line in fp:
+ tokens = line.split("#")[0].strip().split(";")[0].strip().split("=")
+ if len(tokens) != 2:
+ continue
+ if tokens[0].strip().upper() == "INCLUDE":
+ entryList.append(tokens[1].strip())
+ fp.close()
+ except IOError, e:
+ log("Failed to open file %s: %s" % (fileName, str(e)))
+ return entryList
+
+
+def writeVolumeSmbConfFile(entryList, fileName=Globals.VOLUME_SMBCONF_FILE):
+ try:
+ fp = open(fileName, "w")
+ for entry in entryList:
+ fp.write("include = %s\n" % entry)
+ fp.close()
+ return True
+ except IOError, e:
+ log("Failed to write file %s: %s" % (fileName, str(e)))
+ return False
+
+
+def includeVolume(volumeName, fileName=Globals.VOLUME_SMBCONF_FILE):
+ volumeFile = "%s/%s.smbconf" % (Globals.VOLUME_CONF_DIR, volumeName)
+ if not os.path.exists(volumeFile):
+ return False
+ entryList = readVolumeSmbConfFile(fileName)
+ if volumeFile in entryList:
+ return True
+ entryList.append(volumeFile)
+ return writeVolumeSmbConfFile(entryList, fileName)
+
+
+def excludeVolume(volumeName, fileName=Globals.VOLUME_SMBCONF_FILE):
+ volumeFile = "%s/%s.smbconf" % (Globals.VOLUME_CONF_DIR, volumeName)
+ if not os.path.exists(volumeFile):
+ return False
+ entryList = readVolumeSmbConfFile(fileName)
+ if volumeFile not in entryList:
+ return True
+ entryList.remove(volumeFile)
+ log("entryList = %s" % entryList)
+ return writeVolumeSmbConfFile(entryList, fileName)
+
+
+def writeVolumeCifsConfiguration(volumeName, userList, adminUser=None):
+ volumeFile = "%s/%s.smbconf" % (Globals.VOLUME_CONF_DIR, volumeName)
+ try:
+ fp = open(volumeFile, "w")
+ fp.write("[%s]\n" % volumeName)
+ fp.write(" comment = %s volume served by Gluster\n" % volumeName)
+ fp.write(" path = %s/%s\n" % (Globals.CIFS_EXPORT_DIR, volumeName))
+ fp.write(" guest ok = yes\n")
+ fp.write(" public = yes\n")
+ fp.write(" writable = yes\n")
+ if adminUser:
+ fp.write(" admin users = %s, %s\n" % (adminUser, ", ".join(userList)))
+ fp.write(" valid users = %s, %s\n" % (adminUser, ", ".join(userList)))
+ else:
+ fp.write(" admin users = %s\n" % (", ".join(userList)))
+ fp.write(" valid users = %s\n" % (", ".join(userList)))
+ fp.close()
+ return True
+ except IOError, e:
+ log("Failed to write file %s: %s" % (volumeFile, str(e)))
+ return False
+
+
+def removeVolumeCifsConfiguration(volumeName):
+ volumeFile = "%s/%s.smbconf" % (Globals.VOLUME_CONF_DIR, volumeName)
+ try:
+ os.remove(volumeFile)
+ except OSError, e:
+ log("Failed to remove file %s: %s" % (volumeFile, str(e)))
+
+
+def getVolumeListByPartitionName(partitionName):
+ volumeConfigFileList = glob.glob(Globals.VOLUME_CONF_DIR + "/*.xml")
+ if not volumeConfigFileList:
+ return None
+
+ volumeList = []
+ for volumeXmlFile in volumeConfigFileList:
+ volumeDom = XDOM()
+ volumeDom.parseFile(volumeXmlFile)
+ serverTopology = volumeDom.getElementsByTagRoute("volume.topology.group")
+ serverPartitionFound = False
+ for topology in serverTopology:
+ partitionDom = XDOM()
+ for partition in topology.getElementsByTagName("partition"):
+ partitionDom.setDomObj(partition)
+ if partitionDom.getTextByTagRoute("name") == partitionName:
+ serverPartitionFound = True
+ break
+ if serverPartitionFound:
+ volumeList.append(volumeDom.getElementsByTagRoute("volume")[0])
+ break
+ return volumeList
+
+
+def addServerPartitionConfig(inputDom, groupOrder, partitionTag):
+ if not(inputDom and groupOrder and partitionTag):
+ return False
+ groupDom = XDOM()
+ for group in inputDom.getElementsByTagRoute("topology.group"):
+ groupDom.setDomObj(group)
+ order = groupDom.getTextByTagRoute("order")
+ if order and int(order) == groupOrder:
+ group.appendChild(partitionTag)
+ return inputDom
+ return False
+
+
+def removeServerPartitionConfig(inputDom, partitionName):
+ if not(inputDom and partitionName):
+ return False
+ for group in inputDom.getElementsByTagRoute("topology.group"):
+ partitionDom = XDOM()
+ for partition in group.getElementsByTagName("partition"):
+ partitionDom.setDomObj(partition)
+ if partitionDom.getTextByTagRoute("name") == partitionName:
+ group.removeChild(partition)
+ return inputDom
+ return False
+
+
+def updateServerPartitionConfig(inputDom, partitionName, partitionTag):
+ if not(inputDom and partitionName and partitionTag):
+ return False
+ for group in inputDom.getElementsByTagRoute("topology.group"):
+ partitionDom = XDOM()
+ for partition in group.getElementsByTagName("partition"):
+ partitionDom.setDomObj(partition)
+ if partitionDom.getTextByTagRoute("name") == partitionName:
+ try:
+ group.replaceChild(partitionTag, partition)
+ return inputDom
+ except AttributeError:
+ return False
+ return False
+
+
+def getServerPartitionConfigUuid(serverGroupList, serverPartition):
+ for group in serverGroupList:
+ if not group:
+ continue
+ partitionDom = XDOM()
+ for partition in group.getElementsByTagName("partition"):
+ partitionDom.setDomObj(partition)
+ partitionName = partition.getTextByTagName("name")
+ if not partitionName:
+ continue
+ if partitionName == serverPartition:
+ return partitionDom.getTextByTagName("uuid")
+ return False
+
+
+def setServerPartitionConfigProperty(inputDom, partitionName, propertyDict):
+ if not(inputDom and partitionName and propertyDict):
+ return False
+ for group in inputDom.getElementsByTagRoute("topology.group"):
+ partitionDom = XDOM()
+ for partition in group.getElementsByTagName("partition"):
+ partitionDom.setDomObj(partition)
+ if partitionDom.getTextByTagRoute("name") == partitionName:
+ for part in propertyDict.keys():
+ x = partition.getElementsByTagName(part)
+ if x:
+ x[0].childNodes[0].nodeValue = propertyDict[part]
+ return inputDom
+ return False
+
+
+def getSortedServerPartitionConfigProperty(inputDom):
+ groupDict = {}
+ if not inputDom:
+ return None
+ groupDom = XDOM()
+ for group in inputDom.getElementsByTagRoute("topology.group"):
+ groupDom.setDomObj(group)
+ groupOrder = groupDom.getTextByTagRoute("order")
+ if not groupOrder:
+ return None
+ groupOrder = int(groupOrder)
+ if groupOrder < 1:
+ return None
+ partitionDom = XDOM()
+ partitionDict = {}
+ for partition in group.getElementsByTagName("partition"):
+ partitionDom.setDomObj(partition)
+ partitionName = partitionDom.getTextByTagRoute("name")
+ if not partitionName:
+ return None
+ partitionOrder = partitionDom.getTextByTagRoute("order")
+ if not partitionOrder:
+ return None
+ partitionUuid = partitionDom.getTextByTagRoute("uuid")
+ partitionOrder = int(partitionOrder)
+ if partitionOrder < 1:
+ return None
+ partitionDetails = partitionName.split(":")
+ if not partitionDetails or len(partitionDetails) < 1:
+ return None
+ partitionDict[partitionOrder] = { "order":partitionOrder,
+ "servername":partitionDetails[0],
+ "name":partitionDetails[1],
+ "uuid":partitionUuid}
+ groupDict[groupOrder] = partitionDict
+
+ serverList = []
+ groupOrderList = groupDict.keys()
+ groupOrderList.sort()
+ for groupOrder in groupOrderList:
+ partitionOrderList = groupDict[groupOrder].keys()
+ partitionOrderList.sort()
+ for partitionOrder in partitionOrderList:
+ serverList.append(groupDict[groupOrder][partitionOrder])
+
+ return serverList
+
+
+def getSortedServerPartitionList(serverGroupElements):
+ serverPartitionDict = {}
+ groupOrderList = []
+ serverList = []
+ partitionDom = XDOM()
+ for group in serverGroupElements:
+ if not group:
+ continue
+ groupOrderE = group.getElementsByTagName("order")
+ if not (groupOrderE and groupOrderE[0].childNodes):
+ return None
+ value = int(XDOM.getText(groupOrderE[0].childNodes))
+ if value > 0:
+ groupOrderList.append(value)
+ partitionDict = {}
+ for partition in group.getElementsByTagName("partition"):
+ partitionDom.setDomObj(partition)
+
+ partitionName = partitionDom.getTextByTagRoute("name")
+ if not partitionName:
+ return None
+ partitionOrder = partitionDom.getTextByTagRoute("order")
+ if not partitionOrder:
+ return None
+ partitionUuid = partitionDom.getTextByTagRoute("uuid")
+ partitionDict[int(partitionOrder)] = [partitionName, partitionUuid]
+ serverPartitionDict[value] = partitionDict
+ groupOrderList.sort()
+
+ for groupOrder in groupOrderList:
+ items = serverPartitionDict[groupOrder].items()
+ items.sort(key = itemgetter(0))
+ serverList = serverList + [ items[i][1] for i in range(0,len(items))]
+ return serverList
+
+
+def clearExportDirectory(serverList, volumeName, volumeUuid):
+ thisServerName = getCurrentServerName()
+ for exportServer in serverList:
+ serverName, partition = exportServer[0].split(":")
+ if thisServerName != serverName:
+ continue
+ partitionUuid = getUuidByDiskPartition(getDevice(partition))
+ if not partitionUuid:
+ log("unable to find uuid of partition %s" % partition)
+ return False
+ volumeDirName = "%s/%s/%s" % (Globals.GLUSTER_LUN_DIR, partitionUuid, volumeUuid)
+ if os.path.exists(volumeDirName):
+ ## Removing /data/PARTITION-UUID/VOLUME-UUID/
+ ## TODO: Get an option to remove it at this time
+ if runCommandFG("mv -f %s %s.delete" % (volumeDirName, volumeDirName), root=True) != 0:
+ return False
+ if runCommandFG("rm -f %s/%s/volumes/%s" % (Globals.GLUSTER_LUN_DIR, partitionUuid, volumeName), root=True) != 0:
+ return False
+ return True
+
+
+def createExportDirectory(serverList, volumeName, volumeUuid):
+ thisServerName = getCurrentServerName()
+ tempVolumeNameFile = getTempFileName()
+
+ try:
+ fp = open(tempVolumeNameFile, "w")
+ fp.write("VOLUME_NAME=%s\n" % volumeName)
+ fp.write("VOLUME_UUID=%s\n" % volumeUuid)
+ fp.close()
+ except IOError, e:
+ log("failed to create temporary file for volume-name: %s" % (volumeName, str(e)))
+ return False
+
+ for exportServer in serverList:
+ serverName, partition = exportServer[0].split(":")
+ if thisServerName != serverName:
+ continue
+ partitionUuid = getUuidByDiskPartition(getDevice(partition))
+ if not partitionUuid:
+ log("unable to find uuid of partition %s" % partition)
+ return False
+
+ volumeDirName = "%s/%s/%s" % (Globals.GLUSTER_LUN_DIR, partitionUuid, volumeUuid)
+ ## Creating /data/PARTITION-UUID/VOLUME-UUID/
+ if runCommandFG("mkdir %s" % volumeDirName, root=True) != 0:
+ return False
+
+ ## Creating /data/PARTITION-UUID/VOLUME-UUID/exports/
+ ## Creating /data/PARTITION-UUID/VOLUME-UUID/exports/brick1/
+ if runCommandFG("mkdir -p %s/exports/brick1" % volumeDirName, root=True) != 0:
+ return False
+
+ ## Creating /data/PARTITION-UUID/VOLUME-UUID/log/
+ if runCommandFG("mkdir %s/log" % volumeDirName, root=True) != 0:
+ return False
+
+ ## Creating /data/PARTITION-UUID/VOLUME-UUID/config/
+ if runCommandFG("mkdir %s/config" % volumeDirName, root=True) != 0:
+ return False
+
+ volumeLinkDirName = "%s/%s/volumes" % (Globals.GLUSTER_LUN_DIR, partitionUuid)
+ if not os.path.exists(volumeLinkDirName):
+ if runCommandFG("mkdir %s" % volumeLinkDirName, root=True) != 0:
+ return False
+
+ ## Creating symlink
+ ## /data/PARTITION-UUID/volumes/VOLUME-NAME -> /data/PARTITION-UUID/VOLUME-UUID/
+ command = "ln -fTs %s %s/%s" % (volumeDirName,
+ volumeLinkDirName, volumeName)
+ if runCommandFG(command, root=True) != 0:
+ return False
+
+ if runCommandFG("cp -f %s %s/config/volume-name" % (tempVolumeNameFile, volumeDirName), root=True) != 0:
+ return False
+
+ try:
+ os.remove(tempVolumeNameFile)
+ except OSError, e:
+ log("Failed to remove file %s: %s" % (tempVolumeNameFile, str(e)))
+
+ return True
+
+
+def getPartitionListByServerName(volumeDom, serverName, serverPartitionList=None):
+ partitionList = {}
+ if serverPartitionList:
+ for partitionName in serverPartitionList:
+ partitionUuid = getServerDiskPartitionUuid(serverName, partitionName)
+ if not partitionUuid:
+ log(syslog.LOG_ERR, "failed to get disk partition %s uuid of server %s" % (partitionName, serverName))
+ return None
+ partitionList[partitionName] = partitionUuid
+ return partitionList
+ for group in volumeDom.getElementsByTagRoute("topology.group"):
+ for partitionTag in group.getElementsByTagName("partition"):
+ nameE = partitionTag.getElementsByTagName("name")
+ if not nameE:
+ continue
+ partition = XDOM.getText(nameE[0].childNodes)
+ if not partition:
+ continue
+ server, partitionName = partition.split(":")
+ if server != serverName:
+ continue
+ partitionUuid = getServerDiskPartitionUuid(serverName, partitionName)
+ if not partitionUuid:
+ log(syslog.LOG_ERR, "failed to get disk partition %s uuid of server %s" % (partitionName, serverName))
+ return None
+ partitionList[partitionName] = partitionUuid
+ return partitionList
+
+
+def isVolumeRunning(volumeName):
+ return Glusterd.isVolumeRunning(volumeName)
+
+def addVolumeMigrationDetails(sourcePartition, destinationPartition, volumeName):
+ migrationDom = XDOM()
+ if not os.path.exists(Globals.VOLUME_MIGRATION_LIST_FILE):
+ migrationDom.appendTagRoute("volume-migration")
+ else:
+ if not migrationDom.parseFile(Globals.VOLUME_MIGRATION_LIST_FILE):
+ log("Failed to load volume-migration.xml file")
+ return None
+ migrationList = migrationDom.getElementsByTagRoute("volume-migration.migration")
+ for tagE in migrationList:
+ dom = XDOM()
+ dom.setDomObj(tagE)
+ if dom.getTextByTagRoute("source-partition") == sourcePartition and \
+ dom.getTextByTagRoute("destination-partition") == destinationPartition and \
+ dom.getTextByTagRoute("volume-name") == volumeName:
+ return False
+ migrationTag = migrationDom.getElementsByTagRoute("volume-migration")
+ if not migrationTag:
+ return None
+ partitionTag = migrationDom.createTag("migration")
+ partitionTag.appendChild(migrationDom.createTag("source-partition", sourcePartition))
+ partitionTag.appendChild(migrationDom.createTag("destination-partition", destinationPartition))
+ partitionTag.appendChild(migrationDom.createTag("volume-name", volumeName))
+ migrationTag[0].appendChild(partitionTag)
+ if not migrationDom.writexml(Globals.VOLUME_MIGRATION_LIST_FILE):
+ log("Unable to write disk migration details into %s/volume-migration.xml" % Globals.GLUSTER_BASE_DIR)
+ return False
+ return True
+
+
+def removeVolumeMigrationDetails(sourcePartition, destinationPartition, volumeName):
+ migrationDom = XDOM()
+ if not os.path.exists(Globals.VOLUME_MIGRATION_LIST_FILE):
+ return None
+ if not migrationDom.parseFile(Globals.VOLUME_MIGRATION_LIST_FILE):
+ log("Failed to load volume-migration.xml file")
+ return None
+ migrationList = migrationDom.getElementsByTagRoute("volume-migration.migration")
+ for tagE in migrationList:
+ dom = XDOM()
+ dom.setDomObj(tagE)
+ if dom.getTextByTagRoute("source-partition") == sourcePartition and \
+ dom.getTextByTagRoute("destination-partition") == destinationPartition and \
+ dom.getTextByTagRoute("volume-name") == volumeName:
+ migrationDom.getElementsByTagRoute("volume-migration")[0].removeChild(tagE)
+ if not migrationDom.writexml(Globals.VOLUME_MIGRATION_LIST_FILE):
+ log("Unable to write disk migration details into %s/volume-migration.xml" % Globals.GLUSTER_BASE_DIR)
+ return False
+ return True
+
+
+def addPartitionMigrationDetails(sourcePartition, destinationPartition, volumeList=None):
+ migrationDom = XDOM()
+ if not os.path.exists(Globals.MIGRATE_PARTITION_LIST_FILE):
+ migrationDom.appendTagRoute("partition-migration")
+ else:
+ if not migrationDom.parseFile(Globals.MIGRATE_PARTITION_LIST_FILE):
+ log("Failed to load migration.xml file")
+ return None
+ migrationList = migrationDom.getElementsByTagRoute("partition-migration.migration")
+ for tagE in migrationList:
+ dom = XDOM()
+ dom.setDomObj(tagE)
+ if dom.getTextByTagRoute("source-partition") == sourcePartition:
+ return False
+ if dom.getTextByTagRoute("destination-partition") == destinationPartition:
+ return False
+ migrationTag = migrationDom.getElementsByTagRoute("partition-migration")
+ if not migrationTag:
+ return None
+ partitionTag = migrationDom.createTag("migration")
+ partitionTag.appendChild(migrationDom.createTag("source-partition", sourcePartition))
+ partitionTag.appendChild(migrationDom.createTag("destination-partition", destinationPartition))
+ migrationTag[0].appendChild(partitionTag)
+ if not migrationDom.writexml(Globals.MIGRATE_PARTITION_LIST_FILE):
+ log("Unable to write disk migration details into %s/migration.xml" % Globals.GLUSTER_BASE_DIR)
+ return False
+ if volumeList:
+ for volumeName in volumeList:
+ addVolumeMigrationDetails(sourcePartition, destinationPartition, volumeName)
+ return True
+
+
+def removePartitionMigrationDetails(sourcePartition, destinationPartition, volumeList=None):
+ migrationDom = XDOM()
+ if not os.path.exists(Globals.MIGRATE_PARTITION_LIST_FILE):
+ return None
+ if not migrationDom.parseFile(Globals.MIGRATE_PARTITION_LIST_FILE):
+ log("Failed to load migration.xml file")
+ return None
+ migrationList = migrationDom.getElementsByTagRoute("partition-migration.migration")
+ for tagE in migrationList:
+ dom = XDOM()
+ dom.setDomObj(tagE)
+ if dom.getTextByTagRoute("source-partition") == sourcePartition and \
+ dom.getTextByTagRoute("destination-partition") == destinationPartition:
+ migrationDom.getElementsByTagRoute("partition-migration")[0].removeChild(tagE)
+ if not migrationDom.writexml(Globals.MIGRATE_PARTITION_LIST_FILE):
+ log("Unable to write disk migration details into %s/migration.xml" % Globals.GLUSTER_BASE_DIR)
+ return False
+ if volumeList:
+ for volumeName in volumeList:
+ removeVolumeMigrationDetails(sourcePartition, destinationPartition, volumeName)
+ return True
+
+
+def isMigrationInProgress(partition):
+ migrationDom = XDOM()
+ if not os.path.exists(Globals.MIGRATE_PARTITION_LIST_FILE):
+ return None
+ if not migrationDom.parseFile(Globals.MIGRATE_PARTITION_LIST_FILE):
+ log("Failed to load migration.xml file")
+ return None
+ migrationList = migrationDom.getElementsByTagRoute("partition-migration.migration")
+ for tagE in migrationList:
+ dom = XDOM()
+ dom.setDomObj(tagE)
+ if migrationDom.getTextByTagRoute("source-partition") == partition or \
+ migrationDom.getTextByTagRoute("destination-partition") == partition:
+ return True
+ return False
+
+
+def getServerDiskPartitionUuid(serverName, partition):
+ diskConfigDom = XDOM()
+ if not diskConfigDom.parseFile("%s/%s/disk.xml" % (Globals.SERVER_CONF_DIR, serverName)):
+ return None
+ for disk in diskConfigDom.getElementsByTagRoute("disks.disk"):
+ diskDom = XDOM()
+ diskDom.setDomObj(disk)
+ partitionList = diskDom.getElementsByTagRoute("partition")
+ for tagE in partitionList:
+ partitionDom = XDOM()
+ partitionDom.setDomObj(tagE)
+ if partitionDom.getTextByTagRoute("device") == partition:
+ return partitionDom.getTextByTagRoute("uuid")
+
+
+def getVolumeServerList(requestDom, requestFlag=True):
+ if requestFlag:
+ serverGroupElementList = requestDom.getElementsByTagRoute("command.volume.topology.group")
+ else:
+ serverGroupElementList = requestDom.getElementsByTagRoute("volume.topology.group")
+ if not serverGroupElementList:
+ return None
+ serverList = []
+ partitionDom = XDOM()
+ for group in serverGroupElementList:
+ for partition in group.getElementsByTagName("partition"):
+ partitionDom.setDomObj(partition)
+ partitionName = partitionDom.getTextByTagRoute("name")
+ if not partitionName:
+ continue
+ serverPartition = partitionName.split(":")
+ if not(len(serverPartition) > 1 and serverPartition[1]):
+ return None
+ if serverPartition[0] not in serverList:
+ serverList.append(serverPartition[0])
+ return serverList
+
+
+def getVolumeServerListByName(volumeName):
+ serverList = []
+ serverDom = XDOM()
+ volumeDom = XDOM()
+ if not os.path.exists("%s/%s.xml" % (Globals.VOLUME_CONF_DIR, volumeName)):
+ return False
+ if not volumeDom.parseFile("%s/%s.xml" % (Globals.VOLUME_CONF_DIR, volumeName)):
+ return False
+ return getVolumeServerList(volumeDom, False)
+
+
+def getMigrateVolumeServerPartitionInfo(volumeName):
+ volumeMigrationDom = XDOM()
+ if not volumeMigrationDom.parseFile(Globals.VOLUME_MIGRATION_LIST_FILE):
+ Utils.log("Failed to parse file %s" % Globals.VOLUME_MIGRATION_LIST_FILE)
+ return None
+ volumeInfo = {}
+ dom = XDOM()
+ for tagE in volumeMigrationDom.getElementsByTagRoute("volume-migration.migration"):
+ dom.setDomObj(tagE)
+ if dom.getTextByTagRoute("volume-name") == volumeName:
+ volumeInfo['Name'] = volumeName
+ volumeInfo['SourcePartition'] = dom.getTextByTagRoute("source-partition")
+ volumeInfo['DestinationPartition'] = dom.getTextByTagRoute("destination-partition")
+ return volumeInfo
+ return None
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()
diff --git a/src/com.gluster.storage.management.server.scripts/src/nodes/clear_volume_directory.py b/src/com.gluster.storage.management.server.scripts/src/nodes/clear_volume_directory.py
new file mode 100755
index 00000000..3bd0ab6f
--- /dev/null
+++ b/src/com.gluster.storage.management.server.scripts/src/nodes/clear_volume_directory.py
@@ -0,0 +1,101 @@
+#!/usr/bin/python
+# Copyright (C) 2010 Gluster, Inc. <http://www.gluster.com>
+# 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
+# <http://www.gnu.org/licenses/>.
+import os
+import sys
+import syslog
+import time
+from XmlHandler import ResponseXml
+import DiskUtils
+import Utils
+import Common
+from optparse import OptionParser
+
+def clearVolumeDirectory(disk, volumeName, todelete):
+
+ # 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()
+
+ # clear volume directory from the disk
+ volumeDirectory = "%s/%s" % (diskMountPoint, volumeName)
+ newVolumeDirectoryName = "%s_%s" % (volumeDirectory, time.time())
+ command = ["sudo", "mv", "-f", volumeDirectory, newVolumeDirectoryName]
+ rv = Utils.runCommandFG(command, stdout=True, root=True)
+ message = Common.stripEmptyLines(rv["Stdout"])
+ if rv["Stderr"]:
+ error = Common.stripEmptyLines(rv["Stderr"])
+ message += "Error: [%s]" % (error)
+ Common.log(syslog.LOG_ERR, "failed to rename volume directory %s, %s" % (volumeDirectory, error))
+ rs.appendTagRoute("status.code", rv["Status"])
+ rs.appendTagRoute("status.message", message)
+ return rs.toprettyxml()
+
+ if not todelete:
+ rv["Status"] = "0"
+ rs.appendTagRoute("status.code", rv["Status"])
+ rs.appendTagRoute("status.message", message)
+ return rs.toprettyxml()
+
+ command = ["sudo", "rm", "-fr", newVolumeDirectoryName]
+ rv = Utils.runCommandFG(command, stdout=True, root=True)
+ message = Common.stripEmptyLines(rv["Stdout"])
+ if rv["Stderr"]:
+ error = Common.stripEmptyLines(rv["Stderr"])
+ message += "Error: [%s]" % (error)
+ Common.log(syslog.LOG_ERR, "failed to clear volume directory %s, %s" % (newVolumeDirectoryName, error))
+ rs.appendTagRoute("status.code", rv["Status"])
+ rs.appendTagRoute("status.message", message)
+ return rs.toprettyxml()
+
+ if not rv["Status"]:
+ rv["Status"] = "0"
+ rs.appendTagRoute("status.code", rv["Status"])
+ rs.appendTagRoute("status.message", message)
+ return rs.toprettyxml()
+
+def main():
+ parser = OptionParser()
+ parser.add_option("-d", "--delete", dest="deletedir", action="store_true", default=False, help="force delete")
+ (options, args) = parser.parse_args()
+
+ if len(args) != 2:
+ print >> sys.stderr, "usage: %s <disk name> <volume name> [-d/--delete]" % sys.argv[0]
+ sys.exit(-1)
+
+ disk = args[0]
+ volumeName = args[1]
+ print clearVolumeDirectory(disk, volumeName, options.deletedir)
+ sys.exit(0)
+
+if __name__ == "__main__":
+ main()
+
diff --git a/src/com.gluster.storage.management.server.scripts/src/nodes/create_volume_directory.py b/src/com.gluster.storage.management.server.scripts/src/nodes/create_volume_directory.py
new file mode 100755
index 00000000..b8fb2166
--- /dev/null
+++ b/src/com.gluster.storage.management.server.scripts/src/nodes/create_volume_directory.py
@@ -0,0 +1,85 @@
+#!/usr/bin/python
+# Copyright (C) 2010 Gluster, Inc. <http://www.gluster.com>
+# 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
+# <http://www.gnu.org/licenses/>.
+import os
+import sys
+import syslog
+from XmlHandler import ResponseXml
+import DiskUtils
+import Utils
+import Common
+
+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()
+
+ # creating volume directory under disk mount point
+ volumeDirectory = "%s/%s" % (diskMountPoint, volumeName)
+ if os.path.exists(volumeDirectory):
+ Common.log(syslog.LOG_ERR, "Volume directory:%s already exists" % (volumeDirectory))
+ rs.appendTagRoute("status.code", "-2")
+ rs.appendTagRoute("status.message", "Volume directory already exists!")
+ return rs.toprettyxml()
+
+ if not os.path.exists(volumeDirectory):
+ command = ["sudo", "mkdir", volumeDirectory]
+ rv = Utils.runCommandFG(command, stdout=True, root=True)
+ message = Common.stripEmptyLines(rv["Stdout"])
+ if rv["Stderr"]:
+ 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()
+
+ if not rv["Status"]:
+ rv["Status"] = "0"
+ if rv["Status"] == "0":
+ message = volumeDirectory
+ rs.appendTagRoute("status.code", rv["Status"])
+ rs.appendTagRoute("status.message", message)
+ return rs.toprettyxml()
+
+def main():
+ if len(sys.argv) != 3:
+ print >> sys.stderr, "usage: %s <disk name> <volume name>" % sys.argv[0]
+ sys.exit(-1)
+
+ disk = sys.argv[1]
+ volumeName = sys.argv[2]
+ print createDirectory(disk, volumeName)
+ sys.exit(0)
+
+if __name__ == "__main__":
+ main()
diff --git a/src/com.gluster.storage.management.server.scripts/src/nodes/get_disk_mount_point.py b/src/com.gluster.storage.management.server.scripts/src/nodes/get_disk_mount_point.py
new file mode 100755
index 00000000..b2274b4d
--- /dev/null
+++ b/src/com.gluster.storage.management.server.scripts/src/nodes/get_disk_mount_point.py
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+# Copyright (C) 2010 Gluster, Inc. <http://www.gluster.com>
+# 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
+# <http://www.gnu.org/licenses/>.
+
+import os
+import syslog
+import Common
+from DiskUtils import *
+from XmlHandler import ResponseXml
+
+
+def getmountpoint(path):
+ if not path:
+ Common.log(syslog.LOG_ERR, "Not a valid path:%s" % path)
+ rs.appendTagRoute("status.code", "-1")
+ rs.appendTagRoute("status.message", "Error: given path name is empty")
+ return rs.toprettyxml()
+
+ rs = ResponseXml()
+ mountPoint = None
+
+ for line in readFsTab():
+ if path.startswith(line['MountPoint']):
+ if not mountPoint:
+ mountPoint = line['MountPoint']
+ if len(line['MountPoint']) > len(mountPoint):
+ mountPoint = line['MountPoint']
+
+ if "/" == mountPoint or not mountPoint:
+ Common.log(syslog.LOG_ERR, "failed to find mount point of the given path:%s" % path)
+ rs.appendTagRoute("status.code", "-1")
+ rs.appendTagRoute("status.message", "Error: Unable to find disk mount point")
+ return rs.toprettyxml()
+
+ rs.appendTagRoute("status.code", "0")
+ rs.appendTagRoute("status.message", mountPoint)
+ return rs.toprettyxml()
+
+def main():
+ if len(sys.argv) != 2:
+ print >> sys.stderr, "usage: %s <path>" % sys.argv[0]
+ sys.exit(-1)
+
+ path = sys.argv[1]
+ print getmountpoint(path)
+ sys.exit(0)
+
+if __name__ == "__main__":
+ main()
+
diff --git a/src/com.gluster.storage.management.server.scripts/src/nodes/get_disk_name_by_path.py b/src/com.gluster.storage.management.server.scripts/src/nodes/get_disk_name_by_path.py
new file mode 100755
index 00000000..72eb80dd
--- /dev/null
+++ b/src/com.gluster.storage.management.server.scripts/src/nodes/get_disk_name_by_path.py
@@ -0,0 +1,69 @@
+#!/usr/bin/python
+# Copyright (C) 2010 Gluster, Inc. <http://www.gluster.com>
+# 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
+# <http://www.gnu.org/licenses/>.
+
+import os
+import syslog
+import Common
+from DiskUtils import *
+from XmlHandler import ResponseXml
+
+
+def getmountpoint(path):
+ if not path:
+ Common.log(syslog.LOG_ERR, "Not a valid path:%s" % path)
+ rs.appendTagRoute("status.code", "-1")
+ rs.appendTagRoute("status.message", "Error: given path name is empty")
+ return rs.toprettyxml()
+
+ rs = ResponseXml()
+ mountPoint = None
+ fsTabEntry = None
+ for line in readFsTab():
+ if path.startswith(line['MountPoint']):
+ if not mountPoint:
+ mountPoint = line['MountPoint']
+ fsTabEntry = line
+ if len(line['MountPoint']) > len(mountPoint):
+ mountPoint = line['MountPoint']
+ fsTabEntry = line
+
+ if "/" == mountPoint or not mountPoint:
+ Common.log(syslog.LOG_ERR, "failed to find mount point of the given path:%s" % path)
+ rs.appendTagRoute("status.code", "-1")
+ rs.appendTagRoute("status.message", "Error: Unable to find disk mount point")
+ return rs.toprettyxml()
+
+ rs.appendTagRoute("status.code", "0")
+ if fsTabEntry["Device"].startswith("UUID="):
+ rs.appendTagRoute("status.message", getDiskPartitionByUuid(fsTabEntry["Device"].split("UUID=")[-1]))
+ else:
+ rs.appendTagRoute("status.message", "Unable to find disk name")
+ return rs.toprettyxml()
+
+def main():
+ if len(sys.argv) != 2:
+ print >> sys.stderr, "usage: %s <path>" % sys.argv[0]
+ sys.exit(-1)
+
+ path = sys.argv[1]
+ print getmountpoint(path)
+ sys.exit(0)
+
+if __name__ == "__main__":
+ main()
+
diff --git a/src/com.gluster.storage.management.server.scripts/src/nodes/get_volume_brick_log.py b/src/com.gluster.storage.management.server.scripts/src/nodes/get_volume_brick_log.py
new file mode 100755
index 00000000..7c912412
--- /dev/null
+++ b/src/com.gluster.storage.management.server.scripts/src/nodes/get_volume_brick_log.py
@@ -0,0 +1,109 @@
+#!/usr/bin/python
+# Copyright (C) 2009,2010 Gluster, Inc. <http://www.gluster.com>
+# 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
+# <http://www.gnu.org/licenses/>.
+
+import re
+import os
+import sys
+from XmlHandler import ResponseXml
+
+def enumLogType(logCode):
+ if "M" == logCode.upper():
+ return "EMERGENCY"
+ elif "A" == logCode.upper():
+ return "ALERT"
+ elif "C" == logCode.upper():
+ return "CRITICAL"
+ elif "E" == logCode.upper():
+ return "ERROR"
+ elif "W" == logCode.upper():
+ return "WARNING"
+ elif "N" == logCode.upper():
+ return "NOTICE"
+ elif "I" == logCode.upper():
+ return "INFO"
+ elif "D" == logCode.upper():
+ return "DEBUG"
+ elif "T" == logCode.upper():
+ return "TRACE"
+ else:
+ return "UNKNOWN"
+##--end of enumLogType()
+
+def addLog(responseDom, logMessageTag, loginfo):
+ logTag = responseDom.createTag("logMessage", None)
+ logTag.appendChild(responseDom.createTag("timestamp", loginfo[0] + " " + loginfo[1]))
+ logTag.appendChild(responseDom.createTag("severity", enumLogType(loginfo[2])))
+ logTag.appendChild(responseDom.createTag("message", loginfo[3]))
+ logMessageTag.appendChild(logTag)
+ return True
+##--end of addLog()
+
+def logSplit(log):
+ loginfo = log.strip().split(None, 3)
+ loginfo[0] = loginfo[0][1:] #-- Remove '['
+ loginfo[1] = loginfo[1][0:-1] #-- Remove ']'
+ return loginfo
+##--end of logSplit()
+
+def getVolumeLog(logFilePath, tailCount):
+ rs = ResponseXml()
+ if not logFilePath:
+ rs.appendTagRoute("status.code", "-1")
+ rs.appendTagRoute("status.message", "No log file path given")
+ return rs.toprettyxml()
+
+ if not tailCount:
+ rs.appendTagRoute("status.code", "-1")
+ rs.appendTagRoute("status.message", "No tail count given")
+ return rs.toprettyxml()
+
+ pattern = '\[\d{4}-\d{2}-\d{2}\s{1}\d{2}:\d{2}:\d{2}.\d+\]\s{1}([MACEWNIDT]){1}\s+'
+ logMessagesTag = rs.createTag("logMessages")
+ if not os.path.exists(logFilePath):
+ rs.appendTagRoute("status.code", "-1")
+ rs.appendTagRoute("status.message", "volume log file [%s] not found!" % logFilePath)
+ return rs.toprettyxml
+
+ fp = open(logFilePath)
+ #lines = [line for line in fp]
+ lines = [line for line in fp if re.match(pattern, line)]
+ fp.close()
+ i = len(lines) - int(tailCount)
+ if i < 0:
+ i = 0
+ for log in lines[i:]:
+ loginfo = logSplit(log)
+ addLog(rs, logMessagesTag, loginfo)
+ rs.appendTagRoute("status.code", "0")
+ rs.appendTagRoute("status.message", "Success")
+ rs.appendTag(logMessagesTag)
+ return rs.toprettyxml()
+##--end of getVolumeLog()
+
+def main():
+ if len(sys.argv) != 3:
+ print >> sys.stderr, "usage: %s <Log File Path> <Line Count>" % sys.argv[0]
+ sys.exit(-1)
+
+ logFilePath = sys.argv[1]
+ tailCount = sys.argv[2]
+ print getVolumeLog(logFilePath, tailCount)
+ sys.exit(0)
+
+if __name__ == "__main__":
+ main()
diff --git a/src/com.gluster.storage.management.server.scripts/src/nodes/get_volume_log.py b/src/com.gluster.storage.management.server.scripts/src/nodes/get_volume_log.py
new file mode 100755
index 00000000..826ade6e
--- /dev/null
+++ b/src/com.gluster.storage.management.server.scripts/src/nodes/get_volume_log.py
@@ -0,0 +1,132 @@
+# Copyright (C) 2009,2010 Gluster, Inc. <http://www.gluster.com>
+# 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
+# <http://www.gnu.org/licenses/>.
+
+import Globals
+import syslog
+import Commands
+import Utils
+from VolumeUtils import *
+from XmlHandler import ResponseXml
+
+
+def enumLogType(logCode):
+ if "M" == logCode.upper():
+ return "EMERGENCY"
+ elif "A" == logCode.upper():
+ return "ALERT"
+ elif "C" == logCode.upper():
+ return "CRITICAL"
+ elif "E" == logCode.upper():
+ return "ERROR"
+ elif "W" == logCode.upper():
+ return "WARNING"
+ elif "N" == logCode.upper():
+ return "NOTICE"
+ elif "I" == logCode.upper():
+ return "INFO"
+ elif "D" == logCode.upper():
+ return "DEBUG"
+ elif "T" == logCode.upper():
+ return "TRACE"
+ else:
+ return "UNKNOWN"
+##--end of enumLogType()
+
+
+def addLog(responseDom, logMessageTag, loginfo):
+ logTag = responseDom.createTag("log", None)
+ logTag.appendChild(responseDom.createTag("date", loginfo[0]))
+ logTag.appendChild(responseDom.createTag("time", loginfo[1]))
+ logTag.appendChild(responseDom.createTag("type", enumLogType(loginfo[2])))
+ logTag.appendChild(responseDom.createTag("message", loginfo[3]))
+ logMessageTag.appendChild(logTag)
+ return True
+##--end of addLog()
+
+
+def logSplit(log):
+ loginfo = log.strip().split(None, 3)
+ loginfo[0] = loginfo[0][1:] #-- Remove '['
+ loginfo[1] = loginfo[1][0:-1] #-- Remove ']'
+ return loginfo
+##--end of logSplit()
+
+
+def getVolumeLog(volumeName, tailCount):
+ rs = ResponseXml()
+ if not volumeName:
+ rs.appendTagRoute("status.code", "-1")
+ rs.appendTagRoute("status.message", "No volume name given")
+ return rs.toprettyxml()
+
+ if not tailCount:
+ rs.appendTagRoute("status.code", "-1")
+ rs.appendTagRoute("status.message", "No tail count given")
+ return rs.toprettyxml()
+
+ thisServerName = getCurrentServerName()
+ if not thisServerName:
+ rs.appendTagRoute("status.code", "-2")
+ rs.appendTagRoute("status.message", "Failed to get current server name")
+ return rs.toprettyxml()
+
+ volumeDom = XDOM()
+ partitionList = getPartitionListByServerName(volumeDom, thisServerName)
+ if not partitionList:
+ rs.appendTagRoute("status.code", "-3")
+ rs.appendTagRoute("status.message", "Failed to get server partition details")
+ return rs.toprettyxml()
+
+ pattern = '\[\d{4}-\d{2}-\d{2}\s{1}\d{2}:\d{2}:\d{2}.\d+\]\s{1}([MACEWNIDT]){1}\s+'
+ logMessagesTag = rs.createTag("response.logMessages")
+ for partitionName in partitionList:
+ logMessageTag = rs.createTag("logMessage")
+ logMessageTag.appendChild("disk", "%s:%s" % (thisServerName, partitionName))
+
+ logDirectory = "%s/%s/%s/log" % (Globals.GLUSTER_LUN_DIR, partitionList[partitionName], volumeUuid)
+ logFileName = "%s/%s-%s-%s-exports-brick1.log" % (logDirectory,
+ Globals.GLUSTER_LUN_DIR[1:],
+ partitionList[partitionName],
+ volumeUuid)
+ if not os.path.exists(logFileName):
+ Utils.log("volume log file not found %s" % logFileName)
+ continue
+ fp = open(logFileName)
+ lines = [line for line in fp if re.match(pattern, line)]
+ fp.close()
+ i = len(lines) - int(tailCount)
+ if i < 0:
+ i = 0
+ for log in lines[i:]:
+ loginfo = logSplit(log)
+ addLog(rs, logMessageTag, loginfo)
+ logMessagesTag.appendChild(logMessageTag)
+ return rs.toprettyxml()
+##--end of getVolumeLog()
+
+def main():
+ if len(sys.argv) != 3:
+ print >> sys.stderr, "usage: %s <disk name> <volume name>" % sys.argv[0]
+ sys.exit(-1)
+
+ volumeName = sys.argv[1]
+ tailCount = sys.argv[2]
+ print getVolumeLog(volumeName, tailCount)
+ sys.exit(0)
+
+if __name__ == "__main__":
+ main()
diff --git a/src/com.gluster.storage.management.server.scripts/src/nodes/multicast_response.py b/src/com.gluster.storage.management.server.scripts/src/nodes/multicast_response.py
index 64bc0899..dba65c07 100644
--- a/src/com.gluster.storage.management.server.scripts/src/nodes/multicast_response.py
+++ b/src/com.gluster.storage.management.server.scripts/src/nodes/multicast_response.py
@@ -16,10 +16,26 @@
# along with this program. If not, see
# <http://www.gnu.org/licenses/>.
+import os
+import string
+import time
+import Utils
import socket
import struct
import Globals
+def isinpeer():
+ command = "gluster peer status"
+ status = Utils.runCommand(command, output=True, root=True)
+ if status["Status"] == 0:
+ return True
+ #lines = status["Stdout"].split("\n")
+ #for line in lines:
+ # if string.upper(line).startswith("HOSTNAME: %s" % string.upper(socket.gethostname)):
+ # return True
+ Utils.log("command [%s] failed with [%d:%s]" % (command, status["Status"], os.strerror(status["Status"])))
+ return False
+
def response(multiCastGroup, port):
# waiting for the request!
socketRequest = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
@@ -33,6 +49,9 @@ def response(multiCastGroup, port):
#TODO: Remove infinite loop and make this as a deamon (service)
while True:
+ if isinpeer():
+ time.sleep(5)
+ continue
request = socketRequest.recvfrom(1024)
if request and request[0].upper() == "SERVERDISCOVERY":
socketSend.sendto(socket.gethostname(), (multiCastGroup, port))
diff --git a/src/com.gluster.storage.management.server/WebContent/WEB-INF/web.xml b/src/com.gluster.storage.management.server/WebContent/WEB-INF/web.xml
index 34337a5d..6d7d1406 100644
--- a/src/com.gluster.storage.management.server/WebContent/WEB-INF/web.xml
+++ b/src/com.gluster.storage.management.server/WebContent/WEB-INF/web.xml
@@ -37,6 +37,10 @@
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.gluster.storage.management.server.resources</param-value>
</init-param>
+ <init-param>
+ <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
+ <param-value>com.gluster.storage.management.server.filters.GlusterResourceFilterFactory</param-value>
+ </init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
diff --git a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/constants/VolumeOptionsDefaults.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/constants/VolumeOptionsDefaults.java
index 4093a4ee..5c9a6505 100644
--- a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/constants/VolumeOptionsDefaults.java
+++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/constants/VolumeOptionsDefaults.java
@@ -100,9 +100,9 @@ public class VolumeOptionsDefaults {
volumeOptionsInfo.add(new VolumeOptionInfo("diagnostics.dump-fd-stats",
"Statistics related to file-operations would be tracked inside GlusterFS data-structures.", "off"));
volumeOptionsInfo.add(new VolumeOptionInfo("diagnostics.brick-log-level",
- "Changes the log-level of the bricks (servers).", "NORMAL"));
+ "Changes the log-level of the bricks (servers).", "INFO"));
volumeOptionsInfo.add(new VolumeOptionInfo("diagnostics.client-log-level",
- "Changes the log-level of the clients.", "NORMAL"));
+ "Changes the log-level of the clients.", "INFO"));
volumeOptionsInfo.add(new VolumeOptionInfo("nfs.enable-ino32",
"Use this option from the CLI to make Gluster NFS return 32-bit inode numbers instead of 64-bit.",
"off"));
diff --git a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/filters/AuditFilter.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/filters/AuditFilter.java
new file mode 100644
index 00000000..b23d9c4f
--- /dev/null
+++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/filters/AuditFilter.java
@@ -0,0 +1,31 @@
+/**
+ *
+ */
+package com.gluster.storage.management.server.filters;
+
+import com.sun.jersey.spi.container.ContainerRequest;
+import com.sun.jersey.spi.container.ContainerRequestFilter;
+import com.sun.jersey.spi.container.ContainerResponseFilter;
+import com.sun.jersey.spi.container.ResourceFilter;
+
+/**
+ * Resource filter for maintaining audit trail of resource access
+ */
+public class AuditFilter implements ResourceFilter, ContainerRequestFilter {
+
+ @Override
+ public ContainerRequestFilter getRequestFilter() {
+ return this;
+ }
+
+ @Override
+ public ContainerResponseFilter getResponseFilter() {
+ return null;
+ }
+
+ @Override
+ public ContainerRequest filter(ContainerRequest req) {
+ System.out.println("Resource access [" + req.getMethod() + "][" + req.getPath() + "]");
+ return req;
+ }
+}
diff --git a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/filters/GlusterResourceFilterFactory.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/filters/GlusterResourceFilterFactory.java
new file mode 100644
index 00000000..899ba16e
--- /dev/null
+++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/filters/GlusterResourceFilterFactory.java
@@ -0,0 +1,31 @@
+/**
+ *
+ */
+package com.gluster.storage.management.server.filters;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.sun.jersey.api.model.AbstractMethod;
+import com.sun.jersey.spi.container.ResourceFilter;
+import com.sun.jersey.spi.container.ResourceFilterFactory;
+
+/**
+ * Gluster resource filter factory. As of now, this creates only one filter - the audit filter {@code AuditFilter}
+ */
+public class GlusterResourceFilterFactory implements ResourceFilterFactory {
+
+ public GlusterResourceFilterFactory() {
+ }
+
+ /* (non-Javadoc)
+ * @see com.sun.jersey.spi.container.ResourceFilterFactory#create(com.sun.jersey.api.model.AbstractMethod)
+ */
+ @Override
+ public List<ResourceFilter> create(AbstractMethod arg0) {
+ List<ResourceFilter> filters = new ArrayList<ResourceFilter>();
+ filters.add(new AuditFilter());
+
+ return filters;
+ }
+}
diff --git a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/AlertsResource.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/AlertsResource.java
index c4948596..e33e06c9 100644
--- a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/AlertsResource.java
+++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/AlertsResource.java
@@ -53,7 +53,7 @@ public class AlertsResource {
// Alert #4
alert = new Alert();
alert.setId("0004");
- alert.setReference("Volume2:server2:sda1"); // volume:[Disk name]
+ alert.setReference("Volume3:server2:sda1"); // volume:[Disk name]
alert.setType(Alert.ALERT_TYPES.OFFLINE_VOLUME_DISKS_ALERT);
alert.setMessage(alert.getAlertType(alert.getType()) + " in volume [" + alert.getReference().split(":")[0]
+ "] disk [" + alert.getReference().split(":")[1] + ":" + alert.getReference().split(":")[2] + "]");
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 42f7760e..c1f0435c 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
@@ -37,8 +37,8 @@ import com.gluster.storage.management.core.model.Status;
import com.gluster.storage.management.core.response.GenericResponse;
import com.gluster.storage.management.core.response.GlusterServerListResponse;
import com.gluster.storage.management.core.response.GlusterServerResponse;
-import com.gluster.storage.management.core.utils.GlusterUtil;
import com.gluster.storage.management.core.utils.ProcessResult;
+import com.gluster.storage.management.server.utils.GlusterUtil;
import com.sun.jersey.spi.resource.Singleton;
@Component
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..586bf5a3 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
@@ -24,14 +24,29 @@ import static com.gluster.storage.management.core.constants.RESTConstants.FORM_P
import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_VALUE_START;
import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_VALUE_STOP;
import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_VOLUME_NAME;
+import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DELETE_OPTION;
+import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DISKS;
+import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DISK_NAME;
+import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_FROM_TIMESTAMP;
+import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_LINE_COUNT;
+import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_LOG_SEVERITY;
+import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_TO_TIMESTAMP;
+import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_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_DISKS;
+import static com.gluster.storage.management.core.constants.RESTConstants.SUBRESOURCE_LOGS;
import static com.gluster.storage.management.core.constants.RESTConstants.SUBRESOURCE_OPTIONS;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
import java.util.List;
import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
@@ -39,24 +54,35 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
+import com.gluster.storage.management.core.constants.CoreConstants;
import com.gluster.storage.management.core.constants.RESTConstants;
+import com.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import com.gluster.storage.management.core.model.LogMessage;
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.response.GenericResponse;
+import com.gluster.storage.management.core.response.LogMessageListResponse;
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.DateUtil;
import com.gluster.storage.management.server.constants.VolumeOptionsDefaults;
+import com.gluster.storage.management.server.utils.GlusterUtil;
import com.gluster.storage.management.server.utils.ServerUtil;
import com.sun.jersey.api.core.InjectParam;
+import com.sun.jersey.api.representation.Form;
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 = "create_volume_directory.py";
+ private static final String VOLUME_DIRECTORY_CLEANUP_SCRIPT = "clear_volume_directory.py";
+ private static final String VOLUME_BRICK_LOG_SCRIPT = "get_volume_brick_log.py";
+
@InjectParam
private static ServerUtil serverUtil;
private final GlusterUtil glusterUtil = new GlusterUtil();
@@ -71,6 +97,7 @@ public class VolumesResource {
return new VolumeListResponse(Status.STATUS_SUCCESS, glusterUtil.getAllVolumes());
} catch (Exception e) {
// TODO: log the error
+ e.printStackTrace();
return new VolumeListResponse(new Status(Status.STATUS_CODE_FAILURE, e.getMessage()), null);
}
}
@@ -79,24 +106,27 @@ public class VolumesResource {
@Consumes(MediaType.TEXT_XML)
@Produces(MediaType.TEXT_XML)
public Status createVolume(Volume volume) {
- //Create the directories for the volume
- List<String> bricks = new ArrayList<String>();
- for(String disk : volume.getDisks()) {
-
- String brickNotation = prepareBrick(volume, disk);
- if (brickNotation != null) {
- bricks.add(brickNotation);
+ // Create the directories for the volume
+ List<String> disks = volume.getDisks();
+ Status status = createDirectories(disks, volume.getName());
+ if (status.isSuccess()) {
+ List<String> bricks = Arrays.asList(status.getMessage().split(" "));
+ status = glusterUtil.createVolume(volume, bricks);
+ if (status.isSuccess()) {
+ Status optionsStatus = glusterUtil.createOptions(volume);
+ if (!optionsStatus.isSuccess()) {
+ status.setCode(Status.STATUS_CODE_PART_SUCCESS);
+ status.setMessage("Error while setting volume options: " + optionsStatus);
+ }
} 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() + "]");
+ Status cleanupStatus = cleanupDirectories(disks, volume.getName(), disks.size(), "-d"); // delete permanently
+ if (!cleanupStatus.isSuccess()) {
+ status.setMessage(status.getMessage() + CoreConstants.NEWLINE + "Cleanup errors: "
+ + CoreConstants.NEWLINE + cleanupStatus);
+ }
}
}
-
- return glusterUtil.createVolume(volume, bricks);
+ return status;
}
@GET
@@ -105,7 +135,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)
@@ -121,6 +151,42 @@ public class VolumesResource {
return new Status(Status.STATUS_CODE_FAILURE, "Invalid operation code [" + operation + "]");
}
+ @DELETE
+ @Path("{" + PATH_PARAM_VOLUME_NAME + "}")
+ @Produces(MediaType.TEXT_XML)
+ public Status deleteVolume(@QueryParam(QUERY_PARAM_VOLUME_NAME) String volumeName,
+ @QueryParam(QUERY_PARAM_DELETE_OPTION) String deleteOption) {
+ Volume volume = glusterUtil.getVolume(volumeName);
+ Status status = glusterUtil.deleteVolume(volumeName);
+
+ if (status.isSuccess()) {
+ List<String> disks = volume.getDisks();
+ Status postDeleteStatus = postDelete(volumeName, disks, deleteOption);
+
+ if (!postDeleteStatus.isSuccess()) {
+ status.setCode(Status.STATUS_CODE_PART_SUCCESS);
+ status.setMessage("Error while post deletion operation: " + postDeleteStatus);
+ }
+ }
+ return status;
+ }
+
+ private Status postDelete(String volumeName, List<String> disks, String deleteFlag) {
+ String serverName, diskName, diskInfo[];
+ Status result;
+ for (int i = 0; i < disks.size(); i++) {
+ diskInfo = disks.get(i).split(":");
+ serverName = diskInfo[0];
+ diskName = diskInfo[1];
+ result = (Status) serverUtil.executeOnServer(true, serverName, VOLUME_DIRECTORY_CLEANUP_SCRIPT + " "
+ + diskName + " " + volumeName + " " + deleteFlag, Status.class);
+ if (!result.isSuccess()) {
+ return result;
+ }
+ }
+ return new Status(Status.STATUS_CODE_SUCCESS, "Post volume delete operation successfully initiated");
+ }
+
@POST
@Path("{" + PATH_PARAM_VOLUME_NAME + " }/" + SUBRESOURCE_OPTIONS)
@Produces(MediaType.TEXT_XML)
@@ -129,7 +195,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,25 +211,244 @@ 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) {
- String serverName = disk.split(":")[0];
- String diskName = disk.split(":")[1];
- Status result = (Status)serverUtil.executeOnServer(true, serverName, SCRIPT_NAME + " " + vol.getName() + " " + diskName, Status.class);
+
+ @SuppressWarnings("rawtypes")
+ private Status prepareBrick(String serverName, String diskName, String volumeName) {
+ return (Status) ((GenericResponse) serverUtil.executeOnServer(true, serverName, PREPARE_BRICK_SCRIPT + " "
+ + diskName + " " + volumeName, GenericResponse.class)).getStatus();
+ }
+
+ private Status createDirectories(List<String> disks, String volumeName) {
+ List<String> bricks = new ArrayList<String>();
+ Status status = null;
+ for (int i = 0; i < disks.size(); i++) {
+ String disk = disks.get(i);
+
+ String[] diskParts = disk.split(":");
+ String serverName = diskParts[0];
+ String diskName = diskParts[1];
+ try {
+ status = prepareBrick(serverName, diskName, volumeName);
+ } catch (Exception e) {
+ status = new Status(e);
+ }
+ if (status.isSuccess()) {
+ String brickDir = status.getMessage().trim();
+ bricks.add(serverName + ":" + brickDir);
+ } else {
+ // Brick preparation failed. Cleanup directories already created and return failure status
+ Status cleanupStatus = cleanupDirectories(disks, volumeName, i + 1, "-d"); // delete permanently
+ if (!cleanupStatus.isSuccess()) {
+ // append cleanup error to prepare brick error
+ status.setMessage(status.getMessage() + CoreConstants.NEWLINE + cleanupStatus.getMessage());
+ }
+ return status;
+ }
+ }
+ status.setMessage(bricksAsString(bricks));
+ return status;
+ }
+
+ //TODO Can be removed and use StringUtil.ListToString(List<String> list, String delimiter)
+ private String bricksAsString(List<String> bricks) {
+ String bricksStr = "";
+ for (String brickInfo : bricks) {
+ bricksStr += brickInfo + " ";
+ }
+ return bricksStr.trim();
+ }
+
+ @SuppressWarnings("rawtypes")
+ private Status cleanupDirectories(List<String> disks, String volumeName, int maxIndex, String deleteFlag) {
+ String serverName, diskName, diskInfo[];
+ Status result;
+ for (int i = 0; i < maxIndex; i++) {
+ diskInfo = disks.get(i).split(":");
+ serverName = diskInfo[0];
+ diskName = diskInfo[1];
+ result = ((GenericResponse) serverUtil.executeOnServer(true, serverName, VOLUME_DIRECTORY_CLEANUP_SCRIPT + " "
+ + diskName + " " + volumeName + " " + deleteFlag, GenericResponse.class)).getStatus();
+ if (!result.isSuccess()) {
+ return result;
+ }
+ }
+ return new Status(Status.STATUS_CODE_SUCCESS, "Directories cleaned up successfully!");
+ }
+
+ private List<LogMessage> getBrickLogs(Volume volume, String brickName, Integer lineCount)
+ throws GlusterRuntimeException {
+ // brick name format is <serverName>:<brickDirectory>
+ String[] brickParts = brickName.split(":");
+ String serverName = brickParts[0];
+ String brickDir = brickParts[1];
- if(result.isSuccess()) {
- return result.getMessage();
+ String logDir = glusterUtil.getLogLocation(volume.getName(), brickName);
+ String logFileName = glusterUtil.getLogFileNameForBrickDir(brickDir);
+ String logFilePath = logDir + CoreConstants.FILE_SEPARATOR + logFileName;
+
+ // Usage: get_volume_disk_log.py <volumeName> <diskName> <lineCount>
+ Object responseObj = serverUtil.executeOnServer(true, serverName, VOLUME_BRICK_LOG_SCRIPT
+ + " " + logFilePath + " " + lineCount, LogMessageListResponse.class);
+ Status status = null;
+ LogMessageListResponse response = null;
+ if(responseObj instanceof LogMessageListResponse) {
+ response = (LogMessageListResponse)responseObj;
+ status = response.getStatus();
} else {
- return null;
+ status = (Status)responseObj;
+ }
+
+ if (!status.isSuccess()) {
+ throw new GlusterRuntimeException(status.toString());
+ }
+
+ // populate disk and trim other fields
+ List<LogMessage> logMessages = response.getLogMessages();
+ for(LogMessage logMessage : logMessages) {
+ logMessage.setDisk(getDiskForBrick(volume, brickName));
+ logMessage.setMessage(logMessage.getMessage().trim());
+ logMessage.setSeverity(logMessage.getSeverity().trim());
}
+ return logMessages;
+ }
+
+ @GET
+ @Path("{" + PATH_PARAM_VOLUME_NAME + "}/" + SUBRESOURCE_LOGS)
+ public LogMessageListResponse getLogs(@PathParam(PATH_PARAM_VOLUME_NAME) String volumeName,
+ @QueryParam(QUERY_PARAM_DISK_NAME) String diskName, @QueryParam(QUERY_PARAM_LOG_SEVERITY) String severity,
+ @QueryParam(QUERY_PARAM_FROM_TIMESTAMP) String fromTimestamp,
+ @QueryParam(QUERY_PARAM_TO_TIMESTAMP) String toTimestamp,
+ @QueryParam(QUERY_PARAM_LINE_COUNT) Integer lineCount) {
+ List<LogMessage> logMessages = null;
+
+ try {
+ Volume volume = getVolume(volumeName);
+ if (diskName == null || diskName.isEmpty()) {
+ logMessages = getLogsForAllBricks(volume, lineCount);
+ } else {
+ // fetch logs for given brick of the volume
+ logMessages = getBrickLogs(volume, getBrickForDisk(volume, diskName), lineCount);
+ }
+ } catch (Exception e) {
+ return new LogMessageListResponse(new Status(e), null);
+ }
+
+ filterLogsBySeverity(logMessages, severity);
+ filterLogsByTime(logMessages, fromTimestamp, toTimestamp);
+ return new LogMessageListResponse(Status.STATUS_SUCCESS, logMessages);
+ }
+
+ private void filterLogsByTime(List<LogMessage> logMessages, String fromTimestamp, String toTimestamp) {
+ Date fromTime = null, toTime = null;
+
+ if(fromTimestamp != null && !fromTimestamp.isEmpty()) {
+ fromTime = DateUtil.stringToDate(fromTimestamp);
+ }
+
+ if(toTimestamp != null && !toTimestamp.isEmpty()) {
+ toTime = DateUtil.stringToDate(toTimestamp);
+ }
+
+ List<LogMessage> messagesToRemove = new ArrayList<LogMessage>();
+ for(LogMessage logMessage : logMessages) {
+ Date logTimestamp = logMessage.getTimestamp();
+ if(fromTime != null && logTimestamp.before(fromTime)) {
+ messagesToRemove.add(logMessage);
+ continue;
+ }
+
+ if(toTime != null && logTimestamp.after(toTime)) {
+ messagesToRemove.add(logMessage);
+ }
+ }
+ logMessages.removeAll(messagesToRemove);
+ }
+
+ private void filterLogsBySeverity(List<LogMessage> logMessages, String severity) {
+ if(severity == null || severity.isEmpty()) {
+ return;
+ }
+
+ List<LogMessage> messagesToRemove = new ArrayList<LogMessage>();
+ for(LogMessage logMessage : logMessages) {
+ if(!logMessage.getSeverity().equals(severity)) {
+ messagesToRemove.add(logMessage);
+ }
+ }
+ logMessages.removeAll(messagesToRemove);
+ }
+
+ private List<LogMessage> getLogsForAllBricks(Volume volume, Integer lineCount) {
+ List<LogMessage> logMessages;
+ logMessages = new ArrayList<LogMessage>();
+ // fetch logs for every brick of the volume
+ for (String brick : volume.getBricks()) {
+ logMessages.addAll(getBrickLogs(volume, brick, lineCount));
+ }
+
+ // Sort the log messages based on log timestamp
+ Collections.sort(logMessages, new Comparator<LogMessage>() {
+ @Override
+ public int compare(LogMessage message1, LogMessage message2) {
+ return message1.getTimestamp().compareTo(message2.getTimestamp());
+ }
+ });
+
+ return logMessages;
}
- public static void main(String[] args) {
+ @POST
+ @Path("{" + QUERY_PARAM_VOLUME_NAME + "}/" + SUBRESOURCE_DISKS)
+ public Status addDisks(@PathParam(QUERY_PARAM_VOLUME_NAME) String volumeName, @FormParam(QUERY_PARAM_DISKS) String disks) {
+
+ List<String> diskList = Arrays.asList( disks.split(",") ); // Convert from comma separated sting (query parameter) to list
+ Status status = createDirectories(diskList, volumeName);
+ if (status.isSuccess()) {
+ List<String> bricks = Arrays.asList(status.getMessage().split(" "));
+ status = glusterUtil.addBricks(volumeName, bricks);
+
+ if (!status.isSuccess()) {
+ Status cleanupStatus = cleanupDirectories(diskList, volumeName, diskList.size(), "-d"); // Remove the directories if created
+ if (!cleanupStatus.isSuccess()) {
+ // append cleanup error to prepare brick error
+ status.setMessage(status.getMessage() + CoreConstants.NEWLINE + cleanupStatus.getMessage());
+ }
+ }
+ }
+ return status;
+ }
+
+ private String getBrickForDisk(Volume volume, String diskName) {
+ int index = volume.getDisks().indexOf(diskName);
+ return volume.getBricks().get(index);
+ }
+
+ private String getDiskForBrick(Volume volume, String brickName) {
+ int index = volume.getBricks().indexOf(brickName);
+ return volume.getDisks().get(index);
+ }
+
+ public static void main(String[] args) throws ClassNotFoundException {
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());
- }
+ // VolumeListResponse response = vr.getAllVolumes();
+ // for (Volume volume : response.getVolumes()) {
+ // System.out.println("\nName:" + volume.getName() + "\nType: " + volume.getVolumeTypeStr() + "\nStatus: "
+ // + volume.getStatusStr());
+ // }
+ Volume volume = new Volume();
+ volume.setName("vol3");
+ volume.setTransportType(TRANSPORT_TYPE.ETHERNET);
+ List<String> disks = new ArrayList<String>();
+ disks.add("192.168.1.210:sdb");
+ volume.addDisks(disks);
+ volume.setAccessControlList("192.168.*");
+ // Status status = vr.createVolume(volume);
+ // System.out.println(status.getMessage());
+ Form form = new Form();
+ form.add("volumeName", volume.getName());
+ form.add(RESTConstants.FORM_PARAM_DELETE_OPTION, 1);
+ Status status = vr.deleteVolume("Vol2", "1");
+ System.out.println("Code : " + status.getCode());
+ System.out.println("Message " + status.getMessage());
}
}
diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterUtil.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/GlusterUtil.java
index 35bba55d..30f73595 100644
--- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterUtil.java
+++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/GlusterUtil.java
@@ -18,7 +18,7 @@
* along with this program. If not, see
* <http://www.gnu.org/licenses/>.
*/
-package com.gluster.storage.management.core.utils;
+package com.gluster.storage.management.server.utils;
import java.util.ArrayList;
import java.util.List;
@@ -34,6 +34,8 @@ 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.utils.ProcessResult;
+import com.gluster.storage.management.core.utils.ProcessUtil;
public class GlusterUtil {
private static final String glusterFSminVersion = "3.1";
@@ -46,10 +48,14 @@ public class GlusterUtil {
private static final String VOLUME_NAME_PFX = "Volume Name:";
private static final String VOLUME_TYPE_PFX = "Type:";
private static final String VOLUME_STATUS_PFX = "Status:";
+ private static final String VOLUME_NUMBER_OF_BRICKS = "Number of Bricks:";
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 String VOLUME_LOG_LOCATION_PFX = "log file location:";
+ private static final String VOLUME_TYPE_DISTRIBUTE = "Distribute";
+ private static final String VOLUME_TYPE_REPLICATE = "Replicate";
private static final ProcessUtil processUtil = new ProcessUtil();
/**
@@ -165,23 +171,22 @@ public class GlusterUtil {
VOLUME_TYPE volType = volume.getVolumeType();
if (volType == VOLUME_TYPE.DISTRIBUTED_MIRROR) {
volumeType = "replica";
- count = 2;
+ count = volume.getReplicaCount();
} else if (volType == VOLUME_TYPE.DISTRIBUTED_STRIPE) {
volumeType = "stripe";
- count = 4;
+ count = volume.getStripeCount();
}
String transportTypeStr = null;
TRANSPORT_TYPE transportType = volume.getTransportType();
transportTypeStr = (transportType == TRANSPORT_TYPE.ETHERNET) ? "tcp" : "rdma";
-
List<String> command = prepareVolumeCreateCommand(volume, bricks, count, volumeType, transportTypeStr);
ProcessResult result = processUtil.executeCommand(command);
- if(!result.isSuccess()) {
+ if (!result.isSuccess()) {
// TODO: Perform cleanup on all nodes before returning
return new Status(result);
}
-
+
return createOptions(volume);
}
@@ -202,7 +207,7 @@ public class GlusterUtil {
return command;
}
- private Status createOptions(Volume volume) {
+ public Status createOptions(Volume volume) {
Map<String, String> options = volume.getOptions();
if (options != null) {
for (Entry<String, String> option : options.entrySet()) {
@@ -229,6 +234,10 @@ public class GlusterUtil {
return new Status(processUtil.executeCommand(command));
}
+ public Status deleteVolume(String volumeName) {
+ return new Status(processUtil.executeCommand("gluster", "--mode=script", "volume", "delete", volumeName));
+ }
+
private String getVolumeInfo(String volumeName) {
ProcessResult result = new ProcessUtil().executeCommand("gluster", "volume", "info", volumeName);
if (!result.isSuccess()) {
@@ -246,17 +255,40 @@ 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
+ if (volumeType.equals(VOLUME_TYPE_DISTRIBUTE)) {
+ volume.setVolumeType(VOLUME_TYPE.PLAIN_DISTRIBUTE);
+ } else if (volumeType.equals(VOLUME_TYPE_REPLICATE)) {
+ volume.setVolumeType(VOLUME_TYPE.DISTRIBUTED_MIRROR);
+ volume.setReplicaCount(Volume.DEFAULT_REPLICA_COUNT);
+ } else {
+ volume.setVolumeType(VOLUME_TYPE.DISTRIBUTED_STRIPE);
+ volume.setStripeCount(Volume.DEFAULT_STRIPE_COUNT);
+ }
return true;
}
return false;
}
-
+
+
+ private void readReplicaOrStripeCount(Volume volume, String line) {
+ if (extractToken(line, "x") != null) {
+ // expected formated of line is "Number of Bricks: 3 x 2 = 6"
+ int count = Integer.parseInt(line.split("x")[1].split("=")[0].trim());
+ if (volume.getVolumeType() == VOLUME_TYPE.DISTRIBUTED_STRIPE) {
+ volume.setStripeCount(count);
+ } else if (volume.getVolumeType() == VOLUME_TYPE.DISTRIBUTED_MIRROR) {
+ volume.setReplicaCount(count);
+ volume.setStripeCount(0);
+ }
+
+ }
+ return;
+ }
+
private boolean readVolumeStatus(Volume volume, String line) {
String volumeStatus = extractToken(line, VOLUME_STATUS_PFX);
if (volumeStatus != null) {
@@ -265,38 +297,62 @@ public class GlusterUtil {
}
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);
+ 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());
+ String[] brickParts = line.split(":");
+ String serverName = brickParts[1].trim();
+ String brickDir = brickParts[2].trim();
+
+ volume.addBrick(serverName + ":" + brickDir);
+ detectAndAddDiskToVolume(volume, serverName, brickDir);
return true;
}
return false;
}
-
+
+
+ private void detectAndAddDiskToVolume(Volume volume, String serverName, String brickDir) {
+ // brick directory should be of the form /export/<diskname>/volume-name
+ try {
+ volume.addDisk(serverName + ":" + brickDir.split("/")[2].trim());
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // brick directory of a different form, most probably created manually
+ // connect to the server and get disk for the brick directory
+ Status status = new ServerUtil().getDiskForDir(serverName, brickDir);
+ if (status.isSuccess()) {
+ volume.addDisk(serverName + ":" + status.getMessage());
+ } else {
+ // Couldn't fetch disk for the brick directory. Log error and add "unknown" as disk name.
+ System.out.println("Couldn't fetch disk name for brick [" + serverName + ":" + brickDir + "]");
+ volume.addDisk(serverName + ":unknown");
+ }
+ }
+ }
+
+
private boolean readBrickGroup(String line) {
- return extractToken(line, VOLUME_BRICKS_GROUP_PFX) != null;
+ 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());
+ if (line.matches("^[^:]*:.*$")) {
+ int index = line.indexOf(':');
+ volume.setOption(line.substring(0, index).trim(), line.substring(index + 1, line.length()).trim());
return true;
}
return false;
@@ -304,7 +360,7 @@ public class GlusterUtil {
public Volume getVolume(String volumeName) {
List<Volume> volumes = parseVolumeInfo(getVolumeInfo(volumeName));
- if(volumes.size() > 0) {
+ if (volumes.size() > 0) {
return volumes.get(0);
}
return null;
@@ -324,7 +380,6 @@ 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);
}
@@ -337,11 +392,13 @@ public class GlusterUtil {
if (readVolumeType(volume, line))
continue;
+ if (extractToken(line, VOLUME_NUMBER_OF_BRICKS) != null) {
+ readReplicaOrStripeCount(volume, line);
+ }
if (readVolumeStatus(volume, line))
continue;
- if(readTransportType(volume, line))
+ if (readTransportType(volume, line))
continue;
-
if (readBrickGroup(line)) {
isBricksGroupFound = true;
continue;
@@ -361,7 +418,7 @@ public class GlusterUtil {
}
if (isOptionReconfigFound) {
- if(readOption(volume, line)) {
+ if (readOption(volume, line)) {
continue;
} else {
isOptionReconfigFound = false;
@@ -375,8 +432,51 @@ public class GlusterUtil {
return volumes;
}
+
+ public Status addBricks(String volumeName, List<String> bricks) {
+ List<String> command = new ArrayList<String>();
+ command.add("gluster");
+ command.add("volume");
+ command.add("add-brick");
+ command.add(volumeName);
+ command.addAll(bricks);
+ return new Status(processUtil.executeCommand(command));
+ }
+
+
+ public String getLogLocation(String volumeName, String brickName) {
+ ProcessResult result = new ProcessUtil().executeCommand("gluster", "volume", "log", "locate", volumeName,
+ brickName);
+ if (!result.isSuccess()) {
+ throw new GlusterRuntimeException("Command [gluster volume info] failed with error: ["
+ + result.getExitValue() + "][" + result.getOutput() + "]");
+ }
+ String output = result.getOutput();
+ if (output.startsWith(VOLUME_LOG_LOCATION_PFX)) {
+ return output.substring(VOLUME_LOG_LOCATION_PFX.length()).trim();
+ }
+
+ throw new GlusterRuntimeException("Couldn't parse output of [volume log locate] command. [" + output
+ + "] doesn't start with prefix [" + VOLUME_LOG_LOCATION_PFX + "]");
+ }
+
+ public String getLogFileNameForBrickDir(String brickDir) {
+ String logFileName = brickDir;
+ if (logFileName.startsWith(CoreConstants.FILE_SEPARATOR)) {
+ logFileName = logFileName.replaceFirst(CoreConstants.FILE_SEPARATOR, "");
+ }
+ logFileName = logFileName.replaceAll(CoreConstants.FILE_SEPARATOR, "-") + ".log";
+ return logFileName;
+ }
+
+
public static void main(String args[]) {
// List<String> names = new GlusterUtil().getGlusterServerNames();
// System.out.println(names);
+ List<String> disks = new ArrayList<String>();
+ disks.add("server1:sda");
+ disks.add("server1:sdb");
+ Status status = new GlusterUtil().addBricks("Volume3", disks);
+ System.out.println(status);
}
}
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 645b7991..5e423f1c 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
@@ -49,6 +49,7 @@ public class ServerUtil {
private static final String SCRIPT_DIR = "scripts";
private static final String SCRIPT_COMMAND = "python";
+ private static final String REMOTE_SCRIPT_GET_DISK_FOR_DIR = "get_disk_for_dir.py";
public ProcessResult executeGlusterScript(boolean runInForeground, String scriptName, List<String> arguments) {
List<String> command = new ArrayList<String>();
@@ -73,6 +74,7 @@ public class ServerUtil {
* @param expectedClass Class of the object expected from script execution
* @return Response from remote execution of the command
*/
+ @SuppressWarnings("rawtypes")
public Object executeOnServer(boolean runInForeground, String serverName, String commandWithArgs, Class expectedClass) {
StringBuffer output = new StringBuffer();
try {
@@ -91,8 +93,6 @@ public class ServerUtil {
}
connection.close();
- System.out.println("The ouput string is : " + output.toString());
-
return unmarshal(expectedClass, output.toString(), expectedClass != Status.class);
} catch(Exception e) {
// any other exception means unexpected error. return status with error from exception.
@@ -112,6 +112,7 @@ public class ServerUtil {
* 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.
*/
+ @SuppressWarnings("rawtypes")
private Object unmarshal(Class expectedClass, String input, boolean tryStatusOnFailure) {
try {
// create JAXB context and instantiate marshaller
@@ -133,4 +134,13 @@ public class ServerUtil {
// CreateVolumeExportDirectory.py md0 testvol
System.out.println(new ServerUtil().executeOnServer(true, "localhost", "python CreateVolumeExportDirectory.py md0 testvol", Status.class));
}
+
+ /**
+ * @param serverName Server on which the directory is present
+ * @param brickDir Directory whose disk is to be fetched
+ * @return Status object containing the disk name, or error message in case the remote script fails.
+ */
+ public Status getDiskForDir(String serverName, String brickDir) {
+ return (Status) executeOnServer(true, serverName, REMOTE_SCRIPT_GET_DISK_FOR_DIR + " " + brickDir, Status.class);
+ }
} \ No newline at end of file