summaryrefslogtreecommitdiffstats
path: root/src/org.gluster.storage.management.gateway/src/org
diff options
context:
space:
mode:
authorShireesh Anjal <shireesh@gluster.com>2011-11-25 20:13:35 +0530
committerShireesh Anjal <shireesh@gluster.com>2011-11-25 20:13:35 +0530
commit1142b0e41de39010de7845cf70d71dbb001fc1dc (patch)
tree3513487f65c1a7df47996bd2852393aceaac1b8a /src/org.gluster.storage.management.gateway/src/org
parent92c52d8edf285945d31e446503fc742fde9dcc49 (diff)
Renamed projects / packages com.gluster.* to org.gluster.*
Diffstat (limited to 'src/org.gluster.storage.management.gateway/src/org')
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/ClusterInfo.java79
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/GlusterDataSource.java48
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/PersistenceDao.java113
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/ServerInfo.java72
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/filters/AuditFilter.java48
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/filters/AuthenticationFailureFilter.java105
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/filters/GlusterResourceFilterFactory.java31
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/listeners/ShutdownListener.java48
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/AbstractResource.java177
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/ClustersResource.java126
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/DiscoveredServersResource.java101
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/GenericExceptionMapper.java60
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/GlusterServersResource.java343
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/KeysResource.java155
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/TasksResource.java226
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/UsersResource.java124
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/VolumesResource.java383
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/security/GlusterUserDetailsService.java31
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/security/UserAuthDao.java57
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/AbstractGlusterInterface.java38
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/ClusterService.java269
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/DiscoveredServerService.java130
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/Gluster323InterfaceService.java598
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/GlusterInterface.java369
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/GlusterInterfaceService.java256
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/GlusterServerService.java530
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/VolumeService.java984
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/InitServerTask.java161
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/InitializeDiskTask.java198
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/MigrateBrickTask.java220
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/RebalanceVolumeTask.java141
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/ServerSyncTask.java168
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/Task.java113
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/AbstractStatsFactory.java162
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/CpuStatsFactory.java36
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/DBUtil.java84
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/MemoryStatsFactory.java68
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/NetworkStatsFactory.java173
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/PasswordManager.java83
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/ServerUtil.java370
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/SshUtil.java463
-rw-r--r--src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/StatsFactory.java32
42 files changed, 7973 insertions, 0 deletions
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/ClusterInfo.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/ClusterInfo.java
new file mode 100644
index 00000000..ba197b92
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/ClusterInfo.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * 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.gateway.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+
+import org.hibernate.cfg.AnnotationConfiguration;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+
+@Entity(name="cluster_info")
+public class ClusterInfo {
+ @Id
+ @GeneratedValue
+ private Integer id;
+
+ private String name;
+
+ @OneToMany(mappedBy="cluster")
+ private List<ServerInfo> servers = new ArrayList<ServerInfo>();
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setServers(List<ServerInfo> servers) {
+ this.servers = servers;
+ }
+
+ public List<ServerInfo> getServers() {
+ return servers;
+ }
+
+ public void addServer(ServerInfo server) {
+ servers.add(server);
+ }
+
+ public static void main(String args[]) {
+ AnnotationConfiguration config = new AnnotationConfiguration();
+ config.addAnnotatedClass(ClusterInfo.class);
+ config.addAnnotatedClass(ServerInfo.class);
+ config.configure();
+ new SchemaExport(config).create(true, true);
+ }
+
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/GlusterDataSource.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/GlusterDataSource.java
new file mode 100644
index 00000000..f0b0311e
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/GlusterDataSource.java
@@ -0,0 +1,48 @@
+/**
+ * GlusterDataSource.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.gateway.data;
+
+import javax.servlet.ServletContext;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.datasource.DriverManagerDataSource;
+import org.springframework.stereotype.Component;
+
+@Component
+public class GlusterDataSource extends DriverManagerDataSource {
+ @Autowired
+ ServletContext servletContext;
+
+ public GlusterDataSource() {
+ setDriverClassName(org.apache.derby.jdbc.EmbeddedDriver.class.getName());
+
+ setUsername("gluster");
+ // TODO: change to a stronger (encrypted) password
+ setPassword("gluster");
+ }
+
+ public DriverManagerDataSource getDataSource() {
+ // Database directory = work/data relative to context root
+ setUrl("jdbc:derby:" + servletContext.getRealPath("data") + ";create=true");
+
+ return this;
+ }
+} \ No newline at end of file
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/PersistenceDao.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/PersistenceDao.java
new file mode 100644
index 00000000..49348084
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/PersistenceDao.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.gateway.data;
+
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.EntityTransaction;
+import javax.persistence.PersistenceUnit;
+import javax.persistence.Query;
+
+/**
+ *
+ */
+public class PersistenceDao<T> {
+ private Class<T> type;
+
+ private EntityManager entityManager;
+
+ @PersistenceUnit
+ private EntityManagerFactory entityManagerFactory;
+
+ public PersistenceDao(Class<T> type) {
+ this.type = type;
+ }
+
+ public EntityTransaction startTransaction() {
+ EntityTransaction txn = getEntityManager().getTransaction();
+ txn.begin();
+ return txn;
+ }
+
+ private synchronized EntityManager getEntityManager() {
+ if (entityManager == null) {
+ entityManager = entityManagerFactory.createEntityManager();
+ }
+ return entityManager;
+ }
+
+ public Object getSingleResult(String query) {
+ return getEntityManager().createQuery(query).getSingleResult();
+ }
+
+ public Object getSingleResult(String queryString, String... params) {
+ return createQuery(queryString, params).getSingleResult();
+ }
+
+ private Query createQuery(String queryString, String... params) {
+ Query query = getEntityManager().createQuery(queryString);
+ for (int i = 0; i < params.length; i++) {
+ query.setParameter(i + 1, params[i]);
+ }
+ return query;
+ }
+
+ public Object getSingleResultFromSQL(String sqlQuery) {
+ return getEntityManager().createNativeQuery(sqlQuery).getSingleResult();
+ }
+
+ @SuppressWarnings("rawtypes")
+ public List findBySQL(String sqlQuery) {
+ return getEntityManager().createNativeQuery(sqlQuery).getResultList();
+ }
+
+ public T findById(int id) {
+ return getEntityManager().find(type, id);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<T> findAll() {
+ return getEntityManager().createQuery("select t from " + type.getName() + " t").getResultList();
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<T> findBy(String whereClause) {
+ return getEntityManager().createQuery("select t from " + type.getName() + " t where " + whereClause)
+ .getResultList();
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<T> findBy(String whereClause, String... params) {
+ return createQuery("select t from " + type.getName() + " t where " + whereClause, params).getResultList();
+ }
+
+ public void save(Object obj) {
+ getEntityManager().persist(obj);
+ }
+
+ public T update(T obj) {
+ return getEntityManager().merge(obj);
+ }
+
+ public void delete(Object obj) {
+ getEntityManager().remove(obj);
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/ServerInfo.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/ServerInfo.java
new file mode 100644
index 00000000..84df2f1a
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/data/ServerInfo.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * 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.gateway.data;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+
+/**
+ *
+ */
+@Entity(name="server_info")
+public class ServerInfo {
+ @Id
+ @GeneratedValue
+ private Integer id;
+
+ private String name;
+
+ @ManyToOne
+ @JoinColumn(name="cluster_id")
+ private ClusterInfo cluster;
+
+ public ServerInfo() {
+ }
+
+ public ServerInfo(String name) {
+ setName(name);
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setCluster(ClusterInfo cluster) {
+ this.cluster = cluster;
+ }
+
+ public ClusterInfo getCluster() {
+ return cluster;
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/filters/AuditFilter.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/filters/AuditFilter.java
new file mode 100644
index 00000000..76b329ee
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/filters/AuditFilter.java
@@ -0,0 +1,48 @@
+/**
+ *
+ */
+package org.gluster.storage.management.gateway.filters;
+
+import java.security.Principal;
+
+import org.apache.log4j.Logger;
+
+import com.sun.jersey.spi.container.ContainerRequest;
+import com.sun.jersey.spi.container.ContainerRequestFilter;
+import com.sun.jersey.spi.container.ContainerResponse;
+import com.sun.jersey.spi.container.ContainerResponseFilter;
+import com.sun.jersey.spi.container.ResourceFilter;
+
+/**
+ * Resource filter for maintaining audit trail of resource access
+ */
+public class AuditFilter implements ResourceFilter, ContainerRequestFilter, ContainerResponseFilter {
+ private static final Logger logger = Logger.getLogger(AuditFilter.class);
+
+ @Override
+ public ContainerRequestFilter getRequestFilter() {
+ return this;
+ }
+
+ @Override
+ public ContainerResponseFilter getResponseFilter() {
+ return this;
+ }
+
+ @Override
+ public ContainerRequest filter(ContainerRequest req) {
+ Principal principal = req.getUserPrincipal();
+ if(principal != null) {
+ logger.info("REQUEST from [" + principal.getName() + "] : [" + req.getMethod() + "][" + req.getPath() + "]");
+ } else {
+ logger.info("REQUEST [" + req.getMethod() + "][" + req.getPath() + "]");
+ }
+ return req;
+ }
+
+ @Override
+ public ContainerResponse filter(ContainerRequest req, ContainerResponse response) {
+ logger.info("RESPONSE: [" + req.getMethod() + "][" + req.getPath() + "]");
+ return response;
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/filters/AuthenticationFailureFilter.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/filters/AuthenticationFailureFilter.java
new file mode 100644
index 00000000..4bb7f37c
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/filters/AuthenticationFailureFilter.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.gateway.filters;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import javax.ws.rs.core.Response;
+
+/**
+ * @author root
+ *
+ */
+public class AuthenticationFailureFilter implements Filter {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.Filter#destroy()
+ */
+ @Override
+ public void destroy() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public class CharResponseWrapper extends HttpServletResponseWrapper {
+ private CharArrayWriter output;
+
+ public String toString() {
+ return output.toString();
+ }
+
+ public CharResponseWrapper(HttpServletResponse response) {
+ super(response);
+ output = new CharArrayWriter();
+ }
+
+ public PrintWriter getWriter() {
+ return new PrintWriter(output);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse,
+ * javax.servlet.FilterChain)
+ */
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
+ ServletException {
+ HttpServletRequest request = (HttpServletRequest) req;
+ if (request.getRequestURI().contains("download")) {
+ chain.doFilter(req, res);
+ return;
+ }
+
+ CharResponseWrapper wrapper = new CharResponseWrapper((HttpServletResponse) res);
+ chain.doFilter(req, wrapper);
+
+ if(wrapper.getStatus() == Response.Status.UNAUTHORIZED.ordinal()) {
+ PrintWriter out = res.getWriter();
+ out.println("<status><code>1</code><message>Authentication Failed!</message></status>");
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
+ */
+ @Override
+ public void init(FilterConfig arg0) throws ServletException {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/filters/GlusterResourceFilterFactory.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/filters/GlusterResourceFilterFactory.java
new file mode 100644
index 00000000..8772cdcd
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/filters/GlusterResourceFilterFactory.java
@@ -0,0 +1,31 @@
+/**
+ *
+ */
+package org.gluster.storage.management.gateway.filters;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.sun.jersey.api.model.AbstractMethod;
+import com.sun.jersey.spi.container.ResourceFilter;
+import com.sun.jersey.spi.container.ResourceFilterFactory;
+
+/**
+ * Gluster resource filter factory. As of now, this creates only one filter - the audit filter {@code AuditFilter}
+ */
+public class GlusterResourceFilterFactory implements ResourceFilterFactory {
+
+ public GlusterResourceFilterFactory() {
+ }
+
+ /* (non-Javadoc)
+ * @see com.sun.jersey.spi.container.ResourceFilterFactory#create(com.sun.jersey.api.model.AbstractMethod)
+ */
+ @Override
+ public List<ResourceFilter> create(AbstractMethod arg0) {
+ List<ResourceFilter> filters = new ArrayList<ResourceFilter>();
+ filters.add(new AuditFilter());
+
+ return filters;
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/listeners/ShutdownListener.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/listeners/ShutdownListener.java
new file mode 100644
index 00000000..8c6bb42b
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/listeners/ShutdownListener.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.gateway.listeners;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.gluster.storage.management.gateway.utils.DBUtil;
+
+
+/**
+ * Shuts down the Derby database when gateway is being stopped.
+ */
+public class ShutdownListener implements ServletContextListener {
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
+ */
+ @Override
+ public void contextDestroyed(ServletContextEvent arg0) {
+ // Embedded derby must be shut down when the gateway stops.
+ DBUtil.shutdownDerby();
+ System.gc();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
+ */
+ @Override
+ public void contextInitialized(ServletContextEvent arg0) {
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/AbstractResource.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/AbstractResource.java
new file mode 100644
index 00000000..8e26c838
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/AbstractResource.java
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * 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.gateway.resources.v1_0;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.StreamingOutput;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ *
+ */
+public class AbstractResource {
+ @Context
+ protected UriInfo uriInfo;
+
+ /**
+ * Creates a response with HTTP status code of 201 (created) and sets the "location" header to the URI created using
+ * the given path relative to current path.
+ *
+ * @param relativePath
+ * relative path of the created resource - will be set in the "location" header of response.
+ * @return the {@link Response} object
+ */
+ protected Response createdResponse(String relativePath) {
+ return Response.created(createRelatriveURI(relativePath)).build();
+ }
+
+ /**
+ * Creates a response with HTTP status code of 204 (no content)
+ * @return the {@link Response} object
+ */
+ protected Response noContentResponse() {
+ return Response.noContent().build();
+ }
+
+ /**
+ * Creates a response with HTTP status code of 202 (accepted), also setting the location header to given location.
+ * This is typically done while triggering long running tasks
+ *
+ * @param locationURI
+ * URI to be appended to the base URI
+ * @return the {@link Response} object
+ */
+ protected Response acceptedResponse(String locationURI) {
+ return Response.status(Status.ACCEPTED).location(createAbsoluteURI(locationURI)).build();
+ }
+
+ /**
+ * Creates a response with HTTP status code of 404 (not found), also setting the given message in the response body
+ *
+ * @param message
+ * Message to be set in the response body
+ * @return the {@link Response} object
+ */
+ protected Response notFoundResponse(String message) {
+ return Response.status(Status.NOT_FOUND).type(MediaType.TEXT_HTML).entity(message).build();
+ }
+
+ /**
+ * Creates a new URI that is relative to the <b>base URI</b> of the application
+ * @param uriString URI String to be appended to the base URI
+ * @return newly created URI
+ */
+ private URI createAbsoluteURI(String uriString) {
+ return uriInfo.getBaseUriBuilder().path(uriString).build();
+ }
+
+ /**
+ * Creates a response with HTTP status code of 204 (no content), also setting the location header to given location
+ * @param location path of the location to be set relative to current path
+ * @return the {@link Response} object
+ */
+ protected Response noContentResponse(String location) {
+ return Response.noContent().location(createRelatriveURI(location)).build();
+ }
+
+ /**
+ * Creates a URI relative to current URI
+ * @param location path relative to current URI
+ * @return newly created URI
+ */
+ protected URI createRelatriveURI(String location) {
+ return uriInfo.getAbsolutePathBuilder().path(location).build();
+ }
+
+ /**
+ * Creates a response with HTTP status code of 500 (internal server error) and sets the error message in the
+ * response body
+ *
+ * @param errMessage
+ * Error message to be set in the response body
+ * @return the {@link Response} object
+ */
+ protected Response errorResponse(String errMessage) {
+ return Response.serverError().type(MediaType.TEXT_HTML).entity(errMessage).build();
+ }
+
+ /**
+ * Creates a response with HTTP status code of 400 (bad request) and sets the error message in the
+ * response body
+ *
+ * @param errMessage
+ * Error message to be set in the response body
+ * @return the {@link Response} object
+ */
+ protected Response badRequestResponse(String errMessage) {
+ return Response.status(Status.BAD_REQUEST).type(MediaType.TEXT_HTML).entity(errMessage).build();
+ }
+
+ /**
+ * Creates a response with HTTP status code of 401 (unauthorized)
+ *
+ * @return the {@link Response} object
+ */
+ protected Response unauthorizedResponse() {
+ return Response.status(Status.UNAUTHORIZED).build();
+ }
+
+ /**
+ * Creates an OK response and sets the entity in the response body.
+ *
+ * @param entity
+ * Entity to be set in the response body
+ * @param mediaType
+ * Media type to be set on the response
+ * @return the {@link Response} object
+ */
+ protected Response okResponse(Object entity, String mediaType) {
+ return Response.ok(entity).type(mediaType).build();
+ }
+
+ /**
+ * Creates a streaming output response and sets the given streaming output in the response. Typically used for
+ * "download" requests
+ *
+ * @param entity
+ * Entity to be set in the response body
+ * @param mediaType
+ * Media type to be set on the response
+ * @return the {@link Response} object
+ */
+ protected Response streamingOutputResponse(StreamingOutput output) {
+ return Response.ok(output).type(MediaType.APPLICATION_OCTET_STREAM).build();
+ }
+
+ protected StreamingOutput createStreamingOutput(final byte[] data) {
+ return new StreamingOutput() {
+ @Override
+ public void write(OutputStream output) throws IOException {
+ output.write(data);
+ }
+ };
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/ClustersResource.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/ClustersResource.java
new file mode 100644
index 00000000..3c9b3bd2
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/ClustersResource.java
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * 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.gateway.resources.v1_0;
+
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_CLUSTER_NAME;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_SERVER_NAME;
+import static org.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_CLUSTER_NAME;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_CLUSTERS;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.log4j.Logger;
+import org.gluster.storage.management.core.exceptions.GlusterValidationException;
+import org.gluster.storage.management.core.response.ClusterNameListResponse;
+import org.gluster.storage.management.gateway.data.ClusterInfo;
+import org.gluster.storage.management.gateway.services.ClusterService;
+import org.springframework.stereotype.Component;
+
+import com.sun.jersey.api.core.InjectParam;
+import com.sun.jersey.spi.resource.Singleton;
+
+/**
+ *
+ */
+@Component
+@Singleton
+@Path(RESOURCE_PATH_CLUSTERS)
+public class ClustersResource extends AbstractResource {
+ @InjectParam
+ private ClusterService clusterService;
+ private static final Logger logger = Logger.getLogger(ClustersResource.class);
+
+ @GET
+ @Produces(MediaType.APPLICATION_XML)
+ public ClusterNameListResponse getClusters() {
+ List<ClusterInfo> clusters = clusterService.getAllClusters();
+ List<String> clusterList = new ArrayList<String>();
+ for (ClusterInfo cluster : clusters) {
+ clusterList.add(cluster.getName());
+ }
+ return new ClusterNameListResponse(clusterList);
+ }
+
+ @POST
+ public Response createCluster(@FormParam(FORM_PARAM_CLUSTER_NAME) String clusterName) {
+ if(clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Parameter [" + FORM_PARAM_CLUSTER_NAME + "] is missing in request!");
+ }
+
+ if(clusterService.getCluster(clusterName) != null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] already exists!");
+ }
+
+ clusterService.createCluster(clusterName);
+ return createdResponse(clusterName);
+ }
+
+ @PUT
+ public Response registerCluster(@FormParam(FORM_PARAM_CLUSTER_NAME) String clusterName,
+ @FormParam(FORM_PARAM_SERVER_NAME) String knownServer) {
+ if(clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Parameter [" + FORM_PARAM_CLUSTER_NAME + "] is missing in request!");
+ }
+
+ if(knownServer == null || knownServer.isEmpty()) {
+ throw new GlusterValidationException("Parameter [" + FORM_PARAM_SERVER_NAME + "] is missing in request!");
+ }
+
+ if(clusterService.getCluster(clusterName) != null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] already exists!");
+ }
+
+ ClusterInfo mappedCluster = clusterService.getClusterForServer(knownServer);
+ if(mappedCluster != null) {
+ throw new GlusterValidationException("Server [" + knownServer + "] is already present in cluster ["
+ + mappedCluster.getName() + "]!");
+ }
+
+ clusterService.registerCluster(clusterName, knownServer);
+ return noContentResponse(clusterName);
+ }
+
+ @Path("{" + PATH_PARAM_CLUSTER_NAME + "}")
+ @DELETE
+ public Response unregisterCluster(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName) {
+ if(clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Parameter [" + FORM_PARAM_CLUSTER_NAME + "] is missing in request!");
+ }
+
+ ClusterInfo cluster = clusterService.getCluster(clusterName);
+ if(cluster == null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] does not exist!");
+ }
+
+ clusterService.unregisterCluster(cluster);
+ return noContentResponse();
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/DiscoveredServersResource.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/DiscoveredServersResource.java
new file mode 100644
index 00000000..12358f56
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/DiscoveredServersResource.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.gateway.resources.v1_0;
+
+import static org.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_SERVER_NAME;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DETAILS;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_DISCOVERED_SERVERS;
+
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.gluster.storage.management.core.model.Server;
+import org.gluster.storage.management.core.response.ServerListResponse;
+import org.gluster.storage.management.core.response.ServerNameListResponse;
+import org.gluster.storage.management.gateway.services.DiscoveredServerService;
+import org.springframework.stereotype.Component;
+
+import com.sun.jersey.api.core.InjectParam;
+import com.sun.jersey.spi.resource.Singleton;
+
+@Component
+@Singleton
+@Path(RESOURCE_PATH_DISCOVERED_SERVERS)
+public class DiscoveredServersResource extends AbstractResource {
+ @InjectParam
+ private DiscoveredServerService discoveredServerService;
+
+ @GET
+ @Produces(MediaType.APPLICATION_XML)
+ public Response getDiscoveredServersXML(@QueryParam(QUERY_PARAM_DETAILS) Boolean details) {
+ return getDiscoveredServersResponse(details, MediaType.APPLICATION_XML);
+ }
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getDiscoveredServersJSON(@QueryParam(QUERY_PARAM_DETAILS) Boolean details) {
+ return getDiscoveredServersResponse(details, MediaType.APPLICATION_JSON);
+ }
+
+ private Response getDiscoveredServersResponse(Boolean details, String mediaType) {
+ if(details != null && details == true) {
+ try {
+ List<Server> discoveredServers = discoveredServerService.getDiscoveredServerDetails();
+ return okResponse(new ServerListResponse(discoveredServers), mediaType);
+ } catch(Exception e) {
+ return errorResponse(e.getMessage());
+ }
+ } else {
+ return okResponse(new ServerNameListResponse(discoveredServerService.getDiscoveredServerNames()), mediaType);
+ }
+ }
+
+ @Path("{" + PATH_PARAM_SERVER_NAME + "}")
+ @GET
+ @Produces(MediaType.APPLICATION_XML)
+ public Response getDiscoveredServerXML(@PathParam(PATH_PARAM_SERVER_NAME) String serverName) {
+ return getDiscoveredServerResponse(serverName, MediaType.APPLICATION_XML);
+ }
+
+ @Path("{" + PATH_PARAM_SERVER_NAME + "}")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getDiscoveredServerJSON(@PathParam(PATH_PARAM_SERVER_NAME) String serverName) {
+ return getDiscoveredServerResponse(serverName, MediaType.APPLICATION_JSON);
+ }
+
+ private Response getDiscoveredServerResponse(String serverName, String mediaType) {
+ if(serverName == null || serverName.isEmpty()) {
+ return badRequestResponse("Server name must not be empty!");
+ }
+ try {
+ return okResponse(discoveredServerService.getDiscoveredServer(serverName), mediaType);
+ } catch (Exception e) {
+ // TODO: Log the exception
+ return errorResponse(e.getMessage());
+ }
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/GenericExceptionMapper.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/GenericExceptionMapper.java
new file mode 100644
index 00000000..dfe4c85c
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/GenericExceptionMapper.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.gateway.resources.v1_0;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.log4j.Logger;
+import org.gluster.storage.management.core.exceptions.GlusterValidationException;
+
+
+@Provider
+public class GenericExceptionMapper implements ExceptionMapper<Exception> {
+ private static final Logger logger = Logger.getLogger(GenericExceptionMapper.class);
+
+ /* (non-Javadoc)
+ * @see javax.ws.rs.ext.ExceptionMapper#toResponse(java.lang.Throwable)
+ */
+ @Override
+ public Response toResponse(Exception exception) {
+ ResponseBuilder builder;
+ if (exception instanceof GlusterValidationException) {
+ builder = Response.status(Response.Status.BAD_REQUEST);
+ } else {
+ builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR);
+ }
+
+ String errMsg = exception.getMessage();
+ if(errMsg == null) {
+ errMsg = "Following exception occurred : " + exception.getClass().getName();
+ StackTraceElement[] stackTrace = exception.getStackTrace();
+ if(stackTrace.length > 0) {
+ errMsg += " at [" + stackTrace[0].getClassName() + "][" + stackTrace[0].getLineNumber() + "]";
+ }
+ }
+
+ logger.error(errMsg, exception);
+
+ return builder.entity(errMsg).type(MediaType.TEXT_PLAIN).build();
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/GlusterServersResource.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/GlusterServersResource.java
new file mode 100644
index 00000000..1b9a3b71
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/GlusterServersResource.java
@@ -0,0 +1,343 @@
+/*******************************************************************************
+ * 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.gateway.resources.v1_0;
+
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_FSTYPE;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_MOUNTPOINT;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_SERVER_NAME;
+import static org.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_CLUSTER_NAME;
+import static org.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_DISK_NAME;
+import static org.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_SERVER_NAME;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DETAILS;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_INTERFACE;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_MAX_COUNT;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_NEXT_TO;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_PERIOD;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_TYPE;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_DISKS;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_FSTYPES;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_CLUSTERS;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_SERVERS;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_STATISTICS;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_TASKS;
+import static org.gluster.storage.management.core.constants.RESTConstants.STATISTICS_TYPE_CPU;
+import static org.gluster.storage.management.core.constants.RESTConstants.STATISTICS_TYPE_MEMORY;
+import static org.gluster.storage.management.core.constants.RESTConstants.STATISTICS_TYPE_NETWORK;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.gluster.storage.management.core.constants.GlusterConstants;
+import org.gluster.storage.management.core.constants.RESTConstants;
+import org.gluster.storage.management.core.exceptions.GlusterValidationException;
+import org.gluster.storage.management.core.model.GlusterServer;
+import org.gluster.storage.management.core.model.ServerStats;
+import org.gluster.storage.management.core.model.TaskStatus;
+import org.gluster.storage.management.core.response.FsTypeListResponse;
+import org.gluster.storage.management.core.response.GlusterServerListResponse;
+import org.gluster.storage.management.core.response.ServerNameListResponse;
+import org.gluster.storage.management.gateway.data.ClusterInfo;
+import org.gluster.storage.management.gateway.services.ClusterService;
+import org.gluster.storage.management.gateway.services.GlusterServerService;
+import org.gluster.storage.management.gateway.tasks.InitializeDiskTask;
+import org.gluster.storage.management.gateway.utils.CpuStatsFactory;
+import org.gluster.storage.management.gateway.utils.MemoryStatsFactory;
+import org.gluster.storage.management.gateway.utils.NetworkStatsFactory;
+import org.gluster.storage.management.gateway.utils.StatsFactory;
+import org.springframework.stereotype.Component;
+
+import com.sun.jersey.api.core.InjectParam;
+import com.sun.jersey.spi.resource.Singleton;
+
+@Component
+@Singleton
+@Path(RESOURCE_PATH_CLUSTERS + "/{" + PATH_PARAM_CLUSTER_NAME + "}/" + RESOURCE_SERVERS)
+public class GlusterServersResource extends AbstractResource {
+
+ public static final String HOSTNAMETAG = "hostname:";
+
+ @InjectParam
+ private TasksResource taskResource;
+
+ @InjectParam
+ private ClusterService clusterService;
+
+ @InjectParam
+ private CpuStatsFactory cpuStatsFactory;
+
+ @InjectParam
+ private MemoryStatsFactory memoryStatsFactory;
+
+ @InjectParam
+ private NetworkStatsFactory networkStatsFactory;
+
+ @InjectParam
+ private GlusterServerService glusterServerService;
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getGlusterServersJSON(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @QueryParam(QUERY_PARAM_DETAILS) Boolean details, @QueryParam(QUERY_PARAM_MAX_COUNT) Integer maxCount,
+ @QueryParam(QUERY_PARAM_NEXT_TO) String previousServerName) {
+ return getGlusterServers(clusterName, MediaType.APPLICATION_JSON, details, maxCount, previousServerName);
+ }
+
+ @GET
+ @Produces(MediaType.APPLICATION_XML)
+ public Response getGlusterServersXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @QueryParam(QUERY_PARAM_DETAILS) Boolean details, @QueryParam(QUERY_PARAM_MAX_COUNT) Integer maxCount,
+ @QueryParam(QUERY_PARAM_NEXT_TO) String previousServerName) {
+ return getGlusterServers(clusterName, MediaType.APPLICATION_XML, details, maxCount, previousServerName);
+ }
+
+ private Response getGlusterServers(String clusterName, String mediaType, Boolean fetchDetails, Integer maxCount,
+ String previousServerName) {
+ if(fetchDetails == null) {
+ // by default, fetch the server list
+ fetchDetails = false;
+ }
+
+ List<GlusterServer> glusterServers = new ArrayList<GlusterServer>();
+
+ if (clusterName == null || clusterName.isEmpty()) {
+ return badRequestResponse("Cluster name must not be empty!");
+ }
+
+ ClusterInfo cluster = clusterService.getCluster(clusterName);
+ if (cluster == null) {
+ return notFoundResponse("Cluster [" + clusterName + "] not found!");
+ }
+
+ if (cluster.getServers().size() == 0) {
+ return okResponse(new GlusterServerListResponse(glusterServers), mediaType);
+ }
+
+ try {
+ glusterServers = glusterServerService.getGlusterServers(clusterName, fetchDetails, maxCount, previousServerName);
+ } catch (Exception e) {
+ return errorResponse(e.getMessage());
+ }
+
+ if(fetchDetails) {
+ return okResponse(new GlusterServerListResponse(glusterServers), mediaType);
+ } else {
+ // no details to be fetched. Return list of server names.
+ return okResponse(new ServerNameListResponse(getServerNames(glusterServers)), mediaType);
+ }
+ }
+
+ private List<String> getServerNames(List<GlusterServer> glusterServers) {
+ List<String> serverNames = new ArrayList<String>();
+ for(GlusterServer server : glusterServers) {
+ serverNames.add(server.getName());
+ }
+ return serverNames;
+ }
+
+ @GET
+ @Path("{" + PATH_PARAM_SERVER_NAME + "}")
+ @Produces(MediaType.APPLICATION_XML)
+ public Response getGlusterServerXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_SERVER_NAME) String serverName) {
+ return getGlusterServerResponse(clusterName, serverName, MediaType.APPLICATION_XML);
+ }
+
+ @GET
+ @Path("{" + PATH_PARAM_SERVER_NAME + "}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getGlusterServerJSON(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_SERVER_NAME) String serverName) {
+ return getGlusterServerResponse(clusterName, serverName, MediaType.APPLICATION_JSON);
+ }
+
+ private Response getGlusterServerResponse(String clusterName, String serverName, String mediaType) {
+ try {
+ return okResponse(glusterServerService.getGlusterServer(clusterName, serverName, true), mediaType);
+ } catch (Exception e) {
+ return errorResponse(e.getMessage());
+ }
+ }
+
+ @POST
+ public Response addServer(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @FormParam(FORM_PARAM_SERVER_NAME) String serverName) {
+ return createdResponse(glusterServerService.addServerToCluster(clusterName, serverName));
+ }
+
+ @DELETE
+ @Path("{" + PATH_PARAM_SERVER_NAME + "}")
+ public Response removeServer(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_SERVER_NAME) String serverName) {
+ glusterServerService.removeServerFromCluster(clusterName, serverName);
+ return noContentResponse();
+ }
+
+ @GET
+ @Produces(MediaType.APPLICATION_XML)
+ @Path("{" + PATH_PARAM_SERVER_NAME + "}/" + RESOURCE_FSTYPES)
+ public FsTypeListResponse getFsTypes(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, @PathParam(PATH_PARAM_SERVER_NAME) String serverName) {
+ List<String> fsTypes = glusterServerService.getFsTypes(clusterName, serverName);
+ return new FsTypeListResponse(fsTypes);
+ }
+
+ @PUT
+ @Produces(MediaType.APPLICATION_XML)
+ @Path("{" + PATH_PARAM_SERVER_NAME + "}/" + RESOURCE_DISKS + "/{" + PATH_PARAM_DISK_NAME + "}")
+ public Response initializeDisk(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_SERVER_NAME) String serverName, @PathParam(PATH_PARAM_DISK_NAME) String diskName,
+ @FormParam(FORM_PARAM_FSTYPE) String fsType, @FormParam(FORM_PARAM_MOUNTPOINT) String mountPoint) {
+
+ if (clusterName == null || clusterName.isEmpty()) {
+ return badRequestResponse("Cluster name must not be empty!");
+ }
+
+ if (serverName == null || serverName.isEmpty()) {
+ return badRequestResponse("Server name must not be empty!");
+ }
+
+ if (diskName == null || diskName.isEmpty()) {
+ return badRequestResponse("Disk name must not be empty!");
+ }
+
+ if (fsType == null || fsType.isEmpty()) {
+ fsType = GlusterConstants.FSTYPE_DEFAULT;
+ // return badRequestResponse("Parameter [" + FORM_PARAM_FSTYPE + "] is missing in request!");
+ }
+
+ InitializeDiskTask initializeTask = new InitializeDiskTask(clusterService, clusterName, serverName, diskName, fsType, mountPoint);
+ try {
+ initializeTask.start();
+ // Check the initialize disk status
+ TaskStatus taskStatus = initializeTask.checkStatus();
+ initializeTask.getTaskInfo().setStatus(taskStatus);
+ taskResource.addTask(clusterName, initializeTask);
+
+ return acceptedResponse(RESTConstants.RESOURCE_PATH_CLUSTERS + "/" + clusterName + "/" + RESOURCE_TASKS + "/"
+ + initializeTask.getId());
+ } catch (Exception e) {
+ return errorResponse(e.getMessage());
+ }
+ }
+
+ @GET
+ @Produces(MediaType.APPLICATION_XML)
+ @Path(RESOURCE_STATISTICS)
+ public Response getAggregatedPerformanceDataXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @QueryParam(QUERY_PARAM_TYPE) String type, @QueryParam(QUERY_PARAM_PERIOD) String period) {
+ return getAggregaredPerformanceData(clusterName, type, period, MediaType.APPLICATION_XML);
+ }
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path(RESOURCE_STATISTICS)
+ public Response getAggregaredPerformanceDataJSON(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @QueryParam(QUERY_PARAM_TYPE) String type, @QueryParam(QUERY_PARAM_PERIOD) String period) {
+ return getAggregaredPerformanceData(clusterName, type, period, MediaType.APPLICATION_JSON);
+ }
+
+ @GET
+ @Produces(MediaType.APPLICATION_XML)
+ @Path("{" + PATH_PARAM_SERVER_NAME + "}/" + RESOURCE_STATISTICS)
+ public Response getPerformanceDataXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, @PathParam(PATH_PARAM_SERVER_NAME) String serverName,
+ @QueryParam(QUERY_PARAM_TYPE) String type, @QueryParam(QUERY_PARAM_PERIOD) String period,
+ @QueryParam(QUERY_PARAM_INTERFACE) String networkInterface) {
+ return getPerformanceData(clusterName, serverName, type, period, networkInterface, MediaType.APPLICATION_XML);
+ }
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("{" + PATH_PARAM_SERVER_NAME + "}/" + RESOURCE_STATISTICS)
+ public Response getPerformanceDataJSON(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, @PathParam(PATH_PARAM_SERVER_NAME) String serverName,
+ @QueryParam(QUERY_PARAM_TYPE) String type, @QueryParam(QUERY_PARAM_PERIOD) String period,
+ @QueryParam(QUERY_PARAM_INTERFACE) String networkInterface) {
+ return getPerformanceData(clusterName, serverName, type, period, networkInterface, MediaType.APPLICATION_JSON);
+ }
+
+ private Response getAggregaredPerformanceData(String clusterName, String type, String period, String mediaType) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ if (type == null || type.isEmpty()) {
+ throw new GlusterValidationException("Statistics type name must not be empty!");
+ }
+
+ if (period == null || period.isEmpty()) {
+ throw new GlusterValidationException("Statistics period name must not be empty! Valid values are 1d/1w/1m/1y");
+ }
+
+ ClusterInfo cluster = clusterService.getCluster(clusterName);
+ if (cluster == null) {
+ return notFoundResponse("Cluster [" + clusterName + "] not found!");
+ }
+
+ if (cluster.getServers().isEmpty()) {
+ // cluster is empty. return empty stats.
+ return okResponse(new ServerStats(), mediaType);
+ }
+
+ List<String> serverNames = getServerNames(glusterServerService.getGlusterServers(clusterName, false, null, null));
+ return okResponse(getStatsFactory(type).fetchAggregatedStats(serverNames, period), mediaType);
+ }
+
+ private Response getPerformanceData(String clusterName, String serverName, String type, String period, String networkInterface, String mediaType) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ if (serverName == null || serverName.isEmpty()) {
+ throw new GlusterValidationException("Server name must not be empty!");
+ }
+
+ if (type == null || type.isEmpty()) {
+ throw new GlusterValidationException("Statistics type name must not be empty!");
+ }
+
+ if (period == null || period.isEmpty()) {
+ throw new GlusterValidationException("Statistics period name must not be empty! Valid values are 1d/1w/1m/1y");
+ }
+
+ return okResponse(getStatsFactory(type).fetchStats(serverName, period, networkInterface), mediaType);
+ }
+
+ private StatsFactory getStatsFactory(String type) {
+ if(type.equals(STATISTICS_TYPE_CPU)) {
+ return cpuStatsFactory;
+ } else if(type.equals(STATISTICS_TYPE_MEMORY)) {
+ return memoryStatsFactory;
+ } else if(type.equals(STATISTICS_TYPE_NETWORK)) {
+ return networkStatsFactory;
+ } else {
+ throw new GlusterValidationException("Invalid server statistics type [" + type + "]. Valid values are ["
+ + STATISTICS_TYPE_CPU + ", " + STATISTICS_TYPE_NETWORK + ", " + STATISTICS_TYPE_MEMORY + "]");
+ }
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/KeysResource.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/KeysResource.java
new file mode 100644
index 00000000..3063c23e
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/KeysResource.java
@@ -0,0 +1,155 @@
+/**
+ * KeysResource.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.gateway.resources.v1_0;
+
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_KEYS;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.log4j.Logger;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.utils.FileUtil;
+import org.gluster.storage.management.core.utils.ProcessResult;
+import org.gluster.storage.management.core.utils.ProcessUtil;
+import org.gluster.storage.management.gateway.utils.SshUtil;
+
+import com.sun.jersey.multipart.FormDataParam;
+
+@Path(RESOURCE_PATH_KEYS)
+public class KeysResource extends AbstractResource {
+ private static final Logger logger = Logger.getLogger(KeysResource.class);
+ private ProcessUtil processUtil = new ProcessUtil();
+
+ @GET
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ public Response exportSshkeys() {
+ File archiveFile = new File(createSskKeyZipFile());
+ byte[] data = FileUtil.readFileAsByteArray(archiveFile);
+ archiveFile.delete();
+ return streamingOutputResponse(createStreamingOutput(data));
+ }
+
+ private String createSskKeyZipFile() {
+ String targetDir = FileUtil.getTempDirName();
+ String zipFile = targetDir + File.separator + "ssh-keys.tar";
+ String sourcePrivateKeyFile = SshUtil.PRIVATE_KEY_FILE.getAbsolutePath();
+ String sourcePublicKeyFile = SshUtil.PUBLIC_KEY_FILE.getAbsolutePath();
+ String targetPrivateKeyFile = targetDir + File.separator + SshUtil.PRIVATE_KEY_FILE.getName();
+ String targetPubKeyFile = targetDir + File.separator + SshUtil.PUBLIC_KEY_FILE.getName();
+
+ if (!SshUtil.PRIVATE_KEY_FILE.isFile()) {
+ throw new GlusterRuntimeException("No private key file [" + SshUtil.PRIVATE_KEY_FILE.getName() + "] found!");
+ }
+
+ if (!SshUtil.PUBLIC_KEY_FILE.isFile()) {
+ throw new GlusterRuntimeException("No public key file [" + SshUtil.PUBLIC_KEY_FILE.getName() + "] found!");
+ }
+
+ // Copy keys to temp folder
+ ProcessResult result = processUtil.executeCommand("cp", sourcePrivateKeyFile, targetPrivateKeyFile);
+ if (!result.isSuccess()) {
+ throw new GlusterRuntimeException("Failed to copy key files! [" + result.getOutput() + "]");
+ }
+ result = processUtil.executeCommand("cp", sourcePublicKeyFile, targetPubKeyFile);
+ if (!result.isSuccess()) {
+ throw new GlusterRuntimeException("Failed to copy key files! [" + result.getOutput() + "]");
+ }
+
+ // To compress the key files
+ result = processUtil.executeCommand("tar", "cvf", zipFile, "-C", targetDir, SshUtil.PRIVATE_KEY_FILE.getName(),
+ SshUtil.PUBLIC_KEY_FILE.getName());
+ if (!result.isSuccess()) {
+ throw new GlusterRuntimeException("Failed to compress key files! [" + result.getOutput() + "]");
+ }
+
+ // To remove the copied key files
+ try {
+ processUtil.executeCommand("rm", "-f", targetPrivateKeyFile, targetPubKeyFile); // Ignore the errors if any
+ } catch (Exception e) {
+ logger.warn(e.toString());
+ }
+ return zipFile;
+ }
+
+ @POST
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ public Response importSshKeys(@FormDataParam("file") InputStream uploadedInputStream) {
+ File uploadedFile = new File(System.getProperty("java.io.tmpdir") + File.separator + "keys.tar");
+ String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
+
+ writeToFile(uploadedInputStream, uploadedFile.getAbsolutePath());
+
+ // To backup existing SSH private and public keys, if exist.
+ if (SshUtil.PRIVATE_KEY_FILE.isFile()) {
+ if (!SshUtil.PRIVATE_KEY_FILE.renameTo(new File(SshUtil.PRIVATE_KEY_FILE.getAbsolutePath() + "-" + timestamp))) {
+ throw new GlusterRuntimeException("Unable to backup private key!");
+ }
+ }
+
+ if (SshUtil.PUBLIC_KEY_FILE.isFile()) {
+ if (!SshUtil.PUBLIC_KEY_FILE
+ .renameTo(new File(SshUtil.PUBLIC_KEY_FILE.getAbsolutePath() + "-" + timestamp))) {
+ throw new GlusterRuntimeException("Unable to backup public key!");
+ }
+ }
+ // Extract SSH private and public key files.
+ ProcessResult output = processUtil.executeCommand("tar", "xvf", uploadedFile.getAbsolutePath(), "-C",
+ SshUtil.SSH_AUTHORIZED_KEYS_DIR_LOCAL);
+ uploadedFile.delete();
+ if (!output.isSuccess()) {
+ String errMsg = "Error in importing SSH keys: [" + output.toString() + "]";
+ logger.error(errMsg);
+ throw new GlusterRuntimeException(errMsg);
+ }
+ return createdResponse("SSH Key imported successfully");
+ }
+
+ // save uploaded file to the file (with path)
+ private void writeToFile(InputStream inputStream, String toFile) {
+ try {
+ int read = 0;
+ byte[] bytes = new byte[1024];
+
+ OutputStream out = new FileOutputStream(new File(toFile));
+ while ((read = inputStream.read(bytes)) != -1) {
+ out.write(bytes, 0, read);
+ }
+ out.flush();
+ out.close();
+ } catch (IOException e) {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/TasksResource.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/TasksResource.java
new file mode 100644
index 00000000..b57232fe
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/TasksResource.java
@@ -0,0 +1,226 @@
+/**
+ * TaskResource.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.gateway.resources.v1_0;
+
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_OPERATION;
+import static org.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_CLUSTER_NAME;
+import static org.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_TASK_ID;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_CLUSTERS;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_TASKS;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.gluster.storage.management.core.constants.RESTConstants;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.exceptions.GlusterValidationException;
+import org.gluster.storage.management.core.model.Status;
+import org.gluster.storage.management.core.model.TaskInfo;
+import org.gluster.storage.management.core.response.TaskInfoListResponse;
+import org.gluster.storage.management.gateway.tasks.Task;
+import org.springframework.stereotype.Component;
+
+import com.sun.jersey.spi.resource.Singleton;
+
+@Path(RESOURCE_PATH_CLUSTERS + "/{" + PATH_PARAM_CLUSTER_NAME + "}/" + RESOURCE_TASKS)
+@Singleton
+@Component
+
+public class TasksResource extends AbstractResource {
+ private Map<String, Map<String, Task>> tasksMap = new HashMap<String, Map<String, Task>>();
+
+ public TasksResource() {
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public void addTask(String clusterName, Task task) {
+ Map clusterTaskMap = tasksMap.get(clusterName);
+ if(clusterTaskMap == null) {
+ clusterTaskMap = new HashMap<String, Task>();
+ tasksMap.put(clusterName, clusterTaskMap);
+ }
+ // Remove the task if already exist
+ if (clusterTaskMap.containsKey(task.getId())) {
+ removeTask(clusterName, task);
+ }
+ clusterTaskMap.put(task.getId(), task);
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void removeTask(String clusterName, Task task) {
+ Map clusterTaskMap = tasksMap.get(clusterName);
+ if (clusterTaskMap != null) {
+ clusterTaskMap.remove(task.getId());
+ }
+ }
+
+ public List<TaskInfo> getAllTasksInfo(String clusterName) {
+ Map<String, Task> clusterTasksMap = tasksMap.get(clusterName);
+ List<TaskInfo> allTasksInfo = new ArrayList<TaskInfo>();
+ if ( clusterTasksMap == null) {
+ return allTasksInfo;
+ }
+ for (Map.Entry<String, Task> entry : clusterTasksMap.entrySet()) {
+ checkTaskStatus(clusterName, entry.getKey());
+ allTasksInfo.add(entry.getValue().getTaskInfo()); // TaskInfo with latest status
+ }
+ return allTasksInfo;
+ }
+
+ public Task getTask(String clusterName, String taskId) {
+ Map<String, Task> clusterTasksMap = tasksMap.get(clusterName);
+ for (Map.Entry<String, Task> entry : clusterTasksMap.entrySet()) {
+ if (entry.getValue().getId().equals(taskId)) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ public List<Task> getAllTasks(String clusterName) {
+ Map<String, Task> clusterTasksMap = tasksMap.get(clusterName);
+ List<Task> tasks = new ArrayList<Task>();
+ for (Map.Entry<String, Task> entry : clusterTasksMap.entrySet()) {
+ checkTaskStatus(clusterName, entry.getKey());
+ tasks.add( (Task) entry.getValue());
+ }
+ return tasks;
+ }
+
+ @GET
+ @Produces(MediaType.APPLICATION_XML)
+ public Response getTasks(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName) {
+ try {
+ return okResponse(new TaskInfoListResponse(getAllTasksInfo(clusterName)), MediaType.APPLICATION_XML);
+ } catch (GlusterRuntimeException e) {
+ return errorResponse(e.getMessage());
+ }
+ }
+
+ @GET
+ @Path("/{" + PATH_PARAM_TASK_ID + "}")
+ @Produces(MediaType.APPLICATION_XML)
+ public Response getTaskStatus(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_TASK_ID) String taskId) {
+ try {
+ Task task = checkTaskStatus(clusterName, taskId);
+ return okResponse(task.getTaskInfo(), MediaType.APPLICATION_XML);
+ } catch (GlusterRuntimeException e) {
+ return errorResponse(e.getMessage());
+ }
+ }
+
+ private Task checkTaskStatus(String clusterName, String taskId) {
+ Task task = getTask(clusterName, taskId);
+ // No status check required if the task already complete or failure
+ if (task.getTaskInfo().getStatus().getCode() == Status.STATUS_CODE_FAILURE
+ || task.getTaskInfo().getStatus().getCode() == Status.STATUS_CODE_SUCCESS) {
+ return task;
+ }
+ task.getTaskInfo().setStatus(task.checkStatus());
+ return task;
+ }
+
+ @PUT
+ @Path("/{" + PATH_PARAM_TASK_ID + "}")
+ @Produces(MediaType.APPLICATION_XML)
+ public Response performTask(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_TASK_ID) String taskId, @FormParam(FORM_PARAM_OPERATION) String taskOperation) {
+ Task task = getTask(clusterName, taskId);
+
+ try {
+ if (taskOperation.equals(RESTConstants.TASK_RESUME)) {
+ task.resume();
+ } else if (taskOperation.equals(RESTConstants.TASK_PAUSE)) {
+ task.pause();
+ } else if (taskOperation.equals(RESTConstants.TASK_STOP)) {
+ // task.stop();
+ clearTask(clusterName, taskId, taskOperation); // Stop and remove from the task list
+ } else if (taskOperation.equals(RESTConstants.TASK_COMMIT)) {
+ task.commit();
+ }
+ return (Response) noContentResponse();
+ } catch(GlusterValidationException ve) {
+ return badRequestResponse(ve.getMessage());
+ } catch (GlusterRuntimeException e) {
+ return errorResponse(e.getMessage());
+ }
+ }
+
+ @DELETE
+ @Path("/{" + PATH_PARAM_TASK_ID + "}")
+ @Produces(MediaType.APPLICATION_XML)
+ public Response clearTask(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_TASK_ID) String taskId, @QueryParam(FORM_PARAM_OPERATION) String taskOperation) {
+ Task task = getTask(clusterName, taskId);
+ if (task == null) {
+ return notFoundResponse("Requested task not found!");
+ }
+
+ if(clusterName == null || clusterName.isEmpty()) {
+ return badRequestResponse("Parameter [" + PATH_PARAM_CLUSTER_NAME + "] is missing in request!");
+ }
+
+ if(taskOperation == null || taskOperation.isEmpty()) {
+ int taskStatus = task.getTaskInfo().getStatus().getCode();
+ if (taskStatus == Status.STATUS_CODE_SUCCESS || taskStatus == Status.STATUS_CODE_FAILURE) {
+ taskOperation = RESTConstants.TASK_DELETE;
+ } else {
+ taskOperation = RESTConstants.TASK_STOP;
+ }
+// return badRequestResponse("Parameter [" + FORM_PARAM_OPERATION + "] is missing in request!");
+ }
+
+ if(!taskOperation.equals(RESTConstants.TASK_STOP) && !taskOperation.equals(RESTConstants.TASK_DELETE)) {
+ return badRequestResponse("Invalid value [" + taskOperation + "] for parameter [" + FORM_PARAM_OPERATION
+ + "]");
+ }
+
+ try {
+ if (taskOperation.equals(RESTConstants.TASK_STOP)) {
+ task.stop();
+ // On successful, intentionally stopped task can be removed from task list also
+ taskOperation = RESTConstants.TASK_DELETE;
+ }
+
+ if (taskOperation.equals(RESTConstants.TASK_DELETE)) {
+ removeTask(clusterName, task);
+ }
+
+ return noContentResponse();
+ } catch (Exception e) {
+ return errorResponse(e.getMessage());
+ }
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/UsersResource.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/UsersResource.java
new file mode 100644
index 00000000..6594ed0f
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/UsersResource.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.gateway.resources.v1_0;
+
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_NEW_PASSWORD;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_OLD_PASSWORD;
+import static org.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_USER;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_USERS;
+
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.log4j.Logger;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.exceptions.GlusterValidationException;
+import org.gluster.storage.management.core.model.Status;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.dao.SaltSource;
+import org.springframework.security.authentication.encoding.PasswordEncoder;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.provisioning.JdbcUserDetailsManager;
+import org.springframework.stereotype.Component;
+
+import com.sun.jersey.spi.resource.Singleton;
+
+@Singleton
+@Component
+@Path(RESOURCE_PATH_USERS)
+public class UsersResource extends AbstractResource {
+ @Autowired
+ private JdbcUserDetailsManager jdbcUserService;
+
+ @Autowired
+ private PasswordEncoder passwordEncoder;
+
+ @Autowired
+ private SaltSource saltSource;
+
+ @Autowired
+ private UserDetailsService userDetailsService;
+
+ private static final Logger logger = Logger.getLogger(UsersResource.class);
+
+ @Path("{" + PATH_PARAM_USER + "}")
+ @GET
+ @Produces(MediaType.APPLICATION_XML)
+ public Response authenticateXML(@PathParam("user") String user) {
+ // success only if the user passed in query is same as the one passed in security header
+ // spring security would have already authenticated the user credentials
+ return getAuthenticationResponse(user, MediaType.APPLICATION_XML);
+ }
+
+ @Path("{" + PATH_PARAM_USER + "}")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response authenticateJSON(@PathParam("user") String user) {
+ // success only if the user passed in query is same as the one passed in security header
+ // spring security would have already authenticated the user credentials
+ return getAuthenticationResponse(user, MediaType.APPLICATION_JSON);
+ }
+
+ public Response getAuthenticationResponse(String user, String mediaType) {
+ return (SecurityContextHolder.getContext().getAuthentication().getName().equals(user) ? okResponse(
+ Status.STATUS_SUCCESS, mediaType) : unauthorizedResponse());
+ }
+
+ @Path("{" + PATH_PARAM_USER + "}")
+ @PUT
+ public Response changePassword(@PathParam(PATH_PARAM_USER) String username,
+ @FormParam(FORM_PARAM_OLD_PASSWORD) String oldPassword,
+ @FormParam(FORM_PARAM_NEW_PASSWORD) String newPassword) {
+ try {
+ Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+ String loggedInUser = ((UserDetails)auth.getPrincipal()).getUsername();
+ if(!loggedInUser.equals(username)) {
+ // Temporary check as we currently have only one user.
+ throw new GlusterValidationException("User [" + loggedInUser
+ + "] is not allowed to change password of user [" + username + "]!");
+ }
+
+ UserDetails user = userDetailsService.loadUserByUsername(username);
+ Object salt = saltSource.getSalt(user);
+
+ String actualOldPasswordEncoded = ((UserDetails)auth.getPrincipal()).getPassword();
+ String oldPasswordEncoded = passwordEncoder.encodePassword(oldPassword, salt);
+ if(!oldPasswordEncoded.equals(actualOldPasswordEncoded)) {
+ throw new GlusterValidationException("Invalid old password!");
+ }
+
+ String encodedNewPassword = passwordEncoder.encodePassword(newPassword, salt);
+ jdbcUserService.changePassword(oldPassword, encodedNewPassword);
+ } catch (Exception ex) {
+ String errMsg = "Could not change password. Error: [" + ex.getMessage() + "]";
+ logger.error(errMsg, ex);
+ throw new GlusterRuntimeException(errMsg);
+ }
+ return noContentResponse();
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/VolumesResource.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/VolumesResource.java
new file mode 100644
index 00000000..18ed0314
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/resources/v1_0/VolumesResource.java
@@ -0,0 +1,383 @@
+/**
+ * VolumesResource.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.gateway.resources.v1_0;
+
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_ACCESS_PROTOCOLS;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_AUTO_COMMIT;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_BRICKS;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_CIFS_ENABLE;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_CIFS_USERS;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_FIX_LAYOUT;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_FORCE;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_FORCED_DATA_MIGRATE;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_MIGRATE_DATA;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_OPERATION;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_REPLICA_COUNT;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_SOURCE;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_STRIPE_COUNT;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_TARGET;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_TRANSPORT_TYPE;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_VOLUME_NAME;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_VOLUME_OPTIONS;
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_VOLUME_TYPE;
+import static org.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_CLUSTER_NAME;
+import static org.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_VOLUME_NAME;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_BRICKS;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_BRICK_NAME;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DELETE_OPTION;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DOWNLOAD;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_FROM_TIMESTAMP;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_LINE_COUNT;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_LOG_SEVERITY;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_MAX_COUNT;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_NEXT_TO;
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_TO_TIMESTAMP;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_BRICKS;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_DEFAULT_OPTIONS;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_DOWNLOAD;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_LOGS;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_OPTIONS;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_CLUSTERS;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_TASKS;
+import static org.gluster.storage.management.core.constants.RESTConstants.RESOURCE_VOLUMES;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.log4j.Logger;
+import org.gluster.storage.management.core.constants.RESTConstants;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.exceptions.GlusterValidationException;
+import org.gluster.storage.management.core.model.Volume;
+import org.gluster.storage.management.core.model.VolumeLogMessage;
+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.response.LogMessageListResponse;
+import org.gluster.storage.management.core.response.VolumeListResponse;
+import org.gluster.storage.management.core.response.VolumeOptionInfoListResponse;
+import org.gluster.storage.management.core.utils.FileUtil;
+import org.gluster.storage.management.gateway.services.ClusterService;
+import org.gluster.storage.management.gateway.services.VolumeService;
+
+import com.sun.jersey.api.core.InjectParam;
+import com.sun.jersey.spi.resource.Singleton;
+
+@Singleton
+@Path(RESOURCE_PATH_CLUSTERS + "/{" + PATH_PARAM_CLUSTER_NAME + "}/" + RESOURCE_VOLUMES)
+public class VolumesResource extends AbstractResource {
+ private static final Logger logger = Logger.getLogger(VolumesResource.class);
+
+ @InjectParam
+ private ClusterService clusterService;
+
+ @InjectParam
+ private VolumeService volumeService;
+
+ @GET
+ @Produces({ MediaType.APPLICATION_XML })
+ public Response getVolumesXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @QueryParam(QUERY_PARAM_MAX_COUNT) Integer maxCount,
+ @QueryParam(QUERY_PARAM_NEXT_TO) String previousVolumeName) {
+ return okResponse(new VolumeListResponse(volumeService.getVolumes(clusterName, maxCount, previousVolumeName)),
+ MediaType.APPLICATION_XML);
+ }
+
+ @GET
+ @Produces({ MediaType.APPLICATION_JSON })
+ public Response getVolumesJSON(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @QueryParam(QUERY_PARAM_MAX_COUNT) Integer maxCount,
+ @QueryParam(QUERY_PARAM_NEXT_TO) String previousVolumeName) {
+ return okResponse(new VolumeListResponse(volumeService.getVolumes(clusterName, maxCount, previousVolumeName)),
+ MediaType.APPLICATION_JSON);
+ }
+
+ @POST
+ @Produces(MediaType.APPLICATION_XML)
+ public Response createVolume(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @FormParam(FORM_PARAM_VOLUME_NAME) String volumeName, @FormParam(FORM_PARAM_VOLUME_TYPE) String volumeType,
+ @FormParam(FORM_PARAM_TRANSPORT_TYPE) String transportType,
+ @FormParam(FORM_PARAM_REPLICA_COUNT) Integer replicaCount,
+ @FormParam(FORM_PARAM_STRIPE_COUNT) Integer stripeCount, @FormParam(FORM_PARAM_BRICKS) String bricks,
+ @FormParam(FORM_PARAM_ACCESS_PROTOCOLS) String accessProtocols,
+ @FormParam(FORM_PARAM_VOLUME_OPTIONS) String options, @FormParam(FORM_PARAM_CIFS_USERS) String cifsUsers) {
+ int count = 0;
+ if (clusterName == null || clusterName.isEmpty()) {
+ return badRequestResponse("Cluster name must not be empty!");
+ }
+
+ String missingParam = checkMissingParamsForCreateVolume(volumeName, volumeType, transportType, bricks,
+ accessProtocols, options);
+ if (missingParam != null) {
+ throw new GlusterValidationException("Parameter [" + missingParam + "] is missing in request!");
+ }
+
+ // For missing parameter, let default value
+ if (volumeType.equals(VOLUME_TYPE.REPLICATE.toString())
+ || volumeType.equals(VOLUME_TYPE.DISTRIBUTED_REPLICATE.toString())) {
+ count = (replicaCount == null) ? Volume.DEFAULT_REPLICA_COUNT : replicaCount;
+ } else if (volumeType.equals(VOLUME_TYPE.STRIPE.toString())
+ || volumeType.equals(VOLUME_TYPE.DISTRIBUTED_STRIPE.toString())) {
+ count = (stripeCount == null) ? Volume.DEFAULT_STRIPE_COUNT : stripeCount;
+ }
+
+ volumeService.createVolume(clusterName, volumeName, volumeType, transportType, count, bricks, accessProtocols,
+ options, cifsUsers);
+ return createdResponse(volumeName);
+ }
+
+ /**
+ * Returns name of the missing parameter if any. If all parameters are present,
+ */
+ private String checkMissingParamsForCreateVolume(String volumeName, String volumeType,
+ String transportType, String bricks, String accessProtocols,
+ String options) {
+
+ return (volumeName == null || volumeName.isEmpty()) ? FORM_PARAM_VOLUME_NAME :
+ (volumeType == null || volumeType.isEmpty()) ? FORM_PARAM_VOLUME_TYPE :
+ (transportType == null || transportType.isEmpty()) ? FORM_PARAM_TRANSPORT_TYPE :
+ (bricks == null || bricks.isEmpty()) ? FORM_PARAM_BRICKS :
+ (accessProtocols == null || accessProtocols.isEmpty()) ? FORM_PARAM_ACCESS_PROTOCOLS :
+ (options == null || options.isEmpty()) ? FORM_PARAM_VOLUME_OPTIONS :
+ null;
+ }
+
+ @GET
+ @Path("{" + PATH_PARAM_VOLUME_NAME + "}")
+ @Produces(MediaType.APPLICATION_XML)
+ public Response getVolumeXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName) {
+ return okResponse(volumeService.getVolume(clusterName, volumeName), MediaType.APPLICATION_XML);
+ }
+
+ @GET
+ @Path("{" + PATH_PARAM_VOLUME_NAME + "}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getVolumeJSON(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName) {
+ return okResponse(volumeService.getVolume(clusterName, volumeName), MediaType.APPLICATION_JSON);
+ }
+
+ @PUT
+ @Path("{" + PATH_PARAM_VOLUME_NAME + "}")
+ public Response performOperation(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName, @FormParam(FORM_PARAM_OPERATION) String operation,
+ @FormParam(FORM_PARAM_FIX_LAYOUT) Boolean isFixLayout,
+ @FormParam(FORM_PARAM_MIGRATE_DATA) Boolean isMigrateData,
+ @FormParam(FORM_PARAM_FORCED_DATA_MIGRATE) Boolean isForcedDataMigrate,
+ @FormParam(FORM_PARAM_CIFS_ENABLE) Boolean enableCifs, @FormParam(FORM_PARAM_CIFS_USERS) String cifsUsers,
+ @FormParam(FORM_PARAM_BRICKS) String bricks, @FormParam(FORM_PARAM_FORCE) Boolean force) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ if (volumeName == null || volumeName.isEmpty()) {
+ throw new GlusterValidationException("Volume name must not be empty!");
+ }
+
+ if (clusterService.getCluster(clusterName) == null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] not found!");
+ }
+
+ try {
+ if (operation.equals(RESTConstants.TASK_REBALANCE_START)) {
+ String taskId = volumeService.rebalanceStart(clusterName, volumeName, isFixLayout, isMigrateData, isForcedDataMigrate);
+ return acceptedResponse(RESTConstants.RESOURCE_PATH_CLUSTERS + "/" + clusterName + "/" + RESOURCE_TASKS
+ + "/" + taskId);
+ } else if (operation.equals(RESTConstants.TASK_REBALANCE_STOP)) {
+ volumeService.rebalanceStop(clusterName, volumeName);
+ } else if (operation.equals(RESTConstants.FORM_PARAM_CIFS_CONFIG)) {
+ Volume newVolume = volumeService.getVolume(clusterName, volumeName);
+ if (enableCifs) {
+ // After add/modify volume cifs users, start/restart the cifs service
+ volumeService.createCIFSUsers(clusterName, volumeName, cifsUsers);
+ if (newVolume.getStatus() == VOLUME_STATUS.ONLINE) {
+ volumeService.startCifsReExport(clusterName, volumeName);
+ }
+ } else {
+ // Stop the Cifs service and delete the users (!important)
+ if (newVolume.getStatus() == VOLUME_STATUS.ONLINE) {
+ volumeService.stopCifsReExport(clusterName, volumeName);
+ }
+ volumeService.deleteCifsUsers(clusterName, volumeName);
+ }
+ } else if (operation.equals(RESTConstants.TASK_LOG_ROTATE)) {
+ List<String> brickList = Arrays.asList(bricks.split(","));
+ volumeService.logRotate(clusterName, volumeName, brickList);
+ } else {
+ if (force == null) {
+ force = false;
+ }
+ volumeService.performVolumeOperation(clusterName, volumeName, operation, force);
+ }
+ return noContentResponse();
+ } catch(Exception e) {
+ return errorResponse(e.getMessage());
+ }
+ }
+
+ @DELETE
+ @Path("{" + PATH_PARAM_VOLUME_NAME + "}")
+ public Response deleteVolume(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName,
+ @QueryParam(QUERY_PARAM_DELETE_OPTION) Boolean deleteFlag) {
+ volumeService.deleteVolume(clusterName, volumeName, deleteFlag);
+
+ return noContentResponse();
+ }
+
+ @DELETE
+ @Path("{" + PATH_PARAM_VOLUME_NAME + "}/" + RESOURCE_BRICKS)
+ public Response removeBricks(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName, @QueryParam(QUERY_PARAM_BRICKS) String bricks,
+ @QueryParam(QUERY_PARAM_DELETE_OPTION) Boolean deleteFlag) {
+ volumeService.removeBricksFromVolume(clusterName, volumeName, bricks, deleteFlag);
+ return noContentResponse();
+ }
+
+ @POST
+ @Path("{" + PATH_PARAM_VOLUME_NAME + " }/" + RESOURCE_OPTIONS)
+ public Response setOption(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName,
+ @FormParam(RESTConstants.FORM_PARAM_OPTION_KEY) String key,
+ @FormParam(RESTConstants.FORM_PARAM_OPTION_VALUE) String value) {
+ volumeService.setVolumeOption(clusterName, volumeName, key, value);
+
+ return createdResponse(key);
+ }
+
+ @PUT
+ @Path("{" + PATH_PARAM_VOLUME_NAME + " }/" + RESOURCE_OPTIONS)
+ public Response resetOptions(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName) {
+ volumeService.resetVolumeOptions(clusterName, volumeName);
+ return noContentResponse();
+ }
+
+ @GET
+ @Path(RESOURCE_DEFAULT_OPTIONS)
+ @Produces(MediaType.APPLICATION_XML)
+ public VolumeOptionInfoListResponse getOptionsInfoXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName) {
+ return volumeService.getVolumeOptionsInfo(clusterName);
+ }
+
+ @GET
+ @Path(RESOURCE_DEFAULT_OPTIONS)
+ @Produces(MediaType.APPLICATION_JSON)
+ public VolumeOptionInfoListResponse getOptionsInfoJSON(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName) {
+ return volumeService.getVolumeOptionsInfo(clusterName);
+ }
+
+ @GET
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ @Path("{" + PATH_PARAM_VOLUME_NAME + "}/" + RESOURCE_LOGS + "/" + RESOURCE_DOWNLOAD)
+ public Response downloadLogs(@PathParam(PATH_PARAM_CLUSTER_NAME) final String clusterName,
+ @PathParam(PATH_PARAM_VOLUME_NAME) final String volumeName) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ return badRequestResponse("Cluster name must not be empty!");
+ }
+
+ if (volumeName == null || volumeName.isEmpty()) {
+ return badRequestResponse("Volume name must not be empty!");
+ }
+
+ if (clusterService.getCluster(clusterName) == null) {
+ return notFoundResponse("Cluster [" + clusterName + "] not found!");
+ }
+
+ try {
+ final Volume volume = volumeService.getVolume(clusterName, volumeName);
+ File archiveFile = new File(volumeService.downloadLogs(volume));
+ byte[] data = FileUtil.readFileAsByteArray(archiveFile);
+ archiveFile.delete();
+ return streamingOutputResponse(createStreamingOutput(data));
+ } catch (Exception e) {
+ logger.error("Volume [" + volumeName + "] doesn't exist in cluster [" + clusterName + "]! ["
+ + e.getStackTrace() + "]");
+ throw (GlusterRuntimeException) e;
+ }
+ }
+
+ @GET
+ @Path("{" + PATH_PARAM_VOLUME_NAME + "}/" + RESOURCE_LOGS)
+ @Produces(MediaType.APPLICATION_XML)
+ public Response getLogsXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName, @QueryParam(QUERY_PARAM_BRICK_NAME) String brickName,
+ @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_DOWNLOAD) Boolean download) {
+ return getLogs(clusterName, volumeName, brickName, severity, fromTimestamp, toTimestamp, lineCount, MediaType.APPLICATION_XML);
+ }
+
+ @GET
+ @Path("{" + PATH_PARAM_VOLUME_NAME + "}/" + RESOURCE_LOGS)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getLogsJSON(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName, @QueryParam(QUERY_PARAM_BRICK_NAME) String brickName,
+ @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_DOWNLOAD) Boolean download) {
+ return getLogs(clusterName, volumeName, brickName, severity, fromTimestamp, toTimestamp, lineCount, MediaType.APPLICATION_JSON);
+ }
+
+ private Response getLogs(String clusterName, String volumeName, String brickName, String severity,
+ String fromTimestamp, String toTimestamp, Integer lineCount, String mediaType) {
+ List<VolumeLogMessage> logMessages = volumeService.getLogs(clusterName, volumeName, brickName, severity,
+ fromTimestamp, toTimestamp, lineCount);
+
+ return okResponse(new LogMessageListResponse(logMessages), mediaType);
+ }
+
+ @POST
+ @Path("{" + PATH_PARAM_VOLUME_NAME + "}/" + RESOURCE_BRICKS)
+ public Response addBricks(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName, @FormParam(FORM_PARAM_BRICKS) String bricks) {
+ volumeService.addBricksToVolume(clusterName, volumeName, bricks);
+
+ return createdResponse(volumeName + "/" + RESOURCE_BRICKS);
+ }
+
+ @PUT
+ @Path("{" + PATH_PARAM_VOLUME_NAME + "}/" + RESOURCE_BRICKS)
+ public Response migrateBrick(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName,
+ @PathParam(PATH_PARAM_VOLUME_NAME) String volumeName, @FormParam(FORM_PARAM_SOURCE) String fromBrick,
+ @FormParam(FORM_PARAM_TARGET) String toBrick, @FormParam(FORM_PARAM_AUTO_COMMIT) Boolean autoCommit) {
+
+ String taskId = volumeService.migrateBrickStart(clusterName, volumeName, fromBrick, toBrick, autoCommit);
+
+ return acceptedResponse(RESTConstants.RESOURCE_PATH_CLUSTERS + "/" + clusterName + "/" + RESOURCE_TASKS + "/"
+ + taskId);
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/security/GlusterUserDetailsService.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/security/GlusterUserDetailsService.java
new file mode 100644
index 00000000..a73f2d61
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/security/GlusterUserDetailsService.java
@@ -0,0 +1,31 @@
+/**
+ * GlusterUserDetailsService.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.gateway.security;
+
+import org.springframework.security.core.userdetails.UserDetailsService;
+
+/**
+ *
+ */
+public interface GlusterUserDetailsService extends UserDetailsService {
+ void changePassword(String username, String password);
+}
+
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/security/UserAuthDao.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/security/UserAuthDao.java
new file mode 100644
index 00000000..b0473f0c
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/security/UserAuthDao.java
@@ -0,0 +1,57 @@
+/**
+ * UserAuthDao.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.gateway.security;
+
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.DefaultTransactionDefinition;
+
+
+/**
+ *
+ */
+public class UserAuthDao extends JdbcDaoImpl implements GlusterUserDetailsService {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.gluster.storage.management.gateway.security.GlusterUserDetailsService#changePassword(java.lang.String,
+ * java.lang.String)
+ */
+ @Override
+ public void changePassword(String username, String password) {
+ DataSourceTransactionManager txnManager = new DataSourceTransactionManager();
+ txnManager.setDataSource(getDataSource());
+
+ TransactionDefinition def = new DefaultTransactionDefinition();
+ TransactionStatus status = txnManager.getTransaction(def);
+ try {
+ getJdbcTemplate().update("UPDATE USERS SET PASSWORD = ? WHERE USERNAME = ?", password, username);
+ txnManager.commit(status);
+ } catch(Exception e) {
+ txnManager.rollback(status);
+ throw new GlusterRuntimeException("Exception while changing password of user [" + username + "]. Error: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/AbstractGlusterInterface.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/AbstractGlusterInterface.java
new file mode 100644
index 00000000..99935749
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/AbstractGlusterInterface.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * 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.gateway.services;
+
+import org.gluster.storage.management.gateway.utils.ServerUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+
+
+/**
+ * Abstract Gluster Interface - provides functionality common across all versions of GlusterFS e.g. version check.
+ */
+public abstract class AbstractGlusterInterface implements GlusterInterface {
+
+ @Autowired
+ protected ServerUtil serverUtil;
+
+ @Override
+ public String getVersion(String serverName) {
+ return serverUtil.executeOnServer(serverName, "gluster --version").split("\n")[0].replaceAll("glusterfs ", "")
+ .replaceAll(" built.*", "");
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/ClusterService.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/ClusterService.java
new file mode 100644
index 00000000..e7a5af3d
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/ClusterService.java
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * 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.gateway.services;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityTransaction;
+
+import org.apache.log4j.Logger;
+import org.gluster.storage.management.core.constants.CoreConstants;
+import org.gluster.storage.management.core.exceptions.ConnectionException;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.model.GlusterServer;
+import org.gluster.storage.management.core.utils.LRUCache;
+import org.gluster.storage.management.gateway.data.ClusterInfo;
+import org.gluster.storage.management.gateway.data.PersistenceDao;
+import org.gluster.storage.management.gateway.data.ServerInfo;
+import org.gluster.storage.management.gateway.utils.ServerUtil;
+import org.gluster.storage.management.gateway.utils.SshUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+
+/**
+ * Service class for functionality related to clusters
+ */
+@Component
+public class ClusterService {
+ @Autowired
+ private PersistenceDao<ClusterInfo> clusterDao;
+
+ @Autowired
+ private PersistenceDao<ServerInfo> serverDao;
+
+ @Autowired
+ private GlusterServerService glusterServerService;
+
+ @Autowired
+ private SshUtil sshUtil;
+
+ @Autowired
+ private ServerUtil serverUtil;
+
+ private LRUCache<String, GlusterServer> onlineServerCache = new LRUCache<String, GlusterServer>(3);
+
+ private static final Logger logger = Logger.getLogger(ClusterService.class);
+
+ public void addOnlineServer(String clusterName, GlusterServer server) {
+ onlineServerCache.put(clusterName, server);
+ }
+
+ public void removeOnlineServer(String clusterName) {
+ onlineServerCache.remove(clusterName);
+ }
+
+ // uses cache
+ public GlusterServer getOnlineServer(String clusterName, String exceptServerName) {
+ GlusterServer server = onlineServerCache.get(clusterName);
+ if (server != null && !server.getName().equalsIgnoreCase(exceptServerName)) {
+ return server;
+ }
+
+ return getNewOnlineServer(clusterName, exceptServerName);
+ }
+
+ public GlusterServer getNewOnlineServer(String clusterName) {
+ return getNewOnlineServer(clusterName, "");
+ }
+
+ public GlusterServer getOnlineServer(String clusterName) {
+ return getOnlineServer(clusterName, "");
+ }
+
+ // Doesn't use cache
+ public GlusterServer getNewOnlineServer(String clusterName, String exceptServerName) {
+ ClusterInfo cluster = getCluster(clusterName);
+ if (cluster == null) {
+ throw new GlusterRuntimeException("Cluster [" + clusterName + "] is not found!");
+ }
+
+ for (ServerInfo serverInfo : cluster.getServers()) {
+ GlusterServer server = new GlusterServer(serverInfo.getName());
+ try {
+ serverUtil.fetchServerDetails(server); // Online status come with server details
+ // server is online. add it to cache and return
+ if (server.isOnline() && !server.getName().equalsIgnoreCase(exceptServerName)) {
+ addOnlineServer(clusterName, server);
+ return server;
+ }
+ } catch (ConnectionException e) {
+ // server is offline. continue checking next one.
+ continue;
+ }
+ }
+
+ // no online server found.
+ throw new GlusterRuntimeException("No online server found in cluster [" + clusterName + "]");
+ }
+
+ public List<ClusterInfo> getAllClusters() {
+ return clusterDao.findAll();
+ }
+
+ public ClusterInfo getCluster(String clusterName) {
+ List<ClusterInfo> clusters = clusterDao.findBy("UPPER(name) = ?1", clusterName.toUpperCase());
+ if(clusters.size() == 0) {
+ return null;
+ }
+
+ return clusters.get(0);
+ }
+
+ public ClusterInfo getClusterForServer(String serverName) {
+ List<ServerInfo> servers = serverDao.findBy("UPPER(name) = ?1", serverName.toUpperCase());
+ if(servers.size() == 0) {
+ return null;
+ }
+
+ return servers.get(0).getCluster();
+ }
+
+ public void createCluster(String clusterName) {
+ EntityTransaction txn = clusterDao.startTransaction();
+ ClusterInfo cluster = new ClusterInfo();
+ cluster.setName(clusterName);
+
+ try {
+ clusterDao.save(cluster);
+ txn.commit();
+ } catch (RuntimeException e) {
+ txn.rollback();
+ logger.error("Exception while trying to save cluster [" + clusterName + "] : [" + e.getMessage() + "]", e);
+ throw e;
+ }
+ }
+
+ public void registerCluster(String clusterName, String knownServer) {
+ EntityTransaction txn = clusterDao.startTransaction();
+ ClusterInfo cluster = new ClusterInfo();
+ cluster.setName(clusterName);
+
+ GlusterServer server = new GlusterServer(knownServer);
+ try {
+ List<GlusterServer> glusterServers = glusterServerService.getGlusterServers(server.getName());
+ List<ServerInfo> servers = new ArrayList<ServerInfo>();
+ for(GlusterServer glusterServer : glusterServers) {
+ String serverName = glusterServer.getName();
+
+ serverUtil.fetchServerDetails(glusterServer);
+ if(glusterServer.isOnline()) {
+ checkAndSetupPublicKey(serverName);
+ }
+
+ ServerInfo serverInfo = new ServerInfo(serverName);
+ serverInfo.setCluster(cluster);
+ clusterDao.save(serverInfo);
+ servers.add(serverInfo);
+ }
+ cluster.setServers(servers);
+ clusterDao.save(cluster);
+ txn.commit();
+ } catch(RuntimeException e) {
+ logger.error("Error in registering cluster [" + clusterName + "] : " + e.getMessage(), e);
+ txn.rollback();
+ logger.error("Error in registering cluster [" + clusterName + "] : " + e.getMessage(), e);
+ throw e;
+ }
+ }
+
+ private void checkAndSetupPublicKey(String serverName) {
+ if(sshUtil.isPublicKeyInstalled(serverName)) {
+ return;
+ }
+
+ if(!sshUtil.hasDefaultPassword(serverName)) {
+ // public key not installed, default password doesn't work. can't install public key
+ throw new GlusterRuntimeException(
+ "Gluster Management Gateway uses the default password to set up keys on the server."
+ + CoreConstants.NEWLINE + "However it seems that the password on server [" + serverName
+ + "] has been changed manually." + CoreConstants.NEWLINE
+ + "Please reset it back to the standard default password and try again.");
+ }
+
+ // install public key (this will also disable password based ssh login)
+ sshUtil.installPublicKey(serverName);
+ }
+
+ public void unregisterCluster(String clusterName) {
+ ClusterInfo cluster = getCluster(clusterName);
+
+ if (cluster == null) {
+ throw new GlusterRuntimeException("Cluster [" + clusterName + "] doesn't exist!");
+ }
+
+ unregisterCluster(cluster);
+ }
+
+ public void unregisterCluster(ClusterInfo cluster) {
+ EntityTransaction txn = clusterDao.startTransaction();
+ try {
+ for(ServerInfo server : cluster.getServers()) {
+ clusterDao.delete(server);
+ }
+ cluster.getServers().clear();
+ clusterDao.update(cluster);
+ clusterDao.delete(cluster);
+ txn.commit();
+ } catch (RuntimeException e) {
+ logger.error("Error in unregistering cluster [" + cluster.getName() + "] : " + e.getMessage(), e);
+ txn.rollback();
+ throw e;
+ }
+ }
+
+ public void mapServerToCluster(String clusterName, String serverName) {
+ EntityTransaction txn = clusterDao.startTransaction();
+ ClusterInfo cluster = getCluster(clusterName);
+ ServerInfo server = new ServerInfo(serverName);
+ server.setCluster(cluster);
+ try {
+ clusterDao.save(server);
+ cluster.addServer(server);
+ clusterDao.update(cluster);
+ txn.commit();
+ } catch (Exception e) {
+ txn.rollback();
+ throw new GlusterRuntimeException("Couldn't create cluster-server mapping [" + clusterName + "]["
+ + serverName + "]! Error: " + e.getMessage(), e);
+ }
+ }
+
+ public void unmapServerFromCluster(String clusterName, String serverName) {
+ EntityTransaction txn = clusterDao.startTransaction();
+ ClusterInfo cluster = getCluster(clusterName);
+ List<ServerInfo> servers = cluster.getServers();
+ for(ServerInfo server : servers) {
+ if(server.getName().equalsIgnoreCase(serverName)) {
+ servers.remove(server);
+ clusterDao.delete(server);
+ break;
+ }
+ }
+ try {
+ clusterDao.update(cluster);
+ txn.commit();
+ } catch(Exception e) {
+ txn.rollback();
+ throw new GlusterRuntimeException("Couldn't unmap server [" + serverName + "] from cluster [" + clusterName
+ + "]! Error: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/DiscoveredServerService.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/DiscoveredServerService.java
new file mode 100644
index 00000000..95a8eecb
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/DiscoveredServerService.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * 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.gateway.services;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.model.Server;
+import org.gluster.storage.management.core.utils.ProcessUtil;
+import org.gluster.storage.management.gateway.utils.ServerUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+
+/**
+ *
+ */
+@Component
+public class DiscoveredServerService {
+ @Autowired
+ protected ServerUtil serverUtil;
+
+ private List<String> discoveredServerNames = new ArrayList<String>();
+ private static final Logger logger = Logger.getLogger(DiscoveredServerService.class);
+
+ public List<Server> getDiscoveredServerDetails() {
+ try {
+ List<Server> discoveredServers = Collections.synchronizedList(new ArrayList<Server>());
+ List<Thread> threads = createThreads(discoveredServers);
+ ProcessUtil.waitForThreads(threads);
+ return discoveredServers;
+ } catch (Exception e) {
+ String errMsg = "Exception while fetching details of discovered servers! Error: [" + e.getMessage() + "]";
+ logger.error(errMsg, e);
+ throw new GlusterRuntimeException(errMsg, e);
+ }
+ }
+
+ /**
+ * Creates threads that will run in parallel and fetch details of all discovered servers
+ * @param discoveredServers The list to be populated with details of discovered servers
+ * @return
+ * @throws InterruptedException
+ */
+ private List<Thread> createThreads(List<Server> discoveredServers) throws InterruptedException {
+ List<String> discoveredServerNames = getDiscoveredServerNames();
+ List<Thread> threads = new ArrayList<Thread>();
+ for (int i = discoveredServerNames.size() - 1; i >= 0; i--) {
+ Thread thread = new DiscoveredServerDetailsThread(discoveredServers, discoveredServerNames.get(i));
+ threads.add(thread);
+ thread.start();
+ if (i >= 5 && i % 5 == 0) {
+ // After every 5 servers, wait for 1 second so that we don't end up with too many running threads
+ Thread.sleep(1000);
+ }
+ }
+ return threads;
+ }
+
+ public List<String> getDiscoveredServerNames() {
+ return discoveredServerNames;
+ }
+
+ public void setDiscoveredServerNames(List<String> discoveredServerNames) {
+ synchronized (discoveredServerNames) {
+ this.discoveredServerNames = discoveredServerNames;
+ }
+ }
+
+ public void removeDiscoveredServer(String serverName) {
+ discoveredServerNames.remove(serverName);
+ }
+
+ public void addDiscoveredServer(String serverName) {
+ discoveredServerNames.add(serverName);
+ }
+
+ public Server getDiscoveredServer(String serverName) {
+ Server server = new Server(serverName);
+ serverUtil.fetchServerDetails(server);
+ return server;
+ }
+
+ public class DiscoveredServerDetailsThread extends Thread {
+ private List<Server> servers;
+ private String serverName;
+ private final Logger logger = Logger.getLogger(DiscoveredServerDetailsThread.class);
+
+ /**
+ * Private constructor called on each thread
+ * @param servers The list to be populated with fetched server details
+ * @param serverName Name of the server whose details should be fetched by this thread
+ */
+ private DiscoveredServerDetailsThread(List<Server> servers, String serverName) {
+ this.servers = servers;
+ this.serverName = serverName;
+ }
+
+ @Override
+ public void run() {
+ try {
+ logger.info("fetching details of discovered server [" + serverName + "] - start");
+ servers.add(getDiscoveredServer(serverName));
+ logger.info("fetching details of discovered server [" + serverName + "] - end");
+ } catch(Exception e) {
+ logger.warn("fetching details of discovered server [" + serverName + "] - error", e);
+ // eat the exception as we can't consider this server as a discovered server any more
+ }
+ }
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/Gluster323InterfaceService.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/Gluster323InterfaceService.java
new file mode 100644
index 00000000..ec7ae77e
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/Gluster323InterfaceService.java
@@ -0,0 +1,598 @@
+/*******************************************************************************
+ * 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.gateway.services;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.log4j.Logger;
+import org.gluster.storage.management.core.constants.CoreConstants;
+import org.gluster.storage.management.core.constants.GlusterConstants;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.model.Brick;
+import org.gluster.storage.management.core.model.Status;
+import org.gluster.storage.management.core.model.TaskStatus;
+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.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.response.VolumeOptionInfoListResponse;
+import org.gluster.storage.management.core.utils.StringUtil;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+
+/**
+ * Gluster Interface for GlusterFS version 3.2.3
+ */
+@Component
+@Lazy(value=true)
+public class Gluster323InterfaceService extends AbstractGlusterInterface {
+
+ private static final String VOLUME_NAME_PFX = "Volume Name:";
+ private static final String VOLUME_TYPE_PFX = "Type:";
+ private static final String VOLUME_STATUS_PFX = "Status:";
+ private static final String VOLUME_NUMBER_OF_BRICKS = "Number of Bricks:";
+ private static final String VOLUME_TRANSPORT_TYPE_PFX = "Transport-type:";
+ private static final String VOLUME_BRICKS_GROUP_PFX = "Bricks";
+ private static final String VOLUME_OPTIONS_RECONFIG_PFX = "Options Reconfigured";
+ private static final String VOLUME_LOG_LOCATION_PFX = "log file location:";
+ private static final String VOLUME_TYPE_DISTRIBUTE = "Distribute";
+ private static final String VOLUME_TYPE_REPLICATE = "Replicate";
+ private static final String VOLUME_TYPE_DISTRIBUTED_REPLICATTE = "Distributed-Replicate";
+ private static final String VOLUME_TYPE_STRIPE = "Stripe";
+ private static final String VOLUME_TYPE_DISTRIBUTED_STRIPE = "Distributed-Stripe";
+
+ private static final String BRICK_STATUS_SCRIPT = "get_brick_status.py";
+ private static final Logger logger = Logger.getLogger(Gluster323InterfaceService.class);
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#addServer(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void addServer(String existingServer, String newServer) {
+ serverUtil.executeOnServer(existingServer, "gluster peer probe " + newServer);
+ // reverse peer probe to ensure that host names appear in peer status on both sides
+ serverUtil.executeOnServer(newServer, "gluster peer probe " + existingServer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#startVolume(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void startVolume(String volumeName, String knownServer, Boolean force) {
+ serverUtil.executeOnServer(knownServer, "gluster volume start " + volumeName + ((force) ? " force" : ""));
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#stopVolume(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void stopVolume(String volumeName, String knownServer, Boolean force) {
+ serverUtil.executeOnServer(knownServer, "gluster --mode=script volume stop " + volumeName
+ + ((force) ? " force" : ""));
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#resetOptions(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void resetOptions(String volumeName, String knownServer) {
+ serverUtil.executeOnServer(knownServer, "gluster volume reset " + volumeName);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#createVolume(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Integer, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void createVolume(String knownServer, String volumeName, String volumeTypeStr, String transportTypeStr,
+ Integer count, String bricks, String accessProtocols, String options) {
+
+ // TODO: Disable NFS if required depending on value of accessProtocols
+ VOLUME_TYPE volType = Volume.getVolumeTypeByStr(volumeTypeStr);
+ String volTypeArg = null;
+ if (volType == VOLUME_TYPE.REPLICATE || volType == VOLUME_TYPE.DISTRIBUTED_REPLICATE) {
+ volTypeArg = "replica";
+ } else if (volType == VOLUME_TYPE.STRIPE || volType == VOLUME_TYPE.DISTRIBUTED_STRIPE) {
+ volTypeArg = "stripe";
+ }
+
+ String transportTypeArg = null;
+ TRANSPORT_TYPE transportType = Volume.getTransportTypeByStr(transportTypeStr);
+ transportTypeArg = (transportType == TRANSPORT_TYPE.ETHERNET) ? "tcp" : "rdma";
+
+ String command = prepareVolumeCreateCommand(volumeName, StringUtil.extractList(bricks, ","), count,
+ volTypeArg, transportTypeArg);
+
+ serverUtil.executeOnServer(knownServer, command);
+
+ try {
+ createOptions(volumeName, StringUtil.extractMap(options, ",", "="), knownServer);
+ } catch(Exception e) {
+ throw new GlusterRuntimeException(
+ "Volume created successfully, however following errors occurred while setting options: "
+ + CoreConstants.NEWLINE + e.getMessage());
+ }
+ }
+
+ private String prepareVolumeCreateCommand(String volumeName, List<String> brickDirectories, int count,
+ String volumeType, String transportTypeStr) {
+ StringBuilder command = new StringBuilder("gluster volume create " + volumeName + " ");
+ if (volumeType != null) {
+ command.append(volumeType + " " + count + " ");
+ }
+ command.append("transport " + transportTypeStr);
+ for (String brickDir : brickDirectories) {
+ command.append(" " + brickDir);
+ }
+ return command.toString();
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#createOptions(java.lang.String, java.util.Map, java.lang.String)
+ */
+ @Override
+ public void createOptions(String volumeName, Map<String, String> options, String knownServer) {
+ String errors = "";
+ if (options != null) {
+ for (Entry<String, String> option : options.entrySet()) {
+ String key = option.getKey();
+ String value = option.getValue();
+
+ try {
+ setOption(volumeName, key, value, knownServer);
+ } catch(Exception e) {
+ // append error
+ errors += e.getMessage() + CoreConstants.NEWLINE;
+ }
+ }
+ }
+ if (!errors.trim().isEmpty()) {
+ throw new GlusterRuntimeException("Errors while setting option(s) on volume [" + volumeName + "] : "
+ + errors.trim());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#setOption(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void setOption(String volumeName, String key, String value, String knownServer) {
+ serverUtil.executeOnServer(knownServer, "gluster volume set " + volumeName + " " + key + " " + "\""
+ + value + "\"");
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#deleteVolume(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void deleteVolume(String volumeName, String knownServer) {
+ serverUtil.executeOnServer(knownServer, "gluster --mode=script volume delete " + volumeName);
+ }
+
+ private String getVolumeInfo(String volumeName, String knownServer) {
+ return serverUtil.executeOnServer(knownServer, "gluster volume info " + volumeName, String.class);
+ }
+
+ private String getVolumeInfo(String knownServer) {
+ return serverUtil.executeOnServer(knownServer, "gluster volume info", String.class);
+ }
+
+ private boolean readVolumeType(Volume volume, String line) {
+ String volumeType = StringUtil.extractToken(line, VOLUME_TYPE_PFX);
+ if (volumeType != null) {
+ if (volumeType.equals(VOLUME_TYPE_DISTRIBUTE)) {
+ volume.setVolumeType(VOLUME_TYPE.DISTRIBUTE);
+
+ } else if (volumeType.equals(VOLUME_TYPE_REPLICATE)) {
+ volume.setVolumeType(VOLUME_TYPE.REPLICATE);
+ volume.setReplicaCount(Volume.DEFAULT_REPLICA_COUNT);
+
+ } else if ( volumeType.equals(VOLUME_TYPE_DISTRIBUTED_REPLICATTE) ){
+ volume.setVolumeType(VOLUME_TYPE.DISTRIBUTED_REPLICATE);
+ volume.setReplicaCount(Volume.DEFAULT_REPLICA_COUNT);
+
+ } else if ( volumeType.equals(VOLUME_TYPE_STRIPE) ){
+ volume.setVolumeType(VOLUME_TYPE.STRIPE);
+ volume.setReplicaCount(Volume.DEFAULT_REPLICA_COUNT);
+
+ } else if ( volumeType.equals(VOLUME_TYPE_DISTRIBUTED_STRIPE) ){
+ volume.setVolumeType(VOLUME_TYPE.DISTRIBUTED_STRIPE);
+ volume.setReplicaCount(Volume.DEFAULT_STRIPE_COUNT);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void readReplicaOrStripeCount(Volume volume, String line) {
+ if (StringUtil.extractToken(line, "x") != null) {
+ // expected formated of line is "Number of Bricks: 3 x 2 = 6"
+ int count = Integer.parseInt(line.split("x")[1].split("=")[0].trim());
+ if (volume.getVolumeType() == VOLUME_TYPE.STRIPE
+ || volume.getVolumeType() == VOLUME_TYPE.DISTRIBUTED_STRIPE) {
+ volume.setStripeCount(count);
+ } else if (volume.getVolumeType() == VOLUME_TYPE.REPLICATE
+ || volume.getVolumeType() == VOLUME_TYPE.DISTRIBUTED_REPLICATE) {
+ volume.setReplicaCount(count);
+ volume.setStripeCount(0);
+ }
+ }
+ return;
+ }
+
+ private boolean readVolumeStatus(Volume volume, String line) {
+ String volumeStatus = StringUtil.extractToken(line, VOLUME_STATUS_PFX);
+ if (volumeStatus != null) {
+ volume.setStatus(volumeStatus.equals("Started") ? VOLUME_STATUS.ONLINE : VOLUME_STATUS.OFFLINE);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean readTransportType(Volume volume, String line) {
+ String transportType = StringUtil.extractToken(line, VOLUME_TRANSPORT_TYPE_PFX);
+ if (transportType != null) {
+ volume.setTransportType(transportType.equals("tcp") ? TRANSPORT_TYPE.ETHERNET : TRANSPORT_TYPE.INFINIBAND);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean readBrick(Volume volume, String brickLine) {
+ BRICK_STATUS brickStatus;
+ if (brickLine.matches("Brick[0-9]+:.*")) {
+ // line: "Brick1: server1:/export/md0/volume-name"
+ String brickName = brickLine.split(": ")[1];
+ String[] brickParts = brickLine.split(":");
+ String serverName = brickParts[1].trim();
+ String brickDir = brickParts[2].trim();
+ //To get the brick status
+ brickStatus = getBrickStatus(serverName, volume.getName(), brickName);
+
+ addBrickToVolume(volume, serverName, brickDir, brickStatus);
+ return true;
+ }
+ return false;
+ }
+
+ private void addBrickToVolume(Volume volume, String serverName, String brickDir, BRICK_STATUS status) {
+ volume.addBrick(new Brick(serverName, status, brickDir));
+ }
+
+ // Do not throw exception, Gracefully handle as Offline brick.
+ private BRICK_STATUS getBrickStatus(String serverName, String volumeName, String brick){
+ try {
+ String output = serverUtil.executeScriptOnServer(serverName, BRICK_STATUS_SCRIPT + " " + volumeName
+ + " " + brick, String.class);
+ if (output.equals(CoreConstants.ONLINE)) {
+ return BRICK_STATUS.ONLINE;
+ } else {
+ return BRICK_STATUS.OFFLINE;
+ }
+ } catch(Exception e) { // Particularly interested on ConnectionExecption, if the server is offline
+ logger.warn("Exception while fetching brick status for [" + volumeName + "][" + brick
+ + "]. Marking it as offline!", e);
+ return BRICK_STATUS.OFFLINE;
+ }
+ }
+
+ private boolean readBrickGroup(String line) {
+ return StringUtil.extractToken(line, VOLUME_BRICKS_GROUP_PFX) != null;
+ }
+
+ private boolean readOptionReconfigGroup(String line) {
+ return StringUtil.extractToken(line, VOLUME_OPTIONS_RECONFIG_PFX) != null;
+ }
+
+ private boolean readOption(Volume volume, String line) {
+ if (line.matches("^[^:]*:.*$")) {
+ int index = line.indexOf(':');
+ volume.setOption(line.substring(0, index).trim(), line.substring(index + 1, line.length()).trim());
+
+ if (line.substring(0, index).trim().equals(Volume.OPTION_NFS_DISABLE)) {
+ if (line.substring(index + 1, line.length()).trim().equals(GlusterConstants.ON)) {
+ volume.disableNFS();
+ } else {
+ volume.enableNFS();
+ }
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#getVolume(java.lang.String, java.lang.String)
+ */
+ @Override
+ public Volume getVolume(String volumeName, String knownServer) {
+ return parseVolumeInfo(getVolumeInfo(volumeName, knownServer)).get(0);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#getAllVolumes(java.lang.String)
+ */
+ @Override
+ public List<Volume> getAllVolumes(String knownServer) {
+ return parseVolumeInfo(getVolumeInfo(knownServer));
+ }
+
+ private List<Volume> parseVolumeInfo(String volumeInfoText) {
+ List<Volume> volumes = new ArrayList<Volume>();
+ boolean isBricksGroupFound = false;
+ boolean isOptionReconfigFound = false;
+ Volume volume = null;
+
+ for (String line : volumeInfoText.split(CoreConstants.NEWLINE)) {
+ String volumeName = StringUtil.extractToken(line, VOLUME_NAME_PFX);
+ if (volumeName != null) {
+ if (volume != null) {
+ volumes.add(volume);
+ }
+
+ // prepare next volume to be read
+ volume = new Volume();
+ volume.setName(volumeName);
+ isBricksGroupFound = isOptionReconfigFound = false;
+ continue;
+ }
+
+ if (readVolumeType(volume, line))
+ continue;
+ if (StringUtil.extractToken(line, VOLUME_NUMBER_OF_BRICKS) != null) {
+ readReplicaOrStripeCount(volume, line);
+ }
+ if (readVolumeStatus(volume, line))
+ continue;
+ if (readTransportType(volume, line))
+ continue;
+ if (readBrickGroup(line)) {
+ isBricksGroupFound = true;
+ continue;
+ }
+
+ if (isBricksGroupFound) {
+ if (readBrick(volume, line)) {
+ continue;
+ } else {
+ isBricksGroupFound = false;
+ }
+ }
+
+ if (readOptionReconfigGroup(line)) {
+ isOptionReconfigFound = true;
+ continue;
+ }
+
+ if (isOptionReconfigFound) {
+ if (readOption(volume, line)) {
+ continue;
+ } else {
+ isOptionReconfigFound = false;
+ }
+ }
+ }
+
+ // add the last read volume
+ if (volume != null) {
+ volumes.add(volume);
+ }
+
+ return volumes;
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#addBricks(java.lang.String, java.util.List, java.lang.String)
+ */
+ @Override
+ public void addBricks(String volumeName, List<String> bricks, String knownServer) {
+ StringBuilder command = new StringBuilder("gluster volume add-brick " + volumeName);
+ for (String brickDir : bricks) {
+ command.append(" " + brickDir);
+ }
+
+ serverUtil.executeOnServer(knownServer, command.toString());
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#getLogLocation(java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public String getLogLocation(String volumeName, String brickName, String knownServer) {
+ String command = "gluster volume log locate " + volumeName + " " + brickName;
+ String output = serverUtil.executeOnServer(knownServer, command, String.class);
+ if (output.startsWith(VOLUME_LOG_LOCATION_PFX)) {
+ return output.substring(VOLUME_LOG_LOCATION_PFX.length()).trim();
+ }
+
+ throw new GlusterRuntimeException("Couldn't parse output of command [" + command + "]. Output [" + output
+ + "] doesn't start with prefix [" + VOLUME_LOG_LOCATION_PFX + "]");
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#getLogFileNameForBrickDir(java.lang.String)
+ */
+ @Override
+ public String getLogFileNameForBrickDir(String serverName, String brickDir) {
+ String logFileName = brickDir;
+ if (logFileName.length() > 0 && logFileName.charAt(0) == '/') {
+ logFileName = logFileName.replaceFirst("/", "");
+ }
+ logFileName = logFileName.replaceAll("/", "-") + ".log";
+ return logFileName;
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#removeBricks(java.lang.String, java.util.List, java.lang.String)
+ */
+ @Override
+ public void removeBricks(String volumeName, List<String> bricks, String knownServer) {
+ StringBuilder command = new StringBuilder("gluster --mode=script volume remove-brick " + volumeName);
+ for (String brickDir : bricks) {
+ command.append(" " + brickDir);
+ }
+ serverUtil.executeOnServer(knownServer, command.toString());
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#removeServer(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void removeServer(String existingServer, String serverName) {
+ serverUtil.executeOnServer(existingServer, "gluster --mode=script peer detach " + serverName);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#checkRebalanceStatus(java.lang.String, java.lang.String)
+ */
+ @Override
+ public TaskStatus checkRebalanceStatus(String serverName, String volumeName) {
+ String command = "gluster volume rebalance " + volumeName + " status";
+ String output = serverUtil.executeOnServer(serverName, command, String.class).trim();
+ TaskStatus taskStatus = new TaskStatus();
+ if (output.matches("^rebalance completed.*")) {
+ taskStatus.setCode(Status.STATUS_CODE_SUCCESS);
+ } else if (output.matches(".*in progress.*")) {
+ taskStatus.setCode(Status.STATUS_CODE_RUNNING);
+ } else {
+ taskStatus.setCode(Status.STATUS_CODE_FAILURE);
+ }
+ taskStatus.setMessage(output);
+ return taskStatus;
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#stopRebalance(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void stopRebalance(String serverName, String volumeName) {
+ String command = "gluster volume rebalance " + volumeName + " stop";
+ serverUtil.executeOnServer(serverName, command);
+ }
+
+ /**
+ * Performs given Brick Migration (replace-brick) Operation on given volume
+ *
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ * @param volumeName
+ * Volume on which the Brick Migration Operation is to be executed
+ * @param fromBrick
+ * The source Brick (being replaced)
+ * @param toBrick
+ * The destination Brick (which is replacing the source Brick)
+ * @param operation
+ * @return
+ */
+ private String performBrickMigrationOperation(String serverName, String volumeName, String fromBrick,
+ String toBrick, String operation) {
+ String command = "gluster volume replace-brick " + volumeName + " " + fromBrick + " " + toBrick + " "
+ + operation;
+ return serverUtil.executeOnServer(serverName, command, String.class);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#executeBrickMigration(java.lang.String,
+ * java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void startBrickMigration(String serverName, String volumeName, String fromBrick, String toBrick) {
+ performBrickMigrationOperation(serverName, volumeName, fromBrick, toBrick, "start");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.services.GlusterInterface#pauseBrickMigration(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void pauseBrickMigration(String serverName, String volumeName, String fromBrick, String toBrick) {
+ performBrickMigrationOperation(serverName, volumeName, fromBrick, toBrick, "pause");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.services.GlusterInterface#stopBrickMigration(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void stopBrickMigration(String serverName, String volumeName, String fromBrick, String toBrick) {
+ performBrickMigrationOperation(serverName, volumeName, fromBrick, toBrick, "abort");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.services.GlusterInterface#commitBrickMigration(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void commitBrickMigration(String serverName, String volumeName, String fromBrick, String toBrick) {
+ performBrickMigrationOperation(serverName, volumeName, fromBrick, toBrick, "commit");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.services.GlusterInterface#checkBrickMigrationStatus(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public TaskStatus checkBrickMigrationStatus(String serverName, String volumeName, String fromBrick, String toBrick) {
+ String output = performBrickMigrationOperation(serverName, volumeName, fromBrick, toBrick, "status");
+
+ TaskStatus taskStatus = new TaskStatus();
+ if (output.matches("^Number of files migrated.*Migration complete$")
+ || output.matches("^Number of files migrated = 0 .*Current file=")) {
+ // Note: Workaround - if no file in the volume brick to migrate,
+ // Gluster CLI is not giving proper (complete) status
+ taskStatus.setCode(Status.STATUS_CODE_COMMIT_PENDING);
+ taskStatus.setMessage(output.replaceAll("Migration complete", "Commit pending"));
+ } else if (output.matches("^Number of files migrated.*Current file=.*")) {
+ taskStatus.setCode(Status.STATUS_CODE_RUNNING);
+ } else if (output.matches("^replace brick has been paused.*")) {
+ taskStatus.setCode(Status.STATUS_CODE_PAUSE);
+ } else {
+ taskStatus.setCode(Status.STATUS_CODE_FAILURE);
+ }
+
+ taskStatus.setMessage(output);
+ return taskStatus;
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#getVolumeOptionsInfo(java.lang.String)
+ */
+ @Override
+ public VolumeOptionInfoListResponse getVolumeOptionsInfo(String serverName) {
+ return serverUtil.executeOnServer(serverName, "gluster volume set help-xml", VolumeOptionInfoListResponse.class);
+ }
+
+ public void logRotate(String volumeName, List<String> brickList, String knownServer) {
+ if (brickList == null || brickList.size() > 0) {
+ for (String brickDir : brickList) {
+ serverUtil.executeOnServer(knownServer, "gluster volume log rotate " + volumeName + " " + brickDir);
+ }
+ } else {
+ serverUtil.executeOnServer(knownServer, "gluster volume log rotate " + volumeName);
+ }
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/GlusterInterface.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/GlusterInterface.java
new file mode 100644
index 00000000..71c9e081
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/GlusterInterface.java
@@ -0,0 +1,369 @@
+package org.gluster.storage.management.gateway.services;
+
+import java.util.List;
+import java.util.Map;
+
+import org.gluster.storage.management.core.model.TaskStatus;
+import org.gluster.storage.management.core.model.Volume;
+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.response.VolumeOptionInfoListResponse;
+
+
+/**
+ * Interface for interacting with GlusterFS. Every version of GlusterFS supported by the Gluster Management Gateway will
+ * have a corresponding implementation of this interface.
+ */
+public interface GlusterInterface {
+
+ /**
+ * Returns the GlusterFS version on given server.
+ *
+ * @param serverName
+ * Server on which Gluster version is to be checked.
+ * @return
+ */
+ public abstract String getVersion(String serverName);
+
+ /**
+ * Adds the new server to an existing cluster.
+ *
+ * @param existingServer
+ * Server part of the existing cluster.
+ * @param newServer
+ * Server to be added to the cluster.
+ */
+ public abstract void addServer(String existingServer, String newServer);
+
+ /**
+ * Removes given server from the cluster by executing appropriate Gluster command on given server.
+ *
+ * @param existingServer
+ * Server part of the existing cluster.
+ * @param serverName
+ * Server to be removed from the cluster.
+ */
+ public abstract void removeServer(String existingServer, String serverName);
+
+ /**
+ * Starts the given volume by executing appropriate Gluster command on given server.
+ *
+ * @param volumeName
+ * Volume to be started.
+ * @param serverName
+ * Server on which the Gluster command is to be executed. This server must be part of the cluster to
+ * which the volume belongs.
+ * @param force
+ * Flag indicating whether the "force" option should be used for starting the Volume. This is typically
+ * used when Volume is already started, but at least one of its bricks is offline, and results in
+ * bringing up the offline bricks.
+ */
+ public abstract void startVolume(String volumeName, String serverName, Boolean force);
+
+ /**
+ * Stops the given volume by executing appropriate Gluster command on given server.
+ *
+ * @param volumeName
+ * Volume to be stopped.
+ * @param serverName
+ * Server on which the Gluster command is to be executed. This server must be part of the cluster to
+ * which the volume belongs.
+ * @param force
+ * Flag indicating whether the Volume should be stopped forcefully. This is typically used if the regular
+ * stop option fails because of issues like rebalance / brick migration / geo-replication being in
+ * progress. This results in forcefully stopping the volume, leaving the other processes intact.
+ */
+ public abstract void stopVolume(String volumeName, String serverName, Boolean force);
+
+ /**
+ * Resets volume options on the given volume by executing appropriate Gluster command on given server.
+ *
+ * @param volumeName
+ * Volume on which options are to be reset.
+ * @param serverName
+ * Server on which the Gluster command is to be executed. This server must be part of the cluster to
+ * which the volume belongs.
+ */
+ public abstract void resetOptions(String volumeName, String serverName);
+
+ /**
+ * Creates a volume on given volume using given properties.
+ *
+ * @param serverName
+ * Server on which the Gluster command for creating the volume will be executed. This must be part of the
+ * cluster in which the volume is to be created.
+ * @param volumeName
+ * Name of the volume.
+ * @param volumeType
+ * Type of the volume e.g. DISTRIBUTE, REPLICATE, STRIPE, etc. See {@link VOLUME_TYPE} for full list of
+ * valid values.
+ * @param transportType
+ * Transport type of the volume e.g. ETHERNET. See {@link TRANSPORT_TYPE} for full list of valid values.
+ * @param replOrStripeCount
+ * Replica Count or Stripe count depending on the volume type. Ignored in case of pure distribute volumes
+ * (no replicate, no stripe).
+ * @param bricks
+ * Comma separated list of volume brick directories in following format: <br>
+ * server1:dir1,server2:dir2,server3:dir3,...,servern:dirn
+ * @param accessProtocols
+ * Optional parameter indicating access protocols to be enabled for the volume. If empty/null, GLUSTERFS
+ * and NFS will be enabled.
+ * @param options
+ * A comma separated list of volume options to be set on the newly created volume in following format: <br>
+ * key1=value1,key2=value2,key3=value3,...,keyn=valuen
+ */
+ public abstract void createVolume(String serverName, String volumeName, String volumeType, String transportType,
+ Integer replOrStripeCount, String bricks, String accessProtocols, String options);
+
+ /**
+ * Creates / Sets the given options on the given volume by executing appropriate Gluster command on the given
+ * server.
+ *
+ * @param volumeName
+ * Volume on which the options are to be set.
+ * @param options
+ * Map containing the volume options to be set. Key = option key, Value = option value.
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ */
+ public abstract void createOptions(String volumeName, Map<String, String> options, String serverName);
+
+ /**
+ * Sets the given option on given volume by executing appropriate Gluster command on the given server.
+ *
+ * @param volumeName
+ * Volume on which the option is to be set.
+ * @param key
+ * Option key (name)
+ * @param value
+ * Option value
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ */
+ public abstract void setOption(String volumeName, String key, String value, String serverName);
+
+ /**
+ * Deletes the given volume by executing appropriate Gluster command on the given server.
+ *
+ * @param volumeName
+ * Volume to be deleted.
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ */
+ public abstract void deleteVolume(String volumeName, String serverName);
+
+ /**
+ * Fetches properties of the given Volume by executing appropriate Gluster command on the given server.
+ *
+ * @param volumeName
+ * Volume whose properties are to be fetched.
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ * @return A {@link Volume} object containing all properties of the given volume
+ */
+ public abstract Volume getVolume(String volumeName, String serverName);
+
+ /**
+ * Fetches the list of all volumes (along with their properties) by executing appropriate Gluster command on the
+ * given server.
+ *
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ * @return A list of {@link Volume} objects representing every volume present in the cluster to which the given
+ * server belongs.
+ */
+ public abstract List<Volume> getAllVolumes(String serverName);
+
+ /**
+ * Adds given list of bricks to given Volume by executing appropriate Gluster command on the
+ * given server.
+ *
+ * @param volumeName
+ * Volume to which the bricks are to be added.
+ * @param bricks
+ * List of bricks to be added, each in the format serverName:brickDirectory
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ */
+ public abstract void addBricks(String volumeName, List<String> bricks, String serverName);
+
+ /**
+ * Removes given list of bricks from given volume by executing appropriate Gluster command on the
+ * given server.
+ *
+ * @param volumeName
+ * Volume from which the bricks are to be removed
+ * @param bricks
+ * List of bricks to be removed, each in the format serverName:brickDirectory
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ */
+ public abstract void removeBricks(String volumeName, List<String> bricks, String serverName);
+
+ /**
+ * Returns the log location of given brick of given volume by executing appropriate Gluster command on the
+ * given server.
+ *
+ * @param volumeName
+ * Volume for which log location is to be fetched.
+ * @param brickName
+ * Brick of the volume for which log location is to be fetched.
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ * @return Full path of the log file location (directory) for the given Volume Brick.
+ */
+ public abstract String getLogLocation(String volumeName, String brickName, String serverName);
+
+ /**
+ * Returns the log file name for given brick directory.
+ *
+ * @param serverName
+ * Server to which the brick belongs
+ * @param brickDir
+ * Brick directory for which log file name is to be returned.
+ * @return The log file name (without path) for the given brick directory.
+ */
+ public abstract String getLogFileNameForBrickDir(String serverName, String brickDir);
+
+ /**
+ * Checks the status of "Rebalance" operation on given Volume by executing appropriate Gluster command on the
+ * given server.
+ *
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ * @param volumeName
+ * Volume whose rebalance status is to be checked.
+ * @return Object of {@link TaskStatus} representing the status of Volume Rebalance.
+ */
+ public abstract TaskStatus checkRebalanceStatus(String serverName, String volumeName);
+
+ /**
+ * Stops "Rebalance" operation running on given Volume by executing appropriate Gluster command on the
+ * given server.
+ *
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ * @param volumeName
+ * Volume whose Rebalance is to be stopped.
+ */
+ public abstract void stopRebalance(String serverName, String volumeName);
+
+ /**
+ * Starts Brick Migration (replace-brick) on given Volume by executing appropriate Gluster command on the
+ * given server.
+ *
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ * @param volumeName
+ * Volume whose Brick is to be migrated/replaced.
+ * @param fromBrick
+ * The source Brick (to be replaced).
+ * @param toBrick
+ * The destination Brick (will replace the source Brick).
+ */
+ public abstract void startBrickMigration(String serverName, String volumeName, String fromBrick, String toBrick);
+
+ /**
+ * Pauses Brick Migration (replace-brick) running on given Volume by executing appropriate Gluster command on the
+ * given server.
+ *
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ * @param volumeName
+ * Volume whose Brick is being migrated/replaced.
+ * @param fromBrick
+ * The source Brick (being replaced).
+ * @param toBrick
+ * The destination Brick (which is replacing the source Brick).
+ */
+ public abstract void pauseBrickMigration(String serverName, String volumeName, String fromBrick, String toBrick);
+
+ /**
+ * Aborts Brick Migration (replace-brick) running on given Volume by executing appropriate Gluster command on the
+ * given server.
+ *
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ * @param volumeName
+ * Volume whose Brick is being migrated/replaced.
+ * @param fromBrick
+ * The source Brick (being replaced).
+ * @param toBrick
+ * The destination Brick (which is replacing the source Brick)
+ */
+ public abstract void stopBrickMigration(String serverName, String volumeName, String fromBrick, String toBrick);
+
+ /**
+ * Commits Brick Migration (replace-brick) running on given Volume by executing appropriate Gluster command on the
+ * given server.
+ *
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ * @param volumeName
+ * Volume whose Brick is being migrated/replaced.
+ * @param fromBrick
+ * The source Brick (being replaced).
+ * @param toBrick
+ * The destination Brick (which is replacing the source Brick)
+ */
+ public abstract void commitBrickMigration(String serverName, String volumeName, String fromBrick, String toBrick);
+
+ /**
+ * Checks status of Brick Migration (replace-brick) running on given Volume by executing appropriate Gluster command
+ * on the given server.
+ *
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ * @param volumeName
+ * Volume whose Brick is being migrated/replaced.
+ * @param fromBrick
+ * The source Brick (being replaced).
+ * @param toBrick
+ * The destination Brick (which is replacing the source Brick)
+ * @return A {@link TaskStatus} object representing the status of Brick Migration
+ */
+ public abstract TaskStatus checkBrickMigrationStatus(String serverName, String volumeName, String fromBrick,
+ String toBrick);
+
+ /**
+ * Returns information about all the supported Volume Options by executing appropriate Gluster command
+ * on the given server.
+ *
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ * @return A {@link VolumeOptionInfoListResponse} object containing information about each and every supported
+ * Volume Option
+ */
+ public abstract VolumeOptionInfoListResponse getVolumeOptionsInfo(String serverName);
+
+ /**
+ * Rotates the logs for given Bricks of given Volume by executing appropriate Gluster command
+ * on the given server.
+ *
+ * @param volumeName
+ * Volume whose logs are to be rotated.
+ * @param brickList
+ * List of bricks whose logs are to be rotated, each in the format serverName:brickDirectory <br>
+ * This is an optional parameter. If null or empty, all logs of the Volume will be rotated.
+ * @param serverName
+ * The server on which the Gluster command will be executed. This must be part of the cluster to which
+ * the volume belongs.
+ */
+ public abstract void logRotate(String volumeName, List<String> brickList, String serverName);
+} \ No newline at end of file
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/GlusterInterfaceService.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/GlusterInterfaceService.java
new file mode 100644
index 00000000..07e152b8
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/GlusterInterfaceService.java
@@ -0,0 +1,256 @@
+/**
+ * GlusterInterfaceService.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.gateway.services;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.gluster.storage.management.core.model.TaskStatus;
+import org.gluster.storage.management.core.model.Volume;
+import org.gluster.storage.management.core.response.VolumeOptionInfoListResponse;
+import org.springframework.stereotype.Component;
+
+
+@Component
+public class GlusterInterfaceService extends AbstractGlusterInterface {
+ private HashMap<String, GlusterInterface> glusterInterfaces = new HashMap<String, GlusterInterface>();
+
+ /**
+ * Returns an instance of the Gluster Interface for given version of GlusterFS
+ * @param glusterFsVersion
+ * @return
+ */
+ private GlusterInterface getGlusterInterfaceForVersion(String glusterFsVersion) {
+ GlusterInterface glusterInterface = glusterInterfaces.get(glusterFsVersion);
+ if(glusterInterface != null) {
+ return glusterInterface;
+ }
+
+ glusterInterface = serverUtil.getBean(Gluster323InterfaceService.class);
+ glusterInterfaces.put(glusterFsVersion, glusterInterface);
+ return glusterInterface;
+ }
+
+ /**
+ * Returns an instance of Gluster Interface for the version of GlusterFS installed on given server.
+ *
+ * @param serverName
+ * @return
+ */
+ private GlusterInterface getGlusterInterface(String serverName) {
+ return getGlusterInterfaceForVersion(getVersion(serverName));
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#addServer(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void addServer(String existingServer, String newServer) {
+ getGlusterInterface(existingServer).addServer(existingServer, newServer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#startVolume(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void startVolume(String volumeName, String knownServer, Boolean force) {
+ getGlusterInterface(knownServer).startVolume(volumeName, knownServer, force);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#stopVolume(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void stopVolume(String volumeName, String knownServer, Boolean force) {
+ getGlusterInterface(knownServer).stopVolume(volumeName, knownServer, force);
+ }
+
+ public void logRotate(String volumeName, List<String> brickList, String knownServer) {
+ getGlusterInterface(knownServer).logRotate(volumeName, brickList, knownServer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#resetOptions(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void resetOptions(String volumeName, String knownServer) {
+ getGlusterInterface(knownServer).resetOptions(volumeName, knownServer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#createVolume(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Integer, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void createVolume(String knownServer, String volumeName, String volumeTypeStr, String transportTypeStr,
+ Integer count, String bricks, String accessProtocols, String options) {
+ getGlusterInterface(knownServer).createVolume(knownServer, volumeName, volumeTypeStr, transportTypeStr, count,
+ bricks, accessProtocols, options);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#createOptions(java.lang.String, java.util.Map, java.lang.String)
+ */
+ @Override
+ public void createOptions(String volumeName, Map<String, String> options, String knownServer) {
+ getGlusterInterface(knownServer).createOptions(volumeName, options, knownServer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#setOption(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void setOption(String volumeName, String key, String value, String knownServer) {
+ getGlusterInterface(knownServer).setOption(volumeName, key, value, knownServer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#deleteVolume(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void deleteVolume(String volumeName, String knownServer) {
+ getGlusterInterface(knownServer).deleteVolume(volumeName, knownServer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#getVolume(java.lang.String, java.lang.String)
+ */
+ @Override
+ public Volume getVolume(String volumeName, String knownServer) {
+ return getGlusterInterface(knownServer).getVolume(volumeName, knownServer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#getAllVolumes(java.lang.String)
+ */
+ @Override
+ public List<Volume> getAllVolumes(String knownServer) {
+ return getGlusterInterface(knownServer).getAllVolumes(knownServer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#addBricks(java.lang.String, java.util.List, java.lang.String)
+ */
+ @Override
+ public void addBricks(String volumeName, List<String> bricks, String knownServer) {
+ getGlusterInterface(knownServer).addBricks(volumeName, bricks, knownServer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#getLogLocation(java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public String getLogLocation(String volumeName, String brickName, String knownServer) {
+ return getGlusterInterface(knownServer).getLogLocation(volumeName, brickName, knownServer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#getLogFileNameForBrickDir(java.lang.String)
+ */
+ @Override
+ public String getLogFileNameForBrickDir(String serverName, String brickDir) {
+ return getGlusterInterface(serverName).getLogFileNameForBrickDir(serverName, brickDir);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#removeBricks(java.lang.String, java.util.List, java.lang.String)
+ */
+ @Override
+ public void removeBricks(String volumeName, List<String> bricks, String knownServer) {
+ getGlusterInterface(knownServer).removeBricks(volumeName, bricks, knownServer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#removeServer(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void removeServer(String existingServer, String serverName) {
+ getGlusterInterface(serverName).removeServer(existingServer, serverName);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#checkRebalanceStatus(java.lang.String, java.lang.String)
+ */
+ @Override
+ public TaskStatus checkRebalanceStatus(String serverName, String volumeName) {
+ return getGlusterInterface(serverName).checkRebalanceStatus(serverName, volumeName);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#stopRebalance(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void stopRebalance(String serverName, String volumeName) {
+ getGlusterInterface(serverName).stopRebalance(serverName, volumeName);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#executeBrickMigration(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void startBrickMigration(String onlineServerName, String volumeName, String fromBrick, String toBrick) {
+ getGlusterInterface(onlineServerName).startBrickMigration(onlineServerName, volumeName, fromBrick, toBrick);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.services.GlusterInterface#pauseBrickMigration(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void pauseBrickMigration(String serverName, String volumeName, String fromBrick, String toBrick) {
+ getGlusterInterface(serverName).pauseBrickMigration(serverName, volumeName, fromBrick, toBrick);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.services.GlusterInterface#stopBrickMigration(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void stopBrickMigration(String serverName, String volumeName, String fromBrick, String toBrick) {
+ getGlusterInterface(serverName).stopBrickMigration(serverName, volumeName, fromBrick, toBrick);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.services.GlusterInterface#commitBrickMigration(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public void commitBrickMigration(String serverName, String volumeName, String fromBrick, String toBrick) {
+ getGlusterInterface(serverName).commitBrickMigration(serverName, volumeName, fromBrick, toBrick);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.services.GlusterInterface#checkBrickMigrationStatus(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
+ */
+ @Override
+ public TaskStatus checkBrickMigrationStatus(String serverName, String volumeName, String fromBrick, String toBrick) {
+ return getGlusterInterface(serverName).checkBrickMigrationStatus(serverName, volumeName, fromBrick, toBrick);
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#getVolumeOptionsInfo(java.lang.String)
+ */
+ @Override
+ public VolumeOptionInfoListResponse getVolumeOptionsInfo(String serverName) {
+ return getGlusterInterface(serverName).getVolumeOptionsInfo(serverName);
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/GlusterServerService.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/GlusterServerService.java
new file mode 100644
index 00000000..58482958
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/GlusterServerService.java
@@ -0,0 +1,530 @@
+/*******************************************************************************
+ * 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.gateway.services;
+
+import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_SERVER_NAME;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.gluster.storage.management.core.constants.CoreConstants;
+import org.gluster.storage.management.core.exceptions.ConnectionException;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.exceptions.GlusterValidationException;
+import org.gluster.storage.management.core.model.GlusterServer;
+import org.gluster.storage.management.core.model.Server;
+import org.gluster.storage.management.core.model.Server.SERVER_STATUS;
+import org.gluster.storage.management.core.response.StringListResponse;
+import org.gluster.storage.management.core.utils.GlusterCoreUtil;
+import org.gluster.storage.management.core.utils.ProcessUtil;
+import org.gluster.storage.management.core.utils.StringUtil;
+import org.gluster.storage.management.gateway.data.ClusterInfo;
+import org.gluster.storage.management.gateway.data.ServerInfo;
+import org.gluster.storage.management.gateway.utils.ServerUtil;
+import org.gluster.storage.management.gateway.utils.SshUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+
+/**
+ *
+ */
+@Component
+public class GlusterServerService {
+ private static final String HOSTNAME_PFX = "Hostname:";
+ private static final String UUID_PFX = "Uuid:";
+ private static final String STATE_PFX = "State:";
+ private static final String GLUSTER_SERVER_STATUS_ONLINE = "Peer in Cluster (Connected)";
+ private static final String GLUSTERD_INFO_FILE = "/etc/glusterd/glusterd.info";
+
+ @Autowired
+ protected ServerUtil serverUtil;
+
+ @Autowired
+ private ClusterService clusterService;
+
+ @Autowired
+ private GlusterInterfaceService glusterUtil;
+
+ @Autowired
+ private SshUtil sshUtil;
+
+ @Autowired
+ private VolumeService volumeService;
+
+ @Autowired
+ private DiscoveredServerService discoveredServerService;
+
+ private static final Logger logger = Logger.getLogger(GlusterServerService.class);
+
+ public List<GlusterServer> getGlusterServers(String clusterName, boolean fetchDetails, Integer maxCount,
+ String previousServerName) {
+ List<GlusterServer> glusterServers;
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ try {
+ glusterServers = getGlusterServers(clusterName, onlineServer, fetchDetails, maxCount, previousServerName);
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) {
+ // online server has gone offline! try with a different one.
+ onlineServer = clusterService.getNewOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+ glusterServers = getGlusterServers(clusterName, onlineServer, fetchDetails, maxCount, previousServerName);
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+
+ }
+ return glusterServers;
+ }
+
+ private List<GlusterServer> getGlusterServers(String clusterName, GlusterServer onlineServer, boolean fetchDetails,
+ Integer maxCount, String previousServerName) {
+ List<GlusterServer> glusterServers;
+ try {
+ glusterServers = getGlusterServers(onlineServer.getName());
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) {
+ // online server has gone offline! try with a different one.
+ onlineServer = clusterService.getNewOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+ glusterServers = getGlusterServers(onlineServer.getName());
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+
+ // skip the servers by maxCount / previousServerName
+ glusterServers = GlusterCoreUtil.skipEntities(glusterServers, maxCount, previousServerName);
+
+ if (fetchDetails) {
+ String errMsg = fetchDetailsOfServers(Collections.synchronizedList(glusterServers));
+ if (!errMsg.isEmpty()) {
+ throw new GlusterRuntimeException("Couldn't fetch details for server(s): " + errMsg);
+ }
+ }
+ return glusterServers;
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#getGlusterServer(org.gluster.storage.management.core.model.GlusterServer, java.lang.String)
+ */
+ public GlusterServer getGlusterServer(String onlineServer, String serverName) {
+ List<GlusterServer> servers = getGlusterServers(onlineServer);
+ for (GlusterServer server : servers) {
+ if (server.getName().equalsIgnoreCase(serverName)) {
+ return server;
+ }
+ }
+
+ // Server not found. It's possible that the server name returned by glusterfs is actually IP address
+ // Hence fetch details of all servers and then compare the host names again.
+ String errMsg = fetchDetailsOfServers(Collections.synchronizedList(servers));
+ if (!errMsg.isEmpty()) {
+ throw new GlusterRuntimeException("Couldn't fetch details for server(s): " + errMsg);
+ }
+ for (GlusterServer server : servers) {
+ if (server.getName().equalsIgnoreCase(serverName)) {
+ return server;
+ }
+ }
+
+ // still not found!
+ return null;
+ }
+
+ private String getUuid(String serverName) {
+ return serverUtil.executeOnServer(serverName, "cat " + GLUSTERD_INFO_FILE, String.class).split("=")[1];
+ }
+
+ /* (non-Javadoc)
+ * @see org.gluster.storage.management.gateway.utils.GlusterInterface#getGlusterServers(org.gluster.storage.management.core.model.GlusterServer)
+ */
+ public List<GlusterServer> getGlusterServers(String knownServerName) {
+ String output = getPeerStatus(knownServerName);
+
+ GlusterServer knownServer = new GlusterServer(knownServerName);
+ knownServer.setUuid(getUuid(knownServerName));
+
+ List<GlusterServer> glusterServers = new ArrayList<GlusterServer>();
+ glusterServers.add(knownServer);
+
+ GlusterServer server = null;
+ boolean foundHost = false;
+ boolean foundUuid = false;
+ for (String line : output.split(CoreConstants.NEWLINE)) {
+ if (foundHost && foundUuid) {
+ // Host and UUID is found, we should look for state
+ String state = StringUtil.extractToken(line, STATE_PFX);
+ if (state != null) {
+ server.setStatus(state.contains(GLUSTER_SERVER_STATUS_ONLINE) ? SERVER_STATUS.ONLINE
+ : SERVER_STATUS.OFFLINE);
+ // Completed populating current server. Add it to the list
+ // and reset all related variables.
+ glusterServers.add(server);
+
+ foundHost = false;
+ foundUuid = false;
+ server = null;
+ }
+ } else if (foundHost) {
+ // Host is found, look for UUID
+ String uuid = StringUtil.extractToken(line, UUID_PFX);
+ if (uuid != null) {
+ server.setUuid(uuid);
+ foundUuid = true;
+ }
+ } else {
+ // Look for the next host
+ if (server == null) {
+ server = new GlusterServer();
+ }
+ String hostName = StringUtil.extractToken(line, HOSTNAME_PFX);
+ if (hostName != null) {
+ server.setName(hostName);
+ foundHost = true;
+ }
+ }
+
+ }
+ return glusterServers;
+ }
+
+ /**
+ * @param knownServer
+ * A known server on which the gluster command will be executed to fetch peer status
+ * @return Outout of the "gluster peer status" command
+ */
+ private String getPeerStatus(String knownServer) {
+ return serverUtil.executeOnServer(knownServer, "gluster peer status", String.class);
+ }
+
+ private String fetchDetailsOfServers(List<GlusterServer> glusterServers) {
+ try {
+ List<String> errors = Collections.synchronizedList(new ArrayList<String>());
+
+ List<Thread> threads = createThreads(glusterServers, errors);
+ ProcessUtil.waitForThreads(threads);
+
+ return prepareErrorMessage(errors);
+ } catch(InterruptedException e) {
+ String errMsg = "Exception while fetching details of servers! Error: [" + e.getMessage() + "]";
+ logger.error(errMsg, e);
+ throw new GlusterRuntimeException(errMsg, e);
+ }
+ }
+
+ private String prepareErrorMessage(List<String> errors) {
+ String errMsg = "";
+ for(String error : errors) {
+ if(!errMsg.isEmpty()) {
+ errMsg += CoreConstants.NEWLINE;
+ }
+ errMsg += error;
+ }
+
+ return errMsg;
+ }
+
+ /**
+ * Creates threads that will run in parallel and fetch details of given gluster servers
+ * @param discoveredServers The list to be populated with details of gluster servers
+ * @param errors List to be populated with errors if any
+ * @return
+ * @throws InterruptedException
+ */
+ private List<Thread> createThreads(List<GlusterServer> glusterServers, List<String> errors)
+ throws InterruptedException {
+ List<Thread> threads = new ArrayList<Thread>();
+ for (int i = glusterServers.size()-1; i >= 0 ; i--) {
+ Thread thread = new ServerDetailsThread(glusterServers.get(i), errors);
+ threads.add(thread);
+ thread.start();
+ if(i >= 5 && i % 5 == 0) {
+ // After every 5 servers, wait for 1 second so that we don't end up with too many running threads
+ Thread.sleep(1000);
+ }
+ }
+ return threads;
+ }
+
+ public class ServerDetailsThread extends Thread {
+ private List<String> errors;
+ private GlusterServer server;
+ private final Logger logger = Logger.getLogger(ServerDetailsThread.class);
+
+ /**
+ * Private constructor called on each thread
+ * @param server The server whose details are to be fetched by this thread
+ * @param errors
+ */
+ private ServerDetailsThread(GlusterServer server, List<String> errors) {
+ this.errors = errors;
+ this.server = server;
+ }
+
+ @Override
+ public void run() {
+ try {
+ logger.info("fetching details of server [" + server.getName() + "] - start");
+ serverUtil.fetchServerDetails(server);
+ logger.info("fetching details of server [" + server.getName() + "] - end");
+ } catch (Exception e) {
+ logger.error("fetching details of server [" + server.getName() + "] - error", e);
+ errors.add(server.getName() + " : [" + e.getMessage() + "]");
+ }
+ }
+ }
+
+ public GlusterServer getGlusterServer(String clusterName, String serverName, Boolean fetchDetails) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ if (serverName == null || serverName.isEmpty()) {
+ throw new GlusterValidationException("Server name must not be empty!");
+ }
+
+ ClusterInfo cluster = clusterService.getCluster(clusterName);
+ if (cluster == null) {
+ throw new GlusterRuntimeException("Cluster [" + clusterName + "] not found!");
+ }
+
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ return getGlusterServer(clusterName, serverName, onlineServer, fetchDetails);
+ }
+
+ private GlusterServer getGlusterServer(String clusterName, String serverName, GlusterServer onlineServer,
+ Boolean fetchDetails) {
+ GlusterServer server = null;
+ try {
+ server = getGlusterServer(onlineServer.getName(), serverName);
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) {
+ // online server has gone offline! try with a different one.
+ onlineServer = clusterService.getNewOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+ server = getGlusterServer(onlineServer.getName(), serverName);
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+
+ if (fetchDetails && server.isOnline()) {
+ serverUtil.fetchServerDetails(server);
+ }
+ return server;
+ }
+
+ public boolean isValidServer(String clusterName, String serverName) {
+ try {
+ GlusterServer server = getGlusterServer(clusterName, serverName, false);
+ return server != null;
+ } catch(Exception e) {
+ return false;
+ }
+ }
+
+ public void removeServerFromCluster(String clusterName, String serverName) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ if (serverName == null || serverName.isEmpty()) {
+ throw new GlusterValidationException("Server name must not be empty!");
+ }
+
+ ClusterInfo cluster = clusterService.getCluster(clusterName);
+ if (cluster == null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] not found!");
+ }
+
+ List<ServerInfo> servers = cluster.getServers();
+ if (servers == null || servers.isEmpty() || !containsServer(servers, serverName)) {
+ throw new GlusterValidationException("Server [" + serverName + "] is not attached to cluster ["
+ + clusterName + "]!");
+ }
+
+ if (servers.size() == 1) {
+ // Only one server mapped to the cluster, no "peer detach" required.
+ // remove the cached online server for this cluster if present
+ clusterService.removeOnlineServer(clusterName);
+ } else {
+ // get an online server that is not same as the server being removed
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName, serverName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online server found in cluster [" + clusterName + "]");
+ }
+
+ try {
+ glusterUtil.removeServer(onlineServer.getName(), serverName);
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) {
+ // online server has gone offline! try with a different one.
+ onlineServer = clusterService.getNewOnlineServer(clusterName, serverName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online server found in cluster [" + clusterName + "]");
+ }
+ glusterUtil.removeServer(onlineServer.getName(), serverName);
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+
+ clusterService.unmapServerFromCluster(clusterName, serverName);
+
+ // since the server is removed from the cluster, it is now available to be added to other clusters.
+ // Hence add it back to the discovered servers list.
+ discoveredServerService.addDiscoveredServer(serverName);
+
+ try {
+ if (serverUtil.isServerOnline(new Server(serverName))) {
+ volumeService.clearCifsConfiguration(clusterName, onlineServer.getName(), serverName);
+ }
+ } catch (Exception e1) {
+ throw new GlusterRuntimeException(
+ "Server removed from cluster, however deleting cifs configuration failed ! [ "
+ + e1.getMessage() + "]");
+ }
+ if (onlineServer.getName().equals(serverName)) {
+ // since the cached server has been removed from the cluster, remove it from the cache
+ clusterService.removeOnlineServer(clusterName);
+ }
+ }
+ }
+
+ private boolean containsServer(List<ServerInfo> servers, String serverName) {
+ for (ServerInfo server : servers) {
+ if (server.getName().toUpperCase().equals(serverName.toUpperCase())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Adds given server to cluster and returns its host name. e.g. If serverName passed is an IP address, this method
+ * will return the host name of the machine with given IP address.
+ *
+ * @param clusterName
+ * @param serverName
+ * @return
+ */
+ public String addServerToCluster(String clusterName, String serverName) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ if (serverName == null || serverName.isEmpty()) {
+ throw new GlusterValidationException("Parameter [" + FORM_PARAM_SERVER_NAME + "] is missing in request!");
+ }
+
+ ClusterInfo cluster = clusterService.getCluster(clusterName);
+ if (cluster == null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] not found!");
+ }
+
+ boolean publicKeyInstalled = sshUtil.isPublicKeyInstalled(serverName);
+ if (!publicKeyInstalled && !sshUtil.hasDefaultPassword(serverName)) {
+ // public key not installed, default password doesn't work. return with error.
+ throw new GlusterRuntimeException("Gluster Management Gateway uses the default password to set up keys on the server."
+ + CoreConstants.NEWLINE + "However it seems that the password on server [" + serverName
+ + "] has been changed manually." + CoreConstants.NEWLINE
+ + "Please reset it back to the standard default password and try again.");
+ }
+
+ String hostName = serverUtil.fetchHostName(serverName);
+ List<ServerInfo> servers = cluster.getServers();
+ if (servers != null && !servers.isEmpty()) {
+ // cluster has at least one existing server, so that peer probe can be performed
+ performAddServer(clusterName, hostName);
+ } else {
+ // this is the first server to be added to the cluster, which means no
+ // gluster CLI operation required. just add it to the cluster-server mapping
+ }
+
+ // add the cluster-server mapping
+ clusterService.mapServerToCluster(clusterName, hostName);
+
+ // since the server is added to a cluster, it should not more be considered as a
+ // discovered server available to other clusters
+ discoveredServerService.removeDiscoveredServer(hostName);
+
+ if (!publicKeyInstalled) {
+ try {
+ // install public key (this will also disable password based ssh login)
+ sshUtil.installPublicKey(hostName);
+ } catch (Exception e) {
+ throw new GlusterRuntimeException("Public key could not be installed on [" + hostName + "]! Error: ["
+ + e.getMessage() + "]");
+ }
+ }
+ return hostName;
+ }
+
+ private void performAddServer(String clusterName, String serverName) {
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online server found in cluster [" + clusterName + "]");
+ }
+
+ try {
+ glusterUtil.addServer(onlineServer.getName(), serverName);
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) {
+ // online server has gone offline! try with a different one.
+ onlineServer = clusterService.getNewOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online server found in cluster [" + clusterName + "]");
+ }
+ glusterUtil.addServer(onlineServer.getName(), serverName);
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+ }
+
+ public List<String> getFsTypes(String clusterName, String serverName) {
+ if (isValidServer(clusterName, serverName)) {
+ return serverUtil.getFsTypes(serverName);
+ } else {
+ throw new GlusterRuntimeException(serverName + " does not belong to the cluster [" + clusterName + "]");
+ }
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/VolumeService.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/VolumeService.java
new file mode 100644
index 00000000..4235d674
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/services/VolumeService.java
@@ -0,0 +1,984 @@
+/*******************************************************************************
+ * 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.gateway.services;
+
+import static org.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_BRICKS;
+import static org.gluster.storage.management.core.constants.RESTConstants.TASK_START;
+import static org.gluster.storage.management.core.constants.RESTConstants.TASK_STOP;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.text.SimpleDateFormat;
+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 org.apache.log4j.Logger;
+import org.gluster.storage.management.core.constants.CoreConstants;
+import org.gluster.storage.management.core.exceptions.ConnectionException;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.exceptions.GlusterValidationException;
+import org.gluster.storage.management.core.model.Brick;
+import org.gluster.storage.management.core.model.GlusterServer;
+import org.gluster.storage.management.core.model.Volume;
+import org.gluster.storage.management.core.model.VolumeLogMessage;
+import org.gluster.storage.management.core.model.Server.SERVER_STATUS;
+import org.gluster.storage.management.core.model.Volume.NAS_PROTOCOL;
+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.response.LogMessageListResponse;
+import org.gluster.storage.management.core.response.VolumeOptionInfoListResponse;
+import org.gluster.storage.management.core.utils.DateUtil;
+import org.gluster.storage.management.core.utils.FileUtil;
+import org.gluster.storage.management.core.utils.GlusterCoreUtil;
+import org.gluster.storage.management.core.utils.ProcessResult;
+import org.gluster.storage.management.core.utils.ProcessUtil;
+import org.gluster.storage.management.gateway.data.ClusterInfo;
+import org.gluster.storage.management.gateway.resources.v1_0.TasksResource;
+import org.gluster.storage.management.gateway.tasks.MigrateBrickTask;
+import org.gluster.storage.management.gateway.tasks.RebalanceVolumeTask;
+import org.gluster.storage.management.gateway.utils.ServerUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+
+/**
+ *
+ */
+@Component
+public class VolumeService {
+ private static final String ALL_SERVERS_FILE_NAME = "servers";
+ private static final String VOLUME_GET_CIFS_USERS_SCRIPT = "get_volume_user_cifs.py";
+ private static final String VOLUME_CIFS_GRUN_SCRIPT = "grun.py";
+ private static final String VOLUME_CREATE_CIFS_SCRIPT = "create_volume_cifs_all.py";
+ private static final String VOLUME_MODIFY_CIFS_SCRIPT = "update_volume_cifs_all.py";
+ private static final String VOLUME_START_CIFS_PEER_SCRIPT = "start_volume_cifs.py";
+ private static final String VOLUME_STOP_CIFS_PEER_SCRIPT = "stop_volume_cifs.py";
+ private static final String VOLUME_DELETE_CIFS_SCRIPT = "delete_volume_cifs_all.py";
+ private static final String VOLUME_BRICK_LOG_SCRIPT = "get_volume_brick_log.py";
+ private static final String VOLUME_DIRECTORY_CLEANUP_SCRIPT = "clear_volume_directory.py";
+ private static final String REMOVE_SERVER_VOLUME_CIFS_CONFIG = "remove_server_volume_cifs_config.py";
+ private static final String ALL_ONLINE_VOLUMES_FILE_NAME = "volumes";
+
+ @Autowired
+ private ClusterService clusterService;
+
+ @Autowired
+ private GlusterInterfaceService glusterUtil;
+
+ @Autowired
+ private GlusterServerService glusterServerService;
+
+ @Autowired
+ protected ServerUtil serverUtil;
+
+ // TODO: To be replaced with taskService
+ @Autowired
+ private TasksResource taskResource;
+
+ private static final Logger logger = Logger.getLogger(VolumeService.class);
+
+ public void addBricksToVolume(String clusterName, String volumeName, String bricks) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ if (volumeName == null || volumeName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ if (bricks == null || bricks.isEmpty()) {
+ throw new GlusterValidationException("Bricks must not be empty!");
+ }
+
+ if (clusterService.getCluster(clusterName) == null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] not found!");
+ }
+
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ List<String> brickList = Arrays.asList(bricks.split(","));
+ try {
+ glusterUtil.addBricks(volumeName, brickList, onlineServer.getName());
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) {
+ // online server has gone offline! try with a different one.
+ onlineServer = clusterService.getNewOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+ glusterUtil.addBricks(volumeName, brickList, onlineServer.getName());
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+ }
+
+ public Volume getVolume(String clusterName, String volumeName) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ if (clusterService.getCluster(clusterName) == null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] not found!");
+ }
+
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ Volume volume;
+ try {
+ volume = glusterUtil.getVolume(volumeName, onlineServer.getName());
+ // Collect the CIFS users if CIFS Re-exported
+ fetchVolumeCifsUsers(clusterName, volume);
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) {
+ // online server has gone offline! try with a different one.
+ onlineServer = clusterService.getNewOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+ volume = glusterUtil.getVolume(volumeName, onlineServer.getName());
+ // Collect the CIFS users if CIFS Re-exported
+ fetchVolumeCifsUsers(clusterName, volume);
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+ return volume;
+ }
+
+ public List<Volume> getVolumes(String clusterName, Integer maxCount, String previousVolumeName) {
+ List<Volume> volumes = getVolumes(clusterName);
+ // Skip the volumes by maxCount / previousServerName
+ volumes = GlusterCoreUtil.skipEntities(volumes, maxCount, previousVolumeName);
+ // fetch CIFS users of the volumes
+ fetchCifsUsers(clusterName, volumes);
+
+ return volumes;
+ }
+
+ private List<Volume> getVolumes(String clusterName) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ ClusterInfo cluster = clusterService.getCluster(clusterName);
+ if (cluster == null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] not found!");
+ }
+
+ if(cluster.getServers().size() == 0) {
+ // no server added yet. return an empty array.
+ return new ArrayList<Volume>();
+ }
+
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ try {
+ return glusterUtil.getAllVolumes(onlineServer.getName());
+ } catch (Exception e) {
+ // online server has gone offline! try with a different one.
+ onlineServer = clusterService.getNewOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ return glusterUtil.getAllVolumes(onlineServer.getName());
+ }
+ }
+
+ private void fetchVolumeCifsUsers(String clusterName, Volume volume) {
+ List<String> users = new ArrayList<String>();
+ try {
+ ProcessResult result = serverUtil
+ .executeGlusterScript(true, VOLUME_GET_CIFS_USERS_SCRIPT, volume.getName());
+ if (!result.isSuccess()) {
+ throw new GlusterRuntimeException(result.toString());
+ }
+ String output = result.getOutput().trim();
+ if (output.isEmpty()) {
+ volume.disableCifs();
+ } else {
+ users = Arrays.asList(output.split(CoreConstants.NEWLINE));
+ volume.enableCifs();
+ volume.setCifsUsers(users);
+ }
+ } catch (Exception e) {
+ throw new GlusterRuntimeException("Error in fetching CIFS users [" + volume.getName() + "]: "
+ + e.getMessage());
+ }
+ return;
+ }
+
+ private void fetchCifsUsers(String clusterName, List<Volume> volumes) {
+ for (Volume volume: volumes) {
+ fetchVolumeCifsUsers(clusterName, volume);
+ }
+ }
+
+ private File createOnlineServerList(String clusterName) {
+ String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
+ String clusterServersListFile = FileUtil.getTempDirName() + CoreConstants.FILE_SEPARATOR
+ + ALL_SERVERS_FILE_NAME + "_" + timestamp;
+
+ try {
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ List<GlusterServer> glusterServers = glusterServerService.getGlusterServers(onlineServer.getName());
+ File serversFile = new File(clusterServersListFile);
+ FileOutputStream fos = new FileOutputStream(serversFile);
+ for (GlusterServer server : glusterServers) {
+ if (server.getStatus() == SERVER_STATUS.ONLINE) {
+ fos.write((server.getName() + CoreConstants.NEWLINE).getBytes());
+ }
+ }
+ fos.close();
+ return serversFile;
+ } catch (Exception e) {
+ throw new GlusterRuntimeException("Error in preparing server list: [" + e.getMessage() + "]");
+ }
+ }
+
+ public void startCifsReExport(String clusterName, String volumeName) {
+ try {
+ File file = createOnlineServerList(clusterName);
+ ProcessResult result = serverUtil.executeGlusterScript(true, VOLUME_CIFS_GRUN_SCRIPT,
+ file.getAbsolutePath(), VOLUME_START_CIFS_PEER_SCRIPT, volumeName);
+ file.delete();
+ if (!result.isSuccess()) {
+ throw new GlusterRuntimeException(result.toString());
+ }
+ } catch (Exception e) {
+ throw new GlusterRuntimeException("Error in starting CIFS services for volume [" + volumeName + "]: "
+ + e.getMessage());
+ }
+ }
+
+ public void stopCifsReExport(String clusterName, String volumeName) {
+ try {
+ File file = createOnlineServerList(clusterName);
+ ProcessResult result = serverUtil.executeGlusterScript(true, VOLUME_CIFS_GRUN_SCRIPT,
+ file.getAbsolutePath(), VOLUME_STOP_CIFS_PEER_SCRIPT, volumeName);
+ file.delete();
+ if (!result.isSuccess()) {
+ throw new GlusterRuntimeException(result.toString());
+ }
+ } catch (Exception e) {
+ throw new GlusterRuntimeException("Error in stoping CIFS services for volume [" + volumeName + "]: "
+ + e.getMessage());
+ }
+ }
+
+ public void deleteCifsUsers(String clusterName, String volumeName) {
+ try {
+ File file = createOnlineServerList(clusterName);
+ ProcessResult result = serverUtil.executeGlusterScript(true, VOLUME_DELETE_CIFS_SCRIPT,
+ file.getAbsolutePath(), volumeName);
+ file.delete();
+ if (!result.isSuccess()) {
+ throw new GlusterRuntimeException(result.toString());
+ }
+ } catch (Exception e) {
+ throw new GlusterRuntimeException("Error in deleting CIFS configuration [" + volumeName + "]: "
+ + e.getMessage());
+ }
+ }
+
+ public void createCIFSUsers(String clusterName, String volumeName, String cifsUsers) {
+ try {
+ File file = createOnlineServerList(clusterName);
+ List<String> arguments = new ArrayList<String>();
+ arguments.add(file.getAbsolutePath());
+ arguments.add(volumeName);
+ arguments.addAll( Arrays.asList(cifsUsers.replaceAll(" ", "").split(",")));
+ ProcessResult result = serverUtil.executeGlusterScript(true, VOLUME_CREATE_CIFS_SCRIPT, arguments);
+ file.delete();
+ Volume volume = getVolume(clusterName, volumeName);
+ // If the volume service is already in running, create user may start CIFS re-export automatically.
+ if (volume.getStatus() == VOLUME_STATUS.ONLINE) {
+ startCifsReExport(clusterName, volumeName);
+ }
+ /*
+ * else { stopCifsReExport(clusterName, volumeName); }
+ */
+ if (!result.isSuccess()) {
+ throw new GlusterRuntimeException(result.toString());
+ }
+ } catch (Exception e) {
+ throw new GlusterRuntimeException("Error in creating CIFS configuration [" + volumeName + "]: "
+ + e.getMessage());
+ }
+ }
+
+ @Deprecated
+ public void modifyCIFSUsers(String clusterName, String volumeName, String cifsUsers) {
+ try {
+ File file = createOnlineServerList(clusterName);
+ List<String> arguments = new ArrayList<String>();
+ arguments.add(file.getAbsolutePath());
+ arguments.add(volumeName);
+ arguments.addAll( Arrays.asList(cifsUsers.split(",")));
+ ProcessResult result = serverUtil.executeGlusterScript(true, VOLUME_MODIFY_CIFS_SCRIPT, arguments);
+ file.delete();
+ if (!result.isSuccess()) {
+ throw new GlusterRuntimeException(result.toString());
+ }
+ } catch (Exception e) {
+ throw new GlusterRuntimeException("Error in updating CIFS configuration [" + volumeName + "]: "
+ + e.getMessage());
+ }
+ }
+
+ // To clear all the volume CIFS configurations from the server
+ public void clearCifsConfiguration(String clusterName, String onlineServerName, String serverName) {
+ File volumesFile = createOnlineVolumeList(clusterName, onlineServerName);
+ if (volumesFile == null) {
+ return;
+ }
+ try {
+ removeServerVolumeCifsConfig(serverName, volumesFile.getAbsolutePath());
+ volumesFile.delete();
+ } catch(Exception e) {
+ volumesFile.delete();
+ throw new GlusterRuntimeException("Error in clearing volume CIFS configuration: [" + e.getMessage() + "]");
+ }
+ }
+
+ private File createOnlineVolumeList(String clusterName, String onlineServerName) {
+ String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
+ String volumeListFileName = FileUtil.getTempDirName() + CoreConstants.FILE_SEPARATOR
+ + ALL_ONLINE_VOLUMES_FILE_NAME + "_" + timestamp;
+ try {
+ List<Volume> volumes = getVolumes(clusterName);
+ if (volumes == null || volumes.size() == 0) {
+ return null;
+ }
+ File volumesFile = new File(volumeListFileName);
+ FileOutputStream fos = new FileOutputStream(volumesFile);
+ for (Volume volume : volumes) {
+ if (volume.getStatus() == VOLUME_STATUS.ONLINE) {
+ fos.write((volume.getName() + CoreConstants.NEWLINE).getBytes());
+ }
+ }
+ fos.close();
+ return volumesFile;
+ } catch (Exception e) {
+ throw new GlusterRuntimeException("Error in preparing volume list: [" + e.getMessage() + "]");
+ }
+ }
+
+
+ public void removeServerVolumeCifsConfig(String serverName, String volumesFileName) {
+ ProcessResult result = serverUtil.executeGlusterScript(true, REMOVE_SERVER_VOLUME_CIFS_CONFIG, serverName,
+ volumesFileName);
+ if (!result.isSuccess()) {
+ throw new GlusterRuntimeException(result.toString());
+ }
+ }
+
+ public void createVolume(String clusterName, String volumeName, String volumeType, String transportType,
+ Integer count, String bricks, String accessProtocols, String options,
+ String cifsUsers) {
+ if (clusterService.getCluster(clusterName) == null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] not found!");
+ }
+
+ if ((volumeType.equals(VOLUME_TYPE.REPLICATE.toString()) || volumeType.equals(VOLUME_TYPE.DISTRIBUTED_REPLICATE
+ .toString())) && count <= 0) {
+ throw new GlusterValidationException("Replica count must be a positive integer");
+ }
+
+ if ((volumeType.equals(VOLUME_TYPE.STRIPE.toString()) || volumeType.equals(VOLUME_TYPE.DISTRIBUTED_STRIPE
+ .toString())) && count <= 0) {
+ throw new GlusterValidationException("Stripe count must be a positive integer");
+ }
+
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ try {
+ glusterUtil.createVolume(onlineServer.getName(), volumeName, volumeType, transportType, count,
+ bricks, accessProtocols, options);
+
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) {
+ // online server has gone offline! try with a different one.
+ onlineServer = clusterService.getNewOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+ glusterUtil.createVolume(onlineServer.getName(), volumeName, volumeType, transportType, count,
+ bricks, accessProtocols, options);
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+
+ List<String> nasProtocols = Arrays.asList(accessProtocols.split(","));
+ // if cifs enabled
+ if (nasProtocols.contains(NAS_PROTOCOL.CIFS.toString())) {
+ try {
+ createCIFSUsers(clusterName, volumeName, cifsUsers);
+ } catch (Exception e) {
+ throw new GlusterRuntimeException(CoreConstants.NEWLINE + e.getMessage());
+ }
+ }
+ }
+
+ public String downloadLogs(Volume volume) {
+ // create temporary directory
+ File tempDir = FileUtil.createTempDir();
+ String tempDirPath = tempDir.getPath();
+
+ for (Brick brick : volume.getBricks()) {
+ String logDir = glusterUtil.getLogLocation(volume.getName(), brick.getQualifiedName(),
+ brick.getServerName());
+ String logFileName = glusterUtil.getLogFileNameForBrickDir(brick.getServerName(), brick.getBrickDirectory());
+ String logFilePath = logDir + CoreConstants.FILE_SEPARATOR + logFileName;
+
+ serverUtil.getFileFromServer(brick.getServerName(), logFilePath, tempDirPath);
+
+ String fetchedLogFile = tempDirPath + File.separator + logFileName;
+ // append log file name with server name so that log files don't overwrite each other
+ // in cases where the brick log file names are same on multiple servers
+ String localLogFile = tempDirPath + File.separator + brick.getServerName() + "-" + logFileName;
+
+ FileUtil.renameFile(fetchedLogFile, localLogFile);
+ }
+
+ String gzipPath = FileUtil.getTempDirName() + CoreConstants.FILE_SEPARATOR + volume.getName() + "-logs.tar.gz";
+ ProcessUtil.executeCommand("tar", "czvf", gzipPath, "-C", tempDir.getParent(), tempDir.getName());
+
+ // delete the temp directory
+ FileUtil.recursiveDelete(tempDir);
+
+ return gzipPath;
+ }
+
+ public List<VolumeLogMessage> getLogs(String clusterName, String volumeName, String brickName, String severity,
+ String fromTimestamp, String toTimestamp, Integer lineCount) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ if (volumeName == null || volumeName.isEmpty()) {
+ throw new GlusterValidationException("Volume name must not be empty!");
+ }
+
+ if (clusterService.getCluster(clusterName) == null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] not found!");
+ }
+
+ if (lineCount == null || lineCount == 0) {
+ lineCount = 100;
+ }
+
+ List<VolumeLogMessage> logMessages = null;
+ Volume volume = getVolume(clusterName, volumeName);
+
+ if (brickName == null || brickName.isEmpty() || brickName.equals(CoreConstants.ALL)) {
+ logMessages = getLogsForAllBricks(volume, lineCount);
+ } else {
+ // fetch logs for given brick of the volume
+ for (Brick brick : volume.getBricks()) {
+ if (brick.getQualifiedName().equals(brickName)) {
+ logMessages = getBrickLogs(volume, brick, lineCount);
+ break;
+ }
+ }
+ }
+
+ filterLogsBySeverity(logMessages, severity);
+ filterLogsByTime(logMessages, fromTimestamp, toTimestamp);
+ return logMessages;
+ }
+
+ private void filterLogsByTime(List<VolumeLogMessage> logMessages, String fromTimestamp, String toTimestamp) {
+ Date fromTime = null, toTime = null;
+
+ if (fromTimestamp != null && !fromTimestamp.isEmpty()) {
+ fromTime = DateUtil.stringToDate(fromTimestamp);
+ }
+
+ if (toTimestamp != null && !toTimestamp.isEmpty()) {
+ toTime = DateUtil.stringToDate(toTimestamp);
+ }
+
+ List<VolumeLogMessage> messagesToRemove = new ArrayList<VolumeLogMessage>();
+ for (VolumeLogMessage logMessage : logMessages) {
+ Date logTimestamp = logMessage.getTimestamp();
+ if (fromTime != null && logTimestamp.before(fromTime)) {
+ messagesToRemove.add(logMessage);
+ continue;
+ }
+
+ if (toTime != null && logTimestamp.after(toTime)) {
+ messagesToRemove.add(logMessage);
+ }
+ }
+ logMessages.removeAll(messagesToRemove);
+ }
+
+ private void filterLogsBySeverity(List<VolumeLogMessage> logMessages, String severity) {
+ if (severity == null || severity.isEmpty()) {
+ return;
+ }
+
+ List<VolumeLogMessage> messagesToRemove = new ArrayList<VolumeLogMessage>();
+ for (VolumeLogMessage logMessage : logMessages) {
+ if (!logMessage.getSeverity().equals(severity)) {
+ messagesToRemove.add(logMessage);
+ }
+ }
+ logMessages.removeAll(messagesToRemove);
+ }
+
+ private List<VolumeLogMessage> getLogsForAllBricks(Volume volume, Integer lineCount) {
+ List<VolumeLogMessage> logMessages;
+ logMessages = new ArrayList<VolumeLogMessage>();
+ // fetch logs for every brick of the volume
+ for (Brick brick : volume.getBricks()) {
+ logMessages.addAll(getBrickLogs(volume, brick, lineCount));
+ }
+
+ // Sort the log messages based on log timestamp
+ Collections.sort(logMessages, new Comparator<VolumeLogMessage>() {
+ @Override
+ public int compare(VolumeLogMessage message1, VolumeLogMessage message2) {
+ return message1.getTimestamp().compareTo(message2.getTimestamp());
+ }
+ });
+
+ return logMessages;
+ }
+
+ private List<VolumeLogMessage> getBrickLogs(Volume volume, Brick brick, Integer lineCount)
+ throws GlusterRuntimeException {
+ String logDir = glusterUtil.getLogLocation(volume.getName(), brick.getQualifiedName(), brick.getServerName());
+ String logFileName = glusterUtil.getLogFileNameForBrickDir(brick.getServerName(), brick.getBrickDirectory());
+ String logFilePath = logDir + CoreConstants.FILE_SEPARATOR + logFileName;
+
+ // Usage: get_volume_disk_log.py <volumeName> <diskName> <lineCount>
+ LogMessageListResponse response = serverUtil.executeScriptOnServer(brick.getServerName(),
+ VOLUME_BRICK_LOG_SCRIPT + " " + logFilePath + " " + lineCount, LogMessageListResponse.class);
+
+ // populate disk and trim other fields
+ List<VolumeLogMessage> logMessages = response.getLogMessages();
+ for (VolumeLogMessage logMessage : logMessages) {
+ logMessage.setBrick(brick.getQualifiedName());
+ }
+ return logMessages;
+ }
+
+ public String migrateBrickStart(String clusterName, String volumeName, String fromBrick, String toBrick,
+ Boolean autoCommit) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ if (volumeName == null || volumeName.isEmpty()) {
+ throw new GlusterValidationException("Volume name must not be empty!");
+ }
+
+ if (fromBrick == null || fromBrick.isEmpty()) {
+ throw new GlusterValidationException("From brick must not be empty!");
+ }
+
+ if (toBrick == null || toBrick.isEmpty()) {
+ throw new GlusterValidationException("To brick must not be empty!");
+ }
+
+ if (clusterService.getCluster(clusterName) == null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] not found!");
+ }
+
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ if(autoCommit == null) {
+ autoCommit = false;
+ }
+
+ MigrateBrickTask migrateDiskTask = new MigrateBrickTask(clusterService, clusterName, volumeName, fromBrick,
+ toBrick);
+ migrateDiskTask.setAutoCommit(autoCommit);
+ migrateDiskTask.start();
+ taskResource.addTask(clusterName, migrateDiskTask);
+ return migrateDiskTask.getTaskInfo().getName(); // Return Task ID
+ }
+
+ private String getLayout(Boolean isFixLayout, Boolean isMigrateData,
+ Boolean isForcedDataMigrate) {
+ String layout = "";
+ if (isForcedDataMigrate) {
+ layout = "forced-data-migrate";
+ } else if (isMigrateData) {
+ layout = "migrate-data";
+ } else if (isFixLayout) {
+ layout = "fix-layout";
+ }
+ return layout;
+ }
+
+ public String rebalanceStart(String clusterName, String volumeName, Boolean isFixLayout, Boolean isMigrateData,
+ Boolean isForcedDataMigrate) {
+ RebalanceVolumeTask rebalanceTask = new RebalanceVolumeTask(clusterService, clusterName, volumeName, getLayout(
+ isFixLayout, isMigrateData, isForcedDataMigrate));
+ rebalanceTask.start();
+ taskResource.addTask(clusterName, rebalanceTask);
+ return rebalanceTask.getId();
+ }
+
+ public void rebalanceStop(String clusterName, String volumeName) {
+ // TODO: arrive at the task id and fetch it
+ String taskId = "";
+
+ taskResource.getTask(clusterName, taskId).stop();
+ }
+
+ public void startVolume(String clusterName, GlusterServer onlineServer, Volume volume, Boolean force) {
+ glusterUtil.startVolume(volume.getName(), onlineServer.getName(), force);
+
+ // call the start_volume_cifs.py script only if the volume is cifs enabled
+ if (volume.isCifsEnable()) {
+ startCifsReExport(clusterName, volume.getName());
+ }
+ }
+
+ public void stopVolume(String clusterName, GlusterServer onlineServer, Volume volume, Boolean force) {
+ glusterUtil.stopVolume(volume.getName(), onlineServer.getName(), force);
+
+ // call the stop_volume_cifs.py script only if the volume is cifs enabled
+ if (volume.isCifsEnable()) {
+ stopCifsReExport(clusterName, volume.getName());
+ }
+ }
+
+ public void logRotate(String clusterName, String volumeName, List<String> brickList) {
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ try {
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ glusterUtil.logRotate(volumeName, brickList, onlineServer.getName());
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) {
+ // online server has gone offline! try with a different one.
+ onlineServer = clusterService.getNewOnlineServer(clusterName);
+ glusterUtil.logRotate(volumeName, brickList, onlineServer.getName());
+ } else {
+ throw new GlusterRuntimeException("Volume [" + volumeName + "] log rotation failed!", e);
+ }
+ }
+ }
+
+ public void performVolumeOperation(String clusterName, String volumeName, String operation, Boolean force) {
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ try {
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ performOperation(clusterName, volumeName, operation, onlineServer, force);
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) {
+ // online server has gone offline! try with a different one.
+ onlineServer = clusterService.getNewOnlineServer(clusterName);
+ performOperation(clusterName, volumeName, operation, onlineServer, force);
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+ }
+
+ private void performOperation(String clusterName, String volumeName, String operation, GlusterServer onlineServer,
+ Boolean force) {
+ Volume volume = null;
+ try {
+ volume = getVolume(clusterName, volumeName);
+ } catch (Exception e) {
+ throw new GlusterRuntimeException("Could not fetch volume info for volume [" + volumeName + "]"
+ + e.getMessage());
+ }
+
+ if (operation.equals(TASK_START)) {
+ startVolume(clusterName, onlineServer, volume, force);
+ } else if (operation.equals(TASK_STOP)) {
+ stopVolume(clusterName, onlineServer, volume, force);
+ } else {
+ throw new GlusterValidationException("Invalid operation code [" + operation + "]");
+ }
+ }
+
+ public void removeBricksFromVolume(String clusterName, String volumeName, String bricks, Boolean deleteFlag) {
+ // Convert from comma separated string (query parameter)
+ List<String> brickList = Arrays.asList(bricks.split(","));
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ if (volumeName == null || volumeName.isEmpty()) {
+ throw new GlusterValidationException("Volume name must not be empty!");
+ }
+
+ if (bricks == null || bricks.isEmpty()) {
+ throw new GlusterValidationException("Parameter [" + QUERY_PARAM_BRICKS + "] is missing in request!");
+ }
+
+ if (clusterService.getCluster(clusterName) == null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] not found!");
+ }
+
+ if(deleteFlag == null) {
+ deleteFlag = false;
+ }
+
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ removeBricks(clusterName, volumeName, brickList, onlineServer);
+ cleanupDirectories(brickList, volumeName, brickList.size(), deleteFlag);
+ }
+
+ private void removeBricks(String clusterName, String volumeName, List<String> brickList, GlusterServer onlineServer) {
+ try {
+ glusterUtil.removeBricks(volumeName, brickList, onlineServer.getName());
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) {
+ // online server has gone offline! try with a different one.
+ onlineServer = clusterService.getNewOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+ glusterUtil.removeBricks(volumeName, brickList, onlineServer.getName());
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+ }
+
+ private void cleanupDirectories(List<String> bricks, String volumeName, int maxIndex, boolean deleteFlag) {
+ String errors = "";
+ for (int i = 0; i < maxIndex; i++) {
+ String[] brickInfo = bricks.get(i).split(":");
+ String serverName = brickInfo[0];
+ String brickDirectory = brickInfo[1];
+
+ try {
+ serverUtil.executeScriptOnServer(serverName, VOLUME_DIRECTORY_CLEANUP_SCRIPT + " "
+ + brickDirectory + " " + (deleteFlag ? "-d" : ""));
+ } catch(Exception e) {
+ logger.error("Error while cleaning brick [" + serverName + ":" + brickDirectory + "] of volume ["
+ + volumeName + "] : " + e.getMessage(), e);
+ errors += "[" + brickDirectory + "] => " + e.getMessage() + CoreConstants.NEWLINE;
+ }
+ }
+ if(!errors.trim().isEmpty()) {
+ throw new GlusterRuntimeException("Volume directory cleanup errors: " + errors.trim());
+ }
+ }
+
+ public void deleteVolume(String clusterName, String volumeName, Boolean deleteFlag) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty");
+ }
+
+ if (volumeName == null || volumeName.isEmpty()) {
+ throw new GlusterValidationException("Volume name must not be empty");
+ }
+
+ if (clusterService.getCluster(clusterName) == null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] not found!");
+ }
+
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ if(onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ if (deleteFlag == null) {
+ deleteFlag = false;
+ }
+
+ Volume volume = getVolume(clusterName, volumeName);
+
+ List<Brick> bricks = volume.getBricks();
+ glusterUtil.deleteVolume(volumeName, onlineServer.getName());
+
+ try {
+ postDelete(volumeName, bricks, deleteFlag);
+ if (volume.isCifsEnable()) {
+ if (volume.getStatus() == VOLUME_STATUS.ONLINE) {
+ stopCifsReExport(clusterName, volumeName);
+ }
+ deleteCifsUsers(clusterName, volumeName);
+ }
+ } catch(Exception e) {
+ throw new GlusterRuntimeException("Volume [" + volumeName
+ + "] deleted from cluster, however following error(s) occurred: " + CoreConstants.NEWLINE
+ + e.getMessage());
+ }
+ }
+
+ private void postDelete(String volumeName, List<Brick> bricks, boolean deleteFlag) {
+ for (Brick brick : bricks) {
+ String brickDirectory = brick.getBrickDirectory();
+ // String mountPoint = brickDirectory.substring(0, brickDirectory.lastIndexOf("/"));
+
+ serverUtil.executeScriptOnServer(brick.getServerName(), VOLUME_DIRECTORY_CLEANUP_SCRIPT + " "
+ + brickDirectory + " " + (deleteFlag ? "-d" : ""));
+ }
+ }
+
+ public void resetVolumeOptions(String clusterName, String volumeName) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ if(volumeName == null || volumeName.isEmpty()) {
+ throw new GlusterValidationException("Volume name must not be empty!");
+ }
+
+ if (clusterService.getCluster(clusterName) == null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] not found!");
+ }
+
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ try {
+ glusterUtil.resetOptions(volumeName, onlineServer.getName());
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) {
+ // online server has gone offline! try with a different one.
+ onlineServer = clusterService.getNewOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ glusterUtil.resetOptions(volumeName, onlineServer.getName());
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+
+ }
+ }
+
+ public void setVolumeOption(String clusterName, String volumeName, String key, String value) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ if(volumeName == null || volumeName.isEmpty()) {
+ throw new GlusterValidationException("Volume name must not be empty!");
+ }
+
+ if(key == null || key.isEmpty()) {
+ throw new GlusterValidationException("Option key must not be empty!");
+ }
+
+ if(value == null || value.isEmpty()) {
+ throw new GlusterValidationException("Option value must not be empty!");
+ }
+
+ if (clusterService.getCluster(clusterName) == null) {
+ throw new GlusterRuntimeException("Cluster [" + clusterName + "] not found!");
+ }
+
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ try {
+ glusterUtil.setOption(volumeName, key, value, onlineServer.getName());
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) {
+ // online server has gone offline! try with a different one.
+ onlineServer = clusterService.getNewOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+ glusterUtil.setOption(volumeName, key, value, onlineServer.getName());
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+ }
+
+ public VolumeOptionInfoListResponse getVolumeOptionsInfo(String clusterName) {
+ if (clusterName == null || clusterName.isEmpty()) {
+ throw new GlusterValidationException("Cluster name must not be empty!");
+ }
+
+ ClusterInfo cluster = clusterService.getCluster(clusterName);
+ if (cluster == null) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] not found!");
+ }
+
+ if(cluster.getServers().isEmpty()) {
+ throw new GlusterValidationException("Cluster [" + clusterName + "] is empty! Can't fetch Volume Options Information!");
+ }
+
+ GlusterServer onlineServer = clusterService.getOnlineServer(clusterName);
+ if (onlineServer == null) {
+ throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]");
+ }
+
+ try {
+ return glusterUtil.getVolumeOptionsInfo(onlineServer.getName());
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) {
+ onlineServer = clusterService.getNewOnlineServer(clusterName);
+ return glusterUtil.getVolumeOptionsInfo(onlineServer.getName());
+ } else {
+ throw new GlusterRuntimeException("Fetching volume options info failed! [" + e.getMessage() + "]");
+ }
+ }
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/InitServerTask.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/InitServerTask.java
new file mode 100644
index 00000000..3193d926
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/InitServerTask.java
@@ -0,0 +1,161 @@
+/**
+ * GlusterServerInitializer.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.gateway.tasks;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+
+import org.apache.derby.tools.ij;
+import org.gluster.storage.management.core.constants.CoreConstants;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.gateway.data.ClusterInfo;
+import org.gluster.storage.management.gateway.data.PersistenceDao;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.RowCallbackHandler;
+import org.springframework.jdbc.core.support.JdbcDaoSupport;
+import org.springframework.security.authentication.dao.SaltSource;
+import org.springframework.security.authentication.encoding.PasswordEncoder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+
+
+/**
+ * Initializes the Gluster Management Server.
+ */
+public class InitServerTask extends JdbcDaoSupport {
+ @Autowired
+ private PasswordEncoder passwordEncoder;
+
+ @Autowired
+ private SaltSource saltSource;
+
+ @Autowired
+ private UserDetailsService userDetailsService;
+
+ @Autowired
+ private String appVersion;
+
+ @Autowired
+ private PersistenceDao<ClusterInfo> clusterDao;
+
+ @Autowired
+ ServletContext servletContext;
+
+ private static final String SCRIPT_DIR = "data/scripts/";
+
+ public void securePasswords() {
+ getJdbcTemplate().query("select username, password from users", new RowCallbackHandler() {
+ @Override
+ public void processRow(ResultSet rs) throws SQLException {
+ String username = rs.getString(1);
+ String password = rs.getString(2);
+ UserDetails user = userDetailsService.loadUserByUsername(username);
+
+ String encodedPassword = passwordEncoder.encodePassword(password, saltSource.getSalt(user));
+ getJdbcTemplate().update("update users set password = ? where username = ?", encodedPassword, username);
+ logger.debug("Updating password for username: " + username);
+ }
+ });
+ }
+
+ private void executeScript(File script) {
+ ByteArrayOutputStream sqlOut = new ByteArrayOutputStream();
+ int numOfExceptions;
+ try {
+ numOfExceptions = ij.runScript(getJdbcTemplate().getDataSource().getConnection(), new FileInputStream(
+ script), CoreConstants.ENCODING_UTF8, sqlOut, CoreConstants.ENCODING_UTF8);
+ String output = sqlOut.toString();
+ sqlOut.close();
+ logger.debug("Data script [" + script.getName() + "] returned with exit status [" + numOfExceptions
+ + "] and output [" + output + "]");
+ if (numOfExceptions != 0) {
+ throw new GlusterRuntimeException("Server data initialization script [ " + script.getName()
+ + "] failed with [" + numOfExceptions + "] exceptions! [" + output + "]");
+ }
+ } catch (Exception ex) {
+ throw new GlusterRuntimeException("Server data initialization script [" + script.getName() + "] failed!",
+ ex);
+ }
+ }
+
+ private void initDatabase() {
+ logger.info("Initializing server data...");
+ executeScriptsFrom(getDirFromRelativePath(SCRIPT_DIR + appVersion));
+
+ securePasswords(); // encrypt the passwords
+ }
+
+ private File getDirFromRelativePath(String relativePath) {
+ String scriptDirPath = servletContext.getRealPath(relativePath);
+ File scriptDir = new File(scriptDirPath);
+ return scriptDir;
+ }
+
+ private void executeScriptsFrom(File scriptDir) {
+ if (!scriptDir.exists()) {
+ throw new GlusterRuntimeException("Script directory [" + scriptDir.getAbsolutePath() + "] doesn't exist!");
+ }
+
+ List<File> scripts = Arrays.asList(scriptDir.listFiles());
+ if(scripts.size() == 0) {
+ throw new GlusterRuntimeException("Script directory [" + scriptDir.getAbsolutePath() + "] is empty!");
+ }
+
+ Collections.sort(scripts);
+ for (File script : scripts) {
+ executeScript(script);
+ }
+ }
+
+ /**
+ * Initializes the server database, if running for the first time.
+ */
+ public synchronized void initServer() {
+ try {
+ String dbVersion = getDBVersion();
+ if (!appVersion.equals(dbVersion)) {
+ logger.info("App version [" + appVersion + "] differs from data version [" + dbVersion
+ + "]. Trying to upgrade data...");
+ upgradeData(dbVersion, appVersion);
+ }
+ } catch (Exception ex) {
+ logger.info("No cluster created yet. DB version query failed with error [" + ex.getMessage() + "]", ex);
+ // Database not created yet. Create it!
+ initDatabase();
+ }
+ }
+
+ private void upgradeData(String fromVersion, String toVersion) {
+ executeScriptsFrom(getDirFromRelativePath(SCRIPT_DIR + fromVersion + "-" + toVersion));
+ }
+
+ private String getDBVersion() {
+ return (String) clusterDao.getSingleResultFromSQL("select version from version");
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/InitializeDiskTask.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/InitializeDiskTask.java
new file mode 100644
index 00000000..45be980a
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/InitializeDiskTask.java
@@ -0,0 +1,198 @@
+/**
+ * InitializeDiskTask.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.gateway.tasks;
+
+import org.gluster.storage.management.core.constants.GlusterConstants;
+import org.gluster.storage.management.core.exceptions.ConnectionException;
+import org.gluster.storage.management.core.model.InitDiskStatusResponse;
+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.InitDiskStatusResponse.FORMAT_STATUS;
+import org.gluster.storage.management.core.model.TaskInfo.TASK_TYPE;
+import org.gluster.storage.management.gateway.services.ClusterService;
+import org.gluster.storage.management.gateway.utils.ServerUtil;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.ContextLoader;
+
+import com.sun.jersey.core.util.Base64;
+
+public class InitializeDiskTask extends Task {
+
+ private static final String INITIALIZE_DISK_SCRIPT = "format_device.py";
+ private static final String INITIALIZE_DISK_STATUS_SCRIPT = "get_format_device_status.py";
+
+ private String serverName;
+ private String diskName;
+ private String fsType;
+ private String mountPoint;
+ private ServerUtil serverUtil;
+
+ public InitializeDiskTask(ClusterService clusterService, String clusterName, String serverName, String diskName,
+ String fsType, String mountPoint) {
+ // Reference contains "Server:disk"
+ super(clusterService, clusterName, TASK_TYPE.DISK_FORMAT, serverName + ":" + diskName, "Initialize disk "
+ + serverName + ":" + diskName, false, false, false);
+
+ setServerName(serverName);
+ setDiskName(diskName);
+ setFsType(fsType);
+ setMountpoint(mountPoint);
+ taskInfo.setName(getId());
+ init();
+ }
+
+ public InitializeDiskTask(ClusterService clusterService, String clusterName, TaskInfo info) {
+ super(clusterService, clusterName, info);
+ init();
+ }
+
+ private void init() {
+ ApplicationContext ctx = ContextLoader.getCurrentWebApplicationContext();
+ serverUtil = ctx.getBean(ServerUtil.class);
+ }
+
+ @Override
+ public String getId() {
+ return new String(
+ Base64.encode(getClusterName() + "-" + taskInfo.getType() + "-" + serverName + ":" + diskName));
+ }
+
+ @Override
+ public void resume() {
+ getTaskInfo().setStatus(
+ new TaskStatus(new Status(Status.STATUS_CODE_FAILURE,
+ "Stop/Pause/Resume is not supported in Disk Initialization")));
+ }
+
+ @Override
+ public void stop() {
+ getTaskInfo().setStatus(
+ new TaskStatus(new Status(Status.STATUS_CODE_FAILURE,
+ "Stop/Pause/Resume is not supported in Disk Initialization")));
+ }
+
+ @Override
+ public void pause() {
+ getTaskInfo().setStatus(
+ new TaskStatus(new Status(Status.STATUS_CODE_FAILURE,
+ "Stop/Pause/Resume is not supported in Disk Initialization")));
+ }
+
+ @Override
+ public void commit() {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public TASK_TYPE getType() {
+ return TASK_TYPE.DISK_FORMAT;
+ }
+
+
+ @Override
+ public void start() {
+ try {
+ startInitializeDisk(serverName);
+ } catch(ConnectionException e) {
+ // online server might have gone offline. update the failure status
+ getTaskInfo().setStatus(new TaskStatus(new Status(Status.STATUS_CODE_FAILURE, e.getMessage())));
+ }
+ }
+
+ private void startInitializeDisk(String serverName) {
+ String output = serverUtil.executeScriptOnServer(serverName, INITIALIZE_DISK_SCRIPT + " " + getFsType() + " \""
+ + getMountpoint() + "\" " + getDiskName() );
+ TaskStatus taskStatus = new TaskStatus(new Status(Status.STATUS_CODE_RUNNING, output));
+ taskStatus.setPercentageSupported((getFsType().equals(GlusterConstants.FSTYPE_XFS)) ? false : true);
+ getTaskInfo().setStatus(taskStatus);
+ }
+
+ @Override
+ public TaskStatus checkStatus() {
+
+ try {
+ return getInitializingDeviceStatus(serverName, getDiskName());
+ } catch(ConnectionException e) {
+ // online server might have gone offline. update the failure status
+ return new TaskStatus(new Status(Status.STATUS_CODE_FAILURE, e.getMessage()));
+ }
+ }
+
+ private TaskStatus getInitializingDeviceStatus(String serverName, String diskName) {
+ InitDiskStatusResponse initDiskStatusResponse;
+ TaskStatus taskStatus = new TaskStatus();
+
+ try {
+ initDiskStatusResponse = serverUtil.executeScriptOnServer(serverName, INITIALIZE_DISK_STATUS_SCRIPT + " "
+ + diskName, InitDiskStatusResponse.class);
+ } catch(RuntimeException e) {
+ taskStatus.setCode(Status.STATUS_CODE_FAILURE);
+ taskStatus.setMessage(e.getMessage());
+ throw e;
+ }
+
+ if (initDiskStatusResponse.getFormatStatus() == FORMAT_STATUS.COMPLETED) {
+ taskStatus.setCode(Status.STATUS_CODE_SUCCESS);
+ } else if (initDiskStatusResponse.getFormatStatus() == FORMAT_STATUS.IN_PROGRESS) {
+ taskStatus.setCode(Status.STATUS_CODE_RUNNING);
+ taskStatus.setPercentCompleted(Math.round(initDiskStatusResponse.getCompletedBlocks()
+ / initDiskStatusResponse.getTotalBlocks() * 100));
+ } else if(initDiskStatusResponse.getFormatStatus() == FORMAT_STATUS.NOT_RUNNING) {
+ taskStatus.setCode(Status.STATUS_CODE_FAILURE);
+ }
+
+ taskStatus.setMessage(initDiskStatusResponse.getMessage());
+ return taskStatus;
+ }
+
+ public void setDiskName(String diskName) {
+ this.diskName = diskName;
+ }
+
+ public String getDiskName() {
+ return diskName;
+ }
+
+ public void setServerName(String serverName) {
+ this.serverName = serverName;
+ }
+
+ public String getServerName() {
+ return serverName;
+ }
+
+ public void setFsType(String fsType) {
+ this.fsType = fsType;
+ }
+
+ public String getFsType() {
+ return fsType;
+ }
+
+ public void setMountpoint(String deviceMountPoint) {
+ this.mountPoint = deviceMountPoint;
+ }
+
+ public String getMountpoint() {
+ return mountPoint;
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/MigrateBrickTask.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/MigrateBrickTask.java
new file mode 100644
index 00000000..8a31f9a9
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/MigrateBrickTask.java
@@ -0,0 +1,220 @@
+/**
+ * MigrateDiskTask.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.gateway.tasks;
+
+import org.gluster.storage.management.core.exceptions.ConnectionException;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.model.Status;
+import org.gluster.storage.management.core.model.TaskStatus;
+import org.gluster.storage.management.core.model.TaskInfo.TASK_TYPE;
+import org.gluster.storage.management.gateway.services.ClusterService;
+import org.gluster.storage.management.gateway.services.GlusterInterfaceService;
+import org.gluster.storage.management.gateway.utils.ServerUtil;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.ContextLoader;
+
+import com.sun.jersey.core.util.Base64;
+
+public class MigrateBrickTask extends Task {
+
+ private String fromBrick;
+ private String toBrick;
+ private Boolean autoCommit;
+ private GlusterInterfaceService glusterInterface;
+ protected ServerUtil serverUtil;
+
+ public String getFromBrick() {
+ return fromBrick;
+ }
+
+ public void setFromBrick(String fromBrick) {
+ this.fromBrick = fromBrick;
+ }
+
+ public String getToBrick() {
+ return toBrick;
+ }
+
+ public void setToBrick(String toBrick) {
+ this.toBrick = toBrick;
+ }
+
+ public Boolean getAutoCommit() {
+ return autoCommit;
+ }
+
+ public void setAutoCommit(Boolean autoCommit) {
+ this.autoCommit = autoCommit;
+ }
+
+ public MigrateBrickTask(ClusterService clusterService, String clusterName, String volumeName, String fromBrick,
+ String toBrick) {
+ super(clusterService, clusterName, TASK_TYPE.BRICK_MIGRATE, volumeName + "#" + fromBrick + "#" + toBrick,
+ "Brick Migration on volume [" + volumeName + "] from [" + fromBrick + "] to [" + toBrick + "]", true,
+ true, true);
+ setFromBrick(fromBrick);
+ setToBrick(toBrick);
+ taskInfo.setName(getId());
+ init();
+ }
+
+ private void init() {
+ ApplicationContext ctx = ContextLoader.getCurrentWebApplicationContext();
+ glusterInterface = ctx.getBean(GlusterInterfaceService.class);
+ serverUtil = ctx.getBean(ServerUtil.class);
+ }
+
+ @Override
+ public String getId() {
+ return new String(Base64.encode(clusterName + "-" + taskInfo.getType() + "-" + taskInfo.getReference() + "-" + fromBrick + "-"
+ + toBrick));
+ }
+
+ @Override
+ public void start() {
+ try {
+ startMigration(getOnlineServer().getName());
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(getOnlineServer()) == false) {
+ // online server might have gone Offline. try with a new one.
+ startMigration(getNewOnlineServer().getName());
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+ }
+
+ private void startMigration(String onlineServerName) {
+ String volumeName = getTaskInfo().getReference().split("#")[0];
+ glusterInterface.startBrickMigration(onlineServerName, volumeName, getFromBrick(), getToBrick());
+ getTaskInfo().setStatus(new TaskStatus(new Status(Status.STATUS_CODE_RUNNING, "Brick Migration Started.")));
+ }
+
+ @Override
+ public void pause() {
+ try {
+ pauseMigration(getOnlineServer().getName());
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(getOnlineServer()) == false) {
+ // online server might have gone offline. try with a new one.
+ pauseMigration(getNewOnlineServer().getName());
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+ }
+
+ private void pauseMigration(String onlineServer) {
+ String volumeName = getTaskInfo().getReference().split("#")[0];
+ glusterInterface.pauseBrickMigration(onlineServer, volumeName, getFromBrick(), getToBrick());
+ TaskStatus taskStatus = new TaskStatus();
+ taskStatus.setCode(Status.STATUS_CODE_PAUSE);
+ taskStatus.setMessage("Brick Migration Paused");
+ getTaskInfo().setStatus(taskStatus);
+ }
+
+ @Override
+ public void resume() {
+ start();
+ }
+
+ @Override
+ public void commit() {
+ try {
+ commitMigration(getOnlineServer().getName());
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(getOnlineServer()) == false) {
+ // online server might have gone offline. try with a new one.
+ commitMigration(getNewOnlineServer().getName());
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+ }
+
+ private void commitMigration(String serverName) {
+ String volumeName = getTaskInfo().getReference().split("#")[0];
+ glusterInterface.commitBrickMigration(serverName, volumeName, getFromBrick(), getToBrick());
+ TaskStatus taskStatus = new TaskStatus();
+ taskStatus.setCode(Status.STATUS_CODE_SUCCESS);
+ taskStatus.setMessage("Brick Migration Committed.");
+ getTaskInfo().setStatus(taskStatus);
+ }
+
+ @Override
+ public void stop() {
+ try {
+ stopMigration(getOnlineServer().getName());
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(getOnlineServer()) == false) {
+ // online server might have gone offline. try with a new one.
+ stopMigration(getNewOnlineServer().getName());
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+ }
+
+ private void stopMigration(String serverName) {
+ String volumeName = getTaskInfo().getReference().split("#")[0];
+ glusterInterface.stopBrickMigration(serverName, volumeName, getFromBrick(), getToBrick());
+ TaskStatus taskStatus = new TaskStatus();
+ taskStatus.setCode(Status.STATUS_CODE_SUCCESS);
+ taskStatus.setMessage("Brick Migration Stopped");
+ getTaskInfo().setStatus(taskStatus);
+ }
+
+ @Override
+ public TaskStatus checkStatus() {
+ try {
+ return checkMigrationStatus(getOnlineServer().getName());
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(getOnlineServer()) == false) {
+ // online server might have gone offline. try with a new one.
+ return checkMigrationStatus(getNewOnlineServer().getName());
+ }
+ }
+ return null;
+ }
+
+ private TaskStatus checkMigrationStatus(String serverName) {
+ // For committed task, status command (CLI) is invalid, just return current status
+ if (taskInfo.getStatus().getCode() == Status.STATUS_CODE_SUCCESS) {
+ return taskInfo.getStatus();
+ }
+
+ String volumeName = getTaskInfo().getReference().split("#")[0];
+ TaskStatus taskStatus = glusterInterface.checkBrickMigrationStatus(serverName, volumeName, getFromBrick(),
+ getToBrick());
+ if (autoCommit && taskStatus.isCommitPending()) {
+ commitMigration(serverName);
+ return taskInfo.getStatus(); // return the committed status
+ }
+
+ taskInfo.setStatus(taskStatus); // Update the task status
+ return taskStatus;
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/RebalanceVolumeTask.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/RebalanceVolumeTask.java
new file mode 100644
index 00000000..288179e9
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/RebalanceVolumeTask.java
@@ -0,0 +1,141 @@
+/**
+ * RebalanceVolumeTask.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.gateway.tasks;
+
+import org.gluster.storage.management.core.exceptions.ConnectionException;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.model.Status;
+import org.gluster.storage.management.core.model.TaskStatus;
+import org.gluster.storage.management.core.model.TaskInfo.TASK_TYPE;
+import org.gluster.storage.management.gateway.services.ClusterService;
+import org.gluster.storage.management.gateway.services.GlusterInterfaceService;
+import org.gluster.storage.management.gateway.utils.ServerUtil;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.ContextLoader;
+
+import com.sun.jersey.core.util.Base64;
+
+public class RebalanceVolumeTask extends Task {
+
+ private String layout;
+ private String serverName;
+ private ServerUtil serverUtil;
+ private GlusterInterfaceService glusterUtil;
+
+ public RebalanceVolumeTask(ClusterService clusterService, String clusterName, String volumeName, String layout) {
+ super(clusterService, clusterName, TASK_TYPE.VOLUME_REBALANCE, volumeName, "Volume " + volumeName
+ + " Rebalance", false, true, false);
+ setLayout(layout);
+ taskInfo.setName(getId());
+ init();
+ }
+
+ private void init() {
+ ApplicationContext ctx = ContextLoader.getCurrentWebApplicationContext();
+ serverUtil = ctx.getBean(ServerUtil.class);
+ glusterUtil = ctx.getBean(GlusterInterfaceService.class);
+ }
+
+ @Override
+ public String getId() {
+ return new String(Base64.encode(getClusterName() + "-" + taskInfo.getType() + "-" + taskInfo.getReference()));
+ }
+
+ @Override
+ public void start() {
+ try {
+ serverName = getOnlineServer().getName();
+ startRebalance(serverName);
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(getOnlineServer()) == false) {
+ // online server might have gone offline. try with a new one
+ serverName = getNewOnlineServer().getName();
+ startRebalance(serverName);
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+ }
+
+ private void startRebalance(String serverName) {
+ String command = "gluster volume rebalance " + getTaskInfo().getReference() + " " + getLayout() + " start";
+ String output = serverUtil.executeOnServer(serverName, command);
+ getTaskInfo().setStatus(new TaskStatus(new Status(Status.STATUS_CODE_RUNNING, output)));
+ }
+
+ @Override
+ public void resume() {
+ getTaskInfo().setStatus(
+ new TaskStatus(new Status(Status.STATUS_CODE_FAILURE,
+ "Pause/Resume is not supported in Volume Rebalance")));
+ }
+
+ @Override
+ public void stop() {
+ try {
+ glusterUtil.stopRebalance(serverName, getTaskInfo().getReference());
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(getOnlineServer()) == false) {
+ // online server might have gone offline. update the failure status
+ getTaskInfo().setStatus(new TaskStatus(new Status(Status.STATUS_CODE_FAILURE, e.getMessage())));
+ } else {
+ throw new GlusterRuntimeException(e.getMessage());
+ }
+ }
+ }
+
+ @Override
+ public void pause() {
+ getTaskInfo().setStatus(
+ new TaskStatus(new Status(Status.STATUS_CODE_FAILURE,
+ "Pause/Resume is not supported in Volume Rebalance")));
+ }
+
+ @Override
+ public TaskStatus checkStatus() {
+ try {
+ return glusterUtil.checkRebalanceStatus(serverName, getTaskInfo().getReference());
+ } catch (Exception e) {
+ // check if online server has gone offline. If yes, try again one more time.
+ if (e instanceof ConnectionException || serverUtil.isServerOnline(getOnlineServer()) == false) {
+ // online server might have gone offline. update the failure status
+ getTaskInfo().setStatus(new TaskStatus(new Status(Status.STATUS_CODE_FAILURE, e.getMessage())));
+ return getTaskInfo().getStatus();
+ }
+ }
+ return null;
+ }
+
+ public void setLayout(String layout) {
+ this.layout = layout;
+ }
+
+ public String getLayout() {
+ return layout;
+ }
+
+ @Override
+ public void commit() {
+ // TODO Auto-generated method stub
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/ServerSyncTask.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/ServerSyncTask.java
new file mode 100644
index 00000000..c3073d97
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/ServerSyncTask.java
@@ -0,0 +1,168 @@
+/**
+ * ServerDiscoveryTask.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.gateway.tasks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+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.utils.GlusterCoreUtil;
+import org.gluster.storage.management.core.utils.ProcessResult;
+import org.gluster.storage.management.gateway.data.ClusterInfo;
+import org.gluster.storage.management.gateway.data.PersistenceDao;
+import org.gluster.storage.management.gateway.data.ServerInfo;
+import org.gluster.storage.management.gateway.services.ClusterService;
+import org.gluster.storage.management.gateway.services.DiscoveredServerService;
+import org.gluster.storage.management.gateway.services.GlusterServerService;
+import org.gluster.storage.management.gateway.utils.ServerUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+
+/**
+ * Task for syncing server details. This performs two things: <br>
+ * 1. Auto-discovery of servers eligible to be added to the Gluster cluster. <br>
+ * 2. Syncing of cluster-server mapping with actual servers of the cluster. This mapping can go out of sync if user
+ * adds/removes servers manually using the CLI.
+ */
+@Component
+public class ServerSyncTask {
+ private static final String SCRIPT_NAME_SFX = "-discover-servers.py";
+
+ @Autowired
+ private ServerUtil serverUtil;
+
+ @Autowired
+ private DiscoveredServerService discoveredServersService;
+
+ @Autowired
+ private GlusterServerService glusterServerService;
+
+ @Autowired
+ private String discoveryMechanism;
+
+ @Autowired
+ private ClusterService clusterService;
+
+ @Autowired
+ private PersistenceDao<ClusterInfo> clusterDao;
+
+ private static final Logger logger = Logger.getLogger(ServerSyncTask.class);
+
+ public void perform() {
+ discoverServers();
+ syncClusterServerMapping();
+ }
+
+ private void syncClusterServerMapping() {
+ List<ClusterInfo> clusters = clusterService.getAllClusters();
+ for(ClusterInfo cluster : clusters) {
+ try {
+ List<ServerInfo> servers = cluster.getServers();
+ if(servers.isEmpty()) {
+ logger.info("Cluster [" + cluster.getName() + "] is empty, nothing to sync!");
+ continue;
+ }
+ List<GlusterServer> actualServers = glusterServerService.getGlusterServers(cluster.getName(), false,
+ null, null);
+ updateRemovedServers(cluster, servers, actualServers);
+ updateAddedServers(cluster, servers, actualServers);
+ } catch(Exception e) {
+ // log error and continue with next cluster
+ logger.error("Couldn't sync cluster-server mapping for cluster [" + cluster.getName() + "]!", e);
+ continue;
+ }
+ }
+ }
+
+ private void updateAddedServers(ClusterInfo cluster, List<ServerInfo> servers, List<GlusterServer> actualServers) {
+ List<String> addedServers = findAddedServers(cluster.getName(), servers, actualServers);
+ for(String addedServer : addedServers) {
+ clusterService.mapServerToCluster(cluster.getName(), addedServer);
+ }
+ }
+
+ private void updateRemovedServers(ClusterInfo cluster, List<ServerInfo> servers, List<GlusterServer> actualServers) {
+ List<String> removedServers = findRemovedServers(servers, actualServers);
+ for(String removedServer : removedServers) {
+ clusterService.unmapServerFromCluster(cluster.getName(), removedServer);
+ }
+ }
+
+ private List<String> findRemovedServers(List<ServerInfo> servers, List<GlusterServer> actualServers) {
+ List<String> removedServers = new ArrayList<String>();
+
+ for(ServerInfo server : servers) {
+ if (!GlusterCoreUtil.containsEntityWithName(actualServers, server.getName(), true)) {
+ removedServers.add(server.getName());
+ }
+ }
+ return removedServers;
+ }
+
+ private List<String> findAddedServers(String clusterName, List<ServerInfo> servers, List<GlusterServer> actualServers) {
+ List<String> addedServers = new ArrayList<String>();
+ for(GlusterServer actualServer : actualServers) {
+ if(!serverExists(servers, actualServer.getName())) {
+ addedServers.add(actualServer.getName());
+ }
+ }
+ return addedServers;
+ }
+
+ private boolean serverExists(List<ServerInfo> servers, String name) {
+ for(ServerInfo server : servers) {
+ if(server.getName().equalsIgnoreCase(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void discoverServers() {
+ if(discoveryMechanism.equals(GlusterConstants.NONE)) {
+ return;
+ }
+
+ List<String> serverNameList = new ArrayList<String>();
+
+ ProcessResult result = serverUtil.executeGlusterScript(true, discoveryMechanism + SCRIPT_NAME_SFX, new ArrayList<String>());
+ if(result.isSuccess()) {
+ List<String> existingServers = clusterDao.findBySQL("select name from server_info");
+ String serverNames = result.getOutput();
+ String[] parts = serverNames.split(CoreConstants.NEWLINE);
+ for(String serverName : parts) {
+ // The server discovery mechanism will return every server that has not been "peer probed". However we
+ // need to filter out those servers that are the "first" server of a new cluster, and hence are still
+ // not peer probed.
+ if(!existingServers.contains(serverName)) {
+ serverNameList.add(serverName);
+ }
+ }
+ }
+
+ discoveredServersService.setDiscoveredServerNames(serverNameList);
+ }
+} \ No newline at end of file
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/Task.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/Task.java
new file mode 100644
index 00000000..0fee3c2e
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/tasks/Task.java
@@ -0,0 +1,113 @@
+/**
+ * Task.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.gateway.tasks;
+
+import org.gluster.storage.management.core.model.GlusterServer;
+import org.gluster.storage.management.core.model.TaskInfo;
+import org.gluster.storage.management.core.model.TaskStatus;
+import org.gluster.storage.management.core.model.TaskInfo.TASK_TYPE;
+import org.gluster.storage.management.gateway.services.ClusterService;
+
+
+public abstract class Task {
+ public String[] TASK_TYPE_STR = { "Format Disk", "Migrate Brick", "Volume Rebalance" };
+
+ protected TaskInfo taskInfo;
+ protected String clusterName;
+ private ClusterService clusterService;
+
+ public Task(ClusterService clusterService, String clusterName, TASK_TYPE type, String reference, String desc,
+ boolean canPause, boolean canStop, boolean canCommit) {
+ TaskInfo taskInfo = new TaskInfo();
+ taskInfo.setType(type);
+ taskInfo.setReference(reference);
+ taskInfo.setDescription(desc);
+ taskInfo.setPauseSupported(canPause);
+ taskInfo.setStopSupported(canStop);
+ taskInfo.setCommitSupported(canCommit);
+
+ init(clusterService, clusterName, taskInfo);
+
+ }
+
+ public Task(ClusterService clusterService, String clusterName, TaskInfo taskInfo) {
+ init(clusterService, clusterName, taskInfo);
+ }
+
+ private void init(ClusterService clusterService, String clusterName, TaskInfo taskInfo) {
+ this.clusterService = clusterService;
+ setClusterName(clusterName);
+ setTaskInfo(taskInfo);
+ }
+
+ protected GlusterServer getOnlineServer() {
+ return clusterService.getOnlineServer(clusterName);
+ }
+
+ protected GlusterServer getNewOnlineServer() {
+ return clusterService.getNewOnlineServer(clusterName);
+ }
+
+ protected GlusterServer getNewOnlineServer(String exceptServerName) {
+ return clusterService.getNewOnlineServer(clusterName, exceptServerName);
+ }
+
+ public String getTypeStr() {
+ return TASK_TYPE_STR[taskInfo.getType().ordinal()];
+ }
+
+ public TASK_TYPE getType() {
+ return getTaskInfo().getType();
+ }
+
+ public String getClusterName() {
+ return clusterName;
+ }
+
+ public void setClusterName(String clusterName) {
+ this.clusterName = clusterName;
+ }
+
+ public TaskInfo getTaskInfo() {
+ return taskInfo;
+ }
+
+ public void setTaskInfo(TaskInfo info) {
+ this.taskInfo = info;
+ }
+
+ public abstract String getId();
+
+ public abstract void start();
+
+ public abstract void resume();
+
+ public abstract void stop();
+
+ public abstract void pause();
+
+ public abstract void commit();
+
+ /**
+ * This method should check current status of the task and update it's taskInfo accordingly
+ */
+ public abstract TaskStatus checkStatus();
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/AbstractStatsFactory.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/AbstractStatsFactory.java
new file mode 100644
index 00000000..ebc1ccba
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/AbstractStatsFactory.java
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * 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.gateway.utils;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.model.ServerStats;
+import org.gluster.storage.management.core.model.ServerStatsRow;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+
+/**
+ *
+ */
+@Component
+public abstract class AbstractStatsFactory implements StatsFactory {
+ @Autowired
+ protected ServerUtil serverUtil;
+
+ private Logger logger = Logger.getLogger(AbstractStatsFactory.class);
+
+ protected ServerStats getFirstOnlineServerStats(List<String> serverNames, String period,
+ boolean removeServerOnError, boolean removeOnlineServer) {
+ for(int i = serverNames.size() - 1; i >= 0; i--) {
+ String serverName = serverNames.get(i);
+ try {
+ ServerStats stats = fetchStats(serverName, period);
+ if(removeOnlineServer) {
+ serverNames.remove(serverName);
+ }
+ return stats;
+ } catch(Exception e) {
+ // server might be offline - continue with next one
+ logger.warn("Couldn't fetch stats from server [" + serverName + "]!", e);
+ if(removeServerOnError) {
+ serverNames.remove(serverName);
+ }
+ continue;
+ }
+ }
+ throw new GlusterRuntimeException("All servers offline!");
+ }
+
+ protected void aggregateStats(List<String> serverNames, ServerStats aggregatedStats, String period) {
+ if(serverNames.isEmpty()) {
+ return;
+ }
+
+ int rowCount = aggregatedStats.getMetadata().getRowCount();
+ int columnCount = aggregatedStats.getMetadata().getLegend().size();
+ int[][] dataCount = initDataCountArray(rowCount, columnCount);
+
+ List<ServerStats> allStats = serverUtil.executeScriptOnServers(serverNames, getStatsScriptName() + " " + period, ServerStats.class, false);
+
+ for (ServerStats stats : allStats) {
+ // add to aggregated stats
+ addServerStats(stats, aggregatedStats, dataCount);
+ }
+
+ averageAggregatedStats(aggregatedStats, dataCount);
+ }
+
+ /**
+ *
+ * @param statsToBeAdded
+ * @param targetStats
+ * @param dataCount Each element of this matrix will be incremented for every valid element added
+ * @return
+ */
+ protected List<ServerStatsRow> addServerStats(ServerStats statsToBeAdded, ServerStats targetStats, int[][] dataCount) {
+ List<ServerStatsRow> serverStatsRows = statsToBeAdded.getRows();
+ for (int rowNum = 0; rowNum < serverStatsRows.size() && rowNum < targetStats.getMetadata().getRowCount()
+ && rowNum < dataCount.length; rowNum++) {
+ ServerStatsRow row = serverStatsRows.get(rowNum);
+ List<Double> rowData = row.getUsageData();
+
+ List<Double> aggregatedStatsRowData = targetStats.getRows().get(rowNum).getUsageData();
+ for(int i = 1; i < targetStats.getMetadata().getLegend().size(); i++) {
+ // Add the data
+ Double data = rowData.get(i);
+ if(!data.isNaN()) {
+ // data is available. add it.
+ Double oldData = aggregatedStatsRowData.get(i);
+ if(oldData.isNaN()) {
+ oldData = 0d;
+ }
+ aggregatedStatsRowData.set(i, oldData + data);
+ // increment record count. this will be used for calculating average of aggregated data.
+ dataCount[rowNum][i]++;
+ }
+ }
+ }
+ return serverStatsRows;
+ }
+
+ protected void averageAggregatedStats(ServerStats aggregatedStats, int[][] dataCount) {
+ List<ServerStatsRow> rows = aggregatedStats.getRows();
+ for(int rowNum = 0; rowNum < rows.size() && rowNum < dataCount.length; rowNum++) {
+ List<Double> data = rows.get(rowNum).getUsageData();
+ for(int columnNum = 0; columnNum < data.size(); columnNum++) {
+ data.set(columnNum, data.get(columnNum) / dataCount[rowNum][columnNum]);
+ }
+ }
+ }
+
+ protected int[][] initDataCountArray(int rowCount, int columnCount) {
+ int[][] dataCount = new int[rowCount][columnCount];
+ // initialize all data counts to 1
+ for(int rowNum = 0; rowNum < rowCount; rowNum++) {
+ for(int columnNum = 0; columnNum < columnCount; columnNum++) {
+ dataCount[rowNum][columnNum] = 1;
+ }
+ }
+ return dataCount;
+ }
+
+ @Override
+ public ServerStats fetchAggregatedStats(List<String> serverNames, String period) {
+ if(serverNames == null || serverNames.size() == 0) {
+ throw new GlusterRuntimeException("No server names passed to fetchAggregaredStats!");
+ }
+
+ ServerStats firstServerStats = getFirstOnlineServerStats(serverNames, period, true, true);
+
+ ServerStats aggregatedStats = new ServerStats(firstServerStats);
+ aggregateStats(serverNames, aggregatedStats, period);
+ return aggregatedStats;
+ }
+
+ @Override
+ public ServerStats fetchStats(String serverName, String period, String...args) {
+ String argsStr = "";
+ for (String arg : args) {
+ if(arg != null) {
+ argsStr += " " + arg;
+ }
+ }
+ return serverUtil.executeScriptOnServer(serverName, getStatsScriptName() + argsStr + " " + period,
+ ServerStats.class);
+ }
+
+ public abstract String getStatsScriptName();
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/CpuStatsFactory.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/CpuStatsFactory.java
new file mode 100644
index 00000000..412794eb
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/CpuStatsFactory.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.gateway.utils;
+
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ */
+@Component
+public class CpuStatsFactory extends AbstractStatsFactory {
+
+ private static final String CPU_STATS_SCRIPT = "get_rrd_cpu_details.py";
+
+ @Override
+ public String getStatsScriptName() {
+ return CPU_STATS_SCRIPT;
+ }
+
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/DBUtil.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/DBUtil.java
new file mode 100644
index 00000000..1c186a0a
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/DBUtil.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * 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.gateway.utils;
+
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+import org.apache.log4j.Logger;
+import org.gluster.storage.management.core.constants.CoreConstants;
+
+
+/**
+ *
+ */
+public class DBUtil {
+ private static final Logger logger = Logger.getLogger(DBUtil.class);
+ public static void shutdownDerby() {
+ try {
+ // the shutdown=true attribute shuts down Derby
+ DriverManager.getConnection("jdbc:derby:;shutdown=true");
+
+ // To shut down a specific database only, but keep the
+ // engine running (for example for connecting to other
+ // databases), specify a database in the connection URL:
+ //DriverManager.getConnection("jdbc:derby:" + dbName + ";shutdown=true");
+ } catch (Exception e) {
+ if(e instanceof SQLException) {
+ SQLException se = (SQLException) e;
+ if (((se.getErrorCode() == 50000) && ("XJ015".equals(se.getSQLState())))) {
+ // we got the expected exception
+ logger.info("Derby shut down normally");
+ // Note that for single database shutdown, the expected
+ // SQL state is "08006", and the error code is 45000.
+ } else {
+ // if the error code or SQLState is different, we have
+ // an unexpected exception (shutdown failed)
+ logger.error("Derby did not shut down normally!" + inspectSQLException(se), se);
+ }
+ } else {
+ logger.error("Derby did not shut down normally! [" + e.getMessage() + "]", e);
+ }
+ }
+ // force garbage collection to unload the EmbeddedDriver
+ // so Derby can be restarted
+ System.gc();
+ }
+
+ /**
+ * Extracts details of an SQLException chain to <code>String</code>.
+ * Details included are SQL State, Error code, Exception message.
+ *
+ * @param e the SQLException from which to print details.
+ */
+ private static String inspectSQLException(SQLException e)
+ {
+ // Unwraps the entire exception chain to unveil the real cause of the
+ // Exception.
+ String errMsg = "";
+ while (e != null)
+ {
+ errMsg += "\n----- SQLException -----" + CoreConstants.NEWLINE + " SQL State: " + e.getSQLState()
+ + CoreConstants.NEWLINE + " Error Code: " + e.getErrorCode() + CoreConstants.NEWLINE
+ + " Message: " + e.getMessage();
+ e = e.getNextException();
+ }
+ return errMsg;
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/MemoryStatsFactory.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/MemoryStatsFactory.java
new file mode 100644
index 00000000..691662e6
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/MemoryStatsFactory.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.gateway.utils;
+
+import java.util.List;
+
+import org.gluster.storage.management.core.model.ServerStats;
+import org.gluster.storage.management.core.model.ServerStatsRow;
+import org.springframework.stereotype.Component;
+
+
+/**
+ *
+ */
+@Component
+public class MemoryStatsFactory extends AbstractStatsFactory {
+
+ private static final String MEM_STATS_SCRIPT = "get_rrd_memory_details.py";
+
+ @Override
+ public String getStatsScriptName() {
+ return MEM_STATS_SCRIPT;
+ }
+
+ @Override
+ public ServerStats fetchStats(String serverName, String period, String... args) {
+ ServerStats stats = super.fetchStats(serverName, period, args);
+
+ // stats returned by rrd script contains five columns - user, free, cache, buffer, total
+ // out of this, the "user" memory includes cached and buffer. We remove them to get the
+ // actual memory used by "user"
+ for(ServerStatsRow row : stats.getRows()) {
+ List<Double> data = row.getUsageData();
+ Double user = data.get(0);
+ Double free = data.get(1);
+ Double cache = data.get(2);
+ Double buffer = data.get(3);
+ Double total = data.get(4);
+
+ Double actualUser = user - cache - buffer;
+
+ // convert all figures from bytes to percentages
+ data.set(0, (actualUser * 100) / total);
+ data.set(1, (free * 100) / total);
+ data.set(2, (cache * 100) / total);
+ data.set(3, (buffer * 100) / total);
+ data.set(4, (total * 100) / total);
+ }
+
+ return stats;
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/NetworkStatsFactory.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/NetworkStatsFactory.java
new file mode 100644
index 00000000..03c252a9
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/NetworkStatsFactory.java
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * 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.gateway.utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.model.NetworkInterface;
+import org.gluster.storage.management.core.model.Server;
+import org.gluster.storage.management.core.model.ServerStats;
+import org.gluster.storage.management.core.model.ServerStatsRow;
+import org.gluster.storage.management.core.utils.ProcessUtil;
+import org.springframework.stereotype.Component;
+
+
+/**
+ *
+ */
+@Component
+public class NetworkStatsFactory extends AbstractStatsFactory {
+ private static final Logger logger = Logger.getLogger(NetworkStatsFactory.class);
+ private static final String NETWORK_STATS_SCRIPT = "get_rrd_net_details.py";
+ private int[][] dataCount;
+
+ @Override
+ public String getStatsScriptName() {
+ return NETWORK_STATS_SCRIPT;
+ }
+
+ @Override
+ protected ServerStats getFirstOnlineServerStats(List<String> serverNames, String period,
+ boolean removeServerOnError, boolean removeOnlineServer) {
+ ServerStats firstOnlineServerStats = null;
+ for(int i = serverNames.size() - 1; i >= 0; i--) {
+ String serverName = serverNames.get(i);
+ Server server = new Server(serverName);
+ serverUtil.fetchServerDetails(server);
+ if(!server.isOnline()) {
+ if(removeServerOnError) {
+ // server is offline. no point in trying to fetch it's details.
+ serverNames.remove(serverName);
+ }
+ continue;
+ }
+ try {
+ for(NetworkInterface networkInterface : server.getNetworkInterfaces()) {
+ ServerStats stats = fetchStats(serverName, period, networkInterface.getName());
+ if(firstOnlineServerStats == null) {
+ firstOnlineServerStats = stats;
+ int rowCount = firstOnlineServerStats.getMetadata().getRowCount();
+ int columnCount = firstOnlineServerStats.getMetadata().getLegend().size();
+ dataCount = initDataCountArray(rowCount, columnCount);
+ } else {
+ addServerStats(stats, firstOnlineServerStats, dataCount);
+ }
+ }
+
+ if(removeOnlineServer) {
+ serverNames.remove(serverName);
+ }
+ return firstOnlineServerStats;
+ } catch(Exception e) {
+ // server might be offline - continue with next one
+ logger.warn("Couldn't fetch stats from server [" + serverName + "]!", e);
+ if(removeServerOnError) {
+ serverNames.remove(serverName);
+ }
+ continue;
+ }
+ }
+ throw new GlusterRuntimeException("All servers offline!");
+ }
+
+ protected void aggregateStats(List<String> serverNames, ServerStats aggregatedStats, String period) {
+ if(serverNames.isEmpty()) {
+ return;
+ }
+
+ List<ServerStats> statsList = Collections.synchronizedList(new ArrayList<ServerStats>());
+ try {
+ List<Thread> threads = createThreads(serverNames, period, statsList);
+ ProcessUtil.waitForThreads(threads);
+ for(ServerStats stats : statsList) {
+ addServerStats(stats, aggregatedStats, dataCount);
+ }
+ } catch (InterruptedException e) {
+ String errMsg = "Exception while aggregating network statistics on servers [" + serverNames
+ + "] for period [" + period + "]! Error: [" + e.getMessage() + "]";
+ logger.error(errMsg, e);
+ throw new GlusterRuntimeException(errMsg, e);
+ }
+
+ averageAggregatedStats(aggregatedStats, dataCount);
+ }
+
+ private <T> List<Thread> createThreads(List<String> serverNames, String period, List<ServerStats> statsList)
+ throws InterruptedException {
+ List<Thread> threads = new ArrayList<Thread>();
+ for (int i = serverNames.size()-1; i >= 0 ; i--) {
+ Thread thread = new NetworkStatsThread(serverNames.get(i), period, statsList);
+ threads.add(thread);
+ thread.start();
+ if(i >= 5 && i % 5 == 0) {
+ // After every 5 servers, wait for 1 second so that we don't end up with too many running threads
+ Thread.sleep(1000);
+ }
+ }
+ return threads;
+ }
+
+ public class NetworkStatsThread extends Thread {
+ private String serverName;
+ private String period;
+ private List<ServerStats> statsList;
+
+ public NetworkStatsThread(String serverName, String period, List<ServerStats> statsList) {
+ this.serverName = serverName;
+ this.period = period;
+ this.statsList = statsList;
+ }
+
+ @Override
+ public void run() {
+ try {
+ Server server = new Server(serverName);
+ serverUtil.fetchServerDetails(server);
+
+ for (NetworkInterface networkInterface : server.getNetworkInterfaces()) {
+ // fetch the stats and add to aggregated stats
+ statsList.add(fetchStats(serverName, period, networkInterface.getName()));
+ }
+ } catch(Exception e) {
+ // server might be offline - continue with next one
+ logger.warn("Couldn't fetch Network stats from server [" + serverName + "]!", e);
+ }
+ }
+ }
+
+ @Override
+ public ServerStats fetchStats(String serverName, String period, String... args) {
+ ServerStats stats = super.fetchStats(serverName, period, args);
+
+ // the data returned by rrd contains "bytes/sec". Update the stats object to represent KiB/s
+ for(ServerStatsRow row : stats.getRows()) {
+ List<Double> data = row.getUsageData();
+ for (int i = 0; i < data.size(); i++) {
+ Double val = data.get(i);
+ data.set(i, val / 1024);
+ }
+ }
+
+ return stats;
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/PasswordManager.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/PasswordManager.java
new file mode 100644
index 00000000..a44b5bf3
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/PasswordManager.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.gateway.utils;
+
+import org.apache.derby.jdbc.EmbeddedDriver;
+import org.gluster.storage.management.core.constants.CoreConstants;
+import org.gluster.storage.management.gateway.security.UserAuthDao;
+import org.springframework.jdbc.datasource.SimpleDriverDataSource;
+import org.springframework.security.authentication.dao.ReflectionSaltSource;
+import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
+
+
+/**
+ * Tool to reset password of default user gluster
+ */
+public class PasswordManager {
+ private static final int USAGE_ERR = 1;
+ private static final int SQL_ERR = 2;
+
+ private void resetPassword(String username) {
+ try {
+ UserAuthDao userAuthDao = createUserAuthDao();
+ ReflectionSaltSource saltSource = createSaltSource();
+
+ String encodedPassword = new ShaPasswordEncoder(256).encodePassword(CoreConstants.DEFAULT_PASSWORD,
+ saltSource.getSalt(userAuthDao.loadUserByUsername(username)));
+
+ userAuthDao.changePassword(username, encodedPassword);
+
+ System.out.println("Password for user [" + username + "] reset successsfully to default value of ["
+ + CoreConstants.DEFAULT_PASSWORD + "]." + CoreConstants.NEWLINE);
+
+ DBUtil.shutdownDerby();
+ } catch (Exception e) {
+ System.err.println(CoreConstants.NEWLINE + CoreConstants.NEWLINE + "Password reset for user [" + username
+ + "] failed! " + CoreConstants.NEWLINE
+ + "Make sure that the Management Gateway is not running while performing password reset."
+ + CoreConstants.NEWLINE);
+ System.exit(SQL_ERR);
+ }
+ }
+
+ private ReflectionSaltSource createSaltSource() {
+ ReflectionSaltSource saltSource = new ReflectionSaltSource();
+ saltSource.setUserPropertyToUse("username");
+ return saltSource;
+ }
+
+ private UserAuthDao createUserAuthDao() throws InstantiationException, IllegalAccessException,
+ ClassNotFoundException {
+ UserAuthDao authDao = new UserAuthDao();
+ EmbeddedDriver driver = (EmbeddedDriver) Class.forName(EmbeddedDriver.class.getName()).newInstance();
+ SimpleDriverDataSource dataSource = new SimpleDriverDataSource(driver, "jdbc:derby:/opt/glustermg/data", "gluster", "syst3m");
+
+ authDao.setDataSource(dataSource);
+ return authDao;
+ }
+
+ public static void main(String args[]) {
+ if (args.length != 2 || !args[0].equals("reset")) {
+ System.err.println("Usage: java " + PasswordManager.class.getName() + " reset <username>\n");
+ System.exit(USAGE_ERR);
+ }
+
+ new PasswordManager().resetPassword(args[1]);
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/ServerUtil.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/ServerUtil.java
new file mode 100644
index 00000000..f65c5640
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/ServerUtil.java
@@ -0,0 +1,370 @@
+/**
+ * ServerUtil.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.gateway.utils;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+
+import org.apache.log4j.Logger;
+import org.gluster.storage.management.core.constants.CoreConstants;
+import org.gluster.storage.management.core.exceptions.ConnectionException;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.model.Server;
+import org.gluster.storage.management.core.model.Status;
+import org.gluster.storage.management.core.model.Server.SERVER_STATUS;
+import org.gluster.storage.management.core.response.StringListResponse;
+import org.gluster.storage.management.core.utils.ProcessResult;
+import org.gluster.storage.management.core.utils.ProcessUtil;
+import org.gluster.storage.management.gateway.services.GlusterInterfaceService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.ContextLoader;
+
+
+@Component
+public class ServerUtil {
+ @Autowired
+ ServletContext servletContext;
+
+ @Autowired
+ private SshUtil sshUtil;
+
+ @Autowired
+ private String appVersion;
+
+ private static final Logger logger = Logger.getLogger(ServerUtil.class);
+
+ private static final String SCRIPT_DIR = "scripts";
+ private static final String SCRIPT_COMMAND = "python";
+ private static final String REMOTE_SCRIPT_GET_DISK_FOR_DIR = "get_disk_for_dir.py";
+ private static final String REMOTE_SCRIPT_GET_SERVER_DETAILS = "get_server_details.py";
+ private static final String REMOTE_SCRIPT_GET_FILE_SYSTEM_TYPE = "get_filesystem_type.py";
+ private static final String REMOTE_SCRIPT_BASE_DIR = "/opt/glustermg";
+ private static final String REMOTE_SCRIPT_DIR_NAME = "backend";
+
+ public void setSshUtil(SshUtil sshUtil) {
+ this.sshUtil = sshUtil;
+ }
+
+ public ProcessResult executeGlusterScript(boolean runInForeground, String scriptName, String...arguments) {
+ return executeGlusterScript(runInForeground, scriptName, Arrays.asList(arguments));
+ }
+
+ public ProcessResult executeGlusterScript(boolean runInForeground, String scriptName, List<String> arguments) {
+ List<String> command = new ArrayList<String>();
+
+ command.add(SCRIPT_COMMAND);
+ command.add(getScriptPath(scriptName));
+ command.addAll(arguments);
+ return ProcessUtil.executeCommand(runInForeground, command);
+ }
+
+ private String getScriptPath(String scriptName) {
+ return servletContext.getRealPath(SCRIPT_DIR) + CoreConstants.FILE_SEPARATOR + scriptName;
+ }
+
+ private String getRemoteScriptDir() {
+ return REMOTE_SCRIPT_BASE_DIR + File.separator + appVersion + File.separator + REMOTE_SCRIPT_DIR_NAME;
+ }
+
+ /**
+ * Fetch details of the given server. The server name must be populated in the object before calling this method.
+ *
+ * @param server
+ * Server whose details are to be fetched
+ */
+ public void fetchServerDetails(Server server) {
+ try {
+ Server serverDetails = fetchServerDetails(server.getName());
+ server.copyFrom(serverDetails); // Update the details in <Server> object
+ server.setDisks(serverDetails.getDisks());
+ } catch (ConnectionException e) {
+ logger.warn("Couldn't connect to server [" + server.getName() + "]. Marking it offline!", e);
+ server.setStatus(SERVER_STATUS.OFFLINE);
+ }
+ }
+
+ public boolean isServerOnline(Server server) {
+ // fetch latest details and check if server is still online
+ fetchServerDetails(server);
+ return server.isOnline();
+ }
+
+ public String fetchHostName(String serverName) {
+ Object response = fetchServerDetails(serverName);
+ return ((Server) response).getName();
+ }
+
+ private Server fetchServerDetails(String serverName) {
+ // fetch standard server details like cpu, disk, memory details
+ return executeScriptOnServer(serverName, REMOTE_SCRIPT_GET_SERVER_DETAILS, Server.class);
+ }
+
+ /**
+ * Executes given script on all given servers in parallel, collects the output in objects of given class, and
+ * returns a list of all returned objects.
+ *
+ * @param serverNames
+ * @param scriptWithArgs
+ * @param expectedClass
+ * @param failOnError
+ * If true, an exception will be thrown as soon as the script execution fails on any of the servers. If
+ * false, the exception will be caught and logged. Execution on all other servers will continue.
+ * @return
+ */
+ public <T> List<T> executeScriptOnServers(List<String> serverNames, String scriptWithArgs,
+ Class<T> expectedClass, boolean failOnError) {
+ List<T> result = Collections.synchronizedList(new ArrayList<T>());
+ try {
+ List<Thread> threads = createScriptExecutionThreads(serverNames, getRemoteScriptDir() + File.separator
+ + scriptWithArgs, expectedClass, result, failOnError);
+ ProcessUtil.waitForThreads(threads);
+ return result;
+ } catch (InterruptedException e) {
+ String errMsg = "Exception while executing script [" + scriptWithArgs + "] on servers [" + serverNames + "]! Error: [" + e.getMessage() + "]";
+ logger.error(errMsg, e);
+ throw new GlusterRuntimeException(errMsg, e);
+ }
+ }
+
+ /**
+ * Creates threads that will run in parallel and execute the given command on each of the given servers
+ *
+ * @param serverNames
+ * @param commandWithArgs
+ * @param expectedClass
+ * @param result
+ * @param failOnError
+ * If true, an exception will be thrown as soon as the script execution fails on any of the servers. If
+ * false, the exception will be caught and logged. Execution on all other servers will continue.
+ * @return
+ * @throws InterruptedException
+ */
+ private <T> List<Thread> createScriptExecutionThreads(List<String> serverNames, String commandWithArgs, Class<T> expectedClass, List<T> result,
+ boolean failOnError)
+ throws InterruptedException {
+ List<Thread> threads = new ArrayList<Thread>();
+ for (int i = serverNames.size()-1; i >= 0 ; i--) {
+ Thread thread = new RemoteExecutionThread<T>(serverNames.get(i), commandWithArgs, expectedClass, result, failOnError);
+ threads.add(thread);
+ thread.start();
+ if(i >= 5 && i % 5 == 0) {
+ // After every 5 servers, wait for 1 second so that we don't end up with too many running threads
+ Thread.sleep(1000);
+ }
+ }
+ return threads;
+ }
+
+
+ public class RemoteExecutionThread<T> extends Thread {
+ private String serverName;
+ private String commandWithArgs;
+ private List<T> result;
+ private Class<T> expectedClass;
+ private boolean failOnError = false;
+
+ public RemoteExecutionThread(String serverName, String commandWithArgs, Class<T> expectedClass, List<T> result,
+ boolean failOnError) {
+ this.serverName = serverName;
+ this.commandWithArgs = commandWithArgs;
+ this.result = result;
+ this.expectedClass = expectedClass;
+ this.failOnError = failOnError;
+ }
+
+ @Override
+ public void run() {
+ try {
+ result.add(executeOnServer(serverName, commandWithArgs, expectedClass));
+ } catch(Exception e) {
+ String errMsg = "Couldn't execute command [" + commandWithArgs + "] on [" + serverName + "]!";
+ logger.error(errMsg, e);
+ if(failOnError) {
+ throw new GlusterRuntimeException(errMsg, e);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Executes given script on given server. Since the remote server may contain multiple versions of backend, this
+ * method will invoke the script present in directory of same version as the gateway.
+ *
+ * @param serverName
+ * @param scriptWithArgs
+ * The script name followed by arguments to be passed. Note that the script name should not contain path
+ * as it will be automatically identified by the method.
+ * @param expectedClass
+ * Class of the object expected from script execution
+ * @return Output (console/error) from the script execution
+ * @throws GlusterRuntimeException in case the remote execution fails.
+ */
+ public String executeScriptOnServer(String serverName, String scriptWithArgs) {
+ return executeOnServer(serverName, getRemoteScriptDir() + File.separator + scriptWithArgs, String.class);
+ }
+
+ /**
+ * Executes given script on given server. Since the remote server may contain multiple versions of backend, this
+ * method will invoke the script present in directory of same version as the gateway.
+ *
+ * @param serverName
+ * @param scriptWithArgs
+ * The script name followed by arguments to be passed. Note that the script name should not contain path
+ * as it will be automatically identified by the method.
+ * @param expectedClass
+ * Class of the object expected from script execution
+ * @return Object of the expected class from remote execution of the command.
+ * @throws GlusterRuntimeException in case the remote execution fails.
+ */
+ public <T> T executeScriptOnServer(String serverName, String scriptWithArgs,
+ Class<T> expectedClass) {
+ return executeOnServer(serverName, getRemoteScriptDir() + File.separator + scriptWithArgs,
+ expectedClass);
+ }
+
+ /**
+ * Executes given command on given server
+ *
+ * @param serverName
+ * @param commandWithArgs
+ * @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("unchecked")
+ public <T> T executeOnServer(String serverName, String commandWithArgs,
+ Class<T> expectedClass) {
+ String output = executeOnServer(serverName, commandWithArgs);
+ if (expectedClass == String.class) {
+ return (T) output;
+ }
+
+ return unmarshal(expectedClass, output);
+ }
+
+ public String executeOnServer(String serverName, String commandWithArgs) {
+ ProcessResult result = sshUtil.executeRemote(serverName, commandWithArgs);
+
+ if (!result.isSuccess()) {
+ throw new GlusterRuntimeException("Command [" + commandWithArgs + "] failed on [" + serverName
+ + "] with error [" + result.getExitValue() + "][" + result.getOutput() + "]");
+ }
+ return result.getOutput();
+ }
+
+ // This is the old executeOnServer that used socket communication.
+ // We can keep it commented for the time being.
+ // private String executeOnServerUsingSocket(String serverName, String commandWithArgs) {
+ // try {
+ // InetAddress address = InetAddress.getByName(serverName);
+ // Socket connection = new Socket(address, 50000);
+ //
+ // PrintWriter writer = new PrintWriter(connection.getOutputStream(), true);
+ // writer.println(commandWithArgs);
+ // writer.println(); // empty line means end of request
+ //
+ // InputStream inputStream = connection.getInputStream();
+ // int available = inputStream.available();
+ //
+ // StringBuffer output = new StringBuffer();
+ // if( available > 0 ) {
+ // // This happens when PeerAgent sends complete file
+ // byte[] responseData = new byte[available];
+ // inputStream.read(responseData);
+ // output.append(new String(responseData, "UTF-8"));
+ // } else {
+ // // This happens in case of normal XML response from PeerAgent
+ // BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
+ //
+ // String line;
+ // while (!(line = reader.readLine()).trim().isEmpty()) {
+ // output.append(line + CoreConstants.NEWLINE);
+ // }
+ // }
+ // connection.close();
+ //
+ // return output.toString();
+ // } catch (Exception e) {
+ // throw new GlusterRuntimeException("Error during remote execution: [" + e.getMessage() + "]");
+ // }
+ // }
+
+ public void getFileFromServer(String serverName, String remoteFileName, String localDirName) {
+ sshUtil.getFile(serverName, remoteFileName, localDirName);
+ }
+
+ /**
+ * Unmarshals given input string into object of given class
+ *
+ * @param expectedClass
+ * Class whose object is expected
+ * @param input
+ * Input string
+ * @return Object of given expected class
+ */
+ public <T> T unmarshal(Class<T> expectedClass, String input) {
+ try {
+ // create JAXB context and instantiate marshaller
+ JAXBContext context = JAXBContext.newInstance(expectedClass);
+ Unmarshaller um = context.createUnmarshaller();
+ return (T)um.unmarshal(new ByteArrayInputStream(input.getBytes()));
+ } catch (JAXBException e) {
+ String errMsg = "Error during unmarshalling string [" + input + "] for class [" + expectedClass.getName()
+ + ": [" + e.getMessage() + "]";
+ logger.error(errMsg, e);
+ throw new GlusterRuntimeException(errMsg, e);
+ }
+ }
+
+ /**
+ * @param serverName
+ * Server on which the directory is present
+ * @param brickDir
+ * Directory whose disk is to be fetched
+ * @return Status object containing the disk name, or error message in case the remote script fails.
+ */
+ public Status getDiskForDir(String serverName, String brickDir) {
+ return executeScriptOnServer(serverName, REMOTE_SCRIPT_GET_DISK_FOR_DIR + " " + brickDir, Status.class);
+ }
+
+ public <T> T getBean(Class<T> clazz) {
+ ApplicationContext ctx = ContextLoader.getCurrentWebApplicationContext();
+ return ctx.getBean(clazz);
+ }
+
+ public List<String> getFsTypes(String serverName) {
+ String output = executeScriptOnServer(serverName, REMOTE_SCRIPT_GET_FILE_SYSTEM_TYPE);
+ return Arrays.asList(output.trim().split(CoreConstants.NEWLINE));
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/SshUtil.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/SshUtil.java
new file mode 100644
index 00000000..074cbd5b
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/SshUtil.java
@@ -0,0 +1,463 @@
+/*******************************************************************************
+ * 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.gateway.utils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+
+import org.apache.log4j.Logger;
+import org.gluster.storage.management.core.constants.CoreConstants;
+import org.gluster.storage.management.core.exceptions.ConnectionException;
+import org.gluster.storage.management.core.exceptions.GlusterRuntimeException;
+import org.gluster.storage.management.core.utils.FileUtil;
+import org.gluster.storage.management.core.utils.LRUCache;
+import org.gluster.storage.management.core.utils.ProcessResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.ssh2.ChannelCondition;
+import ch.ethz.ssh2.Connection;
+import ch.ethz.ssh2.SCPClient;
+import ch.ethz.ssh2.Session;
+import ch.ethz.ssh2.StreamGobbler;
+
+
+/**
+ *
+ */
+@Component
+public class SshUtil {
+ private static final String TEMP_DIR = "/tmp/";
+ public static final String SSH_AUTHORIZED_KEYS_DIR_LOCAL = "/opt/glustermg/keys/";
+ public static final String SSH_AUTHORIZED_KEYS_DIR_REMOTE = "/root/.ssh/";
+ private static final String SSH_AUTHORIZED_KEYS_FILE = "authorized_keys";
+ private static final String SSH_AUTHORIZED_KEYS_PATH_REMOTE = SSH_AUTHORIZED_KEYS_DIR_REMOTE + SSH_AUTHORIZED_KEYS_FILE;
+ public static final File PRIVATE_KEY_FILE = new File(SSH_AUTHORIZED_KEYS_DIR_LOCAL + "gluster.pem");
+ public static final File PUBLIC_KEY_FILE = new File(SSH_AUTHORIZED_KEYS_DIR_LOCAL + "gluster.pub");
+ private LRUCache<String, Connection> sshConnCache = new LRUCache<String, Connection>(10);
+
+ // TODO: Make user name configurable
+ private static final String USER_NAME = "root";
+ // TODO: Make default password configurable
+ private static final String DEFAULT_PASSWORD = "syst3m";
+
+ private static final Logger logger = Logger.getLogger(SshUtil.class);
+
+ @Autowired
+ private Integer sshConnectTimeout;
+ @Autowired
+ private Integer sshKexTimeout;
+ @Autowired
+ private Integer sshExecTimeout;
+
+ public boolean hasDefaultPassword(String serverName) {
+ try {
+ getConnectionWithPassword(serverName).close();
+ return true;
+ } catch(Exception e) {
+ logger.warn("Couldn't connect to [" + serverName + "] with default password!", e);
+ return false;
+ }
+ }
+
+ /**
+ * Checks if public key of management gateway is configured on given server
+ *
+ * @param serverName
+ * @return true if public key is configured, else false
+ */
+ public boolean isPublicKeyInstalled(String serverName) {
+ try {
+ getConnectionWithPubKey(serverName).close();
+ return true;
+ } catch(ConnectionException e) {
+ logger.warn("Couldn't connect to [" + serverName + "] with public key!", e);
+ return false;
+ }
+ }
+
+ public void getFile(String serverName, String remoteFile, String localDir) {
+ try {
+ Connection conn = getConnection(serverName);
+ SCPClient scpClient = new SCPClient(conn);
+ scpClient.get(remoteFile, localDir);
+ } catch (IOException e) {
+ throw new GlusterRuntimeException("Error while fetching file [" + remoteFile + "] from server ["
+ + serverName + "]", e);
+ }
+ }
+
+ public synchronized void installPublicKey(String serverName) {
+ Connection conn = null;
+ try {
+ conn = getConnectionWithPassword(serverName);
+ } catch(Exception e) {
+ // authentication failed. close the connection.
+ conn.close();
+ if (e instanceof GlusterRuntimeException) {
+ throw (GlusterRuntimeException) e;
+ } else {
+ throw new GlusterRuntimeException("Exception during authentication with public key on server ["
+ + serverName + "]", e);
+ }
+ }
+ SCPClient scpClient = new SCPClient(conn);
+
+ // delete file if it exists
+ File localTempFile = new File(TEMP_DIR + SSH_AUTHORIZED_KEYS_FILE);
+ if(localTempFile.exists()) {
+ localTempFile.delete();
+ }
+
+ try {
+ // get authorized_keys from server
+ scpClient.get(SSH_AUTHORIZED_KEYS_PATH_REMOTE, TEMP_DIR);
+ } catch (IOException e) {
+ // file doesn't exist. it will get created.
+ // create the .ssh directory in case it doesn't exist
+ logger.info("Couldn't fetch file [" + SSH_AUTHORIZED_KEYS_PATH_REMOTE +"].", e);
+ logger.info("Creating /root/.ssh on [" + serverName + "] in case it doesn't exist.");
+ String command = "mkdir -p " + SSH_AUTHORIZED_KEYS_DIR_REMOTE;
+ ProcessResult result = executeCommand(conn, command);
+ if(!result.isSuccess()) {
+ String errMsg = "Command [" + command + "] failed on server [" + serverName + "] with error: " + result;
+ logger.error(errMsg);
+ throw new GlusterRuntimeException(errMsg);
+ }
+ }
+
+ byte[] publicKeyData;
+ try {
+ publicKeyData = FileUtil.readFileAsByteArray(PUBLIC_KEY_FILE);
+ } catch (Exception e) {
+ conn.close();
+ throw new GlusterRuntimeException("Couldn't load public key file [" + PUBLIC_KEY_FILE + "]", e);
+ }
+
+ try {
+ // append it
+ FileOutputStream outputStream = new FileOutputStream(localTempFile, true);
+ outputStream.write(CoreConstants.NEWLINE.getBytes());
+ outputStream.write(publicKeyData);
+ outputStream.close();
+ } catch (Exception e) {
+ conn.close();
+ throw new GlusterRuntimeException("Couldnt append file [" + localTempFile + "] with public key!", e);
+ }
+
+ try {
+ scpClient.put(localTempFile.getAbsolutePath(), SSH_AUTHORIZED_KEYS_FILE, SSH_AUTHORIZED_KEYS_DIR_REMOTE, "0600");
+ } catch (IOException e) {
+ throw new GlusterRuntimeException("Couldn't add public key to server [" + serverName + "]", e);
+ } finally {
+ conn.close();
+ localTempFile.delete();
+ }
+
+ // It was decided NOT to disable password login as this may not be acceptable in a bare-metal environment
+ // disableSshPasswordLogin(serverName, scpClient);
+ }
+
+// private void disableSshPasswordLogin(String serverName, SCPClient scpClient) {
+// ProcessResult result = executeRemote(serverName, SCRIPT_DISABLE_SSH_PASSWORD_AUTH);
+// if(!result.isSuccess()) {
+// throw new GlusterRuntimeException("Couldn't disable SSH password authentication on [" + serverName
+// + "]. Error: " + result);
+// }
+// }
+
+ private synchronized Connection getConnectionWithPassword(String serverName) {
+ Connection conn = createConnection(serverName);
+ if(!authenticateWithPassword(conn)) {
+ conn.close();
+ throw new ConnectionException("SSH Authentication (password) failed for server ["
+ + conn.getHostname() + "]");
+ }
+ return conn;
+ }
+
+ private synchronized Connection getConnectionWithPubKey(String serverName) {
+ Connection conn = createConnection(serverName);
+ if(!authenticateWithPublicKey(conn)) {
+ conn.close();
+ throw new ConnectionException("SSH Authentication (public key) failed for server ["
+ + conn.getHostname() + "]");
+ }
+ return conn;
+ }
+
+ private synchronized Connection getConnection(String serverName) {
+ Connection conn = sshConnCache.get(serverName);
+ if (conn != null) {
+ return conn;
+ }
+
+ conn = createConnection(serverName);
+ try {
+ if(!authenticateWithPublicKey(conn)) {
+ if(!authenticateWithPassword(conn)) {
+ conn.close();
+ throw new ConnectionException("SSH authentication failed on server [" + serverName + "]!");
+ }
+ }
+ } catch(Exception e) {
+ // authentication failed. close the connection.
+ conn.close();
+ if(e instanceof GlusterRuntimeException) {
+ throw (GlusterRuntimeException)e;
+ } else {
+ throw new GlusterRuntimeException("Exception during authentication on server [" + serverName + "]", e);
+ }
+ }
+
+ sshConnCache.put(serverName, conn);
+ return conn;
+ }
+
+ private boolean authenticateWithPublicKey(Connection conn) {
+ try {
+ if (!supportsPublicKeyAuthentication(conn)) {
+ throw new ConnectionException("Public key authentication not supported on [" + conn.getHostname()
+ + "]");
+ }
+
+ if (!conn.authenticateWithPublicKey(USER_NAME, PRIVATE_KEY_FILE, null)) {
+ return false;
+ }
+
+ return true;
+ } catch (IOException e) {
+ throw new ConnectionException("Exception during SSH authentication (public key) for server ["
+ + conn.getHostname() + "]", e);
+ }
+ }
+
+ private boolean authenticateWithPassword(Connection conn) {
+ try {
+ if (!supportsPasswordAuthentication(conn)) {
+ throw new ConnectionException("Password authentication not supported on [" + conn.getHostname()
+ + "]");
+ }
+
+ if (!conn.authenticateWithPassword(USER_NAME, DEFAULT_PASSWORD)) {
+ return false;
+ }
+ return true;
+ } catch (IOException e) {
+ throw new ConnectionException("Exception during SSH authentication (password) for server ["
+ + conn.getHostname() + "]", e);
+ }
+ }
+
+ private boolean supportsPasswordAuthentication(Connection conn) throws IOException {
+ return Arrays.asList(conn.getRemainingAuthMethods(USER_NAME)).contains("password");
+ }
+
+ private boolean supportsPublicKeyAuthentication(Connection conn) throws IOException {
+ return Arrays.asList(conn.getRemainingAuthMethods(USER_NAME)).contains("publickey");
+ }
+
+ private synchronized Connection createConnection(String serverName) {
+ Connection conn = new Connection(serverName);
+ try {
+ conn.connect(null, sshConnectTimeout, sshKexTimeout);
+ } catch (IOException e) {
+ logger.error("Couldn't establish SSH connection with server [" + serverName + "]", e);
+ conn.close();
+ throw new ConnectionException("Exception while creating SSH connection with server [" + serverName + "]", e);
+ }
+ return conn;
+ }
+
+ private boolean wasTerminated(int condition) {
+ return ((condition | ChannelCondition.EXIT_SIGNAL) == condition);
+ }
+
+ private boolean hasErrors(int condition, Session session) {
+ return (hasErrorStream(condition) || (exitedGracefully(condition) && exitedWithError(session)));
+ }
+
+ private boolean timedOut(int condition) {
+ return (condition == ChannelCondition.TIMEOUT);
+ }
+
+ private boolean exitedWithError(Session session) {
+ return session.getExitStatus() != ProcessResult.SUCCESS;
+ }
+
+ private boolean exitedGracefully(int condition) {
+ return (condition | ChannelCondition.EXIT_STATUS) == condition;
+ }
+
+ private boolean hasErrorStream(int condition) {
+ return (condition | ChannelCondition.STDERR_DATA) == condition;
+ }
+
+ private ProcessResult executeCommand(Connection sshConnection, String command) {
+ Session session = null;
+ try {
+ session = sshConnection.openSession();
+ BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(new StreamGobbler(
+ session.getStdout())));
+ BufferedReader stderrReader = new BufferedReader(new InputStreamReader(new StreamGobbler(
+ session.getStderr())));
+ session.execCommand(command);
+ ProcessResult result = getResultOfExecution(session, stdoutReader, stderrReader);
+ return result;
+ } catch (Exception e) {
+ String errMsg = "Exception while executing command [" + command + "] on [" + sshConnection.getHostname()
+ + "]";
+ logger.error(errMsg, e);
+
+ // remove the connection from cache and close it
+ sshConnCache.remove(sshConnection.getHostname());
+ sshConnection.close();
+ if(e instanceof IllegalStateException || e instanceof IOException) {
+ // The connection is no more valid. Create and throw a connection exception.
+ throw new ConnectionException("Couldn't open SSH session on [" + sshConnection.getHostname() + "]!", e);
+ } else {
+ throw new GlusterRuntimeException(errMsg, e);
+ }
+ } finally {
+ if(session != null) {
+ session.close();
+ }
+ }
+ }
+
+ private ProcessResult getResultOfExecution(Session session, BufferedReader stdoutReader, BufferedReader stderrReader) {
+ // Wait for program to come out either
+ // a) gracefully with an exit status, OR
+ // b) because of a termination signal
+ // c) command takes to long to exit (timeout)
+ int condition = session.waitForCondition(ChannelCondition.EXIT_SIGNAL | ChannelCondition.EXIT_STATUS,
+ sshExecTimeout);
+ StringBuilder output = new StringBuilder();
+
+ try {
+ if(!timedOut(condition)) {
+ readFromStream(stdoutReader, output);
+ if (hasErrors(condition, session)) {
+ readFromStream(stderrReader, output);
+ }
+ }
+
+ return prepareProcessResult(session, condition, output.toString().trim());
+ } catch (IOException e) {
+ String errMsg = "Error while reading output stream from SSH connection!";
+ logger.error(errMsg, e);
+ return new ProcessResult(ProcessResult.FAILURE, errMsg);
+ }
+ }
+
+ private ProcessResult prepareProcessResult(Session session, int condition, String output) {
+ ProcessResult result = null;
+
+ if (wasTerminated(condition)) {
+ result = new ProcessResult(ProcessResult.FAILURE, output);
+ } else if (timedOut(condition)) {
+ result = new ProcessResult(ProcessResult.FAILURE, "Command timed out!");
+ } else if (hasErrors(condition, session)) {
+ Integer exitStatus = session.getExitStatus();
+ int statusCode = (exitStatus == null ? ProcessResult.FAILURE : exitStatus);
+ result = new ProcessResult(statusCode, output);
+ } else {
+ result = new ProcessResult(ProcessResult.SUCCESS, output);
+ }
+
+ return result;
+ }
+
+ private void readFromStream(BufferedReader streamReader, StringBuilder output) throws IOException {
+ while (true) {
+ String line = streamReader.readLine();
+ if (line == null) {
+ break;
+ }
+ output.append(line + CoreConstants.NEWLINE);
+ }
+ }
+
+ /**
+ * Executes given command on remote machine using password authentication
+ *
+ * @param serverName
+ * @param command
+ * @return Result of remote execution
+ */
+ public ProcessResult executeRemoteWithPassword(String serverName, String command) {
+ logger.info("Executing command [" + command + "] on server [" + serverName + "] with default password.");
+ Connection conn = null;
+ try {
+ conn = getConnectionWithPassword(serverName);
+ return executeCommand(conn, command);
+ } finally {
+ // we don't cache password based connections. hence the connection must be closed.
+ if(conn != null) {
+ conn.close();
+ }
+ }
+ }
+
+ /**
+ * Executes given command on remote machine using public key authentication
+ *
+ * @param serverName
+ * @param command
+ * @return Result of remote execution
+ */
+ public ProcessResult executeRemote(String serverName, String command) {
+ logger.info("Executing command [" + command + "] on server [" + serverName + "]");
+ return executeCommand(getConnection(serverName), command);
+ }
+
+ public void cleanup() {
+ for (Connection conn : sshConnCache.values()) {
+ conn.close();
+ }
+ }
+
+ public Integer getSshConnectTimeout() {
+ return sshConnectTimeout;
+ }
+
+ public void setSshConnectTimeout(Integer sshConnectTimeout) {
+ this.sshConnectTimeout = sshConnectTimeout;
+ }
+
+ public Integer getSshKexTimeout() {
+ return sshKexTimeout;
+ }
+
+ public void setSshKexTimeout(Integer sshKexTimeout) {
+ this.sshKexTimeout = sshKexTimeout;
+ }
+
+ public Integer getSshExecTimeout() {
+ return sshExecTimeout;
+ }
+
+ public void setSshExecTimeout(Integer sshExecTimeout) {
+ this.sshExecTimeout = sshExecTimeout;
+ }
+}
diff --git a/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/StatsFactory.java b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/StatsFactory.java
new file mode 100644
index 00000000..f856c05a
--- /dev/null
+++ b/src/org.gluster.storage.management.gateway/src/org/gluster/storage/management/gateway/utils/StatsFactory.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * 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.gateway.utils;
+
+import java.util.List;
+
+import org.gluster.storage.management.core.model.ServerStats;
+
+
+/**
+ *
+ */
+public interface StatsFactory {
+ public ServerStats fetchStats(String serverName, String period, String...args);
+ public ServerStats fetchAggregatedStats(List<String> serverName, String period);
+}