diff options
16 files changed, 375 insertions, 113 deletions
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..eb6ef3a0 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,6 +41,7 @@ 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()); @@ -48,10 +49,12 @@ public class DiscoveredServersClient extends AbstractClient {  		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.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 39141bc0..f44b24e5 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,17 +26,24 @@ 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 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_VOLUME_NAME = "volumeName";  	public static final String FORM_PARAM_OPTION_VALUE = "value"; +	  	public static final String PATH_PARAM_VOLUME_NAME = "volumeName";  	public static final String FORM_PARAM_DELETE_OPTION = "value"; -	public static final String SUBRESOURCE_DEFAULT_OPTIONS = "defaultoptions"; -	public static final String SUBRESOURCE_OPTIONS = "options"; +	public static final String QUERY_PARAM_DISK_NAME = "diskName"; +	public static final String QUERY_PARAM_LINE_COUNT = "lineCount"; +  	// 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/LogMessage.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/LogMessage.java index cc3aa043..d81b8d0f 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,24 @@ package com.gluster.storage.management.core.model;  import java.util.Date; +import javax.xml.bind.annotation.XmlRootElement; +  import com.gluster.storage.management.core.utils.StringUtil; +@XmlRootElement  public class LogMessage implements Filterable {  	private Date timestamp;  	private Disk disk;  	private String severity;  	private String message; +	public LogMessage() { +	} +	 +	public LogMessage(String logMessage) { +		// TODO: Parse the log message and extract fields  +	} +	  	public Date getTimestamp() {  		return timestamp;  	} 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 45a9842b..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 @@ -59,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() { 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 50966920..94b23179 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 @@ -72,12 +72,9 @@ public class Volume extends Entity {  	}  	// GlusterFS and NFS export is always enabled -	// Note: logic needs to make NFS optional  	private Set<NAS_PROTOCOL> nasProtocols = new LinkedHashSet<NAS_PROTOCOL>(Arrays.asList(new NAS_PROTOCOL[] {  			NAS_PROTOCOL.GLUSTERFS, NAS_PROTOCOL.NFS })); -	private String accessControlList = "*"; -  	public String getVolumeTypeStr() {  		return getVolumeTypeStr(getVolumeType());  	} 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.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/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 index a94ae25a..27dc8d4b 100644 --- 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 @@ -4,6 +4,7 @@  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; @@ -12,6 +13,7 @@ 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; @@ -42,7 +44,9 @@ public class OptionKeyEditingSupport extends EditingSupport {  		Map<String, String> volumeOptions = volume.getOptions();  		for(VolumeOptionInfo optionInfo : defaults) {  			String optionName = optionInfo.getName(); -			if(!volumeOptions.containsKey(optionName)) { +			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);  			}  		} @@ -61,7 +65,7 @@ public class OptionKeyEditingSupport extends EditingSupport {  			return;  		} -		// value has changed. set volume option at back-end and update model accordingly +		// value has changed. set new value and refresh the viewer.  		volume.getOptions().remove(oldEntry.getKey());  		volume.setOption(newKey, "");  		getViewer().refresh(); @@ -69,16 +73,49 @@ public class OptionKeyEditingSupport extends EditingSupport {  	@Override  	protected Object getValue(Object element) { -		return cellEditor.getValue(); +		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); +		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) { 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 index 5749c703..af1ef949 100644 --- 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 @@ -45,9 +45,6 @@ public class OptionValueEditingSupport extends EditingSupport {  		final String optionValue = (String)value;  		final String oldValue = entry.getValue(); -		guiHelper.setStatusMessage("Setting option [" + optionKey + " = " + optionValue + "]..."); -		getViewer().getControl().update(); -		  		// It is not allowed to change value to empty string  		if(optionValue.isEmpty()) {  			MessageDialog.openError(Display.getDefault().getActiveShell(), "Set Volume Option", @@ -62,6 +59,9 @@ public class OptionValueEditingSupport extends EditingSupport {  		}  		// 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 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..ee7acea5 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 @@ -56,9 +56,10 @@ public class VolumeLogsPage extends Composite {  	private static final String[] LOG_TABLE_COLUMN_NAMES = new String[] { "Date", "Time", "Disk", "Severity", "Message" };  	/** -	 * 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); @@ -70,80 +71,39 @@ 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); -		 -		text = toolkit.createText(composite, "100", SWT.NONE); -		text.setBounds(85, 15, 60, 20); -		 -		Label lblMessagesAndFilter = toolkit.createLabel(composite, "messages, and filter on disk", SWT.NONE); -		lblMessagesAndFilter.setBounds(160, 15, 200, 20); -		 -		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); -		 -		Label lblSeverity = toolkit.createLabel(composite, "Severity", SWT.NONE); -		lblSeverity.setBounds(480, 15, 70, 20); +		createLineCountLabel(composite); +		createLineCountText(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); +		createDiskLabel(composite); +		createDisksCombo(composite); -		Label lblFrom = toolkit.createLabel(composite, "from", SWT.NONE); -		lblFrom.setBounds(0, 60, 40, 20); +		createSeverityLabel(composite); +		createSeverityCombo(composite); -		DateTime dateTime = new DateTime(composite, SWT.BORDER | SWT.DROP_DOWN); -		dateTime.setBounds(45, 60, 120, 20); -		toolkit.adapt(dateTime); -		toolkit.paintBordersFor(dateTime); - -		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); +		createFromDateLabel(composite); +		createFromDateField(composite); +		createFromTimeField(composite); -		Label lblTo = toolkit.createLabel(composite, "To", SWT.NONE); -		lblTo.setBounds(329, 60, 26, 20); +		createToDateLabel(composite); +		createToDateField(composite); +		createToTimeField(composite); -		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); +		createSearchButton(composite); -		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); +		createSeparator(composite); -		Button btngo = toolkit.createButton(composite, "&Go", SWT.NONE); -		btngo.setBounds(605, 55, 60, 30); -		 -		Label separator = toolkit.createLabel(composite, "", SWT.SEPARATOR | SWT.HORIZONTAL | SWT.FILL); -		separator.setBounds(0, 95, 680, 2); -		 -		Label lblFilterString = toolkit.createLabel(composite, "Filter String", SWT.LEFT); -		lblFilterString.setBounds(0, 105, 85, 20); +		createFilterLabel(composite); +		createFilterText(composite); -		text = guiHelper.createFilterText(toolkit, composite); -		text.setBounds(90, 105, 250, 20); +		createLogTableViewer(); +	} +	private void createLogTableViewer() {  		Composite tableViewerComposite = createTableViewerComposite();  		TableViewer tableViewer = new TableViewer(tableViewerComposite, SWT.FLAT | SWT.FULL_SELECTION | SWT.MULTI); @@ -154,6 +114,111 @@ public class VolumeLogsPage extends Composite {  		guiHelper.createFilter(tableViewer, text, false);  		tableViewer.setInput(GlusterDummyModel.getDummyLogMessages().toArray());  	} + +	private void createFilterText(Composite composite) { +		text = guiHelper.createFilterText(toolkit, composite); +		text.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(605, 55, 60, 30); +	} + +	private void createToTimeField(Composite composite) { +		DateTime toTime = new DateTime(composite, SWT.BORDER | SWT.TIME); +		toTime.setBounds(480, 60, 120, 20); +		toolkit.adapt(toTime); +		toolkit.paintBordersFor(toTime); +	} + +	private void createToDateField(Composite composite) { +		DateTime toDate = new DateTime(composite, SWT.BORDER | SWT.DROP_DOWN); +		toDate.setBounds(355, 60, 120, 20); +		toolkit.adapt(toDate); +		toolkit.paintBordersFor(toDate); +	} + +	private void createToDateLabel(Composite composite) { +		Label lblTo = toolkit.createLabel(composite, "To", SWT.NONE); +		lblTo.setBounds(329, 60, 26, 20); +	} + +	private void createFromTimeField(Composite composite) { +		DateTime fromTime = new DateTime(composite, SWT.BORDER | SWT.TIME); +		fromTime.setBounds(171, 60, 120, 20); +		toolkit.adapt(fromTime); +		toolkit.paintBordersFor(fromTime); +	} + +	private void createFromDateField(Composite composite) { +		DateTime fromDate = new DateTime(composite, SWT.BORDER | SWT.DROP_DOWN); +		fromDate.setBounds(45, 60, 120, 20); +		toolkit.adapt(fromDate); +		toolkit.paintBordersFor(fromDate); +	} + +	private void createFromDateLabel(Composite composite) { +		Label lblFrom = toolkit.createLabel(composite, "from", SWT.NONE); +		lblFrom.setBounds(0, 60, 40, 20); +	} + +	private void createSeverityCombo(Composite composite) { +		Combo severityCombo = new Combo(composite, SWT.NONE); +		severityCombo.setBounds(555, 15, 110, 20); +		severityCombo.setItems(new String[] {"ALL", "SEVERE", "WARNING", "DEBUG", "INFO"}); +		toolkit.adapt(severityCombo); +		toolkit.paintBordersFor(severityCombo); +		severityCombo.select(1); +	} + +	private void createSeverityLabel(Composite composite) { +		Label lblSeverity = toolkit.createLabel(composite, "Severity", SWT.NONE); +		lblSeverity.setBounds(480, 15, 70, 20); +	} + +	private void createDisksCombo(Composite composite) { +		Combo disksCombo = new Combo(composite, SWT.NONE); +		disksCombo.setBounds(365, 15, 100, 20); +		disksCombo.setItems(new String[] {"ALL", "sda", "sdb", "sdc", "sdd"}); +		toolkit.adapt(disksCombo); +		toolkit.paintBordersFor(disksCombo); +		disksCombo.select(0); +	} + +	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) { +		text = toolkit.createText(composite, "100", SWT.NONE); +		text.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() {  		Composite tableViewerComposite = new Composite(this, SWT.NO); 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 b30ce379..3dadb947 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,6 +20,7 @@ package com.gluster.storage.management.gui.views.details;  import java.util.Map.Entry; +import org.apache.commons.lang.WordUtils;  import org.eclipse.jface.layout.TableColumnLayout;  import org.eclipse.jface.viewers.ArrayContentProvider;  import org.eclipse.jface.viewers.ColumnLabelProvider; @@ -32,6 +33,8 @@ import org.eclipse.jface.viewers.TableViewerColumn;  import org.eclipse.swt.SWT;  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.events.SelectionAdapter; @@ -64,6 +67,7 @@ public class VolumeOptionsPage extends Composite {  	private GUIHelper guiHelper = GUIHelper.getInstance();  	private Volume volume;  	private DefaultClusterListener clusterListener; +	private Text filterText;  	public enum OPTIONS_TABLE_COLUMN_INDICES {  		OPTION_KEY, OPTION_VALUE @@ -71,6 +75,8 @@ public class VolumeOptionsPage extends Composite {  	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(final Composite parent, int style, Volume volume) {  		super(parent, style); @@ -81,7 +87,8 @@ public class VolumeOptionsPage extends Composite {  		toolkit.paintBordersFor(this);  		setupPageLayout(); -		setupDiskTableViewer(guiHelper.createFilterText(toolkit, this)); +		filterText = guiHelper.createFilterText(toolkit, this); +		setupOptionsTableViewer(filterText);  		createAddButton(); @@ -91,14 +98,6 @@ public class VolumeOptionsPage extends Composite {  		registerListeners(parent);  	} -	/** -	 * @return -	 */ -	private DefaultClusterListener createClusterListener() { -		// TODO Auto-generated method stub -		return null; -	} -  	private void createAddButton() {  		addButton = toolkit.createButton(this, "&Add", SWT.FLAT);  		addButton.addSelectionListener(new SelectionAdapter() { @@ -109,9 +108,11 @@ public class VolumeOptionsPage extends Composite {  				tableViewer.refresh();  				tableViewer.setSelection(new StructuredSelection(getEntry(""))); +				keyColumn.getViewer().editElement(getEntry(""), 0); // edit newly created entry				 -				// disable the add button till user fills up the new option +				// disable the add button AND search filter textbox till user fills up the new option  				addButton.setEnabled(false); +				filterText.setEnabled(false);  			}  			private Entry<String, String> getEntry(String key) { @@ -123,11 +124,29 @@ public class VolumeOptionsPage extends Composite {  				return null;  			}  		}); +		 +		// 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();  			} @@ -145,6 +164,20 @@ public class VolumeOptionsPage extends Composite {  			}  		}); +		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 @@ -159,8 +192,9 @@ public class VolumeOptionsPage extends Composite {  				if(event.getEventType() == EVENT_TYPE.VOLUME_OPTION_SET) {  					Entry<String, String> eventEntry = (Entry<String, String>)event.getEventData();  					if (eventEntry.getKey().equals(volume.getOptions().keySet().toArray()[volume.getOptions().size()-1])) { -						// option has been set successfully by the user. re-enable the add button +						// 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()) { @@ -183,7 +217,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); @@ -210,6 +244,7 @@ public class VolumeOptionsPage extends Composite {  		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(); @@ -223,9 +258,10 @@ public class VolumeOptionsPage extends Composite {  	}  	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(); @@ -234,25 +270,32 @@ public class VolumeOptionsPage extends Composite {  			@SuppressWarnings("unchecked")  			@Override  			public String getToolTipText(Object element) { -				VolumeOptionInfo optionInfo = GlusterDataModelManager.getInstance().getVolumeOptionInfo( -						((Entry<String, String>) element).getKey()); -				return optionInfo.getDescription() + CoreConstants.NEWLINE + "Default value: " +				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 -		keyColumn.setEditingSupport(new OptionKeyEditingSupport(keyColumn.getViewer(), volume)); +		keyEditingSupport = new OptionKeyEditingSupport(keyColumn.getViewer(), volume); +		keyColumn.setEditingSupport(keyEditingSupport);  		return keyColumn.getColumn();  	} -	private void createDiskTableViewer(Composite parent) { +	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() { @@ -262,23 +305,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.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/resources/VolumesResource.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/VolumesResource.java index 073b8c13..92fba9f3 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,8 +24,11 @@ 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_DISK_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_LINE_COUNT;  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_LOGS;  import static com.gluster.storage.management.core.constants.RESTConstants.SUBRESOURCE_OPTIONS;  import java.util.ArrayList; @@ -46,10 +49,13 @@ 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.server.constants.VolumeOptionsDefaults; @@ -64,7 +70,7 @@ import com.sun.jersey.spi.resource.Singleton;  public class VolumesResource {  	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 POST_VOLUME_DELETE_SCRIPT = "post_volume_delete.py"; +	private static final String VOLUME_DISK_LOG_SCRIPT = "get_volume_disk_log.py";  	@InjectParam  	private static ServerUtil serverUtil; @@ -251,6 +257,56 @@ public class VolumesResource {  		}  		return new Status(Status.STATUS_CODE_SUCCESS, "Directories cleaned up successfully!");  	} +	 +	private List<LogMessage> getDiskLogs(String volumeName, String diskName, Integer lineCount) +			throws GlusterRuntimeException { +		String[] diskParts = diskName.split(":");  +		String server = diskParts[0]; +		String disk = diskParts[1]; +				 +		// Usage: get_volume_disk_log.py <volumeName> <diskName> <lineCount> +		Status logStatus = (Status) serverUtil.executeOnServer(true, server, VOLUME_DISK_LOG_SCRIPT + " " + volumeName +				+ " " + disk + " " + lineCount, Status.class); +		if(!logStatus.isSuccess()) { +			throw new GlusterRuntimeException(logStatus.toString()); +		} +		 +		return extractLogMessages(logStatus.getMessage()); +	} + +	private List<LogMessage> extractLogMessages(String logContent) { +		List<LogMessage> logMessages = new ArrayList<LogMessage>(); +		for(String logMessage : logContent.split(CoreConstants.NEWLINE)) { +			logMessages.add(new LogMessage(logMessage)); +		} +		 +		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_LINE_COUNT) Integer lineCount) { +		List<LogMessage> logMessages = null; +		 +		try { +			if (diskName == null || diskName.isEmpty()) { +				logMessages = new ArrayList<LogMessage>(); +				// fetch logs for every brick of the volume +				Volume volume = getVolume(volumeName); +				for (String volumeDisk : volume.getDisks()) { +					logMessages.addAll(getDiskLogs(volumeName, volumeDisk, lineCount)); +				} +			} else { +				// fetch logs for given brick of the volume +				logMessages = getDiskLogs(volumeName, diskName, lineCount); +			} +		} catch (Exception e) { +			return new LogMessageListResponse(new Status(e), null); +		} +		 +		return new LogMessageListResponse(Status.STATUS_SUCCESS, logMessages); +	}  	public static void main(String[] args) throws ClassNotFoundException {  		VolumesResource vr = new VolumesResource(); 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 4f99172d..47657c2a 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 @@ -39,7 +39,6 @@ import org.springframework.stereotype.Component;  import com.gluster.storage.management.core.constants.CoreConstants;  import com.gluster.storage.management.core.model.Status; -import com.gluster.storage.management.core.response.GenericResponse;  import com.gluster.storage.management.core.utils.ProcessResult;  import com.gluster.storage.management.core.utils.ProcessUtil; @@ -75,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 { @@ -114,6 +114,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  | 
