diff options
| author | Shireesh Anjal <shireesh@gluster.com> | 2011-08-01 11:54:01 +0530 |
|---|---|---|
| committer | Shireesh Anjal <shireesh@gluster.com> | 2011-08-01 11:54:01 +0530 |
| commit | 0d12b25f91cfcbb09687d774389d85b8c8ffa249 (patch) | |
| tree | 2d0dd66c137f207c0cecbf583b873d8b9a9f76c7 /src/com.gluster.storage.management.gateway | |
| parent | dda8f6f9845787a16835cef9771263040add8216 (diff) | |
Renamed project and package com.gluster.storage.management.server to com.gluster.storage.management.gateway
Diffstat (limited to 'src/com.gluster.storage.management.gateway')
125 files changed, 9094 insertions, 0 deletions
diff --git a/src/com.gluster.storage.management.gateway/.classpath b/src/com.gluster.storage.management.gateway/.classpath new file mode 100644 index 00000000..107e139d --- /dev/null +++ b/src/com.gluster.storage.management.gateway/.classpath @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v7.0"> + <attributes> + <attribute name="owner.project.facets" value="jst.web"/> + </attributes> + </classpathentry> + <classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/> + <classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.module.container"/> + <classpathentry combineaccessrules="false" kind="src" path="/com.gluster.storage.management.core"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/> + <classpathentry exported="true" kind="lib" path="/com.gluster.storage.management.client/lib/jersey-1.5/jersey-multipart-1.5.jar"/> + <classpathentry kind="output" path="WebContent/WEB-INF/classes"/> +</classpath> diff --git a/src/com.gluster.storage.management.gateway/.project b/src/com.gluster.storage.management.gateway/.project new file mode 100644 index 00000000..3e4dc06a --- /dev/null +++ b/src/com.gluster.storage.management.gateway/.project @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>com.gluster.storage.management.gateway</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.python.pydev.PyDevBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.wst.jsdt.core.javascriptValidator</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.wst.common.project.facet.core.builder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.wst.validation.validationbuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jem.workbench.JavaEMFNature</nature> + <nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature> + <nature>org.eclipse.wst.common.project.facet.core.nature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.eclipse.wst.jsdt.core.jsNature</nature> + </natures> +</projectDescription> diff --git a/src/com.gluster.storage.management.gateway/.settings/.jsdtscope b/src/com.gluster.storage.management.gateway/.settings/.jsdtscope new file mode 100644 index 00000000..3a28de0c --- /dev/null +++ b/src/com.gluster.storage.management.gateway/.settings/.jsdtscope @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="WebContent"/> + <classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/> + <classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject"> + <attributes> + <attribute name="hide" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/> + <classpathentry kind="output" path=""/> +</classpath> diff --git a/src/com.gluster.storage.management.gateway/.settings/org.eclipse.jdt.core.prefs b/src/com.gluster.storage.management.gateway/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..476db350 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +#Wed Dec 29 14:57:23 IST 2010 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/src/com.gluster.storage.management.gateway/.settings/org.eclipse.ltk.core.refactoring.prefs b/src/com.gluster.storage.management.gateway/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 00000000..5d9c71b6 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,3 @@ +#Mon Jan 31 15:29:36 IST 2011 +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.common.component b/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.common.component new file mode 100644 index 00000000..3d5a50bf --- /dev/null +++ b/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.common.component @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project-modules id="moduleCoreId" project-version="1.5.0"> + <wb-module deploy-name="com.gluster.storage.management.gateway"> + <wb-resource deploy-path="/" source-path="/WebContent"/> + <wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/> + <wb-resource deploy-path="/WEB-INF/classes" source-path="/junit"/> + <property name="java-output-path" value="/com.gluster.storage.management.gateway/build/classes"/> + <property name="context-root" value="glustermg"/> + </wb-module> +</project-modules> diff --git a/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.common.project.facet.core.xml b/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 00000000..8f960f27 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?><!-- + Copyright (c) 2011 IBM Corporation and others. + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html + + Contributors: + IBM Corporation - initial API and implementation + --> + +<faceted-project> + <runtime name="Apache Tomcat v7.0"/> + <fixed facet="wst.jsdt.web"/> + <fixed facet="jst.web"/> + <fixed facet="java"/> + <installed facet="java" version="1.6"/> + <installed facet="jst.web" version="3.0"/> + <installed facet="wst.jsdt.web" version="1.0"/> +</faceted-project> diff --git a/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.jsdt.ui.superType.container b/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.jsdt.ui.superType.container new file mode 100644 index 00000000..3bd5d0a4 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.jsdt.ui.superType.container @@ -0,0 +1 @@ +org.eclipse.wst.jsdt.launching.baseBrowserLibrary
\ No newline at end of file diff --git a/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.jsdt.ui.superType.name b/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.jsdt.ui.superType.name new file mode 100644 index 00000000..05bd71b6 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.jsdt.ui.superType.name @@ -0,0 +1 @@ +Window
\ No newline at end of file diff --git a/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.ws.service.policy.prefs b/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.ws.service.policy.prefs new file mode 100644 index 00000000..e5ca6272 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/.settings/org.eclipse.wst.ws.service.policy.prefs @@ -0,0 +1,3 @@ +#Mon Jan 31 15:29:36 IST 2011 +eclipse.preferences.version=1 +org.eclipse.wst.ws.service.policy.projectEnabled=false diff --git a/src/com.gluster.storage.management.gateway/WebContent/META-INF/MANIFEST.MF b/src/com.gluster.storage.management.gateway/WebContent/META-INF/MANIFEST.MF new file mode 100644 index 00000000..5e949512 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0
+Class-Path:
+
diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/antlr-2.7.6.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/antlr-2.7.6.jar Binary files differnew file mode 100644 index 00000000..3702b645 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/antlr-2.7.6.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/aopalliance-1.0.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/aopalliance-1.0.jar Binary files differnew file mode 100644 index 00000000..578b1a0c --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/aopalliance-1.0.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/asm-3.1.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/asm-3.1.jar Binary files differnew file mode 100644 index 00000000..8217cae0 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/asm-3.1.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/commons-collections-3.1.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/commons-collections-3.1.jar Binary files differnew file mode 100644 index 00000000..41e230fe --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/commons-collections-3.1.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/commons-logging-1.1.1.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/commons-logging-1.1.1.jar Binary files differnew file mode 100644 index 00000000..8758a96b --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/commons-logging-1.1.1.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/derby.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/derby.jar Binary files differnew file mode 100644 index 00000000..dc8ae8df --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/derby.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/derbytools.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/derbytools.jar Binary files differnew file mode 100644 index 00000000..ca367aae --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/derbytools.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/dom4j-1.6.1.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/dom4j-1.6.1.jar Binary files differnew file mode 100644 index 00000000..c8c4dbb9 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/dom4j-1.6.1.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/ganymed-ssh2-build250-LICENSE.txt b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/ganymed-ssh2-build250-LICENSE.txt new file mode 100644 index 00000000..3eddd42f --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/ganymed-ssh2-build250-LICENSE.txt @@ -0,0 +1,87 @@ +Copyright (c) 2006 - 2010 Christian Plattner. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+a.) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+b.) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+c.) Neither the name of Christian Plattner nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+
+This software includes work that was released under the following license:
+
+Copyright (c) 2005 - 2006 Swiss Federal Institute of Technology (ETH Zurich),
+ Department of Computer Science (http://www.inf.ethz.ch),
+ Christian Plattner. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+a.) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+b.) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+c.) Neither the name of ETH Zurich nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+
+The Java implementations of the AES, Blowfish and 3DES ciphers have been
+taken (and slightly modified) from the cryptography package released by
+"The Legion Of The Bouncy Castle".
+
+Their license states the following:
+
+Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+(http://www.bouncycastle.org)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/ganymed-ssh2-build250.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/ganymed-ssh2-build250.jar Binary files differnew file mode 100644 index 00000000..c0a9ac7b --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/ganymed-ssh2-build250.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/hibernate-jpa-2.0-api-1.0.0.Final.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/hibernate-jpa-2.0-api-1.0.0.Final.jar Binary files differnew file mode 100644 index 00000000..4c9ac4e9 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/hibernate-jpa-2.0-api-1.0.0.Final.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/hibernate3.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/hibernate3.jar Binary files differnew file mode 100644 index 00000000..c1c81141 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/hibernate3.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jackson-core-asl-1.5.5.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jackson-core-asl-1.5.5.jar Binary files differnew file mode 100644 index 00000000..a3248188 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jackson-core-asl-1.5.5.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jackson-jaxrs-1.5.5.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jackson-jaxrs-1.5.5.jar Binary files differnew file mode 100644 index 00000000..faf12cf6 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jackson-jaxrs-1.5.5.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jackson-mapper-asl-1.5.5.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jackson-mapper-asl-1.5.5.jar Binary files differnew file mode 100644 index 00000000..ca612580 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jackson-mapper-asl-1.5.5.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jackson-xc-1.5.5.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jackson-xc-1.5.5.jar Binary files differnew file mode 100644 index 00000000..57c2c670 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jackson-xc-1.5.5.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/javassist-3.12.0.GA.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/javassist-3.12.0.GA.jar Binary files differnew file mode 100644 index 00000000..8f692f4f --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/javassist-3.12.0.GA.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-client-1.5.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-client-1.5.jar Binary files differnew file mode 100644 index 00000000..62f790fa --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-client-1.5.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-core-1.5.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-core-1.5.jar Binary files differnew file mode 100644 index 00000000..92b38466 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-core-1.5.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-json-1.5.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-json-1.5.jar Binary files differnew file mode 100644 index 00000000..01d8c83b --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-json-1.5.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-multipart-1.5.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-multipart-1.5.jar Binary files differnew file mode 100644 index 00000000..1c134f05 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-multipart-1.5.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-server-1.5.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-server-1.5.jar Binary files differnew file mode 100644 index 00000000..a29d7409 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-server-1.5.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-spring-1.5.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-spring-1.5.jar Binary files differnew file mode 100644 index 00000000..c79b4490 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jersey-spring-1.5.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jettison-1.1.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jettison-1.1.jar Binary files differnew file mode 100644 index 00000000..e4e9c8c3 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jettison-1.1.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jnlp-servlet.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jnlp-servlet.jar Binary files differnew file mode 100644 index 00000000..23782c02 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jnlp-servlet.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jsr311-api-1.1.1.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jsr311-api-1.1.1.jar Binary files differnew file mode 100644 index 00000000..ec8bc818 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jsr311-api-1.1.1.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jta-1.1.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jta-1.1.jar Binary files differnew file mode 100644 index 00000000..6d225b76 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/jta-1.1.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/log4j-1.2.16.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/log4j-1.2.16.jar Binary files differnew file mode 100644 index 00000000..3f9d8476 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/log4j-1.2.16.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/mimepull-1.3.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/mimepull-1.3.jar Binary files differnew file mode 100644 index 00000000..48cc9295 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/mimepull-1.3.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.eclipse.equinox.common_3.6.0.v20100503.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.eclipse.equinox.common_3.6.0.v20100503.jar Binary files differnew file mode 100644 index 00000000..a5d37aa1 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.eclipse.equinox.common_3.6.0.v20100503.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.aop-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.aop-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..db6f529f --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.aop-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.asm-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.asm-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..7cd3a626 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.asm-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.aspects-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.aspects-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..11fc11b9 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.aspects-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.beans-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.beans-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..f2e2e927 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.beans-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.context-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.context-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..bd1367d8 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.context-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.context.support-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.context.support-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..e46f9eb8 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.context.support-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.core-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.core-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..ea9500d6 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.core-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.expression-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.expression-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..6ef99d8d --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.expression-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.instrument-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.instrument-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..e149290e --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.instrument-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.instrument.tomcat-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.instrument.tomcat-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..dcd1e4d9 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.instrument.tomcat-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.jdbc-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.jdbc-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..f7a3b837 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.jdbc-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.jms-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.jms-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..9cea2aef --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.jms-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.orm-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.orm-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..d0550ccd --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.orm-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.oxm-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.oxm-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..1688cf74 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.oxm-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.spring-library-3.0.5.RELEASE.libd b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.spring-library-3.0.5.RELEASE.libd new file mode 100644 index 00000000..b3aa6af8 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.spring-library-3.0.5.RELEASE.libd @@ -0,0 +1,21 @@ +Library-SymbolicName: org.springframework.spring +Library-Version: 3.0.5.RELEASE +Library-Name: Spring Framework +Import-Bundle: + org.springframework.aop;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.asm;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.aspects;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.beans;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.context;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.context.support;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.core;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.expression;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.jdbc;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.jms;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.orm;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.oxm;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.transaction;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.web;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.web.servlet;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + org.springframework.web.portlet;version="[3.0.5.RELEASE, 3.0.5.RELEASE]", + com.springsource.org.aopalliance;version="[1.0.0, 1.0.0]" diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.test-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.test-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..237995c5 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.test-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.transaction-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.transaction-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..2f52122b --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.transaction-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.web-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.web-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..5a2381a0 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.web-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.web.portlet-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.web.portlet-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..6ec9d681 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.web.portlet-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.web.servlet-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.web.servlet-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..6d13bd40 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.web.servlet-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.web.struts-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.web.struts-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..e351ae7b --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/org.springframework.web.struts-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/plugins/com.gluster.storage.management.client_1.0.0.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/plugins/com.gluster.storage.management.client_1.0.0.jar Binary files differnew file mode 100644 index 00000000..56222cbf --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/plugins/com.gluster.storage.management.client_1.0.0.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/plugins/com.gluster.storage.management.core_1.0.0.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/plugins/com.gluster.storage.management.core_1.0.0.jar Binary files differnew file mode 100644 index 00000000..d321e76c --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/plugins/com.gluster.storage.management.core_1.0.0.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/servlet-api.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/servlet-api.jar Binary files differnew file mode 100644 index 00000000..e5bc672b --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/servlet-api.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/slf4j-api-1.6.1.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/slf4j-api-1.6.1.jar Binary files differnew file mode 100644 index 00000000..f1f4fdd2 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/slf4j-api-1.6.1.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/spring-security-config-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/spring-security-config-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..78818fc5 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/spring-security-config-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/spring-security-core-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/spring-security-core-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..a8077d5d --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/spring-security-core-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/spring-security-web-3.0.5.RELEASE.jar b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/spring-security-web-3.0.5.RELEASE.jar Binary files differnew file mode 100644 index 00000000..26611b55 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/lib/spring-security-web-3.0.5.RELEASE.jar diff --git a/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/web.xml b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/web.xml new file mode 100644 index 00000000..631788ff --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/WEB-INF/web.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> + <display-name>glustermg</display-name> + <welcome-file-list> + <welcome-file>index.html</welcome-file> + <welcome-file>index.htm</welcome-file> + <welcome-file>index.jsp</welcome-file> + <welcome-file>default.html</welcome-file> + <welcome-file>default.htm</welcome-file> + <welcome-file>default.jsp</welcome-file> + </welcome-file-list> + <context-param> + <param-name>contextConfigLocation</param-name> + <param-value> + classpath:spring/gluster-server-security.xml + classpath:spring/gluster-server-base.xml + </param-value> + </context-param> + <listener> + <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> + </listener> + <listener> + <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> + </listener> + <servlet> + <servlet-name>gluster-resources-1.0</servlet-name> + <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class> + <init-param> + <param-name>com.sun.jersey.config.property.packages</param-name> + <param-value>com.gluster.storage.management.gateway.resources.v1_0</param-value> + </init-param> + <init-param> + <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name> + <param-value>com.gluster.storage.management.gateway.filters.GlusterResourceFilterFactory</param-value> + </init-param> + <load-on-startup>1</load-on-startup> + </servlet> + <servlet-mapping> + <servlet-name>gluster-resources-1.0</servlet-name> + <url-pattern>/1.0/*</url-pattern> + </servlet-mapping> + <servlet> + <servlet-name>JnlpDownloadServlet</servlet-name> + <servlet-class>jnlp.sample.servlet.JnlpDownloadServlet</servlet-class> + </servlet> + <servlet-mapping> + <servlet-name>JnlpDownloadServlet</servlet-name> + <url-pattern>*.jnlp</url-pattern> + </servlet-mapping> + <servlet-mapping> + <servlet-name>JnlpDownloadServlet</servlet-name> + <url-pattern>*.jar</url-pattern> + </servlet-mapping> + <filter> + <filter-name>springSecurityFilterChain</filter-name> + <filter-class> + org.springframework.web.filter.DelegatingFilterProxy + </filter-class> + </filter> + <filter-mapping> + <filter-name>springSecurityFilterChain</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + <security-constraint> + <web-resource-collection> + <web-resource-name>Gluster Management Gateway</web-resource-name> + <url-pattern>/*</url-pattern> + </web-resource-collection> + <user-data-constraint> + <transport-guarantee>CONFIDENTIAL</transport-guarantee> + </user-data-constraint> + </security-constraint> +</web-app>
\ No newline at end of file diff --git a/src/com.gluster.storage.management.gateway/WebContent/data/scripts/1.0.0/0-version.sql b/src/com.gluster.storage.management.gateway/WebContent/data/scripts/1.0.0/0-version.sql new file mode 100644 index 00000000..4c3d81d1 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/data/scripts/1.0.0/0-version.sql @@ -0,0 +1,2 @@ +create table version (version varchar(16) not null primary key); +insert into version(version) values('1.0.0');
\ No newline at end of file diff --git a/src/com.gluster.storage.management.gateway/WebContent/data/scripts/1.0.0/1-security-schema.sql b/src/com.gluster.storage.management.gateway/WebContent/data/scripts/1.0.0/1-security-schema.sql new file mode 100644 index 00000000..fdde5823 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/data/scripts/1.0.0/1-security-schema.sql @@ -0,0 +1,26 @@ +create table users( + username varchar(50) not null primary key, + password varchar(50) not null, + enabled smallint not null + ); + + create table authorities ( + username varchar(50) not null, + authority varchar(50) not null, + constraint fk_authorities_users foreign key(username) references users(username)); + create unique index ix_auth_username on authorities (username,authority); + +create table groups ( + id bigint generated by default as identity(start with 0) primary key, + group_name varchar(50) not null); + +create table group_authorities ( + group_id bigint not null, + authority varchar(50) not null, + constraint fk_group_authorities_group foreign key(group_id) references groups(id)); + +create table group_members ( + id bigint generated by default as identity(start with 0) primary key, + username varchar(50) not null, + group_id bigint not null, + constraint fk_group_members_group foreign key(group_id) references groups(id)); diff --git a/src/com.gluster.storage.management.gateway/WebContent/data/scripts/1.0.0/2-users-authorities-groups.sql b/src/com.gluster.storage.management.gateway/WebContent/data/scripts/1.0.0/2-users-authorities-groups.sql new file mode 100644 index 00000000..35ccf965 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/data/scripts/1.0.0/2-users-authorities-groups.sql @@ -0,0 +1,21 @@ +-- Create users +insert into users(username, password, enabled) values ('gluster','gluster',1); +insert into users(username, password, enabled) values ('guest','guest',1); + +-- Assign authorities to users (to be removed after implementing user group functionality) +insert into authorities(username,authority) values ('gluster','ROLE_USER'); +insert into authorities(username,authority) values ('gluster','ROLE_ADMIN'); +insert into authorities(username,authority) values ('guest','ROLE_USER'); + +-- Create user groups +insert into groups(group_name) values ('Users'); +insert into groups(group_name) values ('Administrators'); + +-- Add authorities to groups (functionality not yet implemented in code) +insert into group_authorities(group_id, authority) select id,'ROLE_USER' from groups where group_name='Users'; +insert into group_authorities(group_id, authority) select id,'ROLE_USER' from groups where group_name='Administrators'; +insert into group_authorities(group_id, authority) select id,'ROLE_ADMIN' from groups where group_name='Administrators'; + +-- Assign group members +insert into group_members(group_id, username) select id,'guest' from groups where group_name='Users'; +insert into group_members(group_id, username) select id,'gluster' from groups where group_name='Administrators';
\ No newline at end of file diff --git a/src/com.gluster.storage.management.gateway/WebContent/data/scripts/1.0.0/3-cluster-servers.sql b/src/com.gluster.storage.management.gateway/WebContent/data/scripts/1.0.0/3-cluster-servers.sql new file mode 100644 index 00000000..17ca62d2 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/data/scripts/1.0.0/3-cluster-servers.sql @@ -0,0 +1,16 @@ +create table cluster_info ( + id bigint generated by default as identity, + name varchar(255), + primary key (id)); + +create unique index ix_cluster_name on cluster_info (name); + +create table server_info ( + id bigint generated by default as identity, + name varchar(255), + cluster_id bigint, + primary key (id)); + +create unique index ix_cluster_server on server_info (name, cluster_id); + +alter table server_info add constraint FK_CLUSTER_ID foreign key (cluster_id) references cluster_info(id);
\ No newline at end of file diff --git a/src/com.gluster.storage.management.gateway/WebContent/index.html b/src/com.gluster.storage.management.gateway/WebContent/index.html new file mode 100644 index 00000000..4c90162a --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/index.html @@ -0,0 +1,8 @@ +<html> +<head> +<title>Gluster Management Console</title> +</head> +<body> +TODO: Identify the client's platform-browser and invoke appropriate JNLP URL. +</body> +</html> diff --git a/src/com.gluster.storage.management.gateway/WebContent/scripts/Globals.py b/src/com.gluster.storage.management.gateway/WebContent/scripts/Globals.py new file mode 100644 index 00000000..f8a07c25 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/scripts/Globals.py @@ -0,0 +1,123 @@ +# Copyright (C) 2010 Gluster, Inc. <http://www.gluster.com> +# This file is part of Gluster Storage Platform. +# +# Gluster Storage Platform is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 of +# the License, or (at your option) any later version. +# +# Gluster Storage Platform is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see +# <http://www.gnu.org/licenses/>. + +MULTICAST_GROUP = '224.224.1.1' +MULTICAST_PORT = 5353 +GLUSTER_PLATFORM_VERSION = "3.2" + +## System configuration constants +SYSCONFIG_NETWORK_DIR = "/etc/sysconfig/network-scripts" +DNSMASQ_CONF_DIR = "/etc/dnsmasq.d" + +FSTAB_FILE = "/etc/fstab" +NFS_EXPORTS_FILE = "/etc/exports" +SAMBA_CONF_FILE = "/etc/samba/smb.conf" +TIMEZONE_FILE = "/etc/timezone" +ZONEINFO_DIR = "/usr/share/zoneinfo" +LOCALTIME_FILE = "/etc/localtime" +KERBEROS_CONF_FILE = "/etc/krb5.conf" +NSSWITCH_CONF_FILE = "/etc/nsswitch.conf" +NTP_CONF_FILE = "/etc/ntp.conf" +MODPROBE_CONF_FILE = "/etc/modprobe.d/bonding.conf" +SYSCONFIG_NETWORK_FILE = "/etc/sysconfig/network" +RESOLV_CONF_FILE = "/etc/resolv.conf" +DNSMASQ_LEASE_FILE = "/var/tmp/dnsmasq.leases" +LIVE_MODE_FILE = "/etc/live" +ADD_SERVER_COMPLETED_FILE = "/var/tmp/installation-completed" + +DNSMASQ_DNS_CONF_FILE = DNSMASQ_CONF_DIR + "/dns.conf" +DNSMASQ_DHCP_CONF_FILE = DNSMASQ_CONF_DIR + "/dhcp.conf" +## + +## Base constants +MAX_PARTITION_SIZE = 16777216 # 16 TB +OS_PARTITION_SIZE = 4000 # 4 GB +SESSION_TIMEOUT = 1800 # 30 minutes +SERVER_AGENT_PORT = 50000 + +BOOT_PARTITION_LABEL = "GLUSTEROS" +DATA_PARTITION_LABEL = "GLUSTERDATA" +VOLUME_USER_DESCRIPTION = "Gluster Volume User" +SERVER_AGENT_RUN_USERNAME = "gluster" +INSTALLER_SERVER_NAME = "$installer$" + +GLUSTER_BASE_DIR = "/etc/glustermg" +GLUSTER_LUN_DIR = "/data" +REEXPORT_DIR = "/reexport" +NFS_EXPORT_DIR = "/nfs" +CIFS_EXPORT_DIR = "/cifs" +WEBDAV_DOCUMENT_ROOT_DIR = "/var/www/html" +UPDATES_DIR = "/UPDATES" +TRANSPORT_HOME_DIR = "/transport" +GLUSTERFS_LOG_DIR = "/var/log/glusterfs" +LOG_DIR = "/var/log/glustermg" + +GLUSTER_UPDATES_FILE = "updates.xml" +INSTALLER_STATUS_FILE = "/var/log/install-server-status.log" +INSTALL_PLATFORM_LOCK_FILE = "/var/lock/install-gluster-platform.lock" +LAST_ACCESSED_NETWORK_FILE = "last-accessed-network" +PREPARE_DATA_DISK_LOCK_FILE = "/var/tmp/prepare-data-disk.lock" +## + +## Derived constants +GLUSTER_CONF_DIR = GLUSTER_BASE_DIR + "/conf" +GLUSTER_TMP_DIR = GLUSTER_BASE_DIR + "/tmp" +VOLUME_CONF_DIR = GLUSTER_BASE_DIR + "/volumes" +SERVER_CONF_DIR = GLUSTER_BASE_DIR + "/servers" +DNS_RECORDS_DIR = GLUSTER_BASE_DIR + "/dns-records" +INSTALLER_CONF_DIR = SERVER_CONF_DIR + "/" + INSTALLER_SERVER_NAME + +GSN_USER_INFO_FILE = GLUSTER_BASE_DIR + "/gsn-user.info" +GLUSTER_VERSION_FILE = GLUSTER_BASE_DIR + "/version" +GLUSTER_UPDATE_SITE_FILE = GLUSTER_BASE_DIR + "/update-site" +GLUSTER_DIRECTORY_SERVICE_CONF_FILE = GLUSTER_BASE_DIR + "/directory.xml" +GLUSTER_TIME_CONF_FILE = GLUSTER_BASE_DIR + "/timeconfig.xml" +TRANSACTION_KEY_FILE = GLUSTER_BASE_DIR + "/transaction.key" +SERVER_COUNT_FILE = GLUSTER_BASE_DIR + "/server-count" +SIGNATURE_FILE = GLUSTER_BASE_DIR + "/.signature" +GLUSTER_SERVER_POOL_FILE = GLUSTER_BASE_DIR + "/pool" +GLUSTER_ADMIN_FILE = GLUSTER_BASE_DIR + "/.password" + +VOLUME_SMBCONF_FILE = VOLUME_CONF_DIR + "/volumes.smbconf.list" + +GLOBAL_NETWORK_FILE = INSTALLER_CONF_DIR + "/network.xml" +INSTALL_SERVER_CONF_FILE = INSTALLER_CONF_DIR + "/installer.xml" +INSTALLER_INFO_FILE = INSTALLER_CONF_DIR + "/installer.info" +INSTALLED_SERVER_COUNT_FILE = INSTALLER_CONF_DIR + "/installed-server-count" + +SESSION_FILE = GLUSTER_TMP_DIR + "/login.sessions" + +GENERAL_LOG_FILE = LOG_DIR + "/general.log" +INSTALLER_LOG_FILE = LOG_DIR + "/installer.log" +PEER_AGENT_LOG_FILE = LOG_DIR + "/peeragent.log" +SERVER_AGENT_LOG_FILE = LOG_DIR + "/serveragent.log" +TRANSPORT_AGENT_LOG_FILE = LOG_DIR + "/transport.log" +## + + +## Global variables +## TODO: These should be removed +DOWNLOAD_GLUSTER_UPDATE_PROCESS = None +DOWNLOAD_GLUSTER_UPDATE_LEVEL = None +DOWNLOAD_GLUSTER_CURRENT_UPDATE_LEVEL = None +DOWNLOAD_GLUSTER_UPDATE_MD5SUM = None +REQUEST_MAP = {} +VERSION_DICTONARY = {} +## + +AWS_WEB_SERVICE_URL = "http://169.254.169.254/latest" +REAL_SAMBA_CONF_FILE = "/etc/samba/real.smb.conf" diff --git a/src/com.gluster.storage.management.gateway/WebContent/scripts/Protocol.py b/src/com.gluster.storage.management.gateway/WebContent/scripts/Protocol.py new file mode 100644 index 00000000..ff073593 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/scripts/Protocol.py @@ -0,0 +1,438 @@ +# Copyright (C) 2009 Gluster, Inc. <http://www.gluster.com> +# This file is part of Gluster Storage Platform. +# +# Gluster Storage Platform is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 of +# the License, or (at your option) any later version. +# +# Gluster Storage Platform is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see +# <http://www.gnu.org/licenses/>. + +import xml +import xml.parsers.expat +import xml.dom.minidom as MDOM +import os +import Globals +import copy +import Utils + +XML_STRING = 0 +XML_FILE = 1 + +class XDOM: + _domObj = None + + def __init__(self): + self._domObj = MDOM.Document() + return + + @classmethod + def getText(self, nodeList): + rc = "" + for node in nodeList: + if node.nodeType == node.TEXT_NODE: + rc = rc + node.data + return rc.strip() + + def parseString(self, requestString): + try: + self._domObj = MDOM.parseString(requestString) + except xml.parsers.expat.ExpatError, e: + Utils.log("XML string parse error: %s" % str(e)) + return False + return True + + def parseFile(self, fileName): + try: + self._domObj = MDOM.parse(fileName) + except IOError, e: + Utils.log("error reading file: %s" % str(e)) + return False + except xml.parsers.expat.ExpatError, e: + Utils.log("XML file %s parse error: %s" % (fileName, str(e))) + return False + return True + + def setDomObj(self, dom): + if dom and type(dom) != type([]): + self._domObj = dom + return True + return False + + def createTextNode(self, text): + if not self._domObj: + return False + if not text: + return False + return self._domObj.createTextNode(str(text)) + + def createTag(self, tag, text=None): + if not self._domObj: + return None + if tag == None: + return None + + tagE = self._domObj.createElement(str(tag)) + if text: + tagEText = self._domObj.createTextNode(str(text)) + tagE.appendChild(tagEText) + return tagE + + def addTag(self, tag): + if not self._domObj: + return False + if not tag: + return False + + self._domObj.appendChild(tag) + return True + + def createTagRoute(self, tagRoute, text=None): + if not tagRoute: + return False + + tagList = tagRoute.split(".") + tag = None + previousTag = None + for tagName in tagList[:-1]: + newTag = self.createTag(tagName, None) + if not tag: + tag = newTag + previousTag = newTag + continue + previousTag.appendChild(newTag) + previousTag = newTag + + if previousTag: + previousTag.appendChild(self.createTag(tagList[-1], text)) + else: + tag = self.createTag(tagList[-1], text) + return tag + + def appendTagRoute(self, tagRoute, value=None): + if not self._domObj: + return False + if not tagRoute: + return False + + parentTagE = self._domObj + + tagNameList = tagRoute.split(".") + newTagRoute = tagNameList.pop(-1) + + for i in range(len(tagNameList), 0, -1): + tagE = self.getElementsByTagRoute(".".join(tagNameList[:i])) + if tagE: + parentTagE = tagE[0] + break + newTagRoute = tagNameList[i-1] + "." + newTagRoute + + newTagE = self.createTagRoute(newTagRoute, value) + if not newTagE: + return False + try: + parentTagE.appendChild(newTagE) + except xml.dom.HierarchyRequestErr, e: + Utils.log("error occured. %s" + str(e)) + return False + return True + + def setTextByTagRoute(self, tagRoute, tagValue): + if not self._domObj: + return None + + if not tagRoute: + return None + + tagE = self.getElementsByTagRoute(tagRoute) + if not tagE: + return False + + parentTagE = self.getElementsByTagRoute(".".join(tagRoute.split(".")[:-1])) + if not parentTagE: + return False + + parentTagE[0].childNodes.remove(tagE[0]) + parentTagE[0].appendChild(self.createTag(tagRoute.split(".")[-1], tagValue)) + return True + + def getElementsByTagRoute(self, tagRoute): + if not self._domObj: + return None + + if not tagRoute: + return None + + x = None + for tag in tagRoute.split("."): + if x is None: + x = self._domObj.getElementsByTagName(tag) + continue + if x == []: + break + x = x[0].getElementsByTagName(tag) + return x + + def getTextByTagRoute(self, tagRoute): + if not self._domObj: + return None + + x = self.getElementsByTagRoute(tagRoute) + if x: + return self.getText(x[0].childNodes) + return None + + def getElementsByTagName(self, name): + if not self._domObj: + return None + return self._domObj.getElementsByTagName(name) + + def writexml(self, fileName, indent="", addindent="", newl=""): + if not self._domObj: + return None + try: + fp = open(fileName, "w") + self._domObj.writexml(fp, indent, addindent, newl) + fp.close() + return True + except IOError: + return False + + def toString(self, indent=" ", newl="\n", encoding = None): + if not self._domObj: + return None + return self._domObj.toprettyxml(indent, newl, encoding) + + def toxml(self, encoding = None): + if not self._domObj: + return None + return self._domObj.toxml(encoding) + + def toprettyxml(self, indent=" ", newl="\n", encoding = None): + return self.toString(indent, newl, encoding) + + def getAttribute(self, attributeName): + if not attributeName: + return None + try: + return self.getElementsByTagName("command")[0].getAttribute(attributeName) + except IndexError: + return False + + def setAttribute(self, attributeName, attributeValue): + if not (attributeName and attributeValue): + return None + try: + return self.getElementsByTagName("command")[0].setAttribute(attributeName, attributeValue) + except IndexError: + return False + + def getRequestCommand(self): + return self.getAttribute("request") + + def getResponseCommand(self): + return self.getAttribute("response") + + def getResponseCode(self): + return self.getAttribute("response-code") + + def getMessageId(self): + return self.getAttribute("id") + + def getVersion(self): + return self.getAttribute("version") + + def getRequestAction(self): + return self.getAttribute("action") + + def setVersion(self, value): + return self.setAttribute("version", value) + + def setRequestAction(self, value): + return self.setAttribute("action", value) + + def createCommandTag(self, command, responseCode, id, version=Globals.GLUSTER_PLATFORM_VERSION): + commandTag = self._domObj.createElement("command") + commandTag.setAttribute("response", command) + commandTag.setAttribute("response-code", responseCode) + commandTag.setAttribute("id", id) + commandTag.setAttribute("version", version) + return commandTag +##--end of XDOM + +class RequestXml(XDOM): + def __init__(self, requestString, type=None): + if None == requestString: + XDOM.__init__(self) + return + try: + if None == type: + if os.path.isfile(requestString): + self._domObj = MDOM.parse(requestString) + else: + self._domObj = MDOM.parseString(requestString) + elif XML_FILE == type: + self._domObj = MDOM.parse(requestString) + elif XML_STRING == type: + self._domObj = MDOM.parseString(requestString) + except IOError: + XDOM.__init__(self) + except xml.parsers.expat.ExpatError: + XDOM.__init__(self) + +##--end of RequestXML + +class ResponseXml(XDOM): + _commandTag = None + def __init__(self, command, responseCode, id, version=Globals.GLUSTER_PLATFORM_VERSION): + XDOM.__init__(self) + if command and responseCode and id: + self._commandTag = self.createCommandTag(command, responseCode, id, version) + self._domObj.appendChild(self._commandTag) + + def appendCommand(self, command, responseCode, id, version=Globals.GLUSTER_PLATFORM_VERSION): + if command and responseCode and id: + self._commandTag = self.createCommandTag(command, responseCode, id, version) + self._domObj.appendChild(self._commandTag) + return True + return False + + def append(self, tagName, tagValue=None): + if not self._commandTag: + return False + tag = self.createTag(tagName, tagValue) + if tag: + self._commandTag.appendChild(tag) + return True + return False + + def appendTag(self, tag): + if not tag: + return False + if not self._commandTag: + return False + self._commandTag.appendChild(tag) + return True + + def appendTagRoute(self, tagRoute, value=None): + if not self._commandTag: + return False + if not tagRoute: + return False + + parentTagE = self._commandTag + + tagNameList = tagRoute.split(".") + newTagRoute = tagNameList.pop(-1) + + for i in range(len(tagNameList), 0, -1): + tagE = self.getElementsByTagRoute(".".join(["command"] + tagNameList[:i])) + if tagE: + parentTagE = tagE[0] + break + newTagRoute = tagNameList[i-1] + "." + newTagRoute + + newTagE = self.createTagRoute(newTagRoute, value) + if not newTagE: + return False + try: + parentTagE.appendChild(newTagE) + except xml.dom.HierarchyRequestErr, e: + Utils.log("error occured. %s" + str(e)) + return False + return True + + def appendTagRouteOld(self, tagRoute, value=None): + if not tagRoute: + return False + if not self._commandTag: + return False + + tmpTagRoute = "" + previousTagE = self._commandTag + tagE = None + for tagName in tagRoute.split("."): + if not tmpTagRoute: + tagE = self.getElementsByTagRoute("command." + tagName) + else: + tagE = self.getElementsByTagRoute("command." + tmpTagRoute + "." + tagName) + if not tagE: + break + if len(tagE) != 1: + return False + previousTagE = tagE[0] + if not tmpTagRoute: + tmpTagRoute = tagName + else: + tmpTagRoute = tmpTagRoute + "." + tagName + + if tmpTagRoute == tagRoute: + return False + newTagRoute = tagRoute[len(tmpTagRoute):] + if newTagRoute[0] == '.': + newTagRoute = newTagRoute[1:] + + if previousTagE.childNodes and previousTagE.childNodes[0].nodeType == previousTagE.TEXT_NODE: + return False + previousTagE.appendChild(self.createTagRoute(newTagRoute, value)) + return True +##--end of ResponseXml + +def test(): + #volumes = RequestXml(VolumeFile, XML_FILE).getElementsByTagRoute("volume-list.volume") + requestStr = '''<command request="create-volume" id="123" version="3.1"> +<volume> +<name>movies1</name> +<type>cluster mirror</type> +<start>512000</start> +<server>zresearch</server> +<vacl>192.168.20.*</vacl> +<vacl>192.168.30.*</vacl> +<nfs> +<export>no</export> +</nfs> +<cifs> +<export>no</export> +</cifs> +<webdav> +<export>no</export> +</webdav> +</volume> +</command>''' + + requestXml = RequestXml(requestStr) + print requestXml.getAttribute("") + +def test1(): + rs = ResponseXml("create-volume", "OK", "xyz") + rs.appendTagRoute("volume.detail.name", "music") + print rs.toprettyxml() + rs.append("volume", "data") + print rs.toprettyxml() + rs.appendTagRoute("volume.detail.ipaddr", "192.168.10.1") + print rs.toprettyxml() + print rs.appendTagRoute("volume.detail.ipaddr.v6", "ff:ff::ff::") + print rs.toprettyxml() + + print rs.getTextByTagRoute("command.volume.detail") + +def test2(): + rs = ResponseXml("download-volume-logs", "OK", "xyz") + te = rs.createTag("interface", None) + te.appendChild(rs.createTag("device", "DEVICE1")) + te.appendChild(rs.createTag("description", "my device one")) + rs.appendTag(te) + + te = rs.createTag("interface", None) + te.appendChild(rs.createTag("device", "DEVICE2")) + te.appendChild(rs.createTag("description", "my device two")) + rs.appendTag(te) + print rs.toprettyxml() + diff --git a/src/com.gluster.storage.management.gateway/WebContent/scripts/Utils.py b/src/com.gluster.storage.management.gateway/WebContent/scripts/Utils.py new file mode 100644 index 00000000..3408c14a --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/scripts/Utils.py @@ -0,0 +1,1059 @@ +# Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> +# This file is part of Gluster Storage Platform. +# +# Gluster Storage Platform is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 of +# the License, or (at your option) any later version. +# +# Gluster Storage Platform is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see +# <http://www.gnu.org/licenses/>. + +import sys +import os +import re +import socket +import struct +import syslog +import subprocess +#import spwd +import time +#import uuid +import tempfile +import grp +import pwd +import inspect +from datetime import datetime +import urllib + +import Globals +import Protocol + +RUN_COMMAND_ERROR = -1024 +LOG_SYSLOG = 1 +SYSLOG_REQUIRED = False +LOG_FILE_NAME = None +LOG_FILE_OBJ = None + + +def _getLogCode(priority): + if syslog.LOG_EMERG == priority: + return "M" + elif syslog.LOG_ALERT == priority: + return "A" + elif syslog.LOG_CRIT == priority: + return "C" + elif syslog.LOG_ERR == priority: + return "E" + elif syslog.LOG_WARNING == priority: + return "W" + elif syslog.LOG_NOTICE == priority: + return "N" + elif syslog.LOG_INFO == priority: + return "I" + elif syslog.LOG_DEBUG == priority: + return "D" + else: # UNKNOWN + return "X" + + +def setLogFile(fileName): + global LOG_FILE_NAME + + if fileName: + LOG_FILE_NAME = fileName + return True + return False + + +def closeLog(): + global LOG_FILE_OBJ + global SYSLOG_REQUIRED + + if SYSLOG_REQUIRED: + syslog.closelog() + SYSLOG_REQUIRED = False + return True + + if LOG_FILE_OBJ: + try: + LOG_FILE_OBJ.close() + LOG_FILE_OBJ = None + except IOError, e: + sys.stderr.write("Failed to close file: %s\n" % e) + return False + return True + + +def openLog(fileName=None): + global LOG_FILE_NAME + global LOG_FILE_OBJ + global SYSLOG_REQUIRED + + if fileName == LOG_SYSLOG: + syslog.openlog(os.path.basename(sys.argv[0])) + SYSLOG_REQUIRED = True + return True + + if fileName: + LOG_FILE_NAME = fileName + + if not LOG_FILE_NAME: + return False + + closeLog() + + try: + LOG_FILE_OBJ = open(LOG_FILE_NAME, "a") + except IOError, e: + sys.stderr.write("Failed to open file %s: %s\n" % (LOG_FILE_NAME, e)) + return False + return True + +def record(priority, message=None): + global LOG_FILE_OBJ + global SYSLOG_REQUIRED + + stack = inspect.stack()[1] + if stack[3] == "<module>": + prefix = "%s:%s:%s" % (stack[1], stack[2], stack[3]) + else: + prefix = "%s:%s:%s()" % (stack[1], stack[2], stack[3]) + + if type(priority) == type("") or type(priority) == type(u""): + logPriority = syslog.LOG_INFO + logMessage = priority + else: + logPriority = priority + logMessage = message + + if SYSLOG_REQUIRED: + syslog.syslog(logPriority, "[%s]: %s" % (prefix, logMessage)) + return + + fp = sys.stderr + if LOG_FILE_OBJ: + fp = LOG_FILE_OBJ + + fp.write("[%s] %s [%s]: %s" % (str(datetime.now()), _getLogCode(logPriority), prefix, logMessage)) + if logMessage[-1] != '\n': + fp.write("\n") + fp.flush() + return + + +def trace(message): + if message: + log(syslog.LOG_DEBUG, message) + + +def isString(value): + return (type(value) == type("") or type(value) == type(u"")) + + +def getTempFileName(): + filedesc, filename = tempfile.mkstemp(prefix="GSP_") + os.close(filedesc) + return filename + + +def runCommandBG(command, stdinFileObj=None, stdoutFileObj=None, stderrFileObj=None, + shell=False, root=None): + log("runCommandBG(): Trying to execute command [%s]" % command) + + if shell: + if not isString(command): + return None + else: + if isString(command): + command = command.split() + + if root == True: + if shell: + command = "sudo " + command + else: + command = ['sudo'] + command + elif isString(root): + if shell: + command = "sudo -u " + root + " " + command + else: + command = ['sudo', '-u', root] + command + + if not stdinFileObj: + stdinFileObj=subprocess.PIPE + if not stdoutFileObj: + stdoutFileObj=subprocess.PIPE + if not stderrFileObj: + stderrFileObj=subprocess.PIPE + + try: + process = subprocess.Popen(command, + bufsize=-1, + stdin=stdinFileObj, + stdout=stdoutFileObj, + stderr=stderrFileObj, + shell=shell) + return process + except OSError, e: + log("runCommandBG(): Failed to run command [%s]: %s" % (command, e)) + return None + + +def runCommand(command, + input='', output=False, + shell=False, root=None): + rv = {} + rv["Status"] = RUN_COMMAND_ERROR + rv["Stdout"] = None + rv["Stderr"] = None + + try: + stdinFileName = getTempFileName() + stdinFileObj = open(stdinFileName, "w") + stdinFileObj.write(input) + stdinFileObj.close() + stdinFileObj = open(stdinFileName, "r") + + stdoutFileName = getTempFileName() + stdoutFileObj = open(stdoutFileName, "w") + + stderrFileName = getTempFileName() + stderrFileObj = open(stderrFileName, "w") + except IOError, e: + log("Failed to create temporary file for executing command [%s]: %s" % (command, e)) + if output: + return rv + return rv["Status"] + + stdoutContent = None + stderrContent = None + + process = runCommandBG(command, + stdinFileObj=stdinFileObj, + stdoutFileObj=stdoutFileObj, + stderrFileObj=stderrFileObj, + shell=shell, root=root) + if process: + rv['Status'] = process.wait() + rv['Stdout'] = open(stdoutFileName).read() + rv['Stderr'] = open(stderrFileName).read() + + os.remove(stdinFileName) + os.remove(stdoutFileName) + os.remove(stderrFileName) + + log("runCommand(): execution status of command [%s] = [%s]" % (command, rv)) + + if output: + return rv + return rv["Status"] + + +def runCommandFG(command, stdout=False, stderr=False, + shell=False, root=None): + if stdout or stderr: + output = True + else: + output = False + return runCommand(command, output=output, shell=shell, root=root) + + +def IP2Number(ipString): + try: + return socket.htonl(struct.unpack("I", socket.inet_aton(ipString))[0]) + except socket.error: + return None + except TypeError: + return None + except struct.error: + return None + + +def Number2IP(number): + try: + return socket.inet_ntoa(struct.pack("I", socket.ntohl(number))) + except socket.error: + return None + except AttributeError: + return None + except ValueError: + return None + + +def computeHostName(hostName): + if not hostName: + return False + + hostPrefix = "" + for i in range(len(hostName), 0, -1): + pos = i - 1 + if hostName[pos].isdigit(): + continue + break + hostPrefix = hostName[:pos+1] + try: + hostIndex = int(hostName[pos+1:]) + except ValueError: + hostIndex = 0 + # TODO: Check the availablity of the (server) name + return "%s%s" % (hostPrefix, hostIndex + 1) + + +def daemonize(): + try: + pid = os.fork() + if pid > 0: + # exit first parent + sys.exit(0) + except OSError, e: + #sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) + return False + + # decouple from parent environment + os.chdir("/") + os.setsid() + os.umask(0) + + # do second fork + try: + pid = os.fork() + if pid > 0: + # exit from second parent + sys.exit(0) + except OSError, e: + #sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) + return False + + # redirect standard file descriptors + sys.stdout.flush() + sys.stderr.flush() + si = file("/dev/null", 'r') + so = file("/dev/null", 'a+') + se = file("/dev/null", 'a+', 0) + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + return True + + +def getDownloadStatus(fileName): + try: + lines = [line for line in open(fileName) + if "saved" in line or "%" in line] + except IOError: + return 0 + if not lines: + return 0 + if "saved" in lines[-1]: + return 100 + return lines[-1].split("%")[0].split()[-1] + + +def getMeminfo(): + """-> dict of data from meminfo (str:int). + Values are in kilobytes. + """ + import re + re_parser = re.compile(r'^(?P<key>\S*):\s*(?P<value>\d*)\s*kB' ) + result = {} + for line in open('/proc/meminfo'): + match = re_parser.match(line) + if not match: + continue # skip lines that don't parse + key, value = match.groups(['key', 'value']) + result[key] = int(value) + result['MemUsed'] = (result['MemTotal'] - result['MemFree']) + return result + + +def getCpuUsage(): + """-> dict of cpuid : (usertime, nicetime, systemtime, idletime) + cpuid "cpu" means the total for all CPUs. + cpuid "cpuN" means the value for CPU N. + """ + wanted_records = [line for line in open('/proc/stat') if + line.startswith('cpu')] + result = {} + for cpuline in wanted_records: + fields = cpuline.split()[:5] + data = map(int, fields[1:]) + result[fields[0]] = tuple(data) + return result + +def _getCpuStatList(): + try: + fp = open("/proc/stat") + cpuStatList = map(float, fp.readline().split()[1:]) + fp.close() + return cpuStatList + except IOError, e: + Utils.log("Failed to open /proc/stat: %s" % str(e)) + return None + +def getCpuUsageAvg(): + st1 = _getCpuStatList() + time.sleep(2) + st2 = _getCpuStatList() + if not (st1 and st2): + return None + delta = [st2[i] - st1[i] for i in range(len(st1))] + cpuPercent = sum(delta[:3]) / delta[3] * 100.0 + return str('%.4f' % cpuPercent) + +def getLoadavg(): + try: + loadavgstr = open('/proc/loadavg', 'r').readline().strip() + except IOError: + syslog.syslog(syslog.LOG_ERR, "failed to find cpu load") + return None + + data = map(float, loadavgstr.split()[1:]) + # returns 1 minute load average + return data[0] + + +def getInfinibandPortStatus(): + + """ Check for availability of infiniband port + and return which port is active in a key pair value + """ + + # Check for existence of infiniband ports + value = os.popen ("ls /sys/class/infiniband").readline().strip() + + if not value: + return None + + portlist = os.popen ("echo /sys/class/infiniband/*/ports/*").readline().split() + + portkeys = {} + + for port in portlist: + value = os.popen ("cat %s/state" % + port.strip()).readline().split(':')[1].strip() + portkeys[port.strip()] = value + + return portkeys + + +def getServerCount(): + try: + return int(open(Globals.SERVER_COUNT_FILE).read().strip()) + except IOError: + log("failed to read file %s" % Globals.SERVER_COUNT_FILE) + return 1 + except ValueError: + log("invalid number format in file %s" % Globals.SERVER_COUNT_FILE) + return 1 + + +def setServerCount(count): + try: + open(Globals.SERVER_COUNT_FILE, "w").write("%s\n" % count) + return True + except IOError: + log("failed to write file %s" % Globals.SERVER_COUNT_FILE) + return False + + +def getInstalledServerCount(): + try: + return int(open(Globals.INSTALLED_SERVER_COUNT_FILE).read().strip()) + except IOError: + log("failed to read file %s" % Globals.INSTALLED_SERVER_COUNT_FILE) + return 1 + except ValueError: + log("invalid number format in file %s" % Globals.INSTALLED_SERVER_COUNT_FILE) + return 1 + + +def setInstalledServerCount(count): + try: + open(Globals.INSTALLED_SERVER_COUNT_FILE, "w").write("%s\n" % count) + return True + except IOError: + log("failed to write file %s" % Globals.INSTALLED_SERVER_COUNT_FILE) + return False + + +def getLastInstalledServerIpList(): + ipList = {} + networkDom = Protocol.XDOM() + if not networkDom.parseFile(Globals.GLOBAL_NETWORK_FILE): + log("failed to parse file %s" % Globals.GLOBAL_NETWORK_FILE) + for tagE in networkDom.getElementsByTagRoute("server.interface"): + interfaceDom = Protocol.XDOM() + interfaceDom.setDomObj(tagE) + ipAddress = interfaceDom.getTextByTagRoute("ipaddr") + if ipAddress: + ipList[interfaceDom.getTextByTagRoute("device")] = ipAddress + return ipList + + +def getFreeIpAddress(device=None): + serverCount = getServerCount() + installedServerCount = getInstalledServerCount() + if serverCount == installedServerCount: + return None + + availableServerCount = serverCount - installedServerCount + ipList = getLastInstalledServerIpList() + + if not ipList: + return None + + if device: + if device not in ipList.keys(): + return None + deviceIpAddress = ipList[device] + else: + deviceIpAddress = ipList.values()[0] + ipNumber = IP2Number(deviceIpAddress) + + for i in range((ipNumber + availableServerCount), ipNumber, -1): + ipAddress = Number2IP(i) + if runCommandFG(["ping", "-qnc", "1", ipAddress]) != 0: + return ipAddress + return None + + +def getPasswordHash(userName): + try: + #return spwd.getspnam(userName).sp_pwd + return "Not implimented" + except KeyError: + return None + + +def getTransactionKey(): + try: + tokens = open(Globals.TRANSACTION_KEY_FILE).read().split(',') + except IOError: + return None, None + return tokens + + +def generateSignature(): + #return str(uuid.uuid4()) + ('--%f' % time.time()) + return ('--%f' % time.time()) + + +def getSignature(): + try: + return open(Globals.SIGNATURE_FILE).read().strip() + except IOError: + log(syslog.LOG_ERR, "unable to read signaure from %s file" % Globals.SIGNATURE_FILE) + return False + + +def storeSignature(signature, fileName=Globals.SIGNATURE_FILE): + try: + open(fileName, "w").write(signature + "\n") + except IOError: + log(syslog.LOG_ERR, "unable to write signature %s to %s file" % (signature, fileName)) + return False + return True + + +def isUserExist(userName): + try: + grp.getgrnam(userName).gr_gid + return True + except KeyError: + pass + try: + pwd.getpwnam(userName).pw_uid + return True + except KeyError: + pass + return False + + +def getGsnUserInfo(fileName=Globals.GSN_USER_INFO_FILE): + userInfo = {} + userInfo["UserId"] = None + userInfo["Password"] = None + try: + for line in open(fileName): + line = line.strip() + k = line[:line.index("=")] + v = line[line.index("=") + 1:] + if v[0] == "'" or v[0] == '"': + v = v[1:] + if v[-1] == "'" or v[-1] == '"': + v = v[:-1] + if k.upper() == "GSN_ID": + userInfo["UserId"] = v + if k.upper() == "GSN_PASSWORD": + userInfo["Password"] = v + except IOError, e: + log("Failed to read file %s: %s" % (fileName, e)) + return userInfo + + +def setGsnUserInfo(userInfo, fileName=Globals.GSN_USER_INFO_FILE): + try: + fp = open(fileName, "w") + fp.write("GSN_ID=%s\n" % userInfo["UserId"]) + fp.write("GSN_PASSWORD=%s\n" % userInfo["Password"]) + fp.close() + return True + except IOError, e: + log("Failed to write file %s: %s" % (fileName, e)) + return False + + +def getPlatformVersion(fileName=Globals.GLUSTER_VERSION_FILE): + versionInfo = {} + versionInfo["Version"] = None + versionInfo["Update"] = None + try: + lines = open(Globals.GLUSTER_VERSION_FILE).readlines() + for line in open(fileName): + line = line.strip() + k = line[:line.index("=")] + v = line[line.index("=") + 1:] + if v[0] == "'" or v[0] == '"': + v = v[1:] + if v[-1] == "'" or v[-1] == '"': + v = v[:-1] + if k.upper() == "VERSION": + versionInfo["Version"] = v + if k.upper() == "UPDATE": + versionInfo["Update"] = v + except IOError, e: + log("Failed to read file %s: %s" % (fileName, e)) + return versionInfo + + +def setPlatformVersion(versionInfo, fileName=Globals.GLUSTER_VERSION_FILE): + if isString(versionInfo): + tokens = versionInfo.strip().split(".") + if len(tokens) < 2: + log("Invalid version format %s. Expecting <MAJOR>.<MINOR>.<PATCHLEVEL>" % versionInfo) + return False + version = ".".join(tokens[:2]) + update = ".".join(tokens[2:]) + if not update: + update = "0" + else: + version = versionInfo["Version"] + update = versionInfo["Update"] + try: + fp = open(fileName, "w") + fp.write("VERSION=%s\n" % version) + fp.write("UPDATE=%s\n" % update) + fp.close() + return True + except IOError, e: + log("Failed to write file %s: %s" % (fileName, e)) + return False + + +def getGlusterUpdateDom(serverVersion): + errorMessage = "" + updateInfoDom = None + try: + baseUrl = open(Globals.GLUSTER_UPDATE_SITE_FILE).read().strip() + except IOError, e: + log("Failed to read file %s: %s" % (Globals.GLUSTER_UPDATE_SITE_FILE, e)) + errorMessage = "Failed to read update site file" + return updateInfoDom, errorMessage + + try: + url = "%s/%s/%s" % (baseUrl, serverVersion, Globals.GLUSTER_UPDATES_FILE) + connection = urllib.urlopen(url) + if connection.getcode() != 200: + connection.close() + errorMessage = "Error received from server to open URL %s" % url + return updateInfoDom, errorMessage + updateInfoString = connection.read() + connection.close() + except IOError, e: + log("Failed to get update information from URL %s: %s" % (url, e)) + errorMessage = "Error getting update information" + return updateInfoDom, errorMessage + + updateInfoDom = Protocol.XDOM() + if not updateInfoDom.parseString(updateInfoString): + log("XML parse error on update information content [%s]" % updateInfoString) + errorMessage = "Parse error on update information" + updateInfoDom = None + return updateInfoDom, errorMessage + + +def removeFile(fileName, root=False): + if root: + if runCommand("rm %s" % fileName, root=True) == 0: + return True + return False + try: + os.remove(fileName) + return True + except OSError, e: + log("Failed to remove file %s: %s" % (fileName, e)) + return False + + +def isLiveMode(): + return os.path.exists(Globals.LIVE_MODE_FILE) + +def convertKbToMb(kb): + return kb / 1024.0 + + +def getIPIndex(indexFile): + try: + fp = open(indexFile) + line = fp.readline() + fp.close() + index = int(line) + except IOError: + index = 0 + except ValueError: + index = False + return index + +def setIPIndex(index, indexFile): + try: + fp = open(indexFile, "w") + fp.write(str(index)) + fp.close() + except IOError: + return False + return True + +def IP2Number(ipString): + try: + return socket.htonl(struct.unpack("I", socket.inet_aton(ipString))[0]) + except socket.error: + return None + except TypeError: + return None + except struct.error: + return None + +def Number2IP(number): + try: + return socket.inet_ntoa(struct.pack("I", socket.ntohl(number))) + except socket.error: + return None + except AttributeError: + return None + except ValueError: + return None + +def hasEntryFoundInFile(searchString, dnsEntryFileName): + try: + addServerEntryList = open(dnsEntryFileName).read().split() + except IOError: + return None + if searchString in addServerEntryList: + return True + return False + + +def computeIpAddress(ipAddress, startIp, endIp): + startIpNumber = IP2Number(startIp) + endIpNumber = IP2Number(endIp) + if not ipAddress: + return startIp + nextIpNumber = IP2Number(ipAddress) + while True: + nextIpNumber = nextIpNumber + 1 + ipAddress = Number2IP(nextIpNumber) + rv = runCommandFG(["ping", "-qnc", "1", ipAddress]) + if type(rv) == type(True): + return False + if rv != 0: + break + + if nextIpNumber >= startIpNumber and nextIpNumber <= endIpNumber: + return ipAddress + + nextIpNumber = IP2Number(startIp) + while True: + ipAddress = Number2IP(nextIpNumber) + nextIpNumber = nextIpNumber + 1 + rv = runCommandFG(["ping", "-qnc", "1", ipAddress]) + if type(rv) == type(True): + return False + if rv != 0: + break + + if IP2Number(ipAddress) >= startIpNumber and IP2Number(ipAddress) <= endIpNumber: + return ipAddress + return False + + +def setHostNameAndIp(hostName, ipAddress, lastAddServerDetailFile): + try: + fp = open(lastAddServerDetailFile, "w") + fp.write("HOSTNAME=" + hostName + "\n") + fp.write("IPADDRESS=" + ipAddress); + fp.close() + except IOError: + return False + return True + +def getPort(): + try: + fd = open(Globals.PORT_FILE, "r") + portString = fd.readline() + fd.close() + port = int(portString) + except IOError: + port = Globals.DEFAULT_PORT - 2 + except ValueError: + port = Globals.DEFAULT_PORT - 2 + return port + +def setPort(port): + try: + fd = open(Globals.PORT_FILE, "w") + fd.write(str(port)) + fd.close() + except IOError: + return False + return True + +def getServerAgentCredentials(): + try: + lines = open(Globals.SERVERAGENT_AUTH_FILE).readlines() + except IOError: + return None,None + + userName = None + password = None + + for l in lines: + if l[-1] == '\n': + l = l[:-1] + k = l[:l.index('=')] + v = l[l.index('=') + 1:] + if v[0] == "'" or v[0] == '"': + v = v[1:] + if v[-1] == "'" or v[-1] == '"': + v = v[:-1] + if k.upper() == "AGENT_ID": + userName = v + if k.upper() == "AGENT_PASSWORD": + password = v + + return userName, password + +def getGatewayAgentCredentials(): + try: + lines = open(Globals.GATEWAYAGENT_AUTH_FILE).readlines() + except IOError: + return None + + #userName = None + password = None + + for l in lines: + if l[-1] == '\n': + l = l[:-1] + k = l[:l.index('=')] + v = l[l.index('=') + 1:] + if v[0] == "'" or v[0] == '"': + v = v[1:] + if v[-1] == "'" or v[-1] == '"': + v = v[:-1] + #if k.upper() == "AGENT_ID": + # userName = v + if k.upper() == "AGENT_PASSWORD": + password = v + + return password + +def getWebAgentCredentials(): + try: + lines = open(Globals.WEBAGENT_AUTH_FILE).readlines() + except IOError: + return None,None + + userName = None + password = None + + for l in lines: + if l[-1] == '\n': + l = l[:-1] + k = l[:l.index('=')] + v = l[l.index('=') + 1:] + if v[0] == "'" or v[0] == '"': + v = v[1:] + if v[-1] == "'" or v[-1] == '"': + v = v[:-1] + if k.upper() == "AGENT_ID": + userName = v + if k.upper() == "AGENT_PASSWORD": + password = v + + return userName, password + +def daemonize(): + try: + pid = os.fork() + if pid > 0: + # exit first parent + sys.exit(0) + except OSError, e: + #sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) + return False + + # decouple from parent environment + os.chdir("/") + os.setsid() + os.umask(0) + + # do second fork + try: + pid = os.fork() + if pid > 0: + # exit from second parent + sys.exit(0) + except OSError, e: + #sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) + return False + + # redirect standard file descriptors + sys.stdout.flush() + sys.stderr.flush() + si = file("/dev/null", 'r') + so = file("/dev/null", 'a+') + se = file("/dev/null", 'a+', 0) + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + return True + +def getFreeIpAddress(): + startRange, endRange = getStoragePoolInfo() + if not (startRange and endRange): + return None + + startIpNumber = IP2Number(startRange) + endIpNumber = IP2Number(endRange) + + for ipNumber in range(endIpNumber, startIpNumber, -1): + rv = runCommandFG(["ping", "-qnc", "1", Number2IP(ipNumber)]) + if type(rv) == type(True): + return None + if rv != 0: + return Number2IP(ipNumber) + return None + +def getDhcpServerStatus(): + status = runCommandFG(["sudo", "service", "dnsmasq", " status"]) + if type(status) == type(True) or 0 != status: + return False + return True + +def startDhcpServer(): + status = runCommandFG(["sudo", "service", "dnsmasq", " start"]) + if type(status) == type(True) or 0 != status: + return False + return True + +def stopDhcpServer(): + status = runCommandFG(["sudo", "service", "dnsmasq", " stop"]) + if type(status) == type(True) or 0 != status: + return False + return True + +def getStoragePoolInfo(): + startRange = None + endRange = None + try: + for line in open(Globals.GLUSTER_SERVER_POOL_FILE): + tokens = line.split("=") + if tokens[0] == "STARTRANGE": + startRange = tokens[1].strip() + if tokens[0] == "ENDRANGE": + endRange = tokens[1].strip() + except IOError: + log(syslog.LOG_ERR, "unable to read %s file" % Globals.GLUSTER_SERVER_POOL_FILE) + return startRange, endRange + +def configureDnsmasq(serverIpAddress, dhcpIpAddress): + dnsmasqConfFile = Globals.GLUSTER_CONF_CONF_DIR + "/dnsmasq.conf" + serverPortString = "68" + try: + for arg in open("/proc/cmdline").read().strip().split(): + token = arg.split("=") + if token[0] == "dhcp": + serverPortString = token[1] + break + except IOError: + log(syslog.LOG_ERR, "Failed to read /proc/cmdline. Continuing with default port 68") + try: + serverPort = int(serverPortString) + except ValueError: + log(syslog.LOG_ERR, "Invalid dhcp port '%s' in /proc/cmdline. Continuing with default port 68" % serverPortString) + serverPort = 68 + + try: + fp = open(dnsmasqConfFile, "w") + fp.write("no-hosts\n") + #fp.write("addn-hosts=%s\n" % Globals.GLUSTER_DNS_ENTRIES) + fp.write("bind-interfaces\n") + fp.write("except-interface=lo\n") + fp.write("dhcp-range=%s,%s\n" % (dhcpIpAddress, dhcpIpAddress)) + fp.write("dhcp-lease-max=1\n") + #fp.write("dhcp-option=option:router,%s\n" % serverIp) + #fp.write("dhcp-option=option:ntp-server,%s\n" % serverIp) + fp.write("dhcp-alternate-port=%s\n" % serverPort) + fp.write("server=%s\n" % serverIpAddress) + fp.write("dhcp-script=/usr/sbin/server-info\n") + fp.close() + except IOError: + log(syslog.LOG_ERR, "unable to write dnsmasq configuration %s" % dnsmasqConfFile) + return False + status = runCommandFG(["sudo", "cp", "-f", Globals.GLUSTER_CONF_CONF_DIR + "/dnsmasq.conf", Globals.DNSMASQ_CONF_FILE]) + if type(status) == type(True) or 0 != status: + log(syslog.LOG_ERR, "unable to copy dnsmasq configuration to " + Globals.DNSMASQ_CONF_FILE) + return False + return True + +def configureDhcpServer(serverIpAddress, dhcpIpAddress): + return configureDnsmasq(serverIpAddress, dhcpIpAddress) + +def log(priority, message=None): + if type(priority) == type(""): + logPriority = syslog.LOG_INFO + logMessage = priority + else: + logPriority = priority + logMessage = message + if not logMessage: + return + #if Globals.DEBUG: + # sys.stderr.write(logMessage) + else: + syslog.syslog(logPriority, logMessage) + return + + +def stripEmptyLines(content): + ret = "" + for line in content.split("\n"): + if line.strip() != "": + ret += line + return ret + + +def getDeviceFormatStatusFile(device): + return "/var/tmp/format_%s.status" % device.replace('/', '_') + +def getDeviceFormatLockFile(device): + return "/var/lock/format_%s.lock" % device.replace('/', '_') + +def getDeviceFormatOutputFile(device): + return "/var/tmp/format_%s.out" % device.replace('/', '_') diff --git a/src/com.gluster.storage.management.gateway/WebContent/scripts/XmlHandler.py b/src/com.gluster.storage.management.gateway/WebContent/scripts/XmlHandler.py new file mode 100644 index 00000000..72164ffb --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/scripts/XmlHandler.py @@ -0,0 +1,346 @@ +# Copyright (C) 2009 Gluster, Inc. <http://www.gluster.com>
+# This file is part of Gluster Storage Platform.
+#
+# Gluster Storage Platform is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 3 of
+# the License, or (at your option) any later version.
+#
+# Gluster Storage Platform is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+
+import xml
+import xml.parsers.expat
+import xml.dom.minidom as MDOM
+import os
+import Globals
+import copy
+import Utils
+
+XML_STRING = 0
+XML_FILE = 1
+
+class XDOM:
+ _domObj = None
+
+ def __init__(self):
+ self._domObj = MDOM.Document()
+ return
+
+ @classmethod
+ def getText(self, nodeList):
+ rc = ""
+ for node in nodeList:
+ if node.nodeType == node.TEXT_NODE:
+ rc = rc + node.data
+ return rc
+
+ def parseString(self, requestString):
+ try:
+ self._domObj = MDOM.parseString(requestString)
+ except xml.parsers.expat.ExpatError, e:
+ Utils.log("XML string parse error: %s" % str(e))
+ return False
+ return True
+
+ def parseFile(self, fileName):
+ try:
+ self._domObj = MDOM.parse(fileName)
+ except IOError, e:
+ Utils.log("error reading file: %s" % str(e))
+ return False
+ except xml.parsers.expat.ExpatError, e:
+ Utils.log("XML file %s parse error: %s" % (fileName, str(e)))
+ return False
+ return True
+
+ def setDomObj(self, dom):
+ if dom and type(dom) != type([]):
+ self._domObj = dom
+ return True
+ return False
+
+ def createTag(self, tag, text=None):
+ if not self._domObj:
+ return None
+ if tag == None:
+ return None
+
+ tagE = self._domObj.createElement(str(tag))
+ if text:
+ tagEText = self._domObj.createTextNode(str(text))
+ tagE.appendChild(tagEText)
+ return tagE
+
+ def addTag(self, tag):
+ if not self._domObj:
+ return False
+ if not tag:
+ return False
+
+ self._domObj.appendChild(tag)
+ return True
+
+ def createTagRoute(self, tagRoute, text=None):
+ if not tagRoute:
+ return False
+
+ tagList = tagRoute.split(".")
+ tag = None
+ previousTag = None
+ for tagName in tagList[:-1]:
+ newTag = self.createTag(tagName, None)
+ if not tag:
+ tag = newTag
+ previousTag = newTag
+ continue
+ previousTag.appendChild(newTag)
+ previousTag = newTag
+
+ if previousTag:
+ previousTag.appendChild(self.createTag(tagList[-1], text))
+ else:
+ tag = self.createTag(tagList[-1], text)
+ return tag
+
+ def appendTagRoute(self, tagRoute, value=None):
+ if not self._domObj:
+ return False
+ if not tagRoute:
+ return False
+
+ parentTagE = self._domObj
+
+ tagNameList = tagRoute.split(".")
+ newTagRoute = tagNameList.pop(-1)
+
+ for i in range(len(tagNameList), 0, -1):
+ tagE = self.getElementsByTagRoute(".".join(tagNameList[:i]))
+ if tagE:
+ parentTagE = tagE[0]
+ break
+ newTagRoute = tagNameList[i-1] + "." + newTagRoute
+
+ newTagE = self.createTagRoute(newTagRoute, value)
+ if not newTagE:
+ return False
+ try:
+ parentTagE.appendChild(newTagE)
+ except xml.dom.HierarchyRequestErr, e:
+ Utils.log("error occured. %s" + str(e))
+ return False
+ return True
+
+ def setTextByTagRoute(self, tagRoute, tagValue):
+ if not self._domObj:
+ return None
+
+ if not tagRoute:
+ return None
+
+ tagE = self.getElementsByTagRoute(tagRoute)
+ if not tagE:
+ return False
+
+ parentTagE = self.getElementsByTagRoute(".".join(tagRoute.split(".")[:-1]))
+ if not parentTagE:
+ return False
+
+ parentTagE[0].childNodes.remove(tagE[0])
+ parentTagE[0].appendChild(self.createTag(tagRoute.split(".")[-1], tagValue))
+ return True
+
+ def getElementsByTagRoute(self, tagRoute):
+ if not self._domObj:
+ return None
+
+ if not tagRoute:
+ return None
+
+ x = None
+ for tag in tagRoute.split("."):
+ if x is None:
+ x = self._domObj.getElementsByTagName(tag)
+ continue
+ if x == []:
+ break
+ x = x[0].getElementsByTagName(tag)
+ return x
+
+ def getTextByTagRoute(self, tagRoute):
+ if not self._domObj:
+ return None
+
+ x = self.getElementsByTagRoute(tagRoute)
+ if x:
+ return self.getText(x[0].childNodes)
+ return None
+
+ def getElementsByTagName(self, name):
+ if not self._domObj:
+ return None
+ return self._domObj.getElementsByTagName(name)
+
+ def writexml(self, fileName, indent="", addindent="", newl=""):
+ if not self._domObj:
+ return None
+ try:
+ fp = open(fileName, "w")
+ self._domObj.writexml(fp, indent, addindent, newl)
+ fp.close()
+ return True
+ except IOError:
+ return False
+
+ def toString(self, indent=" ", newl="\n", encoding = None):
+ if not self._domObj:
+ return None
+ return self._domObj.toprettyxml(indent, newl, encoding)
+
+ def toxml(self, encoding = None):
+ if not self._domObj:
+ return None
+ return self._domObj.toxml(encoding)
+
+ def toprettyxml(self, indent=" ", newl="\n", encoding = None):
+ return self.toString(indent, newl, encoding)
+
+ def createResponseTag(self):
+ responseTag = self._domObj.createElement("response")
+ return responseTag
+##--end of XDOM
+
+class RequestXml(XDOM):
+ def __init__(self, requestString, type=None):
+ if None == requestString:
+ XDOM.__init__(self)
+ return
+ try:
+ if None == type:
+ if os.path.isfile(requestString):
+ self._domObj = MDOM.parse(requestString)
+ else:
+ self._domObj = MDOM.parseString(requestString)
+ elif XML_FILE == type:
+ self._domObj = MDOM.parse(requestString)
+ elif XML_STRING == type:
+ self._domObj = MDOM.parseString(requestString)
+ except IOError:
+ XDOM.__init__(self)
+ except xml.parsers.expat.ExpatError:
+ XDOM.__init__(self)
+
+##--end of RequestXML
+
+
+class ResponseXml(XDOM):
+ _responseTag = None
+ def __init__(self):
+ XDOM.__init__(self)
+ self._responseTag = self.createResponseTag()
+ self._domObj.appendChild(self._responseTag)
+
+ @classmethod
+ def errorResponse(self, message):
+ if not self.responseTag:
+ return False
+ self.appendTagRoute("status.code", "-1");
+ self.appendTagRoute("status.message", message)
+
+ def append(self, tagName, tagValue=None):
+ if not self._responseTag:
+ return False
+ tag = self.createTag(tagName, tagValue)
+ if tag:
+ self._responseTag.appendChild(tag)
+ return True
+ return False
+
+ def appendTag(self, tag):
+ if not tag:
+ return False
+ if not self._responseTag:
+ return False
+ self._responseTag.appendChild(tag)
+ return True
+
+ def appendTagRoute(self, tagRoute, value=None):
+ if not self._responseTag:
+ return None
+ if not tagRoute:
+ return None
+
+ parentTagE = self._responseTag
+
+ tagNameList = tagRoute.split(".")
+ newTagRoute = tagNameList.pop(-1)
+
+ for i in range(len(tagNameList), 0, -1):
+ tagE = self.getElementsByTagRoute(".".join(["response"] + tagNameList[:i]))
+ if tagE:
+ parentTagE = tagE[0]
+ break
+ newTagRoute = tagNameList[i-1] + "." + newTagRoute
+
+ newTagE = self.createTagRoute(newTagRoute, value)
+ if not newTagE:
+ return None
+ try:
+ parentTagE.appendChild(newTagE)
+ except xml.dom.HierarchyRequestErr, e:
+ Utils.log("error occured. %s" + str(e))
+ return None
+ return newTagE
+
+ def appendTagRouteOld(self, tagRoute, value=None):
+ if not self._responseTag:
+ return False
+ if not tagRoute:
+ return False
+
+ parentTagE = self._responseTag
+
+ tagNameList = tagRoute.split(".")
+ newTagRoute = tagNameList.pop(-1)
+
+ for i in range(len(tagNameList), 0, -1):
+ tagE = self.getElementsByTagRoute(".".join(["response"] + tagNameList[:i]))
+ if tagE:
+ parentTagE = tagE[0]
+ break
+ newTagRoute = tagNameList[i-1] + "." + newTagRoute
+
+ newTagE = self.createTagRoute(newTagRoute, value)
+ if not newTagE:
+ return False
+ try:
+ parentTagE.appendChild(newTagE)
+ except xml.dom.HierarchyRequestErr, e:
+ Utils.log("error occured. %s" + str(e))
+ return False
+ return True
+##--end of ResponseXml
+
+def test():
+ rs = ResponseXml()
+ rs.appendTagRoute("status.code", "0");
+ rs.appendTagRoute("status.message", "SUCCESS")
+ serverTag = rs.appendTagRoute("server.name", "Server1")
+ networkInterfaces = rs.appendTagRoute("server.networkInterfaces", None)
+ networkTag = rs.createTag("networkInterface", None)
+ networkTag.appendChild(rs.createTag("name", "interface1"))
+ networkTag.appendChild(rs.createTag("ipaddress", "192.168.1.40"))
+ networkInterfaces.appendChild(networkTag)
+ networkTag = rs.createTag("networkInterface", None)
+ networkTag.appendChild(rs.createTag("name", "interface2"))
+ networkTag.appendChild(rs.createTag("ipaddress", "192.168.1.41"))
+ networkInterfaces.appendChild(networkTag)
+ print rs.toprettyxml()
+
+#test()
diff --git a/src/com.gluster.storage.management.gateway/WebContent/scripts/add_user_cifs_all.py b/src/com.gluster.storage.management.gateway/WebContent/scripts/add_user_cifs_all.py new file mode 100755 index 00000000..e4b48658 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/scripts/add_user_cifs_all.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# Copyright (C) 2011 Gluster, Inc. <http://www.gluster.com> +# This file is part of Gluster Management Gateway. +# + +import os +import sys +import Utils + + +defaultUid = 1024000 +cifsUserFile = "/etc/glustermg/.users.cifs" + + +def getLastUid(): + if not os.path.exists(cifsUserFile): + return defaultUid + try: + fp = open(cifsUserFile) + content = fp.read() + fp.close() + except IOError, e: + Utils.log("failed to read file %s: %s" % (cifsUserFile, str(e))) + return False + + lines = content.strip().split() + if not lines: + return defaultUid + return int(lines[-1].split(":")[0]) + + +def setUid(uid, userName): + try: + fp = open(cifsUserFile, "a") + fp.write("%s:%s\n" % (uid, userName)) + fp.close() + return True + except IOError, e: + Utils.log("failed to write file %s: %s" % (cifsUserFile, str(e))) + return False + + +def main(): + if len(sys.argv) < 4: + sys.stderr.write("usage: %s SERVER_FILE USERNAME PASSWORD\n" % os.path.basename(sys.argv[0])) + sys.exit(-1) + + serverFile = sys.argv[1] + userName = sys.argv[2] + password = sys.argv[3] + + uid = getLastUid() + if not uid: + sys.exit(10) + + uid += 1 + + rv = Utils.runCommand("grun.py %s add_user_cifs.py %s %s %s" % (serverFile, uid, userName, password)) + if rv == 0: + if not setUid(uid, userName): + sys.exit(11) + sys.exit(rv) + + +if __name__ == "__main__": + main() diff --git a/src/com.gluster.storage.management.gateway/WebContent/scripts/delete_user_cifs_all.py b/src/com.gluster.storage.management.gateway/WebContent/scripts/delete_user_cifs_all.py new file mode 100755 index 00000000..38dd8109 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/scripts/delete_user_cifs_all.py @@ -0,0 +1,53 @@ +#!/usr/bin/python +# Copyright (C) 2011 Gluster, Inc. <http://www.gluster.com> +# This file is part of Gluster Management Gateway. +# + +import os +import sys +import Utils + + +cifsUserFile = "/etc/glustermg/.users.cifs" + + +def removeUser(userName): + try: + fp = open(cifsUserFile) + content = fp.read() + fp.close() + except IOError, e: + Utils.log("failed to read file %s: %s" % (cifsUserFile, str(e))) + return False + + try: + fp = open(cifsUserFile, "w") + lines = content.strip().split() + for line in lines: + if line.split(":")[1] == userName: + continue + fp.write("%s\n" % line) + fp.close() + except IOError, e: + Utils.log("failed to write file %s: %s" % (cifsUserFile, str(e))) + return False + return True + + +def main(): + if len(sys.argv) < 3: + sys.stderr.write("usage: %s SERVER_LIST USERNAME\n" % os.path.basename(sys.argv[0])) + sys.exit(-1) + + serverList = sys.argv[1] + userName = sys.argv[2] + + rv = Utils.runCommand("grun.py %s delete_user_cifs.py %s" % (serverList, userName)) + if rv == 0: + if not removeUser(userName): + sys.exit(10) + sys.exit(rv) + + +if __name__ == "__main__": + main() diff --git a/src/com.gluster.storage.management.gateway/WebContent/scripts/grun.py b/src/com.gluster.storage.management.gateway/WebContent/scripts/grun.py new file mode 100755 index 00000000..ae93b7f2 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/scripts/grun.py @@ -0,0 +1,36 @@ +#!/usr/bin/python +# Copyright (C) 2011 Gluster, Inc. <http://www.gluster.com> +# This file is part of Gluster Management Gateway. +# + +import os +import sys +import Utils + + +def main(): + sshCommandPrefix = "ssh -q -o BatchMode=yes -o GSSAPIAuthentication=no -o PasswordAuthentication=no -o StrictHostKeyChecking=no".split() + + if len(sys.argv) < 3: + sys.stderr.write("usage: %s SERVER_FILE COMMAND [ARGUMENTS]\n" % os.path.basename(sys.argv[0])) + sys.exit(-1) + serverFile = sys.argv[1] + command = sys.argv[2:] + + try: + fp = open(serverFile) + serverNameList = fp.readlines() + fp.close() + except IOError, e: + Utils.log("Failed to read server file %s: %s\n" % (serverFile, str(e))) + sys.exit(1) + + for serverName in serverNameList: + rv = Utils.runCommand(sshCommandPrefix + [serverName.strip()] + command) + print rv + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/src/com.gluster.storage.management.gateway/WebContent/scripts/multicast-discover-servers.py b/src/com.gluster.storage.management.gateway/WebContent/scripts/multicast-discover-servers.py new file mode 100755 index 00000000..ac434827 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/scripts/multicast-discover-servers.py @@ -0,0 +1,90 @@ +#!/usr/bin/python +# Copyright (C) 2009 Gluster, Inc. <http://www.gluster.com> +# This file is part of Gluster Storage Platform. +# +# Gluster Storage Platform is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 of +# the License, or (at your option) any later version. +# +# Gluster Storage Platform is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see +# <http://www.gnu.org/licenses/>. + +import sys +import socket +import signal +import struct +import syslog +import Globals +import time +import Utils +from XmlHandler import * + +class TimeoutException(Exception): + pass + +def timeoutSignal(signum, frame): + raise TimeoutException, "Timed out" + +def serverDiscoveryRequest(multiCastGroup, port): + servers = [] + # Sending request to all the servers + socketSend = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) + socketSend.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) + socketSend.sendto("ServerDiscovery", (multiCastGroup, port)) + + # Waiting for the response + socketReceive = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) + socketReceive.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + socketReceive.bind(('', port)) + mreq = struct.pack("4sl", socket.inet_aton(multiCastGroup), socket.INADDR_ANY) + + socketReceive.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) + sendtime = time.time() + socketSend.sendto("<request><name>ServerDiscovery</name><time>%s</time></request>" % (sendtime), (multiCastGroup, port)) + + try: + while True: + response = socketReceive.recvfrom(200) + if not response: + continue + dom = XDOM() + dom.parseString(response[0]) + if not dom: + continue + if dom.getTextByTagRoute("request.name"): + continue + responsetime = dom.getTextByTagRoute("response.time") + servername = dom.getTextByTagRoute("response.servername") + if responsetime == str(sendtime): + servers.append(servername) + signal.signal(signal.SIGALRM, timeoutSignal) + signal.alarm(3) + except TimeoutException: + return servers + return None + +def main(): + syslog.openlog("discovery server request") + servers = serverDiscoveryRequest(Globals.MULTICAST_GROUP, Globals.MULTICAST_PORT) + if not servers: + Utils.log(syslog.LOG_ERR, "Failed to discover new servers") + sys.exit(-1) + + servers = set(servers) + try: + for server in servers: + print server + except IOError: + Utils.log(syslog.LOG_ERR, "Unable to open file %s" % Globals.DISCOVERED_SERVER_LIST_FILENAME) + sys.exit(-1) + sys.exit(0) + +if __name__ == "__main__": + main() diff --git a/src/com.gluster.storage.management.gateway/WebContent/ssl/gmg-ssl.keystore b/src/com.gluster.storage.management.gateway/WebContent/ssl/gmg-ssl.keystore Binary files differnew file mode 100644 index 00000000..2efe19b0 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/WebContent/ssl/gmg-ssl.keystore diff --git a/src/com.gluster.storage.management.gateway/buckminster.cspex b/src/com.gluster.storage.management.gateway/buckminster.cspex new file mode 100644 index 00000000..31eb9d67 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/buckminster.cspex @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<cspecExtension + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:bc="http://www.eclipse.org/buckminster/Common-1.0" + xmlns="http://www.eclipse.org/buckminster/CSpec-1.0"> + <dependencies> + <!-- Place your Dependencies here --> + </dependencies> + <generators> + <!-- Place your Generators here --> + </generators> + <artifacts> + <!-- Place your Artifacts here --> + </artifacts> + <actions> + <public name="archive" actor="ant"> + <actorProperties> + <property key="buildFile" value="build/glusterserver.ant"/> + </actorProperties> + </public> + </actions> + <groups> + <!-- Place your Groups here --> + </groups> + <alterDependencies> + <!-- Place your Dependency alterations here --> + </alterDependencies> + <alterArtifacts> + <!-- Place your Artifact alterations here --> + </alterArtifacts> + <alterActions> + <!-- Place your Action alterations here --> + </alterActions> + <alterGroups> + <!-- Place your Group alterations here --> + </alterGroups> +</cspecExtension> diff --git a/src/com.gluster.storage.management.gateway/build/glusterserver.ant b/src/com.gluster.storage.management.gateway/build/glusterserver.ant new file mode 100644 index 00000000..68a5bc08 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/build/glusterserver.ant @@ -0,0 +1,65 @@ +<project name="com.gluster.storage.management.gateway" basedir="." default="archive"> + <echo message="basedir=${basedir}" /> + <echo message="buckminster.output=${buckminster.output}" /> + <property name="WEB-INF" value="${basedir}/WebContent/WEB-INF" /> + <property name="OUT" value="${buckminster.output}/glusterserver/" /> + <property name="WAR_FILE_NAME" value="glustermg.war" /> + <property name="TEMP" value="${buckminster.output}/temp" /> + + <target name="help"> + <echo> + -------------------------------------------------- + compile - Compile + archive - Generate WAR file + -------------------------------------------------- + </echo> + </target> + + <target name="init"> + <delete dir="${WEB-INF}/classes" /> + <mkdir dir="${WEB-INF}/classes" /> + <mkdir dir="${WEB-INF}/classes/spring" /> + <mkdir dir="${WEB-INF}/classes/META-INF" /> + </target> + + <target name="compile" depends="init"> + <!-- flatten=true means ignore directory structure and copy files directly to destination --> + <copy todir="${WEB-INF}/lib" flatten="true"> + <fileset dir="${buckminster.output}/.." casesensitive="yes"> + <include name="**/*.jar" /> + </fileset> + </copy> + <copy todir="${WEB-INF}/classes/spring"> + <fileset dir="${basedir}/src/spring" casesensitive="yes"> + <include name="**/*.xml" /> + </fileset> + </copy> + <copy todir="${WEB-INF}/classes/META-INF"> + <fileset dir="${basedir}/src/META-INF" casesensitive="yes"> + <include name="**/*.xml" /> + </fileset> + </copy> + <!-- TODO: make debug option configurable in jenkins --> + <javac srcdir="${basedir}/src" destdir="${WEB-INF}/classes" classpathref="libs" debug="true" debuglevel="lines,vars,source"/> + </target> + + <target name="archive" depends="compile"> + <delete dir="${OUT}" /> + <mkdir dir="${OUT}" /> + <delete dir="${TEMP}" /> + <mkdir dir="${TEMP}" /> + <copy todir="${TEMP}"> + <fileset dir="${basedir}/WebContent"> + <!-- servlet-api.jar is required during compilation. At runtime, it is picked from tomcat lib directory --> + <exclude name="**/servlet-api.jar" /> + </fileset> + </copy> + <war destfile="${OUT}/${WAR_FILE_NAME}" basedir="${TEMP}" compress="true" webxml="${TEMP}/WEB-INF/web.xml" /> + <delete dir="${TEMP}" /> + </target> + + <path id="libs"> + <fileset includes="*.jar" dir="${WEB-INF}/lib" /> + </path> + +</project> diff --git a/src/com.gluster.storage.management.gateway/src/META-INF/persistence.xml b/src/com.gluster.storage.management.gateway/src/META-INF/persistence.xml new file mode 100644 index 00000000..36b252ea --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/META-INF/persistence.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> + <persistence-unit name="gluster-management-gateway" transaction-type="RESOURCE_LOCAL" /> +</persistence>
\ No newline at end of file diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/constants/VolumeOptionsDefaults.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/constants/VolumeOptionsDefaults.java new file mode 100644 index 00000000..1f577c89 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/constants/VolumeOptionsDefaults.java @@ -0,0 +1,118 @@ +/** + * DefaultVolumeOptions.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 com.gluster.storage.management.gateway.constants; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.stereotype.Component; + +import com.gluster.storage.management.core.constants.CoreConstants; +import com.gluster.storage.management.core.model.VolumeOptionInfo; + +@Component +public class VolumeOptionsDefaults { + public List<VolumeOptionInfo> options; + + public VolumeOptionsDefaults() { + } + + /** + * @return list of volume option information objects + */ + public List<VolumeOptionInfo> getDefaults() { + return getVolumeOptionsInfo(); + } + + /** + * Fetches the list of all volume options with their information from GlusterFS and returns the same + * + * @return List of volume option information objects + */ + private List<VolumeOptionInfo> getVolumeOptionsInfo() { + List<VolumeOptionInfo> volumeOptionsInfo = new ArrayList<VolumeOptionInfo>(); + + volumeOptionsInfo + .add(new VolumeOptionInfo( + "cluster.stripe-block-size", + "This could be used in case of a stripe setup. Specifies the size of the stripe unit that will read from or written to the striped servers. " + + CoreConstants.NEWLINE + + "Optionally different stripe unit sizes can be specified for different fies, with the following pattern <filename-pattern:blk-size>. ", + "*:128KB")); + volumeOptionsInfo + .add(new VolumeOptionInfo( + "cluster.self-heal-window-size", + "Specifies the number of maximum number blocks per file for which self-heal process would be applied simultaneously.", + "16")); + volumeOptionsInfo.add(new VolumeOptionInfo("cluster.data-self-heal-algorithm", + "cluster.data-self-heal-algorithm", "auto")); + volumeOptionsInfo + .add(new VolumeOptionInfo( + "network.frame-timeout", + "The time frame after which the operation has to be declared as dead, if the server does not respond for a particular operation.", + "1800")); + volumeOptionsInfo.add(new VolumeOptionInfo("network.ping-timeout", + "The time duration for which the client waits to check if the server is responsive.", "42")); + volumeOptionsInfo.add(new VolumeOptionInfo("auth.allow", + "'IP addresses/Host name' of the clients which should be allowed to access the the volume.", "*")); + volumeOptionsInfo.add(new VolumeOptionInfo("auth.reject", + "'IP addresses/Host name' of the clients which should be denied to access the volume.", "NONE")); + volumeOptionsInfo + .add(new VolumeOptionInfo( + "performance.cache-refresh-timeout", + "The cached data for a file will be retained till 'cache-refresh-timeout' seconds, after which data re-validation is performed.", + "1")); + volumeOptionsInfo.add(new VolumeOptionInfo("performance.cache-size", "Size of the read cache.", "32MB")); + volumeOptionsInfo.add(new VolumeOptionInfo("performance.write-behind-window-size", + "Size of the per-file write-behind buffer.", "1MB")); + volumeOptionsInfo.add(new VolumeOptionInfo("performance.cache-max-file-size", + "performance.cache-max-file-size", "-1")); + volumeOptionsInfo.add(new VolumeOptionInfo("performance.cache-min-file-size", + "performance.cache-min-file-size", "0")); + volumeOptionsInfo + .add(new VolumeOptionInfo( + "performance.io-thread-count", + " Number of threads in the thread-pool in the bricks to improve the concurrency in I/O s of server side.", + "16")); + volumeOptionsInfo + .add(new VolumeOptionInfo( + "diagnostics.latency-measurement", + "Statistics related to the latency of each operation would be tracked inside GlusterFS data-structures.", + "off")); + volumeOptionsInfo.add(new VolumeOptionInfo("diagnostics.dump-fd-stats", + "Statistics related to file-operations would be tracked inside GlusterFS data-structures.", "off")); + volumeOptionsInfo.add(new VolumeOptionInfo("diagnostics.brick-log-level", + "Changes the log-level of the bricks (servers).", "INFO")); + volumeOptionsInfo.add(new VolumeOptionInfo("diagnostics.client-log-level", + "Changes the log-level of the clients.", "INFO")); + volumeOptionsInfo.add(new VolumeOptionInfo("nfs.enable-ino32", + "Use this option from the CLI to make Gluster NFS return 32-bit inode numbers instead of 64-bit.", + "off")); + volumeOptionsInfo + .add(new VolumeOptionInfo( + "nfs.mem-factor", + "This option specifies a multiple that determines the total amount of memory used. Increases this increases the performance of NFS.", + "15")); + volumeOptionsInfo.add(new VolumeOptionInfo("transport.keepalive", "transport.keepalive", "on")); + + return volumeOptionsInfo; + } +}
\ No newline at end of file diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/data/ClusterInfo.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/data/ClusterInfo.java new file mode 100644 index 00000000..3e5ea01a --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/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 com.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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/data/GlusterDataSource.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/data/GlusterDataSource.java new file mode 100644 index 00000000..0802fe93 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/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 com.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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/data/PersistenceDao.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/data/PersistenceDao.java new file mode 100644 index 00000000..eb7d6514 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/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 com.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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/data/ServerInfo.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/data/ServerInfo.java new file mode 100644 index 00000000..a3f8c920 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/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 com.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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/filters/AuditFilter.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/filters/AuditFilter.java new file mode 100644 index 00000000..31810123 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/filters/AuditFilter.java @@ -0,0 +1,38 @@ +/** + * + */ +package com.gluster.storage.management.gateway.filters; + +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 { + + @Override + public ContainerRequestFilter getRequestFilter() { + return this; + } + + @Override + public ContainerResponseFilter getResponseFilter() { + return this; + } + + @Override + public ContainerRequest filter(ContainerRequest req) { + System.out.println("REQUEST: [" + req.getMethod() + "][" + req.getPath() + "]"); + return req; + } + + @Override + public ContainerResponse filter(ContainerRequest req, ContainerResponse response) { + System.out.println("RESPONSE: [" + req.getMethod() + "][" + req.getPath() + "]"); + return response; + } +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/filters/AuthenticationFailureFilter.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/filters/AuthenticationFailureFilter.java new file mode 100644 index 00000000..73a1085e --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/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 com.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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/filters/GlusterResourceFilterFactory.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/filters/GlusterResourceFilterFactory.java new file mode 100644 index 00000000..1c480f3f --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/filters/GlusterResourceFilterFactory.java @@ -0,0 +1,31 @@ +/** + * + */ +package com.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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/AbstractResource.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/AbstractResource.java new file mode 100644 index 00000000..9fc4fceb --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/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 com.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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/ClustersResource.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/ClustersResource.java new file mode 100644 index 00000000..d0da6696 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/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 com.gluster.storage.management.gateway.resources.v1_0; + +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_CLUSTER_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_SERVER_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_CLUSTER_NAME; +import static com.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.springframework.stereotype.Component; + +import com.gluster.storage.management.core.exceptions.GlusterValidationException; +import com.gluster.storage.management.core.response.ClusterNameListResponse; +import com.gluster.storage.management.gateway.data.ClusterInfo; +import com.gluster.storage.management.gateway.services.ClusterService; +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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/DiscoveredServersResource.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/DiscoveredServersResource.java new file mode 100644 index 00000000..2d07bd24 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/DiscoveredServersResource.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * 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 com.gluster.storage.management.gateway.resources.v1_0; + +import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_SERVER_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DETAILS; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_DISCOVERED_SERVERS; + +import java.util.ArrayList; +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.springframework.stereotype.Component; + +import com.gluster.storage.management.core.model.Server; +import com.gluster.storage.management.core.response.ServerListResponse; +import com.gluster.storage.management.core.response.ServerNameListResponse; +import com.gluster.storage.management.gateway.utils.GlusterUtil; +import com.gluster.storage.management.gateway.utils.ServerUtil; +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 + protected ServerUtil serverUtil; + + @InjectParam + protected GlusterUtil glusterUtil; + + private List<String> discoveredServerNames = new ArrayList<String>(); + + 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); + } + + @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 = getDiscoveredServerDetails(); + return okResponse(new ServerListResponse(discoveredServers), mediaType); + } catch(Exception e) { + return errorResponse(e.getMessage()); + } + } else { + return okResponse(new ServerNameListResponse(getDiscoveredServerNames()), mediaType); + } + } + + private List<Server> getDiscoveredServerDetails() { + List<Server> discoveredServers = new ArrayList<Server>(); + for (String serverName : getDiscoveredServerNames()) { + try { + discoveredServers.add(getDiscoveredServer(serverName)); + } catch(Exception e) { + // TODO: Log the exception + // continue with next discovered server + } + } + return discoveredServers; + } + + @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(getDiscoveredServer(serverName), mediaType); + } catch (Exception e) { + // TODO: Log the exception + return errorResponse(e.getMessage()); + } + } + + private Server getDiscoveredServer(String serverName) { + Server server = new Server(serverName); + serverUtil.fetchServerDetails(server); + return server; + } + + public static void main(String[] args) { + Response response = (Response)new DiscoveredServersResource().getDiscoveredServersXML(false); + System.out.println(response.getEntity()); + } +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/GenericExceptionMapper.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/GenericExceptionMapper.java new file mode 100644 index 00000000..3a316c0c --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/GenericExceptionMapper.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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 com.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 com.gluster.storage.management.core.exceptions.GlusterValidationException; + +@Provider +public class GenericExceptionMapper implements ExceptionMapper<Exception> { + + /* (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() + "]"; + } + } + return builder.entity(errMsg).type(MediaType.TEXT_PLAIN).build(); + } +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/GlusterServersResource.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/GlusterServersResource.java new file mode 100644 index 00000000..e6e4f9d6 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/GlusterServersResource.java @@ -0,0 +1,487 @@ +/******************************************************************************* + * 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 com.gluster.storage.management.gateway.resources.v1_0; + +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_FSTYPE; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_SERVER_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_CLUSTER_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_DISK_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_SERVER_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DETAILS; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_INTERFACE; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_PERIOD; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_TYPE; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_DISKS; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_CLUSTERS; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_SERVERS; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_STATISTICS; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_TASKS; +import static com.gluster.storage.management.core.constants.RESTConstants.STATISTICS_TYPE_CPU; +import static com.gluster.storage.management.core.constants.RESTConstants.STATISTICS_TYPE_MEMORY; +import static com.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.springframework.stereotype.Component; + +import com.gluster.storage.management.core.constants.CoreConstants; +import com.gluster.storage.management.core.constants.RESTConstants; +import com.gluster.storage.management.core.exceptions.ConnectionException; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.core.exceptions.GlusterValidationException; +import com.gluster.storage.management.core.model.GlusterServer; +import com.gluster.storage.management.core.model.ServerStats; +import com.gluster.storage.management.core.model.TaskStatus; +import com.gluster.storage.management.core.response.GlusterServerListResponse; +import com.gluster.storage.management.core.response.ServerNameListResponse; +import com.gluster.storage.management.gateway.data.ClusterInfo; +import com.gluster.storage.management.gateway.data.ServerInfo; +import com.gluster.storage.management.gateway.services.ClusterService; +import com.gluster.storage.management.gateway.services.GlusterServerService; +import com.gluster.storage.management.gateway.tasks.InitializeDiskTask; +import com.gluster.storage.management.gateway.utils.CpuStatsFactory; +import com.gluster.storage.management.gateway.utils.GlusterUtil; +import com.gluster.storage.management.gateway.utils.MemoryStatsFactory; +import com.gluster.storage.management.gateway.utils.NetworkStatsFactory; +import com.gluster.storage.management.gateway.utils.ServerUtil; +import com.gluster.storage.management.gateway.utils.SshUtil; +import com.gluster.storage.management.gateway.utils.StatsFactory; +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 DiscoveredServersResource discoveredServersResource; + + @InjectParam + private TasksResource taskResource; + + @InjectParam + private ClusterService clusterService; + + @InjectParam + private SshUtil sshUtil; + + @InjectParam + private CpuStatsFactory cpuStatsFactory; + + @InjectParam + private MemoryStatsFactory memoryStatsFactory; + + @InjectParam + private NetworkStatsFactory networkStatsFactory; + + @InjectParam + private ServerUtil serverUtil; + + @InjectParam + private GlusterUtil glusterUtil; + + @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) { + return getGlusterServers(clusterName, MediaType.APPLICATION_JSON, details); + } + + @GET + @Produces(MediaType.APPLICATION_XML) + public Response getGlusterServersXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, + @QueryParam(QUERY_PARAM_DETAILS) Boolean details) { + return getGlusterServers(clusterName, MediaType.APPLICATION_XML, details); + } + + private Response getGlusterServers(String clusterName, String mediaType, Boolean fetchDetails) { + if(fetchDetails == null) { + // by default, fetch the server details + fetchDetails = true; + } + + 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); + } 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()); + } + } + + 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 (ConnectionException e) { + // 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(serverName, onlineServer.getName()); + } + } + + @POST + public Response addServer(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, + @FormParam(FORM_PARAM_SERVER_NAME) String serverName) { + if (clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Cluster name must not be empty!"); + } + + if (serverName == null || serverName.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_SERVER_NAME + "] is missing in request!"); + } + + ClusterInfo cluster = clusterService.getCluster(clusterName); + if (cluster == null) { + return notFoundResponse("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. + return errorResponse("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 + try { + performAddServer(clusterName, hostName); + } catch (Exception e) { + return errorResponse(e.getMessage()); + } + } 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 + } + + try { + // add the cluster-server mapping + clusterService.mapServerToCluster(clusterName, serverName); + } catch (Exception e) { + return errorResponse(e.getMessage()); + } + + // since the server is added to a cluster, it should not more be considered as a + // discovered server available to other clusters + discoveredServersResource.removeDiscoveredServer(serverName); + + if (!publicKeyInstalled) { + try { + // install public key (this will also disable password based ssh login) + sshUtil.installPublicKey(serverName); + } catch (Exception e) { + return errorResponse("Public key could not be installed on [" + serverName + "]! Error: [" + + e.getMessage() + "]"); + } + } + + return createdResponse(serverName); + } + + @DELETE + @Path("{" + PATH_PARAM_SERVER_NAME + "}") + public Response removeServer(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName, + @PathParam(PATH_PARAM_SERVER_NAME) String serverName) { + 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!"); + } + + ClusterInfo cluster = clusterService.getCluster(clusterName); + if (cluster == null) { + return notFoundResponse("Cluster [" + clusterName + "] not found!"); + } + + List<ServerInfo> servers = cluster.getServers(); + if (servers == null || servers.isEmpty() || !containsServer(servers, serverName)) { + return badRequestResponse("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 { + try { + removeServerFromCluster(clusterName, serverName); + } catch (Exception e) { + return errorResponse(e.getMessage()); + } + } + clusterService.unmapServerFromCluster(clusterName, serverName); + + return noContentResponse(); + } + + private void removeServerFromCluster(String clusterName, String serverName) { + // 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 (ConnectionException e) { + // 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); + } + + if (onlineServer.getName().equals(serverName)) { + // since the cached server has been removed from the cluster, remove it from the cache + clusterService.removeOnlineServer(clusterName); + } + + // 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. + discoveredServersResource.addDiscoveredServer(serverName); + } + + private boolean containsServer(List<ServerInfo> servers, String serverName) { + for (ServerInfo server : servers) { + if (server.getName().toUpperCase().equals(serverName.toUpperCase())) { + return true; + } + } + return false; + } + + @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) { + + 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()) { + return badRequestResponse("Parameter [" + FORM_PARAM_FSTYPE + "] is missing in request!"); + } + + InitializeDiskTask initializeTask = new InitializeDiskTask(clusterService, clusterName, serverName, diskName, fsType); + try { + initializeTask.start(); + // Check the initialize disk status + TaskStatus taskStatus = initializeTask.checkStatus(); + initializeTask.getTaskInfo().setStatus(taskStatus); + taskResource.addTask(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)); + 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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/KeysResource.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/KeysResource.java new file mode 100644 index 00000000..34dad497 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/KeysResource.java @@ -0,0 +1,153 @@ +/** + * 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 com.gluster.storage.management.gateway.resources.v1_0; + +import static com.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 com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.core.utils.FileUtil; +import com.gluster.storage.management.core.utils.ProcessResult; +import com.gluster.storage.management.core.utils.ProcessUtil; +import com.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 = System.getProperty("java.io.tmpdir"); + String zipFile = targetDir + "ssh-keys.tar"; + String sourcePemFile = SshUtil.PRIVATE_KEY_FILE.getAbsolutePath(); + String sourcePubKeyFile = SshUtil.PUBLIC_KEY_FILE.getAbsolutePath(); + String targetPemFile = 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", sourcePemFile, targetPemFile); + if (!result.isSuccess()) { + throw new GlusterRuntimeException("Failed to copy key files! [" + result.getOutput() + "]"); + } + result = processUtil.executeCommand("cp", sourcePubKeyFile, 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", "/tmp", 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", targetPemFile, 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 pem 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 pem 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 pem and public key files. + ProcessResult output = processUtil.executeCommand("tar", "xvf", uploadedFile.getName(), "-C", + SshUtil.SSH_AUTHORIZED_KEYS_DIR_LOCAL); + uploadedFile.delete(); + if (!output.isSuccess()) { + throw new GlusterRuntimeException(output.getOutput()); + } + 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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/TasksResource.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/TasksResource.java new file mode 100644 index 00000000..e5874f4b --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/TasksResource.java @@ -0,0 +1,194 @@ +/** + * 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 com.gluster.storage.management.gateway.resources.v1_0; + +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_OPERATION; +import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_CLUSTER_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_TASK_ID; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_CLUSTERS; +import static com.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.springframework.stereotype.Component; + +import com.gluster.storage.management.core.constants.RESTConstants; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.core.exceptions.GlusterValidationException; +import com.gluster.storage.management.core.model.Status; +import com.gluster.storage.management.core.model.TaskInfo; +import com.gluster.storage.management.core.response.TaskInfoListResponse; +import com.gluster.storage.management.gateway.tasks.Task; +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, Task> tasksMap = new HashMap<String, Task>(); + + public TasksResource() { + } + + public void addTask(Task task) { + tasksMap.put(task.getId(), task); + } + + public void removeTask(Task task) { + tasksMap.remove(task.getId()); + } + + public List<TaskInfo> getAllTasksInfo() { + List<TaskInfo> allTasksInfo = new ArrayList<TaskInfo>(); + for (Map.Entry<String, Task> entry : tasksMap.entrySet()) { + checkTaskStatus(entry.getKey()); + allTasksInfo.add(entry.getValue().getTaskInfo()); // TaskInfo with latest status + } + return allTasksInfo; + } + + public Task getTask(String taskId) { + for (Map.Entry<String, Task> entry : tasksMap.entrySet()) { + if (entry.getValue().getId().equals(taskId)) { + return entry.getValue(); + } + } + return null; + } + + public List<Task> getAllTasks() { + List<Task> tasks = new ArrayList<Task>(); + for (Map.Entry<String, Task> entry : tasksMap.entrySet()) { + checkTaskStatus(entry.getKey()); + tasks.add( (Task) entry.getValue()); + } + return tasks; + } + + @GET + @Produces(MediaType.APPLICATION_XML) + public Response getTasks() { + try { + return okResponse(new TaskInfoListResponse(getAllTasksInfo()), 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_TASK_ID) String taskId) { + try { + Task task = checkTaskStatus(taskId); + return okResponse(task.getTaskInfo(), MediaType.APPLICATION_XML); + } catch (GlusterRuntimeException e) { + return errorResponse(e.getMessage()); + } + } + + private Task checkTaskStatus(String taskId) { + Task task = getTask(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(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(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_TASK_ID) String taskId, + @QueryParam(FORM_PARAM_OPERATION) String taskOperation) { + Task task = getTask(taskId); + if (task == null) { + return notFoundResponse("Task [" + taskId + "] not found!"); + } + + if(taskOperation == null || taskOperation.isEmpty()) { + 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 successfully stopping the task, we can delete (forget) it as it is no more useful + taskOperation = RESTConstants.TASK_DELETE; + } + + if (taskOperation.equals(RESTConstants.TASK_DELETE)) { + removeTask(task); + } + + return noContentResponse(); + } catch (Exception e) { + return errorResponse(e.getMessage()); + } + } +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/UsersResource.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/UsersResource.java new file mode 100644 index 00000000..4b2701f2 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/UsersResource.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * 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 com.gluster.storage.management.gateway.resources.v1_0; + +import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_USER; +import static com.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.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.encoding.PasswordEncoder; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.provisioning.JdbcUserDetailsManager; +import org.springframework.stereotype.Component; + +import com.gluster.storage.management.core.model.Status; +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; + + 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(@FormParam("oldpassword") String oldPassword, + @FormParam("newpassword") String newPassword) { + try { + jdbcUserService.changePassword(oldPassword, passwordEncoder.encodePassword(newPassword, null)); + } catch (Exception ex) { + String errMsg = "Could not change password. Error: [" + ex.getMessage() + "]"; + logger.error(errMsg, ex); + return errorResponse(errMsg); + } + return noContentResponse(); + } +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/VolumesResource.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/VolumesResource.java new file mode 100644 index 00000000..45df1ae7 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/resources/v1_0/VolumesResource.java @@ -0,0 +1,989 @@ +/** + * 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 com.gluster.storage.management.gateway.resources.v1_0; + +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_ACCESS_PROTOCOLS; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_AUTO_COMMIT; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_BRICKS; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_FIX_LAYOUT; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_FORCED_DATA_MIGRATE; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_MIGRATE_DATA; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_OPERATION; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_OPTION_KEY; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_OPTION_VALUE; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_REPLICA_COUNT; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_SOURCE; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_STRIPE_COUNT; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_TARGET; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_TRANSPORT_TYPE; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_VOLUME_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_VOLUME_OPTIONS; +import static com.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_VOLUME_TYPE; +import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_CLUSTER_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.PATH_PARAM_VOLUME_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_BRICKS; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_BRICK_NAME; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DELETE_OPTION; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_DOWNLOAD; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_FROM_TIMESTAMP; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_LINE_COUNT; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_LOG_SEVERITY; +import static com.gluster.storage.management.core.constants.RESTConstants.QUERY_PARAM_TO_TIMESTAMP; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_BRICKS; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_DEFAULT_OPTIONS; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_DOWNLOAD; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_LOGS; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_OPTIONS; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_CLUSTERS; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_TASKS; +import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_VOLUMES; +import static com.gluster.storage.management.core.constants.RESTConstants.TASK_START; +import static com.gluster.storage.management.core.constants.RESTConstants.TASK_STOP; + +import java.io.File; +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 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 com.gluster.storage.management.core.constants.CoreConstants; +import com.gluster.storage.management.core.constants.RESTConstants; +import com.gluster.storage.management.core.exceptions.ConnectionException; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.core.exceptions.GlusterValidationException; +import com.gluster.storage.management.core.model.Brick; +import com.gluster.storage.management.core.model.GlusterServer; +import com.gluster.storage.management.core.model.Status; +import com.gluster.storage.management.core.model.Volume; +import com.gluster.storage.management.core.model.Volume.VOLUME_TYPE; +import com.gluster.storage.management.core.model.VolumeLogMessage; +import com.gluster.storage.management.core.response.GenericResponse; +import com.gluster.storage.management.core.response.LogMessageListResponse; +import com.gluster.storage.management.core.response.VolumeListResponse; +import com.gluster.storage.management.core.response.VolumeOptionInfoListResponse; +import com.gluster.storage.management.core.utils.DateUtil; +import com.gluster.storage.management.core.utils.FileUtil; +import com.gluster.storage.management.core.utils.ProcessUtil; +import com.gluster.storage.management.gateway.constants.VolumeOptionsDefaults; +import com.gluster.storage.management.gateway.data.ClusterInfo; +import com.gluster.storage.management.gateway.services.ClusterService; +import com.gluster.storage.management.gateway.tasks.MigrateBrickTask; +import com.gluster.storage.management.gateway.tasks.RebalanceVolumeTask; +import com.gluster.storage.management.gateway.utils.GlusterUtil; +import com.gluster.storage.management.gateway.utils.ServerUtil; +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 String VOLUME_DIRECTORY_CLEANUP_SCRIPT = "clear_volume_directory.py"; + private static final String VOLUME_BRICK_LOG_SCRIPT = "get_volume_brick_log.py"; + private static final Logger logger = Logger.getLogger(VolumesResource.class); + + @InjectParam + private ServerUtil serverUtil; + + @InjectParam + private GlusterUtil glusterUtil; + + @InjectParam + private ClusterService clusterService; + + @InjectParam + private VolumeOptionsDefaults volumeOptionsDefaults; + + @InjectParam + private TasksResource taskResource; + + @GET + @Produces({MediaType.APPLICATION_XML}) + public Response getVolumesXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName) { + return getVolumes(clusterName, MediaType.APPLICATION_XML); + } + + @GET + @Produces({MediaType.APPLICATION_JSON}) + public Response getVolumesJSON(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName) { + return getVolumes(clusterName, MediaType.APPLICATION_JSON); + } + + public Response getVolumes(String clusterName, String mediaType) { + 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) { + // no server added yet. return an empty array. + return okResponse(new VolumeListResponse(), mediaType); + } + + return okResponse(getVolumes(clusterName), mediaType); + } + + public VolumeListResponse getVolumes(String clusterName) { + GlusterServer onlineServer = clusterService.getOnlineServer(clusterName); + if (onlineServer == null) { + return new VolumeListResponse(new ArrayList<Volume>()); + } + + try { + return new VolumeListResponse(glusterUtil.getAllVolumes(onlineServer.getName())); + } catch (ConnectionException e) { + // online server has gone offline! try with a different one. + onlineServer = clusterService.getNewOnlineServer(clusterName); + if (onlineServer == null) { + return new VolumeListResponse(new ArrayList<Volume>()); + } + + return new VolumeListResponse(glusterUtil.getAllVolumes(onlineServer.getName())); + } + } + + @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) { + if(clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Cluster name must not be empty!"); + } + + String missingParam = checkMissingParamsForCreateVolume(volumeName, volumeType, transportType, replicaCount, stripeCount, bricks, accessProtocols, options); + if(missingParam != null) { + return badRequestResponse("Parameter [" + missingParam + "] is missing in request!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return notFoundResponse("Cluster [" + clusterName + "] not found!"); + } + + if (volumeType.equals(VOLUME_TYPE.DISTRIBUTED_MIRROR) && replicaCount <= 0) { + return badRequestResponse("Replica count must be a positive integer"); + } + + if (volumeType.equals(VOLUME_TYPE.DISTRIBUTED_STRIPE) && stripeCount <= 0) { + return badRequestResponse("Stripe count must be a positive integer"); + } + + try { + performCreateVolume(clusterName, volumeName, volumeType, transportType, replicaCount, stripeCount, bricks, accessProtocols, + options); + return createdResponse(volumeName); + } catch (Exception e) { + return errorResponse(e.getMessage()); + } + } + + public void performCreateVolume(String clusterName, String volumeName, String volumeType, String transportType, Integer replicaCount, + Integer stripeCount, String bricks, String accessProtocols, String options) { + 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, replicaCount, stripeCount, bricks, accessProtocols, options); + } catch (ConnectionException 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 + "]"); + } + + glusterUtil.createVolume(onlineServer.getName(), volumeName, volumeType, transportType, replicaCount, stripeCount, bricks, accessProtocols, options); + } + } + + /** + * Returns name of the missing parameter if any. If all parameters are present, + * @param volumeName + * @param volumeType + * @param transportType + * @param replicaCount + * @param stripeCount + * @param bricks + * @param accessProtocols + * @param options + * @return + */ + private String checkMissingParamsForCreateVolume(String volumeName, String volumeType, + String transportType, Integer replicaCount, Integer stripeCount, 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 : + (replicaCount == null) ? FORM_PARAM_REPLICA_COUNT : + (stripeCount == null) ? FORM_PARAM_STRIPE_COUNT : + (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 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 getVolume(clusterName, volumeName, MediaType.APPLICATION_JSON); + } + + private Response getVolume(String clusterName, String volumeName, String mediaType) { + Volume volume = null; + + if (clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Cluster name must not be empty!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return notFoundResponse("Cluster [" + clusterName + "] not found!"); + } + + try { + volume = getVolume(clusterName, volumeName); + return okResponse(volume, mediaType); + } catch(Exception e) { + return errorResponse(e.getMessage()); + } + } + + private Volume getVolume(String clusterName, String volumeName) { + Volume volume; + GlusterServer onlineServer = clusterService.getOnlineServer(clusterName); + if (onlineServer == null) { + throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]"); + } + + try { + volume = glusterUtil.getVolume(volumeName, onlineServer.getName()); + } catch (ConnectionException 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 + "]"); + } + + volume = glusterUtil.getVolume(volumeName, onlineServer.getName()); + } + return volume; + } + + @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) { + 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 { + if (operation.equals(RESTConstants.TASK_REBALANCE_START)) { + String taskId = rebalanceStart(clusterName, volumeName, isFixLayout, isMigrateData, isForcedDataMigrate); + return acceptedResponse(RESTConstants.RESOURCE_PATH_CLUSTERS + "/" + clusterName + "/" + RESOURCE_TASKS + + "/" + taskId); + } else if (operation.equals(RESTConstants.TASK_REBALANCE_STOP)) { + rebalanceStop(clusterName, volumeName); + } else { + performVolumeOperation(clusterName, volumeName, operation); + } + return noContentResponse(); + } catch(Exception e) { + return errorResponse(e.getMessage()); + } + } + + private void performVolumeOperation(String clusterName, String volumeName, String operation) { + GlusterServer onlineServer = clusterService.getOnlineServer(clusterName); + try { + if (onlineServer == null) { + throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]"); + } + + performOperation(volumeName, operation, onlineServer); + } catch (ConnectionException e) { + // online server has gone offline! try with a different one. + onlineServer = clusterService.getNewOnlineServer(clusterName); + performOperation(volumeName, operation, onlineServer); + } + } + + private void performOperation(String volumeName, String operation, GlusterServer onlineServer) { + if (operation.equals(TASK_START)) { + glusterUtil.startVolume(volumeName, onlineServer.getName()); + } else if (operation.equals(TASK_STOP)) { + glusterUtil.stopVolume(volumeName, onlineServer.getName()); + } else { + throw new GlusterValidationException("Invalid operation code [" + operation + "]"); + } + } + + @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) { + 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!"); + } + + if (deleteFlag == null) { + deleteFlag = false; + } + + Volume volume = null; + try { + volume = getVolume(clusterName, volumeName); + } catch (Exception e) { + // TODO: Log the exception + return errorResponse(e.getMessage()); + } + + List<Brick> bricks = volume.getBricks(); + Status status = glusterUtil.deleteVolume(volumeName, clusterService.getOnlineServer(clusterName) + .getName()); + if(!status.isSuccess()) { + return errorResponse("Couldn't delete volume [" + volumeName + "]. Error: " + status); + } + + try { + postDelete(volumeName, bricks, deleteFlag); + } catch(Exception e) { + return errorResponse("Volume [" + volumeName + + "] deleted from cluster, however following errors happened: " + CoreConstants.NEWLINE + + e.getMessage()); + } + + 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) { + List<String> brickList = Arrays.asList(bricks.split(",")); // Convert from comma separated string (query + // parameter) + 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 (bricks == null || bricks.isEmpty()) { + return badRequestResponse("Parameter [" + QUERY_PARAM_BRICKS + "] is missing in request!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return notFoundResponse("Cluster [" + clusterName + "] not found!"); + } + + if(deleteFlag == null) { + deleteFlag = false; + } + + GlusterServer onlineServer = clusterService.getOnlineServer(clusterName); + if (onlineServer == null) { + return errorResponse("No online servers found in cluster [" + clusterName + "]"); + } + + try { + removeBricks(clusterName, volumeName, brickList, onlineServer); + } catch(Exception e) { + return errorResponse(e.getMessage()); + } + + try { + cleanupDirectories(brickList, volumeName, brickList.size(), deleteFlag); + } catch(Exception e) { + // append cleanup error to prepare brick error + return errorResponse(e.getMessage()); + } + + return noContentResponse(); + } + + public void removeBricks(String clusterName, String volumeName, List<String> brickList, GlusterServer onlineServer) { + Status status; + try { + status = glusterUtil.removeBricks(volumeName, brickList, onlineServer.getName()); + } catch (ConnectionException 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 + "]"); + } + status = glusterUtil.removeBricks(volumeName, brickList, onlineServer.getName()); + } + if (!status.isSuccess()) { + throw new GlusterRuntimeException(status.toString()); + } + } + + @SuppressWarnings("rawtypes") + private void cleanupDirectories(List<String> bricks, String volumeName, int maxIndex, boolean deleteFlag) { + Status result; + String errors = ""; + for (int i = 0; i < maxIndex; i++) { + String[] brickInfo = bricks.get(i).split(":"); + String serverName = brickInfo[0]; + String brickDirectory = brickInfo[1]; + + String mountPoint = brickDirectory.substring(0, brickDirectory.lastIndexOf("/")); + Object response = serverUtil.executeScriptOnServer(true, serverName, VOLUME_DIRECTORY_CLEANUP_SCRIPT + " " + + mountPoint + " " + volumeName + " " + (deleteFlag ? "-d" : ""), GenericResponse.class); + if (response instanceof GenericResponse) { + result = ((GenericResponse) response).getStatus(); + if (!result.isSuccess()) { + errors += "[" + mountPoint + "][" + volumeName + "] => " + result + + CoreConstants.NEWLINE; + } + } else { + Status errStatus = (Status) response; + errors += "[" + mountPoint + "][" + volumeName + "] => " + errStatus + CoreConstants.NEWLINE; + } + } + if(!errors.trim().isEmpty()) { + throw new GlusterRuntimeException("Volume directory cleanup errors: " + errors.trim()); + } + } + + private void postDelete(String volumeName, List<Brick> bricks, boolean deleteFlag) { + Status result; + for (Brick brick : bricks) { + String brickDirectory = brick.getBrickDirectory(); + String mountPoint = brickDirectory.substring(0, brickDirectory.lastIndexOf("/")); + + result = (Status) serverUtil.executeScriptOnServer(true, brick.getServerName(), VOLUME_DIRECTORY_CLEANUP_SCRIPT + + " " + mountPoint + " " + volumeName + (deleteFlag ? " -d" : ""), Status.class); + if (!result.isSuccess()) { + throw new GlusterRuntimeException("Error in post-delete operation of volume [" + volumeName + "]: " + + result); + } + } + } + + @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) { + 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(key == null || key.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_OPTION_KEY + "] is missing in request!"); + } + + if(value == null || value.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_OPTION_VALUE + "] is missing in request!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return notFoundResponse("Cluster [" + clusterName + "] not found!"); + } + + GlusterServer onlineServer = clusterService.getOnlineServer(clusterName); + if (onlineServer == null) { + return errorResponse("No online servers found in cluster [" + clusterName + "]"); + } + + try { + glusterUtil.setOption(volumeName, key, value, onlineServer.getName()); + } catch (ConnectionException e) { + // online server has gone offline! try with a different one. + onlineServer = clusterService.getNewOnlineServer(clusterName); + if (onlineServer == null) { + return errorResponse("No online servers found in cluster [" + clusterName + "]"); + } + + try { + glusterUtil.setOption(volumeName, key, value, onlineServer.getName()); + } catch(Exception e1) { + return errorResponse(e1.getMessage()); + } + } catch(Exception e) { + return errorResponse(e.getMessage()); + } + + 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) { + 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!"); + } + + GlusterServer onlineServer = clusterService.getOnlineServer(clusterName); + if (onlineServer == null) { + return errorResponse("No online servers found in cluster [" + clusterName + "]"); + } + + try { + glusterUtil.resetOptions(volumeName, onlineServer.getName()); + } catch (ConnectionException e) { + // online server has gone offline! try with a different one. + onlineServer = clusterService.getNewOnlineServer(clusterName); + if (onlineServer == null) { + return errorResponse("No online servers found in cluster [" + clusterName + "]"); + } + + try { + glusterUtil.resetOptions(volumeName, onlineServer.getName()); + } catch(Exception e1) { + return errorResponse(e1.getMessage()); + } + } catch(Exception e) { + return errorResponse(e.getMessage()); + } + + return noContentResponse(); + } + + @GET + @Path(RESOURCE_DEFAULT_OPTIONS) + @Produces(MediaType.APPLICATION_XML) + public VolumeOptionInfoListResponse getDefaultOptionsXML(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName) { + // TODO: Fetch all volume options with their default values from GlusterFS + // whenever such a CLI command is made available in GlusterFS + return new VolumeOptionInfoListResponse(Status.STATUS_SUCCESS, volumeOptionsDefaults.getDefaults()); + } + + @GET + @Path(RESOURCE_DEFAULT_OPTIONS) + @Produces(MediaType.APPLICATION_JSON) + public VolumeOptionInfoListResponse getDefaultOptionsJSON(@PathParam(PATH_PARAM_CLUSTER_NAME) String clusterName) { + // TODO: Fetch all volume options with their default values from GlusterFS + // whenever such a CLI command is made available in GlusterFS + return new VolumeOptionInfoListResponse(Status.STATUS_SUCCESS, volumeOptionsDefaults.getDefaults()); + } + + 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.getBrickDirectory()); + String logFilePath = logDir + CoreConstants.FILE_SEPARATOR + logFileName; + + // Usage: get_volume_disk_log.py <volumeName> <diskName> <lineCount> + Object responseObj = serverUtil.executeScriptOnServer(true, brick.getServerName(), VOLUME_BRICK_LOG_SCRIPT + + " " + logFilePath + " " + lineCount, LogMessageListResponse.class); + + LogMessageListResponse response = null; + if (responseObj instanceof LogMessageListResponse) { + response = (LogMessageListResponse) responseObj; + // populate disk and trim other fields + List<VolumeLogMessage> logMessages = response.getLogMessages(); + for (VolumeLogMessage logMessage : logMessages) { + logMessage.setBrickDirectory(brick.getBrickDirectory()); + } + return logMessages; + } else { + Status status = (Status) responseObj; + throw new GlusterRuntimeException(status.toString()); + } + } + + @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 = getVolume(clusterName, volumeName); + File archiveFile = new File(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; + } + } + + + private 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.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"; + new ProcessUtil().executeCommand("tar", "czvf", gzipPath, "-C", tempDir.getParent(), tempDir.getName()); + + // delete the temp directory + FileUtil.recursiveDelete(tempDir); + + return gzipPath; + } + + @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); + } + + public Response getLogs(String clusterName, String volumeName, String brickName, String severity, + String fromTimestamp, String toTimestamp, Integer lineCount, String mediaType) { + 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!"); + } + + List<VolumeLogMessage> logMessages = null; + Volume volume = null; + try { + volume = (Volume) getVolume(clusterName, volumeName); + } catch(Exception e) { + return errorResponse(e.getMessage()); + } + + 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 okResponse(new LogMessageListResponse(logMessages), mediaType); + } + + 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; + } + + @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) { + if (clusterName == null || clusterName.isEmpty()) { + return badRequestResponse("Cluster name must not be empty!"); + } + + if (volumeName == null || volumeName.isEmpty()) { + return badRequestResponse("Cluster name must not be empty!"); + } + + if (bricks == null || bricks.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_BRICKS + "] is missing in request!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return notFoundResponse("Cluster [" + clusterName + "] not found!"); + } + + GlusterServer onlineServer = clusterService.getOnlineServer(clusterName); + if (onlineServer == null) { + return errorResponse("No online servers found in cluster [" + clusterName + "]"); + } + + List<String> brickList = Arrays.asList(bricks.split(",")); + try { + glusterUtil.addBricks(volumeName, brickList, onlineServer.getName()); + } catch (ConnectionException e) { + // online server has gone offline! try with a different one. + onlineServer = clusterService.getNewOnlineServer(clusterName); + if (onlineServer == null) { + return errorResponse("No online servers found in cluster [" + clusterName + "]"); + } + + try { + glusterUtil.addBricks(volumeName, brickList, onlineServer.getName()); + } catch(Exception e1) { + return errorResponse(e1.getMessage()); + } + } catch(Exception e1) { + return errorResponse(e1.getMessage()); + } + + return createdResponse(""); + } + + @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) { + + 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 (fromBrick == null || fromBrick.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_SOURCE + "] is missing in request!"); + } + + if (toBrick == null || toBrick.isEmpty()) { + return badRequestResponse("Parameter [" + FORM_PARAM_TARGET + "] is missing in request!"); + } + + if (clusterService.getCluster(clusterName) == null) { + return notFoundResponse("Cluster [" + clusterName + "] not found!"); + } + + GlusterServer onlineServer = clusterService.getOnlineServer(clusterName); + if (onlineServer == null) { + return errorResponse("No online servers found in cluster [" + clusterName + "]"); + } + + if(autoCommit == null) { + autoCommit = false; + } + + String taskId = null; + try { + taskId = migrateBrickStart(clusterName, volumeName, fromBrick, toBrick, autoCommit); + }catch(Exception e) { + return errorResponse(e.getMessage()); + } + + return acceptedResponse(RESTConstants.RESOURCE_PATH_CLUSTERS + "/" + clusterName + "/" + RESOURCE_TASKS + "/" + + taskId); + } + + private String migrateBrickStart(String clusterName, String volumeName, String fromBrick, String toBrick, + Boolean autoCommit) { + MigrateBrickTask migrateDiskTask = new MigrateBrickTask(clusterService, clusterName, volumeName, fromBrick, + toBrick); + migrateDiskTask.setAutoCommit(autoCommit); + migrateDiskTask.start(); + taskResource.addTask(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; + } + + private 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(rebalanceTask); + return rebalanceTask.getId(); + } + + public void rebalanceStop(String clusterName, String volumeName) { + // TODO: arrive at the task id and fetch it + String taskId = ""; + + taskResource.getTask(taskId).stop(); + } +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/security/GlusterUserDetailsService.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/security/GlusterUserDetailsService.java new file mode 100644 index 00000000..e806051c --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/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 com.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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/security/UserAuthDao.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/security/UserAuthDao.java new file mode 100644 index 00000000..bdc3b19e --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/security/UserAuthDao.java @@ -0,0 +1,42 @@ +/** + * 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 com.gluster.storage.management.gateway.security; + +import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl; + +/** + * @author root + * + */ +public class UserAuthDao extends JdbcDaoImpl implements GlusterUserDetailsService { + + /* + * (non-Javadoc) + * + * @see com.gluster.storage.management.gateway.security.GlusterUserDetailsService#changePassword(java.lang.String, + * java.lang.String) + */ + @Override + public void changePassword(String username, String password) { + getJdbcTemplate().update("UPDATE USERS SET PASSWORD = ? WHERE USERNAME = ?", password, username); + } + +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/services/ClusterService.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/services/ClusterService.java new file mode 100644 index 00000000..e01c5096 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/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 com.gluster.storage.management.gateway.services; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.EntityTransaction; + +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.gluster.storage.management.core.constants.CoreConstants; +import com.gluster.storage.management.core.exceptions.ConnectionException; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.core.model.GlusterServer; +import com.gluster.storage.management.core.model.Server.SERVER_STATUS; +import com.gluster.storage.management.core.utils.LRUCache; +import com.gluster.storage.management.core.utils.ProcessResult; +import com.gluster.storage.management.gateway.data.ClusterInfo; +import com.gluster.storage.management.gateway.data.PersistenceDao; +import com.gluster.storage.management.gateway.data.ServerInfo; +import com.gluster.storage.management.gateway.utils.GlusterUtil; +import com.gluster.storage.management.gateway.utils.ServerUtil; +import com.gluster.storage.management.gateway.utils.SshUtil; + +/** + * Service class for functionality related to clusters + */ +@Component +public class ClusterService { + @Autowired + private PersistenceDao<ClusterInfo> clusterDao; + + @Autowired + private PersistenceDao<ServerInfo> serverDao; + + @Autowired + private GlusterUtil glusterUtil; + + @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().equals(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().equals(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 = glusterUtil.getGlusterServers(server); + List<ServerInfo> servers = new ArrayList<ServerInfo>(); + for(GlusterServer glusterServer : glusterServers) { + String serverName = glusterServer.getName(); + + 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().equals(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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/services/GlusterServerService.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/services/GlusterServerService.java new file mode 100644 index 00000000..d1437cec --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/services/GlusterServerService.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * 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 com.gluster.storage.management.gateway.services; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.gluster.storage.management.core.constants.CoreConstants; +import com.gluster.storage.management.core.exceptions.ConnectionException; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.core.exceptions.GlusterValidationException; +import com.gluster.storage.management.core.model.GlusterServer; +import com.gluster.storage.management.core.model.Server.SERVER_STATUS; +import com.gluster.storage.management.gateway.data.ClusterInfo; +import com.gluster.storage.management.gateway.utils.GlusterUtil; +import com.gluster.storage.management.gateway.utils.ServerUtil; + +/** + * + */ +@Component +public class GlusterServerService { + @Autowired + protected ServerUtil serverUtil; + + @Autowired + private ClusterService clusterService; + + @Autowired + private GlusterUtil glusterUtil; + + public void fetchServerDetails(GlusterServer server) { + try { + server.setStatus(SERVER_STATUS.ONLINE); + serverUtil.fetchServerDetails(server); + } catch (ConnectionException e) { + server.setStatus(SERVER_STATUS.OFFLINE); + } + } + + public List<GlusterServer> getGlusterServers(String clusterName, boolean fetchDetails) { + 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); + } catch (ConnectionException 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 + "]"); + } + glusterServers = getGlusterServers(clusterName, onlineServer, fetchDetails); + } + return glusterServers; + } + + private List<GlusterServer> getGlusterServers(String clusterName, GlusterServer onlineServer, boolean fetchDetails) { + List<GlusterServer> glusterServers; + try { + glusterServers = glusterUtil.getGlusterServers(onlineServer); + } catch (ConnectionException 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 + "]"); + } + + glusterServers = glusterUtil.getGlusterServers(onlineServer); + } + + if (fetchDetails) { + String errMsg = fetchDetailsOfServers(glusterServers, onlineServer); + if (!errMsg.isEmpty()) { + throw new GlusterRuntimeException("Couldn't fetch details for server(s): " + errMsg); + } + } + return glusterServers; + } + + private String fetchDetailsOfServers(List<GlusterServer> glusterServers, GlusterServer onlineServer) { + String errMsg = ""; + + for (GlusterServer server : glusterServers) { + try { + fetchServerDetails(server); + } catch (Exception e) { + errMsg += CoreConstants.NEWLINE + server.getName() + " : [" + e.getMessage() + "]"; + } + } + return errMsg; + } + + 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 = glusterUtil.getGlusterServer(onlineServer, serverName); + } catch (ConnectionException 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 + "]"); + } + server = glusterUtil.getGlusterServer(onlineServer, serverName); + } + + if (fetchDetails && server.isOnline()) { + 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; + } + } +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/InitServerTask.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/InitServerTask.java new file mode 100644 index 00000000..6d525785 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/InitServerTask.java @@ -0,0 +1,162 @@ +/** + * 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 com.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.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.RowCallbackHandler; +import org.springframework.jdbc.core.support.JdbcDaoSupport; +import org.springframework.security.authentication.encoding.PasswordEncoder; + +import com.gluster.storage.management.core.constants.CoreConstants; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.gateway.data.ClusterInfo; +import com.gluster.storage.management.gateway.data.PersistenceDao; + +/** + * Initializes the Gluster Management Server. + */ +public class InitServerTask extends JdbcDaoSupport { + @Autowired + private PasswordEncoder passwordEncoder; + + @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); + String encodedPassword = passwordEncoder.encodePassword(password, null); + 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(); + } + + // For development time debugging. To be removed later. + List<ClusterInfo> clusters = clusterDao.findAll(); + logger.info(clusters.size()); + + if (clusters.size() > 0) { + for (ClusterInfo cluster : clusters) { + logger.info("Cluster: [" + cluster.getId() + "][" + cluster.getName() + "]"); + } + } else { + + } + } + + 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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/InitializeDiskTask.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/InitializeDiskTask.java new file mode 100644 index 00000000..e7da1de6 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/InitializeDiskTask.java @@ -0,0 +1,164 @@ +/** + * 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 com.gluster.storage.management.gateway.tasks; + +import org.springframework.context.ApplicationContext; +import org.springframework.web.context.ContextLoader; + +import com.gluster.storage.management.core.constants.GlusterConstants; +import com.gluster.storage.management.core.exceptions.ConnectionException; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.core.model.Status; +import com.gluster.storage.management.core.model.TaskInfo; +import com.gluster.storage.management.core.model.TaskInfo.TASK_TYPE; +import com.gluster.storage.management.core.model.TaskStatus; +import com.gluster.storage.management.core.utils.ProcessResult; +import com.gluster.storage.management.gateway.services.ClusterService; +import com.gluster.storage.management.gateway.utils.GlusterUtil; +import com.gluster.storage.management.gateway.utils.ServerUtil; +import com.gluster.storage.management.gateway.utils.SshUtil; +import com.sun.jersey.core.util.Base64; + +public class InitializeDiskTask extends Task { + + private static final String INITIALIZE_DISK_SCRIPT = "format_device.py"; + + private String serverName; + private String diskName; + private String fsType; + private ServerUtil serverUtil; + private GlusterUtil glusterUtil; + + public InitializeDiskTask(ClusterService clusterService, String clusterName, String serverName, String diskName, String fsType) { + super(clusterService, clusterName, TASK_TYPE.DISK_FORMAT, diskName, "Initialize disk " + serverName + ":" + + diskName, false, false, false); + + setServerName(serverName); + setDiskName(diskName); + setFsType(fsType); + taskInfo.setName(getId()); + init(); + } + + public InitializeDiskTask(ClusterService clusterService, String clusterName, TaskInfo info) { + super(clusterService, clusterName, info); + init(); + } + + private void init() { + ApplicationContext ctx = ContextLoader.getCurrentWebApplicationContext(); + glusterUtil = ctx.getBean(GlusterUtil.class); + 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 fsTypeCommand = (getFsType().equals(GlusterConstants.FSTYPE_DEFAULT)) ? "" : " -t " + getFsType(); + + String output = (String) serverUtil.executeScriptOnServer(true, serverName, INITIALIZE_DISK_SCRIPT + + fsTypeCommand + " " + getDiskName(), String.class); + 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 glusterUtil.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())); + } + } + + 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; + } +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/MigrateBrickTask.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/MigrateBrickTask.java new file mode 100644 index 00000000..694067aa --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/MigrateBrickTask.java @@ -0,0 +1,227 @@ +/** + * 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 com.gluster.storage.management.gateway.tasks; + +import org.springframework.context.ApplicationContext; +import org.springframework.web.context.ContextLoader; + +import com.gluster.storage.management.core.exceptions.ConnectionException; +import com.gluster.storage.management.core.model.Status; +import com.gluster.storage.management.core.model.TaskInfo.TASK_TYPE; +import com.gluster.storage.management.core.model.TaskStatus; +import com.gluster.storage.management.core.utils.ProcessResult; +import com.gluster.storage.management.gateway.services.ClusterService; +import com.gluster.storage.management.gateway.utils.GlusterUtil; +import com.sun.jersey.core.util.Base64; + +public class MigrateBrickTask extends Task { + + private String fromBrick; + private String toBrick; + private Boolean autoCommit; + private GlusterUtil glusterUtil; + + 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, "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(); + glusterUtil = ctx.getBean(GlusterUtil.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 (ConnectionException e) { + // online server might have gone Offline. try with a new one. + startMigration(getNewOnlineServer().getName()); + } + } + + private void startMigration(String onlineServerName) { + ProcessResult processResult = glusterUtil.executeBrickMigration(onlineServerName, getTaskInfo().getReference(), + getFromBrick(), getToBrick(), "start"); + if (processResult.getOutput().trim().matches(".*started successfully$")) { + getTaskInfo().setStatus( + new TaskStatus(new Status(Status.STATUS_CODE_RUNNING, processResult.getOutput().trim()))); + return; + } + } + + @Override + public void pause() { + try { + pauseMigration(getOnlineServer().getName()); + } catch (ConnectionException e) { + // online server might have gone offline. try with a new one. + pauseMigration(getNewOnlineServer().getName()); + } + } + + private void pauseMigration(String onlineServer) { + ProcessResult processResult = glusterUtil.executeBrickMigration(onlineServer, taskInfo.getReference(), + getFromBrick(), getToBrick(), "pause"); + TaskStatus taskStatus = new TaskStatus(); + if (processResult.getOutput().trim().matches(".*paused successfully$")) { + taskStatus.setCode(Status.STATUS_CODE_PAUSE); + taskStatus.setMessage(processResult.getOutput()); + getTaskInfo().setStatus(taskStatus); + return; + } + } + + @Override + public void resume() { + start(); + } + + @Override + public void commit() { + try { + commitMigration(getOnlineServer().getName()); + } catch (ConnectionException e) { + // online server might have gone offline. try with a new one. + commitMigration(getNewOnlineServer().getName()); + } + } + + private void commitMigration(String serverName) { + ProcessResult processResult = glusterUtil.executeBrickMigration(serverName, getTaskInfo().getReference(), + getFromBrick(), getToBrick(), "commit"); + TaskStatus taskStatus = new TaskStatus(); + if (processResult.isSuccess()) { + if (processResult.getOutput().trim().matches(".*commit successful$")) { + taskStatus.setCode(Status.STATUS_CODE_SUCCESS); + taskStatus.setMessage(processResult.getOutput()); + getTaskInfo().setStatus(taskStatus); + } + } + } + + @Override + public void stop() { + try { + stopMigration(getOnlineServer().getName()); + } catch (ConnectionException e) { + // online server might have gone offline. try with a new one. + stopMigration(getNewOnlineServer().getName()); + } + } + + private void stopMigration(String serverName) { + ProcessResult processResult = glusterUtil.executeBrickMigration(serverName, taskInfo.getReference(), getFromBrick(), + getToBrick(), "abort"); + TaskStatus taskStatus = new TaskStatus(); + if (processResult.getOutput().trim().matches(".*aborted successfully$")) { + taskStatus.setCode(Status.STATUS_CODE_SUCCESS); + taskStatus.setMessage(processResult.getOutput()); + getTaskInfo().setStatus(taskStatus); + } + } + + @Override + public TaskStatus checkStatus() { + try { + return checkMigrationStatus(getOnlineServer().getName()); + } catch (ConnectionException e) { + // online server might have gone offline. try with a new one. + return checkMigrationStatus(getNewOnlineServer().getName()); + } + } + + private TaskStatus checkMigrationStatus(String serverName) { + if (getTaskInfo().getStatus().getCode() == Status.STATUS_CODE_PAUSE) { + return getTaskInfo().getStatus(); + } + // For committed task, status command (CLI) is invalid, just return current status + if (getTaskInfo().getStatus().getCode() == Status.STATUS_CODE_SUCCESS) { + return getTaskInfo().getStatus(); + } + + + TaskStatus taskStatus = new TaskStatus(); + try { + ProcessResult processResult = glusterUtil.executeBrickMigration(serverName, getTaskInfo().getReference(), + getFromBrick(), getToBrick(), "status"); + if (processResult.getOutput().trim().matches("^Number of files migrated.*Migration complete$") + || processResult.getOutput().trim().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); + if (autoCommit) { + commitMigration(serverName); + return getTaskInfo().getStatus(); // return the committed status + } else { + taskStatus.setMessage(processResult.getOutput().trim() + .replaceAll("Migration complete", "Commit pending")); + } + } else if (processResult.getOutput().trim().matches("^Number of files migrated.*Current file=.*")) { + taskStatus.setCode(Status.STATUS_CODE_RUNNING); + } else { + taskStatus.setCode(Status.STATUS_CODE_FAILURE); + } + taskStatus.setMessage(processResult.getOutput()); + } catch (Exception e) { + taskStatus.setCode(Status.STATUS_CODE_FAILURE); + taskStatus.setMessage(e.getMessage()); + } + taskInfo.setStatus(taskStatus); // Update the task status + return taskStatus; + } +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/RebalanceVolumeTask.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/RebalanceVolumeTask.java new file mode 100644 index 00000000..c53f0252 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/RebalanceVolumeTask.java @@ -0,0 +1,129 @@ +/** + * 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 com.gluster.storage.management.gateway.tasks; + +import org.springframework.context.ApplicationContext; +import org.springframework.web.context.ContextLoader; + +import com.gluster.storage.management.core.exceptions.ConnectionException; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.core.model.Status; +import com.gluster.storage.management.core.model.TaskInfo.TASK_TYPE; +import com.gluster.storage.management.core.model.TaskStatus; +import com.gluster.storage.management.core.utils.ProcessResult; +import com.gluster.storage.management.gateway.services.ClusterService; +import com.gluster.storage.management.gateway.utils.GlusterUtil; +import com.gluster.storage.management.gateway.utils.ServerUtil; +import com.gluster.storage.management.gateway.utils.SshUtil; +import com.sun.jersey.core.util.Base64; + +public class RebalanceVolumeTask extends Task { + + private String layout; + private String serverName; + private ServerUtil serverUtil; + private GlusterUtil 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(GlusterUtil.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(ConnectionException e) { + // online server might have gone offline. try with a new one + serverName = getNewOnlineServer().getName(); + startRebalance(serverName); + } + } + + private void startRebalance(String serverName) { + String command = "gluster volume rebalance " + getTaskInfo().getReference() + " " + getLayout() + " start"; + String output = (String)serverUtil.executeOnServer(true, serverName, command, String.class); + 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 (ConnectionException e) { + // online server might have gone offline. update the failure status + getTaskInfo().setStatus(new TaskStatus(new Status(Status.STATUS_CODE_FAILURE, 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(ConnectionException e) { + // 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(); + } + } + + 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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/ServerSyncTask.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/ServerSyncTask.java new file mode 100644 index 00000000..94c743aa --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/ServerSyncTask.java @@ -0,0 +1,154 @@ +/** + * 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 com.gluster.storage.management.gateway.tasks; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.gluster.storage.management.core.constants.CoreConstants; +import com.gluster.storage.management.core.constants.GlusterConstants; +import com.gluster.storage.management.core.model.GlusterServer; +import com.gluster.storage.management.core.utils.GlusterCoreUtil; +import com.gluster.storage.management.core.utils.ProcessResult; +import com.gluster.storage.management.gateway.data.ClusterInfo; +import com.gluster.storage.management.gateway.data.PersistenceDao; +import com.gluster.storage.management.gateway.data.ServerInfo; +import com.gluster.storage.management.gateway.resources.v1_0.DiscoveredServersResource; +import com.gluster.storage.management.gateway.services.ClusterService; +import com.gluster.storage.management.gateway.services.GlusterServerService; +import com.gluster.storage.management.gateway.utils.ServerUtil; + +/** + * 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 DiscoveredServersResource discoveredServersResource; + + @Autowired + private GlusterServerService glusterServerService; + + @Autowired + private String discoveryMechanism; + + @Autowired + private ClusterService clusterService; + + @Autowired + private PersistenceDao<ClusterInfo> clusterDao; + + public void perform() { + discoverServers(); + syncClusterServerMapping(); + } + + private void syncClusterServerMapping() { + List<ClusterInfo> clusters = clusterService.getAllClusters(); + for(ClusterInfo cluster : clusters) { + List<ServerInfo> servers = cluster.getServers(); + List<GlusterServer> actualServers = glusterServerService.getGlusterServers(cluster.getName(), false); + updateRemovedServers(cluster, servers, actualServers); + updateAddedServers(cluster, servers, actualServers); + } + } + + 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); + } + } + } + + discoveredServersResource.setDiscoveredServerNames(serverNameList); + } +}
\ No newline at end of file diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/Task.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/Task.java new file mode 100644 index 00000000..1ba360bc --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/tasks/Task.java @@ -0,0 +1,112 @@ +/** + * 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 com.gluster.storage.management.gateway.tasks; + +import com.gluster.storage.management.core.model.GlusterServer; +import com.gluster.storage.management.core.model.TaskInfo; +import com.gluster.storage.management.core.model.TaskInfo.TASK_TYPE; +import com.gluster.storage.management.core.model.TaskStatus; +import com.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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/AbstractStatsFactory.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/AbstractStatsFactory.java new file mode 100644 index 00000000..13a58729 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/AbstractStatsFactory.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * 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 com.gluster.storage.management.gateway.utils; + +import java.util.List; + +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.core.model.ServerStats; +import com.gluster.storage.management.core.model.ServerStatsRow; +import com.gluster.storage.management.core.model.Status; + +/** + * + */ +@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); + + for (String serverName : serverNames) { + try { + // fetch the stats and add to aggregated stats + addServerStats(fetchStats(serverName, period), aggregatedStats, dataCount); + } catch(Exception e) { + // server might be offline - continue with next one + logger.warn("Couldn't fetch performance stats from server [" + serverName + "]!", e); + continue; + } + } + + 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++) { + 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. + aggregatedStatsRowData.set(i, aggregatedStatsRowData.get(i) + 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++) { + 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; + } + } + Object output = serverUtil.executeScriptOnServer(true, serverName, getStatsScriptName() + argsStr + " " + + period, ServerStats.class); + //String cpuUsageData = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> <xport> <meta> <start>1310468100</start> <step>300</step> <end>1310471700</end> <rows>13</rows> <columns>3</columns> <legend> <entry>user</entry> <entry>system</entry> <entry>total</entry> </legend> </meta> <data> <row><t>1310468100</t><v>2.23802952e-1</v><v>4.3747778209e-01</v><v>6.6128073384e-01</v></row> <row><t>1310468400</t><v>2.3387347338e-01</v><v>4.4642717442e-01</v><v>6.8030064780e-01</v></row> <row><t>1310468700</t><v>5.5043873220e+00</v><v>6.2462376636e+00</v><v>1.1750624986e+01</v></row> <row><t>1310469000</t><v>2.4350593653e+01</v><v>2.6214585217e+01</v><v>5.0565178869e+01</v></row> <row><t>1310469300</t><v>4.0786489953e+01</v><v>4.6784713828e+01</v><v>8.7571203781e+01</v></row> <row><t>1310469600</t><v>4.1459955508e+01</v><v>5.2546309044e+01</v><v>9.4006264551e+01</v></row> <row><t>1310469900</t><v>4.2312286165e+01</v><v>5.2390588332e+01</v><v>9.4702874497e+01</v></row> <row><t>1310470200</t><v>4.2603794982e+01</v><v>5.1598861493e+01</v><v>9.4202656475e+01</v></row> <row><t>1310470500</t><v>3.8238751290e+01</v><v>4.5312089966e+01</v><v>8.3550841256e+01</v></row> <row><t>1310470800</t><v>1.7949961224e+01</v><v>2.1282058418e+01</v><v>3.9232019642e+01</v></row> <row><t>1310471100</t><v>1.2330371421e-01</v><v>4.6347832868e-01</v><v>5.8678204289e-01</v></row> <row><t>1310471400</t><v>1.6313260492e-01</v><v>5.4088119561e-01</v><v>7.0401380052e-01</v></row> <row><t>1310471700</t><v>NaN</v><v>NaN</v><v>NaN</v></row> </data> </xport>"; + //Object output = unmarshal(ServerStats.class, cpuUsageData, false); + if(output instanceof Status) { + throw new GlusterRuntimeException(((Status)output).toString()); + } + return (ServerStats) output; + } + + public abstract String getStatsScriptName(); +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/CpuStatsFactory.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/CpuStatsFactory.java new file mode 100644 index 00000000..b6ef9ef2 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/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 com.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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/GlusterUtil.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/GlusterUtil.java new file mode 100644 index 00000000..55909d54 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/GlusterUtil.java @@ -0,0 +1,662 @@ +/** + * GlusterUtil.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 com.gluster.storage.management.gateway.utils; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.gluster.storage.management.core.constants.CoreConstants; +import com.gluster.storage.management.core.constants.GlusterConstants; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.core.model.Brick; +import com.gluster.storage.management.core.model.Brick.BRICK_STATUS; +import com.gluster.storage.management.core.model.GlusterServer; +import com.gluster.storage.management.core.model.InitDiskStatusResponse; +import com.gluster.storage.management.core.model.InitDiskStatusResponse.FORMAT_STATUS; +import com.gluster.storage.management.core.model.Server.SERVER_STATUS; +import com.gluster.storage.management.core.model.Status; +import com.gluster.storage.management.core.model.TaskStatus; +import com.gluster.storage.management.core.model.Volume; +import com.gluster.storage.management.core.model.Volume.TRANSPORT_TYPE; +import com.gluster.storage.management.core.model.Volume.VOLUME_STATUS; +import com.gluster.storage.management.core.model.Volume.VOLUME_TYPE; +import com.gluster.storage.management.core.utils.GlusterCoreUtil; +import com.gluster.storage.management.core.utils.ProcessResult; +import com.gluster.storage.management.core.utils.StringUtil; +import com.gluster.storage.management.gateway.resources.v1_0.TasksResource; + +@Component +public class GlusterUtil { + private static final String glusterFSminVersion = "3.1"; + + 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 = "Connected"; + + 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_OPTION_AUTH_ALLOW_PFX = "auth.allow:"; + 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 GLUSTERD_INFO_FILE = "/etc/glusterd/glusterd.info"; + + private static final GlusterCoreUtil glusterCoreUtil = new GlusterCoreUtil(); + + private static final String INITIALIZE_DISK_STATUS_SCRIPT = "get_format_device_status.py"; + + @Autowired + private SshUtil sshUtil; + + @Autowired + private ServerUtil serverUtil; + + @Autowired + private TasksResource taskResource; + + public void setSshUtil(SshUtil sshUtil) { + this.sshUtil = sshUtil; + } + + public SshUtil getSshUtil() { + return sshUtil; + } + + /** + * Extract value of given token from given line. It is assumed that the token, if present, will be of the following + * form: <code>token: value</code> + * + * @param line + * Line to be analyzed + * @param token + * Token whose value is to be extracted + * @return Value of the token, if present in the line + */ + private final String extractToken(String line, String token) { + if (line.contains(token)) { + return line.split(token)[1].trim(); + } + return null; + } + + public GlusterServer getGlusterServer(GlusterServer onlineServer, String serverName) { + List<GlusterServer> servers = getGlusterServers(onlineServer); + for (GlusterServer server : servers) { + if (server.getName().equals(serverName)) { + return server; + } + } + return null; + } + + private String getUuid(String serverName) { + ProcessResult result = getSshUtil().executeRemote(serverName, "cat " + GLUSTERD_INFO_FILE); + if (!result.isSuccess()) { + throw new GlusterRuntimeException("Couldn't read file [" + GLUSTERD_INFO_FILE + "]. Error: " + + result.toString()); + } + return result.getOutput().split("=")[1]; + } + + public List<GlusterServer> getGlusterServers(GlusterServer knownServer) { + String output = getPeerStatus(knownServer.getName()); + if (output == null) { + return null; + } + + knownServer.setUuid(getUuid(knownServer.getName())); + + 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 = 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 = 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 = extractToken(line, HOSTNAME_PFX); + if (hostName != null) { + server.setName(hostName); + foundHost = true; + } + } + + } + return glusterServers; + } + + public List<String> getGlusterServerNames(String knownServer) { + String output = getPeerStatus(knownServer); + if (output == null) { + return null; + } + + List<String> glusterServerNames = new ArrayList<String>(); + for (String line : output.split(CoreConstants.NEWLINE)) { + String hostName = extractToken(line, HOSTNAME_PFX); + if (hostName != null) { + glusterServerNames.add(hostName); + } + } + return glusterServerNames; + } + + /** + * @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) { + String output; + ProcessResult result = getSshUtil().executeRemote(knownServer, "gluster peer status"); + if (!result.isSuccess()) { + output = null; + } + output = result.getOutput(); + return output; + } + + public void addServer(String existingServer, String newServer) { + ProcessResult result = sshUtil.executeRemote(existingServer, "gluster peer probe " + newServer); + if(!result.isSuccess()) { + throw new GlusterRuntimeException("Couldn't probe server [" + newServer + "] from [" + existingServer + + "]. Error: " + result); + } + + // reverse peer probe to ensure that host names appear in peer status on both sides + result = sshUtil.executeRemote(newServer, "gluster peer probe " + existingServer); + if(!result.isSuccess()) { + throw new GlusterRuntimeException("Couldn't _reverse_ probe server [" + existingServer + "] from [" + + newServer + "]. Error: " + result); + } + } + + public Status startVolume(String volumeName, String knownServer) { + return new Status(sshUtil.executeRemote(knownServer, "gluster volume start " + volumeName)); + } + + public Status stopVolume(String volumeName, String knownServer) { + return new Status(sshUtil.executeRemote(knownServer, "gluster --mode=script volume stop " + volumeName)); + } + + public void resetOptions(String volumeName, String knownServer) { + ProcessResult result = sshUtil.executeRemote(knownServer, "gluster volume reset " + volumeName); + if(!result.isSuccess()) { + throw new GlusterRuntimeException("Couldn't reset options for volume [" + volumeName + "]! Error: " + + result); + } + } + + public void createVolume(String knownServer, String volumeName, String volumeTypeStr, String transportTypeStr, + Integer replicaCount, Integer stripeCount, String bricks, String accessProtocols, String options) { + + int count = 1; // replica or stripe count + + VOLUME_TYPE volType = Volume.getVolumeTypeByStr(volumeTypeStr); + String volTypeArg = null; + if (volType == VOLUME_TYPE.DISTRIBUTED_MIRROR) { + volTypeArg = "replica"; + count = replicaCount; + } else if (volType == VOLUME_TYPE.DISTRIBUTED_STRIPE) { + volTypeArg = "stripe"; + count = stripeCount; + } + + 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); + ProcessResult result = sshUtil.executeRemote(knownServer, command); + if (!result.isSuccess()) { + throw new GlusterRuntimeException("Error in creating volume [" + volumeName + "]: " + result); + } + + 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(); + } + + 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()); + } + } + + public void setOption(String volumeName, String key, String value, String knownServer) { + ProcessResult result = sshUtil.executeRemote(knownServer, "gluster volume set " + volumeName + " " + key + " " + + "\"" + value + "\""); + if (!result.isSuccess()) { + throw new GlusterRuntimeException("Volume [" + volumeName + "] set [" + key + "=" + value + "] => " + + result); + } + } + + public Status deleteVolume(String volumeName, String knownServer) { + return new Status(sshUtil.executeRemote(knownServer, "gluster --mode=script volume delete " + volumeName)); + } + + private String getVolumeInfo(String volumeName, String knownServer) { + ProcessResult result = sshUtil.executeRemote(knownServer, "gluster volume info " + volumeName); + if (!result.isSuccess()) { + throw new GlusterRuntimeException("Command [gluster volume info " + volumeName + "] failed on [" + + knownServer + "] with error: " + result); + } + return result.getOutput(); + } + + private String getVolumeInfo(String knownServer) { + ProcessResult result = sshUtil.executeRemote(knownServer, "gluster volume info "); + if (!result.isSuccess()) { + throw new GlusterRuntimeException("Command [gluster volume info] failed on [" + knownServer + + "] with error: " + result); + } + return result.getOutput(); + } + + private boolean readVolumeType(Volume volume, String line) { + String volumeType = extractToken(line, VOLUME_TYPE_PFX); + if (volumeType != null) { + if (volumeType.equals(VOLUME_TYPE_DISTRIBUTE)) { + volume.setVolumeType(VOLUME_TYPE.PLAIN_DISTRIBUTE); + } else if (volumeType.equals(VOLUME_TYPE_REPLICATE)) { + volume.setVolumeType(VOLUME_TYPE.DISTRIBUTED_MIRROR); + volume.setReplicaCount(Volume.DEFAULT_REPLICA_COUNT); + } else { + volume.setVolumeType(VOLUME_TYPE.DISTRIBUTED_STRIPE); + volume.setStripeCount(Volume.DEFAULT_STRIPE_COUNT); + } + return true; + } + return false; + } + + private void readReplicaOrStripeCount(Volume volume, String line) { + if (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.DISTRIBUTED_STRIPE) { + volume.setStripeCount(count); + } else if (volume.getVolumeType() == VOLUME_TYPE.DISTRIBUTED_MIRROR) { + volume.setReplicaCount(count); + volume.setStripeCount(0); + } + + } + return; + } + + private boolean readVolumeStatus(Volume volume, String line) { + String volumeStatus = 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 = 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) { + //If brick directory has standard path, find and assign device name otherwise null + String stdBrickDirPattern = "^/export/.*/.*"; // e.g: /export/sdb/test + String deviceName = null; + if (Pattern.matches(stdBrickDirPattern, brickDir) ) { + deviceName = brickDir.split("/")[2].trim(); + } + volume.addBrick(new Brick(serverName, status, deviceName, brickDir)); + } + + // Do not throw exception, Gracefully handle as Offline brick. + private BRICK_STATUS getBrickStatus(String serverName, String volumeName, String brick){ + try { + ProcessResult output = getSshUtil().executeRemote(serverName, "get_brick_status.py" + " " + volumeName + " " + brick); + + if (output.isSuccess() && output.getOutput().equals(CoreConstants.ONLINE)) { + return BRICK_STATUS.ONLINE; + } else { + return BRICK_STATUS.OFFLINE; + } + } catch(Exception e) { // Particularly interested on ConnectionExecption, if the server is offline + return BRICK_STATUS.OFFLINE; + } + } + + private boolean readBrickGroup(String line) { + return extractToken(line, VOLUME_BRICKS_GROUP_PFX) != null; + } + + private boolean readOptionReconfigGroup(String line) { + return 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; + } + + public Volume getVolume(String volumeName, String knownServer) { + return parseVolumeInfo(getVolumeInfo(volumeName, knownServer)).get(0); + } + + 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 = 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 (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; + } + } + } + + if (volume != null) {// Adding the last volume parsed + volumes.add(volume); + } + + return volumes; + } + + 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); + } + + ProcessResult result = sshUtil.executeRemote(knownServer, command.toString()); + if(!result.isSuccess()) { + throw new GlusterRuntimeException("Error in volume [" + volumeName + "] add-brick [" + bricks + "]: " + + result); + } + } + + public String getLogLocation(String volumeName, String brickName, String knownServer) { + String command = "gluster volume log locate " + volumeName + " " + brickName; + ProcessResult result = sshUtil.executeRemote(knownServer, command); + if (!result.isSuccess()) { + throw new GlusterRuntimeException("Command [" + command + "] failed with error: [" + result.getExitValue() + + "][" + result.getOutput() + "]"); + } + String output = result.getOutput(); + 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 + "]"); + } + + public String getLogFileNameForBrickDir(String brickDir) { + String logFileName = brickDir; + if (logFileName.startsWith(File.separator)) { + logFileName = logFileName.replaceFirst(File.separator, ""); + } + logFileName = logFileName.replaceAll(File.separator, "-") + ".log"; + return logFileName; + } + + public Status 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); + } + return new Status(sshUtil.executeRemote(knownServer, command.toString())); + } + + public void removeServer(String existingServer, String serverName) { + ProcessResult result = sshUtil.executeRemote(existingServer, "gluster --mode=script peer detach " + serverName); + if(!result.isSuccess()) { + throw new GlusterRuntimeException("Couldn't remove server [" + serverName + "]! Error: " + result); + } + } + + public TaskStatus checkRebalanceStatus(String serverName, String volumeName) { + String command = "gluster volume rebalance " + volumeName + " status"; + ProcessResult processResult = sshUtil.executeRemote(serverName, command); + TaskStatus taskStatus = new TaskStatus(); + if (processResult.isSuccess()) { + if (processResult.getOutput().trim().matches("^rebalance completed.*")) { + taskStatus.setCode(Status.STATUS_CODE_SUCCESS); + } else if(processResult.getOutput().trim().matches(".*in progress.*")) { + taskStatus.setCode(Status.STATUS_CODE_RUNNING); + } else { + taskStatus.setCode(Status.STATUS_CODE_FAILURE); + } + } else { + taskStatus.setCode(Status.STATUS_CODE_FAILURE); + } + taskStatus.setMessage(processResult.getOutput()); // Common + return taskStatus; + } + + public void stopRebalance(String serverName, String volumeName) { + String command = "gluster volume rebalance " + volumeName + " stop"; + ProcessResult processResult = sshUtil.executeRemote(serverName, command); + TaskStatus taskStatus = new TaskStatus(); + if (processResult.isSuccess()) { + taskStatus.setCode(Status.STATUS_CODE_SUCCESS); + taskStatus.setMessage(processResult.getOutput()); + } + } + + public TaskStatus getInitializingDeviceStatus(String serverName, String diskName) { + Object response = serverUtil.executeScriptOnServer(true, serverName, INITIALIZE_DISK_STATUS_SCRIPT + " " + + diskName, InitDiskStatusResponse.class); + + TaskStatus taskStatus = new TaskStatus(); + if (response instanceof Status) { + taskStatus.setCode(Status.STATUS_CODE_FAILURE); + taskStatus.setMessage(((Status) response).getMessage()); + throw new GlusterRuntimeException(((Status) response).getMessage()); + } + + InitDiskStatusResponse initDiskStatusResponse = (InitDiskStatusResponse) response; + + 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 ProcessResult executeBrickMigration(String onlineServerName, String volumeName, String fromBrick, + String toBrick, String operation) { + String command = "gluster volume replace-brick " + volumeName + " " + fromBrick + " " + toBrick + " " + operation; + ProcessResult processResult = sshUtil.executeRemote(onlineServerName, command); + if (!processResult.isSuccess()) { + throw new GlusterRuntimeException(processResult.toString()); + } + return processResult; + } + + public static void main(String args[]) { + // List<String> names = new GlusterUtil().getGlusterServerNames(); + // System.out.println(names); + List<String> disks = new ArrayList<String>(); + disks.add("server1:sda"); + disks.add("server1:sdb"); + new GlusterUtil().addBricks("Volume3", disks, "localhost"); + } +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/MemoryStatsFactory.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/MemoryStatsFactory.java new file mode 100644 index 00000000..dc88bf52 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/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 com.gluster.storage.management.gateway.utils; + +import java.util.List; + +import org.springframework.stereotype.Component; + +import com.gluster.storage.management.core.model.ServerStats; +import com.gluster.storage.management.core.model.ServerStatsRow; + +/** + * + */ +@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/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/NetworkStatsFactory.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/NetworkStatsFactory.java new file mode 100644 index 00000000..d3d47c58 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/NetworkStatsFactory.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com> + * This file is part of Gluster Management Console. + * + * Gluster Management Console is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Gluster Management Console is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License + * for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see + * <http://www.gnu.org/licenses/>. + *******************************************************************************/ +package com.gluster.storage.management.gateway.utils; + +import java.util.List; + +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.core.model.NetworkInterface; +import com.gluster.storage.management.core.model.Server; +import com.gluster.storage.management.core.model.ServerStats; +import com.gluster.storage.management.core.model.ServerStatsRow; + +/** + * + */ +@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); + 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; + } + + for (String serverName : serverNames) { + try { + Server server = new Server(serverName); + serverUtil.fetchServerDetails(server); + + for (NetworkInterface networkInterface : server.getNetworkInterfaces()) { + // fetch the stats and add to aggregated stats + addServerStats(fetchStats(serverName, period, networkInterface.getName()), aggregatedStats, dataCount); + } + } catch(Exception e) { + // server might be offline - continue with next one + logger.warn("Couldn't fetch Network stats from server [" + serverName + "]!", e); + continue; + } + } + + averageAggregatedStats(aggregatedStats, dataCount); + } + + @Override + public ServerStats fetchStats(String serverName, String period, String... args) { + ServerStats stats = super.fetchStats(serverName, period, args); + + // the data returned by rrd contains "bytes" transferred in the given time step. Update the stats object to represent KiB/s + int step = stats.getMetadata().getStep(); + 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 / step); + } + } + + return stats; + } +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/ServerUtil.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/ServerUtil.java new file mode 100644 index 00000000..91c7c81c --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/ServerUtil.java @@ -0,0 +1,286 @@ +/** + * 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 com.gluster.storage.management.gateway.utils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.ServletContext; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; + +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.gluster.storage.management.core.constants.CoreConstants; +import com.gluster.storage.management.core.exceptions.ConnectionException; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.core.model.Server; +import com.gluster.storage.management.core.model.ServerStats; +import com.gluster.storage.management.core.model.ServerStatsRow; +import com.gluster.storage.management.core.model.Status; +import com.gluster.storage.management.core.response.GenericResponse; +import com.gluster.storage.management.core.utils.ProcessResult; +import com.gluster.storage.management.core.utils.ProcessUtil; + +@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_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, List<String> arguments) { + List<String> command = new ArrayList<String>(); + + command.add(SCRIPT_COMMAND); + command.add(getScriptPath(scriptName)); + command.addAll(arguments); + return new 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) { + Object response = fetchServerDetails(server.getName()); + server.copyFrom((Server) response); // Update the details in <Server> object + server.setDisks(((Server) response).getDisks()); + } + + public String fetchHostName(String serverName) { + Object response = fetchServerDetails(serverName); + return ((Server) response).getName(); + } + + private Object fetchServerDetails(String serverName) { + // fetch standard server details like cpu, disk, memory details + Object response = executeScriptOnServer(true, serverName, REMOTE_SCRIPT_GET_SERVER_DETAILS, Server.class); + if (response instanceof Status) { + throw new GlusterRuntimeException(((Status) response).getMessage()); + } + return response; + } + + /** + * 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 runInForeground + * @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. In case the remote execution fails + * ungracefully, an object of class {@link Status} will be returned. + */ + public Object executeScriptOnServer(boolean runInForeground, String serverName, String scriptWithArgs, + @SuppressWarnings("rawtypes") Class expectedClass) { + return executeOnServer(runInForeground, serverName, getRemoteScriptDir() + File.separator + scriptWithArgs, expectedClass); + } + + /** + * Executes given command on given server + * + * @param runInForeground + * @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("rawtypes") + public Object executeOnServer(boolean runInForeground, String serverName, String commandWithArgs, + Class expectedClass) { + try { + String output = executeOnServer(serverName, commandWithArgs); + + // In case the script execution exits ungracefully, the agent would return a GenericResponse. + // hence pass last argument as true to try GenericResponse unmarshalling in such cases. + Object response = unmarshal(expectedClass, output, expectedClass != GenericResponse.class); + if (expectedClass != GenericResponse.class && response instanceof GenericResponse) { + // expected class was not GenericResponse, but that's what we got. This means the + // script failed ungracefully. Extract and return the status object from the response + return ((GenericResponse) response).getStatus(); + } + return response; + } catch (RuntimeException e) { + // Except for connection exception, wrap any other exception in the a object and return it. + if (e instanceof ConnectionException) { + throw e; + } else { + // error during unmarshalling. return status with error from exception. + return new Status(e); + } + } + } + + private 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 + * @param tryGenericResponseOnFailure + * If true, and if the unmarshalling fails for given class, another unmarshalling will be attempted with + * class {@link GenericResponse}. If this also fails, a status object with exception message is created + * and returned. + * @return Object of given expected class, or a status object in case first unmarshalling fails. + */ + @SuppressWarnings("rawtypes") + private Object unmarshal(Class expectedClass, String input, boolean tryGenericResponseOnFailure) { + try { + // create JAXB context and instantiate marshaller + JAXBContext context = JAXBContext.newInstance(expectedClass); + Unmarshaller um = context.createUnmarshaller(); + return um.unmarshal(new ByteArrayInputStream(input.getBytes())); + } catch (JAXBException e) { + if (tryGenericResponseOnFailure) { + // unmarshalling failed. try to unmarshal a GenericResponse object + return unmarshal(GenericResponse.class, input, false); + + } + return new Status(Status.STATUS_CODE_FAILURE, "Error during unmarshalling string [" + input + + "] for class [" + expectedClass.getName() + ": [" + e.getMessage() + "]"); + } + } + + /** + * @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 (Status) executeScriptOnServer(true, serverName, REMOTE_SCRIPT_GET_DISK_FOR_DIR + " " + brickDir, Status.class); + } + + public static void main(String[] args) { +// ServerStats stats = new ServerUtil().fetchCPUUsageData("s1", "1d"); +// for(ServerStatsRow row : stats.getRows()) { +// System.out.println(row.getUsageData().get(2)); +// } +// JAXBContext context; +// try { +// context = JAXBContext.newInstance(ServerStats.class); +// Marshaller m = context.createMarshaller(); +// ByteArrayOutputStream out = new ByteArrayOutputStream(); +// m.marshal(stats, out); +// ServerStats stats1 = (ServerStats)new ServerUtil().unmarshal(ServerStats.class, out.toString(), false); +// for(ServerStatsRow row : stats1.getRows()) { +// System.out.println(row.getUsageData().get(2)); +// } +// } catch (JAXBException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } + } +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/SshUtil.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/SshUtil.java new file mode 100644 index 00000000..39dd42f9 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/SshUtil.java @@ -0,0 +1,388 @@ +/******************************************************************************* + * 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 com.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.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; + +import com.gluster.storage.management.core.constants.CoreConstants; +import com.gluster.storage.management.core.exceptions.ConnectionException; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; +import com.gluster.storage.management.core.utils.FileUtil; +import com.gluster.storage.management.core.utils.LRUCache; +import com.gluster.storage.management.core.utils.ProcessResult; + +/** + * + */ +@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 + "id_rsa"); + public static final File PUBLIC_KEY_FILE = new File(SSH_AUTHORIZED_KEYS_DIR_LOCAL + "id_rsa.pub"); +// private static final String SCRIPT_DISABLE_SSH_PASSWORD_AUTH = "disable-ssh-password-auth.sh"; + private static final String PRIVATE_KEY_PASSPHRASE = "gluster"; + 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); + return true; + } catch(ConnectionException e) { + return false; + } + } + + public boolean isPublicKeyInstalled(String serverName) { + try { + getConnection(serverName); + return true; + } catch(ConnectionException 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 = getConnectionWithPassword(serverName); + 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. + } + + byte[] publicKeyData; + try { + publicKeyData = FileUtil.readFileAsByteArray(PUBLIC_KEY_FILE); + } catch (Exception e) { + 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) { + 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); + } + + // 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 Connection getConnectionWithPassword(String serverName) { + Connection conn = createConnection(serverName); + authenticateWithPassword(conn); + return conn; + } + + private synchronized Connection getConnection(String serverName) { + Connection conn = sshConnCache.get(serverName); + if (conn != null) { + return conn; + } + + conn = createConnection(serverName); + authenticateWithPublicKey(conn); + sshConnCache.put(serverName, conn); + return conn; + } + + private void 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, PRIVATE_KEY_PASSPHRASE)) { + throw new ConnectionException("SSH Authentication (public key) failed for server [" + + conn.getHostname() + "]"); + } + } catch (IOException e) { + e.printStackTrace(); + throw new ConnectionException("Exception during SSH authentication (public key) for server [" + + conn.getHostname() + "]", e); + } + } + + private void authenticateWithPassword(Connection conn) { + try { + if (!supportsPasswordAuthentication(conn)) { + throw new ConnectionException("Password authentication not supported on [" + conn.getHostname() + + "]"); + } + + if (!conn.authenticateWithPassword(USER_NAME, DEFAULT_PASSWORD)) { + throw new ConnectionException("SSH Authentication (password) failed for server [" + + conn.getHostname() + "]"); + } + } catch (IOException e) { + e.printStackTrace(); + 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 Connection createConnection(String serverName) { + Connection conn; + conn = new Connection(serverName); + try { + conn.connect(null, sshConnectTimeout, sshKexTimeout); + } catch (IOException e) { + logger.error("Couldn't establish SSH connection with server [" + serverName + "]", e); + 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) { + try { + Session 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); + session.close(); + return result; + } catch (IOException e) { + String errMsg = "Exception while executing command [" + command + "] on [" + sshConnection.getHostname() + + "]"; + logger.error(errMsg, e); + throw new GlusterRuntimeException(errMsg, e); + } + } + + 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) { + return executeCommand(getConnectionWithPassword(serverName), command); + } + + private ProcessResult executeRemoteWithPubKey(String serverName, String command) { + try { + return executeCommand(getConnection(serverName), command); + } catch(GlusterRuntimeException e) { + Throwable cause = e.getCause(); + if(cause != null && cause instanceof IOException) { + // cached ssh connection might have gone bad. + // remove it and try with a new one + sshConnCache.remove(serverName); + return executeCommand(getConnection(serverName), command); + } else { + throw e; + } + } + } + + /** + * 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) { + try { + return executeRemoteWithPubKey(serverName, command); + } catch(ConnectionException e) { + // Couldn't connect with public key. Try with default password. + return executeRemoteWithPassword(serverName, command); + } + } + + /** + * 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 isPublicKeySetup(String serverName) { + try { + getConnection(serverName); + return true; + } catch (Exception e) { + return false; + } + } + + public void cleanup() { + for (Connection conn : sshConnCache.values()) { + conn.close(); + } + } +} diff --git a/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/StatsFactory.java b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/StatsFactory.java new file mode 100644 index 00000000..09851367 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/com/gluster/storage/management/gateway/utils/StatsFactory.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * 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 com.gluster.storage.management.gateway.utils; + +import java.util.List; + +import com.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); +} diff --git a/src/com.gluster.storage.management.gateway/src/log4j.properties b/src/com.gluster.storage.management.gateway/src/log4j.properties new file mode 100644 index 00000000..18e2e8b1 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/log4j.properties @@ -0,0 +1,19 @@ +log4j.rootLogger=INFO, CONSOLE + +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.immediateFlush=true +log4j.appender.CONSOLE.target=System.out +log4j.appender.CONSOLE.threshold=DEBUG +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout +log4j.appender.CONSOLE.layout.ConversionPattern=%d{dd MMM, yyyy HH:mm:ss} %p: %c %t - %m%n + +log4j.appender.R=org.apache.log4j.RollingFileAppender +log4j.appender.R.File=/var/log/glustermg/glustermg.log +log4j.appender.R.MaxFileSize=10MB +log4j.appender.R.MaxBackupIndex=10 +log4j.appender.R.layout=org.apache.log4j.PatternLayout +log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n + +log4j.logger.org=ERROR +log4j.logger.com=ERROR +log4j.logger.com.gluster=DEBUG
\ No newline at end of file diff --git a/src/com.gluster.storage.management.gateway/src/spring/gluster-server-base.xml b/src/com.gluster.storage.management.gateway/src/spring/gluster-server-base.xml new file mode 100644 index 00000000..c89eb2f0 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/spring/gluster-server-base.xml @@ -0,0 +1,100 @@ + +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" + xmlns:tx="http://www.springframework.org/schema/tx" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd + http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd + http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> + <context:component-scan base-package="com.gluster.storage.management.gateway" /> + <task:scheduler id="taskScheduler" /> + <task:executor id="taskExecutor" pool-size="1" /> + <task:annotation-driven executor="taskExecutor" scheduler="taskScheduler" /> + + <!-- Syncs discovered servers and cluster-server mapping. --> + <task:scheduled-tasks> + <task:scheduled ref="serverSyncTask" method="perform" fixed-delay="60000" /> + </task:scheduled-tasks> + + <!-- This task keeps checking status of disk migration tasks (wherever auto-commit is set to true) + so that auto-commit can be performed as soon as the migration is complete --> + <!-- task:scheduled-tasks> + <task:scheduled ref="brickMigrationStatusTask" method="checkMigrationStatus" fixed-delay="60000" /> + </task:scheduled-tasks--> + + <!-- Cluster environment. Valid values: vmware, aws, baremetal --> + <bean id="discoveryMechanism" class="java.lang.String"> + <constructor-arg value="multicast" /> + </bean> + + <!-- SSH timeouts - all in milliseconds. zero means no timeout. --> + <!-- Connect the underlying TCP socket to the server with the given timeout value (SSH) --> + <bean id="sshConnectTimeout" class="java.lang.Integer"> + <constructor-arg value="10000" /> + </bean> + <!-- Timeout for complete connection establishment (SSH) --> + <bean id="sshKexTimeout" class="java.lang.Integer"> + <constructor-arg value="60000" /> + </bean> + <!-- Command execution timeout (SSH) --> + <bean id="sshExecTimeout" class="java.lang.Integer"> + <constructor-arg value="120000" /> + </bean> + + <!-- Gluster Management Gateway Version --> + <bean id="appVersion" class="java.lang.String"> + <constructor-arg value="1.0.0" /> + </bean> + + <!-- Derby embedded data source --> + <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="false"> + <property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver" /> + <property name="url" value="jdbc:derby:/opt/glustermg/data;create=true" /> + <property name="username" value="gluster" /> + <property name="password" value="gluster" /> + </bean> + + <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> + <property name="dataSource" ref="dataSource" /> + <property name="jpaVendorAdapter"> + <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> + <property name="showSql" value="true" /> + <property name="generateDdl" value="false" /> + <property name="databasePlatform" value="org.hibernate.dialect.DerbyDialect" /> + </bean> + </property> + </bean> + + <bean + class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> + + <bean id="clusterDao" class="com.gluster.storage.management.gateway.data.PersistenceDao"> + <constructor-arg type="java.lang.Class"> + <value>com.gluster.storage.management.gateway.data.ClusterInfo</value> + </constructor-arg> + </bean> + + <bean id="serverDao" class="com.gluster.storage.management.gateway.data.PersistenceDao"> + <constructor-arg type="java.lang.Class"> + <value>com.gluster.storage.management.gateway.data.ServerInfo</value> + </constructor-arg> + </bean> + + <!-- bean id="dataSourceFactory" class="com.gluster.storage.management.gateway.data.GlusterDataSource" /> + <bean id="dataSource" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> + <property name="targetObject"> + <ref local="dataSourceFactory" /> + </property> + <property name="targetMethod"> + <value>getDataSource</value> + </property> + </bean --> + + <!-- bean id="dataSource" class="com.gluster.storage.management.gateway.data.GlusterDataSource" + lazy-init="false" autowire="byType" / --> + + <bean class="com.gluster.storage.management.gateway.tasks.InitServerTask" init-method="initServer" depends-on="dataSource"> + <property name="dataSource" ref="dataSource" /> + </bean> +</beans>
\ No newline at end of file diff --git a/src/com.gluster.storage.management.gateway/src/spring/gluster-server-security.xml b/src/com.gluster.storage.management.gateway/src/spring/gluster-server-security.xml new file mode 100644 index 00000000..abcd8c05 --- /dev/null +++ b/src/com.gluster.storage.management.gateway/src/spring/gluster-server-security.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/security" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" + xmlns:jdbc="http://www.springframework.org/schema/jdbc" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd + http://www.springframework.org/schema/security + http://www.springframework.org/schema/security/spring-security-3.0.xsd + "> + + <http auto-config="true" use-expressions="true"> + <!-- SSL Protection --> + <intercept-url pattern="/1.0/**" access="hasRole('ROLE_ADMIN') and fullyAuthenticated" requires-channel="https" /> + <intercept-url pattern="*.jnlp" access="hasRole('ROLE_ADMIN') and fullyAuthenticated" requires-channel="any" /> + <!-- intercept-url pattern="/*" access="permitAll" requires-channel="any" / --> + <port-mappings> + <port-mapping http="8080" https="8443" /> + </port-mappings> + + <!-- HTTP basic authentication --> + <http-basic /> + </http> + + <beans:bean + class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" + id="passwordEncoder" /> + + <authentication-manager alias="authenticationManager"> + <authentication-provider user-service-ref="jdbcUserService"> + <!-- Passwords are SHA encrypted --> + <password-encoder hash="sha" /> + </authentication-provider> + </authentication-manager> + + <beans:bean id="jdbcUserService" + class="org.springframework.security.provisioning.JdbcUserDetailsManager" + lazy-init="false"> + <beans:property name="dataSource" ref="dataSource" /> + <beans:property name="authenticationManager" ref="authenticationManager" /> + </beans:bean> +</beans:beans>
\ No newline at end of file |
