diff options
author | Shireesh Anjal <shireesh@gluster.com> | 2011-11-25 20:13:35 +0530 |
---|---|---|
committer | Shireesh Anjal <shireesh@gluster.com> | 2011-11-25 20:13:35 +0530 |
commit | 1142b0e41de39010de7845cf70d71dbb001fc1dc (patch) | |
tree | 3513487f65c1a7df47996bd2852393aceaac1b8a /src/org.gluster.storage.management.console/src | |
parent | 92c52d8edf285945d31e446503fc742fde9dcc49 (diff) |
Renamed projects / packages com.gluster.* to org.gluster.*
Diffstat (limited to 'src/org.gluster.storage.management.console/src')
128 files changed, 18075 insertions, 0 deletions
diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/Activator.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/Activator.java new file mode 100644 index 00000000..0edd74cf --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/Activator.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.gluster.storage.management.console"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + public void editorOpened() { + System.err.println("Editor opened!"); + } + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + + /** + * Returns an image descriptor for the image file at the given + * plug-in relative path + * + * @param path the path + * @return the image descriptor + */ + public static ImageDescriptor getImageDescriptor(String path) { + return imageDescriptorFromPlugin(PLUGIN_ID, path); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/AlertsManager.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/AlertsManager.java new file mode 100644 index 00000000..c44dea09 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/AlertsManager.java @@ -0,0 +1,216 @@ +/** + * AlertsManager.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 org.gluster.storage.management.console; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.preference.IPreferenceStore; +import org.gluster.storage.management.console.preferences.PreferenceConstants; +import org.gluster.storage.management.core.model.Alert; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Cluster; +import org.gluster.storage.management.core.model.Disk; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.Partition; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.Alert.ALERT_TYPES; +import org.gluster.storage.management.core.model.Brick.BRICK_STATUS; +import org.gluster.storage.management.core.model.Server.SERVER_STATUS; +import org.gluster.storage.management.core.model.Volume.VOLUME_STATUS; +import org.gluster.storage.management.core.utils.NumberUtil; + + +public class AlertsManager { + private List<Alert> alerts = new ArrayList<Alert>(); + private Cluster cluster; + + private Double CPU_USAGE_THRESHOLD; + private Double MEMORY_USAGE_THRESHOLD; + private Double DISK_SPACE_USAGE_THRESHOLD; + + public AlertsManager(Cluster cluster) { + this.cluster = cluster; + + IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore(); + CPU_USAGE_THRESHOLD = preferenceStore.getDouble(PreferenceConstants.P_SERVER_CPU_CRITICAL_THRESHOLD); + MEMORY_USAGE_THRESHOLD = preferenceStore.getDouble(PreferenceConstants.P_SERVER_MEMORY_USAGE_THRESHOLD); + DISK_SPACE_USAGE_THRESHOLD = preferenceStore.getDouble(PreferenceConstants.P_DISK_SPACE_USAGE_THRESHOLD); + } + + public List<Alert> getAlerts() { + return alerts; + } + + public Alert getAlert(String id) { + for (Alert alert : getAlerts()) { + if (alert.getId().equals(id)) { + return alert; + } + } + return null; + } + + public void addAlert(Alert alert) { + alerts.add(alert); + } + + public void addAlerts(List<Alert> alerts) { + this.alerts.addAll(alerts); + } + + public void setAlerts(List<Alert> alerts) { + this.alerts = alerts; + } + + public Boolean removeAlert(String id) { + for (int i = 0; i < alerts.size(); i++) { + if (alerts.get(i).getId().equals(id)) { + return (alerts.remove(i) != null); + } + } + return false; + } + + public void clearAll() { + this.alerts.clear(); + } + + public void buildAlerts() { + clearAll(); + addAlerts(getServerAlerts()); + addAlerts(getVolumeAlerts()); + } + + private List<Alert> getServerAlerts() { + List<Alert> serverAlerts = new ArrayList<Alert>(); + Alert offlineServerAlert = getOfflineServerAlerts(); + if (offlineServerAlert != null) { + serverAlerts.add(offlineServerAlert); // Single alert for offline servers + } + + for (GlusterServer server : cluster.getServers()) { + // To check off line servers + // if (server.getStatus() == SERVER_STATUS.OFFLINE) { + // serverAlerts.add(new Alert(ALERT_TYPES.OFFLINE_SERVERS_ALERT, server.getName(), "Server [" + // + server.getName() + "] is Offline")); + // continue; // If the server is Offline skip other Alert builds + // } + + // To check High CPU usage + if (server.getCpuUsage() >= CPU_USAGE_THRESHOLD) { + serverAlerts.add(new Alert(ALERT_TYPES.CPU_USAGE_ALERT, server.getName(), + Alert.ALERT_TYPE_STR[ALERT_TYPES.CPU_USAGE_ALERT.ordinal()] + " [" + + NumberUtil.formatNumber(server.getCpuUsage()) + "] in server [" + server.getName() + + "]")); + } + + // To check High Memory usage + Double memoryUtilized = server.getMemoryInUse() / server.getTotalMemory() * 100d; + if (memoryUtilized >= MEMORY_USAGE_THRESHOLD) { + serverAlerts.add(new Alert(ALERT_TYPES.MEMORY_USAGE_ALERT, server.getName(), + Alert.ALERT_TYPE_STR[ALERT_TYPES.MEMORY_USAGE_ALERT.ordinal()] + " [" + + NumberUtil.formatNumber(memoryUtilized) + "%] in server [" + server.getName() + + "]")); + } + + // To Check low disk space + serverAlerts.addAll(getLowDiskAlerts(server)); + } + return serverAlerts; + } + + private Alert getOfflineServerAlerts() { + List<String> offlineServers = new ArrayList<String>(); + for (GlusterServer server : cluster.getServers()) { + if (server.getStatus() == SERVER_STATUS.OFFLINE) { + offlineServers.add(server.getName()); + } + } + if (offlineServers.size() > 0) { + return new Alert(ALERT_TYPES.OFFLINE_SERVERS_ALERT, "Server", + Alert.ALERT_TYPE_STR[ALERT_TYPES.OFFLINE_SERVERS_ALERT.ordinal()] + "(s) " + + offlineServers.toString()); + } + return null; + } + + private List<Alert> getLowDiskAlerts(GlusterServer server) { + List<Alert> diskAlerts = new ArrayList<Alert>(); + boolean hasPartition; + Double deviceSpaceUsed; + for (Disk disk : server.getDisks()) { + hasPartition = false; + for (Partition partition : disk.getPartitions()) { + hasPartition = true; + deviceSpaceUsed = partition.getSpaceInUse() / partition.getSpace() * 100d; + if (deviceSpaceUsed >= DISK_SPACE_USAGE_THRESHOLD) { + diskAlerts.add(new Alert(ALERT_TYPES.DISK_USAGE_ALERT, partition.getQualifiedName(), + Alert.ALERT_TYPE_STR[ALERT_TYPES.DISK_USAGE_ALERT.ordinal()] + " [" + + NumberUtil.formatNumber(deviceSpaceUsed) + "% used] in disk [" + + partition.getQualifiedName() + "]")); + } + } + if (hasPartition) { + continue; // Do not check disk usage + } + + // If it is disk + deviceSpaceUsed = disk.getSpaceInUse() / disk.getSpace() * 100d; + if (deviceSpaceUsed >= DISK_SPACE_USAGE_THRESHOLD) { + diskAlerts.add(new Alert(ALERT_TYPES.DISK_USAGE_ALERT, disk.getQualifiedName(), + Alert.ALERT_TYPE_STR[ALERT_TYPES.DISK_USAGE_ALERT.ordinal()] + " [" + + NumberUtil.formatNumber(deviceSpaceUsed) + "% used] in [" + + disk.getQualifiedName() + "]")); + } + } + return diskAlerts; + } + + private List<Alert> getVolumeAlerts() { + List<Alert> volumeAlerts = new ArrayList<Alert>(); + List<String> offlineBricks = new ArrayList<String>(); + + for (Volume volume : cluster.getVolumes()) { + if (volume.getStatus() == VOLUME_STATUS.OFFLINE) { + volumeAlerts.add(new Alert(ALERT_TYPES.OFFLINE_VOLUME_ALERT, volume.getName(), + Alert.ALERT_TYPE_STR[ALERT_TYPES.OFFLINE_VOLUME_ALERT.ordinal()] + " [" + volume.getName() + + "]")); + continue; + } + + // To check off line bricks + offlineBricks = new ArrayList<String>(); + for (Brick brick : volume.getBricks()) { + if (brick.getStatus() == BRICK_STATUS.OFFLINE) { + offlineBricks.add(brick.getQualifiedName()); + } + } + // One offline brick alert per volume + if (offlineBricks.size() > 0) { + volumeAlerts.add(new Alert(ALERT_TYPES.OFFLINE_VOLUME_BRICKS_ALERT, volume.getName(), + Alert.ALERT_TYPE_STR[ALERT_TYPES.OFFLINE_VOLUME_BRICKS_ALERT.ordinal()] + " " + + offlineBricks.toString() + " in volume " + volume.getName())); + } + } + return volumeAlerts; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/Application.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/Application.java new file mode 100644 index 00000000..f5a88479 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/Application.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import java.util.ArrayList; +import java.util.Collections; +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; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.PlatformUI; +import org.gluster.storage.management.console.dialogs.LoginDialog; +import org.gluster.storage.management.core.model.Entity; + + +/** + * This class controls all aspects of the application's execution + */ +public class Application implements IApplication { + + public static final String PLUGIN_ID = "org.gluster.storage.management.console"; + private static Application instance; + private List<IEntityListener> entityListeners = Collections.synchronizedList(new ArrayList<IEntityListener>()); + private IStatusLineManager statusLineManager; + + public Application() { + instance = this; + } + + public static Application getApplication() { + 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); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.IApplicationContext) + */ + public Object start(IApplicationContext context) { + setSystemProperties(); + + Display display = PlatformUI.createDisplay(); + + final boolean[] loginSuccess = new boolean[1]; + Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() { + public void run() { + loginSuccess[0] = login(); + } + }); + + if (!loginSuccess[0]) { + return IApplication.EXIT_OK; + } + try { + int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor()); + if (returnCode == PlatformUI.RETURN_RESTART) { + return IApplication.EXIT_RESTART; + } + + return IApplication.EXIT_OK; + } finally { + display.dispose(); + } + } + + private void setSystemProperties() { + // TODO: Trying this to avoid the webstart authentication dialog + // to be tested, and removed if this doesn't work. + System.setProperty("javaws.cfg.jauthenticator", "none"); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.equinox.app.IApplication#stop() + */ + public void stop() { + if (!PlatformUI.isWorkbenchRunning()) + return; + final IWorkbench workbench = PlatformUI.getWorkbench(); + final Display display = workbench.getDisplay(); + display.syncExec(new Runnable() { + public void run() { + if (!display.isDisposed()) + workbench.close(); + } + }); + } + + public void addEntityListener(IEntityListener listener) { + entityListeners.add(listener); + } + + public void entityChanged(Entity entity, String[] paremeters) { + for (IEntityListener listener : entityListeners) { + listener.entityChanged(entity, paremeters); + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ApplicationActionBarAdvisor.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ApplicationActionBarAdvisor.java new file mode 100644 index 00000000..df608eea --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ApplicationActionBarAdvisor.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import org.eclipse.jface.action.GroupMarker; +import org.eclipse.jface.action.ICoolBarManager; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.swt.SWT; +import org.eclipse.ui.IWorkbenchActionConstants; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.actions.ActionFactory; +import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction; +import org.eclipse.ui.application.ActionBarAdvisor; +import org.eclipse.ui.application.IActionBarConfigurer; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.gluster.storage.management.console.utils.GUIHelper; + + +/** + * An action bar advisor is responsible for creating, adding, and disposing of the actions added to a workbench window. + * Each window will be populated with new actions. + */ +public class ApplicationActionBarAdvisor extends ActionBarAdvisor { + private IWorkbenchWindow window; + /* + * Actions - important to allocate these only in makeActions, and then use them in the fill methods. This ensures + * that the actions aren't recreated when fillActionBars is called with FILL_PROXY. + */ + private IWorkbenchAction exitAction; + private IWorkbenchAction aboutAction; + private IWorkbenchAction helpContentsAction; + + private GUIHelper guiHelper = GUIHelper.getInstance(); + + public ApplicationActionBarAdvisor(IActionBarConfigurer configurer) { + super(configurer); + } + + protected void makeActions(final IWorkbenchWindow window) { + this.window = window; + /* + * Creates the actions and registers them. Registering is needed to ensure that key bindings work. The + * corresponding commands keybindings are defined in the plugin.xml file. Registering also provides automatic + * disposal of the actions when the window is closed. + */ + exitAction = ActionFactory.QUIT.create(window); + register(exitAction); + + aboutAction = ActionFactory.ABOUT.create(window); + aboutAction.setText("&About"); + aboutAction.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(Application.PLUGIN_ID, + IImageKeys.HELP_16x16)); + register(aboutAction); + + helpContentsAction = ActionFactory.HELP_CONTENTS.create(window); + helpContentsAction.setText("&Management Console Help"); + helpContentsAction.setAccelerator(SWT.F1); + //helpContentsAction.setImageDescriptor(newImage) + register(helpContentsAction); + } + + protected void fillMenuBar(IMenuManager menuBar) { + // File + MenuManager fileMenu = new MenuManager("&File", IWorkbenchActionConstants.M_FILE); + fileMenu.add(new Separator()); + fileMenu.add(exitAction); + + // Help + MenuManager helpMenu = new MenuManager("&Help", IWorkbenchActionConstants.M_HELP); + helpMenu.add(helpContentsAction); + helpMenu.add(aboutAction); + + menuBar.add(fileMenu); + // Add a group marker indicating where action set menus will appear. + // All action sets from plugin.xml will get added here + menuBar.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS)); + menuBar.add(helpMenu); + } + + protected void fillCoolBar(ICoolBarManager coolBar) { + // All our actions are added to toolbar through the extension point org.eclipse.ui.actionSets + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ApplicationWorkbenchAdvisor.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ApplicationWorkbenchAdvisor.java new file mode 100644 index 00000000..e0fa5539 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ApplicationWorkbenchAdvisor.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import org.eclipse.core.runtime.jobs.IJobChangeEvent; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.jobs.JobChangeAdapter; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.ui.application.IWorkbenchConfigurer; +import org.eclipse.ui.application.IWorkbenchWindowConfigurer; +import org.eclipse.ui.application.WorkbenchAdvisor; +import org.eclipse.ui.application.WorkbenchWindowAdvisor; +import org.gluster.storage.management.console.jobs.DataSyncJob; +import org.gluster.storage.management.console.preferences.PreferenceConstants; + + +/** + * This workbench advisor creates the window advisor, and specifies + * the perspective id for the initial window. + */ +public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor { + private Job syncJob; + private static final IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore(); + private long JOB_INTERVAL = preferenceStore.getLong(PreferenceConstants.P_DATA_SYNC_INTERVAL) * 1000; + private IPropertyChangeListener propertyChangeListener; + + public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) { + return new ApplicationWorkbenchWindowAdvisor(configurer); + } + + public String getInitialWindowPerspectiveId() { + return Perspective.ID; + } + + @Override + public void initialize(IWorkbenchConfigurer configurer) { + super.initialize(configurer); + configurer.setSaveAndRestore(false); // we don't need save/restore as of now + + createPropertyChangeListener(); + preferenceStore.addPropertyChangeListener(propertyChangeListener); + } + + private void createPropertyChangeListener() { + propertyChangeListener = new IPropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent event) { + if(event.getProperty().equals(PreferenceConstants.P_DATA_SYNC_INTERVAL)) { + JOB_INTERVAL = (Integer)event.getNewValue() * 1000L; + } + } + }; + } + + @Override + public void postStartup() { + super.postStartup(); + setupBackgroundJobs(); + } + + private void setupBackgroundJobs() { + syncJob = new DataSyncJob("Retrieving Management Info"); + syncJob.schedule(JOB_INTERVAL); + syncJob.addJobChangeListener(new JobChangeAdapter() { + @Override + public void done(IJobChangeEvent event) { + super.done(event); + + // job done. schedule again after the pre-defined interval + syncJob.schedule(JOB_INTERVAL); + } + }); + } + + @Override + public boolean preShutdown() { + return syncJob.cancel(); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ApplicationWorkbenchWindowAdvisor.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ApplicationWorkbenchWindowAdvisor.java new file mode 100644 index 00000000..41043b27 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ApplicationWorkbenchWindowAdvisor.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import org.eclipse.swt.graphics.Point; +import org.eclipse.ui.application.ActionBarAdvisor; +import org.eclipse.ui.application.IActionBarConfigurer; +import org.eclipse.ui.application.IWorkbenchWindowConfigurer; +import org.eclipse.ui.application.WorkbenchWindowAdvisor; +import org.gluster.storage.management.console.utils.GUIHelper; + + +public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor { + private final static int DEFAULT_WIDTH = 1024; + private final static int DEFAULT_HEIGHT = 768; + private final GUIHelper guiHelper = GUIHelper.getInstance(); + + public ApplicationWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) { + super(configurer); + } + + @Override + public ActionBarAdvisor createActionBarAdvisor(IActionBarConfigurer configurer) { + return new ApplicationActionBarAdvisor(configurer); + } + + @Override + public void preWindowOpen() { + super.preWindowOpen(); + + IWorkbenchWindowConfigurer configurer = getWindowConfigurer(); + configurer.setInitialSize(new Point(DEFAULT_WIDTH, DEFAULT_HEIGHT)); + configurer.setShowCoolBar(true); + configurer.setShowStatusLine(true); + configurer.setShowMenuBar(true); + configurer.setShowProgressIndicator(true); // shows progress indicator in status bar + } + + @Override + public void postWindowCreate() { + super.postWindowCreate(); + guiHelper.centerShellInScreen(getWindowConfigurer().getWindow().getShell()); + getWindowConfigurer().getWindow().getShell().setMaximized(true); + Application.getApplication().setStatusLineManager( + getWindowConfigurer().getActionBarConfigurer().getStatusLineManager()); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/BrickTableLabelProvider.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/BrickTableLabelProvider.java new file mode 100644 index 00000000..80b4c812 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/BrickTableLabelProvider.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import org.eclipse.swt.graphics.Image; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.BricksPage.BRICK_TABLE_COLUMN_INDICES; +import org.gluster.storage.management.console.views.pages.DisksPage.DISK_TABLE_COLUMN_INDICES; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Device; +import org.gluster.storage.management.core.model.Brick.BRICK_STATUS; +import org.gluster.storage.management.core.utils.NumberUtil; + + +public class BrickTableLabelProvider extends TableLabelProviderAdapter { + private GUIHelper guiHelper = GUIHelper.getInstance(); + + @Override + public Image getColumnImage(Object element, int columnIndex) { + + if (!(element instanceof Brick)) { + return null; + } + + Brick brick = (Brick) element; + if (columnIndex == DISK_TABLE_COLUMN_INDICES.STATUS.ordinal()) { + BRICK_STATUS status = brick.getStatus(); + + switch(status) { + case ONLINE: + return guiHelper.getImage(IImageKeys.BRICK_ONLINE_16x16); + case OFFLINE: + return guiHelper.getImage(IImageKeys.BRICK_OFFLINE_16x16); + } + } + return null; + } + + private String getDeviceFreeSpace(Device device) { + if (device != null && device.isReady() && device.getFreeSpace() != null) { + return NumberUtil.formatNumber((device.getFreeSpace() / 1024)); + } else { + return "NA"; + } + } + + private String getDeviceCapacity(Device device) { + if (device != null && device.isReady() && device.getSpace() != null && device.getSpace() != 0.0) { + return NumberUtil.formatNumber((device.getSpace() / 1024)); + } else { + return "NA"; + } + } + + @Override + public String getColumnText(Object element, int columnIndex) { + if (!(element instanceof Brick)) { + return null; + } + + Brick brick = (Brick) element; + Device device = GlusterDataModelManager.getInstance().getDeviceForBrickDir(brick); + return (columnIndex == BRICK_TABLE_COLUMN_INDICES.SERVER.ordinal() ? brick.getServerName() + : columnIndex == BRICK_TABLE_COLUMN_INDICES.BRICK.ordinal() ? brick.getBrickDirectory() + : columnIndex == BRICK_TABLE_COLUMN_INDICES.FREE_SPACE.ordinal() ? getDeviceFreeSpace(device) + : columnIndex == BRICK_TABLE_COLUMN_INDICES.TOTAL_SPACE.ordinal() ? getDeviceCapacity(device) + : columnIndex == BRICK_TABLE_COLUMN_INDICES.STATUS.ordinal() ? brick.getStatusStr() : "Invalid"); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ConsoleConstants.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ConsoleConstants.java new file mode 100644 index 00000000..ced7f9e7 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ConsoleConstants.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +/** + * + */ +public class ConsoleConstants { + public static final String CONSOLE_TITLE = "Gluster Management Console"; + public static final String TERMINAL_VIEW_ID = "org.eclipse.tm.terminal.view.TerminalView"; + public static final String PROPERTY_AUTO_LOGIN_PASSWORD = "auto.login.password"; + public static final String PROPERTY_AUTO_CLUSTER_NAME = "auto.cluster.name"; +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/DeviceTableLabelProvider.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/DeviceTableLabelProvider.java new file mode 100644 index 00000000..6b6f7669 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/DeviceTableLabelProvider.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import org.eclipse.jface.resource.FontRegistry; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.swt.graphics.Image; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import org.gluster.storage.management.core.model.Device; +import org.gluster.storage.management.core.model.Disk; +import org.gluster.storage.management.core.model.Partition; +import org.gluster.storage.management.core.model.Device.DEVICE_STATUS; +import org.gluster.storage.management.core.utils.NumberUtil; + + +public class DeviceTableLabelProvider extends LabelProvider implements ITableLabelProvider { + + private GUIHelper guiHelper = GUIHelper.getInstance(); + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + public enum DEVICE_COLUMN_INDICES { + DISK, PARTITION, FREE_SPACE, SPACE_IN_USE, STATUS + }; + + FontRegistry registry = new FontRegistry(); + + public DeviceTableLabelProvider() { + } + + @Override + public Image getColumnImage(Object element, int columnIndex) { + if (!(element instanceof Device)) { + return null; + } + + Device device = (Device) element; + if (columnIndex == DEVICE_COLUMN_INDICES.STATUS.ordinal()) { + DEVICE_STATUS status = device.getStatus(); + + if (status == null) { + if (element instanceof Partition) { + if (columnIndex == DEVICE_COLUMN_INDICES.STATUS.ordinal()) { + status = device.getStatus(); + } + } + } + + if (status == null) { + return null; + } + + if(element instanceof Disk && ((Disk)element).hasPartitions()) { + // disk has partitions. so don't show status image at disk level. + return null; + } + + switch (status) { + case INITIALIZED: + if(modelManager.isDeviceUsed(device)) { + return guiHelper.getImage(IImageKeys.DISK_IN_USE_16x16); + } else { + return guiHelper.getImage(IImageKeys.DISK_AVAILABLE_16x16); + } + case IO_ERROR: + return guiHelper.getImage(IImageKeys.IO_ERROR_16x16); + case UNINITIALIZED: + return guiHelper.getImage(IImageKeys.DISK_UNINITIALIZED_16x16); + case INITIALIZING: + return guiHelper.getImage(IImageKeys.DISK_INITIALIZING_16x16); + default: + throw new GlusterRuntimeException("Invalid disk status [" + status + "]"); + } + } + + return null; + } + + @Override + public String getText(Object element) { + return super.getText(element); + } + + private String getDeviceFreeSpace(Device device) { + if (device.hasErrors() || device.isUninitialized()) { + return "NA"; + } else { + return NumberUtil.formatNumber((device.getFreeSpace() / 1024)); + } + } + + private String getTotalDeviceSpace(Device device) { + if (device.hasErrors() || device.isUninitialized()) { + return "NA"; + } else { + return NumberUtil.formatNumber((device.getSpace() / 1024)); + } + } + + public String getColumnText(Object element, int columnIndex) { + + if (element == null) { + return ""; + } + + Device device = (Device) element; + if (columnIndex == DEVICE_COLUMN_INDICES.DISK.ordinal()) { + // show value in "disk" column only if it's a disk + if (device instanceof Disk) { + return device.getQualifiedName(); + } else { + return ""; + } + } + + if(element instanceof Disk && ((Disk)element).hasPartitions()) { + // disk has partitions. so don't show any other details + return ""; + } + + if (columnIndex == DEVICE_COLUMN_INDICES.FREE_SPACE.ordinal()) { + return "" + getDeviceFreeSpace(device); + } else if (columnIndex == DEVICE_COLUMN_INDICES.SPACE_IN_USE.ordinal()) { + return "" + getTotalDeviceSpace(device); + } else if (columnIndex == DEVICE_COLUMN_INDICES.PARTITION.ordinal()) { + if (device instanceof Partition) { + return device.getQualifiedName(); + } else { + return ""; + } + } else if (columnIndex == DEVICE_COLUMN_INDICES.STATUS.ordinal()) { + if(device.isUninitialized()) { + return ""; + } + if(modelManager.isDeviceUsed(device)) { + return "In Use"; + } else { + return device.getStatusStr(); + } + } else { + return ""; + } + } +}
\ No newline at end of file diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/EntityGroupContentProvider.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/EntityGroupContentProvider.java new file mode 100644 index 00000000..ca83d520 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/EntityGroupContentProvider.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.gluster.storage.management.core.model.EntityGroup; + + +public class EntityGroupContentProvider<T> implements + IStructuredContentProvider { + @Override + public void dispose() { + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + + } + + @SuppressWarnings("rawtypes") + @Override + public Object[] getElements(Object inputElement) { + if (inputElement instanceof EntityGroup) { + return ((EntityGroup) inputElement).getChildren().toArray(); + } + return null; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/GlusterDataModelManager.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/GlusterDataModelManager.java new file mode 100644 index 00000000..94d3c59c --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/GlusterDataModelManager.java @@ -0,0 +1,1036 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.preference.IPreferenceStore; +import org.gluster.storage.management.client.DiscoveredServersClient; +import org.gluster.storage.management.client.GlusterServersClient; +import org.gluster.storage.management.client.TasksClient; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.preferences.PreferenceConstants; +import org.gluster.storage.management.console.utils.GlusterLogger; +import org.gluster.storage.management.core.constants.GlusterConstants; +import org.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import org.gluster.storage.management.core.model.Alert; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Cluster; +import org.gluster.storage.management.core.model.ClusterListener; +import org.gluster.storage.management.core.model.Device; +import org.gluster.storage.management.core.model.Disk; +import org.gluster.storage.management.core.model.Event; +import org.gluster.storage.management.core.model.GlusterDataModel; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.Partition; +import org.gluster.storage.management.core.model.Server; +import org.gluster.storage.management.core.model.Status; +import org.gluster.storage.management.core.model.TaskInfo; +import org.gluster.storage.management.core.model.TaskStatus; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.VolumeOptionInfo; +import org.gluster.storage.management.core.model.Alert.ALERT_TYPES; +import org.gluster.storage.management.core.model.Brick.BRICK_STATUS; +import org.gluster.storage.management.core.model.Device.DEVICE_STATUS; +import org.gluster.storage.management.core.model.Device.DEVICE_TYPE; +import org.gluster.storage.management.core.model.Event.EVENT_TYPE; +import org.gluster.storage.management.core.model.TaskInfo.TASK_TYPE; +import org.gluster.storage.management.core.model.Volume.TRANSPORT_TYPE; +import org.gluster.storage.management.core.model.Volume.VOLUME_STATUS; +import org.gluster.storage.management.core.model.Volume.VOLUME_TYPE; +import org.gluster.storage.management.core.utils.GlusterCoreUtil; + + +public class GlusterDataModelManager { + private static GlusterDataModelManager instance = new GlusterDataModelManager(); + private GlusterDataModel model; + private String securityToken; + private List<ClusterListener> listeners = new ArrayList<ClusterListener>(); + private List<VolumeOptionInfo> volumeOptionsInfo; + private String clusterName; + private static Boolean syncInProgress = false; + private static final GlusterLogger logger = GlusterLogger.getInstance();; + + private GlusterDataModelManager() { + } + + public String getSecurityToken() { + return securityToken; + } + + public void setSecurityToken(String securityToken) { + this.securityToken = securityToken; + } + + public void setClusterName(String clusterName) { + this.clusterName = clusterName; + } + + public String getClusterName() { + return clusterName; + } + + public GlusterDataModel getModel() { + return model; + } + + public static GlusterDataModelManager getInstance() { + return instance; + } + + public void initializeModel(String clusterName, IProgressMonitor monitor) { + setClusterName(clusterName); + + model = fetchData(monitor); + } + + private GlusterDataModel fetchData(IProgressMonitor monitor) { + GlusterDataModel model = fetchModel(monitor); + + initializeAlerts(model.getCluster()); + initializeVolumeOptionsInfo(model.getCluster()); + + return model; + } + + public void refreshVolumeData(Volume oldVolume) { + VolumesClient volumeClient = new VolumesClient(); + Volume newVolume = volumeClient.getVolume(oldVolume.getName()); + if(!oldVolume.equals(newVolume)) { + volumeChanged(oldVolume, newVolume); + } + } + + private boolean isCancelled(IProgressMonitor monitor) { + if(monitor.isCanceled()) { + monitor.setTaskName("Data sync cancelled!"); + monitor.done(); + return true; + } else { + return false; + } + } + + public GlusterDataModel fetchModel(IProgressMonitor monitor) { + synchronized (syncInProgress) { + if(syncInProgress) { + logger.info("Previous data sync is still running. Skipping this one."); + return null; + } + syncInProgress = true; + } + + try { + logger.info("Starting data sync"); + GlusterDataModel model = new GlusterDataModel("Gluster Data Model"); + Cluster cluster = new Cluster(clusterName, model); + model.addCluster(cluster); + + monitor.beginTask("Data Sync", 6); + + monitor.setTaskName("Syncing servers..."); + initializeGlusterServers(cluster); + monitor.worked(1); + if(isCancelled(monitor)) { + return model; + } + + monitor.setTaskName("Syncing volumes..."); + initializeVolumes(cluster); + monitor.worked(1); + if(isCancelled(monitor)) { + return model; + } + + monitor.setTaskName("Syncing discovered servers..."); + initializeAutoDiscoveredServers(cluster); + monitor.worked(1); + if(isCancelled(monitor)) { + return model; + } + + monitor.setTaskName("Syncing tasks..."); + initializeTasks(cluster); + monitor.worked(1); + if(isCancelled(monitor)) { + return model; + } + + monitor.setTaskName("Syncing aggregated CPU stats..."); + initializeAggregatedCpuStats(cluster); + monitor.worked(1); + if(isCancelled(monitor)) { + return model; + } + + monitor.setTaskName("Syncing aggregated Network stats..."); + initializeAggregatedNetworkStats(cluster); + monitor.worked(1); + + monitor.done(); + return model; + } finally { + syncInProgress = false; + } + } + + public void updateModel(GlusterDataModel model) { + updateVolumes(model); + updateGlusterServers(model); + updateDiscoveredServers(model); + updateTasks(model); + updateAlerts(model); + updateServerStatistics(model); + } + + private void updateServerStatistics(GlusterDataModel newModel) { + model.getCluster().setAggregatedCpuStats(newModel.getCluster().getAggregatedCpuStats()); + model.getCluster().setAggregatedNetworkStats(newModel.getCluster().getAggregatedNetworkStats()); + for(ClusterListener listener : listeners) { + listener.aggregatedStatsChanged(); + } + } + + private void updateAlerts(GlusterDataModel newModel) { + model.getCluster().getAlerts().clear(); + + // generate alerts for "newModel" + initializeAlerts(newModel.getCluster()); + + // set the new alerts on "model" + model.getCluster().setAlerts(newModel.getCluster().getAlerts()); + + // fire event "alertsGenerated" + alertsGenerated(); + } + + private void updateTasks(GlusterDataModel newModel) { + List<TaskInfo> oldTasks = model.getCluster().getTaskInfoList(); + List<TaskInfo> newTasks = newModel.getCluster().getTaskInfoList(); + + Set<TaskInfo> addedTasks = GlusterCoreUtil.getAddedEntities(oldTasks, newTasks, true); + for(TaskInfo task : addedTasks) { + addTask(task); + } + + Set<TaskInfo> removedTasks = GlusterCoreUtil.getAddedEntities(newTasks, oldTasks, true); + for(TaskInfo task : removedTasks) { + removeTask(task); + } + + Map<TaskInfo, TaskInfo> modifiedTasks = GlusterCoreUtil.getModifiedEntities(oldTasks, newTasks); + for(Entry<TaskInfo, TaskInfo> entry : modifiedTasks.entrySet()) { + TaskInfo modifiedTask = entry.getKey(); + modifiedTask.copyFrom(entry.getValue()); + updateTask(modifiedTask); + } + } + + private void updateDiscoveredServers(GlusterDataModel newModel) { + List<Server> oldServers = model.getCluster().getAutoDiscoveredServers(); + List<Server> newServers = newModel.getCluster().getAutoDiscoveredServers(); + + Set<Server> addedServers = GlusterCoreUtil.getAddedEntities(oldServers, newServers, true); + for (Server addedServer : addedServers) { + addDiscoveredServer(addedServer); + } + + Set<Server> removedServers = GlusterCoreUtil.getAddedEntities(newServers, oldServers, true); + for (Server removedServer : removedServers) { + removeDiscoveredServer(removedServer); + } + + Map<Server, Server> modifiedServers = GlusterCoreUtil.getModifiedEntities(oldServers, newServers); + for(Entry<Server, Server> entry : modifiedServers.entrySet()) { + discoveredServerChanged(entry.getKey(), entry.getValue()); + } + } + + private void updateGlusterServers(GlusterDataModel newModel) { + List<GlusterServer> oldServers = model.getCluster().getServers(); + List<GlusterServer> newServers = newModel.getCluster().getServers(); + + Set<GlusterServer> addedServers = GlusterCoreUtil.getAddedEntities(oldServers, newServers, true); + for (GlusterServer addedServer : addedServers) { + addGlusterServer(addedServer); + } + + Set<GlusterServer> removedServers = GlusterCoreUtil.getAddedEntities(newServers, oldServers, true); + for (GlusterServer removedServer : removedServers) { + removeGlusterServer(removedServer); + } + + Map<GlusterServer, GlusterServer> modifiedServers = GlusterCoreUtil.getModifiedEntities(oldServers, newServers); + for(Entry<GlusterServer, GlusterServer> entry : modifiedServers.entrySet()) { + glusterServerChanged(entry.getKey(), entry.getValue()); + } + } + + public void glusterServerChanged(GlusterServer oldServer, GlusterServer newServer) { + oldServer.copyFrom(newServer); + for (ClusterListener listener : listeners) { + listener.serverChanged(oldServer, new Event(EVENT_TYPE.GLUSTER_SERVER_CHANGED, newServer)); + } + + updateDisks(oldServer, oldServer.getDisks(), newServer.getDisks()); + } + + private void updateDisks(Server server, List<Disk> oldDisks, List<Disk> newDisks) { + Set<Disk> addedDisks = GlusterCoreUtil.getAddedEntities(oldDisks, newDisks, false); + addDisks(server, addedDisks); + + Set<Disk> removedDisks = GlusterCoreUtil.getAddedEntities(newDisks, oldDisks, false); + removeDisks(server, removedDisks); + + Map<Disk, Disk> modifiedDisks = GlusterCoreUtil.getModifiedEntities(oldDisks, newDisks); + disksChanged(server, modifiedDisks); + } + + private void disksChanged(Server server, Map<Disk, Disk> modifiedDisks) { + if(modifiedDisks.size() == 0) { + return; + } + + for (Entry<Disk, Disk> entry : modifiedDisks.entrySet()) { + entry.getKey().copyFrom(entry.getValue()); + } + for (ClusterListener listener : listeners) { + if (server instanceof GlusterServer) { + listener.serverChanged((GlusterServer) server, new Event(EVENT_TYPE.DEVICES_CHANGED, modifiedDisks)); + } else { + listener.discoveredServerChanged(server, new Event(EVENT_TYPE.DEVICES_CHANGED, modifiedDisks)); + } + } + } + + public void updateDeviceStatus(String serverName, String deviceName, DEVICE_STATUS status) { + GlusterServer server = model.getCluster().getServer(serverName); + Device device = getDeviceDetails(server, deviceName); + if (device != null) { + device.setStatus(status); + device.setType(DEVICE_TYPE.DATA); + for (ClusterListener listener : listeners) { + listener.serverChanged(server, new Event(EVENT_TYPE.DEVICES_CHANGED, device)); + } + } + } + + private Device getDeviceDetails(GlusterServer server, String deviceName) { + for (Disk disk : server.getDisks()) { + if (disk.hasPartitions()) { + for (Partition partition : disk.getPartitions()) { + if (partition.getName().equals(deviceName)) { + return partition; + } + } + } else { + if (disk.getName().equals(deviceName)) { + return (Device) disk; + } + } + } + return null; + } + + public void addDisks(Server server, Set<Disk> disks) { + if(disks.size() == 0) { + return; + } + + server.addDisks(disks); + for (ClusterListener listener : listeners) { + if(server instanceof GlusterServer) { + listener.serverChanged((GlusterServer)server, new Event(EVENT_TYPE.DEVICES_ADDED, disks)); + } else { + listener.discoveredServerChanged(server, new Event(EVENT_TYPE.DEVICES_ADDED, disks)); + } + } + } + + public void removeDisks(Server server, Set<Disk> disks) { + if(disks.size() == 0) { + return; + } + + for(Disk disk : disks) { + server.removeDisk(disk); + } + + for (ClusterListener listener : listeners) { + if(server instanceof GlusterServer) { + listener.serverChanged((GlusterServer)server, new Event(EVENT_TYPE.DEVICES_REMOVED, disks)); + } else { + listener.discoveredServerChanged(server, new Event(EVENT_TYPE.DEVICES_REMOVED, disks)); + } + } + } + + private void updateVolumes(GlusterDataModel newModel) { + List<Volume> oldVolumes = model.getCluster().getVolumes(); + List<Volume> newVolumes = newModel.getCluster().getVolumes(); + + Set<Volume> addedVolumes = GlusterCoreUtil.getAddedEntities(oldVolumes, newVolumes, false); + for (Volume addedVolume : addedVolumes) { + addVolume(addedVolume); + } + + Set<Volume> removedVolumes = GlusterCoreUtil.getAddedEntities(newVolumes, oldVolumes, false); + for (Volume removedVolume : removedVolumes) { + deleteVolume(removedVolume); + } + + Map<Volume, Volume> modifiedVolumes = GlusterCoreUtil.getModifiedEntities(oldVolumes, newVolumes); + for(Entry<Volume, Volume> entry : modifiedVolumes.entrySet()) { + volumeChanged(entry.getKey(), entry.getValue()); + } + } + + public void volumeChanged(Volume oldVolume, Volume newVolume) { + oldVolume.copyFrom(newVolume); + + if (oldVolume.getStatus() != newVolume.getStatus()) { + updateVolumeStatusAlert(newVolume, newVolume.getStatus()); + } + + for (ClusterListener listener : listeners) { + listener.volumeChanged(oldVolume, new Event(EVENT_TYPE.VOLUME_CHANGED, newVolume)); + } + updateBricks(oldVolume, oldVolume.getBricks(), newVolume.getBricks()); + } + + private void updateBricks(Volume volume, List<Brick> oldBricks, List<Brick> newBricks) { + Set<Brick> addedBricks = GlusterCoreUtil.getAddedEntities(oldBricks, newBricks, false); + addBricks(volume, addedBricks); + + Set<Brick> removedBricks = GlusterCoreUtil.getAddedEntities(newBricks, oldBricks, false); + removeBricks(volume, removedBricks); + + Map<Brick, Brick> modifiedBricks = GlusterCoreUtil.getModifiedEntities(oldBricks, newBricks); + bricksChanged(volume, modifiedBricks); + } + + public void bricksChanged(Volume volume, Map<Brick, Brick> modifiedBricks) { + if(modifiedBricks.size() == 0) { + return; + } + + for(Entry<Brick, Brick> entry : modifiedBricks.entrySet()) { + entry.getKey().copyFrom(entry.getValue()); + } + + for (ClusterListener listener : listeners) { + listener.volumeChanged(volume, new Event(EVENT_TYPE.BRICKS_CHANGED, modifiedBricks)); + } + } + + private void initializeGlusterServers(Cluster cluster) { + cluster.setServers(new GlusterServersClient(cluster.getName()).getServers()); + } + + private void initializeAutoDiscoveredServers(Cluster cluster) { + cluster.setAutoDiscoveredServers(new DiscoveredServersClient(cluster.getName()).getDiscoveredServerDetails()); + } + + private void initializeVolumes(Cluster cluster) { + VolumesClient volumeClient = new VolumesClient(cluster.getName()); + cluster.setVolumes(volumeClient.getAllVolumes()); + } + + private void initializeVolumeOptionsInfo(Cluster cluster) { + if(cluster.getServers().isEmpty()) { + // cluster is empty. we won't be able to fetch the volume options information. + return; + } + this.volumeOptionsInfo = new VolumesClient(clusterName).getVolumeOptionsInfo(); + } + + private void initializeTasks(Cluster cluster) { + List<TaskInfo> taskInfoList = new TasksClient(cluster.getName()).getAllTasks(); + //List<TaskInfo> taskInfoList = getDummyTasks(); + cluster.setTaskInfoList(taskInfoList); + } + + public void initializeAggregatedCpuStats(Cluster cluster) { + IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore(); + String cpuStatsPeriod = preferenceStore.getString(PreferenceConstants.P_CPU_AGGREGATED_CHART_PERIOD); + + cluster.setAggregatedCpuStats(new GlusterServersClient().getAggregatedCpuStats(cpuStatsPeriod)); + } + + public void initializeAggregatedNetworkStats(Cluster cluster) { + IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore(); + String networkStatsPeriod = preferenceStore.getString(PreferenceConstants.P_NETWORK_AGGREGATED_CHART_PERIOD); + + cluster.setAggregatedNetworkStats(new GlusterServersClient().getAggregatedNetworkStats(networkStatsPeriod)); + } + + private List<TaskInfo> getDummyTasks() { + List<TaskInfo> taskInfoList = new ArrayList<TaskInfo>(); + + // Task #1 + TaskInfo taskInfo = new TaskInfo(); + taskInfo.setType(TASK_TYPE.BRICK_MIGRATE); + taskInfo.setName("Migrate Brick-music"); + taskInfo.setPauseSupported(true); + taskInfo.setStopSupported(true); + taskInfo.setStatus(new TaskStatus(new Status(Status.STATUS_CODE_PAUSE, ""))); + + taskInfo.getStatus().setMessage("Paused"); + taskInfo.setDescription("Migrate Brick on volume [Movies] from /export/adb/music to /export/sdc/music."); + taskInfoList.add(taskInfo); + + // Task #2 + taskInfo = new TaskInfo(); + taskInfo.setType(TASK_TYPE.DISK_FORMAT); + taskInfo.setName("Initialize disk [KVM-GVSA1:sdc]"); + taskInfo.setPauseSupported(false); + taskInfo.setStopSupported(false); + taskInfo.setStatus( new TaskStatus(new Status(Status.STATUS_CODE_RUNNING, ""))); + taskInfo.getStatus().setMessage("Format completed 80% ..."); + taskInfo.setDescription("Formatting disk [KVM-GVSA1:sdc]"); + taskInfoList.add(taskInfo); + + // Task #2 + taskInfo = new TaskInfo(); + taskInfo.setType(TASK_TYPE.VOLUME_REBALANCE); + taskInfo.setName("Rebalance volume [songs]"); + taskInfo.setPauseSupported(false); + taskInfo.setStopSupported(false); + taskInfo.setStatus( new TaskStatus(new Status(Status.STATUS_CODE_RUNNING, ""))); + taskInfo.getStatus().setMessage("Rebalance step1: layout fix in progress"); + taskInfo.setDescription("Rebalance volume [songs]"); + taskInfoList.add(taskInfo); + + return taskInfoList; + } + + private List<Alert> getDummyAlerts(Cluster cluster) { + List<Alert> alerts = new ArrayList<Alert>(); + for (Server server : cluster.getServers()) { + if (alerts.size() == 0) { + alerts.add(new Alert(ALERT_TYPES.CPU_USAGE_ALERT, server.getName(), + Alert.ALERT_TYPE_STR[ALERT_TYPES.CPU_USAGE_ALERT.ordinal()] + " [93.42 %] in " + + server.getName())); + continue; + } + + if (alerts.size() == 1) { + alerts.add(new Alert(ALERT_TYPES.MEMORY_USAGE_ALERT, server.getName(), + Alert.ALERT_TYPE_STR[ALERT_TYPES.MEMORY_USAGE_ALERT.ordinal()] + " [91.83 %] in " + + server.getName())); + continue; + } + + if (alerts.size() == 2) { + alerts.add(new Alert(ALERT_TYPES.OFFLINE_SERVERS_ALERT, server.getName(), + Alert.ALERT_TYPE_STR[ALERT_TYPES.OFFLINE_SERVERS_ALERT.ordinal()] + " " + server.getName())); + continue; + } + + if (alerts.size() == 3) { + alerts.add(new Alert(ALERT_TYPES.OFFLINE_VOLUME_BRICKS_ALERT, "songs", + Alert.ALERT_TYPE_STR[ALERT_TYPES.OFFLINE_VOLUME_BRICKS_ALERT.ordinal()] + + " [KVM-GVSA4:/export/hdb4/songs] in volume [songs]")); + continue; + } + } + return alerts; + } + + public void initializeAlerts(Cluster cluster) { + AlertsManager alertsManager = new AlertsManager(cluster); + alertsManager.buildAlerts(); + cluster.setAlerts( alertsManager.getAlerts() ); + //cluster.setAlerts( getDummyAlerts(cluster) ); + } + + public Volume addVolume(List<Volume> volumes, String name, Cluster cluster, VOLUME_TYPE volumeType, + TRANSPORT_TYPE transportType, VOLUME_STATUS status) { + Volume volume = new Volume(name, cluster, volumeType, transportType, status); + volumes.add(volume); + + return volume; + } + +// private Device getDevice(String serverName, String deviceName) { +// List<Device> allDevices = getReadyDevicesOfAllServers(); +// for (Device device : allDevices) { +// if (device.getServerName().equals(serverName) && device.getName().equals(deviceName)) { +// return device; +// } +// } +// return null; +// } + + /* + * @param diskName (sda) + * + * @return The device object for given device name + */ + public Device getDeviceDetails(String deviceName) { + List<Device> allDevices = getReadyDevicesOfAllServers(); + for (Device device : allDevices) { + if (device.getName().equals(deviceName)) { + return device; + } + } + return null; + } + + + public Device getDeviceForBrickDir(Brick brick) { + Device brickDevice = null; + for (Device device : getReadyDevicesOfServer(brick.getServerName(), new ArrayList<Device>())) { + if (brick.getBrickDirectory().startsWith( device.getMountPoint() )) { + if (brickDevice == null || device.getMountPoint().length() > brickDevice.getMountPoint().length()) { + brickDevice = device; + } + } + } + return brickDevice; + } + + public List<Device> getDevicesOfVolume(Volume volume) { + Device device = null; + List<Device> volumeDevices = new ArrayList<Device>(); + for (Brick brick : volume.getBricks()) { + // device = getDevice(brick.getServerName(), brick.getDeviceName()); + device = getDeviceForBrickDir(brick); + if (device != null) { + volumeDevices.add(device); + } + } + return volumeDevices; + } + + public List<Device> getReadyDevicesOfAllServers() { + return getReadyDevicesOfAllServersExcluding(new ArrayList<Device>()); + } + + public List<Device> getReadyDevicesOfAllServersExcluding(List<Device> excludeDevices) { + List<Device> devices = new ArrayList<Device>(); + + for (Server server : model.getCluster().getServers()) { + devices.addAll( getReadyDevicesOfServer(server.getName(), excludeDevices) ); + } + return devices; + } + + public List<Device> getReadyDevicesOfServer(String serverName, List<Device> excludeDevices) { + List<Device> devices = new ArrayList<Device>(); + GlusterServer server = model.getCluster().getServer(serverName); + if (server == null || !server.isOnline()) { + return devices; + } + for (Disk disk : server.getDisks()) { + if (disk.hasPartitions()) { + for (Partition partition : disk.getPartitions()) { + if (partition.isReady() && !excludeDevices.contains(partition)) { + devices.add(partition); + } + } + } else if (disk.isReady() && !excludeDevices.contains(disk)) { + devices.add(disk); + } + } + return devices; + } + + public void addClusterListener(ClusterListener listener) { + listeners.add(listener); + } + + public void removeClusterListener(ClusterListener listener) { + listeners.remove(listener); + } + + public void addGlusterServer(GlusterServer server) { + Cluster cluster = model.getCluster(); + cluster.addServer(server); + + for (ClusterListener listener : listeners) { + listener.serverAdded(server); + } + + removeDiscoveredServer(server.getName()); + } + + public void addDiscoveredServer(Server server) { + Cluster cluster = model.getCluster(); + cluster.addDiscoveredServer(server); + + for (ClusterListener listener : listeners) { + listener.discoveredServerAdded(server); + } + } + + public void discoveredServerChanged(Server oldServer, Server newServer) { + oldServer.copyFrom(newServer); + for (ClusterListener listener : listeners) { + listener.discoveredServerChanged(oldServer, new Event(EVENT_TYPE.DISCOVERED_SERVER_CHANGED, newServer)); + } + updateDisks(oldServer, oldServer.getDisks(), newServer.getDisks()); + } + + public void removeDiscoveredServer(String serverName) { + Cluster cluster = model.getCluster(); + // TODO: Move auto-discovered servers outside the cluster + for(Server server : cluster.getAutoDiscoveredServers()) { + if(server.getName().toUpperCase().equals(serverName.toUpperCase())) { + removeDiscoveredServer(server); + return; + } + } + } + + public void removeDiscoveredServer(Server server) { + Cluster cluster = model.getCluster(); + cluster.removeDiscoveredServer(server); + + for (ClusterListener listener : listeners) { + listener.discoveredServerRemoved(server); + } + } + + public void removeGlusterServer(GlusterServer server) { + Cluster cluster = model.getCluster(); + cluster.removeServer(server); + + // can't use an iterator here. The method AbstractList.Itr#next checks for concurrent modification. + // Since listeners can end up creating new views, which add themselves as listeners, the listeners + // list can be concurrently modified which can result in an exception while using iterator. + // Hence we use List#get instead of the iterator + for(int i = 0; i < listeners.size(); i++) { + ClusterListener listener = listeners.get(i); + listener.serverRemoved(server); + } + + // add it to discovered servers list if it is online server + if (server.isOnline()) { + Server removedServer = new Server(); + removedServer.copyFrom(server); + removedServer.addDisks(server.getDisks()); + addDiscoveredServer(removedServer); + } + } + + public void deleteVolume(Volume volume) { + Cluster cluster = model.getCluster(); + cluster.deleteVolume(volume); + + // can't use an iterator here. The method AbstractList.Itr#next checks for concurrent modification. + // Since listeners can end up creating new views, which add themselves as listeners, the listeners + // list can be concurrently modified which can result in an exception while using iterator. + // Hence we use List#get instead of the iterator + for(int i = 0; i < listeners.size(); i++) { + ClusterListener listener = listeners.get(i); + listener.volumeDeleted(volume); + } + } + + public void updateVolumeStatus(Volume volume, VOLUME_STATUS newStatus) { + volume.setStatus(newStatus); + updateVolumeStatusAlert(volume, newStatus); + + if(newStatus == VOLUME_STATUS.OFFLINE) { + // mark as bricks also as offline + for(Brick brick : volume.getBricks()) { + brick.setStatus(BRICK_STATUS.OFFLINE); + } + for (ClusterListener listener : listeners) { + listener.volumeChanged(volume, new Event(EVENT_TYPE.BRICKS_CHANGED, volume.getBricks())); + } + } else { + Volume newVolume = new VolumesClient().getVolume(volume.getName()); //Getting latest brick info + updateBricks(volume, volume.getBricks(), newVolume.getBricks()); + } + + for (ClusterListener listener : listeners) { + listener.volumeChanged(volume, new Event(EVENT_TYPE.VOLUME_STATUS_CHANGED, newStatus)); + } + } + + private void updateVolumeStatusAlert(Volume volume, VOLUME_STATUS newStatus) { + Alert alert = null; + if (newStatus == VOLUME_STATUS.OFFLINE) { + alert = createOfflineVolumeAlert(volume); + for (ClusterListener listener : listeners) { + listener.alertCreated(alert); + } + } else { + alert = removeOfflineVolumeAlert(volume); + for (ClusterListener listener : listeners) { + listener.alertRemoved(alert); + } + } + } + + private Alert createOfflineVolumeAlert(Volume volume) { + Alert alert = new Alert(ALERT_TYPES.OFFLINE_VOLUME_ALERT, volume.getName(), + Alert.ALERT_TYPE_STR[ALERT_TYPES.OFFLINE_VOLUME_ALERT.ordinal()] + " [" + volume.getName() + "]"); + getModel().getCluster().addAlert(alert); + return alert; + } + + private Alert removeOfflineVolumeAlert(Volume volume) { + List<Alert> clusterAlerts = getModel().getCluster().getAlerts(); + Alert removedAlert = null; + for (Alert alert : clusterAlerts) { + if (alert.getType().equals(ALERT_TYPES.OFFLINE_VOLUME_ALERT) + && alert.getReference().equals(volume.getName())) { + removedAlert = alert; + clusterAlerts.remove(alert); + break; + } + } + return removedAlert; + } + + 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 addBricks(Volume volume, Set<Brick> bricks) { + if(bricks.size() == 0) { + return; + } + + volume.addBricks(bricks); + for (ClusterListener listener : listeners) { + listener.volumeChanged(volume, new Event(EVENT_TYPE.BRICKS_ADDED, bricks)); + } + updateVolumeTypeByBricks(volume); + } + + public void removeBricks(Volume volume, Set<Brick> bricks) { + if(bricks.size() == 0) { + return; + } + + // Remove the bricks from the volume object + for (Brick brick : bricks) { + volume.removeBrick(brick); + } + + for (ClusterListener listener : listeners) { + listener.volumeChanged(volume, new Event(EVENT_TYPE.BRICKS_REMOVED, bricks)); + } + updateVolumeTypeByBricks(volume); + } + + private void updateVolumeTypeByBricks(Volume volume) { + VOLUME_TYPE volumeType = volume.getVolumeType(); + if (volumeType.equals(VOLUME_TYPE.REPLICATE) || volumeType.equals(VOLUME_TYPE.DISTRIBUTED_REPLICATE)) { + if (volume.getBricks().size() > volume.getReplicaCount()) { + volume.setVolumeType(VOLUME_TYPE.DISTRIBUTED_REPLICATE); + } else { + volume.setVolumeType(VOLUME_TYPE.REPLICATE); + } + } else if (volumeType.equals(VOLUME_TYPE.STRIPE) || volumeType.equals(VOLUME_TYPE.DISTRIBUTED_STRIPE)) { + if (volume.getBricks().size() > volume.getStripeCount()) { + volume.setVolumeType(VOLUME_TYPE.DISTRIBUTED_STRIPE); + } else { + volume.setVolumeType(VOLUME_TYPE.STRIPE); + } + } + } + + public void setVolumeOption(Volume volume, String optionKey, String optionValue) { + volume.setOption(optionKey, optionValue); + for (ClusterListener listener : listeners) { + listener.volumeChanged(volume, new Event(EVENT_TYPE.VOLUME_OPTION_SET, optionKey)); + } + } + + public void addVolume(Volume volume) { + Cluster cluster = model.getCluster(); + cluster.addVolume(volume); + + for (ClusterListener listener : listeners) { + listener.volumeCreated(volume); + } + } + + public void addTask(TaskInfo taskInfo) { + Cluster cluster = model.getCluster(); + // To avoid duplicate task, Remove if already exist + TaskInfo existingTaskInfo = getTask(taskInfo.getName()); + if (getTask(taskInfo.getName()) != null) { + removeTask(existingTaskInfo); + } + cluster.addTaskInfo(taskInfo); + for (ClusterListener listener : listeners) { + listener.taskAdded(taskInfo); + } + } + + public TaskInfo getTask(String taskId) { + for (TaskInfo taskInfo: model.getCluster().getTaskInfoList()) { + if (taskInfo.getName().equals(taskId)) { + return taskInfo; + } + } + return null; + } + + public TaskInfo getTaskByReference(String reference) { + for (TaskInfo taskInfo: model.getCluster().getTaskInfoList()) { + if (taskInfo.getReference().equals(reference)) { + return taskInfo; + } + } + return null; + } + + // Updating the Task + public void updateTask(TaskInfo taskInfo) { + for (ClusterListener listener : listeners) { + listener.taskUpdated(taskInfo); + } + } + + public void removeTask(TaskInfo taskInfo) { + model.getCluster().removeTaskInfo(taskInfo); + for (ClusterListener listener : listeners) { + listener.taskRemoved(taskInfo); + } + } + + public void alertsGenerated() { + for (ClusterListener listener : listeners) { + listener.alertsGenerated(); + } + } + + public List<VolumeOptionInfo> getVolumeOptionsInfo() { + if(volumeOptionsInfo == null || volumeOptionsInfo.isEmpty()) { + initializeVolumeOptionsInfo(getModel().getCluster()); + } + return volumeOptionsInfo; + } + + public VolumeOptionInfo getVolumeOptionInfo(String optionKey) { + for (VolumeOptionInfo info : volumeOptionsInfo) { + 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) { + setVolumeOption(volume, Volume.OPTION_AUTH_ALLOW, accessControlList); + } + + public void setNfsEnabled(Volume volume, boolean enabled) { + setVolumeOption(volume, Volume.OPTION_NFS_DISABLE, (enabled) ? GlusterConstants.OFF : GlusterConstants.ON); + } + + public void setCifsConfig(Volume volume, boolean enabled, List<String> cifsUsers) { + if (enabled) { + volume.enableCifs(); + volume.setCifsUsers(cifsUsers); + } else { + volume.disableCifs(); + } + } + + public Server getGlusterServer(String serverName) { + for (Server server : model.getCluster().getServers()) { + if (server.getName().equalsIgnoreCase(serverName)) { + return server; + } + } + return null; + } + + private Boolean isDeviceUsed(Volume volume, Device device) { + Device brickDevice = null; + for (Brick brick : volume.getBricks()) { + brickDevice = getDeviceForBrickDir(brick); + if (brickDevice != null && device.getName().equals(brickDevice.getName()) + && device.getServerName().equalsIgnoreCase(brick.getServerName())) { + return true; + } + } + return false; + } + + public boolean isDeviceUsed(Device device) { + if (device.getStatus() == DEVICE_STATUS.INITIALIZED) { + for (Volume volume : model.getCluster().getVolumes()) { + if (isDeviceUsed(volume, device)) { + return true; + } + } + } + return false; + } + + public List<String> getVolumesOfServer(String serverName) { + List<String> volumeNames = new ArrayList<String>(); + Cluster cluster = model.getCluster(); + for (Volume volume : cluster.getVolumes()) { + for (Brick brick : volume.getBricks()) { + if (serverName.equalsIgnoreCase(brick.getServerName())) { + volumeNames.add(volume.getName()); + break; + } + } + } + return volumeNames; + } + + public List<String> getOfflineServers() { + List<String> offlineServers = new ArrayList<String>(); + for(GlusterServer server : model.getCluster().getServers()) { + if (!server.isOnline()) { + offlineServers.add(server.getName()); + } + } + return offlineServers; + } + + public List<String> getCifsEnabledVolumeNames(List<Volume> selectedVolumes) { + List<String> cifsVolumes = new ArrayList<String>(); + for(Volume volume : selectedVolumes) { + if (volume.isCifsEnable()) { + cifsVolumes.add(volume.getName()); + } + } + return cifsVolumes; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/GlusterServerTableLabelProvider.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/GlusterServerTableLabelProvider.java new file mode 100644 index 00000000..3ea52446 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/GlusterServerTableLabelProvider.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import org.eclipse.swt.graphics.Image; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.GlusterServersPage.GLUSTER_SERVER_TABLE_COLUMN_INDICES; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.Server.SERVER_STATUS; +import org.gluster.storage.management.core.utils.NumberUtil; + + +public class GlusterServerTableLabelProvider extends TableLabelProviderAdapter { + private GUIHelper guiHelper = GUIHelper.getInstance(); + + @Override + public Image getColumnImage(Object element, int columnIndex) { + if (!(element instanceof GlusterServer)) { + return null; + } + + GlusterServer server = (GlusterServer) element; + if(columnIndex == GLUSTER_SERVER_TABLE_COLUMN_INDICES.STATUS.ordinal()) { + SERVER_STATUS status = server.getStatus(); + if(status == SERVER_STATUS.ONLINE) { + return guiHelper.getImage(IImageKeys.STATUS_ONLINE_16x16); + } else { + return guiHelper.getImage(IImageKeys.STATUS_OFFLINE_16x16); + } + } + + return null; + } + + @Override + public String getColumnText(Object element, int columnIndex) { + if (!(element instanceof GlusterServer)) { + return null; + } + + GlusterServer server = (GlusterServer) element; + + if (server.getStatus() == SERVER_STATUS.OFFLINE + && columnIndex != GLUSTER_SERVER_TABLE_COLUMN_INDICES.NAME.ordinal() + && columnIndex != GLUSTER_SERVER_TABLE_COLUMN_INDICES.STATUS.ordinal()) { + return "NA"; + } + + return (columnIndex == GLUSTER_SERVER_TABLE_COLUMN_INDICES.NAME.ordinal() ? server.getName() + : columnIndex == GLUSTER_SERVER_TABLE_COLUMN_INDICES.STATUS.ordinal() ? server.getStatusStr() + // : columnIndex == GLUSTER_SERVER_TABLE_COLUMN_INDICES.PREFERRED_NETWORK.ordinal() ? server.getPreferredNetworkInterface().getName() + : columnIndex == GLUSTER_SERVER_TABLE_COLUMN_INDICES.NUM_OF_CPUS.ordinal() ? "" + server.getNumOfCPUs() + //: columnIndex == SERVER_DISK_TABLE_COLUMN_INDICES.CPU_USAGE.ordinal() ? "" + server.getCpuUsage() + : columnIndex == GLUSTER_SERVER_TABLE_COLUMN_INDICES.TOTAL_MEMORY.ordinal() ? "" + NumberUtil.formatNumber((server.getTotalMemory() / 1024)) + //: columnIndex == SERVER_DISK_TABLE_COLUMN_INDICES.MEMORY_IN_USE.ordinal() ? "" + server.getMemoryInUse() + : columnIndex == GLUSTER_SERVER_TABLE_COLUMN_INDICES.TOTAL_FREE_SPACE.ordinal() ? NumberUtil.formatNumber((server.getFreeDiskSpace() / 1024)) + : columnIndex == GLUSTER_SERVER_TABLE_COLUMN_INDICES.IP_ADDRESSES.ordinal() ? server.getIpAddressesAsString() + : columnIndex == GLUSTER_SERVER_TABLE_COLUMN_INDICES.TOTAL_DISK_SPACE.ordinal() ? NumberUtil.formatNumber((server.getTotalDiskSpace() / 1024)) + : "Invalid"); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ICommandIds.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ICommandIds.java new file mode 100644 index 00000000..c7772a03 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ICommandIds.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +/** + * Interface defining the application's command IDs. + * Key bindings can be defined for specific commands. + * To associate an action with a command, use IAction.setActionDefinitionId(commandId). + * + * @see org.eclipse.jface.action.IAction#setActionDefinitionId(String) + */ +public interface ICommandIds { + + public static final String CMD_OPEN = "org.gluster.storage.management.console.open"; + public static final String CMD_OPEN_MESSAGE = "org.gluster.storage.management.console.openMessage"; + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/IEntityListener.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/IEntityListener.java new file mode 100644 index 00000000..e9e837ba --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/IEntityListener.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import org.gluster.storage.management.core.model.Entity; + +/** + * Any class that is interested in changes to entities in application scope should implement this interface and register + * with the application using {@link Application#addEntityListener(IEntityListener)} + * + * @author root + * + */ +public interface IEntityListener { + /** + * This method is called whenever any attribute of an entity in application scope changes + * @param entity Entity that has changed + * @param paremeters List of attribute names that have changed. This can be null. + */ + public void entityChanged(Entity entity, String[] paremeters); +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/IImageKeys.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/IImageKeys.java new file mode 100644 index 00000000..eba97ccc --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/IImageKeys.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +public interface IImageKeys { + + + public static final String CLUSTER_16x16 = "icons/tango/16x16/cluster.png"; + + public static final String VOLUMES_16x16 = "icons/tango/16x16/volumes.png"; + public static final String VOLUME_16x16 = "icons/tango/16x16/volume.png"; + public static final String CREATE_VOLUME_32x32 = "icons/tango/32x32/create-volume.png"; + public static final String START_VOLUME_32x32 = "icons/tango/32x32/start-volume.png"; + public static final String STOP_VOLUME_32x32 = "icons/tango/32x32/stop-volume.png"; + public static final String RESET_VOLUME_OPTIONS_32x32 = "icons/tango/32x32/reset-volume-option.png"; + public static final String VOLUME_OPTIONS_16x16 = "icons/tango/16x16/volume-options.png"; + public static final String CREATE_VOLUME_48x48 = "icons/tango/48x48/create-volume.png"; + public static final String REMOVE_VOLUME_32x32 = "icons/tango/32x32/remove-volume.png"; + public static final String VOLUME_REBALANCE_32x32 = "icons/tango/32x32/volume-rebalance.png"; + public static final String VOLUME_REBALANCE_22x22 = "icons/tango/22x22/volume-rebalance.png"; + public static final String BRICK_MIGRATE_32x32 = "icons/tango/32x32/migrate-brick.png"; + public static final String BRICK_MIGRATE_22x22 = "icons/tango/22x22/migrate-brick.png"; + public static final String ADD_BRICK_32x32 = "icons/tango/32x32/add-brick.png"; + public static final String REMOVE_BRICK_32x32 = "icons/tango/32x32/remove-brick.png"; + public static final String BRICK_OFFLINE_22x22 = "icons/tango/22x22/offline-brick.png"; + public static final String BRICKS_16x16 = "icons/tango/16x16/bricks.png"; + public static final String BRICK_ONLINE_16x16 = "icons/tango/16x16/online-brick.png"; + public static final String BRICK_OFFLINE_16x16 = "icons/tango/16x16/offline-brick.png"; + public static final String VOLUME_OFFLINE_22x22 = "icons/tango/22x22/offline-volume.png"; + + public static final String SERVERS_16x16 = "icons/tango/16x16/servers.png"; + public static final String SERVER_16x16 = "icons/tango/16x16/server.png"; + public static final String SERVER_WARNING_22x22 = "icons/tango/22x22/server-warning.png"; + public static final String MEMORY_USAGE_ALERT_22x22 = "icons/tango/22x22/high-memory-usage.png"; + public static final String SERVER_OFFLINE_22x22 = "icons/tango/22x22/offline-server.png"; + public static final String ADD_SERVER_32x32 = "icons/tango/32x32/add-server.png"; + public static final String ADD_SERVER_48x48 = "icons/tango/48x48/add-server.png"; + public static final String REMOVE_SERVER_32x32 = "icons/tango/32x32/remove-server.png"; + + public static final String DISK_16x16 = "icons/tango/16x16/disk.png"; + public static final String DISKS_16x16 = "icons/tango/16x16/disk.png"; + public static final String DISK_UNINITIALIZED_16x16 = "icons/tango/16x16/disk-uninitialized.png"; + public static final String IO_ERROR_16x16 = "icons/tango/16x16/disk-error.png"; + public static final String DISK_AVAILABLE_16x16 = "icons/tango/16x16/disk-available.png"; + public static final String DISK_INITIALIZING_16x16 = "icons/tango/16x16/disk-initialisation.png"; + public static final String DISK_INITIALIZING_22x22 = "icons/tango/22x22/disk-initialisation.png"; + public static final String DISK_IN_USE_16x16 = "icons/tango/16x16/disk-inuse.png"; + public static final String LOW_DISK_SPACE_22x22 = "icons/tango/22x22/low-diskspace.png"; + + public static final String STATUS_OFFLINE_16x16 = "icons/tango/16x16/status-offline.png"; + public static final String STATUS_ONLINE_16x16 = "icons/tango/16x16/status-online.png"; + + public static final String HELP_16x16 = "icons/tango/16x16/question.png"; + public static final String SEARCH_22x22 = "icons/tango/22x22/system-search.png"; + public static final String ARROW_UP_16x16 = "icons/tango/16x16/arrow-up.png"; + public static final String ARROW_DOWN_16x16 = "icons/tango/16x16/arrow-down.png"; + + public static final String DOWNLOAD_LOG_32x32 = "icons/tango/32x32/download-log.png"; + + + public static final String PAUSE_TASK_32x32 = "icons/tango/32x32/pause.png"; + public static final String RESUME_TASK_32x32 = "icons/tango/32x32/start.png"; + public static final String STOP_TASK_32x32 = "icons/tango/32x32/stop.png"; + public static final String CLEAR_TASK_32x32 = "icons/tango/32x32/clear-task.png"; + public static final String COMMIT_TASK_32x32 = "icons/tango/32x32/commit-task.png"; + public static final String PAUSE_TASK_16x16 = "icons/tango/16x16/pause.png"; + public static final String RESUME_TASK_16x16 = "icons/tango/16x16/start.png"; + public static final String STOP_TASK_16x16 = "icons/tango/16x16/stop.png"; + public static final String CLEAR_TASK_16x16 = "icons/tango/16x16/close_task.png"; + public static final String COMPLETED_TASK_16x16 = "icons/tango/16x16/task-completed.png"; + + public static final String OVERLAY_OFFLINE_8x8 = "icons/tango/8x8/offline.png"; + public static final String OVERLAY_ONLINE_8x8 = "icons/tango/8x8/online.png"; + public static final String OVERLAY_STAR_8x8 = "icons/tango/8x8/star.png"; + + public static final String SPLASH_IMAGE = "splash.bmp"; + public static final String DIALOG_SPLASH_IMAGE = "images/splash-dialog.bmp"; + + public static final String GAUGE_SMALL = "images/gauge_small.png"; + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/NetworkInterfaceTableLabelProvider.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/NetworkInterfaceTableLabelProvider.java new file mode 100644 index 00000000..53610c3b --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/NetworkInterfaceTableLabelProvider.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + + +import org.gluster.storage.management.console.views.GlusterServerSummaryView.NETWORK_INTERFACE_TABLE_COLUMN_INDICES; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.NetworkInterface; + + +public class NetworkInterfaceTableLabelProvider extends TableLabelProviderAdapter { + @Override + public String getColumnText(Object element, int columnIndex) { + if (!(element instanceof NetworkInterface)) { + return null; + } + + NetworkInterface networkInterface = (NetworkInterface) element; + String columnText = (columnIndex == NETWORK_INTERFACE_TABLE_COLUMN_INDICES.INTERFACE.ordinal() ? networkInterface.getName() + : columnIndex == NETWORK_INTERFACE_TABLE_COLUMN_INDICES.MODEL.ordinal() ? networkInterface.getModel() + : columnIndex == NETWORK_INTERFACE_TABLE_COLUMN_INDICES.SPEED.ordinal() ? networkInterface.getSpeed() + : columnIndex == NETWORK_INTERFACE_TABLE_COLUMN_INDICES.IP_ADDRESS.ordinal() ? networkInterface.getIpAddress() + : columnIndex == NETWORK_INTERFACE_TABLE_COLUMN_INDICES.NETMASK.ordinal() ? networkInterface.getNetMask() + : columnIndex == NETWORK_INTERFACE_TABLE_COLUMN_INDICES.GATEWAY.ordinal() ? networkInterface.getDefaultGateway() + : "Invalid"); + return ((columnText == null || columnText.trim().equals("")) ? CoreConstants.NA : columnText); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/Perspective.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/Perspective.java new file mode 100644 index 00000000..e2a867ed --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/Perspective.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import org.eclipse.ui.IPageLayout; +import org.eclipse.ui.IPerspectiveFactory; + +public class Perspective implements IPerspectiveFactory { + + /** + * The ID of the perspective as specified in the extension. + */ + public static final String ID = Perspective.class.getName(); + + public void createInitialLayout(IPageLayout layout) { + layout.setEditorAreaVisible(false); + //layout.addStandaloneView(ClusterView.ID, false, IPageLayout.LEFT, 0.30f, layout.getEditorArea()); + //layout.addStandaloneView(DetailsView.ID, false, IPageLayout.RIGHT, 0.70f, layout.getEditorArea()); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ServerDiskTableLabelProvider.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ServerDiskTableLabelProvider.java new file mode 100644 index 00000000..aa4c23c4 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ServerDiskTableLabelProvider.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import org.eclipse.swt.graphics.Image; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.ServerDisksPage.SERVER_DISK_TABLE_COLUMN_INDICES; +import org.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import org.gluster.storage.management.core.model.Device; +import org.gluster.storage.management.core.model.Disk; +import org.gluster.storage.management.core.model.Partition; +import org.gluster.storage.management.core.model.Device.DEVICE_STATUS; +import org.gluster.storage.management.core.utils.NumberUtil; + +import static org.gluster.storage.management.console.DeviceTableLabelProvider.DEVICE_COLUMN_INDICES; + + +public class ServerDiskTableLabelProvider extends TableLabelProviderAdapter { + private GUIHelper guiHelper = GUIHelper.getInstance(); + private GlusterDataModelManager glusterDataModelManager = GlusterDataModelManager.getInstance(); + + @Override + public Image getColumnImage(Object element, int columnIndex) { + if (!(element instanceof Device)) { + return null; + } + + Device device = (Device) element; + if (columnIndex == SERVER_DISK_TABLE_COLUMN_INDICES.STATUS.ordinal()) { + DEVICE_STATUS status = device.getStatus(); + + if (status == null) { + return null; + } + + if(element instanceof Disk && ((Disk)element).hasPartitions()) { + // disk has partitions. so don't show status image at disk level. + return null; + } + + switch (status) { + case INITIALIZED: + if(glusterDataModelManager.isDeviceUsed(device)) { + return guiHelper.getImage(IImageKeys.DISK_IN_USE_16x16); + } else { + return guiHelper.getImage(IImageKeys.DISK_AVAILABLE_16x16); + } + case IO_ERROR: + return guiHelper.getImage(IImageKeys.IO_ERROR_16x16); + case UNINITIALIZED: + return guiHelper.getImage(IImageKeys.DISK_UNINITIALIZED_16x16); + case INITIALIZING: + return guiHelper.getImage(IImageKeys.DISK_INITIALIZING_16x16); + default: + throw new GlusterRuntimeException("Invalid disk status [" + status + "]"); + } + } + + return null; + } + + private String getDeviceFreeSpace(Device device) { + if (device.hasErrors() || device.isUninitialized()) { + return "NA"; + } else { + return NumberUtil.formatNumber((device.getFreeSpace() / 1024)); + } + } + + private String getTotalDeviceSpace(Device device) { + if (device.hasErrors() || device.isUninitialized()) { + return "NA"; + } else { + return NumberUtil.formatNumber((device.getSpace() / 1024)); + } + } + + public String getColumnText(Object element, int columnIndex) { + Device device = (Device) element; + if (columnIndex == DEVICE_COLUMN_INDICES.DISK.ordinal()) { + // show value in "disk" column only if it's a disk + if (device instanceof Disk) { + return device.getName(); + } else { + return ""; + } + } + + if(element instanceof Disk && ((Disk)element).hasPartitions()) { + // disk has partitions. so don't show any other details + return ""; + } + + if (columnIndex == DEVICE_COLUMN_INDICES.FREE_SPACE.ordinal()) { + return "" + getDeviceFreeSpace(device); + } else if (columnIndex == DEVICE_COLUMN_INDICES.SPACE_IN_USE.ordinal()) { + return "" + getTotalDeviceSpace(device); + } else if (columnIndex == DEVICE_COLUMN_INDICES.PARTITION.ordinal()) { + if (device instanceof Partition) { + return device.getName(); + } else { + return ""; + } + } else if (columnIndex == DEVICE_COLUMN_INDICES.STATUS.ordinal()) { + if(device.isUninitialized()) { + return ""; + } + if(glusterDataModelManager.isDeviceUsed(device)) { + return "In Use"; + } else { + return device.getStatusStr(); + } + } else { + return ""; + } + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ServerTableLabelProvider.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ServerTableLabelProvider.java new file mode 100644 index 00000000..1afbddac --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/ServerTableLabelProvider.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 org.gluster.storage.management.console; + +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.ServersPage.SERVER_TABLE_COLUMN_INDICES; +import org.gluster.storage.management.core.model.Server; +import org.gluster.storage.management.core.utils.NumberUtil; + + +public class ServerTableLabelProvider extends TableLabelProviderAdapter { + private GUIHelper guiHelper = GUIHelper.getInstance(); + + @Override + public String getColumnText(Object element, int columnIndex) { + if (!(element instanceof Server)) { + return null; + } + + Server server = (Server) element; + return (columnIndex == SERVER_TABLE_COLUMN_INDICES.NAME.ordinal() ? server.getName() + : columnIndex == SERVER_TABLE_COLUMN_INDICES.IP_ADDRESSES.ordinal() ? server.getIpAddressesAsString() + : columnIndex == SERVER_TABLE_COLUMN_INDICES.NUM_OF_DISKS.ordinal() ? "" + + server.getNumOfDisks() : columnIndex == SERVER_TABLE_COLUMN_INDICES.TOTAL_DISK_SPACE + .ordinal() ? NumberUtil.formatNumber((server.getTotalDiskSpace() / 1024)) + // : columnIndex == SERVER_TABLE_COLUMN_INDICES.NUM_OF_CPUS.ordinal() ? "" + + // server.getNumOfCPUs() + // : columnIndex == SERVER_TABLE_COLUMN_INDICES.CPU_USAGE.ordinal() ? "" + server.getCpuUsage() + // : columnIndex == SERVER_TABLE_COLUMN_INDICES.TOTAL_MEMORY.ordinal() ? "" + + // server.getTotalMemory() + // : columnIndex == SERVER_TABLE_COLUMN_INDICES.MEMORY_IN_USE.ordinal() ? "" + + // server.getMemoryInUse() + // : columnIndex == SERVER_TABLE_COLUMN_INDICES.DISK_SPACE_IN_USE.ordinal() ? "" + + // server.getDiskSpaceInUse() + : "Invalid"); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/TableLabelProviderAdapter.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/TableLabelProviderAdapter.java new file mode 100644 index 00000000..622d3dfc --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/TableLabelProviderAdapter.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.swt.graphics.Image; + +public class TableLabelProviderAdapter implements ITableLabelProvider { + + @Override + public void addListener(ILabelProviderListener listener) { + // do nothing + + } + + @Override + public void dispose() { + // do nothing + } + + @Override + public boolean isLabelProperty(Object element, String property) { + return true; + } + + @Override + public void removeListener(ILabelProviderListener listener) { + // do nothing + } + + @Override + public Image getColumnImage(Object element, int columnIndex) { + return null; + } + + @Override + public String getColumnText(Object element, int columnIndex) { + return null; + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/TasksTableLabelProvider.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/TasksTableLabelProvider.java new file mode 100644 index 00000000..ad2a8a7d --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/TasksTableLabelProvider.java @@ -0,0 +1,71 @@ +/** + * TasksTableLabelProvider.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 org.gluster.storage.management.console; + +import org.eclipse.swt.graphics.Image; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.TasksPage.TASK_TABLE_COLUMN_INDICES; +import org.gluster.storage.management.core.model.Status; +import org.gluster.storage.management.core.model.TaskInfo; + + + +public class TasksTableLabelProvider extends TableLabelProviderAdapter { + private GUIHelper guiHelper = GUIHelper.getInstance(); + + @Override + public Image getColumnImage(Object element, int columnIndex) { + + if (!(element instanceof TaskInfo)) { + return null; + } + + TaskInfo taskInfo = (TaskInfo) element; + if (columnIndex == TASK_TABLE_COLUMN_INDICES.STATUS.ordinal()) { + int statusCode = taskInfo.getStatus().getCode(); + + switch (statusCode) { + case Status.STATUS_CODE_SUCCESS: + return guiHelper.getImage(IImageKeys.COMPLETED_TASK_16x16); + case Status.STATUS_CODE_PAUSE: + return guiHelper.getImage(IImageKeys.PAUSE_TASK_16x16); + case Status.STATUS_CODE_RUNNING: + return guiHelper.getImage(IImageKeys.RESUME_TASK_16x16); + case Status.STATUS_CODE_FAILURE: + return guiHelper.getImage(IImageKeys.STATUS_OFFLINE_16x16); + default: + break; + } + } + + return null; + } + + @Override + public String getColumnText(Object element, int columnIndex) { + if (!(element instanceof TaskInfo)) { + return null; + } + + TaskInfo taskInfo = (TaskInfo) element; + return (columnIndex == TASK_TABLE_COLUMN_INDICES.TASK.ordinal()) ? taskInfo.getDescription().trim() : taskInfo.getStatus().getMessage().trim(); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/VolumeLogTableLabelProvider.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/VolumeLogTableLabelProvider.java new file mode 100644 index 00000000..246bd7ca --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/VolumeLogTableLabelProvider.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + + +import org.gluster.storage.management.console.views.pages.VolumeLogsPage.LOG_TABLE_COLUMN_INDICES; +import org.gluster.storage.management.core.model.VolumeLogMessage; +import org.gluster.storage.management.core.utils.DateUtil; + + +public class VolumeLogTableLabelProvider extends TableLabelProviderAdapter { + @Override + public String getColumnText(Object element, int columnIndex) { + if (!(element instanceof VolumeLogMessage)) { + return null; + } + + VolumeLogMessage logMessage = (VolumeLogMessage) 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.BRICK.ordinal() ? logMessage.getBrick() + : columnIndex == LOG_TABLE_COLUMN_INDICES.SEVERITY.ordinal() ? "" + logMessage.getSeverity() + : columnIndex == LOG_TABLE_COLUMN_INDICES.MESSAGE.ordinal() ? logMessage.getMessage() : "Invalid"); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/VolumeOptionsContentProvider.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/VolumeOptionsContentProvider.java new file mode 100644 index 00000000..0cc0b536 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/VolumeOptionsContentProvider.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.gluster.storage.management.core.model.VolumeOptions; + + +/** + * @author root + * + */ +public class VolumeOptionsContentProvider implements IStructuredContentProvider { + + @Override + public void dispose() { + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + @Override + public Object[] getElements(Object inputElement) { + if (inputElement instanceof VolumeOptions) { + return ((VolumeOptions) inputElement).getOptions().toArray(); + } + return null; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/VolumeOptionsTableLabelProvider.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/VolumeOptionsTableLabelProvider.java new file mode 100644 index 00000000..250066bc --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/VolumeOptionsTableLabelProvider.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import java.util.Map.Entry; + +import org.gluster.storage.management.console.views.pages.VolumeOptionsPage.OPTIONS_TABLE_COLUMN_INDICES; +import org.gluster.storage.management.core.model.VolumeOption; + + +public class VolumeOptionsTableLabelProvider extends TableLabelProviderAdapter { + @Override + public String getColumnText(Object element, int columnIndex) { + if (!(element instanceof Entry)) { + return null; + } + + VolumeOption option = (VolumeOption)element; + return (columnIndex == OPTIONS_TABLE_COLUMN_INDICES.OPTION_KEY.ordinal() ? option.getKey() + : columnIndex == OPTIONS_TABLE_COLUMN_INDICES.OPTION_VALUE.ordinal() ? option.getValue() + : "Invalid"); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/VolumeTableLabelProvider.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/VolumeTableLabelProvider.java new file mode 100644 index 00000000..e19a40b1 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/VolumeTableLabelProvider.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console; + +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.swt.graphics.Image; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.VolumesPage.VOLUME_TABLE_COLUMN_INDICES; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.Volume.VOLUME_STATUS; + + +public class VolumeTableLabelProvider implements ITableLabelProvider { + private GUIHelper guiHelper = GUIHelper.getInstance(); + + @Override + public void addListener(ILabelProviderListener listener) { + } + + @Override + public void dispose() { + } + + @Override + public boolean isLabelProperty(Object element, String property) { + return false; + } + + @Override + public void removeListener(ILabelProviderListener listener) { + } + + @Override + public Image getColumnImage(Object element, int columnIndex) { + if (!(element instanceof Volume)) { + return null; + } + + Volume volume = (Volume) element; + if(columnIndex == VOLUME_TABLE_COLUMN_INDICES.VOLUME_STATUS.ordinal()) { + VOLUME_STATUS status = volume.getStatus(); + if(status == VOLUME_STATUS.ONLINE) { + return guiHelper.getImage(IImageKeys.STATUS_ONLINE_16x16); + } else { + return guiHelper.getImage(IImageKeys.STATUS_OFFLINE_16x16); + } + } + + return null; + } + + @Override + public String getColumnText(Object element, int columnIndex) { + if (!(element instanceof Volume)) { + return null; + } + + Volume volume = (Volume) element; + return (columnIndex == VOLUME_TABLE_COLUMN_INDICES.NAME.ordinal() ? volume.getName() + : columnIndex == VOLUME_TABLE_COLUMN_INDICES.VOLUME_TYPE.ordinal() ? volume.getVolumeTypeStr() + : columnIndex == VOLUME_TABLE_COLUMN_INDICES.TRANSPORT_TYPE.ordinal() ? volume.getTransportTypeStr() + : columnIndex == VOLUME_TABLE_COLUMN_INDICES.NUM_OF_BRICKS.ordinal() ? "" + volume.getNumOfBricks() + : columnIndex == VOLUME_TABLE_COLUMN_INDICES.VOLUME_STATUS.ordinal() ? volume.getStatusStr() : "Invalid"); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/AbstractActionDelegate.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/AbstractActionDelegate.java new file mode 100644 index 00000000..efff2a3d --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/AbstractActionDelegate.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.IWorkbenchWindowActionDelegate; +import org.eclipse.ui.internal.UIPlugin; +import org.gluster.storage.management.console.utils.GlusterLogger; +import org.gluster.storage.management.core.model.Entity; + + +/** + * All action delegates in the application should extend from this class. It provides common functionality of grabbing + * the Window object on initialization and extracting the selected entity in case of selection change on the navigation + * tree. + */ +@SuppressWarnings("restriction") +public abstract class AbstractActionDelegate implements IWorkbenchWindowActionDelegate { + protected IWorkbenchWindow window; + protected static final GlusterLogger logger = GlusterLogger.getInstance(); + + // the latest selected entity + protected Entity selectedEntity; + + @Override + public void run(final IAction action) { + try { + performAction(action); + } catch (final Exception e) { + final String actionDesc = action.getDescription(); + logger.error("Exception while running action [" + actionDesc + "]", e); + showErrorDialog(actionDesc, e.getMessage()); + } + } + + abstract protected void performAction(final IAction action); + + @Override + public void selectionChanged(IAction action, ISelection selection) { + if (selection instanceof StructuredSelection) { + Entity selectedEntity = (Entity) ((StructuredSelection) selection).getFirstElement(); + + if (this.selectedEntity == selectedEntity) { + // entity selection has not changed. do nothing. + return; + } + + if (selectedEntity != null) { + this.selectedEntity = selectedEntity; + } + } + } + + @Override + public void init(IWorkbenchWindow window) { + this.window = window; + } + + protected Shell getShell() { + return getWindow().getShell(); + } + + protected IWorkbenchWindow getWindow() { + return window == null ? UIPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow() : window; + } + + protected void showInfoDialog(final String title, final String message) { + MessageDialog.openInformation(getShell(), title, message); + } + + protected void showWarningDialog(final String title, final String message) { + MessageDialog.openWarning(getShell(), title, message); + } + + protected void showErrorDialog(final String title, final String message) { + MessageDialog.openError(getShell(), title, message); + } + + protected boolean showConfirmDialog(final String title, final String message) { + return MessageDialog.openQuestion(getShell(), title, message); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/AbstractMonitoredActionDelegate.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/AbstractMonitoredActionDelegate.java new file mode 100644 index 00000000..00972f9f --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/AbstractMonitoredActionDelegate.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import java.lang.reflect.InvocationTargetException; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.gluster.storage.management.console.ConsoleConstants; + + +/** + * Any action that can potentially run for a long time, and supports monitoring and progress dialog should extend from + * this class + */ +public abstract class AbstractMonitoredActionDelegate extends AbstractActionDelegate { + /* (non-Javadoc) + * @see org.gluster.storage.management.console.actions.AbstractActionDelegate#performAction(org.eclipse.jface.action.IAction) + */ + @Override + protected void performAction(final IAction action) { + try { + new ProgressMonitorDialog(getShell()).run(false, false, new IRunnableWithProgress() { + + @Override + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + performAction(action, monitor); + } + }); + } catch (Exception e) { + String errMsg = "Exception while performing action [" + action.getDescription() + "] : [" + e.getMessage() + "]"; + logger.error(errMsg, e); + showErrorDialog(ConsoleConstants.CONSOLE_TITLE, errMsg); + } + } + + abstract void performAction(IAction action, IProgressMonitor monitor); +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ActionConstants.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ActionConstants.java new file mode 100644 index 00000000..bde96570 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ActionConstants.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +public class ActionConstants { + public static final String ACTION_SET_CLUSTER = "org.gluster.storage.management.console.actionsets.gluster"; + public static final String ACTION_SET_VOLUMES = "org.gluster.storage.management.console.actionsets.volumes"; + public static final String ACTION_SET_VOLUME = "org.gluster.storage.management.console.actionsets.volume"; + public static final String ACTION_SET_DISKS = "org.gluster.storage.management.console.actionsets.disks"; + public static final String ACTION_SET_DISK = "org.gluster.storage.management.console.actionsets.disk"; + public static final String ACTION_SET_GLUSTER_SERVERS = "org.gluster.storage.management.console.actionsets.glusterservers"; + public static final String ACTION_SET_GLUSTER_SERVER = "org.gluster.storage.management.console.actionsets.glusterserver"; + public static final String ACTION_SET_DISCOVERED_SERVERS = "org.gluster.storage.management.console.actionsets.serversdiscovered"; + public static final String ACTION_SET_DISCOVERED_SERVER = "org.gluster.storage.management.console.actionsets.serverdiscovered"; + public static final String ACTION_SET_TASK = "org.gluster.storage.management.console.actionsets.task"; + public static final String ACTION_SET_EDIT = "org.gluster.storage.management.console.actionsets.edit"; + + public static final String COMMAND_CREATE_VOLUME = "org.gluster.storage.management.console.commands.CreateVolume"; + public static final String COMMAND_ADD_SERVER = "org.gluster.storage.management.console.commands.AddServer"; +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/AddBrickAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/AddBrickAction.java new file mode 100644 index 00000000..e67cf9ab --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/AddBrickAction.java @@ -0,0 +1,64 @@ +/** + * AddBrickAction.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 org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.wizard.WizardDialog; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.dialogs.AddBrickWizard; +import org.gluster.storage.management.core.model.Volume; + + +public class AddBrickAction extends AbstractActionDelegate { + private Volume volume; + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + + @Override + public void dispose() { + window = null; + } + + @Override + protected void performAction(IAction action) { + // TODO: open a dialog box + // MessageDialog.openInformation(getShell(), "Action captured", action.getDescription() + "\n" + + // volume.getName()); + AddBrickWizard wizard = new AddBrickWizard(volume); // Also add single page + + WizardDialog dialog = new WizardDialog(getShell(), wizard); + dialog.create(); + dialog.getShell().setSize(1024, 600); + dialog.open(); + } + + @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/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/AddServerAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/AddServerAction.java new file mode 100644 index 00000000..2a622384 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/AddServerAction.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import java.net.URI; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.action.IAction; +import org.gluster.storage.management.client.GlusterServersClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.dialogs.ServerAdditionDialog; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.Server; + + +public class AddServerAction extends AbstractMonitoredActionDelegate { + private GUIHelper guiHelper = GUIHelper.getInstance(); + + @Override + protected void performAction(final IAction action, IProgressMonitor monitor) { + GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + GlusterServersClient glusterServersClient = new GlusterServersClient(); + + Set<Server> selectedServers = GUIHelper.getInstance().getSelectedEntities(getWindow(), Server.class); + Set<Server> successServers = new HashSet<Server>(); + Set<Server> partSuccessServers = new HashSet<Server>(); + String errMsg = ""; + String partErrMsg = ""; + + if (selectedServers.isEmpty()) { + monitor.beginTask("Starting Manual Server Addition", 1); + addServerManually(); + monitor.worked(1); + monitor.done(); + return; + } + + monitor.beginTask("Adding Selected Servers...", selectedServers.size()); + for (Server server : selectedServers) { + if(monitor.isCanceled()) { + break; + } + + monitor.setTaskName("Adding server [" + server.getName() + "]..."); + + try { + URI newServerURI = glusterServersClient.addServer(server.getName()); + modelManager.addGlusterServer(glusterServersClient.getGlusterServer(newServerURI)); + successServers.add(server); + } catch (Exception e) { + if (!errMsg.isEmpty()) { + errMsg += CoreConstants.NEWLINE; + } + errMsg += "Server " + server.getName() + ". Error: [" + e.getMessage() + "]"; + } + monitor.worked(1); + } + monitor.done(); + + showStatusMessage(action.getDescription(), selectedServers, successServers, partSuccessServers, errMsg, + partErrMsg); + } + + private void addServerManually() { + try { + // To open a dialog for server addition + ServerAdditionDialog dialog = new ServerAdditionDialog(getShell()); + dialog.open(); + } catch (Exception e) { + logger.error("Error in Manual server addition", e); + showErrorDialog("Add server", "Add server failed! [" + e.getMessage() + "]"); + } + } + + private void showStatusMessage(String dialogTitle, Set<Server> selectedServers, Set<Server> successServers, + Set<Server> partSuccessServers, String errMsg, String partErrMsg) { + if (successServers.size() == selectedServers.size()) { + if (selectedServers.size() == 1) { + showInfoDialog(dialogTitle, "Server [" + selectedServers.iterator().next() + "] added successfully!"); + } else { + showInfoDialog(dialogTitle, "Following servers added successfully!" + CoreConstants.NEWLINE + + selectedServers); + } + return; + } + + String finalMsg = ""; + if (successServers.size() == 0 && partSuccessServers.size() == 0) { + finalMsg = "Server Addition Failed! Error(s):" + CoreConstants.NEWLINE + errMsg; + } else { + finalMsg = (successServers.isEmpty() ? "" : "Following servers added successfully : " + + CoreConstants.NEWLINE + successServers + CoreConstants.NEWLINE) + + (partSuccessServers.isEmpty() ? "" : "Following servers were added to cluster, but with some errors: " + + CoreConstants.NEWLINE + partErrMsg + CoreConstants.NEWLINE) + + (errMsg.isEmpty() ? "" : CoreConstants.NEWLINE + + "Following errors occurred on other selected servers: " + CoreConstants.NEWLINE + errMsg); + } + showErrorDialog(dialogTitle, finalMsg); + } + + @Override + public void dispose() { + System.out.println("Disposing [" + this.getClass().getSimpleName() + "]"); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ChangePasswordAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ChangePasswordAction.java new file mode 100644 index 00000000..07d59332 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ChangePasswordAction.java @@ -0,0 +1,23 @@ +package org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.gluster.storage.management.console.dialogs.ChangePasswordDialog; + + +public class ChangePasswordAction extends AbstractActionDelegate { + + @Override + protected void performAction(IAction action) { + try { + // To open a dialog for change password + ChangePasswordDialog dialog = new ChangePasswordDialog(getShell()); + dialog.open(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void dispose() { + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ClearTaskAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ClearTaskAction.java new file mode 100644 index 00000000..fcdb67ee --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ClearTaskAction.java @@ -0,0 +1,46 @@ +package org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.gluster.storage.management.client.TasksClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.core.model.Status; +import org.gluster.storage.management.core.model.TaskInfo; + + +public class ClearTaskAction extends AbstractActionDelegate { + private TaskInfo taskInfo; + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + + @Override + protected void performAction(final IAction action) { + final String actionDesc = action.getDescription(); + + try { + new TasksClient().deleteTask(taskInfo.getName()); // taskId + modelManager.removeTask(taskInfo); + action.setEnabled(false); // TODO disable other task buttons + } catch (Exception e) { + showErrorDialog(actionDesc, + "Task [" + taskInfo.getName() + "] could not be cleared! Error: [" + e.getMessage() + "]"); + } + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + action.setEnabled(false); + if (selectedEntity instanceof TaskInfo) { + taskInfo = (TaskInfo) selectedEntity; + action.setEnabled(taskInfo.getStatus().getCode() == Status.STATUS_CODE_SUCCESS + || taskInfo.getStatus().getCode() == Status.STATUS_CODE_FAILURE); + } else { + action.setEnabled(false); + } + } + + @Override + public void dispose() { + + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/CommitTaskAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/CommitTaskAction.java new file mode 100644 index 00000000..3fb59f91 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/CommitTaskAction.java @@ -0,0 +1,72 @@ +package org.gluster.storage.management.console.actions; + + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.widgets.Display; +import org.gluster.storage.management.client.TasksClient; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.core.model.Status; +import org.gluster.storage.management.core.model.TaskInfo; +import org.gluster.storage.management.core.model.TaskStatus; +import org.gluster.storage.management.core.model.Volume; + + +public class CommitTaskAction extends AbstractActionDelegate { + private TaskInfo taskInfo; + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + + @Override + protected void performAction(final IAction action) { + final String actionDesc = action.getDescription(); + try { + new TasksClient().commitTask(taskInfo.getName()); + taskInfo.setStatus(new TaskStatus(new Status(Status.STATUS_CODE_SUCCESS, "Committed"))); + modelManager.removeTask(taskInfo); + showInfoDialog(actionDesc, "Commit successful"); + } catch (Exception e) { + showErrorDialog(actionDesc, + "Task [" + taskInfo.getName() + "] could not be Committed! Error: [" + e.getMessage() + "]"); + return; // Prevent to update model + } + + BusyIndicator.showWhile(Display.getDefault(), new Runnable() { + @Override + public void run() { + try { + String volumeName = taskInfo.getReference().split("#")[0]; //Extract volume name from reference + Volume oldVolume = modelManager.getModel().getCluster().getVolume(volumeName); + Volume newVolume = (new VolumesClient()).getVolume(volumeName); + + modelManager.volumeChanged(oldVolume, newVolume); + } catch (Exception e) { + String errMsg = "Volume brick update failed! [" + e.getMessage() + "]"; + logger.error(errMsg, e); + showInfoDialog(actionDesc, errMsg); + } + } + }); + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + action.setEnabled(false); + if (selectedEntity instanceof TaskInfo) { + taskInfo = (TaskInfo) selectedEntity; + action.setEnabled(taskInfo.getCommitSupported() + && taskInfo.getStatus().getCode() == Status.STATUS_CODE_COMMIT_PENDING); + } + } + + public void updateVolume(String volumeName) { + + } + + @Override + public void dispose() { + + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/CreateVolumeAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/CreateVolumeAction.java new file mode 100644 index 00000000..55d10eda --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/CreateVolumeAction.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 org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.gluster.storage.management.console.dialogs.CreateVolumeWizard; + + +public class CreateVolumeAction extends AbstractActionDelegate { + @Override + protected void performAction(IAction action) { + CreateVolumeWizard wizard = new CreateVolumeWizard(); + + WizardDialog dialog = new WizardDialog(getShell(), wizard) { + @Override + protected Button createButton(Composite parent, int id, String label, boolean defaultButton) { + Button button = super.createButton(parent, id, label, defaultButton); + if (id == IDialogConstants.FINISH_ID) { + button.setText("&Create"); + } + return button; + } + }; + dialog.create(); + dialog.getShell().setSize(510, 620); + dialog.open(); + } + + @Override + public void dispose() { + window = null; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/DeleteVolumeAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/DeleteVolumeAction.java new file mode 100644 index 00000000..fb9e4cc2 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/DeleteVolumeAction.java @@ -0,0 +1,210 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ISelection; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.Volume.VOLUME_STATUS; + + +public class DeleteVolumeAction extends AbstractMonitoredActionDelegate { + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + private List<Volume> selectedVolumes = new ArrayList<Volume>(); + private List<String> selectedVolumeNames = new ArrayList<String>(); + private List<String> onlineVolumeNames = new ArrayList<String>(); + private List<String> deletedVolumeNames = new ArrayList<String>(); + private List<Volume> failedVolumes = new ArrayList<Volume>(); + + @Override + protected void performAction(final IAction action, IProgressMonitor monitor) { + final String actionDesc = action.getDescription(); + + collectVolumeNames(); + String warningMessage; + List<String> cifsVolumes = GlusterDataModelManager.getInstance().getCifsEnabledVolumeNames(selectedVolumes); + List<String> offlineServers = GlusterDataModelManager.getInstance().getOfflineServers(); + // One or more servers are offline, Show warning if cifs is enabled + if (cifsVolumes != null && cifsVolumes.size() > 0 && offlineServers != null && offlineServers.size() > 0) { + Integer userAction = new MessageDialog(getShell(), "CIFS configuration", GUIHelper.getInstance().getImage( + IImageKeys.VOLUME_16x16), + "Performing CIFS updates when one or more servers are offline can trigger " + + "inconsistent behavior for CIFS accesses in the cluster." + CoreConstants.NEWLINE + + "Are you sure you want to continue?", MessageDialog.QUESTION, + new String[] { "No", "Yes" }, -1).open(); + if (userAction != 1) { + return; // Do not delete volume services + } + } + + if (onlineVolumeNames.size() > 0) { // Getting confirmation for stop and delete + warningMessage = "Following volume(s) " + onlineVolumeNames + " are online, " + CoreConstants.NEWLINE + + "Are you sure to continue?" + CoreConstants.NEWLINE + selectedVolumeNames; + } else { + warningMessage = "Are you sure to delete the volumes " + selectedVolumeNames + " ?"; + } + + final Integer deleteOption = new MessageDialog(getShell(), "Delete Volume", GUIHelper.getInstance() + .getImage(IImageKeys.VOLUME_16x16), warningMessage, MessageDialog.QUESTION, new String[] { "Cancel", + "Delete volume and data", "Delete volume, keep data" }, -1).open(); + if (deleteOption <= 0) { // By Cancel button(0) or Escape key(-1) + return; + } + + String errorMessage = deleteVolumes(selectedVolumes, deleteOption, monitor); + + // Display the success or failure info + if (deletedVolumeNames.size() == 0) { // No volume(s) deleted successfully + showErrorDialog(actionDesc, "Volume(s) could not be deleted! " + CoreConstants.NEWLINE + errorMessage); + } else { + String info = "Volume(s) " + deletedVolumeNames + " deleted successfully!"; + if (!failedVolumes.isEmpty()) { + info += CoreConstants.NEWLINE + CoreConstants.NEWLINE + "Volumes " + failedVolumes + + " could not be deleted!" + CoreConstants.NEWLINE + errorMessage; + } + + if (selectedVolumes.size() == deletedVolumeNames.size()) { + showInfoDialog(actionDesc, info); + } else { + showWarningDialog(actionDesc, info); + } + } + } + + private String deleteVolumes(List<Volume> volumes, final Integer deleteOption, IProgressMonitor monitor) { + deletedVolumeNames.clear(); + failedVolumes.clear(); + VolumesClient vc = new VolumesClient(); + boolean confirmDeleteDir = (deleteOption == 1) ? true : false; + + String errorMessage = ""; + + // To calculate the total work we need to sum volumes size + online volumes (because we treat stop and delete as + // separate steps) + List<Volume> onlineVolumes = getOnlineVolumes(volumes); + monitor.beginTask("Deleting Selected Volumes...", volumes.size() + onlineVolumes.size()); + + // Deletion of a volume results in changes to the model, and ultimately updates the "selectedVolumes" list, + // over which we are iterating, thus resulting in ConcurrentModificationException. To avoid this, we iterate + // over an array obtained from the list. + for (Volume volume : volumes.toArray(new Volume[0])) { + if (volume.getStatus() == VOLUME_STATUS.ONLINE) { // stop if online volume + monitor.setTaskName("Stopping volume [" + volume.getName() + "]"); + try { + vc.stopVolume(volume.getName(), false); + } catch (Exception e1) { + // try again with force = true + try { + vc.stopVolume(volume.getName(), true); + } catch(Exception e2) { + // force stop also failed. + // Mark as deletion failed, append error message. + errorMessage += CoreConstants.NEWLINE + "Stop [" + volume.getName() + "] : [" + e2.getMessage() + + "]"; + failedVolumes.add(volume); + // since we are not going to perform delete on this volume, + // mark the deletion task as worked + monitor.worked(1); + + // continue to next volume without trying to delete this one + continue; + } + } finally { + // worked for "stop" operation + monitor.worked(1); + } + } + + monitor.setTaskName("Deleting volume [" + volume.getName() + "]"); + try { + vc.deleteVolume(volume.getName(), confirmDeleteDir); + modelManager.deleteVolume(volume); + deletedVolumeNames.add(volume.getName()); + } catch (Exception e) { + // Volume delete succeeded and post delete operation (directory cleanup, CIFS etc) may fail + if (vc.volumeExists(volume.getName())) { + errorMessage += CoreConstants.NEWLINE + "Delete [" + volume.getName() + "] : [" + e.getMessage() + "]"; + failedVolumes.add(volume); + } else { + errorMessage += CoreConstants.NEWLINE + "Volume [" + volume.getName() + + "] deleted, but following error occured: [" + e.getMessage() + "]"; + modelManager.deleteVolume(volume); + deletedVolumeNames.add(volume.getName()); + } + } finally { + monitor.worked(1); + } + } + monitor.done(); + return errorMessage; + } + + private List<Volume> getOnlineVolumes(List<Volume> volumes) { + List<Volume> onlineVolumes = new ArrayList<Volume>(); + for (Volume volume : volumes) { + if (volume.getStatus() == VOLUME_STATUS.ONLINE) { + onlineVolumes.add(volume); + } + } + return onlineVolumes; + } + + private void collectVolumeNames() { + selectedVolumeNames.clear(); + onlineVolumeNames.clear(); + for (Volume volume : selectedVolumes) { + selectedVolumeNames.add(volume.getName()); + if (volume.getStatus() == VOLUME_STATUS.ONLINE) { + onlineVolumeNames.add(volume.getName()); + } + } + } + + @Override + public void dispose() { + System.out.println("Disposing [" + this.getClass().getSimpleName() + "]"); + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + Set<Volume> selectedVolumeSet = GUIHelper.getInstance().getSelectedEntities(getWindow(), Volume.class); + selectedVolumes.clear(); + if (selectedVolumeSet == null || selectedVolumeSet.isEmpty()) { + super.selectionChanged(action, selection); + if (selectedEntity instanceof Volume) { + selectedVolumes.add((Volume) selectedEntity); + } + } else { + selectedVolumes.addAll(selectedVolumeSet); //TODO reverse the collection to maintain the selected order + } + + action.setEnabled( (selectedVolumes.size() > 0) ); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/DownloadVolumeLogsAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/DownloadVolumeLogsAction.java new file mode 100644 index 00000000..b67e0b2e --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/DownloadVolumeLogsAction.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.FileDialog; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.model.Volume; + + +/** + * + */ +public class DownloadVolumeLogsAction extends AbstractActionDelegate { + private GUIHelper guiHelper = GUIHelper.getInstance(); + + @Override + public void dispose() { + } + + @Override + protected void performAction(IAction action) { + final VolumesClient client = new VolumesClient(); + Volume volume = guiHelper.getSelectedEntity(getWindow(), Volume.class); + + FileDialog dialog = new FileDialog(getShell(), SWT.SAVE); + dialog.setFilterNames(new String[] { "GZipped Tar (*.tar.gz)" }); + dialog.setFilterExtensions(new String[] { "*.tar.gz" }); + String filePath = dialog.open(); + + if (filePath == null) { + return; + } + + String title = "Download Volume Logs [" + volume.getName() + "]"; + try { + client.downloadLogs(volume.getName(), filePath); + showInfoDialog(title, "Volume logs downloaded successfully to [" + filePath + "]"); + } catch (Exception e) { + showErrorDialog(title, e.getMessage()); + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/EditVolumeAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/EditVolumeAction.java new file mode 100644 index 00000000..efb7b780 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/EditVolumeAction.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; + +public class EditVolumeAction extends AbstractActionDelegate { + @Override + protected void performAction(IAction action) { + System.out.println("Running [" + this.getClass().getSimpleName() + "]"); + } + + @Override + public void dispose() { + System.out.println("Disposing [" + this.getClass().getSimpleName() + "]"); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ExportSshKeysAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ExportSshKeysAction.java new file mode 100644 index 00000000..a4b57800 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ExportSshKeysAction.java @@ -0,0 +1,61 @@ +/** + * ExportSshKeysAction.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 org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.FileDialog; +import org.gluster.storage.management.client.KeysClient; + + +/** + * @author root + * + */ +public class ExportSshKeysAction extends AbstractActionDelegate { + + @Override + protected void performAction(IAction action) { + final KeysClient client = new KeysClient(); + FileDialog dialog = new FileDialog(getShell(), SWT.SAVE); + dialog.setFilterNames(new String[] { "Tar (*.tar)" }); + dialog.setFilterExtensions(new String[] { "*.tar" }); + String filePath = dialog.open(); + + if (filePath == null) { + return; + } + + String title = "Export SSH Keys"; + try { + client.exportSshKeys(filePath); + showInfoDialog(title, "SSH keys exported successfully to [" + filePath + "]"); + } catch (Exception e) { + showErrorDialog(title, e.getMessage()); + } + } + + + @Override + public void dispose() { + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ForceStartVolumeAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ForceStartVolumeAction.java new file mode 100644 index 00000000..3c57256a --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ForceStartVolumeAction.java @@ -0,0 +1,71 @@ +package org.gluster.storage.management.console.actions; + +import java.util.Set; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IWorkbenchPart; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.VolumeBricksView; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.Brick.BRICK_STATUS; +import org.gluster.storage.management.core.utils.StringUtil; + + +public class ForceStartVolumeAction extends AbstractActionDelegate { + + private Volume volume; + private GUIHelper guiHelper = GUIHelper.getInstance(); + private Set<Brick> bricks; + + @Override + public void dispose() { + + } + + @Override + protected void performAction(IAction action) { + // volume brick service will be started, do you want to continue? + final String actionDesc = action.getDescription(); + boolean confirmed = showConfirmDialog( + actionDesc, + "The offline Bricks [" + StringUtil.collectionToString(bricks, ", ") + "] of Volume [" + + volume.getName() + "] will be started. Are you sure you want to continue?"); + if (!confirmed) { + return; + } + try { + new VolumesClient().startVolume(volume.getName(), true); + showInfoDialog(actionDesc, "Offline Bricks of Volume [" + volume.getName() + "] started successfully!"); + } catch (Exception e) { + showErrorDialog(actionDesc, e.getMessage()); + } + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + action.setEnabled(false); + volume = guiHelper.getSelectedEntity(window, Volume.class); + if (volume != null) { + // a volume is selected on navigation tree. Let's check if the currently open view is volume bricks view + IWorkbenchPart view = guiHelper.getActiveView(); + if (view instanceof VolumeBricksView) { + // volume bricks view is open. check if any offline brick is selected + bricks = GUIHelper.getInstance().getSelectedEntities(getWindow(), Brick.class); + for (Brick brick : bricks) { + if (brick.getStatus() == BRICK_STATUS.OFFLINE) { + action.setEnabled(true); + } else { + // if any one of the selected brick is online, the disable the button + action.setEnabled(false); + break; + } + } + } + } + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ImportSshKeysAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ImportSshKeysAction.java new file mode 100644 index 00000000..083cf193 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ImportSshKeysAction.java @@ -0,0 +1,44 @@ +package org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.FileDialog; +import org.gluster.storage.management.client.KeysClient; + + +public class ImportSshKeysAction extends AbstractActionDelegate { + + @Override + protected void performAction(IAction action) { + final KeysClient client = new KeysClient(); + + Display.getDefault().asyncExec(new Runnable() { + + @Override + public void run() { + FileDialog dialog = new FileDialog(getShell(), SWT.OPEN); + dialog.setText("Open"); + dialog.setFilterNames(new String[] { "ssh-keys (*.tar)" }); + dialog.setFilterExtensions(new String[] { "*.tar" }); + + String selectedFile = dialog.open(); + if (selectedFile == null) { + return; + } + + String title = "Import SSH Keys"; + try { + client.importSshKeys(selectedFile); + showInfoDialog(title, "SSH keys imported successfully!"); + } catch (Exception e) { + showErrorDialog(title, e.getMessage()); + } + } + }); + } + + @Override + public void dispose() { + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/MigrateBrickAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/MigrateBrickAction.java new file mode 100644 index 00000000..01cc837c --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/MigrateBrickAction.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import java.util.Set; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.wizard.WizardDialog; +import org.gluster.storage.management.console.dialogs.MigrateBrickWizard; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Volume; + + +public class MigrateBrickAction extends AbstractActionDelegate { + private Volume volume; + private Brick brick; + + @Override + protected void performAction(IAction action) { + MigrateBrickWizard wizard = new MigrateBrickWizard(volume, brick); + + WizardDialog dialog = new WizardDialog(window.getShell(), wizard); + dialog.create(); + dialog.getShell().setSize(1024, 600); + dialog.open(); + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + Set<Brick> bricks; + if (selectedEntity instanceof Volume) { + volume = (Volume) selectedEntity; + } + + action.setEnabled(false); + if (selectedEntity instanceof Brick) { + bricks = GUIHelper.getInstance().getSelectedEntities(getWindow(), Brick.class); + if ( bricks.iterator().hasNext()) { + brick = bricks.iterator().next(); + } else { + brick = null; + } + action.setEnabled(brick != null); + } + } + + @Override + public void dispose() { + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/MigrateVolumeAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/MigrateVolumeAction.java new file mode 100644 index 00000000..ed4b47f6 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/MigrateVolumeAction.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; + +public class MigrateVolumeAction extends AbstractActionDelegate { + @Override + protected void performAction(IAction action) { + System.out.println("Running [" + this.getClass().getSimpleName() + "]"); + } + + @Override + public void dispose() { + System.out.println("Disposing [" + this.getClass().getSimpleName() + "]"); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/PauseTaskAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/PauseTaskAction.java new file mode 100644 index 00000000..675095ee --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/PauseTaskAction.java @@ -0,0 +1,67 @@ +/** + * PauseTaskAction.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 org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.gluster.storage.management.client.TasksClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.core.model.Status; +import org.gluster.storage.management.core.model.TaskInfo; +import org.gluster.storage.management.core.model.TaskStatus; + + + +public class PauseTaskAction extends AbstractActionDelegate { + private TaskInfo taskInfo; + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + + @Override + protected void performAction(final IAction action) { + final String actionDesc = action.getDescription(); + + try { + new TasksClient().pauseTask(taskInfo.getName()); + taskInfo.setStatus(new TaskStatus(new Status(Status.STATUS_CODE_PAUSE, "Paused"))); + modelManager.updateTask(taskInfo); + } catch (Exception e) { + showErrorDialog(actionDesc, + "Task [" + taskInfo.getDescription() + "] could not be Paused! Error: [" + e.getMessage() + "]"); + } + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + action.setEnabled(false); + if (selectedEntity instanceof TaskInfo) { + taskInfo = (TaskInfo) selectedEntity; + action.setEnabled(taskInfo.getPauseSupported() && taskInfo.getStatus().getCode() == Status.STATUS_CODE_RUNNING); + } + } + + @Override + public void dispose() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/PreferencesAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/PreferencesAction.java new file mode 100644 index 00000000..11db53e0 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/PreferencesAction.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.ui.actions.ActionFactory; + +public class PreferencesAction extends AbstractActionDelegate { + + @Override + public void dispose() { + + } + + @Override + protected void performAction(IAction action) { + ActionFactory.PREFERENCES.create(window).run(); + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/RebalanceVolumeAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/RebalanceVolumeAction.java new file mode 100644 index 00000000..beae73c6 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/RebalanceVolumeAction.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import java.net.URI; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.gluster.storage.management.client.TasksClient; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.model.Status; +import org.gluster.storage.management.core.model.TaskInfo; +import org.gluster.storage.management.core.model.Volume; + + +public class RebalanceVolumeAction extends AbstractActionDelegate { + private Volume volume; + private GUIHelper guiHelper = GUIHelper.getInstance(); + + @Override + protected void performAction(final IAction action) { + final String actionDesc = action.getDescription(); + try { + TaskInfo existingTaskInfo = GlusterDataModelManager.getInstance().getTaskByReference(volume.getName()); + if (existingTaskInfo != null && existingTaskInfo.getStatus().getCode() != Status.STATUS_CODE_SUCCESS + && existingTaskInfo.getStatus().getCode() != Status.STATUS_CODE_FAILURE) { + showInfoDialog(actionDesc, "Volume [" + volume.getName() + + "] rebalance is already in progress! Try later."); + return; + } + + URI uri = new VolumesClient().rebalanceStart(volume.getName(), false, false, false); + // Add the task to model + TasksClient taskClient = new TasksClient(); + TaskInfo taskInfo = taskClient.getTaskInfo(uri); + if (taskInfo != null) { + GlusterDataModelManager.getInstance().addTask(taskInfo); + } + showInfoDialog(actionDesc, "Volume [" + volume.getName() + "] rebalance started successfully!"); + guiHelper.showTaskView(); + } catch (Exception e) { + showErrorDialog(actionDesc, "Volume rebalance could not be started on [" + volume.getName() + "]! Error: [" + + e.getMessage() + "]"); + } + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + + Volume selectedVolume = guiHelper.getSelectedEntity(getWindow(), Volume.class); + if (selectedVolume != null) { + volume = selectedVolume; + action.setEnabled(true); + } else { + action.setEnabled(false); + } + } + + @Override + public void dispose() { + System.out.println("Disposing [" + this.getClass().getSimpleName() + "]"); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/RefreshDataAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/RefreshDataAction.java new file mode 100644 index 00000000..5e791768 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/RefreshDataAction.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.gluster.storage.management.console.jobs.DataSyncJob; + + +/** + * + */ +public class RefreshDataAction extends AbstractActionDelegate { + /* (non-Javadoc) + * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#dispose() + */ + @Override + public void dispose() { + } + + /* (non-Javadoc) + * @see org.gluster.storage.management.console.actions.AbstractActionDelegate#performAction(org.eclipse.jface.action.IAction) + */ + @Override + protected void performAction(IAction action) { + new DataSyncJob("Retrieving Management Info").schedule(); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/RemoveBrickAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/RemoveBrickAction.java new file mode 100644 index 00000000..658394ed --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/RemoveBrickAction.java @@ -0,0 +1,91 @@ +package org.gluster.storage.management.console.actions; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IWorkbenchPart; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.VolumeBricksView; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.utils.StringUtil; + + +public class RemoveBrickAction extends AbstractActionDelegate { + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + private GUIHelper guiHelper = GUIHelper.getInstance(); + private Set<Brick> bricks; + private Volume volume; + boolean confirmDelete = false; + + @Override + protected void performAction(final IAction action) { + final String actionDesc = action.getDescription(); + List<String> brickList = getBrickList(bricks); + Integer deleteOption = new MessageDialog(getShell(), "Remove Bricks(s)", GUIHelper.getInstance().getImage( + IImageKeys.VOLUME_16x16), "Are you sure you want to remove following bricks from volume [" + volume.getName() + + "] ? " + CoreConstants.NEWLINE + StringUtil.collectionToString(brickList, ", "), MessageDialog.QUESTION, new String[] { + "Cancel", "Remove bricks, delete data", "Remove bricks, keep data" }, -1).open(); + if (deleteOption <= 0) { // By Cancel button(0) or Escape key(-1) + return; + } + + if (deleteOption == 1) { + confirmDelete = true; + } + BusyIndicator.showWhile(Display.getDefault(), new Runnable() { + public void run() { + VolumesClient client = new VolumesClient(); + try { + client.removeBricks(volume.getName(), bricks, confirmDelete); + // Update model with removed bricks in the volume + modelManager.removeBricks(volume, bricks); + + showInfoDialog(actionDesc, "Volume [" + volume.getName() + "] bricks(s) removed successfully!"); + } catch (Exception e) { + showErrorDialog(actionDesc, "Volume [" + volume.getName() + + "] bricks(s) could not be removed! Error: [" + e.getMessage() + "]"); + } + } + }); + } + + @Override + public void dispose() { + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + + action.setEnabled(false); + volume = guiHelper.getSelectedEntity(window, Volume.class); + if (volume != null) { + // a volume is selected on navigation tree. Let's check if the currently open view is volume bricks view + IWorkbenchPart view = guiHelper.getActiveView(); + if (view instanceof VolumeBricksView) { + // volume bricks view is open. check if any brick is selected + bricks = GUIHelper.getInstance().getSelectedEntities(getWindow(), Brick.class); + action.setEnabled(bricks.size() > 0); + } + } + } + + private List<String> getBrickList(Set<Brick> bricks) { + List<String> brickList = new ArrayList<String>(); + for (Brick brick : bricks) { + brickList.add(brick.getServerName() + ":" + brick.getBrickDirectory()); + } + return brickList; + } +}
\ No newline at end of file diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/RemoveServerAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/RemoveServerAction.java new file mode 100644 index 00000000..94f69be8 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/RemoveServerAction.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.gluster.storage.management.client.GlusterServersClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.utils.GlusterLogger; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.GlusterServer; + + +public class RemoveServerAction extends AbstractMonitoredActionDelegate { + private static final GlusterLogger logger = GlusterLogger.getInstance(); + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + private GUIHelper guiHelper = GUIHelper.getInstance(); + + @Override + protected void performAction(final IAction action, IProgressMonitor monitor) { + final String actionDesc = action.getDescription(); + + Set<GlusterServer> selectedServers = guiHelper.getSelectedEntities(getWindow(), GlusterServer.class); + + if (!validate(action, selectedServers)) { + return; + } + + boolean confirmed = showConfirmDialog(actionDesc, "Are you sure you want to remove the server(s) " + + selectedServers + " ?"); + if (!confirmed) { + return; + } + + Set<GlusterServer> successServers = new HashSet<GlusterServer>(); + String errMsg = ""; + monitor.beginTask("Removing Selected Servers...", selectedServers.size()); + for (GlusterServer server : selectedServers) { + monitor.setTaskName("Removing server [" + server.getName() + "]..."); + + GlusterServersClient client = new GlusterServersClient(); + try { + client.removeServer(server.getName()); + GlusterServer glusterServer = server; + modelManager.removeGlusterServer(glusterServer); + successServers.add(server); + } catch (Exception e) { + if (!serverExists(server.getName())) { + modelManager.removeGlusterServer(server); + } + errMsg += "[" + server.getName() + "] : " + e.getMessage() + CoreConstants.NEWLINE; + } + monitor.worked(1); + } + monitor.done(); + showStatusMessage(action.getDescription(), selectedServers, successServers, errMsg); + } + + private Boolean serverExists(String serverName) { + try { + GlusterServersClient client = new GlusterServersClient(); + GlusterServer server = client.getGlusterServer(serverName); + return (server != null && server.getName().length() > 0); + } catch (Exception e) { + logger.error("Error while getting server info", e); + return false; + } + } + + private void showStatusMessage(String dialogTitle, Set<GlusterServer> selectedServers, Set<GlusterServer> successServers, + String errMsg) { + if (successServers.size() == selectedServers.size()) { + if(selectedServers.size() == 1) { + showInfoDialog(dialogTitle, "Server [" + selectedServers.iterator().next() + "] removed successfully!"); + } else { + showInfoDialog(dialogTitle, "Following servers removed successfully: " + CoreConstants.NEWLINE + + selectedServers + CoreConstants.NEWLINE + errMsg); + } + return; + } + + if (successServers.size() == 0) { + errMsg = "Server Removal Failed! Error(s):" + CoreConstants.NEWLINE + errMsg; + } else { + errMsg = "Following servers removed successfully : " + CoreConstants.NEWLINE + successServers + + CoreConstants.NEWLINE + "Following errors occurred on other selected servers: " + + CoreConstants.NEWLINE + errMsg; + } + showErrorDialog(dialogTitle, errMsg); + } + + private boolean validate(IAction action, Set<GlusterServer> selectedServers) { + Map<GlusterServer, List<String>> usedServers = new HashMap<GlusterServer, List<String>>(); + for (GlusterServer server : selectedServers) { + List<String> configuredVolumes = modelManager.getVolumesOfServer(server.getName()); + + if (configuredVolumes.size() > 0) { + usedServers.put(server, configuredVolumes); + } + } + + if (usedServers.size() > 0) { + if (usedServers.size() == 1) { + showErrorDialog(action.getDescription(), "Server [" + usedServers.keySet().iterator().next() + + "] cannot be removed as it is being used by volume(s): " + CoreConstants.NEWLINE + + usedServers.values().iterator().next() ); + } else { + String serverList = ""; + for (Entry<GlusterServer, List<String>> entry : usedServers.entrySet()) { + serverList += entry.getKey() + " -> " + entry.getValue() + CoreConstants.NEWLINE; + } + showErrorDialog(action.getDescription(), + "Following servers cannot be removed as they are being used by volume(s): " + + CoreConstants.NEWLINE + serverList ); + } + return false; + } + return true; + } + + public void dispose() { + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + Set<GlusterServer> selectedServers = GUIHelper.getInstance().getSelectedEntities(getWindow(), + GlusterServer.class); + if(selectedServers == null || selectedServers.isEmpty()) { + action.setEnabled(false); + } else { + action.setEnabled(true); + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ResetVolumeOptionsAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ResetVolumeOptionsAction.java new file mode 100644 index 00000000..ef21aac7 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ResetVolumeOptionsAction.java @@ -0,0 +1,62 @@ +package org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.widgets.Display; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.model.Volume; + + +public class ResetVolumeOptionsAction extends AbstractActionDelegate { + private Volume volume; + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + + @Override + public void dispose() { + } + + @Override + protected void performAction(final IAction action) { + Display.getDefault().asyncExec(new Runnable() { + + @Override + public void run() { + 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; + } + + try { + new VolumesClient().resetVolumeOptions(volume.getName()); + showInfoDialog(actionDesc, "Volume options for [" + volume.getName() + "] reset successfully!"); + modelManager.resetVolumeOptions(volume); + } catch (Exception e) { + showErrorDialog(actionDesc, "Volume options for [" + volume.getName() + + "] could not be reset! Error: [" + e.getMessage() + "]"); + } + } + }); + } + + /* + * (non-Javadoc) + * + * @see + * org.gluster.storage.management.console.actions.AbstractActionDelegate#selectionChanged(org.eclipse.jface.action.IAction + * , org.eclipse.jface.viewers.ISelection) + */ + @Override + public void selectionChanged(IAction action, ISelection selection) { + volume = GUIHelper.getInstance().getSelectedEntity(getWindow(), Volume.class); + if (volume != null) { + action.setEnabled(volume.getOptions().size() > 0); + } else { + action.setEnabled(false); + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ResumeTaskAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ResumeTaskAction.java new file mode 100644 index 00000000..4ecb283d --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ResumeTaskAction.java @@ -0,0 +1,45 @@ +package org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.gluster.storage.management.client.TasksClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.core.model.Status; +import org.gluster.storage.management.core.model.TaskInfo; +import org.gluster.storage.management.core.model.TaskStatus; + + +public class ResumeTaskAction extends AbstractActionDelegate { + private TaskInfo taskInfo; + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + + @Override + protected void performAction(final IAction action) { + final String actionDesc = action.getDescription(); + + try { + new TasksClient().resumeTask(taskInfo.getName()); + taskInfo.setStatus(new TaskStatus(new Status(Status.STATUS_CODE_RUNNING, "Resumed"))); + modelManager.updateTask(taskInfo); + } catch (Exception e) { + showErrorDialog(actionDesc, + "Task [" + taskInfo.getDescription() + "] could not be Resumed! Error: [" + e.getMessage() + "]"); + } + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + action.setEnabled(false); + if (selectedEntity instanceof TaskInfo) { + taskInfo = (TaskInfo) selectedEntity; + action.setEnabled(taskInfo.getStatus().getCode() == Status.STATUS_CODE_PAUSE); + } + } + + @Override + public void dispose() { + + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ServerAdditionAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ServerAdditionAction.java new file mode 100644 index 00000000..c0ed3beb --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/ServerAdditionAction.java @@ -0,0 +1,28 @@ +package org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.gluster.storage.management.console.dialogs.ServerAdditionDialog; +import org.gluster.storage.management.console.utils.GlusterLogger; + + +public class ServerAdditionAction extends AbstractActionDelegate { + private static final GlusterLogger logger = GlusterLogger.getInstance(); + @Override + public void dispose() { + // TODO Auto-generated method stub + + } + + @Override + protected void performAction(IAction action) { + try { + // To open a dialog for server addition + ServerAdditionDialog dialog = new ServerAdditionDialog(getShell()); + dialog.open(); + } catch (Exception e) { + logger.error("Error in Manual server addition", e); + e.printStackTrace(); + } + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/StartVolumeAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/StartVolumeAction.java new file mode 100644 index 00000000..4a8db975 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/StartVolumeAction.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.osgi.internal.signedcontent.Base64; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.AlertsManager; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.Alert.ALERT_TYPES; +import org.gluster.storage.management.core.model.Volume.VOLUME_STATUS; + + +public class StartVolumeAction extends AbstractMonitoredActionDelegate { + //private Volume volume; + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + private List<Volume> selectedVolumes = new ArrayList<Volume>(); + private List<String> selectedVolumeNames = new ArrayList<String>(); + private List<String> offlineVolumeNames = new ArrayList<String>(); + + @Override + protected void performAction(IAction action, IProgressMonitor monitor) { + final String actionDesc = action.getDescription(); + + collectVolumeNames(); + + if (offlineVolumeNames.size() == 0) { + showWarningDialog(actionDesc, "Volumes " + selectedVolumeNames + " already started!"); + return; // Volume already started. Don't do anything. + } + + VolumesClient vc = new VolumesClient(); + Volume newVolume = new Volume(); + List<String> startedVolumes = new ArrayList<String>(); + List<String> failedVolumes = new ArrayList<String>(); + String errorMessage = ""; + List<String> cifsVolumes = GlusterDataModelManager.getInstance().getCifsEnabledVolumeNames(selectedVolumes); + List<String> offlineServers = GlusterDataModelManager.getInstance().getOfflineServers(); + // One or more servers are offline, Show warning if cifs is enabled + if (cifsVolumes != null && cifsVolumes.size() > 0 && offlineServers != null && offlineServers.size() > 0) { + Integer userAction = new MessageDialog(getShell(), "CIFS configuration", GUIHelper.getInstance().getImage( + IImageKeys.VOLUME_16x16), + "Performing CIFS updates when one or more servers are offline can trigger " + + "inconsistent behavior for CIFS accesses in the cluster." + CoreConstants.NEWLINE + + "Are you sure you want to continue?", MessageDialog.QUESTION, + new String[] { "No", "Yes" }, -1).open(); + if (userAction != 1) { + return; // Do not start volume services + } + } + + monitor.beginTask("Starting Selected Volumes...", selectedVolumes.size()); + // Starting of a volume results in changes to the model, and ultimately updates the "selectedVolumes" list, + // over which we are iterating, thus resulting in ConcurrentModificationException. To avoid this, we iterate + // over an array obtained from the list. + for (Volume volume : selectedVolumes.toArray(new Volume[0])) { + if(monitor.isCanceled()) { + break; + } + if (volume.getStatus() == VOLUME_STATUS.ONLINE) { + monitor.worked(1); + continue; // skip if already started + } + try { + monitor.setTaskName("Starting volume [" + volume.getName() + "]"); + vc.startVolume(volume.getName(), false); + modelManager.updateVolumeStatus(volume, VOLUME_STATUS.ONLINE); + startedVolumes.add(volume.getName()); + } catch (Exception e) { + failedVolumes.add(volume.getName()); + // If any post volume start activity failed, update the volume status + if (vc.getVolume(volume.getName()).getStatus() == VOLUME_STATUS.ONLINE) { + modelManager.updateVolumeStatus(volume, VOLUME_STATUS.ONLINE); + } + errorMessage += e.getMessage() + CoreConstants.NEWLINE; + } + + // Update the model by fetching latest volume info (NOT JUST STATUS) + try { + modelManager.refreshVolumeData(volume); + } catch (Exception e) { + errorMessage += "Updating volume info failed on UI. [" + e.getMessage() + "]"; + } + monitor.worked(1); + } + monitor.done(); + + // Display the success or failure info + if (startedVolumes.size() == 0) { // No volume(s) started successfully + showErrorDialog(actionDesc, "Volume(s) " + failedVolumes + " could not be started!" + + CoreConstants.NEWLINE + "Error: [" + errorMessage + "]"); + } else { + String info = "Volume(s) " + startedVolumes + " started successfully!"; + if (!errorMessage.equals("")) { + info += CoreConstants.NEWLINE + CoreConstants.NEWLINE + "Volumes " + failedVolumes + + " failed to start! [" + errorMessage + "]"; + } + if (selectedVolumes.size() == startedVolumes.size()) { + showInfoDialog(actionDesc, info); + } else { + showWarningDialog(actionDesc, info); + } + } + } + + private void collectVolumeNames() { + selectedVolumeNames.clear(); + offlineVolumeNames.clear(); + for (Volume volume : selectedVolumes) { + selectedVolumeNames.add(volume.getName()); + if (volume.getStatus() == VOLUME_STATUS.OFFLINE) { + offlineVolumeNames.add(volume.getName()); + } + } + } + + @Override + public void dispose() { + + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + Set<Volume> selectedVolumeNames = GUIHelper.getInstance().getSelectedEntities(getWindow(), Volume.class); + selectedVolumes.clear(); + if (selectedVolumeNames == null || selectedVolumeNames.isEmpty()) { + super.selectionChanged(action, selection); + if (selectedEntity instanceof Volume) { + selectedVolumes.add((Volume) selectedEntity); + } + } else { + selectedVolumes.addAll(selectedVolumeNames); //TODO reverse the collection to maintain the selected order + } + + action.setEnabled(false); + // To enable the action + for (Volume volume : selectedVolumes) { + if (volume.getStatus() == VOLUME_STATUS.OFFLINE) { + action.setEnabled(true); + break;// If find an online volume, enable the action + } + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/StopTaskAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/StopTaskAction.java new file mode 100644 index 00000000..e5ba017d --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/StopTaskAction.java @@ -0,0 +1,46 @@ +package org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.gluster.storage.management.client.TasksClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.core.model.Status; +import org.gluster.storage.management.core.model.TaskInfo; + + +public class StopTaskAction extends AbstractActionDelegate { + private TaskInfo taskInfo; + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + + @Override + protected void performAction(final IAction action) { + final String actionDesc = action.getDescription(); + + try { + new TasksClient().stopTask(taskInfo.getName()); + // On successful stop clear from the task list + modelManager.removeTask(taskInfo); + action.setEnabled(false); // TODO disable other task buttons + } catch (Exception e) { + showErrorDialog(actionDesc, + "Task [" + taskInfo.getDescription() + "] could not be Stopped! Error: [" + e.getMessage() + "]"); + } + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + action.setEnabled(false); + if (selectedEntity instanceof TaskInfo) { + taskInfo = (TaskInfo) selectedEntity; + action.setEnabled(taskInfo.getStopSupported() + && (taskInfo.getStatus().getCode() == Status.STATUS_CODE_PAUSE + || taskInfo.getStatus().getCode() == Status.STATUS_CODE_RUNNING)); + } + } + + @Override + public void dispose() { + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/StopVolumeAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/StopVolumeAction.java new file mode 100644 index 00000000..e1b67c1d --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/StopVolumeAction.java @@ -0,0 +1,229 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ISelection; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.Volume.VOLUME_STATUS; + + +public class StopVolumeAction extends AbstractMonitoredActionDelegate { + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + private List<Volume> selectedVolumes = new ArrayList<Volume>(); + private List<String> selectedVolumeNames = new ArrayList<String>(); + private List<String> onlineVolumeNames = new ArrayList<String>(); + private List<String> stoppedVolumes = new ArrayList<String>(); + private List<Volume> failedVolumes = new ArrayList<Volume>(); + private String errorMessage = null; + + @Override + protected void performAction(final IAction action, IProgressMonitor monitor) { + collectVolumeNames(); + + if (onlineVolumeNames.size() == 0) { + showWarningDialog(action.getDescription(), "Volumes " + selectedVolumeNames + " already stopped!"); + return; // Volumes already stopped, Don't do anything. + } + + Integer userAction = new MessageDialog(getShell(), "Stop Volume", GUIHelper.getInstance().getImage( + IImageKeys.VOLUME_16x16), "Are you sure you want to stop the following volumes?" + + CoreConstants.NEWLINE + onlineVolumeNames, MessageDialog.QUESTION, + new String[] { "No", "Yes" }, -1).open(); + + if (userAction <= 0) { // user select cancel or pressed escape key + return; + } + + List<String> cifsVolumes = GlusterDataModelManager.getInstance().getCifsEnabledVolumeNames(selectedVolumes); + List<String> offlineServers = GlusterDataModelManager.getInstance().getOfflineServers(); + // One or more servers are offline, Show warning if cifs is enabled + if (cifsVolumes != null && cifsVolumes.size() > 0 && offlineServers != null && offlineServers.size() > 0) { + userAction = new MessageDialog(getShell(), "CIFS configuration", GUIHelper.getInstance().getImage( + IImageKeys.VOLUME_16x16), + "Performing CIFS updates when one or more servers are offline can trigger " + + "inconsistent behavior for CIFS accesses in the cluster." + CoreConstants.NEWLINE + + "Are you sure you want to continue?", MessageDialog.QUESTION, + new String[] { "No", "Yes" }, -1).open(); + if (userAction != 1) { + return; // Do not stop volume services + } + } + + stopVolumes(selectedVolumes, false, monitor); + + // Check for errors, trying to force stop in case of errors + checkForErrors(action, monitor, true); + } + + private void checkForErrors(final IAction action, IProgressMonitor monitor, boolean tryForceStop) { + String message = null; + if (stoppedVolumes.size() == 0) { // No volume(s) stopped successfully + message = "Volume(s) " + failedVolumes + " could not be stopped! " + CoreConstants.NEWLINE + + "Error: [" + errorMessage + "]"; + if(tryForceStop) { + forceStopVolumes(action.getDescription(), message + CoreConstants.NEWLINE + + "Do you want to stop forcefully?", monitor); + // check for errors without trying to force stop in case of errors + checkForErrors(action, monitor, false); + return; + } else { + showErrorDialog(action.getDescription(), message); + return; + } + } else { + message = "Volume(s) " + stoppedVolumes + " stopped successfully!"; + if (!errorMessage.isEmpty()) { + if (failedVolumes.size() > 0) { + message = message + CoreConstants.NEWLINE + CoreConstants.NEWLINE + "Volume(s) " + + failedVolumes + " could not be stopped! [" + errorMessage + "]"; + if(tryForceStop) { + forceStopVolumes(action.getDescription(), message + CoreConstants.NEWLINE + + "Do you want to stop forcefully?", monitor); + // check for errors without trying to force stop in case of errors + checkForErrors(action, monitor, false); + return; + } + } else { // Stop volume success, but post stop volume fails, append the error message + message += CoreConstants.NEWLINE + CoreConstants.NEWLINE + errorMessage; + } + } + + if (errorMessage.isEmpty()) { + showInfoDialog(action.getDescription(), message); + } else { + showWarningDialog(action.getDescription(), message); + } + } + } + + private void forceStopVolumes(String actionDesc, String message, IProgressMonitor monitor) { + boolean forceStop = showConfirmDialog(actionDesc, message); + if (!forceStop) { + return; + } + stopVolumes(failedVolumes, true, monitor); + } + + private void stopVolumes(List<Volume> volumes, Boolean force, IProgressMonitor monitor) { + VolumesClient vc = new VolumesClient(); + Volume newVolume = new Volume(); + stoppedVolumes.clear(); + failedVolumes.clear(); + errorMessage = ""; + + monitor.beginTask("Stopping Selected Volumes...", volumes.size()); + // Stopping of a volume results in changes to the model, and ultimately updates the "selectedVolumes" list, + // over which we are iterating, thus resulting in ConcurrentModificationException. To avoid this, we iterate + // over an array obtained from the list. + for (Volume volume : volumes.toArray(new Volume[0])) { + if(monitor.isCanceled()) { + break; + } + + if (volume.getStatus() == VOLUME_STATUS.OFFLINE) { + monitor.worked(1); + continue; // skip if already stopped + } + try { + monitor.setTaskName("Stopping volume [" + volume.getName() + "]"); + vc.stopVolume(volume.getName(), force); + stoppedVolumes.add(volume.getName()); + modelManager.updateVolumeStatus(volume, VOLUME_STATUS.OFFLINE); + } catch (Exception e) { + // If any post volume stop activity failed, update the volume status + if (vc.getVolume(volume.getName()).getStatus() == VOLUME_STATUS.OFFLINE) { + // stop volume succeed, so add it to stoppedVolumes + stoppedVolumes.add(volume.getName()); + modelManager.updateVolumeStatus(volume, VOLUME_STATUS.OFFLINE); + errorMessage += "Volume [" + volume.getName() + "] stopped, but following error occured: [" + + e.getMessage() + "]"; + } else { + failedVolumes.add(volume); + errorMessage += "[" + volume.getName() + "] : " + e.getMessage() + CoreConstants.NEWLINE; + } + } + + // Update the model by fetching latest volume info (NOT JUST STATUS) + try { + modelManager.refreshVolumeData(volume); + } catch (Exception e) { + errorMessage += "Failed to update volume info on UI. [" + e.getMessage() + "]"; + } + monitor.worked(1); + } + monitor.done(); + } + + private void collectVolumeNames() { + selectedVolumeNames.clear(); + onlineVolumeNames.clear(); + for (Volume volume : selectedVolumes) { + selectedVolumeNames.add(volume.getName()); + if (volume.getStatus() == VOLUME_STATUS.ONLINE) { + onlineVolumeNames.add(volume.getName()); + } + } + } + + @Override + public void dispose() { + } + + /* + * (non-Javadoc) + * + * @see + * org.gluster.storage.management.console.actions.AbstractActionDelegate#selectionChanged(org.eclipse.jface.action.IAction + * , org.eclipse.jface.viewers.ISelection) + */ + @Override + public void selectionChanged(IAction action, ISelection selection) { + Set<Volume> selectedVolumeNames = GUIHelper.getInstance().getSelectedEntities(getWindow(), Volume.class); + selectedVolumes.clear(); + if (selectedVolumeNames == null || selectedVolumeNames.isEmpty()) { + super.selectionChanged(action, selection); + if (selectedEntity instanceof Volume) { + selectedVolumes.add((Volume) selectedEntity); + } + } else { + selectedVolumes.addAll(selectedVolumeNames); //TODO reverse the collection to maintain the selected order + } + + action.setEnabled(false); + // To enable the action + for (Volume volume : selectedVolumes) { + if (volume.getStatus() == VOLUME_STATUS.ONLINE) { + action.setEnabled(true); + break; // If find an online volume, enable the action + } + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/SupportAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/SupportAction.java new file mode 100644 index 00000000..9b698df9 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/SupportAction.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.gluster.storage.management.console.dialogs.GlusterSupportDialog; + + +/** + * + */ +public class SupportAction extends AbstractActionDelegate { + + /* (non-Javadoc) + * @see org.gluster.storage.management.console.actions.AbstractActionDelegate#performAction(org.eclipse.jface.action.IAction) + */ + @Override + protected void performAction(IAction action) { + GlusterSupportDialog dialog = new GlusterSupportDialog(getShell()); + dialog.create(); + dialog.getShell().setSize(770, 430); + dialog.open(); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#dispose() + */ + @Override + public void dispose() { + + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/TerminalAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/TerminalAction.java new file mode 100644 index 00000000..433bff69 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/TerminalAction.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.gluster.storage.management.console.utils.GUIHelper; + + +/** + * + */ +public class TerminalAction extends AbstractActionDelegate { + + /* (non-Javadoc) + * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#dispose() + */ + @Override + public void dispose() { + } + + /* (non-Javadoc) + * @see org.gluster.storage.management.console.actions.AbstractActionDelegate#performAction(org.eclipse.jface.action.IAction) + */ + @Override + protected void performAction(IAction action) { + GUIHelper.getInstance().showTerminalView(); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/TestPopupMenuAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/TestPopupMenuAction.java new file mode 100644 index 00000000..dfb2af2f --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/TestPopupMenuAction.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.actions; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IActionDelegate; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IWorkbenchPart; + +public class TestPopupMenuAction implements IObjectActionDelegate { + + private Shell shell; + + /** + * Constructor for Action1. + */ + public TestPopupMenuAction() { + super(); + } + + /** + * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart) + */ + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + shell = targetPart.getSite().getShell(); + } + + /** + * @see IActionDelegate#run(IAction) + */ + public void run(IAction action) { + MessageDialog.openInformation( + shell, + "glustersp-gui", + "New Action was executed."); + } + + /** + * @see IActionDelegate#selectionChanged(IAction, ISelection) + */ + public void selectionChanged(IAction action, ISelection selection) { + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/VolumeLogRotateAction.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/VolumeLogRotateAction.java new file mode 100644 index 00000000..7477358c --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/actions/VolumeLogRotateAction.java @@ -0,0 +1,64 @@ +package org.gluster.storage.management.console.actions; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IWorkbenchPart; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.VolumeBricksView; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.utils.GlusterCoreUtil; + + + +public class VolumeLogRotateAction extends AbstractActionDelegate { + + private Volume volume; + private GUIHelper guiHelper = GUIHelper.getInstance(); + private Set<Brick> bricks; + + @Override + public void dispose() { + } + + @Override + protected void performAction(IAction action) { + final String actionDesc = action.getDescription(); + List<String> selectedBricks = new ArrayList<String>(); + boolean confirmed = showConfirmDialog(actionDesc, + "Are you sure you want to Rotate logs for volume [" + volume.getName() + "] ? "); + if (!confirmed) { + return; + } + + if (bricks != null) { + selectedBricks = GlusterCoreUtil.getQualifiedBrickList(bricks); + } + try { + new VolumesClient().volumeLogRotate(volume.getName(), selectedBricks); + showInfoDialog(actionDesc, "Volume logs for [" + volume.getName() + "] rotated successfully!"); + } catch (Exception e) { + showErrorDialog(actionDesc, "Volume [" + volume.getName() + "] log rotation failed! Error: [" + e.getMessage() + "]"); + } + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + volume = guiHelper.getSelectedEntity(window, Volume.class); + + if (volume != null) { + // a volume is selected on navigation tree. Let's check if the currently open view is volume bricks view + IWorkbenchPart view = guiHelper.getActiveView(); + if (view instanceof VolumeBricksView) { + // volume bricks view is open. check if any brick is selected + bricks = GUIHelper.getInstance().getSelectedEntities(getWindow(), Brick.class); + } + } + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/AddBrickPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/AddBrickPage.java new file mode 100644 index 00000000..df42f12a --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/AddBrickPage.java @@ -0,0 +1,171 @@ +/** + * 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 org.gluster.storage.management.console.dialogs; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Device; +import org.gluster.storage.management.core.model.Volume; +import org.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 AddBrickPage extends WizardPage { + private List<Device> availableDevices = new ArrayList<Device>(); + private List<Device> selectedDevices = new ArrayList<Device>(); + private Volume volume = null; + private BricksSelectionPage page = null; + + + public static final String PAGE_NAME = "add.disk.volume.page"; + + /** + * @param pageName + */ + protected AddBrickPage(Volume volume) { + super(PAGE_NAME); + this.volume = volume; + setTitle("Add Brick"); + + String description = "Add bricks to [" + volume.getName() + "] "; + if ( volume.getVolumeType() == VOLUME_TYPE.DISTRIBUTED_REPLICATE) { + description += "(in multiples of " + volume.getReplicaCount() + ")"; + } else if (volume.getVolumeType() == VOLUME_TYPE.DISTRIBUTED_STRIPE) { + description += "(in multiples of " + volume.getStripeCount() + ")"; + } + setDescription(description); + + availableDevices = getAvailableDevices(volume); + + setPageComplete(false); + setErrorMessage("Please select bricks to be added to the volume [" + volume.getName() +"]"); + } + + + private boolean isDeviceUsed(Volume volume, Device device){ + for (Brick volumeBrick : volume.getBricks()) { + if ( device.getQualifiedBrickName(volume.getName()).equals(volumeBrick.getQualifiedName())) { + return true; + } + } + return false; + } + + protected List<Device> getAvailableDevices(Volume volume) { + List<Device> availableDevices = new ArrayList<Device>(); + for (Device device : GlusterDataModelManager.getInstance().getReadyDevicesOfAllServers()) { + if ( ! isDeviceUsed(volume, device) ) { + availableDevices.add(device); + } + } + return availableDevices; + } + + public Set<Device> getChosenDevices() { + return new HashSet<Device>(page.getChosenDevices()); + } + + public Set<Brick> getChosenBricks( String volumeName ) { + return page.getChosenBricks(volumeName); + } + + private boolean isValidDiskSelection(int diskCount) { + if ( diskCount == 0) { + return false; + } + switch (volume.getVolumeType()) { + case DISTRIBUTED_REPLICATE: + 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 Brick"); + List<Device> chosenDevices = new ArrayList<Device>(); // or volume.getDisks(); + + page = new BricksSelectionPage(parent, SWT.NONE, availableDevices, chosenDevices, volume.getName()); + page.addDiskSelectionListener(new ListContentChangedListener<Device>() { + @Override + public void listContentChanged(IRemovableContentProvider<Device> contentProvider) { + List<Device> newChosenDevices = page.getChosenDevices(); + + // validate chosen disks + if(isValidDiskSelection(newChosenDevices.size())) { + clearError(); + } else { + setError(); + } + } + }); + setControl(page); + } + + private void setError() { + String errorMessage = null; + if ( volume.getVolumeType() == VOLUME_TYPE.DISTRIBUTE) { + errorMessage = "Please select at least one brick!"; + } else if( volume.getVolumeType() == VOLUME_TYPE.DISTRIBUTED_REPLICATE) { + errorMessage = "Please select bricks in multiples of " + volume.getReplicaCount(); + } else { + errorMessage = "Please select bricks in multiples of " + volume.getStripeCount(); + } + + setPageComplete(false); + setErrorMessage(errorMessage); + } + + private void clearError() { + setErrorMessage(null); + setPageComplete(true); + } + + public BricksSelectionPage getDialogPage() { + return this.page; + } + + public void setPageComplete() { + + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/AddBrickWizard.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/AddBrickWizard.java new file mode 100644 index 00000000..da40f1d9 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/AddBrickWizard.java @@ -0,0 +1,96 @@ +/** + * 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 org.gluster.storage.management.console.dialogs; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.wizard.Wizard; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.utils.StringUtil; + + +/** + * + */ +public class AddBrickWizard extends Wizard { + private AddBrickPage page; + private Volume volume; + + public AddBrickWizard(Volume volume) { + setWindowTitle("Gluster Management Console - Add Brick"); + setHelpAvailable(false); // TODO: Introduce wizard help + this.volume = volume; + } + + public void addPages() { + page = new AddBrickPage(volume); + addPage(page); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.wizard.Wizard#performFinish() + */ + @Override + public boolean performFinish() { + Set<Brick> bricks = page.getChosenBricks(volume.getName()); + VolumesClient volumeClient = new VolumesClient(); + try { + Set<String> brickList = getBrickList(bricks); + + volumeClient.addBricks(volume.getName(), brickList); + + // Update model with new bricks in the volume + GlusterDataModelManager.getInstance().addBricks(volume, bricks); + + MessageDialog.openInformation(getShell(), "Add brick(s) to Volume", "Volume [" + volume.getName() + + "] is expanded with bricks [" + StringUtil.collectionToString(brickList, ", ") + "]"); + return true; + } catch (Exception e) { + MessageDialog.openError(getShell(), "Add brick(s) to Volume", e.getMessage()); + return false; + } + } + + private Set<String> getBrickList(Set<Brick> bricks) { + Set<String> brickList = new HashSet<String>(); + for(Brick brick : bricks) { + brickList.add(brick.getServerName() + ":" + brick.getBrickDirectory()); + } + return brickList; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.wizard.Wizard#canFinish() + */ + @Override + public boolean canFinish() { + return super.canFinish() && page.isPageComplete(); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/BricksSelectionPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/BricksSelectionPage.java new file mode 100644 index 00000000..99f6d201 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/BricksSelectionPage.java @@ -0,0 +1,336 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.dialogs; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +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.Label; +import org.eclipse.swt.widgets.TableItem; +import org.eclipse.swt.widgets.Text; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.console.TableLabelProviderAdapter; +import org.gluster.storage.management.console.utils.EntityViewerFilter; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Device; +import org.gluster.storage.management.core.model.Disk; +import org.gluster.storage.management.core.model.Brick.BRICK_STATUS; +import org.gluster.storage.management.core.utils.NumberUtil; + +import com.richclientgui.toolbox.duallists.CustomTableDualListComposite; +import com.richclientgui.toolbox.duallists.DualListComposite.ListContentChangedListener; +import com.richclientgui.toolbox.duallists.IRemovableContentProvider; +import com.richclientgui.toolbox.duallists.RemovableContentProvider; +import com.richclientgui.toolbox.duallists.TableColumnData; + +public class BricksSelectionPage extends Composite { + private enum DISK_TABLE_COLUMN_INDICES { + SERVER, BRICK_DIRECTORY, FREE_SPACE, TOTAL_SPACE + } + + private static final String[] DISK_TABLE_COLUMNS_NAMES = { "Server", "Brick Directory", "Free Space (GB)", + "Total Space (GB)" }; + + private GUIHelper guiHelper = GUIHelper.getInstance(); + private CustomTableDualListComposite<Device> dualTableViewer; + private Text filterText; + // This list keeps track of the order of the disks as user changes the same by clicking on up/down arrow buttons + private List<Device> chosenDevices = new ArrayList<Device>(); + + private IRemovableContentProvider<Device> chosenBricksContentProvider; + + private Button btnUp; + + private Button btnDown; + + public BricksSelectionPage(final Composite parent, int style, List<Device> allDevices, List<Device> selectedDevices, + String volumeName) { + super(parent, style); + + createPage(allDevices, selectedDevices, volumeName); + + parent.layout(); + } + + public void addDiskSelectionListener(ListContentChangedListener<Device> listener) { + dualTableViewer.addChosenListChangedSelectionListener(listener); + } + + private TableLabelProviderAdapter getDiskLabelProvider(final String volumeName) { + return new TableLabelProviderAdapter() { + + @Override + public String getColumnText(Object element, int columnIndex) { + if (!(element instanceof Device)) { + return null; + } + + Device device = (Device) element; + return (columnIndex == DISK_TABLE_COLUMN_INDICES.SERVER.ordinal() ? device.getServerName() + : columnIndex == DISK_TABLE_COLUMN_INDICES.BRICK_DIRECTORY.ordinal() ? device.getMountPoint() + + "/" + volumeName + : columnIndex == DISK_TABLE_COLUMN_INDICES.FREE_SPACE.ordinal() ? NumberUtil + .formatNumber((device.getFreeSpace() / 1024)) + : columnIndex == DISK_TABLE_COLUMN_INDICES.TOTAL_SPACE.ordinal() ? NumberUtil + .formatNumber((device.getSpace() / 1024)) : "Invalid"); + } + }; + } + + private int indexOf(List<Disk> disks, Disk searchDisk) { + for (Disk disk : disks) { + if (disk.getQualifiedName().equals(searchDisk.getQualifiedName())) { + return disks.indexOf(disk); + } + } + return -1; + } + + private void createPage(List<Device> allDevice, List<Device> selectedDevice, String volumeName) { + setupPageLayout(); + + filterText = guiHelper.createFilterText(this); + new Label(this, SWT.NONE); + + createDualTableViewer(allDevice, selectedDevice, volumeName); + createFilter(filterText, false); // attach filter text to the dual table viewer for auto-filtering + + Composite buttonContainer = new Composite(this, SWT.NONE); + buttonContainer.setLayout(new GridLayout(1, false)); + GridData buttonContainerData = new GridData(SWT.FILL, SWT.CENTER, true, true); + buttonContainerData.minimumWidth = 40; + buttonContainer.setLayoutData(buttonContainerData); + + btnUp = new Button(buttonContainer, SWT.PUSH); + GridData btnUpData = new GridData(SWT.LEFT, SWT.BOTTOM, true, false); + btnUpData.minimumWidth = 30; + btnUp.setLayoutData(btnUpData); + btnUp.setImage(guiHelper.getImage(IImageKeys.ARROW_UP_16x16)); + btnUp.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + chosenDevices = getChosenDevices(); + List<Device> selectedDevices = getSelectedChosenDevices(); + + chosenBricksContentProvider.removeElements(chosenDevices); + for (Device device : selectedDevices) { + int index = chosenDevices.indexOf(device); + Device deviceAbove = chosenDevices.get(index - 1); + chosenDevices.set(index - 1, device); + chosenDevices.set(index, deviceAbove); + } + chosenBricksContentProvider.addElements(chosenDevices); + dualTableViewer.refreshChosenViewer(); + updateButtons(); + } + }); + + btnDown = new Button(buttonContainer, SWT.PUSH); + GridData btnDownData = new GridData(SWT.LEFT, SWT.TOP, true, false); + btnDownData.minimumWidth = 30; + btnDown.setLayoutData(btnDownData); + btnDown.setImage(guiHelper.getImage(IImageKeys.ARROW_DOWN_16x16)); + btnDown.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + + chosenDevices = getChosenDevices(); + List<Device> selectedDevices = getSelectedChosenDevices(); + + chosenBricksContentProvider.removeElements(chosenDevices); + for (Device disk : selectedDevices) { + int index = chosenDevices.indexOf(disk); + Device deviceBelow = chosenDevices.get(index + 1); + chosenDevices.set(index + 1, disk); + chosenDevices.set(index, deviceBelow); + } + chosenBricksContentProvider.addElements(chosenDevices); + dualTableViewer.refreshChosenViewer(); + updateButtons(); + + } + }); + } + + private List<Device> getSelectedChosenDevices() { + TableItem[] selectedItems = dualTableViewer.getChosenTable().getSelection(); + List<Device> selectedDevices = new ArrayList<Device>(); + for (TableItem item : selectedItems) { + selectedDevices.add((Device) item.getData()); + } + return selectedDevices; + } + + private void createFilter(final Text filterText, boolean caseSensitive) { + final String initialFilterString = filterText.getText(); + + final EntityViewerFilter filter = new EntityViewerFilter(initialFilterString, caseSensitive); + // On every keystroke inside the text field, update the filter string + filterText.addKeyListener(new KeyAdapter() { + private String filterString = initialFilterString; + + @Override + public void keyReleased(KeyEvent e) { + String enteredString = filterText.getText(); + if (enteredString.equals(filterString)) { + // Filter string has not changed. don't do anything + return; + } + + // Update filter string + filterString = enteredString; + filter.setFilterString(filterString); + + // Refresh viewer with newly filtered content + dualTableViewer.refreshAvailableViewer(); + dualTableViewer.refreshChosenViewer(); + } + }); + + dualTableViewer.setAvailableViewerFilter(filter); + dualTableViewer.setChosenViewerFilter(filter); + } + + private void createDualTableViewer(List<Device> allDevices, List<Device> selectedDevices, String volumeName) { + TableColumnData[] columnData = createColumnData(); + ITableLabelProvider diskLabelProvider = getDiskLabelProvider(volumeName); + + dualTableViewer = new CustomTableDualListComposite<Device>(this, SWT.NONE, columnData, columnData); + + dualTableViewer.setViewerLabels("Available:", "Selected:"); + + dualTableViewer.setAvailableTableLinesVisible(false); + dualTableViewer.setAvailableTableHeaderVisible(true); + dualTableViewer.setAvailableContentProvider(new RemovableContentProvider<Device>(getAvailableDevice(allDevices, + selectedDevices))); + dualTableViewer.setAvailableLabelProvider(diskLabelProvider); + + dualTableViewer.setChosenTableLinesVisible(true); + dualTableViewer.setChosenTableHeaderVisible(true); + + chosenBricksContentProvider = new RemovableContentProvider<Device>(selectedDevices); + dualTableViewer.setChosenContentProvider(chosenBricksContentProvider); + dualTableViewer.setChosenLabelProvider(diskLabelProvider); + + dualTableViewer.getChosenTable().addSelectionListener(new SelectionAdapter() { + /* + * (non-Javadoc) + * + * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) + */ + @Override + public void widgetSelected(SelectionEvent e) { + updateButtons(); + } + }); + } + + private void updateButtons() { + btnUp.setEnabled(true); + btnDown.setEnabled(true); + List<Device> selectedChosenDevices = getSelectedChosenDevices(); + List<Device> chosenDevices = getChosenDevices(); + for (Device device : selectedChosenDevices) { + int index = chosenDevices.indexOf(device); + if (index == 0) { + btnUp.setEnabled(false); + } + if (index == chosenDevices.size() - 1) { + btnDown.setEnabled(false); + } + } + } + + /** + * @param allDevices + * @param selectedDevices + * @return + */ + private List<Device> getAvailableDevice(List<Device> allDevices, List<Device> selectedDevices) { + List<Device> availableDevices = new ArrayList<Device>(); + for (Device device : allDevices) { + if (!selectedDevices.contains(device)) { + availableDevices.add(device); + } + } + return availableDevices; + } + + private TableColumnData[] createColumnData() { + DISK_TABLE_COLUMN_INDICES[] columns = DISK_TABLE_COLUMN_INDICES.values(); + TableColumnData[] columnData = new TableColumnData[columns.length]; + int columnNum; + for (DISK_TABLE_COLUMN_INDICES column : columns) { + columnNum = column.ordinal(); + columnData[columnNum] = new TableColumnData(columnNum, DISK_TABLE_COLUMNS_NAMES[columnNum], 100); + } + return columnData; + } + + private void setupPageLayout() { + final GridLayout layout = new GridLayout(2, false); + layout.verticalSpacing = 10; + layout.marginTop = 10; + setLayout(layout); + + setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + } + + public List<Device> getChosenDevices() { + Object[] devicesArr = chosenBricksContentProvider.getElements(dualTableViewer); + if (devicesArr != null) { + List<Device> devices = new ArrayList<Device>(); + for (Object device : devicesArr) { + devices.add((Device) device); + } + return devices; + } + return null; + } + + public Set<Brick> getChosenBricks(String volumeName) { + Object[] bricksArr = chosenBricksContentProvider.getElements(dualTableViewer); + + if (bricksArr != null) { + Set<Brick> bricks = new HashSet<Brick>(); + for (Object device : bricksArr) { + bricks.add(new Brick(((Device) device).getServerName(), BRICK_STATUS.ONLINE, ((Device) device) + .getMountPoint() + "/" + volumeName)); // Assumption mount point is not having trailing "/" + } + return bricks; + } + return null; + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/ChangePasswordDialog.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/ChangePasswordDialog.java new file mode 100644 index 00000000..09b4b08c --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/ChangePasswordDialog.java @@ -0,0 +1,284 @@ +/** + * ChangePasswordDialog.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 org.gluster.storage.management.console.dialogs; + +import org.eclipse.core.databinding.DataBindingContext; +import org.eclipse.core.databinding.UpdateValueStrategy; +import org.eclipse.core.databinding.beans.PojoProperties; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.databinding.swt.SWTObservables; +import org.eclipse.jface.databinding.swt.WidgetProperties; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +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.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.gluster.storage.management.client.UsersClient; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.validators.StringRequiredValidator; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.ConnectionDetails; + + +public class ChangePasswordDialog extends Dialog { + public static final int RETURN_CODE_ERROR = 2; + private Text oldPassword; + private Text newPassword; + private Text confirmPassword; + private Button okButton; + + private final GUIHelper guiHelper = GUIHelper.getInstance(); + private Composite composite; + + private final ConnectionDetails connectionDetails = new ConnectionDetails("gluster", ""); + + public ChangePasswordDialog(Shell shell) { + super(shell); + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + + newShell.setText("Gluster Management Console"); + addEscapeListener(newShell); + } + + private void addEscapeListener(Shell shell) { + shell.addTraverseListener(new TraverseListener() { + + @Override + public void keyTraversed(TraverseEvent e) { + if (e.keyCode == SWT.ESC) { + cancelPressed(); + } + } + }); + } + + /** + * Overriding to make sure that the dialog is centered in screen + */ + @Override + protected void initializeBounds() { + super.initializeBounds(); + + guiHelper.centerShellInScreen(getShell()); + } + + private void configureDialogLayout(Composite composite) { + GridLayout layout = (GridLayout) composite.getLayout(); + layout.numColumns = 2; + layout.marginLeft = 20; + layout.marginRight = 20; + layout.marginTop = 20; + layout.horizontalSpacing = 20; + layout.verticalSpacing = 20; + } + + // ------------------------------------------ + + private void createLabel(Composite composite, String label) { + Label passwordLabel = new Label(composite, SWT.NONE); + passwordLabel.setText(label); + passwordLabel.setLayoutData(new GridData(GridData.END, GridData.CENTER, false, false)); + } + + private Text createPasswordText(Composite composite) { + Text field = new Text(composite, SWT.BORDER | SWT.PASSWORD); + GridData layoutData = new GridData(SWT.FILL, GridData.FILL, true, false); + layoutData.widthHint = convertWidthInCharsToPixels(32); + field.setLayoutData(layoutData); + return field; + } + + @Override + protected Control createDialogArea(Composite parent) { + // parent.setBackgroundImage(guiHelper.getImage(IImageKeys.DIALOG_SPLASH_IMAGE)); + parent.setBackgroundMode(SWT.INHERIT_FORCE); + + composite = (Composite) super.createDialogArea(parent); + configureDialogLayout(composite); + + createLabel(composite, "Old Password:"); + oldPassword = createPasswordText(composite); + + createLabel(composite, "New Password:"); + newPassword = createPasswordText(composite); + + createLabel(composite, "Confirm Password:"); + confirmPassword = createPasswordText(composite); + + createListeners(); + + return composite; + } + + /** + * + */ + private void createListeners() { + ModifyListener listener = new ModifyListener() { + + @Override + public void modifyText(ModifyEvent e) { + updateButtonStatus(); + } + }; + + oldPassword.addModifyListener(listener); + newPassword.addModifyListener(listener); + confirmPassword.addModifyListener(listener); + } + + private void updateButtonStatus() { + String oldPwd = oldPassword.getText(); + String newPwd = newPassword.getText(); + String confirmPwd = confirmPassword.getText(); + if(oldPwd.isEmpty() || newPwd.isEmpty() || confirmPwd.isEmpty()) { + okButton.setEnabled(false); + return; + } + + if(!newPwd.equals(confirmPwd)) { + okButton.setEnabled(false); + return; + } + + if (confirmPwd.equals(CoreConstants.DEFAULT_PASSWORD) || isContainsWhiteSpace(confirmPwd)) { + okButton.setEnabled(false); + return; + } + + if (newPwd.length() < 4 ) { // Minimum password length is 4 + okButton.setEnabled(false); + return; + } + + okButton.setEnabled(true); + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + okButton = createButton(parent, IDialogConstants.OK_ID, "&Change", true); + okButton.setEnabled(false); + createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); + + setupDataBinding(); + } + + public boolean isContainsWhiteSpace(String text) { + return text.matches(".*[\\s\\\\].*"); // White space or backslash + } + + public class ConfirmPasswordValidator extends StringRequiredValidator { + public ConfirmPasswordValidator(String errorText, ControlDecoration controlDecoration, Control linkedControl) { + super(errorText, controlDecoration, linkedControl); + } + + @Override + public IStatus validate(Object value) { + + IStatus status = super.validate(value); + if (status.isOK()) { + String errMsg = null; + if (errMsg == null && isContainsWhiteSpace( newPassword.getText())) { + errMsg = "Password should not contain space or back slash characters"; + } + + if (!value.equals(newPassword.getText())) { + errMsg = "Passwords mismatched"; + } + + if (errMsg == null && value.equals(CoreConstants.DEFAULT_PASSWORD)) { + errMsg = "New password should not be a default password"; + } + + if(errMsg != null) { + controlDecoration.setDescriptionText(errMsg); + controlDecoration.show(); + linkedControl.setEnabled(false); + return ValidationStatus.error(errMsg); + } + } + return status; + } + }; + + private void setupDataBinding() { + DataBindingContext dataBindingContext = new DataBindingContext(SWTObservables.getRealm(Display.getCurrent())); + UpdateValueStrategy passwordBindingStrategy = new UpdateValueStrategy(UpdateValueStrategy.POLICY_UPDATE); + UpdateValueStrategy newPwdBindingStrategy = new UpdateValueStrategy(UpdateValueStrategy.POLICY_UPDATE); + UpdateValueStrategy confirmPwdBindingStrategy = new UpdateValueStrategy(UpdateValueStrategy.POLICY_UPDATE); + + // The Validator shows error decoration and disables OK button on + // validation failure + passwordBindingStrategy.setBeforeSetValidator(new StringRequiredValidator("Please enter old password!", + guiHelper.createErrorDecoration(oldPassword), null)); + + dataBindingContext.bindValue(WidgetProperties.text(SWT.Modify).observe(oldPassword), + PojoProperties.value("password").observe(connectionDetails), passwordBindingStrategy, + passwordBindingStrategy); + + newPwdBindingStrategy.setBeforeSetValidator(new StringRequiredValidator("Please enter new password!", guiHelper + .createErrorDecoration(newPassword), null)); + + dataBindingContext.bindValue(WidgetProperties.text(SWT.Modify).observe(newPassword), + PojoProperties.value("newPassword").observe(connectionDetails), newPwdBindingStrategy, + newPwdBindingStrategy); + + confirmPwdBindingStrategy.setBeforeSetValidator(new ConfirmPasswordValidator("Please enter confirm password!", + guiHelper.createErrorDecoration(confirmPassword), null)); + + dataBindingContext.bindValue(WidgetProperties.text(SWT.Modify).observe(confirmPassword), + PojoProperties.value("confirmNewPassword").observe(connectionDetails), confirmPwdBindingStrategy, + confirmPwdBindingStrategy); + } + + protected void okPressed() { + String user = connectionDetails.getUserId(); + String oldPassword = connectionDetails.getPassword(); + String newPassword = connectionDetails.getNewPassword(); + + UsersClient usersClient = new UsersClient(); + try { + usersClient.changePassword(user, oldPassword, newPassword); + MessageDialog.openInformation(getShell(), "Change password", "Password changed successfully!"); + this.close(); + } catch (Exception e) { + MessageDialog.openError(getShell(), "Change password Failed", e.getMessage()); + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/ClusterSelectionDialog.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/ClusterSelectionDialog.java new file mode 100644 index 00000000..44b9e963 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/ClusterSelectionDialog.java @@ -0,0 +1,474 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.dialogs; + +import java.util.List; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StackLayout; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.gluster.storage.management.console.Activator; +import org.gluster.storage.management.console.ConsoleConstants; +import org.gluster.storage.management.console.preferences.PreferenceConstants; +import org.gluster.storage.management.console.utils.GUIHelper; + + +/** + * Cluster selection dialog, which prompts for the cluster name to be managed + */ +public class ClusterSelectionDialog extends Dialog { + private static final String MESSAGE_SELECT_CLUSTER = "Select the Cluster you want to manage in this session."; + private static final String MESSAGE_CREATE_CLUSTER = "Create an empty Cluster and start adding servers to it."; + private static final String MESSAGE_REGISTER_CLUSTER = "Register an existing Cluster with the Management Gateway and start managing it using the Management Console."; + + protected enum CLUSTER_MODE { SELECT, CREATE, REGISTER }; + + private Combo clusterNameCombo = null; + private Text newClusterNameText = null; + private Text existingClusterNameText = null; + private Text serverNameText = null; + private Button okButton; + + private final GUIHelper guiHelper = GUIHelper.getInstance(); + private Composite composite; + private ControlDecoration newClusterNameErrorDecoration; + private ControlDecoration existingClusterNameErrorDecoration; + private ControlDecoration serverNameErrorDecoration; + private List<String> clusters; + private Button selectButton; + private Button createButton; + private Button registerButton; + private Composite clusterSelectionComposite; + private Composite clusterCreationComposite; + private Composite clusterRegisterComposite; + private StackLayout stackLayout; + + private String clusterName; + private CLUSTER_MODE clusterMode; + private String serverName; + private Button dontAskAgainButton; + IPreferenceStore preferenceStore; + + public ClusterSelectionDialog(Shell parentShell, List<String> clusters) { + super(parentShell); + this.clusters = clusters; + preferenceStore = Activator.getDefault().getPreferenceStore(); + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + + newShell.setText("Gluster Management Console - Select Cluster"); + addEscapeListener(newShell); + } + + private void addEscapeListener(Shell shell) { + shell.addTraverseListener(new TraverseListener() { + + @Override + public void keyTraversed(TraverseEvent e) { + if (e.keyCode == SWT.ESC) { + cancelPressed(); + } + } + }); + } + + private void createClusterNameLabel(Composite composite) { + Label clusterNameLabel = new Label(composite, SWT.NONE); + clusterNameLabel.setText("Cluster &Name:"); + clusterNameLabel.setLayoutData(new GridData(GridData.END, GridData.CENTER, false, false)); + } + + private void createClusterNameCombo(Composite composite) { + clusterNameCombo = new Combo(composite, SWT.READ_ONLY); + clusterNameCombo.setItems(clusters.toArray(new String[0])); + clusterNameCombo.select(0); + + String clusterName = preferenceStore.getString(PreferenceConstants.P_DEFAULT_CLUSTER_NAME); + if(clusterName != null && !clusterName.isEmpty()) { + selectCluster(clusterName); + } + } + + public void selectCluster(String clusterName) { + for(int i = 0; i < clusters.size(); i++) { + if(clusterNameCombo.getItem(i).equals(clusterName)) { + clusterNameCombo.select(i); + break; + } + } + } + + private void configureDialogLayout(Composite composite) { + GridLayout layout = (GridLayout) composite.getLayout(); + layout.numColumns = 3; + layout.marginLeft = 20; + layout.marginRight = 20; + layout.marginTop = 20; + layout.horizontalSpacing = 20; + layout.verticalSpacing = 20; + } + + /** + * Overriding to make sure that the dialog is centered in screen + */ + @Override + protected void initializeBounds() { + super.initializeBounds(); + + guiHelper.centerShellInScreen(getShell()); + } + + @Override + protected Control createDialogArea(Composite parent) { + //parent.setBackgroundImage(guiHelper.getImage(IImageKeys.DIALOG_SPLASH_IMAGE)); + // Makes sure that child composites inherit the same background + parent.setBackgroundMode(SWT.INHERIT_FORCE); + + composite = (Composite) super.createDialogArea(parent); + configureDialogLayout(composite); + + createRadioButtons(); + createSubComposites(); + + setupAutoSelectionIfRequired(); + + return composite; + } + + private void setupAutoSelectionIfRequired() { + if (clusters.size() == 0) { + return; + } + + final String clusterName = System.getProperty(ConsoleConstants.PROPERTY_AUTO_CLUSTER_NAME, null); + if (clusterName == null) { + return; + } + + getShell().addShellListener(new ShellAdapter() { + @Override + public void shellActivated(ShellEvent e) { + super.shellActivated(e); + clusterNameCombo.setText(clusterName); + okPressed(); + } + }); + } + + + private void createSubComposites() { + Composite subComposite = new Composite(composite, SWT.NONE); + GridData data = new GridData(); + data.horizontalSpan = 3; + subComposite.setLayoutData(data); + stackLayout = new StackLayout(); + subComposite.setLayout(stackLayout); + + createClusterSelectionComposite(subComposite, stackLayout); + createClusterCreationComposite(subComposite); + createClusterRegisterComposite(subComposite); + + createRadioButtonListeners(subComposite); + if(clusters.size() > 0) { + selectButton.setSelection(true); + stackLayout.topControl = clusterSelectionComposite; + clusterNameCombo.setFocus(); + } else { + createButton.setSelection(true); + stackLayout.topControl = clusterCreationComposite; + newClusterNameText.setFocus(); + } + subComposite.layout(); + } + + private void createClusterRegisterComposite(Composite composite) { + clusterRegisterComposite = new Composite(composite, SWT.NONE); + GridLayout layout = new GridLayout(2, false); + layout.horizontalSpacing = 15; + clusterRegisterComposite.setLayout(layout); + + createClusterNameLabel(clusterRegisterComposite); + existingClusterNameText = createText(clusterRegisterComposite); + existingClusterNameText.setToolTipText("Enter a name for the cluster being registered."); + existingClusterNameErrorDecoration = createErrorDecoration(existingClusterNameText, "Please enter a cluster name!"); + existingClusterNameErrorDecoration.show(); + + createClusterServerLabel(clusterRegisterComposite); + serverNameText = createText(clusterRegisterComposite); + serverNameText.setToolTipText("Enter host name / IP address of one of the servers of the cluster."); + serverNameErrorDecoration = createErrorDecoration(serverNameText, "Please enter a server name!"); + serverNameErrorDecoration.show(); + } + + private void createClusterServerLabel(Composite composite) { + Label serverNameLabel = new Label(composite, SWT.NONE); + serverNameLabel.setText("Server Na&me:"); + serverNameLabel.setLayoutData(new GridData(GridData.END, GridData.CENTER, false, false)); + } + + private void createClusterCreationComposite(Composite subComposite) { + clusterCreationComposite = new Composite(subComposite, SWT.NONE); + GridLayout layout = new GridLayout(2, false); + layout.horizontalSpacing = 15; + clusterCreationComposite.setLayout(layout); + + createClusterNameLabel(clusterCreationComposite); + newClusterNameText = createText(clusterCreationComposite); + newClusterNameText.setToolTipText("Enter name of the cluster to be created"); + newClusterNameErrorDecoration = createErrorDecoration(newClusterNameText, "Please enter cluster name!"); + newClusterNameErrorDecoration.show(); + } + + private Text createText(Composite parent) { + Text text = new Text(parent, SWT.NONE); + GridData layoutData = new GridData(SWT.FILL, GridData.FILL, true, false); + int width = convertWidthInCharsToPixels(32); + layoutData.widthHint = width; + layoutData.minimumWidth = width; + text.setLayoutData(layoutData); + + text.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + validate(); + } + }); + + return text; + } + + private void createClusterSelectionComposite(Composite subComposite, StackLayout stackLayout) { + clusterSelectionComposite = new Composite(subComposite, SWT.NONE); + GridLayout layout = new GridLayout(2, false); + clusterSelectionComposite.setLayout(layout); + + createClusterNameLabel(clusterSelectionComposite); + createClusterNameCombo(clusterSelectionComposite); + createPreferenceCheckbox(clusterSelectionComposite); + + stackLayout.topControl = clusterSelectionComposite; + } + + private void createPreferenceCheckbox(Composite composite) { + GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, false); + layoutData.verticalIndent = 5; + layoutData.horizontalSpan = 2; + + dontAskAgainButton = new Button(composite, SWT.CHECK); + dontAskAgainButton.setLayoutData(layoutData); + dontAskAgainButton.setText("&Don't ask again"); + dontAskAgainButton.setEnabled(true); + dontAskAgainButton.setSelection(false); + dontAskAgainButton.setToolTipText("Always manage the selected cluster without showing this dialog box." + + "This preference can later be changed from the \"Settings\" menu."); + } + + private void createRadioButtons() { + { + if (clusters.size() > 0) { + selectButton = new Button(composite, SWT.RADIO); + selectButton.setText("&Select"); + selectButton.setToolTipText(MESSAGE_SELECT_CLUSTER); + } + } + { + createButton = new Button(composite, SWT.RADIO); + createButton.setText("&Create"); + createButton.setToolTipText(MESSAGE_CREATE_CLUSTER); + } + { + registerButton = new Button(composite, SWT.RADIO); + registerButton.setText("&Register"); + registerButton.setToolTipText(MESSAGE_REGISTER_CLUSTER); + } + } + + private void validate() { + okButton.setEnabled(false); + + if(selectButton != null && selectButton.getSelection()) { + okButton.setEnabled(true); + return; + } + + if(createButton.getSelection()) { + String newClusterName = newClusterNameText.getText().trim(); + if(newClusterName.isEmpty()) { + newClusterNameErrorDecoration.setDescriptionText("Please enter a cluster name!"); + newClusterNameErrorDecoration.show(); + } else if(clusters.contains(newClusterName)) { + newClusterNameErrorDecoration.setDescriptionText("Cluster [" + newClusterName + "] already exists!"); + newClusterNameErrorDecoration.show(); + } else { + okButton.setEnabled(true); + newClusterNameErrorDecoration.hide(); + } + } + + if(registerButton.getSelection()) { + okButton.setEnabled(true); + String clusterName = existingClusterNameText.getText().trim(); + if(existingClusterNameText.getText().trim().isEmpty()) { + existingClusterNameErrorDecoration.setDescriptionText("Please enter a cluster name!"); + existingClusterNameErrorDecoration.show(); + okButton.setEnabled(false); + } else if(clusters.contains(clusterName)) { + existingClusterNameErrorDecoration.setDescriptionText("Cluster [" + clusterName + "] already exists!"); + existingClusterNameErrorDecoration.show(); + okButton.setEnabled(false); + } else { + existingClusterNameErrorDecoration.hide(); + } + + if(serverNameText.getText().trim().isEmpty()) { + serverNameErrorDecoration.show(); + okButton.setEnabled(false); + } else { + serverNameErrorDecoration.hide(); + } + } + } + + private void createRadioButtonListeners(final Composite parent) { + if (clusters.size() > 0) { + selectButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + stackLayout.topControl = clusterSelectionComposite; + clusterNameCombo.select(0); + validate(); + parent.layout(); + clusterNameCombo.setFocus(); + } + }); + } + createButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + stackLayout.topControl = clusterCreationComposite; + validate(); + parent.layout(); + newClusterNameText.setFocus(); + } + }); + registerButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + stackLayout.topControl = clusterRegisterComposite; + validate(); + parent.layout(); + existingClusterNameText.setFocus(); + } + }); + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + okButton = createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true); + createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); + + setupDataBinding(); + } + + private ControlDecoration createErrorDecoration(Text text, String message) { + ControlDecoration errorDecoration = guiHelper.createErrorDecoration(text); + errorDecoration.setDescriptionText(message); + errorDecoration.hide(); + return errorDecoration; + } + + /** + * Sets up data binding between the text fields and the connection details object. Also attaches a "string required" + * validator to the "password" text field. This validator is configured to do the following on validation failure<br> + * <li>show an ERROR decorator</li><li>disable the "Login" button + */ + private void setupDataBinding() { + clusterNameCombo.addModifyListener(new ModifyListener() { + + @Override + public void modifyText(ModifyEvent e) { + if(clusterNameCombo.getText().trim().isEmpty()) { + okButton.setEnabled(false); + newClusterNameErrorDecoration.show(); + } else { + okButton.setEnabled(true); + newClusterNameErrorDecoration.hide(); + } + } + }); + } + + @Override + protected void okPressed() { + if(selectButton != null && selectButton.getSelection()) { + clusterMode = CLUSTER_MODE.SELECT; + clusterName = clusterNameCombo.getText(); + + if(dontAskAgainButton.getSelection()) { + preferenceStore.setValue(PreferenceConstants.P_SHOW_CLUSTER_SELECTION_DIALOG, false); + preferenceStore.setValue(PreferenceConstants.P_DEFAULT_CLUSTER_NAME, clusterName); + } else { + preferenceStore.setValue(PreferenceConstants.P_SHOW_CLUSTER_SELECTION_DIALOG, true); + } + } else if(createButton.getSelection()) { + clusterMode = CLUSTER_MODE.CREATE; + clusterName = newClusterNameText.getText().trim(); + } else if(registerButton.getSelection()) { + clusterMode = CLUSTER_MODE.REGISTER; + clusterName = existingClusterNameText.getText().trim(); + serverName = serverNameText.getText().trim(); + } + super.okPressed(); + } + + public String getClusterName() { + return clusterName; + } + + public CLUSTER_MODE getClusterMode() { + return clusterMode; + } + + public String getServerName() { + return serverName; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/CreateVolumePage1.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/CreateVolumePage1.java new file mode 100644 index 00000000..9f1ec929 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/CreateVolumePage1.java @@ -0,0 +1,473 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.dialogs; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +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.viewers.SelectionChangedEvent; +import org.eclipse.jface.window.Window; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Text; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Device; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.Brick.BRICK_STATUS; +import org.gluster.storage.management.core.model.Volume.NAS_PROTOCOL; +import org.gluster.storage.management.core.model.Volume.TRANSPORT_TYPE; +import org.gluster.storage.management.core.model.Volume.VOLUME_TYPE; +import org.gluster.storage.management.core.utils.ValidationUtil; + + +public class CreateVolumePage1 extends WizardPage { + public static final String PAGE_NAME = "create.volume.page.1"; + private Text txtName; + private ComboViewer typeComboViewer; + private Text txtAccessControl; + private Text txtCifsUsers; + private Volume volume = new Volume(); + private Button btnNfs; + private Button btnCIFS; + private Button btnStartVolume; + private Link linkCustomize; + private List<Device> allDevices; + private List<Device> selectedDevices; + + /** + * Create the wizard. + */ + public CreateVolumePage1() { + super(PAGE_NAME); + setTitle("Create Volume"); + setDescription("Create a new Volume by choosing bricks from the cluster servers and configuring the volume properties."); + + // by default, we create volume with all available disks + allDevices = GlusterDataModelManager.getInstance().getReadyDevicesOfAllServers(); + selectedDevices = allDevices; // volume.setDisks(allDisks); + } + + /** + * Create contents of the wizard. + * @param parent + */ + public void createControl(Composite parent) { + setPageComplete(false); + Composite container = createContainer(parent); + + createEmptyRow(container); + + createNameLabel(container); + createNameText(container); + + createTypeLabel(container); + createTypeCombo(container); + +// createTransportTypeLabel(container); +// createTransportTypeValueLabel(container); + + createDisksLabel(container); + createDisksCustomizeLink(container); + + createNasProtocolLabel(container); + createNasProtocolCheckboxes(container); + + createCifsUserLabel(container); + createCifsUserText(container); + + createEmptyLabel(container); + createCifsUserInfoLabel(container); + + createAccessControlLabel(container); + createAccessControlText(container); + + createEmptyLabel(container); + createAccessControlInfoLabel(container); + + 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 createCifsUserInfoLabel(Composite container) { + Label lblCifsUserInfo = new Label(container, SWT.TOP); + lblCifsUserInfo.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1)); + lblCifsUserInfo.setText("(Comma separated list user names)"); + } + + 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("Allow Access From: "); + } + + private void createCifsUserLabel(Composite container) { + Label lblAccessControl = new Label(container, SWT.NONE); + lblAccessControl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + lblAccessControl.setText("CIFS Users: "); + } + + private void createCifsUserText(Composite container) { + txtCifsUsers = new Text(container, SWT.BORDER); +// txtCifsUsers.setText("testuser1,testuser2,testuser3"); + GridData cifsControlData = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1); + cifsControlData.widthHint = 300; + txtCifsUsers.setLayoutData(cifsControlData); + txtCifsUsers.addModifyListener(new ModifyListener() { + + @Override + public void modifyText(ModifyEvent e) { + validateForm(); + } + }); + } + + 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(true); + btnNfs.setSelection(true); + btnNfs.setText("NFS"); + createEmptyLabel(container); + + btnCIFS = new Button(container, SWT.CHECK); + btnCIFS.setEnabled(true); + btnCIFS.setSelection(false); + btnCIFS.setText("CIFS"); + btnCIFS.addSelectionListener(new SelectionListener() { + + @Override + public void widgetSelected(SelectionEvent e) { + validateForm(); + } + @Override + public void widgetDefaultSelected(SelectionEvent e) { + validateForm(); + } + }); + } + + 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("Access Protocol: "); + } + + private void createDisksCustomizeLink(Composite container) { + linkCustomize = new Link(container, SWT.UNDERLINE_LINK); + linkCustomize.setText("All Brick(s) (<a>customize</a>)" ); + linkCustomize.setEnabled(false); + linkCustomize.addListener (SWT.Selection, new Listener () { + public void handleEvent(Event event) { + Display.getDefault().asyncExec(new Runnable() { + + @Override + public void run() { + SelectDisksDialog dialog = new SelectDisksDialog(getShell(), allDevices, selectedDevices, txtName.getText().trim()); + + dialog.create(); + if(dialog.open() == Window.OK) { + // user has customized disks. get them from the dialog box. + selectedDevices = dialog.getSelectedDevices(); + linkCustomize.setText("" + selectedDevices.size() + " Brick(s) (<a>customize</a>)"); + validateForm(); + } + } + }); + } + }); + } + + 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("Bricks: "); + } + + 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()); + + VOLUME_TYPE[] volumeTypes = new VOLUME_TYPE[3]; + volumeTypes[0] = VOLUME_TYPE.DISTRIBUTE; + volumeTypes[1] = VOLUME_TYPE.REPLICATE; + volumeTypes[2] = VOLUME_TYPE.STRIPE; + + typeComboViewer.setInput(volumeTypes); + typeCombo.select(0); // default type = Plain Distribute + typeComboViewer.setLabelProvider(new LabelProvider() { + @Override + public String getText(Object element) { + return Volume.getVolumeTypeStr((VOLUME_TYPE)element); + } + }); + typeComboViewer.addSelectionChangedListener(new ISelectionChangedListener() { + @Override + public void selectionChanged(SelectionChangedEvent event) { + validateForm(); + } + }); + } + + 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.setTextLimit(32); + 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() { + volume.setName(txtName.getText()); + + IStructuredSelection selection = (IStructuredSelection)typeComboViewer.getSelection(); + volume.setVolumeType((VOLUME_TYPE)selection.getFirstElement()); + volume.setReplicaCount(Volume.DEFAULT_REPLICA_COUNT); + volume.setStripeCount(Volume.DEFAULT_STRIPE_COUNT); + + volume.setTransportType(TRANSPORT_TYPE.ETHERNET); // Support only for Ethernet + Set<NAS_PROTOCOL> nasProtocols = new HashSet<Volume.NAS_PROTOCOL>(); + nasProtocols.add(NAS_PROTOCOL.GLUSTERFS); + nasProtocols.add(NAS_PROTOCOL.NFS); + + volume.setAccessControlList(txtAccessControl.getText()); + + if (btnNfs.getSelection()) { + volume.enableNFS(); + } else { + volume.disableNFS(); + } + + if (btnCIFS.getSelection()) { + volume.enableCifs(); + volume.setCifsUsers(Arrays.asList(txtCifsUsers.getText().split(","))); + } else { + volume.disableCifs(); + } + + addVolumeBricks(); + return volume; + } + + private void addVolumeBricks() { + // first clear existing bricks, if any + volume.getBricks().clear(); + + for (Device device : selectedDevices) { + Brick brick = new Brick(device.getServerName(), BRICK_STATUS.ONLINE, device.getMountPoint() + "/" + + volume.getName()); + volume.addBrick(brick); + } + } + + public Boolean startVolumeAfterCreation() { + return btnStartVolume.getSelection(); + } + + 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(); + validateCifsUsers(); + validateAccessControl(); + validateDisks(); + } + + private void validateDisks() { + int diskCount = selectedDevices.size(); + + if(diskCount < 1) { + setError("At least one brick must be selected!"); + } + + VOLUME_TYPE volumeType = (VOLUME_TYPE) ((IStructuredSelection) typeComboViewer + .getSelection()).getFirstElement(); + if ((volumeType == VOLUME_TYPE.DISTRIBUTED_REPLICATE || volumeType == VOLUME_TYPE.REPLICATE ) && diskCount % 2 != 0) { + setError("Mirror type volume requires bricks in multiples of two"); + } else if ((volumeType == VOLUME_TYPE.DISTRIBUTED_STRIPE || volumeType == VOLUME_TYPE.STRIPE) && diskCount % 4 != 0) { + setError("Stripe type volume requires bricks 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("Invalid IP address/Host name [" + ValidationUtil.getInvalidIpOrHostname(accessControl) + + "]. Please enter a valid value!"); + } + } + + + private void validateCifsUsers() { + if (btnCIFS.getSelection()) { + String cifsUserList = txtCifsUsers.getText().trim(); + if (cifsUserList.length() == 0) { + setError("Please enter cifs user name"); + return; + } + } + } + + private void validateVolumeName() { + String volumeName = txtName.getText().trim(); + String volumeNameToken = "^[a-zA-Z][a-zA-Z0-9\\-]*"; + + if (volumeName.length() > 0) { + linkCustomize.setEnabled(true); + } + + if(volumeName.length() == 0) { + setError("Please enter Volume Name"); + linkCustomize.setEnabled(false); + } + + 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/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/CreateVolumeWizard.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/CreateVolumeWizard.java new file mode 100644 index 00000000..6aa81b7f --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/CreateVolumeWizard.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.dialogs; + +import java.util.List; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.wizard.Wizard; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.utils.GlusterLogger; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.Volume.VOLUME_STATUS; +import org.gluster.storage.management.core.model.Volume.VOLUME_TYPE; + + +public class CreateVolumeWizard extends Wizard { + private static final String title = "Gluster Management Console - Create Volume"; + private CreateVolumePage1 page; + + public CreateVolumeWizard() { + setWindowTitle(title); + setHelpAvailable(false); // TODO: Introduce wizard help + } + + @Override + public void addPages() { + page = new CreateVolumePage1(); + addPage(page); + } + + @Override + public boolean performFinish() { + Volume newVolume = page.getVolume(); + VolumesClient volumesClient = new VolumesClient(); + + try { + List<String> servers = GlusterDataModelManager.getInstance().getOfflineServers(); + // One or more servers are offline, Show warning if cifs is enabled + if (newVolume.isCifsEnable() && servers != null && servers.size() > 0) { + Integer userAction = new MessageDialog(getShell(), "Create Volume", GUIHelper.getInstance().getImage( + IImageKeys.VOLUME_16x16), + "Performing CIFS updates when one or more servers are offline can trigger " + + "inconsistent behavior for CIFS accesses in the cluster." + CoreConstants.NEWLINE + + "Are you sure you want to continue?", MessageDialog.QUESTION, new String[] { "No", + "Yes" }, -1).open(); + if (userAction != 1) { + return false; // To stay on the create dialog + } + } + + volumesClient.createVolume(newVolume); + + // Set proper volume type before assign to model + VOLUME_TYPE volumetype = newVolume.getVolumeType(); + if (volumetype == VOLUME_TYPE.REPLICATE && newVolume.getBricks().size() > newVolume.getReplicaCount()) { + newVolume.setVolumeType(VOLUME_TYPE.DISTRIBUTED_REPLICATE); + } else if (volumetype == VOLUME_TYPE.STRIPE && newVolume.getBricks().size() > newVolume.getStripeCount()) { + newVolume.setVolumeType(VOLUME_TYPE.DISTRIBUTED_STRIPE); + } + + handleSuccess(newVolume, volumesClient); + } catch (Exception e) { + String errMsg = e.getMessage(); + // the error could be in to post-volume-create processing. check if this is the case. + if (volumesClient.volumeExists(newVolume.getName())) { + handlePartSuccess(newVolume, volumesClient, errMsg); + } else { + MessageDialog.openError(getShell(), title, "Volume creation failed! Error: " + errMsg); + return false; + } + } + + return true; + } + + public void handleSuccess(Volume newVolume, VolumesClient volumesClient) { + String message = "Volume created successfully!"; + newVolume.setStatus(VOLUME_STATUS.OFFLINE); + boolean warning = false; + if (page.startVolumeAfterCreation()) { + try { + volumesClient.startVolume(newVolume.getName(), false); + newVolume.setStatus(VOLUME_STATUS.ONLINE); + message = "Volume created and started successfully!"; + } catch(Exception e) { + message = "Volume created successfuly, but couldn't be started. Error: " + e.getMessage(); + warning = true; + } + } + + // update the model + GlusterDataModelManager.getInstance().addVolume(newVolume); + if (warning) { + MessageDialog.openWarning(getShell(), title, message); + } else { + MessageDialog.openInformation(getShell(), title, message); + } + } + + public void handlePartSuccess(Volume newVolume, VolumesClient volumesClient, String errMsg) { + // volume exists. error was in post-volume-create + newVolume.setStatus(VOLUME_STATUS.OFFLINE); + boolean error = false; + String message1 = null; + if (page.startVolumeAfterCreation()) { + if (MessageDialog.openConfirm(getShell(), title, "Volume created, but following error(s) occured: " + + errMsg + CoreConstants.NEWLINE + CoreConstants.NEWLINE + + "Do you still want to start the volume [" + newVolume.getName() + "]?")) { + try { + volumesClient.startVolume(newVolume.getName(), false); + newVolume.setStatus(VOLUME_STATUS.ONLINE); + message1 = "Volume [" + newVolume.getName() + "] started successfully!"; // Only start operation + } catch(Exception e1) { + message1 = "Volume couldn't be started. Error: " + e1.getMessage(); + error = true; + } + } + + if (error) { + MessageDialog.openWarning(getShell(), title, message1); + } else if (message1.trim().length() > 0) { + MessageDialog.openInformation(getShell(), title, message1); + } + } else { // Start volume is not checked + MessageDialog.openWarning(getShell(), title, + "Volume created, but following error(s) occured: " + errMsg); + } + + // Fetching actual volume info (because of partial success) + Volume volume = newVolume; + try { + volume = volumesClient.getVolume(newVolume.getName()); + }catch (Exception e) { + GlusterLogger.getInstance().error("Fetching volume details failed:" + e.getMessage()); + } + GlusterDataModelManager.getInstance().addVolume(volume); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/GlusterSupportDialog.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/GlusterSupportDialog.java new file mode 100644 index 00000000..6eb414f5 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/GlusterSupportDialog.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.dialogs; + +import java.net.MalformedURLException; +import java.net.URL; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.forms.FormDialog; +import org.eclipse.ui.forms.IManagedForm; +import org.eclipse.ui.forms.events.HyperlinkAdapter; +import org.eclipse.ui.forms.events.HyperlinkEvent; +import org.eclipse.ui.forms.widgets.FormText; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.gluster.storage.management.console.utils.GUIHelper; + + +public class GlusterSupportDialog extends FormDialog { + + private final GUIHelper guiHelper = GUIHelper.getInstance(); + private FormToolkit toolkit; + private ScrolledForm form; + private Composite parent; + + public GlusterSupportDialog(Shell shell) { + super(shell); + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + this.parent = newShell; + newShell.setText("Gluster Management Console - Support Information"); + } + + protected void createFormContent(IManagedForm mform) { + form = mform.getForm(); + toolkit = mform.getToolkit(); + form.getBody().setLayout(new GridLayout()); + createSections(); + } + + private void createSections() { + contactGlusterSupportSection(); + commingSoonSection(); + form.layout(); + form.getParent().layout(); + } + + + private void contactGlusterSupportSection() { + Composite section = guiHelper.createSection(form, toolkit, "Contact Gluster Support", null, 1, false); + + FormText formText = toolkit.createFormText(section, false); + toolkit.createLabel(section, "Call 1-800-805-5215", SWT.NONE); + toolkit.createLabel(section, "or", SWT.NONE); + toolkit.createLabel(section, "Email:support@gluster.com", SWT.NONE); +// String supportInfo = "<form>" + +// "Call 1-800-805-5215<br />" + +// "or<br />" + +// "Email:support@gluster.com" + +// "</form>"; +// formText.setText(supportInfo, true, true); + GridData layoutData = new GridData(); + layoutData.widthHint = 730; + layoutData.grabExcessHorizontalSpace = true; + formText.setLayoutData(layoutData); + } + + private void commingSoonSection() { + Composite section = guiHelper.createSection(form, toolkit, "Coming Soon", null, 7, false); + FormText formText = toolkit.createFormText(section, true); + String commingSoonInfo = "<form>" + + "The following features of GlusterFS will soon be supported in upcoming releases of Gluster Management Console " + + "<li>Geo-replication</li>" + + "<li>Directory Quota</li>" + + "<li>Top and Profile</li>" + + "<li>POSIX ACLs Support</li><br />" + + "More information about these features can be found at<br /> " + + "http://www.gluster.com/community/documentation/index.php/Gluster_3.2:_What_is_New_in_this_Release" + + "</form>"; + formText.setText(commingSoonInfo, true, true); + GridData layoutData = new GridData(); + layoutData.widthHint = 700; + layoutData.grabExcessHorizontalSpace = true; + formText.setLayoutData(layoutData); + formText.addHyperlinkListener(new HyperlinkAdapter() { + public void linkActivated(HyperlinkEvent e) { + System.out.println("Link activated: " + e.getHref()); + try { + PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser() + .openURL(new URL((String) e.getHref())); + } catch (PartInitException e1) { + e1.printStackTrace(); + } catch (MalformedURLException e1) { + e1.printStackTrace(); + } + } + }); + } + + @Override + protected Control createButtonBar(Composite parent) { + return null; + } + /** + * Overriding to make sure that the dialog is centered in screen + */ + @Override + protected void initializeBounds() { + super.initializeBounds(); + guiHelper.centerShellInScreen(getShell()); + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/InitDiskDialog.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/InitDiskDialog.java new file mode 100644 index 00000000..4d06fd3a --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/InitDiskDialog.java @@ -0,0 +1,212 @@ +/******************************************************************************* + * + * InitializeDiskTypeSelection.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 org.gluster.storage.management.console.dialogs; + +import java.util.List; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.forms.events.HyperlinkAdapter; +import org.eclipse.ui.forms.events.HyperlinkEvent; +import org.eclipse.ui.forms.widgets.Hyperlink; +import org.gluster.storage.management.console.utils.GUIHelper; + + +public class InitDiskDialog extends Dialog { + + private Combo formatTypeCombo = null; + private final GUIHelper guiHelper = GUIHelper.getInstance(); + private Composite initializeDiskTypeComposite; + private Composite composite; + private String fsType; + private String mountPoint; + private Text mountPointText; + private String deviceName; + private List<String> fsTypes; + private static final String DEFAULT_MOUNT_POINT = "/export/"; + + public InitDiskDialog(Shell parentShell, String deviceName, List<String> fsTypes) { + super(parentShell); + this.fsTypes = fsTypes; + this.deviceName = deviceName; + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + + newShell.setText("Gluster Management Console - Select File System Type"); + addEscapeListener(newShell); + } + + private void addEscapeListener(Shell shell) { + shell.addTraverseListener(new TraverseListener() { + + @Override + public void keyTraversed(TraverseEvent e) { + if (e.keyCode == SWT.ESC) { + cancelPressed(); + } + } + }); + } + + @Override + protected Control createDialogArea(Composite parent) { + // Makes sure that child composites inherit the same background + parent.setBackgroundMode(SWT.INHERIT_FORCE); + + composite = (Composite) super.createDialogArea(parent); + configureDialogLayout(composite); + createComposite(composite); + return composite; + } + + private void configureDialogLayout(Composite composite) { + GridLayout layout = (GridLayout) composite.getLayout(); + layout.numColumns = 3; + layout.marginLeft = 20; + layout.marginRight = 20; + layout.marginTop = 20; + layout.horizontalSpacing = 20; + layout.verticalSpacing = 20; + } + + private void createComposite(Composite composite) { + initializeDiskTypeComposite = new Composite(composite, SWT.NONE); + GridLayout layout = new GridLayout(3, false); + initializeDiskTypeComposite.setLayout(layout); + + createLabel(initializeDiskTypeComposite, "File system "); + createFormatTypeCombo(initializeDiskTypeComposite); + createLabel(initializeDiskTypeComposite, "Mount point "); + createMountPointText(initializeDiskTypeComposite); + createChangeLink(initializeDiskTypeComposite); + } + + private void createLabel(Composite composite, String labelText) { + Label formatTypeLabel = new Label(composite, SWT.NONE); + formatTypeLabel.setText(labelText); + formatTypeLabel.setLayoutData(new GridData(GridData.BEGINNING, GridData.CENTER, false, false)); + } + + private void createFormatTypeCombo(Composite composite) { + formatTypeCombo = new Combo(composite, SWT.READ_ONLY); + formatTypeCombo.setItems(fsTypes.toArray(new String[0])); + formatTypeCombo.select(0); + new Label(composite, SWT.NONE); + } + + private void createMountPointText(Composite container) { + mountPointText = new Text(container, SWT.BORDER); + GridData txtNameData = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1); + txtNameData.widthHint = 400; + mountPointText.setTextLimit(100); + mountPointText.setLayoutData(txtNameData); + mountPointText.setText(DEFAULT_MOUNT_POINT + deviceName); + mountPointText.setEnabled(false); + mountPointText.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + validateMountPoint(); + } + }); + } + + private void createChangeLink(Composite container) { + final Hyperlink changeLink = new Hyperlink(container, SWT.UNDERLINE_SINGLE); + changeLink.setText("change"); + changeLink.setUnderlined(true); + changeLink.setForeground(new Color(Display.getDefault(), 0, 0, 255)); + + changeLink.addHyperlinkListener(new HyperlinkAdapter() { + @Override + public void linkActivated(HyperlinkEvent e) { + if (!mountPointText.isEnabled()) { + changeLink.setVisible(false); + mountPointText.setEnabled(true); + } + } + }); + } + + @Override + protected void okPressed() { + fsType = formatTypeCombo.getText().trim(); + mountPoint = mountPointText.getText().trim(); + if (validateForm()) { + super.okPressed(); + } else { + MessageDialog.openError(getShell(), "Initialize Disk - Error", "Please enter a valid mount point"); + } + } + + @Override + public void cancelPressed() { + super.cancelPressed(); + } + + private boolean validateMountPoint() { + String mountPoint = mountPointText.getText().trim(); + if (mountPoint.isEmpty()) { + return false; + } + return mountPoint.matches("^/.+"); + } + + private boolean validateForm() { + return (!formatTypeCombo.getText().trim().isEmpty() && validateMountPoint()); + } + + /** + * Overriding to make sure that the dialog is centered in screen + */ + @Override + protected void initializeBounds() { + super.initializeBounds(); + + guiHelper.centerShellInScreen(getShell()); + } + + public String getFSType() { + return fsType; + } + + public String getMountPoint() { + return mountPoint; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/LoginDialog.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/LoginDialog.java new file mode 100644 index 00000000..ccbea44f --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/LoginDialog.java @@ -0,0 +1,359 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.dialogs; + +import java.lang.reflect.InvocationTargetException; + +import org.eclipse.core.databinding.DataBindingContext; +import org.eclipse.core.databinding.UpdateValueStrategy; +import org.eclipse.core.databinding.beans.PojoProperties; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.databinding.swt.SWTObservables; +import org.eclipse.jface.databinding.swt.WidgetProperties; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +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.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.gluster.storage.management.client.ClustersClient; +import org.gluster.storage.management.client.UsersClient; +import org.gluster.storage.management.console.Activator; +import org.gluster.storage.management.console.ConsoleConstants; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.console.dialogs.ClusterSelectionDialog.CLUSTER_MODE; +import org.gluster.storage.management.console.preferences.PreferenceConstants; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.validators.StringRequiredValidator; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.ConnectionDetails; + + +/** + * Login dialog, which prompts for the user's account info, and has Login and Cancel buttons. + */ +public class LoginDialog extends Dialog { + public static final int RETURN_CODE_ERROR = 2; + private Text userIdText = null; + private Text passwordText = null; + private Button okButton; + + private final ConnectionDetails connectionDetails = new ConnectionDetails("gluster", ""); + private final GUIHelper guiHelper = GUIHelper.getInstance(); + private Composite composite; + + public LoginDialog(Shell parentShell) { + super(parentShell); + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + + newShell.setText("Gluster Management Console"); + addEscapeListener(newShell); + } + + private void addEscapeListener(Shell shell) { + shell.addTraverseListener(new TraverseListener() { + + @Override + public void keyTraversed(TraverseEvent e) { + if (e.keyCode == SWT.ESC) { + cancelPressed(); + } + } + }); + } + + private void createUserIdLabel(Composite composite) { + Label userIdLabel = new Label(composite, SWT.NONE); + userIdLabel.setText("&User ID:"); + userIdLabel.setLayoutData(new GridData(GridData.END, GridData.CENTER, false, false)); + } + + private void createUserIdText(Composite composite) { + userIdText = new Text(composite, SWT.BORDER); + userIdText.setText("gluster"); + userIdText.setEnabled(false); + + GridData layoutData = new GridData(convertWidthInCharsToPixels(32), 15); + userIdText.setLayoutData(layoutData); + } + + private void createPasswordLabel(Composite composite) { + Label passwordLabel = new Label(composite, SWT.NONE); + passwordLabel.setText("&Password:"); + passwordLabel.setLayoutData(new GridData(GridData.END, GridData.CENTER, false, false)); + } + + private void createPasswordText(Composite composite) { + passwordText = new Text(composite, SWT.BORDER | SWT.PASSWORD); + + GridData layoutData = new GridData(convertWidthInCharsToPixels(32), 15); + passwordText.setLayoutData(layoutData); + } + + private void configureDialogLayout(Composite composite) { + GridLayout layout = (GridLayout) composite.getLayout(); + layout.numColumns = 2; + layout.marginLeft = 10; + layout.marginRight = 10; + layout.marginTop = 30; + layout.marginBottom = 0; + layout.horizontalSpacing = 20; + layout.verticalSpacing = 10; + } + + private void configureButtonCompositeLayout(Composite composite) { + GridLayout layout = (GridLayout) composite.getLayout(); + layout.marginLeft = 20; + layout.marginRight = 20; + layout.marginTop = 0; + layout.horizontalSpacing = 10; + layout.verticalSpacing = 10; + } + + /** + * Overriding to make sure that the dialog is centered in screen + */ + @Override + protected void initializeBounds() { + super.initializeBounds(); + + getShell().setSize(390, 240); + guiHelper.centerShellInScreen(getShell()); + } + + @Override + protected Control createDialogArea(Composite parent) { + parent.setBackgroundImage(guiHelper.getImage(IImageKeys.DIALOG_SPLASH_IMAGE)); + // Makes sure that child composites inherit the same background + parent.setBackgroundMode(SWT.INHERIT_FORCE); + + composite = (Composite) super.createDialogArea(parent); + configureDialogLayout(composite); + GridData layoutData = new GridData(390, 110); + composite.setLayoutData(layoutData); + + createUserIdLabel(composite); + createUserIdText(composite); + + createPasswordLabel(composite); + createPasswordText(composite); + + setupAutoLoginIfRequired(); + return composite; + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + configureButtonCompositeLayout(parent); + + GridData layoutData = new GridData(390, 130); + layoutData.horizontalAlignment = SWT.CENTER; + layoutData.verticalAlignment = SWT.TOP; + layoutData.grabExcessHorizontalSpace = true; + parent.setLayoutData(layoutData); + + layoutData = new GridData(); + layoutData.widthHint = 70; + layoutData.horizontalAlignment = SWT.RIGHT; + layoutData.grabExcessHorizontalSpace = true; + + Button cancelButton = createButton(parent, IDialogConstants.CANCEL_ID, "&Cancel", false); + cancelButton.setLayoutData(layoutData); + + layoutData = new GridData(); + layoutData.widthHint = 70; + layoutData.horizontalAlignment = SWT.LEFT; + layoutData.grabExcessHorizontalSpace = true; + + okButton = createButton(parent, IDialogConstants.OK_ID, "&Login", true); + okButton.setLayoutData(layoutData); + + setupDataBinding(); + } + + private void setupAutoLoginIfRequired() { + final String password = System.getProperty(ConsoleConstants.PROPERTY_AUTO_LOGIN_PASSWORD, null); + if (password == null) { + return; + } + getShell().addShellListener(new ShellAdapter() { + @Override + public void shellActivated(ShellEvent e) { + super.shellActivated(e); + + if (passwordText.getText().isEmpty()) { + // Check whether the password has been passed as system parameter. This can be used for avoiding + // human intervention on login dialog while running SWTBot automated tests. + passwordText.setText(password); + okPressed(); + } + } + }); + } + + /** + * Sets up data binding between the text fields and the connection details object. Also attaches a "string required" + * validator to the "password" text field. This validator is configured to do the following on validation failure<br> + * <li>show an ERROR decorator</li><li>disable the "Login" button + */ + private void setupDataBinding() { + DataBindingContext dataBindingContext = new DataBindingContext(SWTObservables.getRealm(Display.getCurrent())); + UpdateValueStrategy passwordBindingStrategy = new UpdateValueStrategy(UpdateValueStrategy.POLICY_UPDATE); + + // The Validator shows error decoration and disables OK button on + // validation failure + passwordBindingStrategy.setBeforeSetValidator(new StringRequiredValidator("Please enter password!", guiHelper + .createErrorDecoration(passwordText), okButton)); + + dataBindingContext.bindValue(WidgetProperties.text(SWT.Modify).observe(passwordText), + PojoProperties.value("password").observe(connectionDetails), passwordBindingStrategy, + passwordBindingStrategy); + + UpdateValueStrategy userIdBindingStrategy = new UpdateValueStrategy(UpdateValueStrategy.POLICY_UPDATE); + dataBindingContext + .bindValue(WidgetProperties.text(SWT.Modify).observe(userIdText), PojoProperties.value("userId") + .observe(connectionDetails), userIdBindingStrategy, userIdBindingStrategy); + } + + protected void okPressed() { + String user = connectionDetails.getUserId(); + String password = connectionDetails.getPassword(); + + UsersClient usersClient = new UsersClient(); + try { + usersClient.authenticate(user, password); + } catch(Exception e) { + MessageDialog.openError(getShell(), "Authentication Failed", e.getMessage()); + setReturnCode(RETURN_CODE_ERROR); + return; + } + + // authentication successful. close the login dialog and open the next one. + close(); + + IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore(); + boolean showClusterSelectionDialog = preferenceStore.getBoolean(PreferenceConstants.P_SHOW_CLUSTER_SELECTION_DIALOG); + + // If the password is default, Let user to change the password + if (password.equalsIgnoreCase(CoreConstants.DEFAULT_PASSWORD)) { + String oldSecurityTokeString = GlusterDataModelManager.getInstance().getSecurityToken(); + ChangePasswordDialog dialog = new ChangePasswordDialog(getShell()); + + if (dialog.open() == Dialog.CANCEL) { + MessageDialog.openError(getShell(), "Change password Cancelled", + "Password must be changed on first login!" + CoreConstants.NEWLINE + "Application will close."); + cancelPressed(); + return; + } + + // after first login, cluster selection dialog must be shown. + showClusterSelectionDialog = true; + } + + ClustersClient clustersClient = new ClustersClient(); + + CLUSTER_MODE mode; + String clusterName = null; + if (!showClusterSelectionDialog) { + clusterName = preferenceStore.getString(PreferenceConstants.P_DEFAULT_CLUSTER_NAME); + if (clusterName == null || clusterName.isEmpty()) { + // Cluster name not available in preferences. Hence we must show the cluster selection dialog. + showClusterSelectionDialog = true; + } else { + mode = CLUSTER_MODE.SELECT; + } + } + + String serverName = null; + + if (showClusterSelectionDialog) { + ClusterSelectionDialog clusterDialog = new ClusterSelectionDialog(getParentShell(), + clustersClient.getClusterNames()); + int userAction = clusterDialog.open(); + if (userAction == Window.CANCEL) { + MessageDialog.openError(getShell(), "Login Cancelled", + "User cancelled login at cluster selection. Application will close!"); + cancelPressed(); + return; + } + mode = clusterDialog.getClusterMode(); + clusterName = clusterDialog.getClusterName(); + serverName = clusterDialog.getServerName(); + } else { + mode = CLUSTER_MODE.SELECT; + } + + try { + createOrRegisterCluster(clustersClient, clusterName, serverName, mode); + + final String clusterName1 = clusterName; + new ProgressMonitorDialog(getShell()).run(true, false, new IRunnableWithProgress() { + + @Override + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + GlusterDataModelManager.getInstance().initializeModel(clusterName1, monitor); + } + }); + super.okPressed(); + } catch (Exception e) { + String errMsg; + if(e instanceof InvocationTargetException) { + errMsg = ((InvocationTargetException) e).getTargetException().getMessage(); + } else { + errMsg = e.getMessage(); + } + setReturnCode(RETURN_CODE_ERROR); + MessageDialog.openError(getShell(), "Gluster Management Console", errMsg); + } + } + + public void createOrRegisterCluster(ClustersClient clustersClient, String clusterName, String serverName, + CLUSTER_MODE mode) { + switch (mode) { + case SELECT: + return; + case CREATE: + clustersClient.createCluster(clusterName); + break; + case REGISTER: + clustersClient.registerCluster(clusterName, serverName); + break; + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/MigrateBrickPage1.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/MigrateBrickPage1.java new file mode 100644 index 00000000..80265949 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/MigrateBrickPage1.java @@ -0,0 +1,302 @@ +/** + * MigrateBrickPage1.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 org.gluster.storage.management.console.dialogs; + +import java.util.List; + +import org.eclipse.jface.layout.TableColumnLayout; +import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +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.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.TableItem; +import org.eclipse.swt.widgets.Text; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.TableLabelProviderAdapter; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Device; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.utils.NumberUtil; + + +public class MigrateBrickPage1 extends WizardPage { + private static final String PAGE_NAME = "migrate.disk.page.1"; + + private enum DISK_TABLE_COLUMN_INDICES { + SERVER, BRICK_DIRECTORY, FREE_SPACE, TOTAL_SPACE + } + + private static final String[] DISK_TABLE_COLUMN_NAMES = { "Server", "Brick Directory", "Free Space (GB)", "Total Space (GB)" }; + + private Volume volume; + private Brick fromBrick; + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + + private TableViewer tableViewerTo; + + private TableViewer tableViewerFrom; + + private Button autoCompleteCheckbox; + + private ITableLabelProvider getBrickLabelProvider(final String volumeName) { + return new TableLabelProviderAdapter() { + @Override + public String getColumnText(Object element, int columnIndex) { + if (!(element instanceof Brick)) { + return null; + } + Brick brick = (Brick) element; + GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + Device device = modelManager.getDeviceForBrickDir(brick); + // convert MB to GB + String freeSpace = (device == null ? "NA" : NumberUtil.formatNumber(device.getFreeSpace() / 1024)); + String totalSpace = (device == null ? "NA" : NumberUtil.formatNumber(device.getSpace() / 1024)); + return (columnIndex == DISK_TABLE_COLUMN_INDICES.SERVER.ordinal() ? brick.getServerName() + : columnIndex == DISK_TABLE_COLUMN_INDICES.BRICK_DIRECTORY.ordinal() ? brick.getBrickDirectory() + : columnIndex == DISK_TABLE_COLUMN_INDICES.FREE_SPACE.ordinal() ? freeSpace + : columnIndex == DISK_TABLE_COLUMN_INDICES.TOTAL_SPACE.ordinal() ? totalSpace + : "Invalid"); + } + }; + } + + private ITableLabelProvider getDiskLabelProvider(final String volumeName) { + return new TableLabelProviderAdapter() { + + @Override + public String getColumnText(Object element, int columnIndex) { + if (!(element instanceof Device)) { + return null; + } + Device device = (Device) element; + return (columnIndex == DISK_TABLE_COLUMN_INDICES.SERVER.ordinal() ? device.getServerName() + : columnIndex == DISK_TABLE_COLUMN_INDICES.BRICK_DIRECTORY.ordinal() ? device.getMountPoint() + "/" + volumeName + : columnIndex == DISK_TABLE_COLUMN_INDICES.FREE_SPACE.ordinal() ? NumberUtil.formatNumber(device.getFreeSpace() / 1024 ) /* Coverted to GB */ + : columnIndex == DISK_TABLE_COLUMN_INDICES.TOTAL_SPACE.ordinal() ? NumberUtil.formatNumber(device.getSpace() / 1024) : "Invalid"); + } + }; + } + + private void setupDiskTable(Composite parent, Table table) { + table.setHeaderVisible(true); + table.setLinesVisible(false); + + TableColumnLayout tableColumnLayout = guiHelper.createTableColumnLayout(table, DISK_TABLE_COLUMN_NAMES); + parent.setLayout(tableColumnLayout); + + setColumnProperties(table, DISK_TABLE_COLUMN_INDICES.SERVER, SWT.CENTER, 100); + setColumnProperties(table, DISK_TABLE_COLUMN_INDICES.BRICK_DIRECTORY, SWT.CENTER, 100); + setColumnProperties(table, DISK_TABLE_COLUMN_INDICES.FREE_SPACE, SWT.CENTER, 90); + setColumnProperties(table, DISK_TABLE_COLUMN_INDICES.TOTAL_SPACE, SWT.CENTER, 90); + } + + /** + * 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, DISK_TABLE_COLUMN_INDICES columnIndex, int alignment, int weight) { + TableColumn column = table.getColumn(columnIndex.ordinal()); + column.setAlignment(alignment); + + TableColumnLayout tableColumnLayout = (TableColumnLayout) table.getParent().getLayout(); + tableColumnLayout.setColumnData(column, new ColumnWeightData(weight)); + } + + /** + * Create the wizard. + */ + public MigrateBrickPage1(Volume volume, Brick brick) { + super(PAGE_NAME); + this.volume = volume; + this.fromBrick = brick; + setTitle("Migrate Brick [" + volume.getName() + "]"); + setPageDescription(fromBrick.getQualifiedName(), null); + setPageComplete(false); + } + + private void setPageDescription(String source, String target) { + if (source == null) { + source = ""; + } + if (target == null) { + target = ""; + } + setDescription("Migrate data from \"" + source + "\" to \"" + target + "\""); + } + + private Object getSelectedItem(TableViewer tableViewer) { + TableItem[] selectedItems = tableViewer.getTable().getSelection(); + Object selectedDevice = null; + for (TableItem item : selectedItems) { + selectedDevice = item.getData(); + } + return selectedDevice; + } + + private void setupPageLayout(Composite container) { + final GridLayout layout = new GridLayout(2, false); + layout.verticalSpacing = 10; + layout.horizontalSpacing = 10; + layout.marginTop = 10; + layout.marginLeft = 10; + layout.marginRight = 10; + container.setLayout(layout); + } + + private Composite createTableViewerComposite(Composite parent) { + Composite tableViewerComposite = new Composite(parent, SWT.NONE); + tableViewerComposite.setLayout(new FillLayout(SWT.HORIZONTAL)); + tableViewerComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + return tableViewerComposite; + } + + public String getSourceBrickDir() { + Brick sourceBrick = (Brick)getSelectedItem(tableViewerFrom); + return sourceBrick.getQualifiedName(); + } + + public String getTargetBrickDir() { + Device targetDevice = (Device)getSelectedItem(tableViewerTo); + if (targetDevice == null) { + return ""; + } + return targetDevice.getQualifiedBrickName(volume.getName()); + } + + public Boolean getAutoCommitSelection() { + return autoCompleteCheckbox.getSelection(); + } + + /** + * Create contents of the wizard. + * + * @param parent + */ + public void createControl(Composite parent) { + Composite container = new Composite(parent, SWT.NULL); + setControl(container); + + setupPageLayout(container); + + GridData labelLayoutData = new GridData(SWT.LEFT, SWT.BOTTOM, true, false); + labelLayoutData.minimumWidth = 100; + labelLayoutData.verticalAlignment = SWT.BOTTOM; + //labelLayoutData.verticalIndent = 10; + + Label lblFromDisk = new Label(container, SWT.NONE); + lblFromDisk.setText("From Brick:"); + lblFromDisk.setLayoutData(labelLayoutData); + Label lblToDisk = new Label(container, SWT.NONE); + lblToDisk.setText("To Brick:"); + lblToDisk.setLayoutData(labelLayoutData); + + Text txtFilterFrom = guiHelper.createFilterText(container); + Text txtFilterTo = guiHelper.createFilterText(container); + + GlusterDataModelManager glusterDataModelManager = GlusterDataModelManager.getInstance(); + List<Brick> fromBricks = volume.getBricks(); + List<Device> toDevices = glusterDataModelManager.getReadyDevicesOfAllServersExcluding(glusterDataModelManager + .getDevicesOfVolume(volume)); + + tableViewerFrom = createTableViewer(container, getBrickLabelProvider(volume.getName()), fromBricks, + txtFilterFrom); + + if(fromBrick != null) { + setFromDisk(tableViewerFrom, fromBrick); + } + tableViewerTo = createTableViewer(container, getDiskLabelProvider(volume.getName()), toDevices, txtFilterTo); + tableViewerTo.setSelection(new StructuredSelection(fromBrick)); + + // Auto commit selection field + Composite autoCommitContainer = new Composite(container, SWT.NONE); + GridData data = new GridData(); + data.horizontalSpan = 2; + autoCommitContainer.setLayoutData(data); + autoCompleteCheckbox = new Button(autoCommitContainer, SWT.CHECK); + autoCompleteCheckbox.setSelection(true); + Label lblAutoComplete = new Label(autoCommitContainer, SWT.NONE); + lblAutoComplete.setText("Auto commit on migration complete"); + autoCommitContainer.setLayout( container.getLayout()); + } + + private void setFromDisk(TableViewer tableViewer, Brick brickToSelect) { + Table table = tableViewer.getTable(); + for (int i = 0; i < table.getItemCount(); i++) { + TableItem item = table.getItem(i); + if (item.getData() == brickToSelect) { + table.select(i); + return; + } + } + } + + private void refreshButtonStatus() { + if(tableViewerFrom.getSelection().isEmpty() || tableViewerTo.getSelection().isEmpty()) { + setPageComplete(false); + } else { + setPageComplete(true); + } + } + + private <T> TableViewer createTableViewer(Composite container, ITableLabelProvider diskLabelProvider, + List<T> bricks, Text txtFilterText) { + Composite tableViewerComposite = createTableViewerComposite(container); + + TableViewer tableViewer = new TableViewer(tableViewerComposite, SWT.SINGLE); + tableViewer.setContentProvider(new ArrayContentProvider()); + tableViewer.setLabelProvider(diskLabelProvider); + + setupDiskTable(tableViewerComposite, tableViewer.getTable()); + guiHelper.createFilter(tableViewer, txtFilterText, false); + + tableViewer.setInput(bricks.toArray()); + + tableViewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + refreshButtonStatus(); + setPageDescription(getSourceBrickDir(), getTargetBrickDir()); + } + }); + return tableViewer; + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/MigrateBrickWizard.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/MigrateBrickWizard.java new file mode 100644 index 00000000..f5cc0249 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/MigrateBrickWizard.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.dialogs; + +import java.net.URI; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.wizard.Wizard; +import org.gluster.storage.management.client.TasksClient; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Cluster; +import org.gluster.storage.management.core.model.Status; +import org.gluster.storage.management.core.model.TaskInfo; +import org.gluster.storage.management.core.model.TaskStatus; +import org.gluster.storage.management.core.model.Volume; + + +public class MigrateBrickWizard extends Wizard { + private Volume volume; + private Brick brick; + private MigrateBrickPage1 page; + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + private Cluster cluster = modelManager.getModel().getCluster(); + + public MigrateBrickWizard(Volume volume, Brick brick) { + setWindowTitle("Gluster Management Console - Migrate Brick [" + volume.getName() + "]"); + this.volume = volume; + this.brick = brick; + setHelpAvailable(false); // TODO: Introduce wizard help + } + + @Override + public void addPages() { + page = new MigrateBrickPage1(volume, brick); + addPage(page); + } + + @Override + public boolean performFinish() { + + String sourceDir = page.getSourceBrickDir(); + String targetDir = page.getTargetBrickDir(); + Boolean autoCommit = page.getAutoCommitSelection(); + VolumesClient volumesClient = new VolumesClient(); + String dialogTitle = "Brick migration"; + + try { + String reference = volume.getName() + "-" + sourceDir + "-" + targetDir; + TaskInfo existingTaskInfo = GlusterDataModelManager.getInstance().getTaskByReference(reference); + if (existingTaskInfo != null && existingTaskInfo.getStatus().getCode() != Status.STATUS_CODE_SUCCESS + && existingTaskInfo.getStatus().getCode() != Status.STATUS_CODE_FAILURE) { + MessageDialog.openInformation(getShell(), dialogTitle, "Volume brick [" + reference + + "] migration is already in progress! Try later."); + return true; + } + + URI uri = volumesClient.startMigration(volume.getName(), sourceDir, targetDir, autoCommit); + + // To get the object + TasksClient taskClient = new TasksClient(); + TaskInfo taskInfo = taskClient.getTaskInfo(uri); + if (taskInfo != null) { + // cluster.addTaskInfo(taskInfo); + String volumeName = taskInfo.getReference().split("#")[0]; + modelManager.addTask(taskInfo); + modelManager.refreshVolumeData(cluster.getVolume(volumeName)); + + // If auto commit selected and migration operation complete immediately, + if (taskInfo.getStatus().getCode() == TaskStatus.STATUS_CODE_SUCCESS) { + Volume oldVolume = cluster.getVolume(volumeName); + Volume newVolume = (new VolumesClient()).getVolume(volumeName); + + modelManager.volumeChanged(oldVolume, newVolume); + + MessageDialog.openInformation(getShell(), dialogTitle, "Brick migration completed successfully"); + return true; + } + } + MessageDialog.openInformation(getShell(), dialogTitle, "Brick migration started successfully"); + GUIHelper.getInstance().showTaskView(); + + } catch (Exception e) { + MessageDialog.openError(getShell(), dialogTitle, "Brick Migration failed! [" + e.getMessage() + "]"); + } + return true; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/SelectDisksDialog.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/SelectDisksDialog.java new file mode 100644 index 00000000..cc6eb83b --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/SelectDisksDialog.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.dialogs; + +import java.util.List; +import java.util.Set; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Device; + + +public class SelectDisksDialog extends Dialog { + + private BricksSelectionPage disksPage; + private List<Device> allDevices; + private List<Device> selectedDevices; + private String volumeName; + + /** + * Create the dialog. + * + * @param parentShell + */ + public SelectDisksDialog(Shell parentShell, List<Device> allDevices, List<Device> selectedDevices, String volumeName) { + super(parentShell); + setShellStyle(getShellStyle() | SWT.RESIZE); + this.allDevices = allDevices; + this.selectedDevices = selectedDevices; + this.volumeName = volumeName; + } + + /** + * Create contents of the dialog. + * + * @param parent + */ + @Override + protected Control createDialogArea(Composite parent) { + 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); + container.setLayoutData(containerLayoutData); + + getShell().setText("Create Volume - Select Bricks"); + + disksPage = new BricksSelectionPage(container, SWT.NONE, allDevices, selectedDevices, volumeName); + return container; + } + + /** + * Create contents of the button bar. + * + * @param parent + */ + @Override + protected void createButtonsForButtonBar(Composite parent) { + createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true); + createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); + } + + /** + * Return the initial size of the dialog. + */ + @Override + protected Point getInitialSize() { + return new Point(1024, 600); + } + + + @Override + protected void okPressed() { + if (this.getSelectedDevices().size() == 0) { + MessageDialog.openError(getShell(), "Select Brick(s)", "Please select atlease one brick"); + } else { + super.okPressed(); + } + } + + public List<Device> getSelectedDevices() { + return disksPage.getChosenDevices(); + } + + public Set<Brick> getSelectedBricks(String volumeName) { + return disksPage.getChosenBricks(volumeName); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/ServerAdditionDialog.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/ServerAdditionDialog.java new file mode 100644 index 00000000..99d056e6 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/dialogs/ServerAdditionDialog.java @@ -0,0 +1,197 @@ +/** + * ServerAdditionDialog.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 org.gluster.storage.management.console.dialogs; + +import java.net.URI; +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.jface.fieldassist.ControlDecoration; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +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.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.gluster.storage.management.client.GlusterServersClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.model.GlusterServer; + + +public class ServerAdditionDialog extends Dialog { + public static final int RETURN_CODE_ERROR = 2; + private Text serverName; + private Button addButton; + + private GUIHelper guiHelper = GUIHelper.getInstance(); + private ControlDecoration errDecoration; + + private Composite composite; + + public ServerAdditionDialog(Shell shell) { + super(shell); + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + + newShell.setText("Gluster Management Console - Add Server"); + addEscapeListener(newShell); + } + + private void addEscapeListener(Shell shell) { + shell.addTraverseListener(new TraverseListener() { + + @Override + public void keyTraversed(TraverseEvent e) { + if (e.keyCode == SWT.ESC) { + cancelPressed(); + } + } + }); + } + + /** + * Overriding to make sure that the dialog is centered in screen + */ + @Override + protected void initializeBounds() { + super.initializeBounds(); + + guiHelper.centerShellInScreen(getShell()); + } + + private void configureDialogLayout(Composite composite) { + GridLayout layout = (GridLayout) composite.getLayout(); + layout.numColumns = 2; + layout.marginLeft = 20; + layout.marginRight = 20; + layout.marginTop = 20; + layout.horizontalSpacing = 20; + layout.verticalSpacing = 20; + } + + // ------------------------------------------ + + private void createLabel(Composite composite, String label) { + Label passwordLabel = new Label(composite, SWT.NONE); + passwordLabel.setText(label); + passwordLabel.setLayoutData(new GridData(GridData.END, GridData.CENTER, false, false)); + } + + private Text createServerNameText(Composite composite) { + Text field = new Text(composite, SWT.BORDER ); + GridData layoutData = new GridData(SWT.FILL, GridData.FILL, true, false); + layoutData.widthHint = convertWidthInCharsToPixels(32); + field.setLayoutData(layoutData); + return field; + } + + @Override + protected Control createDialogArea(Composite parent) { + composite = (Composite) super.createDialogArea(parent); + configureDialogLayout(composite); + + createLabel(composite, "Server Name:"); + serverName = createServerNameText(composite); + errDecoration = guiHelper.createErrorDecoration(serverName); + + createListeners(); + + return composite; + } + + private void createListeners() { + ModifyListener listener = new ModifyListener() { + + @Override + public void modifyText(ModifyEvent e) { + updateButtonStatus(); + } + }; + + serverName.addModifyListener(listener); + } + + private void updateButtonStatus() { + addButton.setEnabled(true); + errDecoration.hide(); + + if(!serverExists(serverName.getText())) { + addButton.setEnabled(false); + errDecoration.setDescriptionText("Server name already exists."); + errDecoration.show(); + } + + if(serverName.getText().isEmpty()) { + addButton.setEnabled(false); + errDecoration.setDescriptionText("Please enter server name!"); + errDecoration.show(); + } + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + addButton = createButton(parent, IDialogConstants.OK_ID, "&Add Server", true); + addButton.setEnabled(false); + createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); + } + + public Boolean serverExists(String serverName) { + List<GlusterServer> servers = GlusterDataModelManager.getInstance().getModel().getCluster().getServers(); + for (GlusterServer server : servers) { + if (server.getName().equalsIgnoreCase(serverName)) { + return false; + } + } + return true; + } + + protected void okPressed() { + GlusterServersClient serversClient = new GlusterServersClient(); + GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + + try { + String serverNameText = serverName.getText(); + URI newServerURI = serversClient.addServer(serverNameText); + + modelManager.addGlusterServer(serversClient.getGlusterServer(newServerURI)); + + MessageDialog + .openInformation(getShell(), "Add Server", "Server " + serverNameText + " added successfully!"); + } catch (Exception e) { + MessageDialog.openError(getShell(), "Server addition Failed", e.getMessage()); + setReturnCode(RETURN_CODE_ERROR); + } + this.close(); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/jobs/DataSyncJob.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/jobs/DataSyncJob.java new file mode 100644 index 00000000..ee4daafe --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/jobs/DataSyncJob.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.jobs; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.swt.widgets.Display; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.utils.GlusterLogger; +import org.gluster.storage.management.core.model.GlusterDataModel; + + +/** + * + */ +public class DataSyncJob extends Job { + private static final GlusterLogger logger = GlusterLogger.getInstance(); + + public DataSyncJob(String name) { + super(name); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + protected IStatus run(final IProgressMonitor monitor) { + final GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + + // fetch the latest model + final GlusterDataModel model = modelManager.fetchModel(monitor); + if(model == null) { + return Status.CANCEL_STATUS; + } + + monitor.beginTask("Notify views", 1); + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + modelManager.updateModel(model); + } + }); + monitor.worked(1); + monitor.done(); + + return Status.OK_STATUS; + } +}
\ No newline at end of file diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/AlertsPreferencePage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/AlertsPreferencePage.java new file mode 100644 index 00000000..7855f289 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/AlertsPreferencePage.java @@ -0,0 +1,38 @@ +package org.gluster.storage.management.console.preferences; + +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.IntegerFieldEditor; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.gluster.storage.management.console.Activator; + + +public class AlertsPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + public AlertsPreferencePage() { + super(GRID); + setPreferenceStore(Activator.getDefault().getPreferenceStore()); + setDescription("Gluster Management Console - Alerts"); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.FieldEditorPreferencePage#createFieldEditors() + */ + @Override + protected void createFieldEditors() { + addField(new IntegerFieldEditor(PreferenceConstants.P_SERVER_CPU_CRITICAL_THRESHOLD, + "&Server CPU usage threshold:", getFieldEditorParent())); + + addField(new IntegerFieldEditor(PreferenceConstants.P_SERVER_MEMORY_USAGE_THRESHOLD, + "&Server memory usage threshold (%):", getFieldEditorParent())); + + addField(new IntegerFieldEditor(PreferenceConstants.P_DISK_SPACE_USAGE_THRESHOLD, + "&Disk space usage threshold (%):", getFieldEditorParent())); + } + + @Override + public void init(IWorkbench workbench) { + // TODO Auto-generated method stub + + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/ChartsPreferencePage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/ChartsPreferencePage.java new file mode 100644 index 00000000..6c4f9506 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/ChartsPreferencePage.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.preferences; + +import org.eclipse.jface.preference.ComboFieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.gluster.storage.management.console.Activator; + + +/** + * + */ +public class ChartsPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + public ChartsPreferencePage() { + super(GRID); + setPreferenceStore(Activator.getDefault().getPreferenceStore()); + setDescription("Gluster Management Console"); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) + */ + @Override + public void init(IWorkbench workbench) { + } + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.FieldEditorPreferencePage#createFieldEditors() + */ + @Override + protected void createFieldEditors() { + String[][] entryNamesAndValues = new String[][] { + { "1 day", "1d" }, { "1 week", "1w" }, { "1 month", "1m" }, { "1 year", "1y" } }; + addField(new ComboFieldEditor(PreferenceConstants.P_CPU_AGGREGATED_CHART_PERIOD, "Aggregated CPU Usage chart period", entryNamesAndValues, + getFieldEditorParent())); + addField(new ComboFieldEditor(PreferenceConstants.P_NETWORK_AGGREGATED_CHART_PERIOD, "Aggregated Network Usage chart period", entryNamesAndValues, + getFieldEditorParent())); + addField(new ComboFieldEditor(PreferenceConstants.P_CPU_CHART_PERIOD, "CPU Usage chart period", entryNamesAndValues, + getFieldEditorParent())); + addField(new ComboFieldEditor(PreferenceConstants.P_MEM_CHART_PERIOD, "Memory Usage chart period", entryNamesAndValues, + getFieldEditorParent())); + addField(new ComboFieldEditor(PreferenceConstants.P_NETWORK_CHART_PERIOD, "Network Usage chart period", entryNamesAndValues, + getFieldEditorParent())); + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/GlusterPreferencePage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/GlusterPreferencePage.java new file mode 100644 index 00000000..44ff55ed --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/GlusterPreferencePage.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.preferences; + +import java.util.List; + +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.ComboFieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.IntegerFieldEditor; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.gluster.storage.management.client.ClustersClient; +import org.gluster.storage.management.console.Activator; + + +/** + * This class represents a preference page that + * is contributed to the Preferences dialog. By + * subclassing <samp>FieldEditorPreferencePage</samp>, we + * can use the field support built into JFace that allows + * us to create a page that is small and knows how to + * save, restore and apply itself. + * <p> + * This page is used to modify preferences only. They + * are stored in the preference store that belongs to + * the main plug-in class. That way, preferences can + * be accessed directly via the preference store. + */ +public class GlusterPreferencePage + extends FieldEditorPreferencePage + implements IWorkbenchPreferencePage { + + private List<String> clusterNames; + + public GlusterPreferencePage() { + super(GRID); + setPreferenceStore(Activator.getDefault().getPreferenceStore()); + setDescription("Gluster Management Console"); + } + + /** + * Creates the field editors. Field editors are abstractions of + * the common GUI blocks needed to manipulate various types + * of preferences. Each field editor knows how to save and + * restore itself. + */ + public void createFieldEditors() { + addField( + new BooleanFieldEditor( + PreferenceConstants.P_SHOW_CLUSTER_SELECTION_DIALOG, + "&Show Cluster Selection Dialog on Login:", + getFieldEditorParent())); + + String[][] clusterNamesArr = new String[clusterNames.size()][2]; + for(int i = 0; i < clusterNames.size(); i++) { + String clusterName = clusterNames.get(i); + clusterNamesArr[i][0] = clusterName; + clusterNamesArr[i][1] = clusterName; + } + + addField(new ComboFieldEditor(PreferenceConstants.P_DEFAULT_CLUSTER_NAME, "Default &Cluster to manage:", + clusterNamesArr, getFieldEditorParent())); + addField(new IntegerFieldEditor(PreferenceConstants.P_DATA_SYNC_INTERVAL, "&Refresh Interval (sec):", + getFieldEditorParent())); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) + */ + public void init(IWorkbench workbench) { + clusterNames = new ClustersClient().getClusterNames(); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/PreferenceConstants.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/PreferenceConstants.java new file mode 100644 index 00000000..1422a77d --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/PreferenceConstants.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.preferences; + +/** + * Constant definitions for plug-in preferences + */ +public class PreferenceConstants { + + public static final String P_SHOW_CLUSTER_SELECTION_DIALOG = "show.cluster.selection.dialog"; + public static final String P_DEFAULT_CLUSTER_NAME = "default.cluster.name"; + public static final String P_DATA_SYNC_INTERVAL = "data.sync.interval"; + + public static final String P_SERVER_CPU_CRITICAL_THRESHOLD = "server.cpu.threshold"; + public static final String P_SERVER_MEMORY_USAGE_THRESHOLD = "server.memory.threshold"; + public static final String P_DISK_SPACE_USAGE_THRESHOLD = "disk.space.threshold"; // in percentage + + public static final String P_CPU_CHART_PERIOD = "cpu.chart.period"; + public static final String P_MEM_CHART_PERIOD = "memory.chart.period"; + public static final String P_NETWORK_CHART_PERIOD = "network.chart.period"; + public static final String P_DEFAULT_NETWORK_INTERFACE_PFX = "default.network.interface."; + public static final String P_CPU_AGGREGATED_CHART_PERIOD = "cpu.aggregated.chart.period"; + public static final String P_NETWORK_AGGREGATED_CHART_PERIOD = "network.aggregated.chart.period"; +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/PreferenceInitializer.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/PreferenceInitializer.java new file mode 100644 index 00000000..36d8b652 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/preferences/PreferenceInitializer.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.preferences; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.jface.preference.IPreferenceStore; +import org.gluster.storage.management.console.Activator; + + +/** + * Class used to initialize default preference values. + */ +public class PreferenceInitializer extends AbstractPreferenceInitializer { + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer#initializeDefaultPreferences() + */ + public void initializeDefaultPreferences() { + IPreferenceStore store = Activator.getDefault().getPreferenceStore(); + + store.setDefault(PreferenceConstants.P_SHOW_CLUSTER_SELECTION_DIALOG, true); + + // default data sync interval = 5 minutes + store.setDefault(PreferenceConstants.P_DATA_SYNC_INTERVAL, 300); + + // Default CPU utilisation threshold + store.setDefault(PreferenceConstants.P_SERVER_CPU_CRITICAL_THRESHOLD, 80); + + // Default Memory threshold + store.setDefault(PreferenceConstants.P_SERVER_MEMORY_USAGE_THRESHOLD, 80); + + // Default disk free space threshold + store.setDefault(PreferenceConstants.P_DISK_SPACE_USAGE_THRESHOLD, 80); + + // Default period for server statistics charts + store.setDefault(PreferenceConstants.P_CPU_CHART_PERIOD, "1d"); + store.setDefault(PreferenceConstants.P_MEM_CHART_PERIOD, "1d"); + store.setDefault(PreferenceConstants.P_NETWORK_CHART_PERIOD, "1d"); + store.setDefault(PreferenceConstants.P_CPU_AGGREGATED_CHART_PERIOD, "1d"); + store.setDefault(PreferenceConstants.P_NETWORK_AGGREGATED_CHART_PERIOD, "1d"); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/toolbar/GlusterToolbarManager.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/toolbar/GlusterToolbarManager.java new file mode 100644 index 00000000..fc8ef865 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/toolbar/GlusterToolbarManager.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.toolbar; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.gluster.storage.management.console.actions.ActionConstants; +import org.gluster.storage.management.core.model.Cluster; +import org.gluster.storage.management.core.model.Entity; +import org.gluster.storage.management.core.model.EntityGroup; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.Server; +import org.gluster.storage.management.core.model.TaskInfo; +import org.gluster.storage.management.core.model.Volume; + + +public class GlusterToolbarManager implements ToolbarManager { + private enum ENTITY_TYPE { + CLUSTER, VOLUMES, VOLUME, GLUSTER_SERVERS, GLUSTER_SERVER, DISCOVERED_SERVERS, DISCOVERED_SERVER, TASK + }; + + private IWorkbenchWindow window; + private final Map<Class<? extends Entity>, ENTITY_TYPE> entityTypeMap = createEntityTypeMap(); + private final Map<ENTITY_TYPE, String> actionSetMap = createActionSetMap(); + + private Map<Class<? extends Entity>, ENTITY_TYPE> createEntityTypeMap() { + Map<Class<? extends Entity>, ENTITY_TYPE> entityTypeMap = new HashMap<Class<? extends Entity>, GlusterToolbarManager.ENTITY_TYPE>(); + entityTypeMap.put(Cluster.class, ENTITY_TYPE.CLUSTER); + entityTypeMap.put(Volume.class, ENTITY_TYPE.VOLUME); + entityTypeMap.put(Server.class, ENTITY_TYPE.DISCOVERED_SERVER); + entityTypeMap.put(GlusterServer.class, ENTITY_TYPE.GLUSTER_SERVER); + entityTypeMap.put(TaskInfo.class, ENTITY_TYPE.TASK); + + return entityTypeMap; + } + + private Map<ENTITY_TYPE, String> createActionSetMap() { + Map<ENTITY_TYPE, String> actionSetMap = new HashMap<GlusterToolbarManager.ENTITY_TYPE, String>(); + actionSetMap.put(ENTITY_TYPE.CLUSTER, ActionConstants.ACTION_SET_CLUSTER); + actionSetMap.put(ENTITY_TYPE.VOLUMES, ActionConstants.ACTION_SET_VOLUMES); + actionSetMap.put(ENTITY_TYPE.VOLUME, ActionConstants.ACTION_SET_VOLUME); + actionSetMap.put(ENTITY_TYPE.GLUSTER_SERVERS, ActionConstants.ACTION_SET_GLUSTER_SERVERS); + actionSetMap.put(ENTITY_TYPE.GLUSTER_SERVER, ActionConstants.ACTION_SET_GLUSTER_SERVER); + actionSetMap.put(ENTITY_TYPE.DISCOVERED_SERVERS, ActionConstants.ACTION_SET_DISCOVERED_SERVERS); + actionSetMap.put(ENTITY_TYPE.DISCOVERED_SERVER, ActionConstants.ACTION_SET_DISCOVERED_SERVER); + actionSetMap.put(ENTITY_TYPE.TASK, ActionConstants.ACTION_SET_TASK); + + return actionSetMap; + } + + public GlusterToolbarManager(IWorkbenchWindow window) { + this.window = window; + } + + @SuppressWarnings("rawtypes") + private ENTITY_TYPE getEntityType(Entity entity) { + if (entity instanceof EntityGroup) { + EntityGroup entityGroup = (EntityGroup) entity; + if (entityGroup.getEntityType() == Volume.class) { + return ENTITY_TYPE.VOLUMES; + } else if (entityGroup.getEntityType() == GlusterServer.class) { + return ENTITY_TYPE.GLUSTER_SERVERS; + } else { + return ENTITY_TYPE.DISCOVERED_SERVERS; + } + } + + return entityTypeMap.get(entity.getClass()); + } + + @Override + public void updateToolbar(Entity entity) { + ENTITY_TYPE entityType = getEntityType(entity); + IWorkbenchPage page = window.getActivePage(); + + for (ENTITY_TYPE targetEntityType : actionSetMap.keySet()) { + String actionSetId = actionSetMap.get(targetEntityType); + if (entityType == targetEntityType) { + // show only the action set mapped to given entity + page.showActionSet(actionSetId); + } else { + page.hideActionSet(actionSetId); + } + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/toolbar/ToolbarManager.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/toolbar/ToolbarManager.java new file mode 100644 index 00000000..e4b8d320 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/toolbar/ToolbarManager.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 org.gluster.storage.management.console.toolbar; + +import org.gluster.storage.management.core.model.Entity; + +/** + * Whenever the current selection/action demands changes to the toolbar, the toolbar manager is used to update the + * toolbar. + */ +public interface ToolbarManager { + /** + * Updates the toolbar for given entity. This typically means that user is working with the given entity, and hence + * the toolbar actions related to that entity should be made visible, and other un-related actions should be hidden. + * + * @param entity + */ + public void updateToolbar(Entity entity); +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/ChartUtil.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/ChartUtil.java new file mode 100644 index 00000000..b5d2a602 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/ChartUtil.java @@ -0,0 +1,339 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.utils; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.birt.chart.util.CDateTime; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CCombo; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +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.Hyperlink; +import org.gluster.storage.management.console.Activator; +import org.gluster.storage.management.console.preferences.PreferenceConstants; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.constants.GlusterConstants; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.NetworkInterface; +import org.gluster.storage.management.core.model.ServerStats; +import org.gluster.storage.management.core.model.ServerStatsRow; + +import com.ibm.icu.util.Calendar; + +/** + * + */ +public class ChartUtil { + private static final ChartUtil instance = new ChartUtil(); + private static final int CHART_WIDTH = 350; + private static final IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore(); + + private ChartUtil() { + } + + public static ChartUtil getInstance() { + return instance; + } + + /** + * @param toolkit + * @param section + * @param stats + * @param dataColumnIndex + * @param unit + * @param timestampFormat + * @param listener + * @param maxValue + * @return The composite containing the various links related to the created chart + */ + public Composite createAreaChart(FormToolkit toolkit, Composite section, ServerStats stats, int dataColumnIndex, + String unit, String timestampFormat, ChartPeriodLinkListener listener, double maxValue, int linkColumnCount) { + if (stats == null) { + toolkit.createLabel(section, "Server statistics not available. Try after some time!"); + return null; + } + + List<Calendar> timestamps = new ArrayList<Calendar>(); + List<Double> data = new ArrayList<Double>(); + + extractChartData(stats, timestamps, data, dataColumnIndex); + + if (timestamps.size() == 0) { + toolkit.createLabel(section, "Server statistics not available!" + CoreConstants.NEWLINE + + "Check if all services are running properly " + CoreConstants.NEWLINE + + "on the cluster servers, or try after some time!"); + return null; + } + + createAreaChart(section, timestamps.toArray(new Calendar[0]), data.toArray(new Double[0]), unit, + timestampFormat, maxValue); + + // Calendar[] timestamps = new Calendar[] { new CDateTime(1000l*1310468100), new CDateTime(1000l*1310468400), + // new CDateTime(1000l*1310468700), + // new CDateTime(1000l*1310469000), new CDateTime(1000l*1310469300), new CDateTime(1000l*1310469600), new + // CDateTime(1000l*1310469900), + // new CDateTime(1000l*1310470200), new CDateTime(1000l*1310470500), new CDateTime(1000l*1310470800), new + // CDateTime(1000l*1310471100), + // new CDateTime(1000l*1310471400), new CDateTime(1000l*1310471700), new CDateTime(1000l*1310472000), new + // CDateTime(1000l*1310472300), + // new CDateTime(1000l*1310472600), new CDateTime(1000l*1310472900), new CDateTime(1000l*1310473200), new + // CDateTime(1000l*1310473500), + // new CDateTime(1000l*1310473800) }; + // + // Double[] values = new Double[] { 10d, 11.23d, 17.92d, 18.69d, 78.62d, 89.11d, 92.43d, 89.31d, 57.39d, 18.46d, + // 10.44d, 16.28d, 13.51d, 17.53d, 12.21, 20d, 21.43d, 16.45d, 14.86d, 15.27d }; + // createLineChart(section, timestamps, values, "%"); + Composite chartLinksComposite = createChartLinks(toolkit, section, linkColumnCount, listener); + + if (linkColumnCount == 5) { + createNetworkInterfaceCombo(section, chartLinksComposite, toolkit, + (NetworkChartPeriodLinkListener) listener); + } + return chartLinksComposite; + } + + private ChartViewerComposite createAreaChart(Composite section, Calendar timestamps[], Double values[], + String unit, String timestampFormat, double maxValue) { + ChartViewerComposite chartViewerComposite = new ChartViewerComposite(section, SWT.NONE, timestamps, values, + unit, timestampFormat, maxValue); + GridData data = new GridData(SWT.FILL, SWT.FILL, false, false); + data.widthHint = CHART_WIDTH; + data.heightHint = 250; + data.verticalAlignment = SWT.CENTER; + chartViewerComposite.setLayoutData(data); + return chartViewerComposite; + } + + private void extractChartData(ServerStats stats, List<Calendar> timestamps, List<Double> data, int dataColumnIndex) { + for (ServerStatsRow row : stats.getRows()) { + Double cpuUsage = row.getUsageData().get(dataColumnIndex); + if (!cpuUsage.isNaN()) { + timestamps.add(new CDateTime(row.getTimestamp() * 1000)); + data.add(cpuUsage); + } + } + } + + private Composite createChartLinks(FormToolkit toolkit, Composite section, int columnCount, + ChartPeriodLinkListener listener) { + GridLayout layout = new org.eclipse.swt.layout.GridLayout(columnCount, false); + layout.marginBottom = 0; + layout.marginTop = 0; + layout.marginLeft = (CHART_WIDTH - (50 * columnCount)) / 2; + Composite graphComposite = toolkit.createComposite(section, SWT.NONE); + graphComposite.setLayout(layout); + GridData data = new GridData(SWT.FILL, SWT.FILL, false, false); + data.widthHint = CHART_WIDTH; + graphComposite.setLayoutData(data); + + createStatsLink(toolkit, listener, graphComposite, "1 day", GlusterConstants.STATS_PERIOD_1DAY); + createStatsLink(toolkit, listener, graphComposite, "1 week", GlusterConstants.STATS_PERIOD_1WEEK); + createStatsLink(toolkit, listener, graphComposite, "1 month", GlusterConstants.STATS_PERIOD_1MONTH); + createStatsLink(toolkit, listener, graphComposite, "1 year", GlusterConstants.STATS_PERIOD_1YEAR); + + return graphComposite; + } + + private void createStatsLink(FormToolkit toolkit, ChartPeriodLinkListener listener, Composite parent, String label, + String statsPeriod) { + Hyperlink link1 = toolkit.createHyperlink(parent, label, SWT.NONE); + link1.addHyperlinkListener(listener.getInstance(statsPeriod)); + if (listener.getStatsPeriod().equals(statsPeriod)) { + link1.setEnabled(false); + } + } + + public abstract class ChartPeriodLinkListener extends HyperlinkAdapter { + protected String statsPeriod; + protected String unit; + protected int columnCount; + protected double maxValue; + protected FormToolkit toolkit; + protected String serverName; + protected int dataColumnIndex; + + public String getStatsPeriod() { + return this.statsPeriod; + } + + public ChartPeriodLinkListener(String serverName, String statsPeriod, String unit, double maxValue, + int columnCount, int dataColumnIndex, FormToolkit toolkit) { + this.serverName = serverName; + this.statsPeriod = statsPeriod; + this.unit = unit; + this.columnCount = columnCount; + this.maxValue = maxValue; + this.dataColumnIndex = dataColumnIndex; + this.toolkit = toolkit; + } + + public ChartPeriodLinkListener(String serverName, String statsPeriod, FormToolkit toolkit) { + this.statsPeriod = statsPeriod; + this.serverName = serverName; + this.toolkit = toolkit; + } + + @Override + public void linkActivated(HyperlinkEvent e) { + super.linkActivated(e); + Composite section = ((Hyperlink) e.getSource()).getParent().getParent(); + updatePreference(serverName); + } + + public abstract ChartPeriodLinkListener getInstance(String statsPeriod); + + protected abstract void updatePreference(String serverName); + } + + public class CpuChartPeriodLinkListener extends ChartPeriodLinkListener { + public CpuChartPeriodLinkListener(String serverName, String statsPeriod, FormToolkit toolkit) { + super(serverName, statsPeriod, toolkit); + } + + private CpuChartPeriodLinkListener(String serverName, String statsPeriod, String unit, double maxValue, + int columnCount, int dataColumnIndex, FormToolkit toolkit) { + super(serverName, statsPeriod, unit, maxValue, columnCount, dataColumnIndex, toolkit); + } + + @Override + protected void updatePreference(String serverName) { + ServerStats stats; + if (serverName == null) { + preferenceStore.setValue(PreferenceConstants.P_CPU_AGGREGATED_CHART_PERIOD, statsPeriod); + } else { + preferenceStore.setValue(PreferenceConstants.P_CPU_CHART_PERIOD, statsPeriod); + } + } + + @Override + public ChartPeriodLinkListener getInstance(String statsPeriod) { + return new CpuChartPeriodLinkListener(serverName, statsPeriod, "%", 100, 4, dataColumnIndex, toolkit); + } + } + + public class MemoryChartPeriodLinkListener extends ChartPeriodLinkListener { + public MemoryChartPeriodLinkListener(String serverName, String statsPeriod, FormToolkit toolkit) { + super(serverName, statsPeriod, toolkit); + } + + private MemoryChartPeriodLinkListener(String serverName, String statsPeriod, String unit, double maxValue, + int columnCount, int dataColumnIndex, FormToolkit toolkit) { + super(serverName, statsPeriod, unit, maxValue, columnCount, dataColumnIndex, toolkit); + } + + @Override + protected void updatePreference(String serverName) { + preferenceStore.setValue(PreferenceConstants.P_MEM_CHART_PERIOD, statsPeriod); + } + + @Override + public ChartPeriodLinkListener getInstance(String statsPeriod) { + return new MemoryChartPeriodLinkListener(serverName, statsPeriod, "%", 100, 4, dataColumnIndex, toolkit); + } + } + + public class NetworkChartPeriodLinkListener extends ChartPeriodLinkListener { + private GlusterServer server; + + public NetworkChartPeriodLinkListener(GlusterServer server, String statsPeriod, FormToolkit toolkit) { + super(server == null ? null : server.getName(), statsPeriod, toolkit); + this.setServer(server); + } + + private NetworkChartPeriodLinkListener(GlusterServer server, String statsPeriod, String unit, double maxValue, + int columnCount, int dataColumnIndex, FormToolkit toolkit) { + super(server == null ? null : server.getName(), statsPeriod, unit, maxValue, columnCount, dataColumnIndex, + toolkit); + this.setServer(server); + } + + @Override + protected void updatePreference(String serverName) { + if (serverName == null) { + preferenceStore.setValue(PreferenceConstants.P_NETWORK_AGGREGATED_CHART_PERIOD, statsPeriod); + } else { + preferenceStore.setValue(PreferenceConstants.P_NETWORK_CHART_PERIOD, statsPeriod); + } + } + + @Override + public ChartPeriodLinkListener getInstance(String statsPeriod) { + // if serverName is null, it means we are showing aggregated stats - so the "network interface" combo will + // not be shown in the "links" composite. + int columnCount = (serverName == null ? 4 : 5); + return new NetworkChartPeriodLinkListener(server, statsPeriod, "KiB/s", -1d, columnCount, dataColumnIndex, + toolkit); + } + + public void setServer(GlusterServer server) { + this.server = server; + } + + public GlusterServer getServer() { + return server; + } + } + + public void createNetworkInterfaceCombo(final Composite section, final Composite graphComposite, + final FormToolkit toolkit, final NetworkChartPeriodLinkListener networkChartPeriodLinkListener) { + final GlusterServer server = networkChartPeriodLinkListener.getServer(); + final CCombo interfaceCombo = new CCombo(graphComposite, SWT.DROP_DOWN | SWT.READ_ONLY | SWT.BORDER | SWT.FLAT); + final List<NetworkInterface> niList = server.getNetworkInterfaces(); + final String[] interfaces = new String[niList.size()]; + + for (int i = 0; i < interfaces.length; i++) { + interfaces[i] = niList.get(i).getName(); + } + interfaceCombo.setItems(interfaces); + interfaceCombo.select(0); + interfaceCombo.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + preferenceStore.setValue(PreferenceConstants.P_DEFAULT_NETWORK_INTERFACE_PFX + server.getName(), interfaces[interfaceCombo.getSelectionIndex()]); + } + }); + } + + public void refreshChartSection(FormToolkit toolkit, Composite section, ServerStats stats, String statsPeriod, + String unit, double maxValue, int columnCount, ChartPeriodLinkListener linkListener, int dataColumnIndex) { + GUIHelper.getInstance().clearSection(section); + createAreaChart(toolkit, section, stats, dataColumnIndex, unit, getTimestampFormatForPeriod(statsPeriod), + linkListener, maxValue, columnCount); + section.layout(); + } + + public String getTimestampFormatForPeriod(String statsPeriod) { + if (statsPeriod.equals(GlusterConstants.STATS_PERIOD_1DAY)) { + return "HH:mm"; + } else if (statsPeriod.equals(GlusterConstants.STATS_PERIOD_1WEEK)) { + return "dd-MMM HH:mm"; + } else { + return "dd-MMM"; + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/ChartViewerComposite.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/ChartViewerComposite.java new file mode 100644 index 00000000..4f2e7ecf --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/ChartViewerComposite.java @@ -0,0 +1,475 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.utils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.birt.chart.api.ChartEngine; +import org.eclipse.birt.chart.device.IDeviceRenderer; +import org.eclipse.birt.chart.device.IUpdateNotifier; +import org.eclipse.birt.chart.exception.ChartException; +import org.eclipse.birt.chart.factory.GeneratedChartState; +import org.eclipse.birt.chart.factory.Generator; +import org.eclipse.birt.chart.model.Chart; +import org.eclipse.birt.chart.model.ChartWithoutAxes; +import org.eclipse.birt.chart.model.attribute.Anchor; +import org.eclipse.birt.chart.model.attribute.AxisType; +import org.eclipse.birt.chart.model.attribute.Bounds; +import org.eclipse.birt.chart.model.attribute.ChartDimension; +import org.eclipse.birt.chart.model.attribute.LineAttributes; +import org.eclipse.birt.chart.model.attribute.LineStyle; +import org.eclipse.birt.chart.model.attribute.Position; +import org.eclipse.birt.chart.model.attribute.Text; +import org.eclipse.birt.chart.model.attribute.TickStyle; +import org.eclipse.birt.chart.model.attribute.impl.BoundsImpl; +import org.eclipse.birt.chart.model.attribute.impl.ColorDefinitionImpl; +import org.eclipse.birt.chart.model.attribute.impl.JavaDateFormatSpecifierImpl; +import org.eclipse.birt.chart.model.attribute.impl.LineAttributesImpl; +import org.eclipse.birt.chart.model.attribute.impl.NumberFormatSpecifierImpl; +import org.eclipse.birt.chart.model.component.Axis; +import org.eclipse.birt.chart.model.component.Series; +import org.eclipse.birt.chart.model.component.impl.SeriesImpl; +import org.eclipse.birt.chart.model.data.DateTimeDataElement; +import org.eclipse.birt.chart.model.data.DateTimeDataSet; +import org.eclipse.birt.chart.model.data.NumberDataSet; +import org.eclipse.birt.chart.model.data.SeriesDefinition; +import org.eclipse.birt.chart.model.data.TextDataSet; +import org.eclipse.birt.chart.model.data.impl.DateTimeDataElementImpl; +import org.eclipse.birt.chart.model.data.impl.DateTimeDataSetImpl; +import org.eclipse.birt.chart.model.data.impl.NumberDataElementImpl; +import org.eclipse.birt.chart.model.data.impl.NumberDataSetImpl; +import org.eclipse.birt.chart.model.data.impl.SeriesDefinitionImpl; +import org.eclipse.birt.chart.model.data.impl.TextDataSetImpl; +import org.eclipse.birt.chart.model.impl.ChartWithAxesImpl; +import org.eclipse.birt.chart.model.impl.ChartWithoutAxesImpl; +import org.eclipse.birt.chart.model.layout.Legend; +import org.eclipse.birt.chart.model.layout.Plot; +import org.eclipse.birt.chart.model.type.AreaSeries; +import org.eclipse.birt.chart.model.type.PieSeries; +import org.eclipse.birt.chart.model.type.impl.AreaSeriesImpl; +import org.eclipse.birt.chart.model.type.impl.PieSeriesImpl; +import org.eclipse.birt.core.framework.PlatformConfig; +import org.eclipse.core.runtime.Platform; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; + +import com.ibm.icu.util.Calendar; + +/** + * + */ +public final class ChartViewerComposite extends Composite implements PaintListener, IUpdateNotifier { + + public enum CHART_TYPE { + PIE, LINE + }; + + private IDeviceRenderer deviceReader = null; + private Chart chart = null; + private GeneratedChartState generatedChartState = null; + private boolean needsGeneration = true; + + private static Logger logger = Logger.getLogger(ChartViewerComposite.class.getName()); + + /** + * @param parent + * Parent composite of this pie chart viewer composite + * @param style + * SWT style to be used + * @param categories + * Categories of the pie chart + * @param values + * Values of each category in the pie chart Constructs a pie + * chart viewer composite for given categories and values + */ + public ChartViewerComposite(Composite parent, int style, String[] categories, Double[] values) { + super(parent, style); + init(); + + chart = createPieChart(categories, values); + addPaintListener(this); + } + + /** + * @param parent + * Parent composite of this pie chart viewer composite + * @param style + * SWT style to be used + * @param categories + * Categories of the pie chart + * @param values + * Values of each category in the pie chart Constructs a pie + * chart viewer composite for given categories and values + * @param maxValue + */ + public ChartViewerComposite(Composite parent, int style, Calendar[] categories, Double[] values, String unit, String timestampFormat, double maxValue) { + super(parent, style); + init(); + + createSingleAreaChart(categories, values, unit, timestampFormat, maxValue); + addPaintListener(this); + } + + public void init() { + try { + PlatformConfig config = new PlatformConfig(); + config.setBIRTHome(Platform.getInstallLocation().getURL().getPath()); + // Get the connection with SWT device to render the graphics. + deviceReader = ChartEngine.instance(config).getRenderer("dv.SWT");//$NON-NLS-1$ + } catch (ChartException ex) { + logger.log(Level.SEVERE, "Could not create Chart Renderer for SWT", ex); + } + + addControlListener(new ControlListener() { + + public void controlMoved(ControlEvent e) { + needsGeneration = true; + } + + public void controlResized(ControlEvent e) { + needsGeneration = true; + } + }); + } + + private void createSingleAreaChart(Calendar[] timestamps, Double[] values, String unit, String timestampFormat, double maxValue) { + createAreaChart(timestamps, new Double[][] {values}, unit, timestampFormat, maxValue); + } + + /** + * Creates a line chart model as a reference implementation + * @param maxValue + * + * @return An instance of the simulated runtime chart model (containing + * filled datasets) + */ + private final void createAreaChart(Calendar[] timestamps, Double[][] values, final String unit, final String timestampFormat, double maxValue) { + chart = ChartWithAxesImpl.create(); + // Plot + chart.getBlock().setBackground(ColorDefinitionImpl.WHITE()); + Plot p = chart.getPlot(); + p.getClientArea().setBackground(ColorDefinitionImpl.WHITE()); + p.setBackground(ColorDefinitionImpl.WHITE()); + + // Title + chart.getTitle().getLabel().getCaption().setValue("Line Chart");//$NON-NLS-1$ + chart.getTitle().setVisible(false); + chart.getTitle().getLabel().setVisible(true); + chart.getTitle().getInsets().set(0, 10, 0, 0); + chart.getTitle().setAnchor(Anchor.SOUTH_LITERAL); + + // Legend + Legend lg = chart.getLegend(); + lg.setVisible(false); + LineAttributes lia = lg.getOutline( ); + lia.setStyle( LineStyle.SOLID_LITERAL ); + lg.getText( ).getFont( ).setSize( 10 ); + //lg.getInsets( ).set( 10, 5, 0, 0 ); + lg.getInsets( ).set( 0, 0, 0, 0 ); + lg.getOutline( ).setVisible( false ); + lg.setAnchor( Anchor.NORTH_LITERAL ); + + updateDataSet(timestamps, values, unit, timestampFormat, maxValue); + } + + private void updateDataSet(Calendar[] timestamps, Double[][] values, final String unit, final String timestampFormat, double maxValue) { + Axis xAxisPrimary = setupXAxis(timestamps, timestampFormat); + + if(maxValue <= 0) { + maxValue = getMaxValue(values); + } + Axis yAxisPrimary = setupYAxis(unit, xAxisPrimary, maxValue); + configureXSeries(timestamps, xAxisPrimary); + configureYSeries(values, yAxisPrimary); + } + + private double getMaxValue(Double[][] values) { + double maxValue = -1; + for(Double[] seriesValues : values) { + double seriesMaxVal = Collections.max(Arrays.asList(seriesValues)); + if(seriesMaxVal > maxValue) { + maxValue = Math.round(seriesMaxVal) + 5 - (Math.round(seriesMaxVal) % 5); + } + } + return maxValue; + } + + private void configureYSeries(Double[][] values, Axis yAxisPrimary) { + SeriesDefinition sdY = SeriesDefinitionImpl.create(); + sdY.getSeriesPalette().shift(-3); + yAxisPrimary.getSeriesDefinitions().add(sdY); + + for (int i = 0; i < values.length; i++) { + // Y-Sereis + AreaSeries ls = (AreaSeries) AreaSeriesImpl.create(); + // LineSeries ls = (LineSeries) LineSeriesImpl.create(); + + NumberDataSet orthoValues = NumberDataSetImpl.create(values[i]); + ls.setDataSet(orthoValues); + ls.getLineAttributes().setColor(ColorDefinitionImpl.create(50, 50, 255)); +// for (int j = 0; j < ls.getMarkers().size(); j++) { +// ( (Marker) ls.getMarkers( ).get( j ) ).setType( MarkerType.CIRCLE_LITERAL); +// ((Marker) ls.getMarkers().get(j)).setVisible(true); +// } + ls.setTranslucent(true); + // don't show values on each point on the line chart + ls.getLabel().setVisible(false); + sdY.getSeries().add(ls); + } + } + + private void configureXSeries(Calendar[] timestamps, Axis xAxisPrimary) { + // Data Set + DateTimeDataSet categoryValues = DateTimeDataSetImpl.create(timestamps); + + // X-Series + Series seCategory = SeriesImpl.create(); + seCategory.setDataSet(categoryValues); + SeriesDefinition sdX = SeriesDefinitionImpl.create(); + + xAxisPrimary.getSeriesDefinitions().add(sdX); + sdX.getSeries().add(seCategory); + } + + private Axis setupYAxis(final String unit, Axis xAxisPrimary, double maxValue) { + Axis yAxisPrimary = ((ChartWithAxesImpl)chart).getPrimaryOrthogonalAxis(xAxisPrimary); + if(maxValue > 0) { + yAxisPrimary.getScale().setMax(NumberDataElementImpl.create(maxValue)); + yAxisPrimary.getScale().setStep(maxValue / 5); + } + yAxisPrimary.getScale().setMin(NumberDataElementImpl.create(0)); + yAxisPrimary.setGapWidth(0); + yAxisPrimary.getScale().setMajorGridsStepNumber(1); + yAxisPrimary.getMajorGrid().setTickStyle(TickStyle.LEFT_LITERAL); + yAxisPrimary.getMajorGrid().setLineAttributes(LineAttributesImpl.create(ColorDefinitionImpl.GREY(), LineStyle.SOLID_LITERAL, 1)); + yAxisPrimary.getLabel().setVisible(true); + yAxisPrimary.getLabel().getCaption().getFont().setSize(8); + yAxisPrimary.setFormatSpecifier(new NumberFormatSpecifierImpl() { + @Override + public String getSuffix() { + return " " + unit; + } + }); + return yAxisPrimary; + } + + private Axis setupXAxis(Calendar[] timestamps, final String timestampFormat) { + Axis xAxisPrimary = ((ChartWithAxesImpl)chart).getPrimaryBaseAxes()[0]; + xAxisPrimary.setType(AxisType.TEXT_LITERAL); + DateTimeDataElement dtde = DateTimeDataElementImpl.create(timestamps[timestamps.length-1]); + DateTimeDataElement dtde1 = DateTimeDataElementImpl.create(timestamps[0]); + xAxisPrimary.getScale().setMax(dtde); + xAxisPrimary.getScale().setStep((dtde.getValue() - dtde1.getValue())/ 10); + xAxisPrimary.getScale().setMajorGridsStepNumber(timestamps.length > 10 ? timestamps.length / 10 : 1); + //xAxisPrimary.getMajorGrid().setTickStyle(TickStyle.ABOVE_LITERAL); + xAxisPrimary.getMajorGrid().getTickAttributes().setVisible(false); + xAxisPrimary.getMajorGrid().setLineAttributes(LineAttributesImpl.create(ColorDefinitionImpl.GREY(), LineStyle.SOLID_LITERAL, 1)); + xAxisPrimary.getTitle().setVisible(false); + xAxisPrimary.getTitle().getInsets().set(1, 1, 1, 1); + xAxisPrimary.getLabel().getInsets().set(1, 1, 1, 1); + //xAxisPrimary.getLabel().getCaption().setFont(createChartFont()); + xAxisPrimary.getLabel( ).getCaption( ).getFont( ).setSize(8); + //commenting to check whether this is causing the problem on windows + //xAxisPrimary.getLabel( ).getCaption( ).getFont( ).setRotation( 75 ); + xAxisPrimary.setFormatSpecifier( JavaDateFormatSpecifierImpl.create( timestampFormat ) ); + return xAxisPrimary; + } + + /** + * @param categories + * Categories of the pie chart + * @param values + * Values of each category in the pie chart + * @return The chart object created for given categories and values + */ + public static final Chart createPieChart(String[] categories, Double[] values) { + ChartWithoutAxes pieChart = ChartWithoutAxesImpl.create(); + + // script hook to NOT show the label if value is zero + pieChart.setScript("function beforeDrawDataPointLabel( dph, label, icsc ){ if (dph.getOrthogonalValue() == 0){ label.setVisible(false); } } "); + + // Plot + pieChart.setSeriesThickness(10); + pieChart.setDimension(ChartDimension.TWO_DIMENSIONAL_WITH_DEPTH_LITERAL); + pieChart.getBlock().setBackground(ColorDefinitionImpl.WHITE()); + Plot p = pieChart.getPlot(); + + p.getClientArea().setBackground(null); + p.getClientArea().getOutline().setVisible(false); + p.getOutline().setVisible(false); + + // Legend + Legend lg = pieChart.getLegend(); + lg.setMaxPercent(0.7); + lg.getText().getFont().setSize(9); + lg.setBackground(null); + lg.getOutline().setVisible(false); + lg.setVisible(true); + + // Title + pieChart.getTitle().getLabel().getCaption().setValue("Pie Chart");//$NON-NLS-1$ + pieChart.getTitle().getOutline().setVisible(false); + pieChart.getTitle().setVisible(false); + + TextDataSet categoryValues = TextDataSetImpl.create(categories); + NumberDataSet seriesOneValues = NumberDataSetImpl.create(values); + + // Base Series + Series seCategory = SeriesImpl.create(); + seCategory.setDataSet(categoryValues); + + SeriesDefinition sd = SeriesDefinitionImpl.create(); + pieChart.getSeriesDefinitions().add(sd); + sd.setSeriesPalette(new GlusterChartPalette()); + sd.getSeriesPalette().shift(0); + sd.getSeries().add(seCategory); + + // Orthogonal Series + PieSeries sePie = (PieSeries) PieSeriesImpl.create(); + sePie.setRatio(0.60); + sePie.setDataSet(seriesOneValues); + sePie.setSeriesIdentifier("Chart");//$NON-NLS-1$ + sePie.getTitle().setVisible(false); // no title + sePie.getLabel().setVisible(true); // show label (values) + sePie.setExplosion(0); // no gap between the pie slices + sePie.setLabelPosition(Position.INSIDE_LITERAL); + Text labelCaption = sePie.getLabel().getCaption(); + labelCaption.setColor(ColorDefinitionImpl.CYAN()); + labelCaption.getFont().setSize(8); + labelCaption.getFont().setBold(true); + + SeriesDefinition seriesDefinition = SeriesDefinitionImpl.create(); + seriesDefinition.getQuery().setDefinition("query.definition");//$NON-NLS-1$ + sd.getSeriesDefinitions().add(seriesDefinition); + seriesDefinition.getSeries().add(sePie); + + return pieChart; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events + * .PaintEvent) + */ + public final void paintControl(PaintEvent e) { + Rectangle d = ((Composite) e.getSource()).getBounds(); + Image imgChart = new Image(this.getDisplay(), d); + GC gcImage = new GC(imgChart); + deviceReader.setProperty(IDeviceRenderer.GRAPHICS_CONTEXT, gcImage); + deviceReader.setProperty(IDeviceRenderer.UPDATE_NOTIFIER, this); + + Bounds bo = BoundsImpl.create(0, 0, d.width, d.height); + bo.scale(71d / deviceReader.getDisplayServer().getDpiResolution()); + + Generator gr = Generator.instance(); + if (needsGeneration) { + needsGeneration = false; + try { + generatedChartState = gr.build(deviceReader.getDisplayServer(), chart, bo, null, null, null); + } catch (ChartException ce) { + ce.printStackTrace(); + } + } + + try { + gr.render(deviceReader, generatedChartState); + GC gc = e.gc; + gc.drawImage(imgChart, d.x, d.y); + } catch (ChartException gex) { + logger.log(Level.SEVERE, "Exception while rendering pie chart [" + gex.getMessage() + "]", gex); + } + } + +// public void chartRefresh(Calendar[] timestamps, Double[][] values, String unit, String timestampFormat) +// { +// if ( !isDisposed( ) ) +// { +// final Generator gr = Generator.instance( ); +// updateDataSet( timestamps, values, unit, timestampFormat); +// +// // Refresh +// try +// { +// gr.refresh( generatedChartState ); +// } +// catch ( ChartException ex ) +// { +// // TODO: log the exception +// ex.printStackTrace( ); +// } +// redraw( ); +// } +// } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.device.IUpdateNotifier#getDesignTimeModel() + */ + public Chart getDesignTimeModel() { + return chart; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.device.IUpdateNotifier#getRunTimeModel() + */ + public Chart getRunTimeModel() { + return generatedChartState.getChartModel(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.device.IUpdateNotifier#peerInstance() + */ + public Object peerInstance() { + return this; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.device.IUpdateNotifier#regenerateChart() + */ + public void regenerateChart() { + redraw(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.device.IUpdateNotifier#repaintChart() + */ + public void repaintChart() { + redraw(); + } +}
\ No newline at end of file diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/EntityViewerFilter.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/EntityViewerFilter.java new file mode 100644 index 00000000..2ee6d535 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/EntityViewerFilter.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.utils; + +import java.util.Map.Entry; + +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.gluster.storage.management.core.model.Filterable; +import org.gluster.storage.management.core.utils.StringUtil; + + +public class EntityViewerFilter extends ViewerFilter { + + private String filterString; + private boolean caseSensitive = false; + + public EntityViewerFilter(String filterString, boolean caseSensitive) { + this.filterString = filterString; + this.caseSensitive = caseSensitive; + } + + public boolean isCaseSensitive() { + return caseSensitive; + } + + public void setCaseSensitive(boolean caseSensitive) { + this.caseSensitive = caseSensitive; + } + + public String getFilterString() { + return filterString; + } + + public void setFilterString(String filterString) { + this.filterString = filterString; + } + + @SuppressWarnings("unchecked") + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + if (filterString == null || filterString.isEmpty()) { + // No filter string. select everything + return true; + } + + if (element instanceof Filterable) { + return ((Filterable) element).filter(filterString, caseSensitive); + } + + if(element instanceof Entry) { + Entry<String, String> entry = (Entry<String, String>)element; + return StringUtil.filterString(entry.getKey() + entry.getValue(), filterString, caseSensitive); + } + + if(element instanceof String) { + return StringUtil.filterString((String)element, filterString, caseSensitive); + } + + return false; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/GUIHelper.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/GUIHelper.java new file mode 100644 index 00000000..600a941c --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/GUIHelper.java @@ -0,0 +1,481 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.utils; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuCreator; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.fieldassist.FieldDecorationRegistry; +import org.eclipse.jface.layout.TableColumnLayout; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.ColumnLayoutData; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.TabFolder; +import org.eclipse.swt.widgets.TabItem; +import org.eclipse.swt.widgets.Table; +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; +import org.eclipse.ui.forms.events.ExpansionEvent; +import org.eclipse.ui.forms.widgets.ColumnLayout; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.eclipse.ui.forms.widgets.Section; +import org.eclipse.ui.progress.IProgressConstants; +import org.gluster.storage.management.console.Application; +import org.gluster.storage.management.console.ConsoleConstants; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.console.views.NavigationView; +import org.gluster.storage.management.console.views.TasksView; +import org.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import org.gluster.storage.management.core.model.Disk; +import org.gluster.storage.management.core.utils.JavaUtil; + + +public class GUIHelper { + private static final GUIHelper instance = new GUIHelper(); + private static final ImageUtil imageUtil = new ImageUtil(); + private static final GlusterLogger logger = GlusterLogger.getInstance(); + + private GUIHelper() { + + } + + public static GUIHelper getInstance() { + return instance; + } + + public ScrolledForm setupForm(Composite parent, FormToolkit toolkit, final String formName) { + return setupForm(toolkit, formName, toolkit.createScrolledForm(parent)); + } + + public ScrolledForm setupForm(FormToolkit toolkit, final String formName, ScrolledForm form) { + form.setText(formName); + toolkit.decorateFormHeading(form.getForm()); + + ColumnLayout layout = new ColumnLayout(); + + // layout.topMargin = 0; + // layout.bottomMargin = 5; + // layout.leftMargin = 10; + // layout.rightMargin = 10; + // layout.horizontalSpacing = 10; + // layout.verticalSpacing = 10; + // layout.maxNumColumns = 4; + // layout.minNumColumns = 1; + + form.getBody().setLayout(layout); + return form; + } + + public Composite createSection(final ScrolledForm form, FormToolkit toolkit, String title, String desc, + int numColumns, boolean collapsible) { + int style = Section.TITLE_BAR | Section.EXPANDED; + if (desc != null && !desc.isEmpty()) { + style |= Section.DESCRIPTION; + } + if (collapsible) { + style |= Section.TWISTIE; + } + + Section section = toolkit.createSection(form.getBody(), style); + section.setText(title); + section.setDescription(desc); + + // toolkit.createCompositeSeparator(section); + Composite client = toolkit.createComposite(section); + GridLayout layout = new GridLayout(); + layout.marginWidth = layout.marginHeight = 0; + layout.numColumns = numColumns; + layout.verticalSpacing = 10; + layout.marginBottom = 15; + layout.marginTop = 10; + + client.setLayout(layout); + section.setClient(client); + + section.addExpansionListener(new ExpansionAdapter() { + public void expansionStateChanged(ExpansionEvent e) { + form.reflow(false); + } + }); + return client; + } + + public Composite createTab(TabFolder tabFolder, String title, String imageKey) { + TabItem item = new TabItem(tabFolder, SWT.NONE); + item.setText(title); + item.setImage(getImage(imageKey)); + + Composite composite = new Composite(tabFolder, SWT.NONE); + composite.setLayout(new FillLayout()); + + item.setControl(composite); + + return composite; + } + + public ImageDescriptor getImageDescriptor(String imagePath) { + return imageUtil.getImageDescriptor(imagePath); + } + + public Image getImage(String imagePath) { + return imageUtil.getImage(imagePath); + } + + public Action createPullDownMenu(String menuName, String iconPath, final MenuManager menuManager) { + Action action = new Action(menuName, IAction.AS_DROP_DOWN_MENU) { + public void run() { + } + }; + action.setMenuCreator(new IMenuCreator() { + + @Override + public Menu getMenu(Menu menu) { + return null; + } + + @Override + public Menu getMenu(Control control) { + return menuManager.createContextMenu(control); + } + + @Override + public void dispose() { + } + }); + action.setImageDescriptor(getImageDescriptor(iconPath)); + return action; + } + + public TableColumnLayout createTableColumnLayout(Table table, String[] columns) { + TableColumnLayout tableColumnLayout = new TableColumnLayout(); + ColumnLayoutData defaultColumnLayoutData = new ColumnWeightData(100); + + for (String columnName : columns) { + TableColumn column = new TableColumn(table, SWT.LEFT); + column.setText(columnName); + + tableColumnLayout.setColumnData(column, defaultColumnLayoutData); + } + + return tableColumnLayout; + } + + /** + * Creates a filter for given structured viewer that will filter the contents of the viewer based on the current + * text of the text field + * + * @param viewer + * Structured viewer for which the filter is to be created + * @param filterText + * The text field whose contents are to be used for filtering + * @param caseSensitive + * Flag indicating whether the filtering should be case sensitive + * @return The newly created filter + */ + public EntityViewerFilter createFilter(final StructuredViewer viewer, final Text filterText, boolean caseSensitive) { + final String initialFilterString = filterText.getText(); + + final EntityViewerFilter filter = new EntityViewerFilter(initialFilterString, caseSensitive); + // On every keystroke inside the text field, update the filter string + filterText.addKeyListener(new KeyAdapter() { + private String filterString = initialFilterString; + + @Override + public void keyReleased(KeyEvent e) { + String enteredString = filterText.getText(); + if (enteredString.equals(filterString)) { + // Filter string has not changed. don't do anything + return; + } + + // Update filter string + filterString = enteredString; + filter.setFilterString(filterString); + + // Refresh viewer with newly filtered content + viewer.refresh(true); + if(viewer instanceof TreeViewer) { + ((TreeViewer)viewer).expandAll(); + } + } + }); + + viewer.addFilter(filter); + return filter; + } + + public IViewPart getView(String viewId) { + IViewReference[] views = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage() + .getViewReferences(); + for (IViewReference view : views) { + if (view.getId().equals(viewId)) { + return view.getView(false); + } + } + 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); + passwordErrorDecoration.setImage(FieldDecorationRegistry.getDefault() + .getFieldDecoration(FieldDecorationRegistry.DEC_ERROR).getImage()); + return passwordErrorDecoration; + } + + public void centerShellInScreen(Shell shell) { + Rectangle monitorBounds = shell.getMonitor().getBounds(); + Rectangle myBounds = shell.getBounds(); + + int x = monitorBounds.x + (monitorBounds.width - myBounds.width) / 2; + int y = monitorBounds.y + (monitorBounds.height - myBounds.height) / 2; + shell.setLocation(x, y); + } + + public Text createFilterText(FormToolkit toolkit, Composite parent) { + final String tooltipMessage = "Start typing to filter table contents."; + final Text filterText = toolkit.createText(parent, "", SWT.FLAT); + + GridData data = new GridData(SWT.RIGHT, SWT.CENTER, false, false); + data.widthHint = 300; + filterText.setLayoutData(data); + + ControlDecoration searchDecoration = new ControlDecoration(filterText, SWT.LEFT); + searchDecoration.setImage(getImage(IImageKeys.SEARCH_22x22)); + searchDecoration.show(); + searchDecoration.setShowHover(true); + searchDecoration.setDescriptionText(tooltipMessage); + searchDecoration.setMarginWidth(5); + + filterText.setToolTipText(tooltipMessage); + return filterText; + } + + public Text createFilterText(Composite parent) { + final String tooltipMessage = "Start typing to filter table contents."; + final Text filterText = new Text(parent, SWT.FLAT); + + GridData data = new GridData(SWT.LEFT, SWT.CENTER, false, false); + data.widthHint = 300; + filterText.setLayoutData(data); + + ControlDecoration searchDecoration = new ControlDecoration(filterText, SWT.RIGHT); + searchDecoration.setImage(getImage(IImageKeys.SEARCH_22x22)); + searchDecoration.show(); + searchDecoration.setShowHover(true); + searchDecoration.setDescriptionText(tooltipMessage); + + filterText.setToolTipText(tooltipMessage); + return filterText; + } + + /** + * 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, int columnIndex, int alignment, int weight) { + TableColumn column = table.getColumn(columnIndex); + column.setAlignment(alignment); + + TableColumnLayout tableColumnLayout = (TableColumnLayout) table.getParent().getLayout(); + tableColumnLayout.setColumnData(column, new ColumnWeightData(weight)); + } + + /** + * Fetches the currently selected objects from the workbench site and returns the one of given type. If none of the + * selected objects are of given type, returns null + * + * @param site + * The workbench site + * @param expectedType + * Type of the selected object to look for + * @return The selected object of given type if found, else null + */ + public <T> T getSelectedEntity(IWorkbenchSite site, Class<T> expectedType) { + return getSelectedEntity(site.getWorkbenchWindow(), expectedType); + } + + @SuppressWarnings({ "unchecked" }) + public <T> T getSelectedEntity(IWorkbenchWindow window, Class<T> expectedType) { + ISelection selection = window.getSelectionService().getSelection(NavigationView.ID); + if (selection instanceof IStructuredSelection) { + Iterator<Object> iter = ((IStructuredSelection) selection).iterator(); + while (iter.hasNext()) { + Object selectedObj = iter.next(); + if (selectedObj.getClass() == expectedType) { + return (T)selectedObj; + } + } + } + return null; + } + + /** + * Fetches the currently selected objects from the workbench site and returns those of given type. If none of the + * selected objects are of given type, returns null + * + * @param site + * The workbench site + * @param expectedType + * Type of the selected objects to look for + * @return The selected objects of given type if found, else null + */ + public <T> Set<T> getSelectedEntities(IWorkbenchSite site, Class<T> expectedType) { + return getSelectedEntities(site.getWorkbenchWindow(), expectedType); + } + + @SuppressWarnings("unchecked") + public <T> Set<T> getSelectedEntities(IWorkbenchWindow window, Class<T> expectedType) { + Set<T> selectedEntities = new HashSet<T>(); + ISelection selection = window.getSelectionService().getSelection(); + if (selection instanceof IStructuredSelection) { + Iterator<Object> iter = ((IStructuredSelection) selection).iterator(); + while (iter.hasNext()) { + Object selectedObj = iter.next(); + if (selectedObj.getClass() == expectedType) { + selectedEntities.add((T) selectedObj); + } + } + } + return selectedEntities; + } + + + public void configureCheckboxTableViewer(final CheckboxTableViewer tableViewer) { + tableViewer.addCheckStateListener(new ICheckStateListener() { + + @Override + public void checkStateChanged(CheckStateChangedEvent event) { + tableViewer.setSelection(new StructuredSelection(tableViewer.getCheckedElements())); + } + }); + + tableViewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @SuppressWarnings("unchecked") + @Override + public void selectionChanged(SelectionChangedEvent event) { + List<Object> checkedElements = Arrays.asList(tableViewer.getCheckedElements()); + List<Object> selectedElements = ((IStructuredSelection)event.getSelection()).toList(); + + if (JavaUtil.listsDiffer(checkedElements, selectedElements)) { + tableViewer.setSelection(new StructuredSelection(tableViewer.getCheckedElements())); + } + } + }); + } + + public void showView(String viewId) { + try { + PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(viewId); + } catch (PartInitException e) { + String errMsg = "Could not open view [" + viewId + "]"; + logger.error(errMsg, e); + throw new GlusterRuntimeException(errMsg, e); + } + } + + public void showTerminalView() { + showView(ConsoleConstants.TERMINAL_VIEW_ID); + } + + public void showProgressView() { + showView(IProgressConstants.PROGRESS_VIEW_ID); + } + + public void showTaskView() { + NavigationView navigationView = (NavigationView) getView(NavigationView.ID); + navigationView.selectEntity(GlusterDataModelManager.getInstance().getModel().getCluster()); + + showView(TasksView.ID); + } + + public void setStatusMessage(String message) { + clearStatusMessage(); + Application.getApplication().getStatusLineManager().setMessage(message); + Application.getApplication().getStatusLineManager().setMessage(message); + } + + public void clearStatusMessage() { + Application.getApplication().getStatusLineManager().setMessage(null); + } + + public String getDiskToolTip(Disk disk) { + return disk.getQualifiedName() + " - " + disk.getDescription(); + } + + public void clearSection(Composite section) { + if (section.isDisposed()) { + return; + } + for(Control control : section.getChildren()) { + if(! control.isDisposed()) { + control.dispose(); + } + } + section.layout(true); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/GlusterChartPalette.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/GlusterChartPalette.java new file mode 100644 index 00000000..4c579103 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/GlusterChartPalette.java @@ -0,0 +1,479 @@ +/*********************************************************************** + * Copyright (c) 2004 Actuate Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Actuate Corporation - initial API and implementation + ***********************************************************************/ + +package org.gluster.storage.management.console.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.birt.chart.engine.i18n.Messages; +import org.eclipse.birt.chart.log.ILogger; +import org.eclipse.birt.chart.log.Logger; +import org.eclipse.birt.chart.model.attribute.AttributeFactory; +import org.eclipse.birt.chart.model.attribute.AttributePackage; +import org.eclipse.birt.chart.model.attribute.ColorDefinition; +import org.eclipse.birt.chart.model.attribute.Fill; +import org.eclipse.birt.chart.model.attribute.Palette; +import org.eclipse.birt.chart.model.attribute.impl.ColorDefinitionImpl; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.common.notify.NotificationChain; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.emf.ecore.impl.ENotificationImpl; +import org.eclipse.emf.ecore.impl.EObjectImpl; +import org.eclipse.emf.ecore.util.EObjectContainmentEList; +import org.eclipse.emf.ecore.util.InternalEList; + +import com.ibm.icu.util.ULocale; + +/** + * <!-- begin-user-doc --> An implementation of the model object ' + * <em><b>Palette</b></em>'. <!-- end-user-doc --> + * <p> + * The following features are implemented: + * <ul> + * <li>{@link org.eclipse.birt.chart.model.attribute.impl.GlusterChartPalette#getName <em>Name</em>}</li> + * <li>{@link org.eclipse.birt.chart.model.attribute.impl.GlusterChartPalette#getEntries <em>Entries</em>}</li> + * </ul> + * </p> + * + * @generated + */ +public class GlusterChartPalette extends EObjectImpl implements Palette +{ + + /** + * The default value of the '{@link #getName() <em>Name</em>}' attribute. + * <!-- begin-user-doc --> <!-- end-user-doc --> + * @see #getName() + * @generated + * @ordered + */ + protected static final String NAME_EDEFAULT = null; + + /** + * The cached value of the '{@link #getName() <em>Name</em>}' attribute. + * <!-- begin-user-doc --> <!-- end-user-doc --> + * @see #getName() + * @generated + * @ordered + */ + protected String name = NAME_EDEFAULT; + + /** + * The cached value of the '{@link #getEntries() <em>Entries</em>}' containment reference list. + * <!-- begin-user-doc --> <!-- end-user-doc --> + * @see #getEntries() + * @generated + * @ordered + */ + protected EList<Fill> entries; + + private static ILogger logger = Logger.getLogger( "org.eclipse.birt.chart.engine/model.attribute.impl" ); //$NON-NLS-1$ + + private static List colorLib = new ArrayList( 32 ); + static + { + colorLib.add( ColorDefinitionImpl.create ( 0, 1, 252) ); + colorLib.add( ColorDefinitionImpl.create ( 255, 0, 255) ); + //colorLib.add( ColorDefinitionImpl.BLUE() ); + //colorLib.add( ColorDefinitionImpl.create( 232, 172, 57 ) ); + } + + /** + * <!-- begin-user-doc --> <!-- end-user-doc --> + * @generated + */ + public GlusterChartPalette( ) + { + super( ); + } + + /** + * <!-- begin-user-doc --> <!-- end-user-doc --> + * @generated + */ + @Override + protected EClass eStaticClass( ) + { + return AttributePackage.Literals.PALETTE; + } + + /** + * <!-- begin-user-doc --> <!-- end-user-doc --> + * @generated + */ + public String getName( ) + { + return name; + } + + /** + * <!-- begin-user-doc --> <!-- end-user-doc --> + * @generated + */ + public void setName( String newName ) + { + String oldName = name; + name = newName; + if ( eNotificationRequired( ) ) + eNotify( new ENotificationImpl( this, + Notification.SET, + AttributePackage.PALETTE__NAME, + oldName, + name ) ); + } + + /** + * <!-- begin-user-doc --> <!-- end-user-doc --> + * @generated + */ + public EList<Fill> getEntries( ) + { + if ( entries == null ) + { + entries = new EObjectContainmentEList<Fill>( Fill.class, + this, + AttributePackage.PALETTE__ENTRIES ); + } + return entries; + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + @Override + public NotificationChain eInverseRemove( InternalEObject otherEnd, + int featureID, NotificationChain msgs ) + { + switch ( featureID ) + { + case AttributePackage.PALETTE__ENTRIES : + return ( (InternalEList<?>) getEntries( ) ).basicRemove( otherEnd, + msgs ); + } + return super.eInverseRemove( otherEnd, featureID, msgs ); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + @Override + public Object eGet( int featureID, boolean resolve, boolean coreType ) + { + switch ( featureID ) + { + case AttributePackage.PALETTE__NAME : + return getName( ); + case AttributePackage.PALETTE__ENTRIES : + return getEntries( ); + } + return super.eGet( featureID, resolve, coreType ); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + @SuppressWarnings("unchecked") + @Override + public void eSet( int featureID, Object newValue ) + { + switch ( featureID ) + { + case AttributePackage.PALETTE__NAME : + setName( (String) newValue ); + return; + case AttributePackage.PALETTE__ENTRIES : + getEntries( ).clear( ); + getEntries( ).addAll( (Collection<? extends Fill>) newValue ); + return; + } + super.eSet( featureID, newValue ); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + @Override + public void eUnset( int featureID ) + { + switch ( featureID ) + { + case AttributePackage.PALETTE__NAME : + setName( NAME_EDEFAULT ); + return; + case AttributePackage.PALETTE__ENTRIES : + getEntries( ).clear( ); + return; + } + super.eUnset( featureID ); + } + + /** + * <!-- begin-user-doc --> + * <!-- end-user-doc --> + * @generated + */ + @Override + public boolean eIsSet( int featureID ) + { + switch ( featureID ) + { + case AttributePackage.PALETTE__NAME : + return NAME_EDEFAULT == null ? name != null + : !NAME_EDEFAULT.equals( name ); + case AttributePackage.PALETTE__ENTRIES : + return entries != null && !entries.isEmpty( ); + } + return super.eIsSet( featureID ); + } + + /** + * <!-- begin-user-doc --> <!-- end-user-doc --> + * @generated + */ + @Override + public String toString( ) + { + if ( eIsProxy( ) ) + return super.toString( ); + + StringBuffer result = new StringBuffer( super.toString( ) ); + result.append( " (name: " ); //$NON-NLS-1$ + result.append( name ); + result.append( ')' ); + return result.toString( ); + } + + /** + * A convenience method provided to create an empty or pre-initialized + * palette + * + * NOTE: Manually written + * + * @param bEmpty + */ + public static final Palette create( int iIndex, boolean bEmpty ) + { + final Palette p = AttributeFactory.eINSTANCE.createPalette( ); + + if ( !bEmpty ) + { + p.shift( iIndex ); + } + return p; + } + + /** + * A convenience method provided to create a palette with a single color + * entry + * + * NOTE: Manually written + * + * @param f + */ + public static final Palette create( Fill f ) + { + final Palette p = AttributeFactory.eINSTANCE.createPalette( ); + p.getEntries( ).add( f ); + return p; + } + + /** + * Shift the list content from tail to head. + * + * @param lst + * @param pos + */ + private static final void shiftList( final List lst, int pos ) + { + int size = lst.size( ); + + if ( pos < 1 ) + { + pos = 0; + } + + if ( pos >= size ) + { + pos = pos % size; + } + + if ( pos == 0 ) + { + return; + } + + Object[] array = lst.toArray( ); + + lst.clear( ); + + for ( int i = pos; i < array.length; i++ ) + { + lst.add( array[i] ); + } + + for ( int i = 0; i < pos; i++ ) + { + lst.add( array[i] ); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.model.attribute.Palette#update(int) + */ + public final void update( int iIndex ) + { + final EList el = getEntries( ); + el.clear( ); + if ( iIndex < 0 ) + { + // a rotation version of palette-0, rataion pos is the negatvie + // index. + ArrayList al = new ArrayList( ); + + al.add( ColorDefinitionImpl.create( 80, 166, 218 ) ); + al.add( ColorDefinitionImpl.create( 242, 88, 106 ) ); + al.add( ColorDefinitionImpl.create( 232, 172, 57 ) ); + al.add( ColorDefinitionImpl.create( 128, 255, 128 ) ); + al.add( ColorDefinitionImpl.create( 64, 128, 128 ) ); + al.add( ColorDefinitionImpl.create( 128, 128, 192 ) ); + al.add( ColorDefinitionImpl.create( 170, 85, 85 ) ); + al.add( ColorDefinitionImpl.create( 128, 128, 0 ) ); + + shiftList( al, -iIndex ); + + el.addAll( al ); + } + else if ( iIndex == 0 ) + { + el.add( ColorDefinitionImpl.create( 80, 166, 218 ) ); + el.add( ColorDefinitionImpl.create( 242, 88, 106 ) ); + el.add( ColorDefinitionImpl.create( 232, 172, 57 ) ); + el.add( ColorDefinitionImpl.create( 128, 255, 128 ) ); + el.add( ColorDefinitionImpl.create( 64, 128, 128 ) ); + el.add( ColorDefinitionImpl.create( 128, 128, 192 ) ); + el.add( ColorDefinitionImpl.create( 170, 85, 85 ) ); + el.add( ColorDefinitionImpl.create( 128, 128, 0 ) ); + } + else if ( iIndex == 1 ) + { + el.add( ColorDefinitionImpl.create( 225, 225, 255 ) ); + el.add( ColorDefinitionImpl.create( 223, 197, 41 ) ); + el.add( ColorDefinitionImpl.create( 249, 225, 191 ) ); + el.add( ColorDefinitionImpl.create( 255, 205, 225 ) ); + el.add( ColorDefinitionImpl.create( 225, 255, 225 ) ); + el.add( ColorDefinitionImpl.create( 255, 191, 255 ) ); + el.add( ColorDefinitionImpl.create( 185, 185, 221 ) ); + el.add( ColorDefinitionImpl.create( 40, 255, 148 ) ); + } + else + { + logger.log( ILogger.WARNING, + Messages.getString( "error.unknown.palette", //$NON-NLS-1$ + new Object[]{ + Integer.valueOf( iIndex ) + }, + ULocale.getDefault( ) ) ); + update( 0 ); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.birt.chart.model.attribute.Palette#update(org.eclipse.birt.chart.model.attribute.Fill) + */ + public final void update( Fill f ) + { + final EList el = getEntries( ); + el.clear( ); + el.add( f ); + } + + public void shift( int step ) + { + shift( step, colorLib.size( ) ); + } + + public void shift( int step, int size ) + { + if ( size <= 0 || size > colorLib.size( ) ) + { + size = colorLib.size( ); + } + + final EList<Fill> el = getEntries( ); + el.clear( ); + + if ( step == 0 || Math.abs( step ) >= size ) + { + // Do nothing + step = 0; + } + else if ( step < 0 ) + { + // Move to the left side + step = -step; + } + else if ( step > 0 ) + { + // Move to the right side + step = size - step; + } + + for ( int i = step; i < size; i++ ) + { + el.add( ( (ColorDefinition) colorLib.get( i ) ).copyInstance( ) ); + } + for ( int i = 0; i < step; i++ ) + { + el.add( ( (ColorDefinition) colorLib.get( i ) ).copyInstance( ) ); + } + } + + /** + * A convenient method to get an instance copy. This is much faster than the + * ECoreUtil.copy(). + */ + public Palette copyInstance( ) + { + GlusterChartPalette dest = new GlusterChartPalette( ); + dest.set( this ); + return dest; + } + + protected void set( Palette src ) + { + if ( src.getEntries( ) != null ) + { + EList<Fill> list = getEntries( ); + for ( Fill element : src.getEntries( ) ) + { + list.add( element.copyInstance( ) ); + } + } + name = src.getName( ); + } + +} // GlusterChartPalette diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/GlusterLogger.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/GlusterLogger.java new file mode 100644 index 00000000..4a2ba53b --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/GlusterLogger.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.utils; + +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.Status; +import org.gluster.storage.management.console.Activator; + + +/** + * + */ +public class GlusterLogger { + private static final ILog log = Activator.getDefault().getLog(); + private static GlusterLogger instance = new GlusterLogger(); + + private GlusterLogger() { + } + + public static GlusterLogger getInstance() { + return instance; + } + + private void log(String message, int severity, Throwable t) { + log.log(new Status(severity, Activator.PLUGIN_ID, message, t)); + } + + public void error(String message) { + log(message, Status.ERROR, null); + } + + public void error(String message, Throwable t) { + log(message, Status.ERROR, t); + } + + public void warn(String message) { + log(message, Status.WARNING, null); + } + + public void warn(String message, Throwable t) { + log(message, Status.WARNING, t); + } + + public void info(String message) { + log(message, Status.INFO, null); + } + + public void info(String message, Throwable t) { + log(message, Status.INFO, t); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/ImageUtil.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/ImageUtil.java new file mode 100644 index 00000000..eb4a31b9 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/ImageUtil.java @@ -0,0 +1,52 @@ +/** + * ImageUtil.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 org.gluster.storage.management.console.utils; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.gluster.storage.management.console.Application; +import org.gluster.storage.management.core.utils.LRUCache; + + +/** + * + */ +public class ImageUtil { + private static final LRUCache<String, Image> imageCache = new LRUCache<String, Image>(20); + + public ImageDescriptor getImageDescriptor(String imagePath) { + return AbstractUIPlugin.imageDescriptorFromPlugin(Application.PLUGIN_ID, imagePath); + } + + public synchronized Image getImage(String imagePath) { + if(imageCache.containsKey(imagePath)) { + return imageCache.get(imagePath); + } + return createImage(imagePath); + } + + private Image createImage(String imagePath) { + Image image = getImageDescriptor(imagePath).createImage(); + imageCache.put(imagePath, image); + return image; + } +}
\ No newline at end of file diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/TableViewerComparator.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/TableViewerComparator.java new file mode 100644 index 00000000..9df3942a --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/utils/TableViewerComparator.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.utils; + +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.swt.SWT; + +/** + * Comparator for sorting contents of a table viewer + */ +public class TableViewerComparator extends ViewerComparator { + private int column = -1; + private static final int ASCENDING = 0; + private static final int DESCENDING = 1; + private static final int NONE = -1; + private int direction = DESCENDING; + + public TableViewerComparator() { + this(NONE); + } + + public TableViewerComparator(int direction) { + this.direction = direction; + } + + public int getDirection() { + return direction == DESCENDING ? SWT.DOWN : (direction == ASCENDING ? SWT.UP : SWT.NONE); + } + + public void setColumn(int column) { + if (column == this.column) { + // Same column as last sort; toggle the direction + direction = 1 - direction; + } else { + // first column selection or new column; do an ascending sort + direction = ASCENDING; + this.column = column; + } + } + + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + if(direction == NONE) { + // no sorting + return 0; + } + + int result = super.compare(viewer, e1, e2); + // If descending order, flip the direction + if (direction == DESCENDING) { + result = -result; + } + + return result; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/validators/StringRequiredValidator.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/validators/StringRequiredValidator.java new file mode 100644 index 00000000..5d364606 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/validators/StringRequiredValidator.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.validators; + +import org.eclipse.core.databinding.validation.IValidator; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.swt.widgets.Control; + +public class StringRequiredValidator implements IValidator { + protected final String errorText; + protected final ControlDecoration controlDecoration; + protected final Control linkedControl; + + public StringRequiredValidator(String errorText, ControlDecoration controlDecoration, Control linkedControl) { + super(); + this.errorText = errorText; + this.controlDecoration = controlDecoration; + this.linkedControl = linkedControl; + } + + public StringRequiredValidator(String errorText, ControlDecoration controlDecoration) { + this(errorText, controlDecoration, null); + } + + public IStatus validate(Object value) { + if (value instanceof String) { + if (((String) value).isEmpty()) { + controlDecoration.setDescriptionText(errorText); + controlDecoration.show(); + if (linkedControl != null) { + linkedControl.setEnabled(false); + } + return ValidationStatus.error(errorText); + } + } + if(linkedControl != null) { + linkedControl.setEnabled(true); + } + controlDecoration.hide(); + return Status.OK_STATUS; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/ClusterAdapterFactory.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/ClusterAdapterFactory.java new file mode 100644 index 00000000..a830d4ea --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/ClusterAdapterFactory.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views; + +import org.eclipse.core.runtime.IAdapterFactory; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.model.IWorkbenchAdapter; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.gluster.storage.management.console.Application; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.core.model.Cluster; +import org.gluster.storage.management.core.model.Entity; +import org.gluster.storage.management.core.model.EntityGroup; +import org.gluster.storage.management.core.model.GlusterDataModel; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.Server; +import org.gluster.storage.management.core.model.Volume; + + +public class ClusterAdapterFactory implements IAdapterFactory { + private IWorkbenchAdapter entityAdapter = new IWorkbenchAdapter() { + + @Override + public Object getParent(Object o) { + return ((Entity) o).getParent(); + } + + @Override + public String getLabel(Object o) { + return ((Entity)o).getName(); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public ImageDescriptor getImageDescriptor(Object object) { + String iconPath = null; + + if(object instanceof GlusterDataModel || object instanceof Cluster) { + iconPath = IImageKeys.CLUSTER_16x16; + } + + if(object instanceof EntityGroup) { + Class<? extends Entity> entityType = ((EntityGroup) object).getEntityType(); + if(entityType == Volume.class) { + iconPath = IImageKeys.VOLUMES_16x16; + } else { + iconPath = IImageKeys.SERVERS_16x16; + } + } + + if(object instanceof Volume) { + iconPath = IImageKeys.VOLUME_16x16; + } + + if(object instanceof Server || object instanceof GlusterServer) { + iconPath = IImageKeys.SERVER_16x16; + } + + return AbstractUIPlugin.imageDescriptorFromPlugin( + Application.PLUGIN_ID, iconPath); + } + + @Override + public Object[] getChildren(Object o) { + return ((Entity)o).getChildren().toArray(); + } + }; + + @SuppressWarnings("rawtypes") + @Override + public Object getAdapter(Object adaptableObject, Class adapterType) { + if (adapterType == IWorkbenchAdapter.class) { + if (adaptableObject instanceof Entity) { + return entityAdapter; + } + } + return null; + } + + @SuppressWarnings("rawtypes") + @Override + public Class[] getAdapterList() { + return new Class[] { IWorkbenchAdapter.class }; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/ClusterSummaryView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/ClusterSummaryView.java new file mode 100644 index 00000000..41d2daa7 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/ClusterSummaryView.java @@ -0,0 +1,469 @@ +/** + * DiscoveredServerView.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 org.gluster.storage.management.console.views; + +import java.util.List; + +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +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 org.gluster.storage.management.console.Activator; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.console.actions.ActionConstants; +import org.gluster.storage.management.console.preferences.PreferenceConstants; +import org.gluster.storage.management.console.utils.ChartUtil; +import org.gluster.storage.management.console.utils.ChartViewerComposite; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.utils.ChartUtil.ChartPeriodLinkListener; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.constants.GlusterConstants; +import org.gluster.storage.management.core.model.Alert; +import org.gluster.storage.management.core.model.Cluster; +import org.gluster.storage.management.core.model.ClusterListener; +import org.gluster.storage.management.core.model.DefaultClusterListener; +import org.gluster.storage.management.core.model.EntityGroup; +import org.gluster.storage.management.core.model.GlusterDataModel; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.Server; +import org.gluster.storage.management.core.model.ServerStats; +import org.gluster.storage.management.core.model.Status; +import org.gluster.storage.management.core.model.TaskInfo; +import org.gluster.storage.management.core.model.Server.SERVER_STATUS; + + +/** + * + */ +public class ClusterSummaryView extends ViewPart { + public static final String ID = ClusterSummaryView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private final FormToolkit toolkit = new FormToolkit(Display.getCurrent()); + private ScrolledForm form; + private Cluster cluster; + private Composite cpuChartSection; + private Composite networkChartSection; + private GlusterDataModel model = GlusterDataModelManager.getInstance().getModel(); + private ClusterListener clusterListener; + private static final IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore(); + private Composite alertsSection; + private Composite tasksSection; + private static final ChartUtil chartUtil = ChartUtil.getInstance(); + private IPropertyChangeListener propertyChangeListener; + private Label onlineServerCountLabel; + private Label offlineServerCountLabel; + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) + */ + @Override + public void createPartControl(Composite parent) { + if (cluster == null) { + cluster = model.getCluster(); + } + setPartName("Summary"); + createSections(parent); + + createListeners(); + } + + private void createListeners() { + createClusterListener(); + GlusterDataModelManager.getInstance().addClusterListener(clusterListener); + + createPropertyChangeListener(); + preferenceStore.addPropertyChangeListener(propertyChangeListener ); + } + + private void createPropertyChangeListener() { + propertyChangeListener = new IPropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent event) { + String preferenceName = event.getProperty(); + GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + if(preferenceName.equals(PreferenceConstants.P_CPU_AGGREGATED_CHART_PERIOD)) { + modelManager.initializeAggregatedCpuStats(cluster); + String cpuStatsPeriod = (String)event.getNewValue(); + refreshChartSection(cpuChartSection, cluster.getAggregatedCpuStats(), cpuStatsPeriod, "%", 100, 4, + chartUtil.new CpuChartPeriodLinkListener(null, cpuStatsPeriod, toolkit), 2); + } else if(preferenceName.equals(PreferenceConstants.P_NETWORK_AGGREGATED_CHART_PERIOD)) { + modelManager.initializeAggregatedNetworkStats(cluster); + String networkStatsPeriod = (String)event.getNewValue(); + refreshChartSection(networkChartSection, cluster.getAggregatedNetworkStats(), networkStatsPeriod, "KiB/s", -1, + 4, chartUtil.new NetworkChartPeriodLinkListener(null, networkStatsPeriod, toolkit), 2); + } + } + }; + } + + private void createClusterListener() { + clusterListener = new DefaultClusterListener() { + @Override + public void aggregatedStatsChanged() { + super.aggregatedStatsChanged(); + refreshCharts(); + populateOnlineOfflineServerCount(); + } + + @Override + public void alertsGenerated() { + super.alertsGenerated(); + guiHelper.clearSection(alertsSection); + populateAlerts(); + } + + @Override + public void taskAdded(TaskInfo taskInfo) { + super.taskAdded(taskInfo); + updateTaskSection(); + } + + @Override + public void taskRemoved(TaskInfo taskInfo) { + super.taskRemoved(taskInfo); + updateTaskSection(); + } + + @Override + public void taskUpdated(TaskInfo taskInfo) { + super.taskUpdated(taskInfo); + updateTaskSection(); + } + + private void updateTaskSection() { + guiHelper.clearSection(tasksSection); + populateTasksSection(); + } + }; + } + + @Override + public void dispose() { + super.dispose(); + GlusterDataModelManager.getInstance().removeClusterListener(clusterListener); + preferenceStore.removePropertyChangeListener(propertyChangeListener); + } + + private void refreshCharts() { + String cpuStatsPeriod = preferenceStore.getString(PreferenceConstants.P_CPU_AGGREGATED_CHART_PERIOD); + String networkStatsPeriod = preferenceStore.getString(PreferenceConstants.P_NETWORK_AGGREGATED_CHART_PERIOD); + refreshChartSection(cpuChartSection, cluster.getAggregatedCpuStats(), cpuStatsPeriod, "%", 100, 4, + chartUtil.new CpuChartPeriodLinkListener(null, cpuStatsPeriod, toolkit), 2); + refreshChartSection(networkChartSection, cluster.getAggregatedNetworkStats(), networkStatsPeriod, "KiB/s", -1, + 4, chartUtil.new NetworkChartPeriodLinkListener(null, networkStatsPeriod, toolkit), 2); + } + + private int getServerCountByStatus(Cluster cluster, SERVER_STATUS status) { + int count = 0; + for (GlusterServer server : cluster.getServers()) { + if (server.getStatus() == status) { + count++; + } + } + return count; + } + + private void createServersSection() { + Composite section = guiHelper.createSection(form, toolkit, "Servers", null, 2, false); + toolkit.createLabel(section, "Online : "); + onlineServerCountLabel = toolkit.createLabel(section, ""); + + toolkit.createLabel(section, "Offline : "); + offlineServerCountLabel = toolkit.createLabel(section, ""); + populateOnlineOfflineServerCount(); + } + + private void populateOnlineOfflineServerCount() { + int onlineServerCount = getServerCountByStatus(cluster, SERVER_STATUS.ONLINE); + int offlineServerCount = getServerCountByStatus(cluster, SERVER_STATUS.OFFLINE); + onlineServerCountLabel.setText("" + onlineServerCount); + onlineServerCountLabel.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GREEN)); + offlineServerCountLabel.setText("" + offlineServerCount); + offlineServerCountLabel.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_RED)); + } + + + private void createDiskSpaceSection() { + Composite section = guiHelper.createSection(form, toolkit, "Disk Space", null, 3, false); + if (cluster.getServers().size() == 0) { + toolkit.createLabel(section, "This section will be populated after at least" + CoreConstants.NEWLINE + + "one server is added to the storage cloud."); + return; + } + + double totalDiskSpace = cluster.getTotalDiskSpace(); + double diskSpaceInUse = cluster.getDiskSpaceInUse(); + Double[] values = new Double[] { diskSpaceInUse / 1024, (totalDiskSpace - diskSpaceInUse) / 1024 }; + createDiskSpaceChart(section, values); + } + + private void createDiskSpaceChart(Composite section, Double[] values) { + String[] categories = new String[] { "Used Space (GB)", "Free Space (GB)" }; + ChartViewerComposite chartViewerComposite = new ChartViewerComposite(section, SWT.NONE, categories, values); + + GridData data = new GridData(SWT.FILL, SWT.FILL, false, false); + data.widthHint = 400; + data.heightHint = 180; + data.verticalAlignment = SWT.CENTER; + chartViewerComposite.setLayoutData(data); + } + + private void createAlertsSection() { + alertsSection = guiHelper.createSection(form, toolkit, "Alerts", null, 1, false); + populateAlerts(); + } + + private void populateAlerts() { + List<Alert> alerts = cluster.getAlerts(); + for (Alert alert : alerts) { + addAlertLabel(alertsSection, alert); + } + alertsSection.layout(); + form.reflow(true); + } + + private void addAlertLabel(Composite section, Alert alert) { + CLabel lblAlert = new CLabel(section, SWT.FLAT); + GridData layoutData = new GridData(); + layoutData.widthHint = 400; + layoutData.horizontalIndent = 20; + lblAlert.setLayoutData(layoutData); + + Image alertImage = null; + switch (alert.getType()) { + case OFFLINE_VOLUME_BRICKS_ALERT: + alertImage = guiHelper.getImage(IImageKeys.BRICK_OFFLINE_22x22); + break; + case DISK_USAGE_ALERT: + alertImage = guiHelper.getImage(IImageKeys.LOW_DISK_SPACE_22x22); + break; + case OFFLINE_SERVERS_ALERT: + alertImage = guiHelper.getImage(IImageKeys.SERVER_OFFLINE_22x22); + break; + case MEMORY_USAGE_ALERT: + alertImage = guiHelper.getImage(IImageKeys.MEMORY_USAGE_ALERT_22x22); + break; + case CPU_USAGE_ALERT: + alertImage = guiHelper.getImage(IImageKeys.SERVER_WARNING_22x22); + break; + case OFFLINE_VOLUME_ALERT: + alertImage = guiHelper.getImage(IImageKeys.VOLUME_OFFLINE_22x22); + break; + } + lblAlert.setText(alert.getMessage()); + ControlDecoration dec = new ControlDecoration(lblAlert, SWT.LEFT); + dec.setImage(alertImage); + lblAlert.redraw(); + } + + private void createActionsSection() { + Composite section = guiHelper.createSection(form, toolkit, "Actions", null, 1, false); + + ImageHyperlink imageHyperlink = toolkit.createImageHyperlink(section, SWT.NONE); + imageHyperlink.setText("Create Volume"); + imageHyperlink.setImage(guiHelper.getImage(IImageKeys.CREATE_VOLUME_48x48)); + imageHyperlink.addHyperlinkListener(new HyperlinkAdapter() { + @Override + public void linkActivated(HyperlinkEvent e) { + IHandlerService hs = (IHandlerService) getSite().getService(IHandlerService.class); + try { + hs.executeCommand(ActionConstants.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_48x48)); + imageHyperlink.addHyperlinkListener(new HyperlinkAdapter() { + @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().getEntityGroup(Server.class); + + NavigationView navigationView = (NavigationView) guiHelper.getView(NavigationView.ID); + navigationView.selectEntity(autoDiscoveredServersEntityGroup); + } + }); + } + + private void createSections(Composite parent) { + form = guiHelper.setupForm(parent, toolkit, "Cluster Summary"); + + if (cluster.getServers().size() > 0 + && (cluster.getAggregatedCpuStats().getRows() == null || cluster.getAggregatedNetworkStats().getRows() == null)) { + // cluster has servers, but stats are null. Happens when user logs in to a new cluster, ' + // and then adds the first server. + GlusterDataModelManager.getInstance().initializeAggregatedCpuStats(cluster); + GlusterDataModelManager.getInstance().initializeAggregatedNetworkStats(cluster); + } + + createServersSection(); + createDiskSpaceSection(); + createCPUUsageSection(); + createNetworkUsageSection(); + createActionsSection(); + createAlertsSection(); + createTasksSection(); + + parent.layout(); // IMP: lays out the form properly + } + + private Composite createAreaChartSection(ServerStats stats, String sectionTitle, int dataColumnIndex, String unit, String timestampFormat, ChartPeriodLinkListener listener, double maxValue, int chartLinkColumnCount) { + Composite section = guiHelper.createSection(form, toolkit, sectionTitle, null, 1, false); + if (cluster.getServers().size() == 0) { + toolkit.createLabel(section, "This section will be populated after at least" + CoreConstants.NEWLINE + + "one server is added to the storage cloud."); + return section; + } + + ChartUtil.getInstance().createAreaChart(toolkit, section, stats, dataColumnIndex, unit, timestampFormat, listener, maxValue, chartLinkColumnCount); + return section; + } + + private void createCPUUsageSection() { + IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore(); + String cpuStatsPeriod = preferenceStore.getString(PreferenceConstants.P_CPU_AGGREGATED_CHART_PERIOD); + + // in case of CPU usage, there are three elements in usage data: user, system and total. we use total. + cpuChartSection = createAreaChartSection(cluster.getAggregatedCpuStats(), "CPU Usage (Aggregated)", 2, "%", + getTimestampFormatForPeriod(cpuStatsPeriod), chartUtil.new CpuChartPeriodLinkListener(null, + cpuStatsPeriod, toolkit), 100, 4); + } + + private String getTimestampFormatForPeriod(String statsPeriod) { + if(statsPeriod.equals(GlusterConstants.STATS_PERIOD_1DAY)) { + return "HH:mm"; + } else if (statsPeriod.equals(GlusterConstants.STATS_PERIOD_1WEEK)) { + return "dd-MMM HH:mm"; + } else { + return "dd-MMM"; + } + } + + private void createNetworkUsageSection() { + IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore(); + String networkStatsPeriod = preferenceStore.getString(PreferenceConstants.P_NETWORK_AGGREGATED_CHART_PERIOD); + + // in case of network usage, there are three elements in usage data: received, transmitted and total. we use total. + networkChartSection = createAreaChartSection(cluster.getAggregatedNetworkStats(), "Network Usage (Aggregated)", + 2, "KiB/s", getTimestampFormatForPeriod(networkStatsPeriod), + chartUtil.new NetworkChartPeriodLinkListener(null, networkStatsPeriod, toolkit), -1, 4); + } + + private void createTasksSection() { + tasksSection = guiHelper.createSection(form, toolkit, CoreConstants.RUNNING_TASKS, null, 1, false); + populateTasksSection(); + } + + private void populateTasksSection() { + for (TaskInfo taskInfo : cluster.getTaskInfoList()) { + if (taskInfo.getStatus().getCode() != Status.STATUS_CODE_SUCCESS) { + addTaskLabel(tasksSection, taskInfo); + } + } + tasksSection.layout(); + form.reflow(true); + } + + private void addTaskLabel(Composite section, TaskInfo taskInfo) { + //TODO: create link and open the task progress view + CLabel lblAlert = new CLabel(section, SWT.NONE); + + Image taskImage = null; + switch(taskInfo.getType()) { + case DISK_FORMAT: + taskImage = guiHelper.getImage(IImageKeys.DISK_INITIALIZING_22x22); + break; + case BRICK_MIGRATE: + taskImage = guiHelper.getImage(IImageKeys.BRICK_MIGRATE_22x22); + break; + case VOLUME_REBALANCE: + taskImage = guiHelper.getImage(IImageKeys.VOLUME_REBALANCE_22x22); + break; + } + + String description = taskInfo.getDescription(); + switch (taskInfo.getStatus().getCode()) { + case Status.STATUS_CODE_PAUSE: + description += " (paused)"; + break; + case Status.STATUS_CODE_COMMIT_PENDING: + description += " (commit pending)"; + break; + case Status.STATUS_CODE_FAILURE: + description += " (failed)"; + break; + } + lblAlert.setText(description); + lblAlert.setImage(taskImage); + lblAlert.redraw(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.part.WorkbenchPart#setFocus() + */ + @Override + public void setFocus() { + if (form != null) { + form.setFocus(); + } + } + + private void refreshChartSection(Composite section, ServerStats stats, String statsPeriod, String unit, + double maxValue, int columnCount, ChartPeriodLinkListener linkListener, int dataColumnIndex) { + if(stats == null) { + return; + } + + for (Control control : section.getChildren()) { + if (!control.isDisposed()) { + control.dispose(); + } + } + chartUtil.createAreaChart(toolkit, section, stats, dataColumnIndex, unit, + getTimestampFormatForPeriod(statsPeriod), linkListener, maxValue, columnCount); + section.layout(); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/DiscoveredServerView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/DiscoveredServerView.java new file mode 100644 index 00000000..7ae6fe61 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/DiscoveredServerView.java @@ -0,0 +1,90 @@ +/** + * DiscoveredServerView.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 org.gluster.storage.management.console.views; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.model.Server; +import org.gluster.storage.management.core.utils.NumberUtil; + + +/** + * @author root + * + */ +public class DiscoveredServerView extends ViewPart { + public static final String ID = DiscoveredServerView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private final FormToolkit toolkit = new FormToolkit(Display.getCurrent()); + private ScrolledForm form; + private Server server; + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) + */ + @Override + public void createPartControl(Composite parent) { + if (server == null) { + server = guiHelper.getSelectedEntity(getSite(), Server.class); + } + createSections(parent); + } + + private void createServerSummarySection() { + Composite section = guiHelper.createSection(form, toolkit, "Summary", null, 2, false); + + toolkit.createLabel(section, "Number of CPUs: ", SWT.NONE); + toolkit.createLabel(section, "" + server.getNumOfCPUs(), SWT.NONE); + + toolkit.createLabel(section, "Total Memory (GB): ", SWT.NONE); + toolkit.createLabel(section, "" + NumberUtil.formatNumber((server.getTotalMemory() / 1024)), SWT.NONE); + + toolkit.createLabel(section, "Total Disk Space (GB): ", SWT.NONE); + toolkit.createLabel(section, "" + NumberUtil.formatNumber((server.getTotalDiskSpace() / 1024)), SWT.NONE); + } + + private void createSections(Composite parent) { + String serverName = server.getName(); + form = guiHelper.setupForm(parent, toolkit, "Discovered Server Summary [" + serverName + "]"); + createServerSummarySection(); + + parent.layout(); // IMP: lays out the form properly + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.part.WorkbenchPart#setFocus() + */ + @Override + public void setFocus() { + if (form != null) { + form.setFocus(); + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/DiscoveredServersView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/DiscoveredServersView.java new file mode 100644 index 00000000..e3e7ec8a --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/DiscoveredServersView.java @@ -0,0 +1,83 @@ +/** + * DiscoveredServersView.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 org.gluster.storage.management.console.views; + +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.ServersPage; +import org.gluster.storage.management.core.model.Entity; +import org.gluster.storage.management.core.model.EntityGroup; +import org.gluster.storage.management.core.model.Server; + + +/** + * + */ +public class DiscoveredServersView extends ViewPart implements IDoubleClickListener { + public static final String ID = DiscoveredServersView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private EntityGroup<Server> servers; + private ServersPage page; + + public DiscoveredServersView() { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public void createPartControl(Composite parent) { + if (servers == null) { + Object selectedObj = guiHelper.getSelectedEntity(getSite(), EntityGroup.class); + if (selectedObj != null && ((EntityGroup) selectedObj).getEntityType() == Server.class) { + servers = (EntityGroup<Server>)selectedObj; + } + } + + page = new ServersPage(parent, getSite(), servers); + page.addDoubleClickListener(this); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.part.WorkbenchPart#setFocus() + */ + @Override + public void setFocus() { + page.setFocus(); + } + + @Override + public void doubleClick(DoubleClickEvent event) { + NavigationView clusterView = (NavigationView) guiHelper.getView(NavigationView.ID); + if (clusterView != null) { + clusterView.selectEntity((Entity) ((StructuredSelection) event.getSelection()).getFirstElement()); + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/DisksView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/DisksView.java new file mode 100644 index 00000000..1263a07f --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/DisksView.java @@ -0,0 +1,45 @@ +package org.gluster.storage.management.console.views; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.DisksPage; +import org.gluster.storage.management.core.model.Disk; +import org.gluster.storage.management.core.model.EntityGroup; +import org.gluster.storage.management.core.model.GlusterServer; + + +public class DisksView extends ViewPart { + public static final String ID = DisksView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private EntityGroup<GlusterServer> servers; + private DisksPage page; + + @SuppressWarnings("unchecked") + @Override + public void createPartControl(Composite parent) { + if (servers == null) { + servers = guiHelper.getSelectedEntity(getSite(), EntityGroup.class); + } + + page = new DisksPage(parent, SWT.NONE, getSite(), getAllDisks(servers)); + //page.layout(); // IMP: lays out the form properly + } + + private List<Disk> getAllDisks(EntityGroup<GlusterServer> servers) { + List<Disk> disks = new ArrayList<Disk>(); + for(GlusterServer server : servers.getEntities()) { + disks.addAll(server.getDisks()); + } + return disks; + } + + @Override + public void setFocus() { + page.setFocus(); + } +}
\ No newline at end of file diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServerDisksView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServerDisksView.java new file mode 100644 index 00000000..8aa0fde1 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServerDisksView.java @@ -0,0 +1,84 @@ +/** + * GlusterServerDisksView.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 org.gluster.storage.management.console.views; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.ServerDisksPage; +import org.gluster.storage.management.core.model.ClusterListener; +import org.gluster.storage.management.core.model.DefaultClusterListener; +import org.gluster.storage.management.core.model.Event; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.Event.EVENT_TYPE; + + +public class GlusterServerDisksView extends ViewPart { + public static final String ID = GlusterServerDisksView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private ClusterListener clusterListener; + private GlusterServer server; + private ServerDisksPage page; + + /* (non-Javadoc) + * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) + */ + @Override + public void createPartControl(Composite parent) { + if (server == null) { + server = guiHelper.getSelectedEntity(getSite(), GlusterServer.class); + } + page = new ServerDisksPage(parent, SWT.NONE, getSite(), server.getDisks()); + + final ViewPart thisView = this; + clusterListener = new DefaultClusterListener() { + @Override + public void serverChanged(GlusterServer server, Event event) { + super.serverChanged(server, event); + if(event.getEventType() == EVENT_TYPE.GLUSTER_SERVER_CHANGED) { + if(!server.isOnline()) { + getViewSite().getPage().hideView(thisView); + } + } + } + }; + + GlusterDataModelManager.getInstance().addClusterListener(clusterListener); + + parent.layout(); // IMP: lays out the form properly + } + + @Override + public void dispose() { + super.dispose(); + GlusterDataModelManager.getInstance().removeClusterListener(clusterListener); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.part.WorkbenchPart#setFocus() + */ + @Override + public void setFocus() { + page.setFocus(); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServerLogsView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServerLogsView.java new file mode 100644 index 00000000..2e8ebe60 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServerLogsView.java @@ -0,0 +1,59 @@ +/** + * GlusterServerLogsView.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 org.gluster.storage.management.console.views; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.ServerLogsPage; +import org.gluster.storage.management.core.model.GlusterServer; + + +public class GlusterServerLogsView extends ViewPart { + public static final String ID = GlusterServerLogsView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private GlusterServer server; + private ServerLogsPage page; + + /* (non-Javadoc) + * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) + */ + @Override + public void createPartControl(Composite parent) { + if (server == null) { + server = guiHelper.getSelectedEntity(getSite(), GlusterServer.class); + } + + page = new ServerLogsPage(parent, SWT.NONE, server); + + parent.layout(); // IMP: lays out the form properly + } + + /* (non-Javadoc) + * @see org.eclipse.ui.part.WorkbenchPart#setFocus() + */ + @Override + public void setFocus() { + page.setFocus(); + } +} + diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServerSummaryView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServerSummaryView.java new file mode 100644 index 00000000..e5570ad9 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServerSummaryView.java @@ -0,0 +1,544 @@ +/** + * GlusterServerSummaryView.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 org.gluster.storage.management.console.views; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.birt.chart.util.CDateTime; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.layout.TableColumnLayout; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.ProgressBar; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.Hyperlink; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.client.GlusterServersClient; +import org.gluster.storage.management.console.Activator; +import org.gluster.storage.management.console.ConsoleConstants; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.console.NetworkInterfaceTableLabelProvider; +import org.gluster.storage.management.console.preferences.PreferenceConstants; +import org.gluster.storage.management.console.toolbar.GlusterToolbarManager; +import org.gluster.storage.management.console.utils.ChartUtil; +import org.gluster.storage.management.console.utils.ChartViewerComposite; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.utils.GlusterLogger; +import org.gluster.storage.management.console.utils.ChartUtil.ChartPeriodLinkListener; +import org.gluster.storage.management.core.model.ClusterListener; +import org.gluster.storage.management.core.model.DefaultClusterListener; +import org.gluster.storage.management.core.model.Event; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.ServerStats; +import org.gluster.storage.management.core.model.ServerStatsRow; +import org.gluster.storage.management.core.model.Event.EVENT_TYPE; +import org.gluster.storage.management.core.model.Server.SERVER_STATUS; +import org.gluster.storage.management.core.utils.NumberUtil; + +import com.ibm.icu.util.Calendar; +import com.richclientgui.toolbox.gauges.CoolGauge; + +public class GlusterServerSummaryView extends ViewPart { + public static final String ID = GlusterServerSummaryView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private final FormToolkit toolkit = new FormToolkit(Display.getCurrent()); + private ScrolledForm form; + private GlusterServer server; + private ClusterListener clusterListener; + private static final int CHART_WIDTH = 350; + private static final int CHART_HEIGHT = 250; + private static final GlusterLogger logger = GlusterLogger.getInstance(); + private static final IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore(); + + public enum NETWORK_INTERFACE_TABLE_COLUMN_INDICES { + INTERFACE, MODEL, SPEED, IP_ADDRESS, NETMASK, GATEWAY + }; + + private static final String[] NETWORK_INTERFACE_TABLE_COLUMN_NAMES = { "Interface", "Model", "Speed", "IP Address", + "Netmask", "Gateway" }; + private CoolGauge cpuGauge; + private IPropertyChangeListener propertyChangeListener; + private Composite cpuUsageSection; + private Composite networkUsageSection; + private Composite memoryUsageSection; + private static final ChartUtil chartUtil = ChartUtil.getInstance(); + private Composite serverSummarySection; + private Label numCpus; + private ProgressBar memoryUsageBar; + private ProgressBar diskUsageBar; + private CLabel lblServerStatus; + + @Override + public void createPartControl(Composite parent) { + if (server == null) { + server = guiHelper.getSelectedEntity(getSite(), GlusterServer.class); + } + setPartName("Summary"); + createSections(parent); + + createListeners(); + } + + private void createListeners() { + // Refresh the server details whenever the server has changed + createClusterListener(); + GlusterDataModelManager.getInstance().addClusterListener(clusterListener); + + createPropertyChangeListener(); + preferenceStore.addPropertyChangeListener(propertyChangeListener); + } + + private void createPropertyChangeListener() { + propertyChangeListener = new IPropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent event) { + String propertyName = event.getProperty(); + if(propertyName.equals(PreferenceConstants.P_CPU_CHART_PERIOD)) { + refreshCpuChart(); + } else if(propertyName.equals(PreferenceConstants.P_MEM_CHART_PERIOD)) { + refreshMemoryChart(); + } else if (propertyName.equals(PreferenceConstants.P_NETWORK_CHART_PERIOD) + || propertyName.equals(PreferenceConstants.P_DEFAULT_NETWORK_INTERFACE_PFX + server.getName())) { + refreshNetworkChart(); + } + } + }; + } + + private void createClusterListener() { + final GlusterToolbarManager toolbarManager = new GlusterToolbarManager(getSite().getWorkbenchWindow()); + final GlusterServer thisServer = server; + clusterListener = new DefaultClusterListener() { + + @Override + public void serverChanged(GlusterServer server, Event event) { + if (event.getEventType() == EVENT_TYPE.GLUSTER_SERVER_CHANGED && server == thisServer) { + updateServerDetails(); + toolbarManager.updateToolbar(server); + refreshCharts(); + } + } + }; + } + + private void refreshCharts() { + refreshCpuChart(); + refreshMemoryChart(); + refreshNetworkChart(); + } + + private void refreshNetworkChart() { + guiHelper.clearSection(networkUsageSection); + String statsPeriod = preferenceStore.getString(PreferenceConstants.P_NETWORK_CHART_PERIOD); + String networkInterface = preferenceStore.getString(PreferenceConstants.P_DEFAULT_NETWORK_INTERFACE_PFX + server.getName()); + if(networkInterface == null || networkInterface.isEmpty()) { + networkInterface = server.getNetworkInterfaces().get(0).getName(); + } + ServerStats stats = new GlusterServersClient().getNetworkStats(server.getName(), networkInterface, statsPeriod); + chartUtil.refreshChartSection(toolkit, networkUsageSection, stats, statsPeriod, "KiB/s", -1, 5, chartUtil.new NetworkChartPeriodLinkListener(server, statsPeriod, toolkit), 2); + } + + private void refreshMemoryChart() { + guiHelper.clearSection(memoryUsageSection); + String statsPeriod = preferenceStore.getString(PreferenceConstants.P_MEM_CHART_PERIOD); + ServerStats stats = new GlusterServersClient().getMemoryStats(server.getName(), statsPeriod); + chartUtil.refreshChartSection(toolkit, memoryUsageSection, stats, statsPeriod, "%", 100, 4, chartUtil.new MemoryChartPeriodLinkListener(server.getName(), statsPeriod, toolkit), 0); + } + + private void refreshCpuChart() { + guiHelper.clearSection(cpuUsageSection); + String statsPeriod = preferenceStore.getString(PreferenceConstants.P_CPU_CHART_PERIOD); + ServerStats stats = new GlusterServersClient().getCpuStats(server.getName(), statsPeriod); + chartUtil.refreshChartSection(toolkit, cpuUsageSection, stats, statsPeriod, "%", 100, 4, + chartUtil.new CpuChartPeriodLinkListener(server.getName(), statsPeriod, toolkit), 2); + } + + private void updateServerDetails() { + // TODO: Update the server details (cpu usage, memory usage) + populateServerSummarySection(server); + + } + + @Override + public void dispose() { + super.dispose(); + GlusterDataModelManager.getInstance().removeClusterListener(clusterListener); + preferenceStore.removePropertyChangeListener(propertyChangeListener); + } + + private void createAreaChart(Composite section, Calendar timestamps[], Double values[], String unit) { + ChartViewerComposite chartViewerComposite = new ChartViewerComposite(section, SWT.NONE, timestamps, values, unit, "HH:mm", 100); + GridData data = new GridData(SWT.FILL, SWT.FILL, false, false); + data.widthHint = CHART_WIDTH; + data.heightHint = CHART_HEIGHT; + chartViewerComposite.setLayoutData(data); + } + + private void extractChartData(ServerStats stats, List<Calendar> timestamps, List<Double> data, int dataColumnIndex) { + for(ServerStatsRow row : stats.getRows()) { + Double cpuUsage = row.getUsageData().get(dataColumnIndex); + if(!cpuUsage.isNaN()) { + timestamps.add(new CDateTime(row.getTimestamp() * 1000)); + data.add(cpuUsage); + } + } + } + + private void createAreaChartSection(ServerStats stats, String sectionTitle, int dataColumnIndex, String unit) { + List<Calendar> timestamps = new ArrayList<Calendar>(); + List<Double> data = new ArrayList<Double>(); + extractChartData(stats, timestamps, data, dataColumnIndex); + + if(timestamps.size() == 0) { + // Log a message saying no CPU stats available + return; + } + + Composite section = guiHelper.createSection(form, toolkit, sectionTitle, null, 1, false); + createAreaChart(section, timestamps.toArray(new Calendar[0]), data.toArray(new Double[0]), unit); + +// Calendar[] timestamps = new Calendar[] { new CDateTime(1000l*1310468100), new CDateTime(1000l*1310468400), new CDateTime(1000l*1310468700), +// new CDateTime(1000l*1310469000), new CDateTime(1000l*1310469300), new CDateTime(1000l*1310469600), new CDateTime(1000l*1310469900), +// new CDateTime(1000l*1310470200), new CDateTime(1000l*1310470500), new CDateTime(1000l*1310470800), new CDateTime(1000l*1310471100), +// new CDateTime(1000l*1310471400), new CDateTime(1000l*1310471700), new CDateTime(1000l*1310472000), new CDateTime(1000l*1310472300), +// new CDateTime(1000l*1310472600), new CDateTime(1000l*1310472900), new CDateTime(1000l*1310473200), new CDateTime(1000l*1310473500), +// new CDateTime(1000l*1310473800) }; +// +// Double[] values = new Double[] { 10d, 11.23d, 17.92d, 18.69d, 78.62d, 89.11d, 92.43d, 89.31d, 57.39d, 18.46d, 10.44d, 16.28d, 13.51d, 17.53d, 12.21, 20d, 21.43d, 16.45d, 14.86d, 15.27d }; +// createLineChart(section, timestamps, values, "%"); + createChartLinks(section, 4); + } + + private void createMemoryUsageSection() { + String memStatsPeriod = preferenceStore.getString(PreferenceConstants.P_MEM_CHART_PERIOD); + memoryUsageSection = guiHelper.createSection(form, toolkit, "Memory Usage", null, 1, false); + + ServerStats stats; + try { + stats = new GlusterServersClient().getMemoryStats(server.getName(), memStatsPeriod); + } catch(Exception e) { + logger.error("Couldn't fetch memory usage statistics for server [" + server.getName() + "]", e); + toolkit.createLabel(memoryUsageSection, "Couldn't fetch memory usage statistics for server [" + server.getName() + "]! Error: [" + e.getMessage() + "]"); + return; + } + + // in case of memory usage, there are four elements in usage data: user, free, cache, buffer and total. we use "user". + ChartUtil chartUtil = ChartUtil.getInstance(); + chartUtil.createAreaChart(toolkit, memoryUsageSection, stats, 0, "%", chartUtil + .getTimestampFormatForPeriod(memStatsPeriod), + chartUtil.new MemoryChartPeriodLinkListener(server.getName(), memStatsPeriod, toolkit), 100, 4); + } + + private void createCPUUsageSection() { + String cpuStatsPeriod = preferenceStore.getString(PreferenceConstants.P_CPU_CHART_PERIOD); + cpuUsageSection = guiHelper.createSection(form, toolkit, "CPU Usage", null, 1, false); + + ServerStats stats; + try { + stats = new GlusterServersClient().getCpuStats(server.getName(), cpuStatsPeriod); + } catch(Exception e) { + logger.error("Couldn't fetch CPU usage statistics for server [" + server.getName() + "]", e); + toolkit.createLabel(cpuUsageSection, "Couldn't fetch CPU usage statistics for server [" + server.getName() + "]! Error: [" + e.getMessage() + "]"); + return; + } + + // in case of CPU usage, there are three elements in usage data: user, system and total. we use total. + chartUtil.createAreaChart(toolkit, cpuUsageSection, stats, 2, "%", chartUtil + .getTimestampFormatForPeriod(cpuStatsPeriod), + chartUtil.new CpuChartPeriodLinkListener(server.getName(), cpuStatsPeriod, toolkit), 100, 4); + } + + private void createNetworkUsageSection() { + final String networkStatsPeriod = preferenceStore.getString(PreferenceConstants.P_NETWORK_CHART_PERIOD); + networkUsageSection = guiHelper.createSection(form, toolkit, "Network Usage", null, 1, false); + + String networkInterface = server.getNetworkInterfaces().get(0).getName(); + ServerStats stats; + try { + stats = new GlusterServersClient().getNetworkStats(server.getName(), networkInterface, networkStatsPeriod); + } catch(Exception e) { + logger.error("Couldn't fetch Network usage statistics for server [" + server.getName() + "] network interface [" + networkInterface + "]", e); + toolkit.createLabel(networkUsageSection, "Couldn't fetch CPU usage statistics for server [" + server.getName() + "]! Error: [" + e.getMessage() + "]"); + return; + } + + // in case of network usage, there are three elements in usage data: received, transmitted and total. we use total. + final ChartUtil chartUtil = ChartUtil.getInstance(); + final ChartPeriodLinkListener networkChartPeriodLinkListener = chartUtil.new NetworkChartPeriodLinkListener(server, networkStatsPeriod, toolkit); + chartUtil.createAreaChart(toolkit, networkUsageSection, stats, 2, "KiB/s", chartUtil + .getTimestampFormatForPeriod(networkStatsPeriod), + networkChartPeriodLinkListener , -1, 5); + } + + private Composite createChartLinks(Composite section, int columnCount) { + GridLayout layout = new org.eclipse.swt.layout.GridLayout(columnCount, false); + layout.marginBottom = 0; + layout.marginTop = 0; + layout.marginLeft = (CHART_WIDTH - (50*columnCount)) / 2; + Composite graphComposite = toolkit.createComposite(section, SWT.NONE); + graphComposite.setLayout(layout); + GridData data = new GridData(SWT.FILL, SWT.FILL, false, false); + data.widthHint = CHART_WIDTH; + graphComposite.setLayoutData(data); + + Label label1 = toolkit.createLabel(graphComposite, "1 day"); + Hyperlink link1 = toolkit.createHyperlink(graphComposite, "1 week", SWT.NONE); + Hyperlink link2 = toolkit.createHyperlink(graphComposite, "1 month", SWT.NONE); + Hyperlink link3 = toolkit.createHyperlink(graphComposite, "1 year", SWT.NONE); + + return graphComposite; + } + + private void createSections(Composite parent) { + String serverName = server.getName(); + form = guiHelper.setupForm(parent, toolkit, "Server Summary [" + serverName + "]"); + createServerSummarySection(server, toolkit, form); + + if (server.getStatus() == SERVER_STATUS.ONLINE) { + try { + new ProgressMonitorDialog(getSite().getShell()).run(false, false, new IRunnableWithProgress() { + + @Override + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + monitor.beginTask("Creating Server Summary View", 4); + monitor.setTaskName("Creating Memory Usage Section"); + createMemoryUsageSection(); + monitor.worked(1); + + monitor.setTaskName("Creating Network Usage Section"); + createNetworkUsageSection(); + monitor.worked(1); + + monitor.setTaskName("Creating CPU Usage Section"); + createCPUUsageSection(); + monitor.worked(1); + + monitor.setTaskName("Creating Network Interfaces Section"); + createNetworkInterfacesSection(server, toolkit, form); + monitor.worked(1); + monitor.done(); + } + }); + } catch (Exception e) { + String errMsg = "Exception while creating the Gluster Server Summary View : [" + e.getMessage() + "]"; + logger.error(errMsg, e); + MessageDialog.openError(getSite().getShell(), ConsoleConstants.CONSOLE_TITLE, errMsg); + } + } + + parent.layout(); // IMP: lays out the form properly + } + + private void createServerSummarySection(GlusterServer server, FormToolkit toolkit, final ScrolledForm form) { + serverSummarySection = guiHelper.createSection(form, toolkit, "Summary", null, 2, false); + // toolkit.createLabel(section, "Preferred Network: ", SWT.NONE); + // toolkit.createLabel(section, server.getPreferredNetworkInterface().getName(), SWT.NONE); + + if (server.isOnline()) { + toolkit.createLabel(serverSummarySection, "Number of CPUs: ", SWT.NONE); + numCpus = toolkit.createLabel(serverSummarySection, "" + server.getNumOfCPUs(), SWT.NONE); + + toolkit.createLabel(serverSummarySection, "% CPU Usage (avg): ", SWT.NONE); + cpuGauge = new CoolGauge(serverSummarySection, guiHelper.getImage(IImageKeys.GAUGE_SMALL)); + + toolkit.createLabel(serverSummarySection, "Memory Usage: ", SWT.NONE); + memoryUsageBar = new ProgressBar(serverSummarySection, SWT.SMOOTH); + + // toolkit.createLabel(section, "Memory Usage: ", SWT.NONE); + // final CoolProgressBar bar = new CoolProgressBar(section,SWT.HORIZONTAL, + // guiHelper.getImage(IImageKeys.PROGRESS_BAR_LEFT), + // guiHelper.getImage(IImageKeys.PROGRESS_BAR_FILLED), + // guiHelper.getImage(IImageKeys.PROGRESS_BAR_EMPTY), + // guiHelper.getImage(IImageKeys.PROGRESS_BAR_RIGHT)); + // bar.updateProgress(server.getMemoryInUse() / server.getTotalMemory()); + + // toolkit.createLabel(section, "Total Disk Space (GB): ", SWT.NONE); + // toolkit.createLabel(section, online ? "" + server.getTotalDiskSpace() : "NA", SWT.NONE); + // + // toolkit.createLabel(section, "Disk Space in Use (GB): ", SWT.NONE); + // toolkit.createLabel(section, online ? "" + server.getDiskSpaceInUse() : "NA", SWT.NONE); + + toolkit.createLabel(serverSummarySection, "Disk Usage: ", SWT.NONE); + diskUsageBar = new ProgressBar(serverSummarySection, SWT.SMOOTH); + } + + toolkit.createLabel(serverSummarySection, "Status: ", SWT.NONE); + lblServerStatus = new CLabel(serverSummarySection, SWT.NONE); + populateServerSummarySection(server); + } + + private void populateServerSummarySection(GlusterServer server) { + if (server.isOnline()) { + numCpus.setText("" + server.getNumOfCPUs()); + numCpus.redraw(); + + cpuGauge.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false)); + cpuGauge.setGaugeNeedleColour(Display.getDefault().getSystemColor(SWT.COLOR_RED)); + cpuGauge.setGaugeNeedleWidth(2); + cpuGauge.setGaugeNeedlePivot(new Point(66, 65)); + + cpuGauge.setPoints(getPnts()); + cpuGauge.setLevel(server.getCpuUsage() / 100); + cpuGauge.setToolTipText(server.getCpuUsage() + "%"); + cpuGauge.redraw(); + + memoryUsageBar.setMinimum(0); + memoryUsageBar.setMaximum((int) Math.round(server.getTotalMemory())); + memoryUsageBar.setSelection((int) Math.round(server.getMemoryInUse())); + memoryUsageBar.setToolTipText("Total: " + NumberUtil.formatNumber((server.getTotalMemory() / 1024)) + + "GB, In Use: " + NumberUtil.formatNumber((server.getMemoryInUse() / 1024)) + "GB"); + + diskUsageBar.setMinimum(0); + diskUsageBar.setMaximum((int) Math.round(server.getTotalDiskSpace())); + diskUsageBar.setSelection((int) Math.round(server.getDiskSpaceInUse())); + diskUsageBar.setToolTipText("Total: " + NumberUtil.formatNumber((server.getTotalDiskSpace() / 1024)) + + "GB, In Use: " + NumberUtil.formatNumber((server.getDiskSpaceInUse() / 1024)) + "GB"); + + } + lblServerStatus.setText(server.getStatusStr()); + lblServerStatus.setImage(server.getStatus() == GlusterServer.SERVER_STATUS.ONLINE ? guiHelper + .getImage(IImageKeys.STATUS_ONLINE_16x16) : guiHelper.getImage(IImageKeys.STATUS_OFFLINE_16x16)); + toolkit.adapt(lblServerStatus, true, true); + + serverSummarySection.layout(); + form.reflow(true); + } + + private List<Point> getPnts() { + final List<Point> pnts = new ArrayList<Point>(); + pnts.add(new Point(47, 98)); + pnts.add(new Point(34, 84)); + pnts.add(new Point(29, 65)); + pnts.add(new Point(33, 48)); + pnts.add(new Point(48, 33)); + pnts.add(new Point(66, 28)); + pnts.add(new Point(83, 32)); + pnts.add(new Point(98, 47)); + pnts.add(new Point(103, 65)); + pnts.add(new Point(98, 83)); + pnts.add(new Point(84, 98)); + return pnts; + } + + private Composite createNetworkInterfacesSection(GlusterServer server, FormToolkit toolkit, ScrolledForm form) { + final Composite section = guiHelper.createSection(form, toolkit, "Network Interfaces", null, 1, false); + createNetworkInterfacesTableViewer(createTableViewerComposite(section), server); + // Hyperlink changePreferredNetworkLink = toolkit.createHyperlink(section, "Change Preferred Network", + // SWT.NONE); + // changePreferredNetworkLink.addHyperlinkListener(new HyperlinkAdapter() { + // + // @Override + // public void linkActivated(HyperlinkEvent e) { + // new MessageDialog( + // section.getShell(), + // "Gluster Storage Platform", + // guiHelper.getImage(IImageKeys.SERVER), + // "This will show additional controls to help user choose a new network interface. TO BE IMPLEMENTED.", + // MessageDialog.INFORMATION, new String[] { "OK" }, 0).open(); + // } + // }); + return section; + } + + private TableViewer createNetworkInterfacesTableViewer(final Composite parent, GlusterServer server) { + TableViewer tableViewer = new TableViewer(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.MULTI); + // TableViewer tableViewer = new TableViewer(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.MULTI); + tableViewer.setLabelProvider(new NetworkInterfaceTableLabelProvider()); + tableViewer.setContentProvider(new ArrayContentProvider()); + + setupNetworkInterfaceTable(parent, tableViewer.getTable()); + tableViewer.setInput(server.getNetworkInterfaces().toArray()); + + return tableViewer; + } + + private void setupNetworkInterfaceTable(Composite parent, Table table) { + table.setHeaderVisible(true); + table.setLinesVisible(false); + + TableColumnLayout tableColumnLayout = guiHelper.createTableColumnLayout(table, + NETWORK_INTERFACE_TABLE_COLUMN_NAMES); + parent.setLayout(tableColumnLayout); + + setColumnProperties(table, NETWORK_INTERFACE_TABLE_COLUMN_INDICES.INTERFACE, SWT.CENTER, 70); + setColumnProperties(table, NETWORK_INTERFACE_TABLE_COLUMN_INDICES.MODEL, SWT.CENTER, 70); + setColumnProperties(table, NETWORK_INTERFACE_TABLE_COLUMN_INDICES.SPEED, SWT.CENTER, 70); + setColumnProperties(table, NETWORK_INTERFACE_TABLE_COLUMN_INDICES.IP_ADDRESS, SWT.CENTER, 100); + setColumnProperties(table, NETWORK_INTERFACE_TABLE_COLUMN_INDICES.NETMASK, SWT.CENTER, 70); + setColumnProperties(table, NETWORK_INTERFACE_TABLE_COLUMN_INDICES.GATEWAY, SWT.CENTER, 70); + } + + private Composite createTableViewerComposite(Composite parent) { + Composite tableViewerComposite = new Composite(parent, SWT.NO); + tableViewerComposite.setLayout(new FillLayout(SWT.HORIZONTAL)); + GridData tableLayoutData = new GridData(SWT.FILL, SWT.FILL, true, false); + tableLayoutData.widthHint = 400; + tableLayoutData.minimumWidth = 400; + // tableLayoutData.grabExcessHorizontalSpace = true; + tableViewerComposite.setLayoutData(tableLayoutData); + return tableViewerComposite; + } + + /** + * 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, NETWORK_INTERFACE_TABLE_COLUMN_INDICES columnIndex, int alignment, + int weight) { + TableColumn column = table.getColumn(columnIndex.ordinal()); + column.setAlignment(alignment); + + TableColumnLayout tableColumnLayout = (TableColumnLayout) table.getParent().getLayout(); + tableColumnLayout.setColumnData(column, new ColumnWeightData(weight)); + } + + @Override + public void setFocus() { + form.setFocus(); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServersSummaryView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServersSummaryView.java new file mode 100644 index 00000000..c7435166 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServersSummaryView.java @@ -0,0 +1,290 @@ +/** + * GlusterServersSummaryView.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 org.gluster.storage.management.console.views; + +import java.util.List; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.console.utils.ChartViewerComposite; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.Alert; +import org.gluster.storage.management.core.model.ClusterListener; +import org.gluster.storage.management.core.model.DefaultClusterListener; +import org.gluster.storage.management.core.model.EntityGroup; +import org.gluster.storage.management.core.model.Event; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.Status; +import org.gluster.storage.management.core.model.TaskInfo; +import org.gluster.storage.management.core.model.Alert.ALERT_TYPES; +import org.gluster.storage.management.core.model.Server.SERVER_STATUS; +import org.gluster.storage.management.core.model.TaskInfo.TASK_TYPE; + + +/** + * + */ +public class GlusterServersSummaryView extends ViewPart { + public static final String ID = GlusterServersSummaryView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private final FormToolkit toolkit = new FormToolkit(Display.getCurrent()); + private ScrolledForm form; + private ClusterListener clusterListener; + private EntityGroup<GlusterServer> servers; + private Composite alertsSection; + private Composite serversAvailabilitySection; + private Composite tasksSection; + + /* (non-Javadoc) + * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) + */ + @SuppressWarnings("unchecked") + @Override + public void createPartControl(Composite parent) { + if (servers == null) { + servers = guiHelper.getSelectedEntity(getSite(), EntityGroup.class); + } + setPartName("Summary"); + createSections(parent); + + clusterListener = new DefaultClusterListener() { + @Override + public void serverAdded(GlusterServer server) { + super.serverAdded(server); + updateServerAvailabilitySection(); + } + + @Override + public void serverRemoved(GlusterServer server) { + super.serverRemoved(server); + updateServerAvailabilitySection(); + } + + @Override + public void serverChanged(GlusterServer server, Event event) { + super.serverChanged(server, event); + updateServerAvailabilitySection(); + } + + private void updateServerAvailabilitySection() { + guiHelper.clearSection(serversAvailabilitySection); + populateAvailabilitySection(); + } + + @Override + public void alertsGenerated() { + super.alertsGenerated(); + guiHelper.clearSection(alertsSection); + populateAlertSection(); + } + + @Override + public void taskAdded(TaskInfo taskInfo) { + super.taskAdded(taskInfo); + updateTasksSection(); + } + + @Override + public void taskRemoved(TaskInfo taskInfo) { + super.taskRemoved(taskInfo); + updateTasksSection(); + } + + @Override + public void taskUpdated(TaskInfo taskInfo) { + super.taskUpdated(taskInfo); + updateTasksSection(); + } + + private void updateTasksSection() { + guiHelper.clearSection(tasksSection); + populateTasksSection(); + } + }; + GlusterDataModelManager.getInstance().addClusterListener(clusterListener); + } + + @Override + public void dispose() { + super.dispose(); + GlusterDataModelManager.getInstance().removeClusterListener(clusterListener); + } + + /** + * @param parent + */ + private void createSections(Composite parent) { + form = guiHelper.setupForm(parent, toolkit, "Servers - Summary"); + + createSummarySection(); + createRunningTasksSection(); + createAlertsSection(); + + parent.layout(); // IMP: lays out the form properly + } + + private void createSummarySection() { + serversAvailabilitySection = guiHelper.createSection(form, toolkit, "Availability", null, 2, false); + populateAvailabilitySection(); + } + + private void populateAvailabilitySection() { + if (servers.getEntities().size() == 0) { + toolkit.createLabel(serversAvailabilitySection, "This section will be populated after at least" + + CoreConstants.NEWLINE + "one server is added to the storage cloud."); + return; + } + + Double[] values = new Double[] { Double.valueOf(getServerCountByStatus(servers, SERVER_STATUS.ONLINE)), + Double.valueOf(getServerCountByStatus(servers, SERVER_STATUS.OFFLINE)) }; + createStatusChart(serversAvailabilitySection, values); + } + + private int getServerCountByStatus(EntityGroup<GlusterServer> servers, SERVER_STATUS status) { + int count = 0; + for (GlusterServer server : servers.getEntities()) { + if (server.getStatus() == status) { + count++; + } + } + return count; + } + + private void createStatusChart(Composite section, Double[] values) { + String[] categories = new String[] { "Online", "Offline" }; + ChartViewerComposite chartViewerComposite = new ChartViewerComposite(section, SWT.NONE, categories, values); + + GridData data = new GridData(SWT.FILL, SWT.FILL, false, false); + data.widthHint = 300; + data.heightHint = 150; + chartViewerComposite.setLayoutData(data); + } + + private void createAlertsSection() { + alertsSection = guiHelper.createSection(form, toolkit, "Alerts", null, 1, false); + populateAlertSection(); + } + + private void populateAlertSection() { + List<Alert> alerts = GlusterDataModelManager.getInstance().getModel().getCluster().getAlerts(); + + for (Alert alert : alerts) { + if (alert.getType() == ALERT_TYPES.DISK_USAGE_ALERT || alert.getType() != ALERT_TYPES.OFFLINE_SERVERS_ALERT + || alert.getType() == ALERT_TYPES.MEMORY_USAGE_ALERT + || alert.getType() == ALERT_TYPES.CPU_USAGE_ALERT) { + addAlertLabel(alertsSection, alert); + } + } + alertsSection.pack(true); + form.reflow(true); + } + + private void addAlertLabel(Composite section, Alert alert) { + CLabel lblAlert = new CLabel(section, SWT.FLAT); + Image alertImage = null; + switch (alert.getType()) { + case DISK_USAGE_ALERT: + alertImage = guiHelper.getImage(IImageKeys.LOW_DISK_SPACE_22x22); + break; + case OFFLINE_SERVERS_ALERT: + alertImage = guiHelper.getImage(IImageKeys.SERVER_OFFLINE_22x22); + break; + case MEMORY_USAGE_ALERT: + alertImage = guiHelper.getImage(IImageKeys.MEMORY_USAGE_ALERT_22x22); + break; + case CPU_USAGE_ALERT: + alertImage = guiHelper.getImage(IImageKeys.SERVER_WARNING_22x22); + break; + } + lblAlert.setImage(alertImage); + lblAlert.setText(alert.getMessage()); + lblAlert.redraw(); + } + + private void createRunningTasksSection() { + tasksSection = guiHelper.createSection(form, toolkit, CoreConstants.RUNNING_TASKS, null, 1, false); + populateTasksSection(); + } + + private void populateTasksSection() { + for (TaskInfo taskInfo : GlusterDataModelManager.getInstance().getModel().getCluster().getTaskInfoList()) { + // Exclude volume related tasks + if (taskInfo.getStatus().getCode() != Status.STATUS_CODE_SUCCESS + && taskInfo.getType() != TASK_TYPE.VOLUME_REBALANCE + && taskInfo.getType() != TASK_TYPE.BRICK_MIGRATE) { + addTaskLabel(tasksSection, taskInfo); + } + } + tasksSection.layout(); + form.reflow(true); + } + + private void addTaskLabel(Composite section, TaskInfo taskInfo) { + CLabel lblTask = new CLabel(section, SWT.NONE); + Image taskImage = null; + switch(taskInfo.getType()) { + case DISK_FORMAT: + taskImage = guiHelper.getImage(IImageKeys.DISK_INITIALIZING_22x22); + break; + case BRICK_MIGRATE: + taskImage = guiHelper.getImage(IImageKeys.BRICK_MIGRATE_22x22); + break; + case VOLUME_REBALANCE: + taskImage = guiHelper.getImage(IImageKeys.VOLUME_REBALANCE_22x22); + break; + } + + String description = taskInfo.getDescription(); + switch (taskInfo.getStatus().getCode()) { + case Status.STATUS_CODE_PAUSE: + description += " (paused)"; + break; + case Status.STATUS_CODE_COMMIT_PENDING: + description += " (commit pending)"; + break; + case Status.STATUS_CODE_FAILURE: + description += " (failed)"; + break; + } + + lblTask.setText(description); + lblTask.setImage(taskImage); + lblTask.redraw(); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.part.WorkbenchPart#setFocus() + */ + @Override + public void setFocus() { + form.setFocus(); + } +}
\ No newline at end of file diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServersView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServersView.java new file mode 100644 index 00000000..fe4e3bb3 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterServersView.java @@ -0,0 +1,78 @@ +/** + * GlusterServersView.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 org.gluster.storage.management.console.views; + +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.GlusterServersPage; +import org.gluster.storage.management.core.model.Entity; +import org.gluster.storage.management.core.model.EntityGroup; +import org.gluster.storage.management.core.model.GlusterServer; + + +/** + * @author root + * + */ +public class GlusterServersView extends ViewPart implements IDoubleClickListener { + public static final String ID = GlusterServersView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private EntityGroup<GlusterServer> servers; + private GlusterServersPage page; + + /* (non-Javadoc) + * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) + */ + @SuppressWarnings("unchecked") + @Override + public void createPartControl(Composite parent) { + if (servers == null) { + servers = guiHelper.getSelectedEntity(getSite(), EntityGroup.class); + } + + page = new GlusterServersPage(getSite(), parent, SWT.NONE, servers); + page.addDoubleClickListener(this); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.part.WorkbenchPart#setFocus() + */ + @Override + public void setFocus() { + page.setFocus(); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent) + */ + @Override + public void doubleClick(DoubleClickEvent event) { + NavigationView clusterView = (NavigationView) guiHelper.getView(NavigationView.ID); + if (clusterView != null) { + clusterView.selectEntity((Entity) ((StructuredSelection) event.getSelection()).getFirstElement()); + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterViewsManager.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterViewsManager.java new file mode 100644 index 00000000..a4d0a98f --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/GlusterViewsManager.java @@ -0,0 +1,130 @@ +/** + * GlusterViewsManager.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 org.gluster.storage.management.console.views; + +import org.eclipse.ui.IViewReference; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PartInitException; +import org.gluster.storage.management.console.ConsoleConstants; +import org.gluster.storage.management.core.model.Cluster; +import org.gluster.storage.management.core.model.Entity; +import org.gluster.storage.management.core.model.EntityGroup; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.Server; +import org.gluster.storage.management.core.model.Volume; + + +/** + * @see ViewsManager + */ +public class GlusterViewsManager implements ViewsManager { + private IWorkbenchPage page; + + public GlusterViewsManager(IWorkbenchPage page) { + this.page = page; + } + + /* (non-Javadoc) + * @see org.gluster.storage.management.console.views.ViewsManager#updateViews(org.gluster.storage.management.core.model.Entity) + */ + @SuppressWarnings("rawtypes") + @Override + public void updateViews(Entity entity) { + closeAllViews(); + + try { + if (entity instanceof EntityGroup) { + showViewsForEntityGroup((EntityGroup)entity); + } else if (entity.getClass() == Server.class) { + showViewsForDiscoveredServer((Server)entity); + } else if (entity.getClass() == GlusterServer.class) { + showViewsForGlusterServer((GlusterServer)entity); + } else if (entity instanceof Volume) { + showViewsForVolume((Volume)entity); + } else if (entity instanceof Cluster) { + showViewsForCluster((Cluster)entity); + } + } catch (PartInitException e) { + e.printStackTrace(); + } + } + + private void closeAllViews() { + IViewReference[] viewReferences = page.getViewReferences(); + for (final IViewReference viewReference : viewReferences) { + if (!(viewReference.getId().equals(NavigationView.ID) || viewReference.getId().equals( + ConsoleConstants.TERMINAL_VIEW_ID))) { + page.hideView(viewReference); + } + } + } + + private void showViewsForCluster(Cluster cluster) throws PartInitException { + page.showView(ClusterSummaryView.ID); + page.showView(TasksView.ID, null, IWorkbenchPage.VIEW_CREATE); + } + + private void showViewsForVolume(Volume volume) throws PartInitException { + page.showView(VolumeSummaryView.ID); + page.showView(VolumeBricksView.ID, null, IWorkbenchPage.VIEW_CREATE); + page.showView(VolumeOptionsView.ID, null, IWorkbenchPage.VIEW_CREATE); + page.showView(VolumeLogsView.ID, null, IWorkbenchPage.VIEW_CREATE); + } + + private void showViewsForGlusterServer(GlusterServer server) throws PartInitException { + page.showView(GlusterServerSummaryView.ID); + if (server.getStatus() == GlusterServer.SERVER_STATUS.ONLINE) { + page.showView(GlusterServerDisksView.ID, null, IWorkbenchPage.VIEW_CREATE); + //page.showView(GlusterServerLogsView.ID, null, IWorkbenchPage.VIEW_CREATE); + } + } + + private void showViewsForDiscoveredServer(Server server) throws PartInitException { + page.showView(DiscoveredServerView.ID); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void showViewsForEntityGroup(EntityGroup entityGroup) throws PartInitException { + Class entityType = entityGroup.getEntityType(); + if (entityType == Server.class) { + showViewForServers(entityGroup); + } else if (entityType == Volume.class) { + showViewsForVolumes(entityGroup); + } else if (entityType == GlusterServer.class) { + showViewsForGlusterServers(entityGroup); + } + } + + private void showViewsForGlusterServers(EntityGroup<GlusterServer> server) throws PartInitException { + page.showView(GlusterServersSummaryView.ID); + page.showView(GlusterServersView.ID, null, IWorkbenchPage.VIEW_CREATE); + page.showView(DisksView.ID, null, IWorkbenchPage.VIEW_CREATE); + } + + private void showViewsForVolumes(EntityGroup<Volume> volumes) throws PartInitException { + page.showView(VolumesSummaryView.ID); + page.showView(VolumesView.ID, null, IWorkbenchPage.VIEW_CREATE); + } + + private void showViewForServers(EntityGroup<Server> servers) throws PartInitException { + page.showView(DiscoveredServersView.ID); + } +}
\ No newline at end of file diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/NavigationTreeLabelDecorator.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/NavigationTreeLabelDecorator.java new file mode 100644 index 00000000..45f7119f --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/NavigationTreeLabelDecorator.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views; + +import org.eclipse.jface.viewers.IDecoration; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ILightweightLabelDecorator; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.gluster.storage.management.console.Application; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.core.model.EntityGroup; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.Server; +import org.gluster.storage.management.core.model.Volume; + + +public class NavigationTreeLabelDecorator implements ILightweightLabelDecorator { + + @Override + public void addListener(ILabelProviderListener listener) { + } + + @Override + public void dispose() { + } + + @Override + public boolean isLabelProperty(Object element, String property) { + return false; + } + + @Override + public void removeListener(ILabelProviderListener listener) { + } + + @SuppressWarnings("rawtypes") + @Override + public void decorate(Object element, IDecoration decoration) { + if (element instanceof Volume) { + Volume volume = (Volume) element; + if (volume.getStatus() == Volume.VOLUME_STATUS.OFFLINE) { + decoration.addOverlay(AbstractUIPlugin.imageDescriptorFromPlugin(Application.PLUGIN_ID, + IImageKeys.OVERLAY_OFFLINE_8x8)); + } else { + decoration.addOverlay(AbstractUIPlugin.imageDescriptorFromPlugin(Application.PLUGIN_ID, + IImageKeys.OVERLAY_ONLINE_8x8)); + } + } + + if (element instanceof GlusterServer) { + GlusterServer server = (GlusterServer) element; + if (server.getStatus() == GlusterServer.SERVER_STATUS.OFFLINE) { + decoration.addOverlay(AbstractUIPlugin.imageDescriptorFromPlugin(Application.PLUGIN_ID, + IImageKeys.OVERLAY_OFFLINE_8x8)); + } else { + decoration.addOverlay(AbstractUIPlugin.imageDescriptorFromPlugin(Application.PLUGIN_ID, + IImageKeys.OVERLAY_ONLINE_8x8)); + } + } + + if (element instanceof Server) { + decoration.addOverlay(AbstractUIPlugin.imageDescriptorFromPlugin(Application.PLUGIN_ID, + IImageKeys.OVERLAY_STAR_8x8)); + } + + if(element instanceof EntityGroup && ((EntityGroup)element).getEntityType() == Server.class) { + decoration.addOverlay(AbstractUIPlugin.imageDescriptorFromPlugin(Application.PLUGIN_ID, + IImageKeys.OVERLAY_STAR_8x8)); + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/NavigationView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/NavigationView.java new file mode 100644 index 00000000..0c565a24 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/NavigationView.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views; + +import org.eclipse.core.runtime.IAdapterFactory; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.model.BaseWorkbenchContentProvider; +import org.eclipse.ui.model.WorkbenchLabelProvider; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.toolbar.GlusterToolbarManager; +import org.gluster.storage.management.core.model.DefaultClusterListener; +import org.gluster.storage.management.core.model.Entity; +import org.gluster.storage.management.core.model.Event; +import org.gluster.storage.management.core.model.GlusterDataModel; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.Volume; + + +public class NavigationView extends ViewPart implements ISelectionListener { + public static final String ID = NavigationView.class.getName(); + private TreeViewer treeViewer; + private IAdapterFactory adapterFactory = new ClusterAdapterFactory(); + private GlusterToolbarManager toolbarManager; + private Entity entity; + private GlusterViewsManager viewsManager; + private DefaultClusterListener clusterListener; + + @Override + public void createPartControl(Composite parent) { + createNavigationTree(parent); + + // Create the views and toolbar managers + toolbarManager = new GlusterToolbarManager(getSite().getWorkbenchWindow()); + viewsManager = new GlusterViewsManager(getSite().getPage()); + + // listen to selection events to update views/toolbar accordingly + getSite().getPage().addSelectionListener(this); + } + + private void createNavigationTree(Composite parent) { + GlusterDataModel model = GlusterDataModelManager.getInstance().getModel(); + + Platform.getAdapterManager().registerAdapters(adapterFactory, Entity.class); + treeViewer = new TreeViewer(parent, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL); + treeViewer.setLabelProvider(WorkbenchLabelProvider.getDecoratingWorkbenchLabelProvider()); + treeViewer.setContentProvider(new BaseWorkbenchContentProvider()); + treeViewer.setInput(model); + treeViewer.expandAll(); + // select the first element by default + treeViewer.setSelection(new StructuredSelection(model.getChildren().get(0))); + + setupContextMenu(); + + // register as selection provider so that other views can listen to any selection events on the tree + getSite().setSelectionProvider(treeViewer); + + clusterListener = new DefaultClusterListener() { + public void modelChanged() { + treeViewer.refresh(true); + } + + @Override + public void volumeChanged(Volume volume, Event event) { + super.volumeChanged(volume, event); + treeViewer.update(volume, null); + if (volume == entity) { + // this makes sure that the toolbar buttons get updated according to new status + selectEntity(volume); + } + } + + @Override + public void volumeDeleted(Volume volume) { + super.volumeDeleted(volume); + if(volume == entity) { + // volume selected was deleted. select the root element in the tree. + selectEntity(GlusterDataModelManager.getInstance().getModel().getCluster()); + } + } + + @Override + public void serverRemoved(GlusterServer server) { + super.serverRemoved(server); + if(server == entity) { + // server selected was removed. select the root element in the tree. + selectEntity(GlusterDataModelManager.getInstance().getModel().getCluster()); + } + }; + }; + GlusterDataModelManager.getInstance().addClusterListener(clusterListener); + } + + private void setupContextMenu() { + MenuManager menuManager = new MenuManager("&Gluster", "gluster.context.menu"); + Menu contextMenu = menuManager.createContextMenu(treeViewer.getControl()); + treeViewer.getTree().setMenu(contextMenu); + getSite().registerContextMenu(menuManager, treeViewer); + } + + public void selectEntity(Entity entity) { + treeViewer.setSelection(new StructuredSelection(entity)); + treeViewer.reveal(entity); + setFocus(); // this ensures that the "selection changed" event gets fired + } + + @Override + public void setFocus() { + treeViewer.getControl().setFocus(); + } + + @Override + public void selectionChanged(IWorkbenchPart part, ISelection selection) { + if (part instanceof NavigationView && selection instanceof TreeSelection) { + Entity selectedEntity = (Entity) ((TreeSelection) selection).getFirstElement(); + + if (selectedEntity != null && selectedEntity != entity) { + entity = selectedEntity; + + // update views and toolbar buttons visibility based on selected entity + viewsManager.updateViews(entity); + toolbarManager.updateToolbar(entity); + + // Opening of other views may cause navigation tree to lose focus; get it back. + setFocus(); + } + } + } + + @Override + public void dispose() { + super.dispose(); + GlusterDataModelManager.getInstance().removeClusterListener(clusterListener); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/TasksView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/TasksView.java new file mode 100644 index 00000000..9732a460 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/TasksView.java @@ -0,0 +1,39 @@ +package org.gluster.storage.management.console.views; + +import java.util.List; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.views.pages.TasksPage; +import org.gluster.storage.management.core.model.TaskInfo; + + +public class TasksView extends ViewPart { + + public static final String ID = TasksView.class.getName(); + private TasksPage page; + + + public TasksView() { + // TODO Auto-generated constructor stub + } + + @Override + public void createPartControl(Composite parent) { + page = new TasksPage(getSite(), parent, SWT.NONE, getAllTasks()); + page.layout(); // IMP: lays out the form properly + } + + + private List<TaskInfo> getAllTasks() { + return GlusterDataModelManager.getInstance().getModel().getCluster().getTaskInfoList(); + } + + @Override + public void setFocus() { + page.setFocus(); + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/ViewsManager.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/ViewsManager.java new file mode 100644 index 00000000..76581416 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/ViewsManager.java @@ -0,0 +1,38 @@ +/** + * ViewsManager.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 org.gluster.storage.management.console.views; + +import org.gluster.storage.management.core.model.Entity; + +/** + * Whenever the current selection/action demands opening different set of views, the views manager is used to open + * appropriate views. + */ +public interface ViewsManager { + /** + * Updates the views for given entity. This typically means that user is working with the given entity, and hence + * the views related to that entity should be made visible, and other un-related views should be hidden. + * + * @param entity + * The entity for which views are to be updated + */ + public void updateViews(Entity entity); +}
\ No newline at end of file diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeBricksView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeBricksView.java new file mode 100644 index 00000000..33f7c970 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeBricksView.java @@ -0,0 +1,39 @@ +package org.gluster.storage.management.console.views; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.BricksPage; +import org.gluster.storage.management.core.model.Volume; + + +public class VolumeBricksView extends ViewPart { + public static final String ID = VolumeBricksView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private BricksPage page; + private Volume volume; + + @Override + public void createPartControl(Composite parent) { + if (volume == null) { + volume = guiHelper.getSelectedEntity(getSite(), Volume.class); + } + + createPage(parent); + } + + /** + * @param parent + */ + private void createPage(Composite parent) { + page = new BricksPage(parent, SWT.NONE, getSite(), volume.getBricks()); + parent.layout(); // IMP: lays out the form properly + } + + @Override + public void setFocus() { + page.setFocus(); + } +} + diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeLogsView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeLogsView.java new file mode 100644 index 00000000..eeffe6d5 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeLogsView.java @@ -0,0 +1,58 @@ +package org.gluster.storage.management.console.views; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.VolumeLogsPage; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.VolumeLogMessage; +import org.gluster.storage.management.core.utils.DateUtil; + + +public class VolumeLogsView extends ViewPart implements IDoubleClickListener { + VolumeLogsPage logsPage; + public static final String ID = VolumeLogsView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private Volume volume; + + @Override + public void createPartControl(Composite parent) { + if (volume == null) { + volume = guiHelper.getSelectedEntity(getSite(), Volume.class); + } + + createPage(parent); + } + + private void createPage(Composite parent) { + logsPage = new VolumeLogsPage(parent, SWT.NONE, volume); + logsPage.addDoubleClickListener(this); + + parent.layout(); // IMP: lays out the form properly + } + + @Override + public void setFocus() { + logsPage.setFocus(); + } + + @Override + public void doubleClick(DoubleClickEvent event) { + VolumeLogMessage volumeLogMessage = (VolumeLogMessage) ((StructuredSelection) event.getSelection()) + .getFirstElement(); + String message = DateUtil.formatDate(volumeLogMessage.getTimestamp()) + " " + + DateUtil.formatTime(volumeLogMessage.getTimestamp()) + " [" + volumeLogMessage.getSeverity() + "]" + + CoreConstants.NEWLINE + CoreConstants.NEWLINE + volumeLogMessage.getMessage(); + + new MessageDialog(getSite().getShell(), "Log message from " + volumeLogMessage.getBrick(), null, message, + MessageDialog.NONE, new String[] { "Close" }, 0).open(); + + } +}
\ No newline at end of file diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeOptionsView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeOptionsView.java new file mode 100644 index 00000000..144f1441 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeOptionsView.java @@ -0,0 +1,36 @@ +package org.gluster.storage.management.console.views; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.VolumeOptionsPage; +import org.gluster.storage.management.core.model.Volume; + + +public class VolumeOptionsView extends ViewPart { + public static final String ID = VolumeOptionsView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private VolumeOptionsPage page; + private Volume volume; + + @Override + public void createPartControl(Composite parent) { + if (volume == null) { + volume = guiHelper.getSelectedEntity(getSite(), Volume.class); + } + + createPage(parent); + } + + private void createPage(Composite parent) { + page = new VolumeOptionsPage(parent, SWT.NONE, volume); + parent.layout(); // IMP: lays out the form properly + } + + @Override + public void setFocus() { + page.setFocus(); + } +} + diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeSummaryView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeSummaryView.java new file mode 100644 index 00000000..2257c56f --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeSummaryView.java @@ -0,0 +1,859 @@ +package org.gluster.storage.management.console.views; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +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.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +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; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.forms.events.HyperlinkAdapter; +import org.eclipse.ui.forms.events.HyperlinkEvent; +import org.eclipse.ui.forms.widgets.FormText; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.Hyperlink; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.console.toolbar.GlusterToolbarManager; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.constants.GlusterConstants; +import org.gluster.storage.management.core.model.Alert; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.Cluster; +import org.gluster.storage.management.core.model.DefaultClusterListener; +import org.gluster.storage.management.core.model.Device; +import org.gluster.storage.management.core.model.Disk; +import org.gluster.storage.management.core.model.Event; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.Partition; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.Server.SERVER_STATUS; +import org.gluster.storage.management.core.model.Volume.VOLUME_TYPE; +import org.gluster.storage.management.core.utils.NumberUtil; +import org.gluster.storage.management.core.utils.StringUtil; +import org.gluster.storage.management.core.utils.ValidationUtil; + + +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 Label volumeType; + private CLabel lblStatusValue; + private DefaultClusterListener volumeChangedListener; + private Hyperlink changeLink; + private Hyperlink cifsChangeLink; + private Text accessControlText; + private Text cifsUsersText; + private ControlDecoration errDecoration; + private ControlDecoration errCifsDecoration; + private Composite parent; + private static final String COURIER_FONT = "Courier"; + private GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + private Cluster cluster = modelManager.getModel().getCluster(); + private Button nfsCheckBox; + private FormText glusterNfsMountText; + private String nfsMountInfo; + private Label nfsLabel; + private String nfs; + + private Label numberOfBricks; + private Label totalDiskSpace; + private Composite alertsSection; + private Button cifsCheckbox; + private Label cifsLabel; + private Composite cifsUpdateLinkComposite; + + @Override + public void createPartControl(Composite parent) { + if (volume == null) { + volume = guiHelper.getSelectedEntity(getSite(), Volume.class); + } + + this.parent = parent; + setPartName("Summary"); + createSections(); + + final GlusterToolbarManager toolbarManager = new GlusterToolbarManager(getSite().getWorkbenchWindow()); + // Refresh the navigation tree whenever there is a change to the data model + volumeChangedListener = new DefaultClusterListener() { + @Override + public void volumeChanged(Volume volume, Event event) { + updateVolumeStatusLabel(); + populateAccessControlText(); + changeNFSStatus(volume.isNfsEnabled()); + updateBrickChanges(volume); + toolbarManager.updateToolbar(volume); + cifsCheckbox.setSelection(volume.isCifsEnable()); + populateCifsUsersText(); + renderVolumeTypeField(); + } + + @Override + public void alertsGenerated() { + super.alertsGenerated(); + guiHelper.clearSection(alertsSection); + populateAlertSection(); + alertsSection.layout(); + } + }; + modelManager.addClusterListener(volumeChangedListener); + } + + @Override + public void dispose() { + super.dispose(); + modelManager.removeClusterListener(volumeChangedListener); + } + + private void createSections() { + form = guiHelper.setupForm(parent, toolkit, "Volume Properties [" + volume.getName() + "]"); + + createVolumePropertiesSection(); + createVolumeMountingInfoSection(); + createVolumeAlertsSection(); + + parent.layout(); // IMP: lays out the form properly + } + + private void createVolumeAlertsSection() { + alertsSection = guiHelper.createSection(form, toolkit, "Alerts", null, 1, false); + populateAlertSection(); + } + + private void populateAlertSection() { + List<Alert> alerts = cluster.getAlerts(); + + for (int i = 0; i < alerts.size(); i++) { + if (alerts.get(i).getType() == Alert.ALERT_TYPES.OFFLINE_VOLUME_BRICKS_ALERT + && alerts.get(i).getReference().split(":")[0].trim().equals(volume.getName())) { + addAlertLabel(alertsSection, alerts.get(i)); + } + } + } + + private void addAlertLabel(Composite section, Alert alert) { + CLabel lblAlert = new CLabel(section, SWT.NONE); + lblAlert.setImage(guiHelper.getImage(IImageKeys.BRICK_OFFLINE_22x22)); + lblAlert.setText(alert.getMessage()); + lblAlert.redraw(); + } + + private FormText setFormTextStyle(FormText formText, String fontName, int size, int style) { + Font font = new Font(Display.getCurrent(), new FontData(fontName, size, style)); + formText.setFont(font); + return formText; + } + + private void createVolumeMountingInfoSection() { + String glusterFs = "Gluster:"; + nfs = "NFS:"; + String onlineServers = getOnlineServers(10); // Limited to 10 servers + String firstOnlineServer = onlineServers.split(",")[0].trim(); + String glusterFsMountInfo = "mount -t glusterfs " + firstOnlineServer + ":/" + volume.getName() + + " <mount-point>"; + nfsMountInfo = "mount -t nfs " + firstOnlineServer + ":/" + volume.getName() + " <mount-point>"; + // TODO: if more than 10 servers... + String info = "Server can be any server name in the storage cloud eg. <" + onlineServers + ">"; + + Composite section = guiHelper.createSection(form, toolkit, "Mounting Information", null, 3, false); + + toolkit.createLabel(section, glusterFs, SWT.NORMAL); + FormText glusterfsMountText = setFormTextStyle(toolkit.createFormText(section, true), COURIER_FONT, 10, + SWT.NONE); + glusterfsMountText.setText(glusterFsMountInfo, false, false); + glusterfsMountText.setLayoutData(new GridData(GridData.BEGINNING, GridData.VERTICAL_ALIGN_CENTER, false, false, + 2, 0)); // Label spanned two column + + nfsLabel = toolkit.createLabel(section, nfs, SWT.NONE); + GridData data = new GridData(); + data.horizontalAlignment = SWT.FILL; + nfsLabel.setLayoutData(data); + + glusterNfsMountText = setFormTextStyle(toolkit.createFormText(section, true), COURIER_FONT, 10, SWT.NONE); + glusterNfsMountText.setText(nfsMountInfo, false, false); + glusterNfsMountText.setLayoutData(new GridData(GridData.BEGINNING, GridData.VERTICAL_ALIGN_CENTER, false, + false, 2, 0)); + + changeNFSStatus(nfsCheckBox.getSelection()); + + toolkit.createLabel(section, ""); + Label infoLabel = toolkit.createLabel(section, info, SWT.NONE); + infoLabel.setLayoutData(new GridData(GridData.BEGINNING, GridData.VERTICAL_ALIGN_CENTER, false, false, 2, 0)); + + // TODO: implement a logic to identify the corresponding glusterfs client download link + String message = "You can download GlusterFS client from"; + String glusterFSDownloadlinkText = "here."; + final String glusterFSDownloadlink = "http://download.gluster.com/pub/gluster/glusterfs/"; + + toolkit.createLabel(section, ""); + toolkit.createLabel(section, message); + Hyperlink link = toolkit.createHyperlink(section, glusterFSDownloadlinkText, SWT.NORMAL); + link.addHyperlinkListener(new HyperlinkAdapter() { + public void linkActivated(HyperlinkEvent e) { + try { + System.out.println(e.getLabel() + " [" + e.getHref() + "]"); + PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser() + .openURL(new URL(glusterFSDownloadlink)); + } catch (PartInitException e1) { + e1.printStackTrace(); + } catch (MalformedURLException e1) { + e1.printStackTrace(); + } + } + }); + } + + private String getOnlineServers(int maxServers) { + List<String> OnlineServers = new ArrayList<String>(); + for (GlusterServer server : cluster.getServers()) { + if (server.getStatus() == SERVER_STATUS.ONLINE) { + OnlineServers.add(server.getName()); + if (OnlineServers.size() >= maxServers) { + break; + } + } + } + return StringUtil.collectionToString(OnlineServers, ", ") + ((OnlineServers.size() > maxServers) ? "..." : ""); + } + + /** + * + */ + private void createVolumePropertiesSection() { + Composite section = guiHelper.createSection(form, toolkit, "Properties", null, 3, false); + + createVolumeTypeField(section); + + VOLUME_TYPE volumeType = volume.getVolumeType(); + if (volumeType == VOLUME_TYPE.DISTRIBUTED_REPLICATE) { + createReplicaCountField(section); + } + + if (volumeType == VOLUME_TYPE.DISTRIBUTED_STRIPE) { + createStripeCountField(section); + } + + createNumOfBricksField(section); + createDiskSpaceField(section); + // createTransportTypeField(section); + createNASProtocolField(section); + createCifsField(section); + createAccessControlField(section); + createStatusField(section); + } + + private GridData createDefaultLayoutData() { + GridData layoutData = new GridData(); + layoutData.minimumWidth = 300; + layoutData.widthHint = 300; + return layoutData; + } + + private void createCifsField(Composite section) { + cifsLabel = toolkit.createLabel(section, "CIFS: ", SWT.NONE); + cifsUsersText = toolkit.createText(section, volume.getAccessControlList(), SWT.BORDER); + populateCifsUsersText(); + addKeyListenerForCifsUser(); + + cifsUpdateLinkComposite = toolkit.createComposite(section, SWT.NONE); + cifsUpdateLinkComposite.setLayout(new FillLayout()); + cifsUpdateLinkComposite.setVisible(volume.isCifsEnable()); + + createChangeLinkForCifs(cifsUpdateLinkComposite); + renderCifsUsers(cifsCheckbox.getSelection()); + errCifsDecoration = guiHelper.createErrorDecoration(cifsUsersText); + errCifsDecoration.hide(); + } + + private void createAccessControlField(Composite section) { + toolkit.createLabel(section, "Allow Access From: ", SWT.NONE); + accessControlText = toolkit.createText(section, volume.getAccessControlList(), SWT.BORDER); + + populateAccessControlText(); + addKeyListenerForAccessControl(); + accessControlText.setLayoutData(createDefaultLayoutData()); + accessControlText.setEnabled(false); + 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) { + changeLink = toolkit.createHyperlink(section, "change", SWT.NONE); + changeLink.addHyperlinkListener(new HyperlinkAdapter() { + + private void finishEdit() { + saveAccessControlList(); + } + + private void startEdit() { + accessControlText.setEnabled(true); + accessControlText.setFocus(); + accessControlText.selectAll(); + changeLink.setText("update"); + } + + @Override + public void linkActivated(HyperlinkEvent e) { + if (accessControlText.isEnabled()) { + // we were already in edit mode. + finishEdit(); + } else { + // Get in to edit mode + startEdit(); + } + } + }); + } + + 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() { + try { + new VolumesClient().setVolumeOption(volume.getName(), Volume.OPTION_AUTH_ALLOW, newACL); + accessControlText.setEnabled(false); + changeLink.setText("change"); + + modelManager.setAccessControlList(volume, newACL); + } catch (Exception e) { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Access control", e.getMessage()); + } + } + }); + } else { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Access control", "Invalid IP / Host name "); + } + guiHelper.clearStatusMessage(); + parent.update(); + } + + private void createChangeLinkForCifs(Composite section) { + cifsChangeLink = toolkit.createHyperlink(section, "change", SWT.NONE); + cifsChangeLink.addHyperlinkListener(new HyperlinkAdapter() { + + private void finishEdit() { + saveCifsConfiguration(); + } + + private void startEdit() { + if (cifsCheckbox.getSelection()) { + enableCifsUsersControls(true); + cifsUsersText.selectAll(); + } + } + + @Override + public void linkActivated(HyperlinkEvent e) { + if (cifsUsersText.isEnabled()) { + // we were already in edit mode. + finishEdit(); + } else { + // Get in to edit mode + startEdit(); + } + } + }); + } + + private void saveCifsConfiguration() { + guiHelper.setStatusMessage("Setting Cifs Configuration..."); + parent.update(); + + // To check if no changes in the users list + if (!isvalidCifsUser()) { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Cifs Configuration", + "Please enter cifs users name"); + enableCifsUsersControls(true); + validateCifsUsers(); + // } else if (cifsUsers.equals(configuredUsers)) { // Nothing to do. + // enableCifsUsersControls(false); + } else { + BusyIndicator.showWhile(Display.getDefault(), new Runnable() { + @Override + public void run() { + VolumesClient vc = new VolumesClient(); + Volume newVolume = new Volume(); + Integer userAction = 1; + String cifsUsers = cifsUsersText.getText().trim(); + List<String> servers = GlusterDataModelManager.getInstance().getOfflineServers(); + // One or more servers are offline, Show warning if cifs is enabled + if (servers != null && servers.size() > 0) { + userAction = new MessageDialog(parent.getShell(), "CIFS configuration", GUIHelper + .getInstance().getImage(IImageKeys.VOLUME_16x16), + "Performing CIFS updates when one or more servers are offline can trigger " + + "inconsistent behavior for CIFS accesses in the cluster." + + CoreConstants.NEWLINE + CoreConstants.NEWLINE + + "Are you sure you want to continue?", MessageDialog.QUESTION, new String[] { + "No", "Yes" }, -1).open(); + } + + // If no cifs users and removing cifs config, nothing to do + if (!(!cifsCheckbox.getSelection() && volume.getCifsUsers().toString().equals("[]") && (cifsUsers + .isEmpty() || cifsUsers.equals(""))) && userAction == 1) { + try { + vc.setCifsConfig(volume.getName(), cifsCheckbox.getSelection(), cifsUsers); + enableCifsUsersControls(false); + newVolume = vc.getVolume(volume.getName()); + modelManager.volumeChanged(volume, newVolume); + showCifsUsersControls(volume.isCifsEnable()); + } catch (Exception e) { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Cifs Configuration", + e.getMessage()); + cifsCheckbox.setSelection(volume.isCifsEnable()); + enableCifsUsersControls(cifsCheckbox.getSelection()); + populateCifsUsersText(); + } + } else { + newVolume = vc.getVolume(volume.getName()); + modelManager.volumeChanged(volume, newVolume); + showCifsUsersControls(volume.isCifsEnable()); + } + } + }); + } + guiHelper.clearStatusMessage(); + parent.update(); + } + + private void saveNFSOption() { + guiHelper.setStatusMessage("Setting NFS option..."); + parent.update(); + + BusyIndicator.showWhile(Display.getDefault(), new Runnable() { + @Override + public void run() { + try { + boolean enableNfs = nfsCheckBox.getSelection(); + new VolumesClient().setVolumeOption(volume.getName(), Volume.OPTION_NFS_DISABLE, + (enableNfs) ? GlusterConstants.OFF : GlusterConstants.ON); + modelManager.setNfsEnabled(volume, enableNfs); + } catch (Exception e) { + MessageDialog.openError(Display.getDefault().getActiveShell(), "NFS Option", e.getMessage()); + } + } + }); + guiHelper.clearStatusMessage(); + parent.update(); + } + + private void addKeyListenerForAccessControl() { + 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 = modelManager.getVolumeOptionDefaultValue(Volume.OPTION_AUTH_ALLOW); + } + accessControlText.setText(accessControlList); + } + + private void addKeyListenerForCifsUser() { + cifsUsersText.addKeyListener(new KeyAdapter() { + public void keyReleased(KeyEvent key) { + switch (key.keyCode) { + case SWT.ESC: + // Reset to default + populateCifsUsersText(); + enableCifsUsersControls(false); + if (cifsUsersText.getText().trim().length() == 0) { // Disable CIFS as well + cifsCheckbox.setSelection(false); + } + form.reflow(true); + break; + case 13: + // User has pressed enter. Save the new value + saveCifsConfiguration(); + break; + } + validateCifsUsers(); + } + }); + } + + private void populateCifsUsersText() { + List<String> userList = volume.getCifsUsers(); + if (volume.isCifsEnable() && userList != null) { + cifsUsersText.setText(StringUtil.collectionToString(userList, ",")); + } else { + cifsUsersText.setText(""); + } + } + + private void createNASProtocolField(final Composite section) { + toolkit.createLabel(section, "Access Protocols: ", SWT.NONE); + + Composite nasProtocolsComposite = toolkit.createComposite(section); + nasProtocolsComposite.setLayout(new FillLayout()); + + createCheckbox(nasProtocolsComposite, "Gluster", true, false); + + nfsCheckBox = createCheckbox(nasProtocolsComposite, "NFS", volume.isNfsEnabled(), true); + + nfsCheckBox.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + if (!nfsCheckBox.getSelection()) { + Integer userAction = new MessageDialog(parent.getShell(), "NFS Re-export", GUIHelper.getInstance() + .getImage(IImageKeys.VOLUME_16x16), + "Are you sure you want to stop the NFS Re-export for volume [" + volume.getName() + "]?", + MessageDialog.QUESTION, new String[] { "No", "Yes" }, -1).open(); + if (userAction <= 0) { // user select cancel or pressed escape key + nfsCheckBox.setSelection(true); + return; + } + } + saveNFSOption(); + } + }); + + // CIFS checkbox + cifsCheckbox = createCheckbox(nasProtocolsComposite, "CIFS", volume.isCifsEnable(), true); + createCifsCheckboxListner(cifsCheckbox); + + toolkit.createLabel(section, "", SWT.NONE); // dummy + } + + private void createCifsCheckboxListner(final Button cifsCheckbox) { + cifsCheckbox.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + if (cifsCheckbox.getSelection()) { + // need to enable cifs + // TODO: Open the text box (empty and enabled), + // the hyperlink next to the textbox should have label "update" + // when user clicks on that hyperlink, + // saveCifsConfiguration should be called + // Also, if user presses the "ESC" key, + // return to the previous state of checkbox and hide the textbox + hyperlink + showCifsUsersControls(true); + enableCifsUsersControls(true); + // saveCifsConfiguration(); + } else { + // need to disable cifs + // TODO: hide the textbox and the link AFTER disabling cifs + if ((volume.getCifsUsers() == null || volume.getCifsUsers().toString().equals("[]")) + && cifsUsersText.getText().trim().equals("")) { + showCifsUsersControls(false); + enableCifsUsersControls(false); + } else { + + Integer userAction = new MessageDialog(parent.getShell(), "CIFS Re-export", GUIHelper + .getInstance().getImage(IImageKeys.VOLUME_16x16), + "Are you sure you want to stop the CIFS re-export for volume [" + volume.getName() + + "]?", MessageDialog.QUESTION, new String[] { "No", "Yes" }, -1).open(); + if (userAction <= 0) { // user select cancel or pressed escape key + cifsCheckbox.setSelection(true); // back to previous state. + } else { + showCifsUsersControls(false); + enableCifsUsersControls(false); + saveCifsConfiguration(); + } + } + } + populateCifsUsersText(); + form.reflow(true); + } + }); + } + + private void renderCifsUsers(Boolean cifsSelection) { + if (cifsSelection) { + enableCifsUsersControls(false); + showCifsUsersControls(true); + } else { + showCifsUsersControls(false); + } + } + + private void showCifsUsersControls(Boolean visible) { + if (visible) { + GridData data = new GridData(); + data.heightHint = 20; + data.widthHint = 100; + cifsLabel.setLayoutData(data); + + GridData data1 = new GridData(); + data1.heightHint = 20; + data1.widthHint = 300; + + cifsUsersText.setLayoutData(data1); + + GridData data2 = new GridData(); + data2.heightHint = 25; + data2.widthHint = 75; + cifsUpdateLinkComposite.setLayoutData(data2); + } else { + GridData data = new GridData(); + data.heightHint = 0; + + cifsLabel.setLayoutData(data); + cifsUsersText.setLayoutData(data); + cifsUpdateLinkComposite.setLayoutData(data); + } + + cifsLabel.setVisible(visible); + cifsUsersText.setVisible(visible); + cifsUpdateLinkComposite.setVisible(visible); + form.reflow(true); + } + + private void enableCifsUsersControls(Boolean enable) { + cifsUsersText.setEnabled(enable); + cifsChangeLink.setText((enable) ? "update" : "change"); + if (enable) { + cifsUsersText.setFocus(); + validateCifsUsers(); + } else { + if (errCifsDecoration != null) { + errCifsDecoration.hide(); + } + } + } + + private Button createCheckbox(Composite parent, String label, boolean checked, boolean enabled) { + final Button checkBox = toolkit.createButton(parent, label, SWT.CHECK); + checkBox.setSelection(checked); + checkBox.setEnabled(enabled); + return checkBox; + } + + private void changeNFSStatus(Boolean isNFSExported) { + glusterNfsMountText.setVisible(isNFSExported); + nfsLabel.setVisible(isNFSExported); + nfsCheckBox.setSelection(isNFSExported); + } + + private void updateBrickChanges(Volume volume) { + numberOfBricks.setText("" + volume.getNumOfBricks()); + totalDiskSpace.setText("" + NumberUtil.formatNumber(getTotalDiskSpace() / 1024)); + } + + private double getDiskSize(String serverName, String deviceName) { + double diskSize = 0; + GlusterServer server = cluster.getServer(serverName); + if (server.getStatus() == SERVER_STATUS.ONLINE) { + for (Disk disk : server.getDisks()) { + if (disk.getName().equals(deviceName)) { + diskSize = disk.getSpace(); + break; + } + + if (disk.hasPartitions()) { + for (Partition partition : disk.getPartitions()) { + if (partition.getName().equals(deviceName)) { + diskSize = partition.getSpace(); + break; + } + } + } + } + } + return diskSize; + } + + private double getTotalDiskSpace() { + List<Double> diskSizes = getVolumeDiskSizes(); + VOLUME_TYPE volumeType = volume.getVolumeType(); + double diskSize = 0d; + if (volumeType == VOLUME_TYPE.DISTRIBUTE || volumeType == VOLUME_TYPE.STRIPE + || volumeType == VOLUME_TYPE.DISTRIBUTED_STRIPE) { + for (Double size : diskSizes) { + diskSize += size; + } + } else { // Replicate or distributed replicate + int replicaCount = volume.getReplicaCount(); + if (replicaCount == 0) { + replicaCount = Volume.DEFAULT_REPLICA_COUNT; + } + int startIndex = 0; + for (int i = 0; i < (diskSizes.size() / replicaCount); i++) { + startIndex = i * replicaCount; + diskSize += Collections.min(diskSizes.subList(startIndex, startIndex + replicaCount)); + } + } + return diskSize; + } + + private List<Double> getVolumeDiskSizes() { + List<Double> diskSizes = new ArrayList<Double>(); + Device device; + for (Brick brick : volume.getBricks()) { + device = modelManager.getDeviceForBrickDir(brick); + diskSizes.add( (device == null) ? 0d : getDiskSize(brick.getServerName(), device.getName()) ); + } + return diskSizes; + } + + + + private void createDiskSpaceField(Composite section) { + Label diskSpaceLabel = toolkit.createLabel(section, "Total Disk Space (GB): ", SWT.NONE); + diskSpaceLabel.setToolTipText("<b>bold</b>normal"); + totalDiskSpace = toolkit.createLabel(section, + "" + NumberUtil.formatNumber(getTotalDiskSpace() / 1024), SWT.NONE); + toolkit.createLabel(section, "", SWT.NONE); // dummy + } + + private void createStatusField(Composite section) { + toolkit.createLabel(section, "Status: ", SWT.NONE); + + lblStatusValue = new CLabel(section, SWT.NONE); + updateVolumeStatusLabel(); + + toolkit.createLabel(section, "", SWT.NONE); // dummy + } + + private void updateVolumeStatusLabel() { + lblStatusValue.setText(volume.getStatusStr()); + lblStatusValue.setImage((volume.getStatus() == Volume.VOLUME_STATUS.ONLINE) ? guiHelper + .getImage(IImageKeys.STATUS_ONLINE_16x16) : guiHelper.getImage(IImageKeys.STATUS_OFFLINE_16x16)); + GridData data = new GridData(); + data.horizontalAlignment = SWT.FILL; + lblStatusValue.setLayoutData(data); + lblStatusValue.redraw(); + } + + private void createTransportTypeField(Composite section) { + toolkit.createLabel(section, "Transport Type: ", SWT.NONE); + toolkit.createLabel(section, "" + volume.getTransportTypeStr(), SWT.NONE); + toolkit.createLabel(section, "", SWT.NONE); // dummy + } + + private void createNumOfBricksField(Composite section) { + toolkit.createLabel(section, "Number of Bricks: ", SWT.NONE); + numberOfBricks = toolkit.createLabel(section, "" + volume.getNumOfBricks(), SWT.NONE); + toolkit.createLabel(section, "", SWT.NONE); // dummy + } + + private void createStripeCountField(Composite section) { + toolkit.createLabel(section, "Stripe Count: ", SWT.NONE); + toolkit.createLabel(section, "" + volume.getStripeCount(), SWT.NONE); + toolkit.createLabel(section, "", SWT.NONE); // dummy + } + + private void createReplicaCountField(Composite section) { + toolkit.createLabel(section, "Replica Count: ", SWT.NONE); + toolkit.createLabel(section, "" + volume.getReplicaCount(), SWT.NONE); + toolkit.createLabel(section, "", SWT.NONE); // dummy + } + + private void createVolumeTypeField(Composite section) { + toolkit.createLabel(section, "Volume Type: ", SWT.NONE); + volumeType = toolkit.createLabel(section, volume.getVolumeTypeStr(), SWT.NONE); + toolkit.createLabel(section, "", SWT.NONE); + } + + private void renderVolumeTypeField() { + volumeType.setText(volume.getVolumeTypeStr()); + } + + @Override + 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("Invalid IP address/Host name [" + + ValidationUtil.getInvalidIpOrHostname(accessControlText.getText()) + + "]. Please enter a valid value!"); + errDecoration.show(); + } + } + + private void validateCifsUsers() { + errCifsDecoration.hide(); + if (cifsCheckbox.getSelection()) { + String cifsUserList = cifsUsersText.getText().trim(); + if (cifsUserList.length() == 0) { + errCifsDecoration.setDescriptionText("Please enter cifs user name"); + errCifsDecoration.show(); + } + } + } + + private boolean isvalidCifsUser() { + if (cifsCheckbox.getSelection()) { + String cifsUserList = cifsUsersText.getText().trim(); + return (cifsUserList.length() != 0); + } + validateCifsUsers(); + return true; + } + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeView.java new file mode 100644 index 00000000..27df48c1 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumeView.java @@ -0,0 +1,90 @@ +/** + * DiscoveredServerView.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 org.gluster.storage.management.console.views; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.model.Server; +import org.gluster.storage.management.core.utils.NumberUtil; + + +/** + * @author root + * + */ +public class VolumeView extends ViewPart { + public static final String ID = VolumeView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private final FormToolkit toolkit = new FormToolkit(Display.getCurrent()); + private ScrolledForm form; + private Server server; + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) + */ + @Override + public void createPartControl(Composite parent) { + if (server == null) { + server = guiHelper.getSelectedEntity(getSite(), Server.class); + } + createSections(parent, server, toolkit); + } + + private void createServerSummarySection(Server server, FormToolkit toolkit, final ScrolledForm form) { + Composite section = guiHelper.createSection(form, toolkit, "Summary", null, 2, false); + + toolkit.createLabel(section, "Number of CPUs: ", SWT.NONE); + toolkit.createLabel(section, "" + server.getNumOfCPUs(), SWT.NONE); + + toolkit.createLabel(section, "Total Memory (GB): ", SWT.NONE); + toolkit.createLabel(section, "" + server.getTotalMemory(), SWT.NONE); + + toolkit.createLabel(section, "Total Disk Space (GB): ", SWT.NONE); + toolkit.createLabel(section, "" + NumberUtil.formatNumber(server.getTotalDiskSpace()), SWT.NONE); + } + + private void createSections(Composite parent, Server server, FormToolkit toolkit) { + String serverName = server.getName(); + form = guiHelper.setupForm(parent, toolkit, "Discovered Server Summary [" + serverName + "]"); + createServerSummarySection(server, toolkit, form); + + parent.layout(); // IMP: lays out the form properly + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.part.WorkbenchPart#setFocus() + */ + @Override + public void setFocus() { + if (form != null) { + form.setFocus(); + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumesSummaryView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumesSummaryView.java new file mode 100644 index 00000000..549353e8 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumesSummaryView.java @@ -0,0 +1,301 @@ +/** + * VolumesSummaryView.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 org.gluster.storage.management.console.views; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.IImageKeys; +import org.gluster.storage.management.console.utils.ChartViewerComposite; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.Alert; +import org.gluster.storage.management.core.model.Cluster; +import org.gluster.storage.management.core.model.ClusterListener; +import org.gluster.storage.management.core.model.DefaultClusterListener; +import org.gluster.storage.management.core.model.EntityGroup; +import org.gluster.storage.management.core.model.Event; +import org.gluster.storage.management.core.model.Status; +import org.gluster.storage.management.core.model.TaskInfo; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.TaskInfo.TASK_TYPE; +import org.gluster.storage.management.core.model.Volume.VOLUME_STATUS; + + +/** + * + */ +public class VolumesSummaryView extends ViewPart { + public static final String ID = VolumesSummaryView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private final FormToolkit toolkit = new FormToolkit(Display.getCurrent()); + private ScrolledForm form; + private EntityGroup<Volume> volumes; + private Cluster cluster = GlusterDataModelManager.getInstance().getModel().getCluster(); + private ClusterListener clusterListener; + + private static final String ALERTS = "Alerts"; + private static final String VOLUMES_SUMMARY = "Volumes - Summary"; + private static final String AVAILABILITY = "Availability"; + private Composite alertsSection; + private Composite tasksSection; + private Composite summarySection; + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets + * .Composite) + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public void createPartControl(Composite parent) { + if (volumes == null) { + Object selectedObj = guiHelper.getSelectedEntity(getSite(), EntityGroup.class); + if (selectedObj != null && ((EntityGroup) selectedObj).getEntityType() == Volume.class) { + volumes = (EntityGroup<Volume>) selectedObj; + } + } + + setPartName("Summary"); + createSections(parent); + + clusterListener = new DefaultClusterListener() { + @Override + public void volumeCreated(Volume volume) { + super.volumeCreated(volume); + updateSummarySection(); + } + + @Override + public void volumeDeleted(Volume volume) { + super.volumeDeleted(volume); + updateSummarySection(); + } + + @Override + public void volumeChanged(Volume volume, Event event) { + super.volumeChanged(volume, event); + updateSummarySection(); + } + + private void updateAlertSection() { + guiHelper.clearSection(alertsSection); + populateAlertSection(); + } + + private void updateSummarySection() { + guiHelper.clearSection(summarySection); + populateSummarySection(); + summarySection.layout(); + form.reflow(true); + } + + @Override + public void alertsGenerated() { + super.alertsGenerated(); + guiHelper.clearSection(alertsSection); + populateAlertSection(); + } + + @Override + public void alertRemoved(Alert alert) { + super.alertRemoved(alert); + updateAlertSection(); + } + + @Override + public void alertCreated(Alert alert) { + super.alertCreated(alert); + updateAlertSection(); + } + + @Override + public void taskAdded(TaskInfo taskInfo) { + super.taskAdded(taskInfo); + updateTasksSection(); + } + + @Override + public void taskRemoved(TaskInfo taskInfo) { + super.taskRemoved(taskInfo); + updateTasksSection(); + } + + @Override + public void taskUpdated(TaskInfo taskInfo) { + super.taskUpdated(taskInfo); + updateTasksSection(); + } + + private void updateTasksSection() { + guiHelper.clearSection(tasksSection); + populateTasks(); + } + }; + GlusterDataModelManager.getInstance().addClusterListener(clusterListener); + } + + @Override + public void dispose() { + super.dispose(); + GlusterDataModelManager.getInstance().removeClusterListener(clusterListener); + } + + private void createSections(Composite parent) { + form = guiHelper.setupForm(parent, toolkit, VOLUMES_SUMMARY); + createSummarySection(); + createTasksSection(); + createAlertsSection(); + + parent.layout(); // IMP: lays out the form properly + } + + private void createAlertsSection() { + alertsSection = guiHelper.createSection(form, toolkit, ALERTS, null, 1, false); + populateAlertSection(); + } + + private void populateAlertSection() { + for (Alert alert : cluster.getAlerts()) { + if (alert.getType() == Alert.ALERT_TYPES.OFFLINE_VOLUME_BRICKS_ALERT + || alert.getType() == Alert.ALERT_TYPES.OFFLINE_VOLUME_ALERT) { + addAlertLabel(alertsSection, alert); + } + } + alertsSection.pack(true); + form.reflow(true); + } + + private void addAlertLabel(Composite section, Alert alert) { + CLabel lblAlert = new CLabel(section, SWT.NONE); + + Image alertImage = null; + switch (alert.getType()) { + case OFFLINE_VOLUME_BRICKS_ALERT: + alertImage = guiHelper.getImage(IImageKeys.BRICK_OFFLINE_22x22); + break; + case OFFLINE_VOLUME_ALERT: + alertImage = guiHelper.getImage(IImageKeys.VOLUME_OFFLINE_22x22); + break; + } + lblAlert.setImage(alertImage); + lblAlert.setText(alert.getMessage()); + lblAlert.redraw(); + } + + private void createTasksSection() { + tasksSection = guiHelper.createSection(form, toolkit, CoreConstants.RUNNING_TASKS, null, 1, false); + populateTasks(); + } + + private void populateTasks() { + for (TaskInfo taskInfo : cluster.getTaskInfoList()) { + if ((taskInfo.getType() == TASK_TYPE.BRICK_MIGRATE || taskInfo.getType() == TASK_TYPE.VOLUME_REBALANCE) + && taskInfo.getStatus().getCode() != Status.STATUS_CODE_SUCCESS) + addTaskLabel(tasksSection, taskInfo); + } + tasksSection.pack(true); + form.reflow(true); + } + + private void addTaskLabel(Composite section, TaskInfo taskInfo) { + // Task related to Volumes context + if (taskInfo.getStatus().isPercentageSupported()) { + // TODO Progress bar or link to progress view + } + + CLabel lblTask = new CLabel(section, SWT.NONE); + String description = taskInfo.getDescription(); + switch (taskInfo.getStatus().getCode()) { + case Status.STATUS_CODE_PAUSE: + description += " (paused)"; + break; + case Status.STATUS_CODE_COMMIT_PENDING: + description += " (commit pending)"; + break; + case Status.STATUS_CODE_FAILURE: + description += " (failed)"; + break; + } + lblTask.setText(description); + lblTask.setImage((taskInfo.getType() == TASK_TYPE.BRICK_MIGRATE) ? guiHelper + .getImage(IImageKeys.BRICK_MIGRATE_32x32) : guiHelper.getImage(IImageKeys.VOLUME_REBALANCE_32x32)); + lblTask.redraw(); + } + + private void createSummarySection() { + summarySection = guiHelper.createSection(form, toolkit, AVAILABILITY, null, 2, false); + populateSummarySection(); + } + + private void populateSummarySection() { + if(volumes.getEntities().size() == 0) { + toolkit.createLabel(summarySection, + "This section will be populated after at least" + CoreConstants.NEWLINE +"one volume is created the storage cloud."); + return; + } + + Double[] values = new Double[] { Double.valueOf(getVolumeCountByStatus(volumes, VOLUME_STATUS.ONLINE)), + Double.valueOf(getVolumeCountByStatus(volumes, VOLUME_STATUS.OFFLINE)) }; + createStatusChart(toolkit, summarySection, values); + } + + private int getVolumeCountByStatus(EntityGroup<Volume> volumes, VOLUME_STATUS status) { + int count = 0; + for (Volume volume : volumes.getEntities()) { + if (volume.getStatus() == status) { + count++; + } + } + return count; + } + + private void createStatusChart(FormToolkit toolkit, Composite section, Double[] values) { + String[] categories = new String[] { "Online", "Offline" }; + ChartViewerComposite chartViewerComposite = new ChartViewerComposite(section, SWT.NONE, categories, values); + + GridData data = new GridData(SWT.FILL, SWT.FILL, false, false); + data.widthHint = 300; + data.heightHint = 150; + chartViewerComposite.setLayoutData(data); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.part.WorkbenchPart#setFocus() + */ + @Override + public void setFocus() { + if (form != null) { + form.setFocus(); + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumesView.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumesView.java new file mode 100644 index 00000000..41ccba1d --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/VolumesView.java @@ -0,0 +1,80 @@ +/** + * DiscoveredServersView.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 org.gluster.storage.management.console.views; + +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.part.ViewPart; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.views.pages.VolumesPage; +import org.gluster.storage.management.core.model.Entity; +import org.gluster.storage.management.core.model.EntityGroup; +import org.gluster.storage.management.core.model.Volume; + + +/** + * + */ +public class VolumesView extends ViewPart implements IDoubleClickListener { + public static final String ID = VolumesView.class.getName(); + private static final GUIHelper guiHelper = GUIHelper.getInstance(); + private EntityGroup<Volume> volumes; + private VolumesPage page; + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public void createPartControl(Composite parent) { + if (volumes == null) { + Object selectedObj = guiHelper.getSelectedEntity(getSite(), EntityGroup.class); + if (selectedObj != null && ((EntityGroup) selectedObj).getEntityType() == Volume.class) { + volumes = (EntityGroup<Volume>)selectedObj; + } + } + + page = new VolumesPage(parent, getSite(), volumes); + page.addDoubleClickListener(this); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.part.WorkbenchPart#setFocus() + */ + @Override + public void setFocus() { + page.setFocus(); + } + + @Override + public void doubleClick(DoubleClickEvent event) { + NavigationView clusterView = (NavigationView) guiHelper.getView(NavigationView.ID); + if (clusterView != null) { + clusterView.selectEntity((Entity) ((StructuredSelection) event.getSelection()).getFirstElement()); + } + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/AbstractDisksPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/AbstractDisksPage.java new file mode 100644 index 00000000..6d2959d2 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/AbstractDisksPage.java @@ -0,0 +1,335 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views.pages; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.TreeEditor; +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.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.ui.IWorkbenchSite; +import org.eclipse.ui.forms.events.HyperlinkAdapter; +import org.eclipse.ui.forms.events.HyperlinkEvent; +import org.eclipse.ui.forms.widgets.ImageHyperlink; +import org.gluster.storage.management.client.GlusterServersClient; +import org.gluster.storage.management.client.TasksClient; +import org.gluster.storage.management.console.Application; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.IEntityListener; +import org.gluster.storage.management.console.dialogs.InitDiskDialog; +import org.gluster.storage.management.console.utils.GlusterLogger; +import org.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import org.gluster.storage.management.core.model.ClusterListener; +import org.gluster.storage.management.core.model.DefaultClusterListener; +import org.gluster.storage.management.core.model.Device; +import org.gluster.storage.management.core.model.Disk; +import org.gluster.storage.management.core.model.Entity; +import org.gluster.storage.management.core.model.Event; +import org.gluster.storage.management.core.model.GlusterServer; +import org.gluster.storage.management.core.model.Status; +import org.gluster.storage.management.core.model.TaskInfo; +import org.gluster.storage.management.core.model.Device.DEVICE_STATUS; +import org.gluster.storage.management.core.model.Event.EVENT_TYPE; + + +public abstract class AbstractDisksPage extends AbstractTableTreeViewerPage<Disk> implements IEntityListener { + protected List<Disk> disks; + protected static final GlusterLogger logger = GlusterLogger.getInstance(); + + /** + * @return Index of the "status" column in the table. Return -1 if status column is not displayed + */ + protected abstract int getStatusColumnIndex(); + + public AbstractDisksPage(final Composite parent, int style, IWorkbenchSite site, List<Disk> disks) { + super(site, parent, style, false, true, disks); + this.disks = disks; + + // creates hyperlinks for "uninitialized" disks + setupStatusCellEditor(); + // Listen for disk status change events + Application.getApplication().addEntityListener(this); + } + + protected ClusterListener createClusterListener() { + return new DefaultClusterListener() { + @Override + public void serverChanged(GlusterServer server, Event event) { + super.serverChanged(server, event); + EVENT_TYPE eventType = event.getEventType(); + switch (eventType) { + case DEVICES_REMOVED: + case DEVICES_ADDED: + case DEVICES_CHANGED: + case GLUSTER_SERVER_CHANGED: + treeViewer.refresh(true); + default: + break; + } + } + }; + } + + private void createInitializeLink(final TreeItem item, final int rowNum, final Device uninitializedDevice) { + final Tree tree = treeViewer.getTree(); + final TreeEditor editor = new TreeEditor(tree); + editor.grabHorizontal = true; + editor.horizontalAlignment = SWT.RIGHT; + + tree.addPaintListener(new PaintListener() { + private TreeItem myItem = item; + private int myRowNum = rowNum; + private ImageHyperlink myLink = null; + private TreeEditor myEditor = null; + + private void createLinkFor(Device uninitializedDevice, TreeItem item1, int rowNum1) { + myItem = item1; + myRowNum = rowNum1; + + myEditor = new TreeEditor(tree); + myEditor.grabHorizontal = true; + myEditor.horizontalAlignment = SWT.RIGHT; + + myLink = toolkit.createImageHyperlink(tree, SWT.NONE); + // link.setImage(guiHelper.getImage(IImageKeys.DISK_UNINITIALIZED)); + myLink.setText("Initialize"); + myLink.addHyperlinkListener(new StatusLinkListener(myLink, myEditor, treeViewer, uninitializedDevice)); + + myEditor.setEditor(myLink, item1, getStatusColumnIndex()); + + myItem.addDisposeListener(new DisposeListener() { + @Override + public void widgetDisposed(DisposeEvent e) { + myLink.dispose(); + myEditor.dispose(); + } + }); + } + + @Override + public void paintControl(PaintEvent e) { + int itemCount = tree.getItemCount(); + + // Find the table item corresponding to our disk + + Device device = null; + int rowNum1 = -1; + TreeItem item1 = null; + + mainloop: + for (int i = 0; i < itemCount; i++) { + item1 = tree.getItem(i); + + device = (Device) item1.getData(); + if (device != null && device == uninitializedDevice) { + // this is an uninitialized "disk" + rowNum1 = i; + break; + } + + int partitionCount = item1.getItemCount(); + for(int j = 0; j < partitionCount; j++) { + TreeItem partitionItem = item1.getItem(j); + // check each partition + Device partition = (Device)partitionItem.getData(); + if(partition != null && partition == uninitializedDevice) { + // this is an uninitialized "partition" + rowNum1 = i + j; + item1 = partitionItem; + device = partition; + // found the uninitialized device. break out. + break mainloop; + } + } + } + + if (rowNum1 == -1) { + // item disposed and disk not visible. nothing to do. + return; + } + + if (myEditor == null || myItem.isDisposed()) { + // item visible, and + // either editor never created, OR + // old item disposed. create the link for it + createLinkFor(device, item1, rowNum1); + } + + if (rowNum1 != myRowNum) { + // disk visible, but at a different row num. re-create the link + myLink.dispose(); + myEditor.dispose(); + createLinkFor(device, item1, rowNum1); + } + + myEditor.layout(); // IMPORTANT. Without this, the link location goes for a toss on maximize + restore + } + }); + } + + private void setupStatusCellEditor() { + final TreeViewer viewer = treeViewer; + final Tree tree = viewer.getTree(); + int rowNum = 0; + for (int i = 0; i < tree.getItemCount(); i++, rowNum++) { + final TreeItem item = tree.getItem(i); + if (item.isDisposed() || item.getData() == null) { + continue; + } + final Disk disk = (Disk) item.getData(); + if (disk.isUninitialized()) { + createInitializeLink(item, rowNum, disk); + } + + if (disk.hasPartitions()) { + for(int j = 0; j < disk.getPartitions().size(); j++, rowNum++) { + TreeItem partitionItem = item.getItem(j); + // check each partition + Device partition = (Device)partitionItem.getData(); + if (partition.isUninitialized()) { + createInitializeLink(partitionItem, rowNum, partition); + } + } + } + } + } + + private final class StatusLinkListener extends HyperlinkAdapter { + private final Device device; + private final TreeEditor myEditor; + private final ImageHyperlink myLink; + private final TreeViewer viewer; + + private StatusLinkListener(ImageHyperlink link, TreeEditor editor, TreeViewer viewer, Device device) { + this.device = device; + this.viewer = viewer; + this.myEditor = editor; + this.myLink = link; + } + + private void updateStatus(final DEVICE_STATUS status, final boolean disposeEditor) { + if (disposeEditor) { + myLink.dispose(); + myEditor.dispose(); + } + device.setStatus(status); + viewer.update(device, new String[] { "status" }); + Application.getApplication().entityChanged(device, new String[] { "status" }); + } + + @Override + public void linkActivated(HyperlinkEvent e) { + GlusterDataModelManager modelManager = GlusterDataModelManager.getInstance(); + + // If the same task is already running return + String reference = device.getServerName() + ":" + device.getName(); + TaskInfo existingTaskInfo = modelManager.getTaskByReference(reference); + if (existingTaskInfo != null && existingTaskInfo.getStatus().getCode() != Status.STATUS_CODE_SUCCESS + && existingTaskInfo.getStatus().getCode() != Status.STATUS_CODE_FAILURE) { + MessageDialog.openInformation(getShell(), "Initialize disk - Error", "Initializing disk [" + reference + + "] is already in progress! Try later."); + return; + } + + // To collect the available fsType + GlusterServersClient serversClient = new GlusterServersClient(); + List<String> fsTypes = new ArrayList<String>(); + try { + fsTypes = serversClient.getFSTypes(device.getServerName()); + } catch (GlusterRuntimeException e1) { + MessageDialog.openError(getShell(), "Initialize disk - Error", e1.getMessage()); + return; + } + + InitDiskDialog formatDialog = new InitDiskDialog(getShell(), device.getName(), fsTypes); + int userAction = formatDialog.open(); + if (userAction == Window.CANCEL) { + // formatDialog.cancelPressed(); + return; + } + + try { + + URI uri = serversClient.initializeDisk(device.getServerName(), device.getName(), formatDialog.getFSType(), formatDialog.getMountPoint()); + + TasksClient taskClient = new TasksClient(); + TaskInfo taskInfo = taskClient.getTaskInfo(uri); + + if (taskInfo != null) { + modelManager.addTask(taskInfo); + } + + if (taskInfo.getStatus().getCode() == Status.STATUS_CODE_RUNNING) { + updateStatus(DEVICE_STATUS.INITIALIZING, true); + } else if (taskInfo.getStatus().getCode() == Status.STATUS_CODE_SUCCESS) { + // If format completed (instantly), get the server details and update the server in the model + GlusterServer oldServer = modelManager.getModel().getCluster().getServer(device.getServerName()); + GlusterServer newServer = serversClient.getGlusterServer(device.getServerName()); + modelManager.glusterServerChanged(oldServer, newServer); + // updateStatus(DEVICE_STATUS.INITIALIZED, true); + // GlusterDataModelManager.getInstance().updateDeviceStatus(device.getServerName(), device.getName(), + // DEVICE_STATUS.INITIALIZED); + } else { + MessageDialog.openError(getShell(), "Initialize disk - Error", taskInfo.getStatus().getMessage()); + } + guiHelper.showTaskView(); + } catch (Exception e1) { + logger.error("Exception while initialize disk", e1); + MessageDialog.openError(getShell(), "Initialize disk - Error", e1.getMessage()); + } + } + } + + @Override + public void entityChanged(final Entity entity, final String[] paremeters) { + if (!(entity instanceof Device)) { + return; + } + final Device device = (Device) entity; + + Display.getDefault().syncExec(new Runnable() { + public void run() { + treeViewer.update(device, paremeters); + + if (device.isUninitialized()) { + Tree tree = treeViewer.getTree(); + + for (int rowNum = 0; rowNum < tree.getItemCount(); rowNum++) { + TreeItem item = tree.getItem(rowNum); + if (item.getData() == device) { + createInitializeLink(item, rowNum, device); + } + } + } + } + }); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/AbstractTableTreeViewerPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/AbstractTableTreeViewerPage.java new file mode 100644 index 00000000..55535637 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/AbstractTableTreeViewerPage.java @@ -0,0 +1,191 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views.pages; + +import java.util.List; + +import org.eclipse.jface.layout.TreeColumnLayout; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.SWT; +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.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.TreeColumn; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchSite; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.model.ClusterListener; +import org.gluster.storage.management.core.model.Disk; + + +public abstract class AbstractTableTreeViewerPage<T> extends Composite implements ISelectionListener { + + protected final FormToolkit toolkit = new FormToolkit(Display.getCurrent()); + protected TreeViewer treeViewer; + protected GUIHelper guiHelper = GUIHelper.getInstance(); + protected Composite parent; + protected IWorkbenchSite site; + private ClusterListener clusterListener; + + private Text filterText; + + private void setupPageLayout() { + final GridLayout layout = new GridLayout(1, false); + layout.verticalSpacing = 10; + layout.marginTop = 10; + setLayout(layout); + } + + private Composite createTreeViewerComposite() { + Composite tableViewerComposite = new Composite(this, SWT.NO); + tableViewerComposite.setLayout(new FillLayout(SWT.HORIZONTAL)); + tableViewerComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + GridData layoutData = new GridData(); + layoutData.horizontalSpan=5; + layoutData.grabExcessHorizontalSpace = true; + layoutData.horizontalAlignment = SWT.FILL; + layoutData.verticalAlignment = SWT.FILL; + layoutData.grabExcessVerticalSpace = true; + tableViewerComposite.setLayoutData(layoutData); + + return tableViewerComposite; + } + + + public AbstractTableTreeViewerPage(IWorkbenchSite site, final Composite parent, int style, boolean useChechboxes, + boolean multiSelection, List<Disk> allDisks) { + super(parent, style); + + setupPageLayout(); + //new FormToolkit(Display.getCurrent()).createButton(this, "test1", SWT.PUSH); + + this.parent = parent; + this.site = site; + + toolkit.adapt(this); + toolkit.paintBordersFor(this); + + GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); + parent.setLayoutData(data); + + filterText = guiHelper.createFilterText(toolkit, this); + + Composite tableViewerComposite = createTreeViewerComposite(); + createTreeViewer(allDisks, tableViewerComposite); + parent.layout(); // Important - this actually paints the table + + createListeners(parent); + } + + private void createListeners(final Composite parent) { + /** + * 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 + */ + addPaintListener(new PaintListener() { + + @Override + public void paintControl(PaintEvent e) { + parent.layout(); + } + }); + + clusterListener = createClusterListener(); + GlusterDataModelManager.getInstance().addClusterListener(clusterListener); + + addDisposeListener(new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent e) { + toolkit.dispose(); + GlusterDataModelManager.getInstance().removeClusterListener(clusterListener); + } + }); + } + + protected abstract ClusterListener createClusterListener(); + protected abstract IBaseLabelProvider getLabelProvider(); + protected abstract IContentProvider getContentProvider(); + + private void createTreeViewer(List<Disk> allDisks, Composite tableViewerComposite) { + treeViewer = new TreeViewer(tableViewerComposite); + treeViewer.getTree().setHeaderVisible(true); + treeViewer.getTree().setLinesVisible(true); + + TreeColumnLayout ad = new TreeColumnLayout(); + tableViewerComposite.setLayout(ad); + + TreeColumn column = new TreeColumn(treeViewer.getTree(),SWT.NONE); + column.setWidth(100); + column.setText("Disk"); + ad.setColumnData(column, new ColumnWeightData(50, 100)); + + column = new TreeColumn(treeViewer.getTree(),SWT.NONE); + column.setWidth(100); + column.setText("Partition"); + ad.setColumnData(column,new ColumnWeightData(50, 100)); + + column = new TreeColumn(treeViewer.getTree(),SWT.NONE); + column.setWidth(100); + column.setText("Free Space (GB)"); + ad.setColumnData(column, new ColumnWeightData(50, 100)); + + column = new TreeColumn(treeViewer.getTree(),SWT.NONE); + column.setWidth(100); + column.setText("Total Space (GB)"); + ad.setColumnData(column,new ColumnWeightData(50, 100)); + + column = new TreeColumn(treeViewer.getTree(),SWT.NONE); + column.setWidth(100); + column.setText("Status"); + ad.setColumnData(column,new ColumnWeightData(50, 100)); + + treeViewer.setLabelProvider(getLabelProvider()); + treeViewer.setContentProvider(getContentProvider()); + treeViewer.setInput(allDisks); + + // Create a case insensitive filter for the table viewer using the filter text field + guiHelper.createFilter(treeViewer, filterText, false); + + treeViewer.expandAll(); + } + + + /* (non-Javadoc) + * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + @Override + public void selectionChanged(IWorkbenchPart part, ISelection selection) { + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/AbstractTableViewerPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/AbstractTableViewerPage.java new file mode 100644 index 00000000..88468eb3 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/AbstractTableViewerPage.java @@ -0,0 +1,263 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views.pages; + +import java.util.List; + +import org.eclipse.jface.layout.TableColumnLayout; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchSite; +import org.eclipse.ui.forms.events.HyperlinkAdapter; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.Hyperlink; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.console.utils.TableViewerComparator; +import org.gluster.storage.management.core.model.ClusterListener; + + +public abstract class AbstractTableViewerPage<T> extends Composite implements ISelectionListener { + + private boolean useCheckboxes; + private boolean multiSelection; + + protected final FormToolkit toolkit = new FormToolkit(Display.getCurrent()); + protected TableViewer tableViewer; + protected GUIHelper guiHelper = GUIHelper.getInstance(); + protected Composite parent; + protected IWorkbenchSite site; + + private Hyperlink linkAll, linkNone; + private ClusterListener clusterListener; + + public AbstractTableViewerPage(IWorkbenchSite site, final Composite parent, int style, boolean useChechboxes, boolean multiSelection, Object model) { + super(parent, style); + this.parent = parent; + this.site = site; + + this.useCheckboxes = useChechboxes; + this.multiSelection = multiSelection; + + toolkit.adapt(this); + toolkit.paintBordersFor(this); + + setupPageLayout(); + + createCheckboxSelectionLinks(); + + Text filterText = guiHelper.createFilterText(toolkit, this); + + setupTableViewer(site, filterText); + tableViewer.setInput(model); + // register as selection provider so that other views can listen to any selection events on the tree + site.setSelectionProvider(tableViewer); + site.getPage().addSelectionListener(this); + + + parent.layout(); // Important - this actually paints the table + + createListeners(parent); + } + + public void createCheckboxSelectionLinks() { + if (useCheckboxes) { + // create the "select all/none" links + toolkit.createLabel(this, "Select"); + linkAll = toolkit.createHyperlink(this, "all", SWT.NONE); + linkAll.addHyperlinkListener(new HyperlinkAdapter() { + @Override + public void linkActivated(org.eclipse.ui.forms.events.HyperlinkEvent e) { + ((CheckboxTableViewer) tableViewer).setAllChecked(true); + tableViewer.setSelection(new StructuredSelection(getAllEntities())); + } + }); + + toolkit.createLabel(this, " / "); + + linkNone = toolkit.createHyperlink(this, "none", SWT.NONE); + linkNone.addHyperlinkListener(new HyperlinkAdapter() { + @Override + public void linkActivated(org.eclipse.ui.forms.events.HyperlinkEvent e) { + ((CheckboxTableViewer) tableViewer).setAllChecked(false); + tableViewer.setSelection(StructuredSelection.EMPTY); + } + }); + } else { + // create dummy labels to maintain layout + toolkit.createLabel(this, ""); + toolkit.createLabel(this, ""); + toolkit.createLabel(this, ""); + toolkit.createLabel(this, ""); + } + } + + private void createListeners(final Composite parent) { + /** + * 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 + */ + addPaintListener(new PaintListener() { + + @Override + public void paintControl(PaintEvent e) { + parent.layout(); + } + }); + + clusterListener = createClusterListener(); + GlusterDataModelManager.getInstance().addClusterListener(clusterListener); + + addDisposeListener(new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent e) { + toolkit.dispose(); + GlusterDataModelManager.getInstance().removeClusterListener(clusterListener); + } + }); + } + + protected abstract ClusterListener createClusterListener(); + protected abstract String[] getColumnNames(); + protected abstract void setColumnProperties(Table table); + protected abstract IBaseLabelProvider getLabelProvider(); + protected abstract IContentProvider getContentProvider(); + protected abstract List<T> getAllEntities(); + + public void addDoubleClickListener(IDoubleClickListener listener) { + tableViewer.addDoubleClickListener(listener); + } + + private void setupPageLayout() { + final GridLayout layout = new GridLayout(5, false); + layout.verticalSpacing = 10; + layout.marginTop = 10; + setLayout(layout); + } + + protected void setupTable(Composite parent, Table table) { + table.setHeaderVisible(true); + table.setLinesVisible(false); + + TableColumnLayout tableColumnLayout = guiHelper.createTableColumnLayout(table, getColumnNames()); + parent.setLayout(tableColumnLayout); + + setColumnProperties(table); + } + + private void createTableViewer(Composite parent) { + int style = SWT.FLAT | SWT.FULL_SELECTION; + style |= (multiSelection ? SWT.MULTI : SWT.SINGLE); + + if(useCheckboxes) { + tableViewer = CheckboxTableViewer.newCheckList(parent, style); + } else { + tableViewer = new TableViewer(parent, style); + } + + tableViewer.setLabelProvider(getLabelProvider()); + tableViewer.setContentProvider(getContentProvider()); + setupTable(parent, tableViewer.getTable()); + } + + private Composite createTableViewerComposite() { + Composite tableViewerComposite = new Composite(this, SWT.NO); + tableViewerComposite.setLayout(new FillLayout(SWT.HORIZONTAL)); + tableViewerComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + GridData layoutData = new GridData(); + layoutData.horizontalSpan=5; + layoutData.grabExcessHorizontalSpace = true; + layoutData.horizontalAlignment = SWT.FILL; + layoutData.verticalAlignment = SWT.FILL; + layoutData.grabExcessVerticalSpace = true; + tableViewerComposite.setLayoutData(layoutData); + + return tableViewerComposite; + } + + private void setupTableViewer(IWorkbenchSite site, final Text filterText) { + Composite tableViewerComposite = createTableViewerComposite(); + createTableViewer(tableViewerComposite); + site.setSelectionProvider(tableViewer); + + if(useCheckboxes) { + // make sure that table selection is driven by checkbox selection + guiHelper.configureCheckboxTableViewer((CheckboxTableViewer)tableViewer); + } + + // Create a case insensitive filter for the table viewer using the filter text field + guiHelper.createFilter(tableViewer, filterText, false); + + tableViewer.setComparator(createViewerComparator()); + for (int columnIndex = 0; columnIndex < tableViewer.getTable().getColumnCount(); columnIndex++) { + TableColumn column = tableViewer.getTable().getColumn(columnIndex); + column.addSelectionListener(getColumnSelectionAdapter(column, columnIndex)); + } + } + + private SelectionAdapter getColumnSelectionAdapter(final TableColumn column, final int columnIndex) { + return new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + ViewerComparator viewerComparator = tableViewer.getComparator(); + if(viewerComparator instanceof TableViewerComparator) { + TableViewerComparator comparator = (TableViewerComparator)viewerComparator; + comparator.setColumn(columnIndex); + tableViewer.getTable().setSortDirection(comparator.getDirection()); + tableViewer.getTable().setSortColumn(column); + tableViewer.refresh(); + } + } + }; + } + + protected abstract ViewerComparator createViewerComparator(); + + /* (non-Javadoc) + * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + @Override + public void selectionChanged(IWorkbenchPart part, ISelection selection) { + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/BricksPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/BricksPage.java new file mode 100644 index 00000000..c9660a77 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/BricksPage.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views.pages; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.ui.IWorkbenchSite; +import org.gluster.storage.management.console.BrickTableLabelProvider; +import org.gluster.storage.management.core.model.Brick; +import org.gluster.storage.management.core.model.ClusterListener; +import org.gluster.storage.management.core.model.DefaultClusterListener; +import org.gluster.storage.management.core.model.Event; +import org.gluster.storage.management.core.model.Volume; + + +public class BricksPage extends AbstractTableViewerPage<Brick> { + private List<Brick> bricks; + + public enum BRICK_TABLE_COLUMN_INDICES { + SERVER, BRICK, FREE_SPACE, TOTAL_SPACE, STATUS + }; + + private static final String[] DISK_TABLE_COLUMN_NAMES = new String[] { "Server", "Brick Directory", "Free Space (GB)", + "Total Space (GB)", "Status" }; + + public BricksPage(Composite parent, int style, IWorkbenchSite site, final List<Brick> bricks) { + super(site, parent, style, true, true, bricks); + this.bricks = bricks; + } + + @Override + protected ClusterListener createClusterListener() { + return new DefaultClusterListener() { + @SuppressWarnings("unchecked") + @Override + public void volumeChanged(Volume volume, Event event) { + switch (event.getEventType()) { + case BRICKS_ADDED: + tableViewer.add(((Collection<Brick>) event.getEventData()).toArray()); + parent.update(); + break; + + case BRICKS_REMOVED: + tableViewer.remove(((Collection<Brick>) event.getEventData()).toArray()); + parent.update(); + break; + + case BRICKS_CHANGED: + Object eventData = event.getEventData(); + Brick[] updatedBricks; + if(eventData instanceof Map) { + updatedBricks = ((Map<Brick, Brick>) eventData).keySet().toArray(new Brick[0]); + } else { + updatedBricks = ((Collection<Brick>)eventData).toArray(new Brick[0]); + } + tableViewer.update(updatedBricks, null); + parent.update(); + tableViewer.refresh(true); + default: + break; + } + } + }; + } + + @Override + protected String[] getColumnNames() { + return DISK_TABLE_COLUMN_NAMES; + } + + @Override + protected void setColumnProperties(Table table) { + guiHelper.setColumnProperties(table, BRICK_TABLE_COLUMN_INDICES.SERVER.ordinal(), SWT.CENTER, 100); + guiHelper.setColumnProperties(table, BRICK_TABLE_COLUMN_INDICES.BRICK.ordinal(), SWT.CENTER, 100); + guiHelper.setColumnProperties(table, BRICK_TABLE_COLUMN_INDICES.FREE_SPACE.ordinal(), SWT.CENTER, 90); + guiHelper.setColumnProperties(table, BRICK_TABLE_COLUMN_INDICES.TOTAL_SPACE.ordinal(), SWT.CENTER, 90); + } + + @Override + protected ITableLabelProvider getLabelProvider() { + return new BrickTableLabelProvider(); + } + + @Override + protected IContentProvider getContentProvider() { + return new ArrayContentProvider(); + } + + @Override + protected List<Brick> getAllEntities() { + return bricks; + } + + @Override + protected ViewerComparator createViewerComparator() { + return null; + } +}
\ No newline at end of file diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/DiskTreeContentProvider.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/DiskTreeContentProvider.java new file mode 100644 index 00000000..a7518d3c --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/DiskTreeContentProvider.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views.pages; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.gluster.storage.management.core.model.Disk; +import org.gluster.storage.management.core.model.Partition; + + +public class DiskTreeContentProvider implements ITreeContentProvider { + + private List<Disk> disks = new ArrayList<Disk>(); + + public DiskTreeContentProvider(List<Disk> disks) { + this.disks = disks; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) + */ + public Object[] getElements(Object inputElement) { + return ((List<Disk>) inputElement).toArray(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IContentProvider#dispose() + */ + public void dispose() { + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, + * java.lang.Object, java.lang.Object) + */ + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object) + */ + public Object[] getChildren(Object parentElement) { + if(parentElement instanceof Disk) { + return ((Disk)parentElement).getPartitions().toArray(); + } else { + return null; + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object) + */ + public Object getParent(Object element) { + if (element == null) { + return null; + } + + if(element instanceof Partition) { + // find the disk of this partition and return + return getDiskForPartition((Partition)element); + } else { + return null; + } + } + + private Disk getDiskForPartition(Partition partition) { + for(Disk disk : disks) { + for(Partition diskPartition : disk.getPartitions()) { + if(partition.getName().equals(diskPartition.getName())) { + return disk; + } + } + } + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object) + */ + public boolean hasChildren(Object element) { + return (element instanceof Disk && ((Disk)element).getPartitions().size() > 0); + } + + + +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/DisksPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/DisksPage.java new file mode 100644 index 00000000..21704d4c --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/DisksPage.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views.pages; + +import java.util.List; + +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.IWorkbenchSite; +import org.gluster.storage.management.console.DeviceTableLabelProvider; +import org.gluster.storage.management.core.model.Disk; +import org.gluster.storage.management.core.model.Entity; + + +public class DisksPage extends AbstractDisksPage { + + public enum DISK_TABLE_COLUMN_INDICES { + DISK, PARTITION, FREE_SPACE, TOTAL_SPACE, STATUS + }; + + private static final String[] DISK_TABLE_COLUMN_NAMES = new String[] { "Disk", "Partition", "Free Space (GB)", + "Total Space (GB)", "Status" }; + + public DisksPage(final Composite parent, int style, IWorkbenchSite site, List<Disk> disks) { + super(parent, style, site, disks); + } + + private String getDiskTableColumnDesc(DISK_TABLE_COLUMN_INDICES idx) { + return DISK_TABLE_COLUMN_NAMES[idx.ordinal()]; + } + + @Override + protected DeviceTableLabelProvider getLabelProvider() { + return new DeviceTableLabelProvider(); + } + + + @Override + protected IContentProvider getContentProvider() { + return new DiskTreeContentProvider(disks); + } + + @Override + protected int getStatusColumnIndex() { + return DISK_TABLE_COLUMN_INDICES.STATUS.ordinal(); + } + + @Override + public void entityChanged(Entity entity, String[] paremeters) { + // TODO Auto-generated method stub + + } + +}
\ No newline at end of file diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/GlusterServersPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/GlusterServersPage.java new file mode 100644 index 00000000..bf0e797d --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/GlusterServersPage.java @@ -0,0 +1,138 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views.pages; + +import java.util.List; + +import org.eclipse.jface.layout.TableColumnLayout; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.ui.IWorkbenchSite; +import org.gluster.storage.management.console.EntityGroupContentProvider; +import org.gluster.storage.management.console.GlusterServerTableLabelProvider; +import org.gluster.storage.management.console.utils.TableViewerComparator; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.ClusterListener; +import org.gluster.storage.management.core.model.DefaultClusterListener; +import org.gluster.storage.management.core.model.EntityGroup; +import org.gluster.storage.management.core.model.Event; +import org.gluster.storage.management.core.model.GlusterServer; + + +public class GlusterServersPage extends AbstractTableViewerPage<GlusterServer> { + private List<GlusterServer> glusterServers; + + public enum GLUSTER_SERVER_TABLE_COLUMN_INDICES { + NAME, IP_ADDRESSES, NUM_OF_CPUS, TOTAL_MEMORY, TOTAL_FREE_SPACE, TOTAL_DISK_SPACE, STATUS // Removed PREFERRED_NETWORK + }; + + private static final String[] GLUSTER_SERVER_TABLE_COLUMN_NAMES = new String[] { "Name", "IP Address(es)", + "Number" + CoreConstants.NEWLINE + "of CPUs", "Total" + CoreConstants.NEWLINE + "Memory (GB)", + "Free Space (GB)", "Total " + CoreConstants.NEWLINE + " Space (GB)", "Status" }; // Removed "Preferred\nNetwork", + + public GlusterServersPage(IWorkbenchSite site, final Composite parent, int style, final EntityGroup<GlusterServer> servers) { + super(site, parent, style, true, true, servers); + this.glusterServers = servers.getEntities(); + } + + @Override + protected ViewerComparator createViewerComparator() { + return new TableViewerComparator(); + } + + @Override + protected ClusterListener createClusterListener() { + return new DefaultClusterListener() { + + @Override + public void serverAdded(GlusterServer server) { + tableViewer.add(server); + parent.update(); + } + + @Override + public void serverRemoved(GlusterServer server) { + tableViewer.remove(server); + parent.update(); + } + + @Override + public void serverChanged(GlusterServer server, Event event) { + tableViewer.update(server, null); + parent.update(); + } + }; + } + + @Override + protected void setColumnProperties(Table table) { + setColumnProperties(table, GLUSTER_SERVER_TABLE_COLUMN_INDICES.NAME, SWT.CENTER, 100); + setColumnProperties(table, GLUSTER_SERVER_TABLE_COLUMN_INDICES.STATUS, SWT.CENTER, 70); + // setColumnProperties(table, GLUSTER_SERVER_TABLE_COLUMN_INDICES.PREFERRED_NETWORK, SWT.CENTER, 90); + setColumnProperties(table, GLUSTER_SERVER_TABLE_COLUMN_INDICES.NUM_OF_CPUS, SWT.CENTER, 90); + //setColumnProperties(table, SERVER_DISK_TABLE_COLUMN_INDICES.CPU_USAGE, SWT.CENTER, 90); + setColumnProperties(table, GLUSTER_SERVER_TABLE_COLUMN_INDICES.TOTAL_MEMORY, SWT.CENTER, 90); + //setColumnProperties(table, SERVER_DISK_TABLE_COLUMN_INDICES.MEMORY_IN_USE, SWT.CENTER, 90); + setColumnProperties(table, GLUSTER_SERVER_TABLE_COLUMN_INDICES.TOTAL_FREE_SPACE, SWT.CENTER, 90); + setColumnProperties(table, GLUSTER_SERVER_TABLE_COLUMN_INDICES.TOTAL_DISK_SPACE, SWT.CENTER, 90); + //setColumnProperties(table, SERVER_DISK_TABLE_COLUMN_INDICES.DISK_SPACE_IN_USE, SWT.CENTER, 90); + } + + @Override + protected IBaseLabelProvider getLabelProvider() { + return new GlusterServerTableLabelProvider(); + } + + @Override + protected IContentProvider getContentProvider() { + return new EntityGroupContentProvider<GlusterServer>(); + } + + @Override + protected String[] getColumnNames() { + return GLUSTER_SERVER_TABLE_COLUMN_NAMES; + } + + @Override + protected List<GlusterServer> getAllEntities() { + return glusterServers; + } + + /** + * 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, GLUSTER_SERVER_TABLE_COLUMN_INDICES columnIndex, int alignment, int weight) { + TableColumn column = table.getColumn(columnIndex.ordinal()); + column.setAlignment(alignment); + + TableColumnLayout tableColumnLayout = (TableColumnLayout) table.getParent().getLayout(); + tableColumnLayout.setColumnData(column, new ColumnWeightData(weight)); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/OptionKeyEditingSupport.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/OptionKeyEditingSupport.java new file mode 100644 index 00000000..676e0ee4 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/OptionKeyEditingSupport.java @@ -0,0 +1,120 @@ +/** + * + */ +package org.gluster.storage.management.console.views.pages; + +import java.util.ArrayList; +import java.util.List; + +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 org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.VolumeOption; +import org.gluster.storage.management.core.model.VolumeOptionInfo; +import org.gluster.storage.management.core.model.VolumeOptions; + + +/** + * 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().getVolumeOptionsInfo(); + 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>(); + VolumeOptions 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]); + } + + @Override + protected void setValue(final Object element, final Object value) { + VolumeOption oldEntry = (VolumeOption)element; + Integer newValue = (Integer)value; + String newKey = allowedKeys[newValue]; + + if (((VolumeOption)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) { + VolumeOption 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(VolumeOption entryBeingAdded) { + for(int index = 0; index < allowedKeys.length; index++) { + if(allowedKeys[index].equals(entryBeingAdded.getKey())) { + return index; + } + } + return -1; + } + + protected VolumeOption getEntryBeingAdded() { + List<VolumeOption> options = volume.getOptions().getOptions(); + int size = options.size(); + String lastValue = options.get(size - 1).getValue(); + if(lastValue == null || lastValue.isEmpty()) { + // it's the LAST entry, and it's value is empty. + // means this is a new row being added in the table viewer. + return options.get(size - 1); + } + return null; + } + + @Override + protected boolean canEdit(Object element) { + VolumeOption entry = (VolumeOption)element; + return (entry.getKey().isEmpty() || entry.getValue().isEmpty()); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/OptionValueEditingSupport.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/OptionValueEditingSupport.java new file mode 100644 index 00000000..b2493f0f --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/OptionValueEditingSupport.java @@ -0,0 +1,110 @@ +/** + * + */ +package org.gluster.storage.management.console.views.pages; + +import java.util.List; + +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 org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.VolumeOption; +import org.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().getVolumeOptionsInfo(); + private GUIHelper guiHelper = GUIHelper.getInstance(); + + public OptionValueEditingSupport(ColumnViewer viewer, Volume volume) { + super(viewer); + this.volume = volume; + this.cellEditor = new TextCellEditor((Composite) viewer.getControl()); + } + + @Override + protected void setValue(final Object element, final Object value) { + final VolumeOption entry = (VolumeOption)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(); + try { + client.setVolumeOption(volume.getName(), optionKey, optionValue); + GlusterDataModelManager.getInstance().setVolumeOption(volume, optionKey, optionValue); + } catch(Exception e) { + MessageDialog.openError(Display.getDefault().getActiveShell(), "Set Volume Option", e.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 ""; + } + + @Override + protected Object getValue(Object element) { + VolumeOption entry = (VolumeOption) 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/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/ServerDisksPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/ServerDisksPage.java new file mode 100644 index 00000000..da25e49f --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/ServerDisksPage.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views.pages; + +import java.util.List; + +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.IWorkbenchSite; +import org.gluster.storage.management.console.ServerDiskTableLabelProvider; +import org.gluster.storage.management.core.model.Disk; +import org.gluster.storage.management.core.model.Entity; + + +public class ServerDisksPage extends AbstractDisksPage { + + public ServerDisksPage(Composite parent, int style, IWorkbenchSite site, List<Disk> disks) { + super(parent, style, site, disks); + } + + public enum SERVER_DISK_TABLE_COLUMN_INDICES { + DISK, PARTITION, FREE_SPACE, TOTAL_SPACE, STATUS + }; + + private static final String[] SERVER_DISK_TABLE_COLUMN_NAMES = new String[] { "Disk", "Partition", "Free Space (GB)", + "Total Space (GB)", "Status" }; + + @Override + protected int getStatusColumnIndex() { + return SERVER_DISK_TABLE_COLUMN_INDICES.STATUS.ordinal(); + } + + @Override + protected ServerDiskTableLabelProvider getLabelProvider() { + // return new DeviceTableLabelProvider(); + return new ServerDiskTableLabelProvider(); + } + + + @Override + protected IContentProvider getContentProvider() { + return new DiskTreeContentProvider(disks); + } + + @Override + public void entityChanged(Entity entity, String[] paremeters) { + // TODO Auto-generated method stub + + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/ServerLogsPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/ServerLogsPage.java new file mode 100644 index 00000000..ec7c65c6 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/ServerLogsPage.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views.pages; + +import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.ListViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.VerifyEvent; +import org.eclipse.swt.events.VerifyListener; +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.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.model.GlusterServer; + + +public class ServerLogsPage extends Composite { + + private final FormToolkit toolkit = new FormToolkit(Display.getCurrent()); + private final GUIHelper guiHelper = GUIHelper.getInstance(); + private Text text; + private Table table; + + 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" }; + + /** + * Create the composite. + * + * @param parent + * @param style + */ + public ServerLogsPage(Composite parent, int style, GlusterServer server) { + super(parent, style); + addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + toolkit.dispose(); + } + }); + toolkit.adapt(this); + toolkit.paintBordersFor(this); + + setLayout(new GridLayout(1, false)); + GridData layoutData = new GridData(); + layoutData.grabExcessHorizontalSpace = true; + layoutData.grabExcessVerticalSpace = true; + setLayoutData(layoutData); + + 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); + text.setTextLimit(4); + text.addVerifyListener(new VerifyListener() { + + @Override + public void verifyText(VerifyEvent event) { + // Assume we allow it + event.doit = true; + + String text = event.text; + char[] chars = text.toCharArray(); + + // Don't allow if text contains non-digit characters + for (int i = 0; i < chars.length; i++) { + if (!Character.isDigit(chars[i])) { + event.doit = false; + break; + } + } + + } + }); + + Label lblMessagesAndFilter = toolkit.createLabel(composite, " messages from ", SWT.CENTER); + lblMessagesAndFilter.setBounds(160, 15, 110, 20); + + Combo combo = new Combo(composite, SWT.CENTER); + combo.setBounds(295, 15, 100, 20); + combo.setItems(new String[] { "syslog", "dmesg" }); + toolkit.adapt(combo); + toolkit.paintBordersFor(combo); + combo.select(0); + + Button btngo = toolkit.createButton(composite, "&Go", SWT.NONE); + btngo.setBounds(410, 13, 50, 30); + + Label separator = toolkit.createLabel(composite, "", SWT.SEPARATOR | SWT.HORIZONTAL | SWT.FILL); + separator.setBounds(0, 50, 500, 2); + + Label lblFilterString = toolkit.createLabel(composite, "Filter String", SWT.LEFT); + lblFilterString.setBounds(0, 65, 100, 20); + + text = guiHelper.createFilterText(toolkit, composite); + text.setBounds(105, 65, 250, 20); + + Composite logContentsComposite = createLogContentsComposite(toolkit); + // Text logContentsText = toolkit.createText(logContentsComposite, "", SWT.MULTI | SWT.FLAT | SWT.BORDER); + // logContentsText.setEditable(false); + // populateDummyLogContent(logContentsText); + + ListViewer logViewer = new ListViewer(logContentsComposite, SWT.BORDER | SWT.V_SCROLL | SWT.NO); + logViewer.setContentProvider(new ArrayContentProvider()); + guiHelper.createFilter(logViewer, text, false); + logViewer.setInput(getDummyLogContents()); + + // TODO: Link the filter string with the contents text + } + + private Composite createLogContentsComposite(FormToolkit toolkit) { + Composite tableViewerComposite = toolkit.createComposite(this, SWT.NONE); + tableViewerComposite.setLayout(new FillLayout(SWT.HORIZONTAL)); + GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true); + layoutData.verticalIndent = 10; + tableViewerComposite.setLayoutData(layoutData); + return tableViewerComposite; + } + + private String[] getDummyLogContents() { + + String[] logMessages = { + "Jan 19 13:43:08 shireesh-laptop dhclient: last message repeated 6 times", + "Jan 19 13:44:08 shireesh-laptop dhclient: last message repeated 5 times", + "Jan 19 13:44:47 shireesh-laptop dhclient: last message repeated 2 times", + "Jan 19 13:44:47 shireesh-laptop dhclient: DHCPREQUEST of 192.168.1.174 on eth1 to 255.255.255.255 port 67", + "Jan 19 13:45:49 shireesh-laptop dhclient: last message repeated 6 times", + "Jan 19 13:46:59 shireesh-laptop dhclient: last message repeated 6 times", + "Jan 19 13:48:01 shireesh-laptop dhclient: last message repeated 4 times", + "Jan 19 13:49:02 shireesh-laptop dhclient: last message repeated 5 times", + "Jan 19 13:50:08 shireesh-laptop dhclient: last message repeated 4 times", + "Jan 19 13:51:08 shireesh-laptop dhclient: last message repeated 6 times", + "Jan 19 13:52:08 shireesh-laptop dhclient: last message repeated 4 times", + "Jan 19 13:53:08 shireesh-laptop dhclient: last message repeated 6 times", + "Jan 19 13:54:08 shireesh-laptop dhclient: last message repeated 5 times", + "Jan 19 13:55:08 shireesh-laptop dhclient: last message repeated 4 times", + "Jan 19 13:56:08 shireesh-laptop dhclient: last message repeated 4 times", + "Jan 19 13:57:08 shireesh-laptop dhclient: last message repeated 3 times", + "Jan 19 13:58:08 shireesh-laptop dhclient: last message repeated 6 times", + "Jan 19 13:59:08 shireesh-laptop dhclient: last message repeated 4 times", + "Jan 19 13:59:40 shireesh-laptop dhclient: last message repeated 3 times", + "Jan 19 13:59:40 shireesh-laptop NetworkManager: <info> DHCP: device eth1 state changed bound -> expire", + "Jan 19 13:59:40 shireesh-laptop dhclient: DHCPDISCOVER on eth1 to 255.255.255.255 port 67 interval 8", + "Jan 19 13:59:40 shireesh-laptop NetworkManager: <info> DHCP: device eth1 state changed expire -> preinit", + "Jan 19 13:59:40 shireesh-laptop dhclient: DHCPOFFER of 192.168.1.174 from 192.168.1.1", + "Jan 19 13:59:40 shireesh-laptop dhclient: DHCPREQUEST of 192.168.1.174 on eth1 to 255.255.255.255 port 67", + "Jan 19 13:59:40 shireesh-laptop dhclient: DHCPACK of 192.168.1.174 from 192.168.1.1", + "Jan 19 13:59:40 shireesh-laptop dhclient: bound to 192.168.1.174 -- renewal in 3205 seconds.", + "Jan 19 13:59:40 shireesh-laptop NetworkManager: <info> DHCP: device eth1 state changed preinit -> bound", + "Jan 19 13:59:40 shireesh-laptop NetworkManager: <info> address 192.168.1.174", + "Jan 19 13:59:40 shireesh-laptop NetworkManager: <info> prefix 24 (255.255.255.0)", + "Jan 19 13:59:40 shireesh-laptop NetworkManager: <info> gateway 192.168.1.1", + "Jan 19 13:59:40 shireesh-laptop NetworkManager: <info> nameserver '192.168.1.1'", + "Jan 19 13:59:40 shireesh-laptop NetworkManager: <info> domain name 'in.gluster.com'", + "Jan 19 14:03:53 shireesh-laptop avahi-daemon[1098]: Invalid legacy unicast query packet.", + "Jan 19 14:03:53 shireesh-laptop avahi-daemon[1098]: Received response from host 192.168.1.155 with invalid source port 37219 on interface 'eth0.0'", + "Jan 19 14:03:54 shireesh-laptop avahi-daemon[1098]: Invalid legacy unicast query packet.", + "Jan 19 14:03:54 shireesh-laptop avahi-daemon[1098]: Invalid legacy unicast query packet.", + "Jan 19 14:03:54 shireesh-laptop avahi-daemon[1098]: Received response from host 192.168.1.155 with invalid source port 37219 on interface 'eth0.0'", + "Jan 19 14:05:09 shireesh-laptop avahi-daemon[1098]: last message repeated 8 times", + "Jan 19 14:12:48 shireesh-laptop NetworkManager: <debug> [1295426568.002642] periodic_update(): Roamed from BSSID E0:CB:4E:C0:0B:7F (glfs) to (none) ((none))", + "Jan 19 14:12:54 shireesh-laptop NetworkManager: <debug> [1295426574.002448] periodic_update(): Roamed from BSSID (none) ((none)) to E0:CB:4E:C0:0B:7F (glfs)", + "Jan 19 14:17:01 shireesh-laptop CRON[5321]: (root) CMD ( cd / && run-parts --report /etc/cron.hourly)" }; + + return logMessages; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/ServersPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/ServersPage.java new file mode 100644 index 00000000..ae34e948 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/ServersPage.java @@ -0,0 +1,138 @@ + /******************************************************************************* + * 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 org.gluster.storage.management.console.views.pages; + +import java.util.List; + +import org.eclipse.jface.layout.TableColumnLayout; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.ui.IWorkbenchSite; +import org.gluster.storage.management.console.EntityGroupContentProvider; +import org.gluster.storage.management.console.ServerTableLabelProvider; +import org.gluster.storage.management.core.model.ClusterListener; +import org.gluster.storage.management.core.model.DefaultClusterListener; +import org.gluster.storage.management.core.model.EntityGroup; +import org.gluster.storage.management.core.model.Event; +import org.gluster.storage.management.core.model.Server; + + +public class ServersPage extends AbstractTableViewerPage<Server> { + private List<Server> servers; + + public enum SERVER_TABLE_COLUMN_INDICES { + NAME, IP_ADDRESSES, NUM_OF_DISKS, TOTAL_DISK_SPACE + }; + + private static final String[] SERVER_TABLE_COLUMN_NAMES = new String[] { "Name", "IP Address(es)", "Number of Disks", "Total Disk Space (GB)" }; + + public ServersPage(final Composite parent, IWorkbenchSite site, EntityGroup<Server> serversGroup) { + super(site, parent, SWT.NONE, true, true, serversGroup); + this.servers = serversGroup.getEntities(); + } + + @Override + protected ClusterListener createClusterListener() { + return new DefaultClusterListener() { + @Override + public void discoveredServerRemoved(Server server) { + tableViewer.remove(server); + parent.update(); + } + + @Override + public void discoveredServerAdded(Server server) { + tableViewer.add(server); + parent.update(); + } + + @Override + public void discoveredServerChanged(Server server, Event event) { + tableViewer.update(server, null); + parent.update(); + } + }; + } + + public void setInput(EntityGroup<Server> servers) { + tableViewer.setInput(servers); + tableViewer.refresh(); + } + + @Override + protected void setColumnProperties(Table table) { + setColumnProperties(table, SERVER_TABLE_COLUMN_INDICES.NAME, SWT.CENTER, 70); + setColumnProperties(table, SERVER_TABLE_COLUMN_INDICES.IP_ADDRESSES, SWT.CENTER, 100); + setColumnProperties(table, SERVER_TABLE_COLUMN_INDICES.NUM_OF_DISKS, SWT.CENTER, 70); + setColumnProperties(table, SERVER_TABLE_COLUMN_INDICES.TOTAL_DISK_SPACE, SWT.CENTER, 70); + // setColumnProperties(table, SERVER_DISK_TABLE_COLUMN_INDICES.NUM_OF_CPUS, SWT.CENTER, 90); + // setColumnProperties(table, SERVER_DISK_TABLE_COLUMN_INDICES.CPU_USAGE, SWT.CENTER, 90); + // setColumnProperties(table, SERVER_DISK_TABLE_COLUMN_INDICES.TOTAL_MEMORY, SWT.CENTER, 90); + // setColumnProperties(table, SERVER_DISK_TABLE_COLUMN_INDICES.MEMORY_IN_USE, SWT.CENTER, 90); + // setColumnProperties(table, SERVER_DISK_TABLE_COLUMN_INDICES.DISK_SPACE_IN_USE, SWT.CENTER, 90); + } + + @Override + protected String[] getColumnNames() { + return SERVER_TABLE_COLUMN_NAMES; + } + + @Override + protected IBaseLabelProvider getLabelProvider() { + return new ServerTableLabelProvider(); + } + + @Override + protected IContentProvider getContentProvider() { + return new EntityGroupContentProvider<Server>(); + } + + @Override + protected List<Server> getAllEntities() { + return servers; + } + + /** + * Sets properties for alignment and weight of given column of given table + * + * @param table + * @param columnIndex + * @param alignment + * @param weight + */ + private void setColumnProperties(Table table, SERVER_TABLE_COLUMN_INDICES columnIndex, int alignment, int weight) { + TableColumn column = table.getColumn(columnIndex.ordinal()); + column.setAlignment(alignment); + + TableColumnLayout tableColumnLayout = (TableColumnLayout) table.getParent().getLayout(); + tableColumnLayout.setColumnData(column, new ColumnWeightData(weight)); + } + + @Override + protected ViewerComparator createViewerComparator() { + // TODO Auto-generated method stub + return null; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/TasksPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/TasksPage.java new file mode 100644 index 00000000..aeea018c --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/TasksPage.java @@ -0,0 +1,151 @@ +/** + * TasksPage.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 org.gluster.storage.management.console.views.pages; + +import java.util.List; + +import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchSite; +import org.gluster.storage.management.console.TasksTableLabelProvider; +import org.gluster.storage.management.console.toolbar.GlusterToolbarManager; +import org.gluster.storage.management.core.model.ClusterListener; +import org.gluster.storage.management.core.model.DefaultClusterListener; +import org.gluster.storage.management.core.model.Entity; +import org.gluster.storage.management.core.model.TaskInfo; + + +public class TasksPage extends AbstractTableViewerPage<TaskInfo> { + private List<TaskInfo> taskInfoList; + private TaskInfo selectedTask; + + public enum TASK_TABLE_COLUMN_INDICES { + TASK, STATUS + }; + + private static final String[] TASK_TABLE_COLUMN_NAMES = new String[] { "Task", "Status"}; + + + public TasksPage(IWorkbenchSite site, Composite parent, int style, List<TaskInfo> taskInfo) { + super(site, parent, style, false, false, taskInfo); + this.taskInfoList = taskInfo; + } + + /* (non-Javadoc) + * @see org.gluster.storage.management.console.views.pages.AbstractTableViewerPage#createClusterListener() + */ + @Override + protected ClusterListener createClusterListener() { + return new DefaultClusterListener() { + @Override + public void taskAdded(TaskInfo taskInfo) { + tableViewer.add(taskInfo); + parent.update(); + } + + @Override + public void taskRemoved(TaskInfo taskInfo) { + tableViewer.remove(taskInfo); + parent.update(); + // hide the task related actionset as no task is selected + // site.getPage().hideActionSet(ActionConstants.ACTION_SET_TASK); + } + + @Override + public void taskUpdated(TaskInfo taskInfo) { + tableViewer.update(taskInfo, null); + parent.update(); + // fire selection event so that toolbar gets updated + // (the action class listens to selection and enables/disables automatically) + tableViewer.setSelection(new StructuredSelection(taskInfo)); + } + }; + } + + /* (non-Javadoc) + * @see org.gluster.storage.management.console.views.pages.AbstractTableViewerPage#getColumnNames() + */ + @Override + protected String[] getColumnNames() { + return TASK_TABLE_COLUMN_NAMES; + } + + /* (non-Javadoc) + * @see org.gluster.storage.management.console.views.pages.AbstractTableViewerPage#setColumnProperties(org.eclipse.swt.widgets.Table) + */ + @Override + protected void setColumnProperties(Table table) { + guiHelper.setColumnProperties(table, TASK_TABLE_COLUMN_INDICES.TASK.ordinal(), SWT.LEFT, 50); + guiHelper.setColumnProperties(table, TASK_TABLE_COLUMN_INDICES.STATUS.ordinal(), SWT.LEFT, 50); + } + + /* (non-Javadoc) + * @see org.gluster.storage.management.console.views.pages.AbstractTableViewerPage#getLabelProvider() + */ + @Override + protected IBaseLabelProvider getLabelProvider() { + return new TasksTableLabelProvider(); + } + + /* (non-Javadoc) + * @see org.gluster.storage.management.console.views.pages.AbstractTableViewerPage#getContentProvider() + */ + @Override + protected IContentProvider getContentProvider() { + return new ArrayContentProvider(); + } + + /* (non-Javadoc) + * @see org.gluster.storage.management.console.views.pages.AbstractTableViewerPage#getAllEntities() + */ + @Override + protected List<TaskInfo> getAllEntities() { + return taskInfoList; + } + + /* (non-Javadoc) + * @see org.gluster.storage.management.console.views.pages.AbstractTableViewerPage#selectionChanged(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + @Override + public void selectionChanged(IWorkbenchPart part, ISelection selection) { + if (selection instanceof StructuredSelection) { + Entity selectedEntity = (Entity) ((StructuredSelection) selection).getFirstElement(); + if (selectedEntity != null && selectedEntity instanceof TaskInfo && selectedEntity != selectedTask) { + selectedTask = (TaskInfo)selectedEntity; + new GlusterToolbarManager(part.getSite().getWorkbenchWindow()).updateToolbar(selectedTask); + } + } + } + + @Override + protected ViewerComparator createViewerComparator() { + // TODO Auto-generated method stub + return null; + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/VolumeLogsPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/VolumeLogsPage.java new file mode 100644 index 00000000..d32589bb --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/VolumeLogsPage.java @@ -0,0 +1,431 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views.pages; + +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; +import org.eclipse.jface.viewers.IDoubleClickListener; +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.events.VerifyEvent; +import org.eclipse.swt.events.VerifyListener; +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.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.DateTime; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.gluster.storage.management.client.VolumesClient; +import org.gluster.storage.management.console.VolumeLogTableLabelProvider; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.constants.GlusterConstants; +import org.gluster.storage.management.core.constants.GlusterConstants.VOLUME_LOG_LEVELS; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.VolumeLogMessage; + + +public class VolumeLogsPage extends Composite { + + private final FormToolkit toolkit = new FormToolkit(Display.getCurrent()); + private final GUIHelper guiHelper = GUIHelper.getInstance(); + private Text filterText; + private Text lineCountText; + private Volume volume; + + public enum LOG_TABLE_COLUMN_INDICES { + DATE, TIME, BRICK, SEVERITY, MESSAGE + }; + + private static final String[] LOG_TABLE_COLUMN_NAMES = new String[] { "Date", "Time", "Brick", "Severity", "Message" }; + private TableViewer tableViewer; + private Combo bricksCombo; + private Combo severityCombo; + private DateTime fromDate; + private DateTime fromTime; + private DateTime toDate; + private DateTime toTime; + private Button fromCheckbox; + private Button toCheckbox; + + /** + * 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(); + } + }); + toolkit.adapt(this); + toolkit.paintBordersFor(this); + + configureLayout(); + + Composite composite = toolkit.createComposite(this, SWT.NONE); + toolkit.paintBordersFor(composite); + + createLineCountLabel(composite); + createLineCountText(composite); + + createBricksLabel(composite); + createBricksCombo(composite); + + createSeverityLabel(composite); + createSeverityCombo(composite); + + createFromDateLabel(composite); + createFromDateField(composite); + createFromTimeField(composite); + createFromCheckbox(composite); + + createToDateLabel(composite); + createToDateField(composite); + createToTimeField(composite); + createToCheckbox(composite); + + createSearchButton(composite); + + createSeparator(composite); + + createFilterLabel(composite); + createFilterText(composite); + + createLogTableViewer(); + } + + private void createLogTableViewer() { + Composite tableViewerComposite = createTableViewerComposite(); + + 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, "&Fetch Logs", SWT.NONE); + btnGo.setBounds (615, 55, 75, 30); + btnGo.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + VolumesClient client = new VolumesClient(); + + 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; + } + + try { + List<VolumeLogMessage> logMessages = client.getLogs(volume.getName(), bricksCombo.getText(), + severityCombo.getText(), fromTimestamp, toTimestamp, + Integer.parseInt(lineCountText.getText())); + tableViewer.setInput(logMessages.toArray(new VolumeLogMessage[0])); + tableViewer.refresh(); + } catch (Exception ex) { + MessageDialog.openError(getShell(), "Volume Logs", + "Error while fetching volume logs: [" + ex.getMessage() + "]"); + } + } + }); + } + + protected boolean validateTimeRange(Date fromTimestamp, Date toTimestamp) { + if (fromTimestamp == null && toTimestamp == null) { + // no time range selected. nothing to validate. + return true; + } + + 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; + } + + 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; + } + } + + 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); + + severityCombo.setItems(GlusterConstants.VOLUME_LOG_LEVELS_ARR.toArray(new String[0])); + severityCombo.select(VOLUME_LOG_LEVELS.ERROR.ordinal()); + severityCombo.add(CoreConstants.ALL, 0); + + toolkit.adapt(severityCombo); + toolkit.paintBordersFor(severityCombo); + } + + private void createSeverityLabel(Composite composite) { + Label lblSeverity = toolkit.createLabel(composite, "Severity", SWT.NONE); + lblSeverity.setBounds(480, 15, 70, 20); + } + + private void createBricksCombo(Composite composite) { + bricksCombo = new Combo(composite, SWT.READ_ONLY); + bricksCombo.setBounds(365, 15, 100, 20); + bricksCombo.setItems( volume.getBrickDirectories().toArray(new String[0])); + bricksCombo.add(CoreConstants.ALL, 0); + toolkit.adapt(bricksCombo); + toolkit.paintBordersFor(bricksCombo); + bricksCombo.select(0); + } + + private void createBricksLabel(Composite composite) { + Label lblMessagesAndFilter = toolkit.createLabel(composite, "messages, and filter on bricks", 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); + lineCountText.setTextLimit(4); + lineCountText.addVerifyListener(new VerifyListener() { + + @Override + public void verifyText(VerifyEvent event) { + // Assume we allow it + event.doit = true; + + String text = event.text; + char[] chars = text.toCharArray(); + + // Don't allow if text contains non-digit characters + for (int i = 0; i < chars.length; i++) { + if (!Character.isDigit(chars[i])) { + event.doit = false; + break; + } + } + } + }); + } + + 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); + tableViewerComposite.setLayout(new FillLayout(SWT.HORIZONTAL)); + GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true); + layoutData.verticalIndent = 10; + tableViewerComposite.setLayoutData(layoutData); + return tableViewerComposite; + } + + private void setupLogsTable(Composite parent, Table table) { + table.setHeaderVisible(true); + table.setLinesVisible(false); + + TableColumnLayout tableColumnLayout = guiHelper.createTableColumnLayout(table, LOG_TABLE_COLUMN_NAMES); + parent.setLayout(tableColumnLayout); + + setColumnProperties(table, LOG_TABLE_COLUMN_INDICES.DATE, SWT.CENTER, 50); + setColumnProperties(table, LOG_TABLE_COLUMN_INDICES.TIME, SWT.CENTER, 50); + setColumnProperties(table, LOG_TABLE_COLUMN_INDICES.BRICK, SWT.CENTER, 50); + setColumnProperties(table, LOG_TABLE_COLUMN_INDICES.SEVERITY, SWT.CENTER, 50); + setColumnProperties(table, LOG_TABLE_COLUMN_INDICES.MESSAGE, SWT.LEFT, 100); + } + + /** + * Sets properties for alignment and weight of given column of given table + * + * @param table + * @param columnIndex + * @param alignment + * @param weight + */ + private void setColumnProperties(Table table, LOG_TABLE_COLUMN_INDICES columnIndex, int alignment, int weight) { + TableColumn column = table.getColumn(columnIndex.ordinal()); + column.setAlignment(alignment); + + 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(); + } + + public void addDoubleClickListener(IDoubleClickListener listener) { + tableViewer.addDoubleClickListener(listener); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/VolumeOptionsPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/VolumeOptionsPage.java new file mode 100644 index 00000000..4363b7e9 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/VolumeOptionsPage.java @@ -0,0 +1,360 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views.pages; + +import java.util.List; + +import org.apache.commons.lang.WordUtils; +import org.eclipse.jface.layout.TableColumnLayout; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ColumnLayoutData; +import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +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; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +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; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.gluster.storage.management.console.GlusterDataModelManager; +import org.gluster.storage.management.console.VolumeOptionsContentProvider; +import org.gluster.storage.management.console.VolumeOptionsTableLabelProvider; +import org.gluster.storage.management.console.utils.GUIHelper; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.DefaultClusterListener; +import org.gluster.storage.management.core.model.Event; +import org.gluster.storage.management.core.model.Volume; +import org.gluster.storage.management.core.model.VolumeOption; +import org.gluster.storage.management.core.model.VolumeOptionInfo; + + +public class VolumeOptionsPage extends Composite { + + private final FormToolkit toolkit = new FormToolkit(Display.getCurrent()); + private TableViewer tableViewer; + private GUIHelper guiHelper = GUIHelper.getInstance(); + private Volume volume; + private DefaultClusterListener clusterListener; + private Text filterText; + private List<VolumeOptionInfo> defaultVolumeOptions = GlusterDataModelManager.getInstance() + .getVolumeOptionsInfo(); + + 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 addTopButton; + private Button addBottomButton; + private TableViewerColumn keyColumn; + private OptionKeyEditingSupport keyEditingSupport; + + public VolumeOptionsPage(final Composite parent, int style, Volume volume) { + super(parent, style); + + this.volume = volume; + + toolkit.adapt(this); + toolkit.paintBordersFor(this); + + setupPageLayout(); + addTopButton = createAddButton(); + filterText = guiHelper.createFilterText(toolkit, this); + + setupOptionsTableViewer(filterText); + + addBottomButton = createAddButton(); + + if (defaultVolumeOptions.size() == volume.getOptions().size()) { + setAddButtonsEnabled(false); + } + + tableViewer.setInput(volume.getOptions()); + + parent.layout(); // Important - this actually paints the table + registerListeners(parent); + } + + private void setAddButtonsEnabled(boolean enable) { + addTopButton.setEnabled(enable); + addBottomButton.setEnabled(enable); + } + + private Button createAddButton() { + return toolkit.createButton(this, "&Add", SWT.FLAT); + } + + private void registerListeners(final Composite parent) { + /** + * 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 + */ + addPaintListener(new PaintListener() { + + @Override + public void paintControl(PaintEvent e) { + parent.layout(); + } + }); + + clusterListener = new DefaultClusterListener() { + @Override + public void volumeChanged(Volume volume, Event event) { + super.volumeChanged(volume, event); + + switch (event.getEventType()) { + case VOLUME_OPTIONS_RESET: + if (!tableViewer.getControl().isDisposed()) { + //While reseting the options, clear the filter text before refreshing the tree + filterText.setText(""); + tableViewer.refresh(); + setAddButtonsEnabled(true); + } + break; + + case VOLUME_OPTION_SET: + String key = (String)event.getEventData(); + if (isNewOption(volume, key)) { + // option has been set successfully by the user. re-enable the add button and search filter + // textbox + setAddButtonsEnabled(true); + filterText.setEnabled(true); + } + + if (defaultVolumeOptions.size() == volume.getOptions().size()) { + setAddButtonsEnabled(false); + } + + tableViewer.refresh(); + break; + case VOLUME_CHANGED: + tableViewer.refresh(); + if(volume.getOptions().size() == defaultVolumeOptions.size()) { + setAddButtonsEnabled(false); + } else { + setAddButtonsEnabled(true); + } + default: + break; + } + } + + 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().getOptions().get(volume.getOptions().size() - 1).getKey()); + } + }; + + SelectionListener addButtonSelectionListener = 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 + setAddButtonsEnabled(false); + filterText.setEnabled(false); + } + + private VolumeOption getEntry(String key) { + for (VolumeOption entry : volume.getOptions().getOptions()) { + if (entry.getKey().equals(key)) { + return entry; + } + } + return null; + } + }; + addTopButton.addSelectionListener(addButtonSelectionListener); + addBottomButton.addSelectionListener(addButtonSelectionListener); + + // 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) { + setAddButtonsEnabled(false); + } else { + if (defaultVolumeOptions.size() == volume.getOptions().size()) { + setAddButtonsEnabled(false); + } else { + setAddButtonsEnabled(true); + } + } + } + }); + + GlusterDataModelManager.getInstance().addClusterListener(clusterListener); + + addDisposeListener(new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent e) { + toolkit.dispose(); + + if (!(addTopButton.isEnabled() || addBottomButton.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); + } + }); + } + + private void setupPageLayout() { + final GridLayout layout = new GridLayout(2, false); + layout.verticalSpacing = 10; + layout.marginTop = 10; + setLayout(layout); + } + + private void setupOptionsTable(Composite parent) { + Table table = tableViewer.getTable(); + table.setHeaderVisible(true); + table.setLinesVisible(true); + + TableColumnLayout tableColumnLayout = createTableColumnLayout(); + parent.setLayout(tableColumnLayout); + + setColumnProperties(table, OPTIONS_TABLE_COLUMN_INDICES.OPTION_KEY, SWT.CENTER, 100); + setColumnProperties(table, OPTIONS_TABLE_COLUMN_INDICES.OPTION_VALUE, SWT.CENTER, 100); + } + + private TableColumnLayout createTableColumnLayout() { + TableColumnLayout tableColumnLayout = new TableColumnLayout(); + ColumnLayoutData defaultColumnLayoutData = new ColumnWeightData(100); + + tableColumnLayout.setColumnData(createKeyColumn(), defaultColumnLayoutData); + tableColumnLayout.setColumnData(createValueColumn(), defaultColumnLayoutData); + + return tableColumnLayout; + } + + private TableColumn createValueColumn() { + TableViewerColumn valueColumn = new TableViewerColumn(tableViewer, SWT.NONE); + valueColumn.getColumn() + .setText(OPTIONS_TABLE_COLUMN_NAMES[OPTIONS_TABLE_COLUMN_INDICES.OPTION_VALUE.ordinal()]); + valueColumn.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + return ((VolumeOption) element).getValue(); + } + }); + + // User can edit value of a volume option + valueColumn.setEditingSupport(new OptionValueEditingSupport(valueColumn.getViewer(), volume)); + + return valueColumn.getColumn(); + } + + private TableColumn createKeyColumn() { + keyColumn = new TableViewerColumn(tableViewer, SWT.NONE); + keyColumn.getColumn().setText(OPTIONS_TABLE_COLUMN_NAMES[OPTIONS_TABLE_COLUMN_INDICES.OPTION_KEY.ordinal()]); + keyColumn.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + return ((VolumeOption) element).getKey(); + } + + @Override + public String getToolTipText(Object element) { + String key = ((VolumeOption) 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 createOptionsTableViewer(Composite parent) { + tableViewer = new TableViewer(parent, SWT.FLAT | SWT.FULL_SELECTION | SWT.SINGLE); + tableViewer.setLabelProvider(new VolumeOptionsTableLabelProvider()); + tableViewer.setContentProvider(new VolumeOptionsContentProvider()); + tableViewer.getTable().setLinesVisible(true); + + setupOptionsTable(parent); + } + + private Composite createTableViewerComposite() { + Composite tableViewerComposite = new Composite(this, SWT.NO); + tableViewerComposite.setLayout(new FillLayout(SWT.HORIZONTAL)); + + GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true); + layoutData.horizontalSpan = 2; + tableViewerComposite.setLayoutData(layoutData); + + return tableViewerComposite; + } + + private void setupOptionsTableViewer(final Text filterText) { + Composite tableViewerComposite = createTableViewerComposite(); + createOptionsTableViewer(tableViewerComposite); + ColumnViewerToolTipSupport.enableFor(tableViewer); + // Create a case insensitive filter for the table viewer using the filter text field + guiHelper.createFilter(tableViewer, filterText, false); + } + + private void setColumnProperties(Table table, OPTIONS_TABLE_COLUMN_INDICES columnIndex, int alignment, int weight) { + TableColumn column = table.getColumn(columnIndex.ordinal()); + column.setAlignment(alignment); + + TableColumnLayout tableColumnLayout = (TableColumnLayout) table.getParent().getLayout(); + tableColumnLayout.setColumnData(column, new ColumnWeightData(weight)); + } +} diff --git a/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/VolumesPage.java b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/VolumesPage.java new file mode 100644 index 00000000..60d00a7f --- /dev/null +++ b/src/org.gluster.storage.management.console/src/org/gluster/storage/management/console/views/pages/VolumesPage.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * 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 org.gluster.storage.management.console.views.pages; + +import java.util.List; + +import org.eclipse.jface.layout.TableColumnLayout; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.ui.IWorkbenchSite; +import org.gluster.storage.management.console.EntityGroupContentProvider; +import org.gluster.storage.management.console.VolumeTableLabelProvider; +import org.gluster.storage.management.console.toolbar.GlusterToolbarManager; +import org.gluster.storage.management.core.constants.CoreConstants; +import org.gluster.storage.management.core.model.ClusterListener; +import org.gluster.storage.management.core.model.DefaultClusterListener; +import org.gluster.storage.management.core.model.EntityGroup; +import org.gluster.storage.management.core.model.Event; +import org.gluster.storage.management.core.model.Volume; + + +public class VolumesPage extends AbstractTableViewerPage<Volume> { + private List<Volume> volumes; + + final GlusterToolbarManager toolbarManager = new GlusterToolbarManager( site.getWorkbenchWindow()); + + public enum VOLUME_TABLE_COLUMN_INDICES { + NAME, VOLUME_TYPE, NUM_OF_BRICKS, TRANSPORT_TYPE, VOLUME_STATUS + }; + + private static final String[] VOLUME_TABLE_COLUMN_NAMES = new String[] { "Name", "Volume Type", + "Number of" + CoreConstants.NEWLINE + "Bricks", "Transport Type", "Status" }; + + public VolumesPage(final Composite parent, IWorkbenchSite site, EntityGroup<Volume> volumes) { + super(site, parent, SWT.NONE, true, true, volumes); + this.volumes = volumes.getEntities(); + } + + @Override + protected String[] getColumnNames() { + return VOLUME_TABLE_COLUMN_NAMES; + } + + @Override + protected void setColumnProperties(Table table) { + setColumnProperties(table, VOLUME_TABLE_COLUMN_INDICES.VOLUME_STATUS, SWT.CENTER, 50); + setColumnProperties(table, VOLUME_TABLE_COLUMN_INDICES.NUM_OF_BRICKS, SWT.CENTER, 50); + setColumnProperties(table, VOLUME_TABLE_COLUMN_INDICES.TRANSPORT_TYPE, SWT.CENTER, 70); + } + + @Override + protected IBaseLabelProvider getLabelProvider() { + return new VolumeTableLabelProvider(); + } + + @Override + protected IContentProvider getContentProvider() { + return new EntityGroupContentProvider<Volume>(); + } + + @Override + protected ClusterListener createClusterListener() { + return new DefaultClusterListener() { + @Override + public void volumeCreated(Volume volume) { + tableViewer.add(volume); + parent.update(); + toolbarManager.updateToolbar(volume); + } + + @Override + public void volumeDeleted(Volume volume) { + tableViewer.remove(volume); + parent.update(); + toolbarManager.updateToolbar(volume); + } + + @Override + public void volumeChanged(Volume volume, Event event) { + tableViewer.update(volume, null); + parent.update(); + toolbarManager.updateToolbar(volume); + } + }; + } + + /** + * 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, VOLUME_TABLE_COLUMN_INDICES columnIndex, int alignment, int weight) { + TableColumn column = table.getColumn(columnIndex.ordinal()); + column.setAlignment(alignment); + + TableColumnLayout tableColumnLayout = (TableColumnLayout) table.getParent().getLayout(); + tableColumnLayout.setColumnData(column, new ColumnWeightData(weight)); + } + + @Override + protected List<Volume> getAllEntities() { + return volumes; + } + + @Override + protected ViewerComparator createViewerComparator() { + // TODO Auto-generated method stub + return null; + } +} diff --git a/src/org.gluster.storage.management.console/src/test.xml b/src/org.gluster.storage.management.console/src/test.xml new file mode 100644 index 00000000..12bbf745 --- /dev/null +++ b/src/org.gluster.storage.management.console/src/test.xml @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?> |