diff options
Diffstat (limited to 'src')
15 files changed, 487 insertions, 108 deletions
diff --git a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/GlusterDataModelManager.java b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/GlusterDataModelManager.java index 0f932df0..a8134c7d 100644 --- a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/GlusterDataModelManager.java +++ b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/GlusterDataModelManager.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.Map; import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; import com.gluster.storage.management.core.model.Cluster; @@ -41,9 +42,10 @@ import com.gluster.storage.management.core.model.Volume; import com.gluster.storage.management.core.model.Volume.TRANSPORT_TYPE; import com.gluster.storage.management.core.model.Volume.VOLUME_STATUS; import com.gluster.storage.management.core.model.Volume.VOLUME_TYPE; +import com.gluster.storage.management.core.model.VolumeOptionInfo; import com.gluster.storage.management.core.response.RunningTaskListResponse; import com.gluster.storage.management.core.response.VolumeListResponse; -import com.gluster.storage.management.client.VolumesClient; +import com.gluster.storage.management.core.response.VolumeOptionInfoListResponse; public class GlusterDataModelManager { // private Server discoveredServer1, discoveredServer2, discoveredServer3, @@ -57,6 +59,7 @@ public class GlusterDataModelManager { private String securityToken; private String serverName; private List<ClusterListener> listeners = new ArrayList<ClusterListener>(); + private List<VolumeOptionInfo> volumeOptionsDefaults; private GlusterDataModelManager() { } @@ -130,12 +133,20 @@ public class GlusterDataModelManager { createDummyLogMessages(); initializeRunningTasks(cluster); - initializeAlerts(cluster); + initializeVolumeOptionsDefaults(); model.addCluster(cluster); } + private void initializeVolumeOptionsDefaults() { + VolumeOptionInfoListResponse response = new VolumesClient(getSecurityToken()).getVolumeOptionsDefaults(); + if(!response.getStatus().isSuccess()) { + throw new GlusterRuntimeException("Error fetching volume option defaults: [" + response.getStatus().getMessage() + "]"); + } + this.volumeOptionsDefaults = response.getOptions(); + } + private void addVolumeOptions() { for (Volume vol : new Volume[] { volume1, volume2, volume3, volume4, volume5 }) { for (int i = 1; i <= 5; i++) { @@ -376,6 +387,13 @@ public class GlusterDataModelManager { listener.volumeChanged(volume, new Event(EVENT_TYPE.VOLUME_STATUS_CHANGED, newStatus)); } } + + public void resetVolumeOptions(Volume volume) { + volume.getOptions().clear(); + for (ClusterListener listener : listeners) { + listener.volumeChanged(volume, new Event(EVENT_TYPE.VOLUME_OPTIONS_RESET, null)); + } + } public void addVolume(Volume volume) { Cluster cluster = model.getCluster(); @@ -385,4 +403,8 @@ public class GlusterDataModelManager { listener.volumeCreated(volume); } } + + public List<VolumeOptionInfo> getVolumeOptionsDefaults() { + return volumeOptionsDefaults; + } } diff --git a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/VolumesClient.java b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/VolumesClient.java index a479e1f7..1d9ddbff 100644 --- a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/VolumesClient.java +++ b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/VolumesClient.java @@ -68,7 +68,7 @@ public class VolumesClient extends AbstractClient { } public Status resetVolumeOptions(String volume) { - return (Status)putRequest(volume, Status.class); + return (Status)putRequest(volume + "/" + RESTConstants.SUBRESOURCE_OPTIONS, Status.class); } public VolumeListResponse getAllVolumes() { @@ -79,14 +79,9 @@ public class VolumesClient extends AbstractClient { return (Volume) fetchSubResource(volumeName, Volume.class); } - public List<VolumeOptionInfo> getVolumeOptionsDefaults() { - String responseStr = (String)fetchSubResource( - RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS, String.class); - System.out.println(responseStr); - - VolumeOptionInfoListResponse response = (VolumeOptionInfoListResponse) fetchSubResource( - RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS, VolumeOptionInfoListResponse.class); - return response.getOptions(); + public VolumeOptionInfoListResponse getVolumeOptionsDefaults() { + return ((VolumeOptionInfoListResponse) fetchSubResource(RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS, + VolumeOptionInfoListResponse.class)); } public static void main(String[] args) { diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Event.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Event.java index 65501a2b..bac86a2e 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Event.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Event.java @@ -24,7 +24,8 @@ public class Event { DISK_REMOVED, NETWORK_INTERFACE_ADDED, NETWORK_INTERFACE_REMOVED, - VOLUME_STATUS_CHANGED + VOLUME_STATUS_CHANGED, + VOLUME_OPTIONS_RESET } private EVENT_TYPE eventType; diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Volume.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Volume.java index baa3edb9..daa96cd4 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Volume.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Volume.java @@ -49,7 +49,7 @@ public class Volume extends Entity { GLUSTERFS, NFS }; - private static final String OPTION_AUTH_ALLOW = "auth.allow:"; + private static final String OPTION_AUTH_ALLOW = "auth.allow"; private static final String[] VOLUME_TYPE_STR = new String[] { "Plain Distribute", "Distributed Mirror", "Distributed Stripe" }; diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterUtil.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterUtil.java index 38de196a..5ed83810 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterUtil.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/GlusterUtil.java @@ -49,6 +49,7 @@ public class GlusterUtil { private static final String VOLUME_TRANSPORT_TYPE_PFX = "Transport-type:"; private static final String VOLUME_BRICKS_GROUP_PFX = "Bricks"; private static final String VOLUME_OPTIONS_RECONFIG_PFX = "Options Reconfigured"; + private static final String VOLUME_OPTION_AUTH_ALLOW = "auth.allow:"; private static final ProcessUtil processUtil = new ProcessUtil(); @@ -178,9 +179,11 @@ public class GlusterUtil { List<String> command = prepareVolumeCreateCommand(volume, bricks, count, volumeType, transportTypeStr); ProcessResult result = processUtil.executeCommand(command); if(!result.isSuccess()) { + // TODO: Perform cleanup on all nodes before returning return new Status(result); } - return new Status(result); + + return createOptions(volume); } private List<String> prepareVolumeCreateCommand(Volume volume, List<String> bricks, int count, String volumeType, @@ -322,7 +325,7 @@ public class GlusterUtil { String volumeName = extractToken(line, VOLUME_NAME_PFX); if (volumeName != null) { if (volume != null) { - // add the previously read volume to volume list + volumes.add(volume); } diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/ValidationUtil.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/ValidationUtil.java new file mode 100644 index 00000000..ac77c76f --- /dev/null +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/ValidationUtil.java @@ -0,0 +1,68 @@ +package com.gluster.storage.management.core.utils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ValidationUtil { + + // Access control may contains IP with wild card(*), hostname and/or multiple ip/hostnames + public static boolean isValidAccessControl(String ac) { + String access[] = ac.split(","); + boolean isValidAccessControl = true; + for (int i = 0; i < access.length && isValidAccessControl; i++) { + isValidAccessControl = (isValidIpWithWC(access[i]) || isValidHostName(access[i])); + } + return isValidAccessControl; + } + + public static boolean isValidIpWithWC(String ip) { + String ipAddress[] = ip.split("\\."); + boolean isValid = true; + + if (ip.equals("0.0.0.0") || ip.equals("255.255.255.255")) { // Invalidate the special ip's + isValid = false; + } + + for (int i = 0; i < ipAddress.length && isValid; i++) { + if (ipAddress[i].equals("*")) { + isValid = (i == ipAddress.length - 1) ? isValid : false; + } else { + isValid = isValidIpQuad(ipAddress[i]); + } + } + return isValid; + } + + public static boolean isValidIp(String ip) { + String ipAddress[] = ip.split("\\."); + boolean isValid = true; + + if (ip.equals("0.0.0.0") || ip.equals("255.255.255.255")) { // Invalidate the special ip's + isValid = false; + } + + for (int i = 0; i < ipAddress.length && isValid; i++) { + isValid = isValidIpQuad(ipAddress[i]); + } + return isValid; + } + + 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 = "Selvam-sd.com"; + System.out.println(isValidHostName(hostName)); + } + +} diff --git a/src/com.gluster.storage.management.gui/META-INF/MANIFEST.MF b/src/com.gluster.storage.management.gui/META-INF/MANIFEST.MF index 13fb07dc..2e59c854 100644 --- a/src/com.gluster.storage.management.gui/META-INF/MANIFEST.MF +++ b/src/com.gluster.storage.management.gui/META-INF/MANIFEST.MF @@ -21,8 +21,7 @@ Require-Bundle: org.eclipse.ui;bundle-version="3.6.1", org.eclipse.birt.chart.device.swt;bundle-version="2.6.1", com.ibm.icu;bundle-version="4.2.1", com.richclientgui.rcptoolbox;bundle-version="1.0.5", - org.eclipse.core.resources, - org.eclipse.equinox.common.source;bundle-version="3.6.0" + org.eclipse.core.resources Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . diff --git a/src/com.gluster.storage.management.gui/icons/reset-options.png b/src/com.gluster.storage.management.gui/icons/reset-options.png Binary files differnew file mode 100644 index 00000000..7b93eb05 --- /dev/null +++ b/src/com.gluster.storage.management.gui/icons/reset-options.png diff --git a/src/com.gluster.storage.management.gui/plugin.xml b/src/com.gluster.storage.management.gui/plugin.xml index f5ab3db7..97a60ad1 100644 --- a/src/com.gluster.storage.management.gui/plugin.xml +++ b/src/com.gluster.storage.management.gui/plugin.xml @@ -223,6 +223,12 @@ </command> <command categoryId="com.gluster.storage.management.gui.category" + description="Reset all options of a Volume" + id="com.gluster.storage.management.gui.commands.ResetVolumeOptions" + name="Reset Options"> + </command> + <command + categoryId="com.gluster.storage.management.gui.category" description="Rebalance Volume" id="com.gluster.storage.management.gui.commands.RebalanceVolume" name="Rebalance Volume"> @@ -320,6 +326,11 @@ id="com.gluster.storage.management.gui.KeyConfig" name="Gluster"> </scheme> + <key + commandId="com.gluster.storage.management.gui.commands.ResetVolumeOptions" + schemeId="com.gluster.storage.management.gui.KeyConfig" + sequence="CTRL+SHIFT+O"> + </key> </extension> <extension id="product" @@ -504,6 +515,22 @@ </action> <action allowLabelUpdate="false" + class="com.gluster.storage.management.gui.actions.ResetVolumeOptionsAction" + definitionId="com.gluster.storage.management.gui.commands.ResetVolumeOptions" + icon="icons/reset-options.png" + id="com.gluster.storage.management.gui.actions.ResetVolumeOptionsAction" + label="Reset &Options" + menubarPath="com.gluster.storage.management.gui.menu.volume/volume" + mode="FORCE_TEXT" + pulldown="false" + retarget="false" + state="false" + style="push" + toolbarPath="Normal" + tooltip="Reset all options of the volume"> + </action> + <action + allowLabelUpdate="false" class="com.gluster.storage.management.gui.actions.RebalanceVolumeAction" definitionId="com.gluster.storage.management.gui.commands.RebalanceVolume" icon="icons/volume-rebalance.png" diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/ResetVolumeOptionsAction.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/ResetVolumeOptionsAction.java new file mode 100644 index 00000000..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/login/LoginDialog.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/login/LoginDialog.java index 30406e27..b955f0e3 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/login/LoginDialog.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/login/LoginDialog.java @@ -197,12 +197,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/views/VolumeSummaryView.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumeSummaryView.java index 798c2a40..fe583a67 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumeSummaryView.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumeSummaryView.java @@ -1,5 +1,9 @@ package com.gluster.storage.management.gui.views; +import java.util.Map; + +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CLabel; import org.eclipse.swt.layout.FillLayout; @@ -17,13 +21,16 @@ import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.part.ViewPart; import com.gluster.storage.management.client.GlusterDataModelManager; +import com.gluster.storage.management.client.VolumesClient; import com.gluster.storage.management.core.model.DefaultClusterListener; import com.gluster.storage.management.core.model.Event; import com.gluster.storage.management.core.model.Event.EVENT_TYPE; +import com.gluster.storage.management.core.model.Status; import com.gluster.storage.management.core.model.Volume; import com.gluster.storage.management.core.model.Volume.NAS_PROTOCOL; import com.gluster.storage.management.core.model.Volume.VOLUME_TYPE; import com.gluster.storage.management.core.utils.NumberUtil; +import com.gluster.storage.management.core.utils.ValidationUtil; import com.gluster.storage.management.gui.IImageKeys; import com.gluster.storage.management.gui.toolbar.GlusterToolbarManager; import com.gluster.storage.management.gui.utils.GUIHelper; @@ -36,6 +43,8 @@ public class VolumeSummaryView extends ViewPart { private Volume volume; private CLabel lblStatusValue; private DefaultClusterListener volumeChangedListener; + + private static final String VOLUME_OPTION_AUTH_ALLOW = "auth.allow"; @Override public void createPartControl(Composite parent) { @@ -44,12 +53,12 @@ public class VolumeSummaryView extends ViewPart { } createSections(parent); - + // Refresh the navigation tree whenever there is a change to the data model volumeChangedListener = new DefaultClusterListener() { @Override public void volumeChanged(Volume volume, Event event) { - if(event.getEventType() == EVENT_TYPE.VOLUME_STATUS_CHANGED) { + if (event.getEventType() == EVENT_TYPE.VOLUME_STATUS_CHANGED) { updateVolumeStatusLabel(); new GlusterToolbarManager(getSite().getWorkbenchWindow()).updateToolbar(volume); } @@ -57,8 +66,10 @@ public class VolumeSummaryView extends ViewPart { }; GlusterDataModelManager.getInstance().addClusterListener(volumeChangedListener); } - - /* (non-Javadoc) + + /* + * (non-Javadoc) + * * @see org.eclipse.ui.part.WorkbenchPart#dispose() */ @Override @@ -131,12 +142,28 @@ public class VolumeSummaryView extends ViewPart { final Hyperlink changeLink = toolkit.createHyperlink(section, "change", SWT.NONE); changeLink.addHyperlinkListener(new HyperlinkAdapter() { + @SuppressWarnings("static-access") private void finishEdit() { - // TODO: Update value to back-end - // TODO: Validation of entered text - volume.setAccessControlList(accessControlText.getText()); - accessControlText.setEnabled(false); - changeLink.setText("change"); + + if (new ValidationUtil().isValidAccessControl(accessControlText.getText())) { + Status status = (new VolumesClient(GlusterDataModelManager.getInstance().getSecurityToken())) + .setVolumeOption(volume.getName(), VOLUME_OPTION_AUTH_ALLOW, accessControlText.getText()); + if (status.isSuccess()) { + volume.setAccessControlList(accessControlText.getText()); + accessControlText.setEnabled(false); + changeLink.setText("change"); + MessageDialog.openInformation(Display.getDefault().getActiveShell(), "Access control", + status.getMessage()); + } else { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Access control", + status.getMessage()); + } + + } else { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Access control", + "Invalid IP / Host name "); + } + } private void startEdit() { @@ -167,7 +194,8 @@ public class VolumeSummaryView extends ViewPart { final Button nfsCheckBox = createCheckbox(nasProtocolsComposite, "NFS", volume.getNASProtocols().contains(NAS_PROTOCOL.NFS)); - createChangeLinkForNASProtocol(section, nfsCheckBox); + toolkit.createLabel(section, "", SWT.NONE); // dummy + // createChangeLinkForNASProtocol(section, nfsCheckBox); } private Button createCheckbox(Composite parent, String label, boolean selected) { diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionKeyEditingSupport.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionKeyEditingSupport.java new file mode 100644 index 00000000..56f25997 --- /dev/null +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionKeyEditingSupport.java @@ -0,0 +1,86 @@ +/** + * + */ +package com.gluster.storage.management.gui.views.details; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ColumnViewer; +import org.eclipse.jface.viewers.ComboBoxCellEditor; +import org.eclipse.jface.viewers.EditingSupport; +import org.eclipse.swt.widgets.Composite; + +import com.gluster.storage.management.client.GlusterDataModelManager; +import com.gluster.storage.management.core.model.Volume; +import com.gluster.storage.management.core.model.VolumeOptionInfo; + +/** + * Editing support for the "value" column in volume options table viewer. + */ +public class OptionKeyEditingSupport extends EditingSupport { + private CellEditor cellEditor; + private Volume volume; + private List<VolumeOptionInfo> defaults = GlusterDataModelManager.getInstance().getVolumeOptionsDefaults(); + private String[] allowedKeys; + + public OptionKeyEditingSupport(ColumnViewer viewer, Volume volume) { + super(viewer); + this.volume = volume; + allowedKeys = getAllowedKeys(); + this.cellEditor = new ComboBoxCellEditor((Composite) viewer.getControl(), allowedKeys); + } + + /** + * @return array of option keys that are not already set on the volume + */ + private String[] getAllowedKeys() { + ArrayList<String> keys = new ArrayList<String>(); + Map<String, String> volumeOptions = volume.getOptions(); + for(VolumeOptionInfo optionInfo : defaults) { + String optionName = optionInfo.getName(); + if(!volumeOptions.containsKey(optionName)) { + keys.add(optionName); + } + } + return keys.toArray(new String[0]); + } + + @SuppressWarnings("unchecked") + @Override + protected void setValue(final Object element, final Object value) { + Integer newValue = (Integer)value; + String newKey = allowedKeys[newValue]; + + if (((Entry<String, String>)element).getKey().equals(newKey)) { + // selected value is same as old one. + return; + } + + // value has changed. set volume option at back-end and update model accordingly + volume.getOptions().remove(""); + volume.setOption(newKey, ""); + getViewer().refresh(); + } + + @SuppressWarnings("unchecked") + @Override + protected Object getValue(Object element) { + return cellEditor.getValue(); + } + + @Override + protected CellEditor getCellEditor(Object element) { + return cellEditor; + } + + @SuppressWarnings("unchecked") + @Override + protected boolean canEdit(Object element) { + Entry<String, String> entry = (Entry<String, String>)element; + return (entry.getKey().isEmpty() || entry.getValue().isEmpty()); + } +} diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionValueEditingSupport.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionValueEditingSupport.java new file mode 100644 index 00000000..f975f1ff --- /dev/null +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/OptionValueEditingSupport.java @@ -0,0 +1,103 @@ +/** + * + */ +package com.gluster.storage.management.gui.views.details; + +import java.util.List; +import java.util.Map.Entry; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ColumnViewer; +import org.eclipse.jface.viewers.EditingSupport; +import org.eclipse.jface.viewers.TextCellEditor; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; + +import com.gluster.storage.management.client.GlusterDataModelManager; +import com.gluster.storage.management.client.VolumesClient; +import com.gluster.storage.management.core.model.Status; +import com.gluster.storage.management.core.model.Volume; +import com.gluster.storage.management.core.model.VolumeOptionInfo; + +/** + * Editing support for the "value" column in volume options table viewer. + */ +public class OptionValueEditingSupport extends EditingSupport { + private CellEditor cellEditor; + private Volume volume; + private List<VolumeOptionInfo> defaults = GlusterDataModelManager.getInstance().getVolumeOptionsDefaults(); + + public OptionValueEditingSupport(ColumnViewer viewer, Volume volume) { + super(viewer); + this.volume = volume; + this.cellEditor = new TextCellEditor((Composite) viewer.getControl()); + } + + @SuppressWarnings("unchecked") + @Override + protected void setValue(final Object element, final Object value) { + final Entry<String, String> entry = (Entry<String, String>) element; + + // It is not allowed to change value to empty string + if(((String)value).isEmpty()) { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Set Volume Option", + "Option value can't be empty! Please enter a valid value."); + cellEditor.setFocus(); + return; + } + + if (entry.getValue().equals(value)) { + // value is same as that present in the model. return without doing anything. + return; + } + + // value has changed. set volume option at back-end and update model accordingly + BusyIndicator.showWhile(Display.getDefault(), new Runnable() { + + @Override + public void run() { + VolumesClient client = new VolumesClient(GlusterDataModelManager.getInstance().getSecurityToken()); + Status status = client.setVolumeOption(volume.getName(), entry.getKey(), (String) value); + if (status.isSuccess()) { + volume.setOption(entry.getKey(), (String) value); + } else { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Set Volume Option", + status.getMessage()); + } + getViewer().update(entry, null); + } + }); + } + + /** + * @param key Key whose default value is to be fetched + * @return Default value of the volume option with given key + */ + private String getDefaultValue(String key) { + for(VolumeOptionInfo optionInfo : defaults) { + if(optionInfo.getName().equals(key)) { + return optionInfo.getDefaultValue(); + } + } + return ""; + } + + @SuppressWarnings("unchecked") + @Override + protected Object getValue(Object element) { + Entry<String, String> entry = (Entry<String, String>) element; + return entry.getValue().isEmpty() ? getDefaultValue(entry.getKey()) : entry.getValue(); + } + + @Override + protected CellEditor getCellEditor(Object element) { + return cellEditor; + } + + @Override + protected boolean canEdit(Object element) { + return true; + } +} diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeOptionsPage.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeOptionsPage.java index cd425dc2..5a1a41e9 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeOptionsPage.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/VolumeOptionsPage.java @@ -20,29 +20,25 @@ package com.gluster.storage.management.gui.views.details; import java.util.Map.Entry; -import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.viewers.ArrayContentProvider; -import org.eclipse.jface.viewers.CellEditor; -import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.ColumnLayoutData; -import org.eclipse.jface.viewers.ColumnViewer; import org.eclipse.jface.viewers.ColumnWeightData; -import org.eclipse.jface.viewers.EditingSupport; +import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; -import org.eclipse.swt.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,8 +47,9 @@ import org.eclipse.swt.widgets.Text; import org.eclipse.ui.forms.widgets.FormToolkit; import com.gluster.storage.management.client.GlusterDataModelManager; -import com.gluster.storage.management.client.VolumesClient; -import com.gluster.storage.management.core.model.Status; +import com.gluster.storage.management.core.model.DefaultClusterListener; +import com.gluster.storage.management.core.model.Event; +import com.gluster.storage.management.core.model.Event.EVENT_TYPE; import com.gluster.storage.management.core.model.Volume; import com.gluster.storage.management.gui.VolumeOptionsTableLabelProvider; import com.gluster.storage.management.gui.utils.GUIHelper; @@ -70,30 +67,55 @@ public class VolumeOptionsPage extends Composite { private static final String[] OPTIONS_TABLE_COLUMN_NAMES = new String[] { "Option Key", "Option Value" }; - public VolumeOptionsPage(Composite parent, int style) { + public VolumeOptionsPage(final Composite parent, int style, Volume volume) { super(parent, style); - addDisposeListener(new DisposeListener() { - public void widgetDisposed(DisposeEvent e) { - toolkit.dispose(); - } - }); - + + this.volume = volume; toolkit.adapt(this); toolkit.paintBordersFor(this); setupPageLayout(); Text filterText = guiHelper.createFilterText(toolkit, this); setupDiskTableViewer(filterText); - } - - public VolumeOptionsPage(final Composite parent, int style, Volume volume) { - this(parent, style); - this.volume = volume; + + createAddButton(); - tableViewer.setInput(volume.getOptions().entrySet().toArray()); + tableViewer.setInput(volume.getOptions().entrySet()); parent.layout(); // Important - this actually paints the table + registerListeners(parent); + } + + private void createAddButton() { + Button addButton = toolkit.createButton(this, "&Add", SWT.FLAT); + addButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + volume.setOption("", ""); + + tableViewer.refresh(); + tableViewer.setSelection(new StructuredSelection(getEntry(""))); + } + + private Entry getEntry(String key) { + for(Entry entry : volume.getOptions().entrySet()) { + if(entry.getKey().equals(key)) { + return entry; + } + } + return null; + } + }); + } + + private void registerListeners(final Composite parent) { + addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + toolkit.dispose(); + } + }); + /** * Ideally not required. However the table viewer is not getting laid out properly on performing * "maximize + restore" So this is a hack to make sure that the table is laid out again on re-size of the window @@ -105,6 +127,18 @@ public class VolumeOptionsPage extends Composite { parent.layout(); } }); + + GlusterDataModelManager.getInstance().addClusterListener(new DefaultClusterListener() { + @Override + public void volumeChanged(Volume volume, Event event) { + super.volumeChanged(volume, event); + if(event.getEventType() == EVENT_TYPE.VOLUME_OPTIONS_RESET) { + if(!tableViewer.getControl().isDisposed()) { + tableViewer.refresh(); + } + } + } + }); } @@ -137,59 +171,6 @@ 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() @@ -202,7 +183,7 @@ 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(); } @@ -216,12 +197,15 @@ public class VolumeOptionsPage extends Composite { return ((Entry<String, String>) element).getKey(); } }); + + // Editing support required when adding new key + keyColumn.setEditingSupport(new OptionKeyEditingSupport(keyColumn.getViewer(), volume)); + return keyColumn.getColumn(); } private void createDiskTableViewer(Composite parent) { - tableViewer = CheckboxTableViewer.newCheckList(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.MULTI); - // TableViewer tableViewer = new TableViewer(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.MULTI); + tableViewer = new TableViewer(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.SINGLE); tableViewer.setLabelProvider(new VolumeOptionsTableLabelProvider()); tableViewer.setContentProvider(new ArrayContentProvider()); |
