diff options
| author | Shireesh Anjal <shireesh@gluster.com> | 2011-05-03 21:45:10 +0530 |
|---|---|---|
| committer | Shireesh Anjal <shireesh@gluster.com> | 2011-05-04 15:08:00 +0530 |
| commit | 40deed80b5e5f7cd7121ae5a6b2b5bce74350226 (patch) | |
| tree | 0f13b00349dceb46e9670b435107c1b22e659278 /src | |
| parent | 5589f8924c3167b046cae892d1bbcaf776663edf (diff) | |
Story #42 - Volume logs download
Diffstat (limited to 'src')
21 files changed, 1055 insertions, 74 deletions
diff --git a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java index a4df0e58..f8005044 100644 --- a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java +++ b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java @@ -8,6 +8,7 @@ import javax.ws.rs.core.MultivaluedMap; import com.gluster.storage.management.client.utils.ClientUtil;
import com.gluster.storage.management.core.constants.RESTConstants;
import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.representation.Form;
@@ -47,6 +48,16 @@ public abstract class AbstractClient { return res.queryParams(queryParams).header(HTTP_HEADER_AUTH, authHeader).accept(MediaType.TEXT_XML)
.get(responseClass);
}
+
+ private Object downloadResource(WebResource res, MultivaluedMap<String, String> queryParams, Class responseClass) {
+ return res.queryParams(queryParams).header(HTTP_HEADER_AUTH, authHeader).accept(MediaType.TEXT_XML)
+ .get(responseClass);
+ }
+
+ protected Object downloadResource(WebResource res) {
+ ClientResponse response = res.header(HTTP_HEADER_AUTH, authHeader).accept(MediaType.APPLICATION_OCTET_STREAM).get(ClientResponse.class);
+ return response;
+ }
/**
* Fetches the default resource (the one returned by {@link AbstractClient#getResourceName()}) by dispatching a GET
@@ -92,6 +103,10 @@ public abstract class AbstractClient { return fetchResource(resource.path(subResourceName), NO_PARAMS, responseClass);
}
+ protected Object downloadSubResource(String subResourceName) {
+ return downloadResource(resource.path(subResourceName));
+ }
+
/**
* Fetches the resource whose name is arrived at by appending the "subResourceName" parameter to the default
* resource (the one returned by {@link AbstractClient#getResourceName()})
@@ -109,7 +124,7 @@ public abstract class AbstractClient { Class responseClass) {
return fetchResource(resource.path(subResourceName), queryParams, responseClass);
}
-
+
/**
* Submits given Form using POST method to the resource and returns the object received as response
*
diff --git a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/VolumesClient.java b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/VolumesClient.java index 33a93690..4af70c5a 100644 --- a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/VolumesClient.java +++ b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/VolumesClient.java @@ -138,6 +138,10 @@ public class VolumesClient extends AbstractClient { return (LogMessageListResponse) fetchSubResource(volumeName + "/" + RESTConstants.SUBRESOURCE_LOGS, queryParams, LogMessageListResponse.class); } + + public void downloadLogs(String volumeName) { + downloadSubResource((volumeName) + "/" + RESTConstants.SUBRESOURCE_LOGS + "/" + RESTConstants.SUBRESOURCE_DOWNLOAD); + } private MultivaluedMap<String, String> prepareGetLogQueryParams(String diskName, String severity, Date fromTimestamp, Date toTimestamp, int messageCount) { @@ -222,15 +226,16 @@ public class VolumesClient extends AbstractClient { // } // System.out.println(client.getVolume("Volume3").getOptions()); // System.out.println(client.setVolumeOption("Volume3", "network.frame-timeout", "600").getMessage()); - List<Disk> disks = new ArrayList<Disk>(); - Disk disk = new Disk(); - disk.setServerName("server1"); - disk.setName("sda"); - disk.setStatus(DISK_STATUS.READY); - disks.add(disk); - - Status status = client.addDisks("Volume3", disks); - System.out.println(status.getMessage()); +// List<Disk> disks = new ArrayList<Disk>(); +// Disk disk = new Disk(); +// disk.setServerName("server1"); +// disk.setName("sda"); +// disk.setStatus(DISK_STATUS.READY); +// disks.add(disk); +// +// Status status = client.addDisks("Volume3", disks); +// System.out.println(status.getMessage()); + client.downloadLogs("vol1"); } } } diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/RESTConstants.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/RESTConstants.java index 5da9e6b1..6c7b69da 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/RESTConstants.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/constants/RESTConstants.java @@ -29,6 +29,7 @@ public class RESTConstants { public static final String SUBRESOURCE_DEFAULT_OPTIONS = "defaultoptions"; public static final String SUBRESOURCE_OPTIONS = "options"; public static final String SUBRESOURCE_LOGS = "logs"; + public static final String SUBRESOURCE_DOWNLOAD = "download"; public static final String SUBRESOURCE_DISKS = "disks"; @@ -53,6 +54,7 @@ public class RESTConstants { public static final String QUERY_PARAM_LOG_SEVERITY = "severity"; public static final String QUERY_PARAM_FROM_TIMESTAMP = "fromTimestamp"; public static final String QUERY_PARAM_TO_TIMESTAMP = "toTimestamp"; + public static final String QUERY_PARAM_DOWNLOAD = "download"; // Running tasks resource public static final String RESOURCE_PATH_RUNNING_TASKS = "/cluster/runningtasks"; diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Cluster.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Cluster.java index 8fa247a1..1a62b293 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Cluster.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Cluster.java @@ -123,4 +123,20 @@ public class Cluster extends Entity { public void addAlert(Alert alert) { this.alerts.add(alert); } + + public double getTotalDiskSpace() { + double totalDiskSpace = 0; + for(GlusterServer server : getServers()) { + totalDiskSpace += server.getTotalDiskSpace(); + } + return totalDiskSpace; + } + + public double getDiskSpaceInUse() { + double diskSpaceInUse = 0; + for(GlusterServer server : getServers()) { + diskSpaceInUse += server.getDiskSpaceInUse(); + } + return diskSpaceInUse; + } }
\ No newline at end of file diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Server.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Server.java index bc0c42bc..4f870eaf 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Server.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/model/Server.java @@ -102,6 +102,15 @@ public class Server extends Entity { public double getDiskSpaceInUse() { return diskSpaceInUse; } + + /** + * Total disk space in use is automatically calculated, and hence this method should never be called. It is required + * only to make sure that the element "diskSpaceInUse" gets added to the XML tag when jersey converts the server + * object to XML for sending to client. + */ + public void setDiskSpaceInUse(double diskSpaceInUse) { + this.diskSpaceInUse = diskSpaceInUse; + } @XmlElementWrapper(name = "networkInterfaces") @XmlElement(name = "networkInterface", type = NetworkInterface.class) diff --git a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/FileUtil.java b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/FileUtil.java index c650d632..c6394a3e 100644 --- a/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/FileUtil.java +++ b/src/com.gluster.storage.management.core/src/com/gluster/storage/management/core/utils/FileUtil.java @@ -20,7 +20,10 @@ package com.gluster.storage.management.core.utils; import java.io.File; import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; import java.io.InputStream; +import java.util.UUID; import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; @@ -42,4 +45,80 @@ public class FileUtil { public InputStream loadResource(String resourcePath) { return this.getClass().getClassLoader().getResourceAsStream(resourcePath); } + + public void createTextFile(String fileName, String contents) { + try { + FileWriter writer = new FileWriter(fileName); + writer.write(contents); + } catch (Exception e) { + throw new GlusterRuntimeException("Exception while trying to create text file [" + fileName + "]", e); + } + } + + public String getTempDirName() { + return System.getProperty("java.io.tmpdir"); + } + + /** + * Create a new temporary directory. Use something like + * {@link #recursiveDelete(File)} to clean this directory up since it isn't + * deleted automatically + * @return the new directory + * @throws IOException if there is an error creating the temporary directory + */ + public File createTempDir() + { + final File sysTempDir = new File(getTempDirName()); + File newTempDir; + final int maxAttempts = 9; + int attemptCount = 0; + do + { + attemptCount++; + if(attemptCount > maxAttempts) + { + throw new GlusterRuntimeException( + "The highly improbable has occurred! Failed to " + + "create a unique temporary directory after " + + maxAttempts + " attempts."); + } + String dirName = UUID.randomUUID().toString(); + newTempDir = new File(sysTempDir, dirName); + } while(newTempDir.exists()); + + if(newTempDir.mkdirs()) + { + return newTempDir; + } + else + { + throw new GlusterRuntimeException( + "Failed to create temp dir named " + + newTempDir.getAbsolutePath()); + } + } + + /** + * Recursively delete file or directory + * + * @param fileOrDir + * the file or dir to delete + * @return true if all files are successfully deleted + */ + public boolean recursiveDelete(File fileOrDir) + { + if(fileOrDir.isDirectory()) + { + // recursively delete contents + for(File innerFile: fileOrDir.listFiles()) + { + if(!recursiveDelete(innerFile)) + { + return false; + } + } + } + + return fileOrDir.delete(); + } }
\ No newline at end of file diff --git a/src/com.gluster.storage.management.gui/icons/memory.png b/src/com.gluster.storage.management.gui/icons/memory.png Binary files differnew file mode 100644 index 00000000..4c71a247 --- /dev/null +++ b/src/com.gluster.storage.management.gui/icons/memory.png diff --git a/src/com.gluster.storage.management.gui/icons/server-warning.png b/src/com.gluster.storage.management.gui/icons/server-warning.png Binary files differnew file mode 100644 index 00000000..c2888d19 --- /dev/null +++ b/src/com.gluster.storage.management.gui/icons/server-warning.png diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/ApplicationWorkbenchWindowAdvisor.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/ApplicationWorkbenchWindowAdvisor.java index 05d7443f..3a9a4531 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/ApplicationWorkbenchWindowAdvisor.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/ApplicationWorkbenchWindowAdvisor.java @@ -56,6 +56,7 @@ public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor { 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/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/IImageKeys.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/IImageKeys.java index fdcf7aac..d11decb6 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/IImageKeys.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/IImageKeys.java @@ -24,6 +24,8 @@ public interface IImageKeys { public static final String SERVERS = "icons/servers.png"; public static final String VOLUME = "icons/volume.png"; public static final String SERVER = "icons/server.png"; + public static final String SERVER_WARNING = "icons/server-warning.png"; + public static final String MEMORY = "icons/memory.png"; public static final String GSN = "icons/gsn.png"; public static final String SETTINGS = "icons/settings.png"; public static final String ADD = "icons/plus-white.png"; diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/utils/GlusterChartPalette.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/utils/GlusterChartPalette.java new file mode 100644 index 00000000..e68b8870 --- /dev/null +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/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 com.gluster.storage.management.gui.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/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/ClusterSummaryView.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/ClusterSummaryView.java index 079d6bcf..4ec1f586 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/ClusterSummaryView.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/ClusterSummaryView.java @@ -20,10 +20,15 @@ */ package com.gluster.storage.management.gui.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.swt.widgets.Label; import org.eclipse.ui.forms.events.HyperlinkAdapter; import org.eclipse.ui.forms.events.HyperlinkEvent; import org.eclipse.ui.forms.widgets.FormToolkit; @@ -33,14 +38,19 @@ import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.part.ViewPart; import com.gluster.storage.management.client.GlusterDataModelManager; +import com.gluster.storage.management.core.model.Alert; import com.gluster.storage.management.core.model.Cluster; +import com.gluster.storage.management.core.model.Disk; +import com.gluster.storage.management.core.model.Disk.DISK_STATUS; import com.gluster.storage.management.core.model.EntityGroup; import com.gluster.storage.management.core.model.GlusterDataModel; import com.gluster.storage.management.core.model.GlusterServer; import com.gluster.storage.management.core.model.GlusterServer.SERVER_STATUS; +import com.gluster.storage.management.core.model.RunningTask; import com.gluster.storage.management.core.model.Server; import com.gluster.storage.management.core.model.Volume; import com.gluster.storage.management.core.model.Volume.VOLUME_STATUS; +import com.gluster.storage.management.core.utils.NumberUtil; import com.gluster.storage.management.gui.IImageKeys; import com.gluster.storage.management.gui.actions.IActionConstants; import com.gluster.storage.management.gui.utils.GUIHelper; @@ -97,29 +107,100 @@ public class ClusterSummaryView extends ViewPart { Double[] values = new Double[] { Double.valueOf(getVolumeCountByStatus(cluster, VOLUME_STATUS.ONLINE)), Double.valueOf(getVolumeCountByStatus(cluster, VOLUME_STATUS.OFFLINE)) }; - createStatusChart(toolkit, section, values); + createDiskSpaceChart(toolkit, section, values); } private void createServersSection() { - Composite section = guiHelper.createSection(form, toolkit, "Servers", null, 1, false); + Composite section = guiHelper.createSection(form, toolkit, "Servers", null, 2, false); - Double[] values = new Double[] { Double.valueOf(getServerCountByStatus(cluster, SERVER_STATUS.ONLINE)), - Double.valueOf(getServerCountByStatus(cluster, SERVER_STATUS.OFFLINE)) }; + int onlineServerCount = getServerCountByStatus(cluster, SERVER_STATUS.ONLINE); + int offlineServerCount = getServerCountByStatus(cluster, SERVER_STATUS.OFFLINE); + + toolkit.createLabel(section, "Online : "); + Label label = toolkit.createLabel(section, "" + onlineServerCount); + label.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GREEN)); + + toolkit.createLabel(section, "Offline : "); + label = toolkit.createLabel(section, "" + offlineServerCount); + label.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_RED)); + } + + private void createDiskSpaceSection() { + Composite section = guiHelper.createSection(form, toolkit, "Disk Space", null, 3, false); + + double totalDiskSpace = cluster.getTotalDiskSpace(); + double diskSpaceInUse = cluster.getDiskSpaceInUse(); + Double[] values = new Double[] { diskSpaceInUse, totalDiskSpace - diskSpaceInUse }; + createDiskSpaceChart(toolkit, section, values); + } - createStatusChart(toolkit, section, values); + private int getDiskCountByStatus(Cluster cluster, DISK_STATUS status) { + int diskCount = 0; + for(GlusterServer server : cluster.getServers()) { + for(Disk disk : server.getDisks()) { + if(disk.getStatus() == status) { + diskCount++; + } + } + } + return diskCount; } - private void createStatusChart(FormToolkit toolkit, Composite section, Double[] values) { - String[] categories = new String[] { "Online", "Offline" }; + private int getDiskCount(Cluster cluster) { + int diskCount = 0; + for(GlusterServer server : cluster.getServers()) { + diskCount += server.getDisks().size(); + } + return diskCount; + } + + private void createDiskSpaceChart(FormToolkit toolkit, Composite section, Double[] values) { + String[] categories = new String[] { "Used Space: " + NumberUtil.formatNumber(values[0]) + " GB", + "Free Space: " + NumberUtil.formatNumber(values[1]) + " GB"}; PieChartViewerComposite chartViewerComposite = new PieChartViewerComposite(section, SWT.NONE, categories, values); - GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); - data.widthHint = 250; - data.heightHint = 250; + GridData data = new GridData(SWT.FILL, SWT.FILL, false, false); + data.widthHint = 400; + data.heightHint = 150; + data.verticalAlignment = SWT.CENTER; chartViewerComposite.setLayoutData(data); } + private void createAlertsSection() { + Composite section = guiHelper.createSection(form, toolkit, "Alerts", null, 1, false); + List<Alert> alerts = GlusterDataModelManager.getInstance().getModel().getCluster().getAlerts(); + + for (Alert alert : alerts) { + addAlertLabel(section, alert); + } + } + + private void addAlertLabel(Composite section, Alert alert) { + CLabel lblAlert = new CLabel(section, SWT.FLAT); + Image alertImage = null; + switch (alert.getType()) { + case OFFLINE_VOLUME_DISKS_ALERT: + alertImage = guiHelper.getImage(IImageKeys.DISK_OFFLINE); + break; + case DISK_USAGE_ALERT: + alertImage = guiHelper.getImage(IImageKeys.DISK); + break; + case OFFLINE_SERVERS_ALERT: + alertImage = guiHelper.getImage(IImageKeys.STATUS_OFFLINE); + break; + case MEMORY_USAGE_ALERT: + alertImage = guiHelper.getImage(IImageKeys.MEMORY); + break; + case CPU_USAGE_ALERT: + alertImage = guiHelper.getImage(IImageKeys.SERVER_WARNING); + break; + } + lblAlert.setImage(alertImage); + lblAlert.setText(alert.getMessage()); + lblAlert.redraw(); + } + private void createActionsSection() { Composite section = guiHelper.createSection(form, toolkit, "Actions", null, 1, false); @@ -157,13 +238,58 @@ public class ClusterSummaryView extends ViewPart { private void createSections(Composite parent) { form = guiHelper.setupForm(parent, toolkit, "Cluster Summary"); - createVolumesSection(); createServersSection(); + createDiskSpaceSection(); + createCPUUsageSection(); + //createMemoryUsageSection(); createActionsSection(); + createAlertsSection(); + createRunningTasksSection(); parent.layout(); // IMP: lays out the form properly } + private void createMemoryUsageSection() { + Composite section = guiHelper.createSection(form, toolkit, "Memory Usage (aggregated)", null, 1, false); + toolkit.createLabel(section, "Historical Memory Usage graph aggregated across all servers will be displayed here."); + } + + private void createCPUUsageSection() { + Composite section = guiHelper.createSection(form, toolkit, "CPU Usage (aggregated)", null, 1, false); + toolkit.createLabel(section, "Historical CPU Usage graph aggregated across all servers will be displayed here."); + } + + private void createRunningTasksSection() { + Composite section = guiHelper.createSection(form, toolkit, "Running Tasks", null, 1, false); + + List<RunningTask> runningTasks = cluster.getRunningTasks(); + + for (RunningTask task : runningTasks) { + addRunningTaskLabel(section, task); + } + } + + private void addRunningTaskLabel(Composite section, RunningTask task) { + // Task related to Volumes context + CLabel lblAlert = new CLabel(section, SWT.NONE); + lblAlert.setText(task.getTaskInfo()); + + Image taskImage = null; + switch(task.getType()) { + case DISK_FORMAT: + taskImage = guiHelper.getImage(IImageKeys.DISK); + break; + case DISK_MIGRATE: + taskImage = guiHelper.getImage(IImageKeys.DISK_MIGRATE); + break; + case VOLUME_REBALANCE: + taskImage = guiHelper.getImage(IImageKeys.VOLUME_REBALANCE); + break; + } + lblAlert.setImage(taskImage); + lblAlert.redraw(); + } + /* * (non-Javadoc) * diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/GlusterServerSummaryView.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/GlusterServerSummaryView.java index 42a14dd4..87ad17d6 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/GlusterServerSummaryView.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/GlusterServerSummaryView.java @@ -114,8 +114,8 @@ public class GlusterServerSummaryView extends ViewPart { memoryUsageBar.setMinimum(0); memoryUsageBar.setMaximum((int) Math.round(server.getTotalMemory())); memoryUsageBar.setSelection((int) Math.round(server.getMemoryInUse())); - memoryUsageBar.setToolTipText("Total: " + server.getTotalMemory() + "GB, In Use: " - + server.getMemoryInUse() + "GB"); + memoryUsageBar.setToolTipText("Total: " + NumberUtil.formatNumber(server.getTotalMemory()) + "GB, In Use: " + + NumberUtil.formatNumber(server.getMemoryInUse()) + "GB"); // toolkit.createLabel(section, "Memory Usage: ", SWT.NONE); // final CoolProgressBar bar = new CoolProgressBar(section,SWT.HORIZONTAL, diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/GlusterServersSummaryView.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/GlusterServersSummaryView.java index 428e55d5..bb9114b3 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/GlusterServersSummaryView.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/GlusterServersSummaryView.java @@ -94,9 +94,9 @@ public class GlusterServersSummaryView extends ViewPart { String[] categories = new String[] { "Online", "Offline" }; PieChartViewerComposite chartViewerComposite = new PieChartViewerComposite(section, SWT.NONE, categories, values); - GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); - data.widthHint = 250; - data.heightHint = 250; + GridData data = new GridData(SWT.FILL, SWT.FILL, false, false); + data.widthHint = 300; + data.heightHint = 150; chartViewerComposite.setLayoutData(data); } diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumesSummaryView.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumesSummaryView.java index b929a2b2..2b9d7178 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumesSummaryView.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/VolumesSummaryView.java @@ -157,9 +157,9 @@ public class VolumesSummaryView extends ViewPart { PieChartViewerComposite chartViewerComposite = new PieChartViewerComposite(section, SWT.NONE, categories, values); - GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); - data.widthHint = 250; - data.heightHint = 250; + GridData data = new GridData(SWT.FILL, SWT.FILL, false, false); + data.widthHint = 300; + data.heightHint = 150; chartViewerComposite.setLayoutData(data); } diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/tabcreators/PieChartViewerComposite.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/tabcreators/PieChartViewerComposite.java index 80c9c807..4e3d808a 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/tabcreators/PieChartViewerComposite.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/views/details/tabcreators/PieChartViewerComposite.java @@ -57,6 +57,8 @@ import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; +import com.gluster.storage.management.gui.utils.GlusterChartPalette; + /** * */ @@ -122,17 +124,19 @@ public final class PieChartViewerComposite extends Composite implements ChartWithoutAxes pieChart = ChartWithoutAxesImpl.create(); // Plot - pieChart.setSeriesThickness(2); + 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.getText().getFont().setSize(8); + lg.setMaxPercent(0.7); + lg.getText().getFont().setSize(9); lg.setBackground(null); lg.getOutline().setVisible(false); lg.setVisible(true); @@ -151,15 +155,18 @@ public final class PieChartViewerComposite extends Composite implements 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("Cities");//$NON-NLS-1$ - sePie.getTitle().setVisible(false); - sePie.setExplosion(2); + sePie.setSeriesIdentifier("Chart");//$NON-NLS-1$ + sePie.getTitle().setVisible(false); // no title + sePie.getLabel().setVisible(false); // no label (values) + sePie.setExplosion(0); // no gap between the pie slices SeriesDefinition seriesDefinition = SeriesDefinitionImpl.create(); seriesDefinition.getQuery().setDefinition("query.definition");//$NON-NLS-1$ diff --git a/src/com.gluster.storage.management.server.scripts/src/nodes/PeerAgent.py b/src/com.gluster.storage.management.server.scripts/src/nodes/PeerAgent.py index 909d24a3..a09e055a 100755 --- a/src/com.gluster.storage.management.server.scripts/src/nodes/PeerAgent.py +++ b/src/com.gluster.storage.management.server.scripts/src/nodes/PeerAgent.py @@ -74,11 +74,10 @@ def executeCommand(command): rv = Utils.runCommandFG(command, stdout=True, root=True) statusCode = rv["Status"] if statusCode != 0: + output = "output: [" + stripEmptyLines(rv["Stdout"]) + "] error: [" + stripEmptyLines(rv["Stderr"]) + "]"; rs = ResponseXml() - rs.appendTagRoute("status", statusCode); - rs.appendTagRoute("output", stripEmptyLines(rv["Stdout"])) - rs.appendTagRoute("message", stripEmptyLines(rv["Stderr"])) - print rs.toprettyxml() + rs.appendTagRoute("status.code", statusCode); + rs.appendTagRoute("status.message", output); return rs.toprettyxml() else: return rv["Stdout"] diff --git a/src/com.gluster.storage.management.server.scripts/src/nodes/get_file.py b/src/com.gluster.storage.management.server.scripts/src/nodes/get_file.py new file mode 100755 index 00000000..826ade6e --- /dev/null +++ b/src/com.gluster.storage.management.server.scripts/src/nodes/get_file.py @@ -0,0 +1,132 @@ +# Copyright (C) 2009,2010 Gluster, Inc. <http://www.gluster.com> +# This file is part of Gluster Storage Platform. +# +# Gluster Storage Platform is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 of +# the License, or (at your option) any later version. +# +# Gluster Storage Platform is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see +# <http://www.gnu.org/licenses/>. + +import Globals +import syslog +import Commands +import Utils +from VolumeUtils import * +from XmlHandler import ResponseXml + + +def enumLogType(logCode): + if "M" == logCode.upper(): + return "EMERGENCY" + elif "A" == logCode.upper(): + return "ALERT" + elif "C" == logCode.upper(): + return "CRITICAL" + elif "E" == logCode.upper(): + return "ERROR" + elif "W" == logCode.upper(): + return "WARNING" + elif "N" == logCode.upper(): + return "NOTICE" + elif "I" == logCode.upper(): + return "INFO" + elif "D" == logCode.upper(): + return "DEBUG" + elif "T" == logCode.upper(): + return "TRACE" + else: + return "UNKNOWN" +##--end of enumLogType() + + +def addLog(responseDom, logMessageTag, loginfo): + logTag = responseDom.createTag("log", None) + logTag.appendChild(responseDom.createTag("date", loginfo[0])) + logTag.appendChild(responseDom.createTag("time", loginfo[1])) + logTag.appendChild(responseDom.createTag("type", enumLogType(loginfo[2]))) + logTag.appendChild(responseDom.createTag("message", loginfo[3])) + logMessageTag.appendChild(logTag) + return True +##--end of addLog() + + +def logSplit(log): + loginfo = log.strip().split(None, 3) + loginfo[0] = loginfo[0][1:] #-- Remove '[' + loginfo[1] = loginfo[1][0:-1] #-- Remove ']' + return loginfo +##--end of logSplit() + + +def getVolumeLog(volumeName, tailCount): + rs = ResponseXml() + if not volumeName: + rs.appendTagRoute("status.code", "-1") + rs.appendTagRoute("status.message", "No volume name given") + return rs.toprettyxml() + + if not tailCount: + rs.appendTagRoute("status.code", "-1") + rs.appendTagRoute("status.message", "No tail count given") + return rs.toprettyxml() + + thisServerName = getCurrentServerName() + if not thisServerName: + rs.appendTagRoute("status.code", "-2") + rs.appendTagRoute("status.message", "Failed to get current server name") + return rs.toprettyxml() + + volumeDom = XDOM() + partitionList = getPartitionListByServerName(volumeDom, thisServerName) + if not partitionList: + rs.appendTagRoute("status.code", "-3") + rs.appendTagRoute("status.message", "Failed to get server partition details") + return rs.toprettyxml() + + pattern = '\[\d{4}-\d{2}-\d{2}\s{1}\d{2}:\d{2}:\d{2}.\d+\]\s{1}([MACEWNIDT]){1}\s+' + logMessagesTag = rs.createTag("response.logMessages") + for partitionName in partitionList: + logMessageTag = rs.createTag("logMessage") + logMessageTag.appendChild("disk", "%s:%s" % (thisServerName, partitionName)) + + logDirectory = "%s/%s/%s/log" % (Globals.GLUSTER_LUN_DIR, partitionList[partitionName], volumeUuid) + logFileName = "%s/%s-%s-%s-exports-brick1.log" % (logDirectory, + Globals.GLUSTER_LUN_DIR[1:], + partitionList[partitionName], + volumeUuid) + if not os.path.exists(logFileName): + Utils.log("volume log file not found %s" % logFileName) + continue + fp = open(logFileName) + lines = [line for line in fp if re.match(pattern, line)] + fp.close() + i = len(lines) - int(tailCount) + if i < 0: + i = 0 + for log in lines[i:]: + loginfo = logSplit(log) + addLog(rs, logMessageTag, loginfo) + logMessagesTag.appendChild(logMessageTag) + return rs.toprettyxml() +##--end of getVolumeLog() + +def main(): + if len(sys.argv) != 3: + print >> sys.stderr, "usage: %s <disk name> <volume name>" % sys.argv[0] + sys.exit(-1) + + volumeName = sys.argv[1] + tailCount = sys.argv[2] + print getVolumeLog(volumeName, tailCount) + sys.exit(0) + +if __name__ == "__main__": + main() diff --git a/src/com.gluster.storage.management.server/build/glusterserver.ant b/src/com.gluster.storage.management.server/build/glusterserver.ant index 88602fb6..98012515 100644 --- a/src/com.gluster.storage.management.server/build/glusterserver.ant +++ b/src/com.gluster.storage.management.server/build/glusterserver.ant @@ -39,7 +39,8 @@ <include name="**/*.sql" /> </fileset> </copy> - <javac srcdir="${basedir}/src" destdir="${WEB-INF}/classes" classpathref="libs" /> + <!-- TODO: make debug option configurable in jenkins --> + <javac srcdir="${basedir}/src" destdir="${WEB-INF}/classes" classpathref="libs" debug="true" debuglevel="lines,vars,source"/> </target> <target name="archive" depends="compile"> diff --git a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/VolumesResource.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/VolumesResource.java index fd4643a6..e635510a 100644 --- a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/VolumesResource.java +++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/VolumesResource.java @@ -31,6 +31,7 @@ import static com.gluster.storage.management.core.constants.RESTConstants.PATH_P import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DELETE_OPTION; import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DISKS; import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DISK_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DOWNLOAD; import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_FROM_TIMESTAMP; import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_LINE_COUNT; import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_LOG_SEVERITY; @@ -39,15 +40,22 @@ import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_ import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_VOLUMES; import static com.gluster.storage.management.core.constants.RESTConstants.SUBRESOURCE_DEFAULT_OPTIONS; import static com.gluster.storage.management.core.constants.RESTConstants.SUBRESOURCE_DISKS; +import static com.gluster.storage.management.core.constants.RESTConstants.SUBRESOURCE_DOWNLOAD; import static com.gluster.storage.management.core.constants.RESTConstants.SUBRESOURCE_LOGS; import static com.gluster.storage.management.core.constants.RESTConstants.SUBRESOURCE_OPTIONS; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; +import java.util.zip.GZIPOutputStream; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -59,7 +67,10 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.StreamingOutput; import com.gluster.storage.management.core.constants.CoreConstants; import com.gluster.storage.management.core.constants.RESTConstants; @@ -73,6 +84,8 @@ import com.gluster.storage.management.core.response.LogMessageListResponse; import com.gluster.storage.management.core.response.VolumeListResponse; import com.gluster.storage.management.core.response.VolumeOptionInfoListResponse; import com.gluster.storage.management.core.utils.DateUtil; +import com.gluster.storage.management.core.utils.FileUtil; +import com.gluster.storage.management.core.utils.ProcessUtil; import com.gluster.storage.management.server.constants.VolumeOptionsDefaults; import com.gluster.storage.management.server.utils.GlusterUtil; import com.gluster.storage.management.server.utils.ServerUtil; @@ -219,8 +232,14 @@ public class VolumesResource { @SuppressWarnings("rawtypes") private Status prepareBrick(String serverName, String diskName, String volumeName) { - return (Status) ((GenericResponse) serverUtil.executeOnServer(true, serverName, PREPARE_BRICK_SCRIPT + " " - + diskName + " " + volumeName, GenericResponse.class)).getStatus(); + Object response = serverUtil.executeOnServer(true, serverName, PREPARE_BRICK_SCRIPT + " " + + diskName + " " + volumeName, GenericResponse.class); + if(response instanceof GenericResponse) { + return ((GenericResponse)response).getStatus(); + } else { + // in case of script failure on server, a Status object will be returned + return (Status) response; + } } private Status createDirectories(List<String> disks, String volumeName) { @@ -232,11 +251,8 @@ public class VolumesResource { String[] diskParts = disk.split(":"); String serverName = diskParts[0]; String diskName = diskParts[1]; - try { - status = prepareBrick(serverName, diskName, volumeName); - } catch (Exception e) { - status = new Status(e); - } + + status = prepareBrick(serverName, diskName, volumeName); if (status.isSuccess()) { String brickDir = status.getMessage().trim(); bricks.add(serverName + ":" + brickDir); @@ -271,15 +287,24 @@ public class VolumesResource { diskInfo = disks.get(i).split(":"); serverName = diskInfo[0]; diskName = diskInfo[1]; - result = ((GenericResponse) serverUtil.executeOnServer(true, serverName, VOLUME_DIRECTORY_CLEANUP_SCRIPT - + " " + diskName + " " + volumeName + " " + deleteFlag, GenericResponse.class)).getStatus(); - if (!result.isSuccess()) { - return result; + + Object response = serverUtil.executeOnServer(true, serverName, VOLUME_DIRECTORY_CLEANUP_SCRIPT + + " " + diskName + " " + volumeName + " " + deleteFlag, GenericResponse.class); + if(response instanceof GenericResponse) { + result = ((GenericResponse)response).getStatus(); + if (!result.isSuccess()) { + // TODO: append error and continue with cleaning up of other directories + return result; + } + } else { + // TODO: append error and continue with cleaning up of other directories + // In case of script execution failure, a Status object will be returned. + return (Status)response; } } return new Status(Status.STATUS_CODE_SUCCESS, "Directories cleaned up successfully!"); } - + private List<LogMessage> getBrickLogs(Volume volume, String brickName, Integer lineCount) throws GlusterRuntimeException { // brick name format is <serverName>:<brickDirectory> @@ -316,6 +341,61 @@ public class VolumesResource { } return logMessages; } + + @GET + @Produces(MediaType.APPLICATION_OCTET_STREAM) + @Path("{" + PATH_PARAM_VOLUME_NAME + "}/" + SUBRESOURCE_LOGS + "/" + SUBRESOURCE_DOWNLOAD) + public StreamingOutput getLogs(@PathParam(PATH_PARAM_VOLUME_NAME) final String volumeName) { + return new StreamingOutput() { + + @Override + public void write(OutputStream output) throws IOException, WebApplicationException { + Volume volume = getVolume(volumeName); + try { + String archiveFileName = downloadLogs(volume); + FileInputStream inputStream = new FileInputStream(archiveFileName); + int size = inputStream.available(); + byte[] data = new byte[size]; + inputStream.read(data); + inputStream.close(); + output.write(data); + } catch (Exception e) { + e.printStackTrace(); + throw new GlusterRuntimeException("Exception while downloading/archiving volume log files!", e); + } + } + }; + } + + private String downloadLogs(Volume volume) { + FileUtil fileUtil = new FileUtil(); + + // create temporary directory + File tempDir = fileUtil.createTempDir(); + String tempDirPath = tempDir.getPath(); + + for(String brickName : volume.getBricks()) { + // brick name format is <serverName>:<brickDirectory> + String[] brickParts = brickName.split(":"); + String serverName = brickParts[0]; + String brickDir = brickParts[1]; + + String logDir = glusterUtil.getLogLocation(volume.getName(), brickName); + String logFileName = glusterUtil.getLogFileNameForBrickDir(brickDir); + String logFilePath = logDir + CoreConstants.FILE_SEPARATOR + logFileName; + + String logContents = serverUtil.getFileFromServer(serverName, logFilePath); + fileUtil.createTextFile(tempDirPath + CoreConstants.FILE_SEPARATOR + logFileName, logContents); + } + + String gzipPath = fileUtil.getTempDirName() + CoreConstants.FILE_SEPARATOR + volume.getName() + "-logs.tar.gz"; + new ProcessUtil().executeCommand("tar", "czvf", gzipPath, tempDirPath); + + // delete the temp directory + fileUtil.recursiveDelete(tempDir); + + return gzipPath; + } @GET @Path("{" + PATH_PARAM_VOLUME_NAME + "}/" + SUBRESOURCE_LOGS) @@ -323,7 +403,8 @@ public class VolumesResource { @QueryParam(QUERY_PARAM_DISK_NAME) String diskName, @QueryParam(QUERY_PARAM_LOG_SEVERITY) String severity, @QueryParam(QUERY_PARAM_FROM_TIMESTAMP) String fromTimestamp, @QueryParam(QUERY_PARAM_TO_TIMESTAMP) String toTimestamp, - @QueryParam(QUERY_PARAM_LINE_COUNT) Integer lineCount) { + @QueryParam(QUERY_PARAM_LINE_COUNT) Integer lineCount, + @QueryParam(QUERY_PARAM_DOWNLOAD) Boolean download) { List<LogMessage> logMessages = null; try { diff --git a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/ServerUtil.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/ServerUtil.java index 5e423f1c..59dc36c2 100644 --- a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/ServerUtil.java +++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/ServerUtil.java @@ -22,6 +22,7 @@ package com.gluster.storage.management.server.utils; import java.io.BufferedReader; import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetAddress; @@ -38,7 +39,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.gluster.storage.management.core.constants.CoreConstants; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; import com.gluster.storage.management.core.model.Status; +import com.gluster.storage.management.core.response.GenericResponse; import com.gluster.storage.management.core.utils.ProcessResult; import com.gluster.storage.management.core.utils.ProcessUtil; @@ -71,34 +74,57 @@ public class ServerUtil { * @param runInForeground * @param serverName * @param commandWithArgs - * @param expectedClass Class of the object expected from script execution - * @return Response from remote execution of the command + * @param expectedClass + * Class of the object expected from script execution + * @return Object of the expected class from remote execution of the command. In case the remote execution fails + * ungracefully, an object of class {@link Status} will be returned. */ @SuppressWarnings("rawtypes") - public Object executeOnServer(boolean runInForeground, String serverName, String commandWithArgs, Class expectedClass) { - StringBuffer output = new StringBuffer(); + public Object executeOnServer(boolean runInForeground, String serverName, String commandWithArgs, + Class expectedClass) { + try { + String output = executeOnServer(serverName, commandWithArgs); + + // In case the script execution exits ungracefully, the agent would return a GenericResponse. + // hence pass last argument as true to try GenericResponse unmarshalling in such cases. + Object response = unmarshal(expectedClass, output, expectedClass != GenericResponse.class); + if (expectedClass != GenericResponse.class && response instanceof GenericResponse) { + // expected class was not GenericResponse, but that's what we got. This means the + // script failed ungracefully. Extract and return the status object from the response + return ((GenericResponse) response).getStatus(); + } + return response; + } catch (Exception e) { + // any other exception means unexpected error. return status with error from exception. + return new Status(e); + } + } + + private String executeOnServer(String serverName, String commandWithArgs) { try { InetAddress address = InetAddress.getByName(serverName); Socket connection = new Socket(address, 50000); PrintWriter writer = new PrintWriter(connection.getOutputStream(), true); - BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8")); + InputStream inputStream = connection.getInputStream(); writer.println(commandWithArgs); writer.println(); // empty line means end of request - String line; - while (!(line = reader.readLine()).trim().isEmpty()) { - output.append(line + CoreConstants.NEWLINE); - } - + int available = inputStream.available(); + byte[] responseData = new byte[available]; + inputStream.read(responseData); connection.close(); - return unmarshal(expectedClass, output.toString(), expectedClass != Status.class); - } catch(Exception e) { - // any other exception means unexpected error. return status with error from exception. - return new Status(Status.STATUS_CODE_FAILURE, "Error during remote execution: [" + e.getMessage() + "]"); + + return new String(responseData, "UTF-8"); + } catch (Exception e) { + throw new GlusterRuntimeException("Error during remote execution: [" + e.getMessage() + "]"); } } + + public String getFileFromServer(String serverName, String fileName) { + return executeOnServer(serverName, "get_file " + fileName); + } /** * Unmarshals given input string into object of given class @@ -107,22 +133,23 @@ public class ServerUtil { * Class whose object is expected * @param input * Input string - * @param tryStatusOnFailure + * @param tryGenericResponseOnFailure * If true, and if the unmarshalling fails for given class, another unmarshalling will be attempted with - * class Status. If that also fails, a status object with exception message is created and returned. + * class {@link GenericResponse}. If this also fails, a status object with exception message is created + * and returned. * @return Object of given expected class, or a status object in case first unmarshalling fails. */ @SuppressWarnings("rawtypes") - private Object unmarshal(Class expectedClass, String input, boolean tryStatusOnFailure) { + private Object unmarshal(Class expectedClass, String input, boolean tryGenericResponseOnFailure) { try { // create JAXB context and instantiate marshaller JAXBContext context = JAXBContext.newInstance(expectedClass); Unmarshaller um = context.createUnmarshaller(); return um.unmarshal(new ByteArrayInputStream(input.getBytes())); } catch (JAXBException e) { - if(tryStatusOnFailure) { - // unmarshalling failed. try to unmarshal a Status object - return unmarshal(Status.class, input, false); + if(tryGenericResponseOnFailure) { + // unmarshalling failed. try to unmarshal a GenericResponse object + return unmarshal(GenericResponse.class, input, false); } return new Status(Status.STATUS_CODE_FAILURE, "Error during unmarshalling string [" + input @@ -130,9 +157,9 @@ public class ServerUtil { } } - public static void main(String args[]) { + public static void main(String args[]) throws Exception { // CreateVolumeExportDirectory.py md0 testvol - System.out.println(new ServerUtil().executeOnServer(true, "localhost", "python CreateVolumeExportDirectory.py md0 testvol", Status.class)); + System.out.println(new ServerUtil().getFileFromServer("localhost", "/tmp/python/PeerAgent.py")); } /** |
