ソースを参照

AMBARI-15865. Add 'ambari-logsearch-solr-client' module for ambari (oleewere)

oleewere 9 年 前
コミット
aa6a2a9cee
45 ファイル変更2037 行追加180 行削除
  1. 6 2
      ambari-common/src/main/python/resource_management/libraries/functions/package_conditions.py
  2. 66 0
      ambari-common/src/main/python/resource_management/libraries/functions/solr_cloud_util.py
  3. 53 55
      ambari-logsearch/ambari-logsearch-assembly/pom.xml
  4. 1 1
      ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/solr-client/control
  5. 0 0
      ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/solr-client/postinst
  6. 0 0
      ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/solr-client/postrm
  7. 0 0
      ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/solr-client/preinst
  8. 0 0
      ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/solr-client/prerm
  9. 56 0
      ambari-logsearch/ambari-logsearch-solr-client/build.xml
  10. 117 0
      ambari-logsearch/ambari-logsearch-solr-client/pom.xml
  11. 331 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/AmbariSolrCloudCLI.java
  12. 221 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/AmbariSolrCloudClient.java
  13. 114 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/AmbariSolrCloudClientBuilder.java
  14. 28 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/AmbariSolrCloudClientException.java
  15. 58 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/AbstractRetryCommand.java
  16. 53 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/AbstractSolrRetryCommand.java
  17. 44 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/AbstractZookeeperRetryCommand.java
  18. 34 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/CheckConfigZkCommand.java
  19. 60 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/CreateCollectionCommand.java
  20. 51 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/CreateShardCommand.java
  21. 40 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/DownloadConfigZkCommand.java
  22. 39 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/GetShardsCommand.java
  23. 49 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/ListCollectionCommand.java
  24. 40 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/UploadConfigZkCommand.java
  25. 71 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/util/ShardUtils.java
  26. 40 0
      ambari-logsearch/ambari-logsearch-solr-client/src/main/resources/log4j.properties
  27. 6 1
      ambari-logsearch/ambari-logsearch-solr-client/src/main/resources/solrCloudCli.sh
  28. 134 0
      ambari-logsearch/ambari-logsearch-solr-client/src/test/java/org/apache/ambari/logsearch/solr/AmbariSolrCloudClientTest.java
  29. 1 0
      ambari-logsearch/pom.xml
  30. 13 0
      ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/configuration/logfeeder-env.xml
  31. 10 0
      ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/configuration/logsearch-env.xml
  32. 19 0
      ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/configuration/logsearch-solr-env.xml
  33. 41 1
      ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/metainfo.xml
  34. 4 4
      ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/scripts/logsearch_solr.py
  35. 51 0
      ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/scripts/logsearch_solr_client.py
  36. 3 0
      ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/scripts/params.py
  37. 14 15
      ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/scripts/setup_logsearch.py
  38. 103 0
      ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/scripts/setup_logsearch_solr.py
  39. 0 70
      ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/scripts/setup_solr.py
  40. 38 0
      ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/templates/solr-client-log4j.properties.j2
  41. 1 1
      ambari-server/src/main/resources/stacks/HDP/2.3/services/LOGSEARCH/role_command_order.json
  42. 7 18
      ambari-server/src/test/python/stacks/2.4/LOGSEARCH/test_logsearch.py
  43. 6 6
      ambari-server/src/test/python/stacks/2.4/LOGSEARCH/test_solr.py
  44. 1 0
      ambari-server/src/test/python/stacks/2.4/configs/default.json
  45. 13 6
      ambari-web/app/data/HDP2/site_properties.js

+ 6 - 2
ambari-common/src/main/python/resource_management/libraries/functions/package_conditions.py

@@ -48,7 +48,11 @@ def should_install_ams_grafana():
 
 def should_install_logsearch_solr():
   config = Script.get_config()
-  return 'role' in config and config['role'] != "LOGSEARCH_LOGFEEDER"
+  return 'role' in config and config['role'] == "LOGSEARCH_SOLR"
+
+def should_install_logsearch_solr_client():
+  config = Script.get_config()
+  return 'role' in config and config['role'] == "LOGSEARCH_SOLR_CLIENT"
 
 def should_install_logsearch_portal():
   config = Script.get_config()
@@ -82,4 +86,4 @@ def should_install_ranger_tagsync():
   ranger_tagsync_enabled = default('/configurations/ranger-tagsync-site/ranger.tagsync.enabled', False)
   has_ranger_tagsync = len(ranger_tagsync_hosts) > 0
 
-  return has_ranger_tagsync or ranger_tagsync_enabled
+  return has_ranger_tagsync or ranger_tagsync_enabled

+ 66 - 0
ambari-common/src/main/python/resource_management/libraries/functions/solr_cloud_util.py

@@ -0,0 +1,66 @@
+"""
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+"""
+from resource_management.libraries.functions.format import format
+from resource_management.core.resources.system import Execute
+
+__all__ = ["upload_configuration_to_zk", "create_collection"]
+
+def __create_solr_cloud_cli_prefix(zookeeper_quorum, solr_znode, java64_home):
+  solr_cli_prefix = format('export JAVA_HOME={java64_home} ; /usr/lib/ambari-logsearch-solr-client/solrCloudCli.sh ' \
+                           '-z {zookeeper_quorum}{solr_znode}')
+  return solr_cli_prefix
+
+def upload_configuration_to_zk(zookeeper_quorum, solr_znode, config_set, config_set_dir, tmp_config_set_dir,
+                         java64_home, user, group, retry = 5, interval = 10):
+  """
+  Upload configuration set to zookeeper with solrCloudCli.sh
+  At first, it tries to download configuration set if exists into a temporary location, then upload that one to
+  zookeeper. (if the configuration changed there, in that case the user wont redefine it)
+  If the configuration set does not exits in zookeeper then upload it based on the config_set_dir parameter.
+  """
+  solr_cli_prefix = __create_solr_cloud_cli_prefix(zookeeper_quorum, solr_znode, java64_home)
+  Execute(format('{solr_cli_prefix} --download-config -d {tmp_config_set_dir} -cs {config_set} -rt {retry} -i {interval}'),
+          only_if=format("{solr_cli_prefix} --check-config -cs {config_set} -rt {retry} -i {interval}"),
+          user=user,
+          group=group
+          )
+
+  Execute(format(
+    '{solr_cli_prefix} --upload-config -d {config_set_dir} -cs {config_set} -rt {retry} -i {interval}'),
+    not_if=format("test -d {tmp_config_set_dir}"),
+    user=user,
+    group=group
+  )
+
+def create_collection(zookeeper_quorum, solr_znode, collection, config_set, java64_home, user, group,
+                      shards = 1, replication_factor = 1, max_shards = 1, retry = 5, interval = 10):
+  """
+  Create Solr collection based on a configuration set in zookeeper.
+  If this method called again the with higher shard number (or max_shard number), then it will indicate
+  the cli tool to add new shards to the Solr collection. This can be useful after added a new Solr Cloud
+  instance to the cluster.
+  """
+  solr_cli_prefix = __create_solr_cloud_cli_prefix(zookeeper_quorum, solr_znode, java64_home)
+
+  if max_shards == 1: # if max shards is not specified use this strategy
+    max_shards = replication_factor * shards
+
+  Execute(format('{solr_cli_prefix} --create-collection -c {collection} -cs {config_set} -s {shards} -r {replication_factor} '\
+    '-m {max_shards} -rt {retry} -i {interval}'), user=user, group=group
+  )

+ 53 - 55
ambari-logsearch/ambari-logsearch-assembly/pom.xml

@@ -31,15 +31,15 @@
     <solr.tar>http://apache.mirrors.lucidnetworks.net/lucene/solr/${solr.version}/solr-${solr.version}.tgz</solr.tar>
     <solr.mapping.path>${mapping.base.path}/ambari-logsearch-solr</solr.mapping.path>
     <solr.package.name>ambari-logsearch-solr</solr.package.name>
+    <solr.client.package.name>ambari-logsearch-solr-client</solr.client.package.name>
+    <solr.client.mapping.path>${mapping.base.path}/${solr.client.package.name}</solr.client.mapping.path>
+    <solr.client.dir>${project.basedir}/../ambari-logsearch-solr-client</solr.client.dir>
     <logsearch.portal.package.name>ambari-logsearch-portal</logsearch.portal.package.name>
     <logsearch.portal.mapping.path>${mapping.base.path}/ambari-logsearch-portal</logsearch.portal.mapping.path>
     <logsearch.portal.dir>${project.basedir}/../ambari-logsearch-portal</logsearch.portal.dir>
     <logsearch.logfeeder.package.name>ambari-logsearch-logfeeder</logsearch.logfeeder.package.name>
     <logsearch.logfeeder.mapping.path>${mapping.base.path}/ambari-logsearch-logfeeder</logsearch.logfeeder.mapping.path>
     <logsearch.logfeeder.dir>${project.basedir}/../ambari-logsearch-logfeeder</logsearch.logfeeder.dir>
-    <logsearch.appender.package.name>ambari-logsearch-appender</logsearch.appender.package.name>
-    <logsearch.appender.mapping.path>${mapping.base.path}/ambari-logsearch-appender</logsearch.appender.mapping.path>
-    <logsearch.appender.dir>${project.basedir}/../ambari-logsearch-appender</logsearch.appender.dir>
     <logsearch.portal.conf.mapping.path>/etc/${logsearch.portal.package.name}/conf</logsearch.portal.conf.mapping.path>
     <logsearch.logfeeder.conf.mapping.path>/etc/${logsearch.logfeeder.package.name}/conf
     </logsearch.logfeeder.conf.mapping.path>
@@ -99,6 +99,27 @@
                   </mappings>
                 </configuration>
               </execution>
+              <execution>
+                <id>logsearch-solr-client</id>
+                <phase>package</phase>
+                <goals>
+                  <goal>rpm</goal>
+                </goals>
+                <configuration>
+                  <group>Development</group>
+                  <name>${solr.client.package.name}</name>
+                  <mappings>
+                    <mapping>
+                      <directory>${solr.client.mapping.path}</directory>
+                      <sources>
+                        <source>
+                          <location>${solr.client.dir}/target/package</location>
+                        </source>
+                      </sources>
+                    </mapping>
+                  </mappings>
+                </configuration>
+              </execution>
               <execution>
                 <id>logsearch-portal</id>
                 <phase>package</phase>
@@ -186,27 +207,6 @@
                   </mappings>
                 </configuration>
               </execution>
-              <execution>
-                <id>logsearch-appender</id>
-                <phase>package</phase>
-                <goals>
-                  <goal>rpm</goal>
-                </goals>
-                <configuration>
-                  <group>Development</group>
-                  <name>${logsearch.appender.package.name}</name>
-                  <mappings>
-                    <mapping>
-                      <directory>${logsearch.appender.mapping.path}</directory>
-                      <sources>
-                        <source>
-                          <location>${logsearch.appender.dir}/target/package</location>
-                        </source>
-                      </sources>
-                    </mapping>
-                  </mappings>
-                </configuration>
-              </execution>
             </executions>
           </plugin>
           <plugin>
@@ -273,9 +273,9 @@
                       <directory>${project.basedir}/src/main/package/deb</directory>
                       <excludes>
                         <exclude>solr/postinst</exclude>
+                        <exclude>solr-client/postinst</exclude>
                         <exclude>portal/postinst</exclude>
                         <exclude>logfeeder/postinst</exclude>
-                        <exclude>appender/postinst</exclude>
                       </excludes>
                       <filtering>false</filtering>
                     </resource>
@@ -283,9 +283,9 @@
                       <directory>${project.basedir}/src/main/package/deb</directory>
                       <includes>
                         <include>solr/postinst</include>
+                        <include>solr-client/postinst</include>
                         <include>portal/postinst</include>
                         <include>logfeeder/postinst</include>
-                        <include>appender/postinst</include>
                       </includes>
                       <filtering>true</filtering>
                     </resource>
@@ -325,6 +325,32 @@
                 </configuration>
               </execution>
 
+              <execution>
+                <phase>package</phase>
+                <id>jdeb-solr-client</id>
+                <goals>
+                  <goal>jdeb</goal>
+                </goals>
+                <configuration>
+                  <controlDir>${basedir}/src/main/package/deb/solr-client</controlDir>
+                  <deb>${basedir}/target/${solr.client.package.name}_${package-version}-${package-release}.deb</deb>
+                  <skip>false</skip>
+                  <skipPOMs>false</skipPOMs>
+                  <dataSet>
+                    <data>
+                      <src>${solr.client.dir}/target/ambari-logsearch-solr-client.tar.gz</src>
+                      <type>archive</type>
+                      <mapper>
+                        <type>perm</type>
+                        <user>root</user>
+                        <group>root</group>
+                        <prefix>${solr.client.mapping.path}</prefix>
+                      </mapper>
+                    </data>
+                  </dataSet>
+                </configuration>
+              </execution>
+
               <execution>
                 <id>jdeb-portal</id>
                 <phase>package</phase>
@@ -422,34 +448,6 @@
                   </dataSet>
                 </configuration>
               </execution>
-
-              <execution>
-                <id>jdeb-appender</id>
-                <phase>package</phase>
-                <goals>
-                  <goal>jdeb</goal>
-                </goals>
-                <configuration>
-                  <controlDir>${basedir}/src/main/package/deb/appender</controlDir>
-                  <deb>${basedir}/target/${logsearch.appender.package.name}_${package-version}-${package-release}.deb
-                  </deb>
-                  <skip>false</skip>
-                  <skipPOMs>false</skipPOMs>
-                  <dataSet>
-                    <data>
-                      <src>${logsearch.appender.dir}/target/ambari-logsearch-appender.tar.gz</src>
-                      <type>archive</type>
-                      <mapper>
-                        <prefix>${logsearch.appender.mapping.path}</prefix>
-                        <type>perm</type>
-                        <user>root</user>
-                        <group>root</group>
-                      </mapper>
-                    </data>
-                  </dataSet>
-                </configuration>
-              </execution>
-
             </executions>
           </plugin>
           <plugin>
@@ -502,7 +500,7 @@
     </dependency>
     <dependency>
       <groupId>org.apache.ambari</groupId>
-      <artifactId>ambari-logsearch-appender</artifactId>
+      <artifactId>ambari-logsearch-solr-client</artifactId>
       <version>${project.version}</version>
     </dependency>
   </dependencies>

+ 1 - 1
ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/appender/control → ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/solr-client/control

@@ -12,7 +12,7 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License
-Package: [[logsearch.logfeeder.package.name]]
+Package: [[solr.client.package.name]]
 Version: [[package-version]]-[[package-release]]
 Section: [[deb.section]]
 Priority: [[deb.priority]]

+ 0 - 0
ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/appender/postinst → ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/solr-client/postinst


+ 0 - 0
ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/appender/postrm → ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/solr-client/postrm


+ 0 - 0
ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/appender/preinst → ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/solr-client/preinst


+ 0 - 0
ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/appender/prerm → ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/solr-client/prerm


+ 56 - 0
ambari-logsearch/ambari-logsearch-solr-client/build.xml

@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<project basedir="." default="build" name="logsearch">
+  <property environment="env"/>
+  <property name="debuglevel" value="source,lines,vars"/>
+  <dirname property="builddir" file="build.xml"/>
+  <property name="target" value="1.7"/>
+  <property name="source" value="1.7"/>
+  <target name="init">
+  </target>
+  <target name="build"/>
+
+  <target name="package">
+    <delete dir="target/package"/>
+    <copy todir="target/package/libs" includeEmptyDirs="no">
+      <fileset dir="target/libs"/>
+    </copy>
+    <copy todir="target/package/libs" includeEmptyDirs="no">
+      <fileset file="target/*.jar"/>
+    </copy>
+    <copy todir="target/package" includeEmptyDirs="no">
+      <fileset file="src/main/resources/solrCloudCli.sh"/>
+    </copy>
+    <copy todir="target/package" includeEmptyDirs="no">
+      <fileset file="src/main/resources/log4j.properties"/>
+    </copy>
+    <chmod file="target/package/*.sh" perm="755"/>
+    <tar compression="gzip" destfile="target/ambari-logsearch-solr-client.tar.gz">
+      <tarfileset mode="755" dir="target/package">
+        <include name="*.sh"/>
+      </tarfileset>
+      <tarfileset mode="664" dir="target/package">
+        <exclude name="*.sh"/>
+      </tarfileset>
+    </tar>
+
+  </target>
+
+  <target description="Build all projects which reference this project. Useful to propagate changes."
+          name="build-refprojects"/>
+</project>

+ 117 - 0
ambari-logsearch/ambari-logsearch-solr-client/pom.xml

@@ -0,0 +1,117 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>ambari-logsearch</artifactId>
+    <groupId>org.apache.ambari</groupId>
+    <version>2.0.0.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <url>http://maven.apache.org</url>
+  <name>Ambari Logsearch Solr Client</name>
+
+  <artifactId>ambari-logsearch-solr-client</artifactId>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.solr</groupId>
+      <artifactId>solr-solrj</artifactId>
+      <version>${solr.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-cli</groupId>
+      <artifactId>commons-cli</artifactId>
+      <version>1.3.1</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.easymock</groupId>
+      <artifactId>easymock</artifactId>
+      <version>3.4</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <version>2.8</version>
+        <executions>
+          <execution>
+            <id>copy-dependencies</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-dependencies</goal>
+            </goals>
+            <configuration>
+              <outputAbsoluteArtifactFilename>true</outputAbsoluteArtifactFilename>
+              <outputDirectory>${basedir}/target/libs</outputDirectory>
+              <overWriteReleases>false</overWriteReleases>
+              <overWriteSnapshots>false</overWriteSnapshots>
+              <overWriteIfNewer>true</overWriteIfNewer>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <version>1.7</version>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <configuration>
+              <target>
+                <ant antfile="build.xml">
+                  <target name="package"/>
+                </ant>
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>

+ 331 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/AmbariSolrCloudCLI.java

@@ -0,0 +1,331 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ambari.logsearch.solr;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class AmbariSolrCloudCLI {
+
+  private static final Logger LOG = LoggerFactory.getLogger(AmbariSolrCloudCLI.class);
+
+  private static final int ZK_CLIENT_TIMEOUT = 10000;
+  private static final int ZK_CLIENT_CONNECT_TIMEOUT = 10000;
+  private static final String CREATE_COLLECTION_COMMAND = "create-collection";
+  private static final String UPLOAD_CONFIG_COMMAND = "upload-config";
+  private static final String DOWNLOAD_CONFIG_COMMAND = "download-config";
+  private static final String CONFIG_CHECK_COMMAND = "check-config";
+  private static final String CREATE_SHARD_COMMAND = "create-shard";
+  private static final String CMD_LINE_SYNTAX =
+    "\n./solrCloudCli.sh --create-collection -z host1:2181,host2:2181/ambari-solr -c collection -cs conf_set"
+      + "\n./solrCloudCli.sh --upload-config -z host1:2181,host2:2181/ambari-solr -d /tmp/myonfig_dir -cs config_set"
+      + "\n./solrCloudCli.sh --download-config -z host1:2181,host2:2181/ambari-solr -cs config_set -d /tmp/myonfig_dir"
+      + "\n./solrCloudCli.sh --check-config -z host1:2181,host2:2181/ambari-solr -cs config_set"
+      + "\n./solrCloudCli.sh --create-shard -z host1:2181,host2:2181/ambari-solr -c collection -sn myshard\n";
+
+  public static void main(String[] args) {
+    Options options = new Options();
+    HelpFormatter helpFormatter = new HelpFormatter();
+    helpFormatter.setDescPadding(10);
+    helpFormatter.setWidth(200);
+
+    final Option helpOption = Option.builder("h")
+      .longOpt("help")
+      .desc("Print commands")
+      .build();
+
+    final Option createCollectionOption = Option.builder("cc")
+      .longOpt(CREATE_COLLECTION_COMMAND)
+      .desc("Create collection in Solr (command)")
+      .build();
+
+    final Option uploadConfigurationOption = Option.builder("uc")
+      .longOpt(UPLOAD_CONFIG_COMMAND)
+      .desc("Upload configuration set to Zookeeper (command)")
+      .build();
+
+    final Option downloadConfigOption = Option.builder("dc")
+      .longOpt(DOWNLOAD_CONFIG_COMMAND)
+      .desc("Download configuration set from Zookeeper (command)")
+      .build();
+
+    final Option checkConfigOption = Option.builder("chc")
+      .longOpt(CONFIG_CHECK_COMMAND)
+      .desc("Check configuration exists in Zookeeper (command)")
+      .build();
+
+    final Option createShardOption = Option.builder("csh")
+      .longOpt(CREATE_SHARD_COMMAND)
+      .desc("Create shard in Solr (command)")
+      .build();
+
+    final Option shardNameOption = Option.builder("sn")
+      .longOpt("shard-name")
+      .desc("Name of the shard for create-shard command")
+      .numberOfArgs(1)
+      .argName("my_new_shard")
+      .build();
+
+    final Option disableShardingOption = Option.builder("ns")
+      .longOpt("no-sharding")
+      .desc("Sharding not used when creating collection")
+      .build();
+
+    final Option zookeeperHostOption = Option.builder("z")
+      .longOpt("zookeeper-host")
+      .desc("Zookeeper quorum and Znode")
+      .numberOfArgs(1)
+      .argName("host:port,host:port../ambari-solr")
+      .build();
+
+    final Option collectionOption = Option.builder("c")
+      .longOpt("collection")
+      .desc("Collection name")
+      .numberOfArgs(1)
+      .argName("collection name")
+      .build();
+
+    final Option configSetOption = Option.builder("cs")
+      .longOpt("config-set")
+      .desc("Configuration set")
+      .numberOfArgs(1)
+      .argName("config_set")
+      .build();
+
+    final Option configDirOption = Option.builder("d")
+      .longOpt("config-dir")
+      .desc("Configuration directory")
+      .numberOfArgs(1)
+      .argName("config_dir")
+      .build();
+
+    final Option shardsOption = Option.builder("s")
+      .longOpt("shards")
+      .desc("Number of shards")
+      .numberOfArgs(1)
+      .argName("shard number")
+      .type(Integer.class)
+      .build();
+
+    final Option replicationOption = Option.builder("r")
+      .longOpt("replication")
+      .desc("Replication factor")
+      .numberOfArgs(1)
+      .argName("replication factor")
+      .type(Integer.class)
+      .build();
+
+    final Option retryOption = Option.builder("rt")
+      .longOpt("retry")
+      .desc("Number of retries for access Solr [default:10]")
+      .numberOfArgs(1)
+      .argName("number of retries")
+      .type(Integer.class)
+      .build();
+
+    final Option intervalOption = Option.builder("i")
+      .longOpt("interval")
+      .desc("Interval for retry logic in sec [default:5]")
+      .numberOfArgs(1)
+      .argName("interval")
+      .type(Integer.class)
+      .build();
+
+    final Option maxShardsOption = Option.builder("m")
+      .longOpt("max-shards")
+      .desc("Max number of shards per node (default: replication * shards)")
+      .numberOfArgs(1)
+      .argName("max number of shards")
+      .build();
+
+    final Option routerNameOption = Option.builder("rn")
+      .longOpt("router-name")
+      .desc("Router name for collection [default:implicit]")
+      .numberOfArgs(1)
+      .argName("router_name")
+      .build();
+
+    final Option routerFieldOption = Option.builder("rf")
+      .longOpt("router-field")
+      .desc("Router field for collection [default:_router_field_]")
+      .numberOfArgs(1)
+      .argName("router_field")
+      .build();
+
+    options.addOption(helpOption);
+    options.addOption(retryOption);
+    options.addOption(intervalOption);
+    options.addOption(zookeeperHostOption);
+    options.addOption(configSetOption);
+    options.addOption(configDirOption);
+    options.addOption(collectionOption);
+    options.addOption(shardsOption);
+    options.addOption(replicationOption);
+    options.addOption(maxShardsOption);
+    options.addOption(routerNameOption);
+    options.addOption(routerFieldOption);
+    options.addOption(shardNameOption);
+    options.addOption(disableShardingOption);
+    options.addOption(createCollectionOption);
+    options.addOption(downloadConfigOption);
+    options.addOption(uploadConfigurationOption);
+    options.addOption(checkConfigOption);
+    options.addOption(createShardOption);
+
+
+    try {
+      CommandLineParser cmdLineParser = new DefaultParser();
+      CommandLine cli = cmdLineParser.parse(options, args);
+
+      if(cli.hasOption('h')) {
+        helpFormatter.printHelp("sample", options);
+        exit(0, null);
+      }
+      String command = "";
+      if (cli.hasOption("cc")) {
+        command = CREATE_COLLECTION_COMMAND;
+        validateRequiredOptions(cli, command, zookeeperHostOption, collectionOption, configSetOption);
+      } else if (cli.hasOption("uc")) {
+        command = UPLOAD_CONFIG_COMMAND;
+        validateRequiredOptions(cli, command, zookeeperHostOption, configSetOption, configDirOption);
+      } else if (cli.hasOption("dc")) {
+        command = DOWNLOAD_CONFIG_COMMAND;
+        validateRequiredOptions(cli, command, zookeeperHostOption, configSetOption, configDirOption);
+      } else if (cli.hasOption("csh")) {
+        command = CREATE_SHARD_COMMAND;
+        validateRequiredOptions(cli, command, zookeeperHostOption, collectionOption, shardNameOption);
+      } else if (cli.hasOption("chc")) {
+        command = CONFIG_CHECK_COMMAND;
+        validateRequiredOptions(cli, command, zookeeperHostOption, configSetOption);
+      } else {
+        List<String> commands = Arrays.asList(CREATE_COLLECTION_COMMAND, CREATE_SHARD_COMMAND, UPLOAD_CONFIG_COMMAND,
+          DOWNLOAD_CONFIG_COMMAND, CONFIG_CHECK_COMMAND);
+        helpFormatter.printHelp(CMD_LINE_SYNTAX, options);
+        exit(1, String.format("One of the supported commands is required (%s)", StringUtils.join(commands, "|")));
+      }
+
+      String zookeeperHosts = cli.getOptionValue('z');
+      String collection = cli.getOptionValue('c');
+      String configSet = cli.getOptionValue("cs");
+      String configDir = cli.getOptionValue("d");
+      int shards = cli.hasOption('s') ? Integer.parseInt(cli.getOptionValue('s')) : 1;
+      int replication = cli.hasOption('r') ? Integer.parseInt(cli.getOptionValue('r')) : 1;
+      int retry = cli.hasOption("rt") ? Integer.parseInt(cli.getOptionValue("rt")) : 5;
+      int interval = cli.hasOption('i') ? Integer.parseInt(cli.getOptionValue('i')) : 10;
+      int maxShards = cli.hasOption('m') ? Integer.parseInt(cli.getOptionValue('m')) : shards * replication;
+      String routerName = cli.hasOption("rn") ? cli.getOptionValue("rn") : "implicit";
+      String routerField = cli.hasOption("rn") ? cli.getOptionValue("rn") : "_router_field_";
+      String shardName = cli.hasOption("sn") ? cli.getOptionValue("sn") : null;
+      boolean isSplitting = !cli.hasOption("ns");
+
+
+      AmbariSolrCloudClientBuilder clientBuilder = new AmbariSolrCloudClientBuilder()
+        .withZookeeperHosts(zookeeperHosts)
+        .withCollection(collection)
+        .withConfigSet(configSet)
+        .withShards(shards)
+        .withReplication(replication)
+        .withMaxShardsPerNode(maxShards)
+        .withRetry(retry)
+        .withInterval(interval)
+        .withRouterName(routerName)
+        .withRouterField(routerField)
+        .withSplitting(isSplitting)
+        .withSolrZkClient(ZK_CLIENT_TIMEOUT, ZK_CLIENT_CONNECT_TIMEOUT);
+
+      AmbariSolrCloudClient solrCloudClient = null;
+      switch (command) {
+        case CREATE_COLLECTION_COMMAND:
+          solrCloudClient = clientBuilder
+            .withSolrCloudClient()
+            .build();
+          solrCloudClient.createCollection();
+          break;
+        case UPLOAD_CONFIG_COMMAND:
+          solrCloudClient = clientBuilder
+            .withConfigDir(configDir)
+            .build();
+          solrCloudClient.uploadConfiguration();
+          break;
+        case DOWNLOAD_CONFIG_COMMAND:
+          solrCloudClient = clientBuilder
+            .withConfigDir(configDir)
+            .build();
+          solrCloudClient.downloadConfiguration();
+          break;
+        case CONFIG_CHECK_COMMAND:
+          solrCloudClient = clientBuilder.build();
+          boolean configExists = solrCloudClient.configurationExists();
+          if (!configExists) {
+            exit(1, null);
+          }
+          break;
+        case CREATE_SHARD_COMMAND:
+          solrCloudClient = clientBuilder
+            .withSolrCloudClient()
+            .withSolrZkClient(ZK_CLIENT_TIMEOUT, ZK_CLIENT_CONNECT_TIMEOUT)
+            .build();
+          solrCloudClient.createShard(shardName);
+          break;
+        default:
+          throw new AmbariSolrCloudClientException(String.format("Not found command: '%s'", command));
+      }
+    } catch (Exception e) {
+      helpFormatter.printHelp(
+        CMD_LINE_SYNTAX, options);
+      exit(1, e.getMessage());
+    }
+    exit(0, null);
+  }
+
+  private static void validateRequiredOptions(CommandLine cli, String command, Option... optionsToValidate)
+    throws AmbariSolrCloudClientException {
+    List<String> requiredOptions = new ArrayList<>();
+    for (Option opt : optionsToValidate) {
+      if (!cli.hasOption(opt.getOpt())) {
+        requiredOptions.add(opt.getOpt());
+      }
+    }
+    if (!requiredOptions.isEmpty()) {
+      throw new AmbariSolrCloudClientException(
+        String.format("The following options required for '%s' : %s",
+          command, StringUtils.join(requiredOptions, ",")));
+    }
+  }
+
+  private static void exit(int exitCode, String message) {
+    if (message != null){
+      LOG.error(message);
+    }
+    LOG.info("Return code: {}", exitCode);
+    System.exit(exitCode);
+  }
+}

+ 221 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/AmbariSolrCloudClient.java

@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.solr;
+
+import org.apache.ambari.logsearch.solr.commands.CheckConfigZkCommand;
+import org.apache.ambari.logsearch.solr.commands.CreateCollectionCommand;
+import org.apache.ambari.logsearch.solr.commands.CreateShardCommand;
+import org.apache.ambari.logsearch.solr.commands.DownloadConfigZkCommand;
+import org.apache.ambari.logsearch.solr.commands.GetShardsCommand;
+import org.apache.ambari.logsearch.solr.commands.ListCollectionCommand;
+import org.apache.ambari.logsearch.solr.commands.UploadConfigZkCommand;
+import org.apache.ambari.logsearch.solr.util.ShardUtils;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.SolrZkClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Client for communicate with Solr (and Zookeeper)
+ */
+public class AmbariSolrCloudClient {
+
+  private static final Logger LOG = LoggerFactory.getLogger(AmbariSolrCloudClient.class);
+
+  private final String zookeeperHosts;
+  private final String collection;
+  private final String configSet;
+  private final String configDir;
+  private final int shards;
+  private final int replication;
+  private final int retryTimes;
+  private final int interval;
+  private final CloudSolrClient solrCloudClient;
+  private final SolrZkClient solrZkClient;
+  private final int maxShardsPerNode;
+  private final String routerName;
+  private final String routerField;
+  private final boolean splitting;
+
+  public AmbariSolrCloudClient(AmbariSolrCloudClientBuilder builder) {
+    this.zookeeperHosts = builder.zookeeperHosts;
+    this.collection = builder.collection;
+    this.configSet = builder.configSet;
+    this.configDir = builder.configDir;
+    this.shards = builder.shards;
+    this.replication = builder.replication;
+    this.retryTimes = builder.retryTimes;
+    this.interval = builder.interval;
+    this.solrCloudClient = builder.solrCloudClient;
+    this.solrZkClient = builder.solrZkClient;
+    this.maxShardsPerNode = builder.maxShardsPerNode;
+    this.routerName = builder.routerName;
+    this.routerField = builder.routerField;
+    this.splitting = builder.splitting;
+  }
+
+  /**
+   * Get Solr collections
+   */
+  public List<String> listCollections() throws Exception {
+    return new ListCollectionCommand(getRetryTimes(), getInterval()).run(this);
+  }
+
+  /**
+   * Create Solr collection if exists
+   */
+  public String createCollection() throws Exception {
+    List <String> collections = listCollections();
+    if (!collections.contains(getCollection())) {
+      String collection = new CreateCollectionCommand(getRetryTimes(), getInterval()).run(this);
+      LOG.info("Collection '{}' created.", collection);
+    } else {
+      LOG.info("Collection '{}' already exits.", getCollection());
+      if (this.isSplitting()) {
+        createShard(null);
+      }
+    }
+    return getCollection();
+  }
+
+  /**
+   * Upload config set to zookeeper
+   */
+  public String uploadConfiguration() throws Exception {
+    String configSet = new UploadConfigZkCommand(getRetryTimes(), getInterval()).run(this);
+    LOG.info("'{}' is uploaded to zookeeper.", configSet);
+    return configSet;
+  }
+
+  /**
+   * Download config set from zookeeper
+   */
+  public String downloadConfiguration() throws Exception {
+    String configDir = new DownloadConfigZkCommand(getRetryTimes(), getInterval()).run(this);
+    LOG.info("Config set is download from zookeeper. ({})", configDir);
+    return configDir;
+  }
+
+  /**
+   * Get configuration if exists in zookeeper
+   */
+  public boolean configurationExists() throws Exception {
+    boolean configExits = new CheckConfigZkCommand(getRetryTimes(), getInterval()).run(this);
+    if (configExits) {
+      LOG.info("Config {} exits", configSet);
+    } else {
+      LOG.info("Configuration '{}' does not exist", configSet);
+    }
+    return configExits;
+  }
+
+  /**
+   * Create shard in collection - create a new one if shard name specified, if not create based on
+   * the number of shards logic (with shard_# suffix)
+   * @param shard name of the created shard
+   */
+  public Collection<String> createShard(String shard) throws Exception {
+    Collection<String> existingShards = getShardNames();
+    if (shard != null) {
+      new CreateShardCommand(shard, getRetryTimes(), getInterval()).run(this);
+      existingShards.add(shard);
+    } else {
+      List<String> shardList = ShardUtils.generateShardList(getMaxShardsPerNode());
+      for (String shardName : shardList) {
+        if (!existingShards.contains(shardName)) {
+          new CreateShardCommand(shardName, getRetryTimes(), getInterval()).run(this);
+          LOG.info("New shard added to collection '{}': {}", getCollection(), shardName);
+          existingShards.add(shardName);
+        }
+      }
+    }
+    return existingShards;
+  }
+
+  /**
+   * Get shard names
+   */
+  public Collection<String> getShardNames() throws Exception {
+    Collection<Slice> slices = new GetShardsCommand(getRetryTimes(), getInterval()).run(this);
+    return ShardUtils.getShardNamesFromSlices(slices, this.getCollection());
+  }
+
+  public String getZookeeperHosts() {
+    return zookeeperHosts;
+  }
+
+  public String getCollection() {
+    return collection;
+  }
+
+  public String getConfigSet() {
+    return configSet;
+  }
+
+  public String getConfigDir() {
+    return configDir;
+  }
+
+  public int getShards() {
+    return shards;
+  }
+
+  public int getReplication() {
+    return replication;
+  }
+
+  public int getRetryTimes() {
+    return retryTimes;
+  }
+
+  public int getInterval() {
+    return interval;
+  }
+
+  public CloudSolrClient getSolrCloudClient() {
+    return solrCloudClient;
+  }
+
+  public SolrZkClient getSolrZkClient() {
+    return solrZkClient;
+  }
+
+  public int getMaxShardsPerNode() {
+    return maxShardsPerNode;
+  }
+
+  public String getRouterName() {
+    return routerName;
+  }
+
+  public String getRouterField() {
+    return routerField;
+  }
+
+  public boolean isSplitting() {
+    return splitting;
+  }
+}

+ 114 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/AmbariSolrCloudClientBuilder.java

@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ambari.logsearch.solr;
+
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.common.cloud.SolrZkClient;
+
+public class AmbariSolrCloudClientBuilder {
+  String zookeeperHosts;
+  String collection;
+  String configSet;
+  String configDir;
+  int shards = 1;
+  int replication = 1;
+  int retryTimes = 10;
+  int interval = 5;
+  int maxShardsPerNode = replication * shards;
+  String routerName = "implicit";
+  String routerField = "_router_field_";
+  CloudSolrClient solrCloudClient;
+  SolrZkClient solrZkClient;
+  boolean splitting;
+
+  public AmbariSolrCloudClient build() {
+    return new AmbariSolrCloudClient(this);
+  }
+
+  public AmbariSolrCloudClientBuilder withZookeeperHosts(String zookeeperHosts) {
+    this.zookeeperHosts = zookeeperHosts;
+    return this;
+  }
+
+  public AmbariSolrCloudClientBuilder withCollection(String collection) {
+    this.collection = collection;
+    return this;
+  }
+
+  public AmbariSolrCloudClientBuilder withConfigSet(String configSet) {
+    this.configSet = configSet;
+    return this;
+  }
+
+  public AmbariSolrCloudClientBuilder withConfigDir(String configDir) {
+    this.configDir = configDir;
+    return this;
+  }
+
+  public AmbariSolrCloudClientBuilder withShards(int shards) {
+    this.shards = shards;
+    return this;
+  }
+
+  public AmbariSolrCloudClientBuilder withReplication(int replication) {
+    this.replication = replication;
+    return this;
+  }
+
+  public AmbariSolrCloudClientBuilder withRetry(int retryTimes) {
+    this.retryTimes = retryTimes;
+    return this;
+  }
+
+  public AmbariSolrCloudClientBuilder withInterval(int interval) {
+    this.interval = interval;
+    return this;
+  }
+
+  public AmbariSolrCloudClientBuilder withMaxShardsPerNode(int maxShardsPerNode) {
+    this.maxShardsPerNode = maxShardsPerNode;
+    return this;
+  }
+
+  public AmbariSolrCloudClientBuilder withRouterName(String routerName) {
+    this.routerName = routerName;
+    return this;
+  }
+
+  public AmbariSolrCloudClientBuilder withRouterField(String routerField) {
+    this.routerField = routerField;
+    return this;
+  }
+
+  public AmbariSolrCloudClientBuilder withSplitting(boolean splitting) {
+    this.splitting = splitting;
+    return this;
+  }
+
+  public AmbariSolrCloudClientBuilder withSolrCloudClient() {
+    this.solrCloudClient = new CloudSolrClient(this.zookeeperHosts);
+    return this;
+  }
+
+  public AmbariSolrCloudClientBuilder withSolrZkClient(int zkClientTimeout, int zkClientConnectTimeout) {
+    this.solrZkClient = new SolrZkClient(this.zookeeperHosts, zkClientTimeout, zkClientConnectTimeout);
+    return this;
+  }
+}

+ 28 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/AmbariSolrCloudClientException.java

@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.solr;
+
+public class AmbariSolrCloudClientException extends Exception{
+  public AmbariSolrCloudClientException(String message) {
+    super(message);
+  }
+  public AmbariSolrCloudClientException(String message, Throwable throwable) {
+    super(message, throwable);
+  }
+}

+ 58 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/AbstractRetryCommand.java

@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.solr.commands;
+
+import org.apache.ambari.logsearch.solr.AmbariSolrCloudClient;
+import org.apache.ambari.logsearch.solr.AmbariSolrCloudClientException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractRetryCommand<RESPONSE> {
+  private static final Logger LOG = LoggerFactory.getLogger(AbstractRetryCommand.class);
+
+  private final int interval;
+  private final int maxRetries;
+
+  public AbstractRetryCommand(int maxRetries, int interval) {
+    this.maxRetries = maxRetries;
+    this.interval = interval;
+  }
+
+  public abstract RESPONSE createAndProcessRequest(AmbariSolrCloudClient solrCloudClient) throws Exception;
+
+  public RESPONSE run(AmbariSolrCloudClient solrCloudClient) throws Exception {
+    return retry(0, solrCloudClient);
+  }
+
+  private RESPONSE retry(int tries, AmbariSolrCloudClient solrCloudClient) throws Exception {
+    try {
+      return createAndProcessRequest(solrCloudClient);
+    } catch (Exception ex) {
+      LOG.error(ex.getMessage(), ex);
+      tries++;
+      LOG.info("Command failed, tries again (tries: {})", tries);
+      if (maxRetries == tries) {
+        throw new AmbariSolrCloudClientException(String.format("Maximum retries exceeded: %d", tries), ex);
+      } else {
+        Thread.sleep(interval * 1000);
+        return retry(tries, solrCloudClient);
+      }
+    }
+  }
+}

+ 53 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/AbstractSolrRetryCommand.java

@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.solr.commands;
+
+import org.apache.ambari.logsearch.solr.AmbariSolrCloudClient;
+import org.apache.ambari.logsearch.solr.AmbariSolrCloudClientException;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.client.solrj.response.SolrResponseBase;
+
+public abstract class AbstractSolrRetryCommand<REQUEST extends CollectionAdminRequest, RESPONSE>
+  extends AbstractRetryCommand<RESPONSE> {
+
+  public AbstractSolrRetryCommand(int maxRetries, int interval) {
+    super(maxRetries, interval);
+  }
+
+  public abstract RESPONSE handleResponse(CollectionAdminResponse response, AmbariSolrCloudClient client) throws Exception;
+
+  public abstract REQUEST createRequest(AmbariSolrCloudClient client);
+
+  public abstract String errorMessage(AmbariSolrCloudClient client);
+
+  @Override
+  public RESPONSE createAndProcessRequest(AmbariSolrCloudClient client) throws Exception {
+    REQUEST request = createRequest(client);
+    CollectionAdminResponse response = (CollectionAdminResponse) request.process(client.getSolrCloudClient());
+    handleErrorIfExists(response, errorMessage(client));
+    return handleResponse(response, client);
+  }
+
+  private void handleErrorIfExists(SolrResponseBase response, String message) throws AmbariSolrCloudClientException {
+    if (response.getStatus() != 0) {
+      throw new AmbariSolrCloudClientException(message);
+    }
+  }
+}

+ 44 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/AbstractZookeeperRetryCommand.java

@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.solr.commands;
+
+import org.apache.ambari.logsearch.solr.AmbariSolrCloudClient;
+import org.apache.solr.common.cloud.SolrZkClient;
+import org.apache.solr.common.cloud.ZkConfigManager;
+
+public abstract class AbstractZookeeperRetryCommand<RESPONSE> extends AbstractRetryCommand<RESPONSE> {
+
+  public AbstractZookeeperRetryCommand(int maxRetries, int interval) {
+    super(maxRetries, interval);
+  }
+
+  protected abstract RESPONSE executeZkCommand(ZkConfigManager zkConfigManager, AmbariSolrCloudClient client)
+    throws Exception;
+
+  @Override
+  public RESPONSE createAndProcessRequest(AmbariSolrCloudClient client) throws Exception {
+    SolrZkClient zkClient = client.getSolrZkClient();
+    ZkConfigManager zkConfigManager = createZkConfigManager(zkClient);
+    return executeZkCommand(zkConfigManager, client);
+  }
+
+  protected ZkConfigManager createZkConfigManager(SolrZkClient zkClient) {
+    return new ZkConfigManager(zkClient);
+  }
+}

+ 34 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/CheckConfigZkCommand.java

@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.solr.commands;
+
+import org.apache.ambari.logsearch.solr.AmbariSolrCloudClient;
+import org.apache.solr.common.cloud.ZkConfigManager;
+
+public class CheckConfigZkCommand extends AbstractZookeeperRetryCommand<Boolean> {
+
+  public CheckConfigZkCommand(int maxRetries, int interval) {
+    super(maxRetries, interval);
+  }
+
+  @Override
+  protected Boolean executeZkCommand(ZkConfigManager zkConfigManager, AmbariSolrCloudClient client) throws Exception {
+    return zkConfigManager.configExists(client.getConfigSet());
+  }
+}

+ 60 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/CreateCollectionCommand.java

@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.solr.commands;
+
+import org.apache.ambari.logsearch.solr.AmbariSolrCloudClient;
+import org.apache.ambari.logsearch.solr.util.ShardUtils;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CreateCollectionCommand extends AbstractSolrRetryCommand<CollectionAdminRequest.Create ,String> {
+
+  public CreateCollectionCommand(int maxRetries, int interval) {
+    super(maxRetries, interval);
+  }
+
+  @Override
+  public String handleResponse(CollectionAdminResponse response, AmbariSolrCloudClient client) throws Exception {
+    return client.getCollection();
+  }
+
+  @Override
+  public CollectionAdminRequest.Create createRequest(AmbariSolrCloudClient client) {
+    CollectionAdminRequest.Create request = new CollectionAdminRequest.Create();
+    request.setConfigName(client.getConfigSet());
+    request.setCollectionName(client.getCollection());
+    request.setNumShards(client.getShards());
+    request.setReplicationFactor(client.getReplication());
+    request.setMaxShardsPerNode(client.getMaxShardsPerNode());
+    if (client.isSplitting()) {
+      request.setShards(ShardUtils.generateShardListStr(client.getMaxShardsPerNode()));
+      request.setRouterName(client.getRouterName());
+      request.setRouterField(client.getRouterField());
+    }
+    return request;
+  }
+
+  @Override
+  public String errorMessage(AmbariSolrCloudClient client) {
+    return String.format("Cannot create collection: '%s'", client.getCollection());
+  }
+}

+ 51 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/CreateShardCommand.java

@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.solr.commands;
+
+import org.apache.ambari.logsearch.solr.AmbariSolrCloudClient;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+
+public class CreateShardCommand extends AbstractSolrRetryCommand<CollectionAdminRequest.CreateShard, String> {
+
+  private final String shardName;
+
+  public CreateShardCommand(String shardName, int maxRetries, int interval) {
+    super(maxRetries, interval);
+    this.shardName = shardName;
+  }
+
+  @Override
+  public String handleResponse(CollectionAdminResponse response, AmbariSolrCloudClient client) throws Exception {
+    return shardName;
+  }
+
+  @Override
+  public CollectionAdminRequest.CreateShard createRequest(AmbariSolrCloudClient client) {
+    CollectionAdminRequest.CreateShard createShardRequest = new CollectionAdminRequest.CreateShard();
+    createShardRequest.setCollectionName(client.getCollection());
+    createShardRequest.setShardName(shardName);
+    return createShardRequest;
+  }
+
+  @Override
+  public String errorMessage(AmbariSolrCloudClient client) {
+    return String.format("Cannot add shard to collection '%s'", client.getCollection());
+  }
+}

+ 40 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/DownloadConfigZkCommand.java

@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.solr.commands;
+
+import org.apache.ambari.logsearch.solr.AmbariSolrCloudClient;
+import org.apache.solr.common.cloud.ZkConfigManager;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class DownloadConfigZkCommand extends AbstractZookeeperRetryCommand<String> {
+
+  public DownloadConfigZkCommand(int maxRetries, int interval) {
+    super(maxRetries, interval);
+  }
+
+  @Override
+  protected String executeZkCommand(ZkConfigManager zkConfigManager, AmbariSolrCloudClient client) throws Exception {
+    Path configDir = Paths.get(client.getConfigDir());
+    String configSet = client.getConfigSet();
+    zkConfigManager.downloadConfigDir(configSet, configDir);
+    return configDir.toString();
+  }
+}

+ 39 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/GetShardsCommand.java

@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.solr.commands;
+
+import org.apache.ambari.logsearch.solr.AmbariSolrCloudClient;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkStateReader;
+
+import java.util.Collection;
+
+public class GetShardsCommand extends AbstractRetryCommand<Collection<Slice>> {
+
+  public GetShardsCommand(int maxRetries, int interval) {
+    super(maxRetries, interval);
+  }
+
+  @Override
+  public Collection<Slice> createAndProcessRequest(AmbariSolrCloudClient solrCloudClient) throws Exception {
+    ZkStateReader zkReader = new ZkStateReader(solrCloudClient.getSolrZkClient());
+    zkReader.createClusterStateWatchersAndUpdate();
+    return zkReader.getClusterState().getSlices(solrCloudClient.getCollection());
+  }
+}

+ 49 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/ListCollectionCommand.java

@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.solr.commands;
+
+import org.apache.ambari.logsearch.solr.AmbariSolrCloudClient;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+
+import java.util.List;
+
+public class ListCollectionCommand extends AbstractSolrRetryCommand<CollectionAdminRequest.List, List<String>> {
+
+  public ListCollectionCommand(int maxRetries, int interval) {
+    super(maxRetries, interval);
+  }
+
+  @Override
+  public List<String> handleResponse(CollectionAdminResponse response, AmbariSolrCloudClient client) throws Exception {
+    List<String> allCollectionList = (List<String>) response
+      .getResponse().get("collections");
+    return allCollectionList;
+  }
+
+  @Override
+  public CollectionAdminRequest.List createRequest(AmbariSolrCloudClient client) {
+    return new CollectionAdminRequest.List();
+  }
+
+  @Override
+  public String errorMessage(AmbariSolrCloudClient client) {
+    return "Cannot get collections.";
+  }
+}

+ 40 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/commands/UploadConfigZkCommand.java

@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.solr.commands;
+
+import org.apache.ambari.logsearch.solr.AmbariSolrCloudClient;
+import org.apache.solr.common.cloud.ZkConfigManager;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class UploadConfigZkCommand extends AbstractZookeeperRetryCommand<String> {
+
+  public UploadConfigZkCommand(int maxRetries, int interval) {
+    super(maxRetries, interval);
+  }
+
+  @Override
+  protected String executeZkCommand(ZkConfigManager zkConfigManager, AmbariSolrCloudClient client) throws Exception {
+    Path configDir = Paths.get(client.getConfigDir());
+    String configSet = client.getConfigSet();
+    zkConfigManager.uploadConfigDir(configDir, configSet);
+    return configSet;
+  }
+}

+ 71 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/java/org/apache/ambari/logsearch/solr/util/ShardUtils.java

@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.solr.util;
+
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.Slice;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+public class ShardUtils {
+
+  private static final Logger LOG = LoggerFactory.getLogger(ShardUtils.class);
+
+  public static String generateShardListStr(int maxShardsPerNode) {
+    String shardsListStr = "";
+    for (int i = 0; i < maxShardsPerNode; i++) {
+      if (i != 0) {
+        shardsListStr += ",";
+      }
+      String shard = "shard" + i;
+      shardsListStr += shard;
+    }
+    return shardsListStr;
+  }
+
+  public static List<String> generateShardList(int maxShardsPerNode) {
+    List<String> shardsList = new ArrayList<>();
+    for (int i = 0; i < maxShardsPerNode; i++) {
+      shardsList.add("shard" + i);
+    }
+    return shardsList;
+  }
+
+  public static Collection<String> getShardNamesFromSlices(Collection<Slice> slices, String collection) {
+    Collection<String> result = new HashSet<String>();
+    Iterator<Slice> iter = slices.iterator();
+    while (iter.hasNext()) {
+      Slice slice = iter.next();
+      for (Replica replica : slice.getReplicas()) {
+        LOG.info("collectionName=" + collection + ", slice.name="
+          + slice.getName() + ", slice.state=" + slice.getState()
+          + ", replica.core=" + replica.getStr("core")
+          + ", replica.state=" + replica.getStr("state"));
+        result.add(slice.getName());
+      }
+    }
+    return result;
+  }
+}

+ 40 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/main/resources/log4j.properties

@@ -0,0 +1,40 @@
+# Copyright 2011 The Apache Software Foundation
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+solr.client.log.dir=/var/log/ambari-logsearch-solr-client
+
+log4j.rootLogger=INFO,file,stdout,stderr
+
+log4j.appender.file=org.apache.log4j.RollingFileAppender
+log4j.appender.file.File=${solr.client.log.dir}/solr-client.log
+log4j.appender.file.MaxFileSize=80MB
+log4j.appender.file.MaxBackupIndex=60
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{DATE} %5p [%t] %c{1}:%L - %m%n
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Threshold=INFO
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%m%n
+
+log4j.appender.stderr=org.apache.log4j.ConsoleAppender
+log4j.appender.stderr.Threshold=ERROR
+log4j.appender.stderr.Target=System.err
+log4j.appender.stderr.layout=org.apache.log4j.PatternLayout
+log4j.appender.stderr.layout.ConversionPattern=%m%n

+ 6 - 1
ambari-logsearch/ambari-logsearch-assembly/src/main/package/deb/appender/posttrm → ambari-logsearch/ambari-logsearch-solr-client/src/main/resources/solrCloudCli.sh

@@ -12,4 +12,9 @@
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
-# limitations under the License
+# limitations under the License.
+
+JVM="java"
+sdir="`dirname \"$0\"`"
+
+PATH=$JAVA_HOME/bin:$PATH $JVM -classpath "$sdir:$sdir/libs/*" org.apache.ambari.logsearch.solr.AmbariSolrCloudCLI ${1+"$@"}

+ 134 - 0
ambari-logsearch/ambari-logsearch-solr-client/src/test/java/org/apache/ambari/logsearch/solr/AmbariSolrCloudClientTest.java

@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.solr;
+
+import static org.easymock.EasyMock.anyString;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.common.cloud.SolrZkClient;
+import org.apache.solr.common.util.NamedList;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class AmbariSolrCloudClientTest {
+
+  private AmbariSolrCloudClient underTest;
+
+  private CloudSolrClient mockedSolrClient;
+
+  private SolrZkClient mockedSolrZkClient;
+
+  private CollectionAdminResponse mockedResponse;
+
+  @Before
+  public void setUp() {
+    AmbariSolrCloudClientBuilder builder = new AmbariSolrCloudClientBuilder();
+
+    mockedSolrClient = createMock(CloudSolrClient.class);
+    mockedSolrZkClient = createMock(SolrZkClient.class);
+    mockedResponse = createMock(CollectionAdminResponse.class);
+
+    builder.solrCloudClient = mockedSolrClient;
+    builder.solrZkClient = mockedSolrZkClient;
+
+    underTest = builder
+      .withZookeeperHosts("localhost1:2181,localhost2:2182")
+      .withCollection("collection1")
+      .withConfigSet("configSet")
+      .withShards(1)
+      .withReplication(1)
+      .withMaxShardsPerNode(2)
+      .withInterval(1)
+      .withRetry(2)
+      .withRouterName("routerName")
+      .withRouterField("routerField")
+      .build();
+  }
+
+  @Test
+  public void testCreateCollectionWhenCollectionDoesNotExist() throws Exception {
+    // GIVEN
+    NamedList<Object> namedList = new NamedList<>();
+    namedList.add("collections", Arrays.asList("collection1", "collection2"));
+
+    expect(mockedSolrClient.request(anyObject(CollectionAdminRequest.class), anyString())).andReturn(namedList).times(1);
+    replay(mockedSolrClient);
+
+    // WHEN
+    String result = underTest.createCollection();
+    // THEN
+    assertEquals("collection1", result);
+    verify(mockedSolrClient);
+  }
+
+  @Test
+  public void testCreateCollectionWhenCollectionExists() throws Exception {
+    // GIVEN
+    NamedList<Object> namedList = new NamedList<>();
+    namedList.add("collections", Arrays.asList("collection2", "collection3"));
+
+    expect(mockedSolrClient.request(anyObject(CollectionAdminRequest.class), anyString())).andReturn(namedList).times(2);
+    replay(mockedSolrClient);
+
+    // WHEN
+    String result = underTest.createCollection();
+    // THEN
+    assertEquals("collection1", result);
+    verify(mockedSolrClient);
+  }
+
+  @Test
+  public void testListCollections() throws Exception {
+    // GIVEN
+    NamedList<Object> namedList = new NamedList<>();
+    namedList.add("collections", Arrays.asList("collection1", "collection2"));
+
+    expect(mockedSolrClient.request(anyObject(CollectionAdminRequest.class), anyString())).andReturn(namedList);
+
+    replay(mockedSolrClient);
+    // WHEN
+    List<String> result = underTest.listCollections();
+
+    // THEN
+    assertTrue(result.contains("collection1"));
+    assertTrue(result.contains("collection2"));
+    assertEquals(2, result.size());
+  }
+
+  @Test(expected = AmbariSolrCloudClientException.class)
+  public void testRetries() throws Exception {
+    // GIVEN
+    expect(mockedSolrClient.request(anyObject(CollectionAdminRequest.class), anyString())).andThrow(new RuntimeException("ex")).times(2);
+    replay(mockedSolrClient);
+    // WHEN
+    underTest.listCollections();
+  }
+}

+ 1 - 0
ambari-logsearch/pom.xml

@@ -33,6 +33,7 @@
     <module>ambari-logsearch-appender</module>
     <module>ambari-logsearch-portal</module>
     <module>ambari-logsearch-logfeeder</module>
+    <module>ambari-logsearch-solr-client</module>
   </modules>
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

+ 13 - 0
ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/configuration/logfeeder-env.xml

@@ -29,6 +29,9 @@
     <value>/var/run/ambari-logsearch-logfeeder</value>
     <description>logfeeder Process ID Directory</description>
     <display-name>Logfeeder pid dir</display-name>
+    <value-attributes>
+      <type>directory</type>
+    </value-attributes>
   </property>
 
   <property>
@@ -36,6 +39,9 @@
     <value>/var/log/ambari-logsearch-logfeeder</value>
     <description>Log dir for logfeeder</description>
     <display-name>Logfeeder log dir</display-name>
+    <value-attributes>
+      <type>directory</type>
+    </value-attributes>
   </property>
 
   <property>
@@ -43,6 +49,9 @@
     <value>/etc/ambari-logsearch-logfeeder/conf/checkpoints</value>
     <description>Checkpoint folder for logfeeder</description>
     <display-name>Logfeeder checkpoint dir</display-name>
+    <value-attributes>
+      <type>directory</type>
+    </value-attributes>
   </property>
 
   <property>
@@ -58,6 +67,10 @@
     <property-type>USER</property-type>
     <description>logfeeder user</description>
     <display-name>Logsfeeder User</display-name>
+    <value-attributes>
+      <type>user</type>
+      <overridable>false</overridable>
+    </value-attributes>
   </property>
 
   <property>

+ 10 - 0
ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/configuration/logsearch-env.xml

@@ -27,6 +27,9 @@
     <value>/var/run/ambari-logsearch-portal</value>
     <description>Logsearch Process ID Directory</description>
     <display-name>Logsearch pid dir</display-name>
+    <value-attributes>
+      <type>directory</type>
+    </value-attributes>
   </property>
 
   <property>
@@ -34,6 +37,9 @@
     <value>/var/log/ambari-logsearch-portal</value>
     <description>Log directory for Logsearch</description>
     <display-name>Logsearch log dir</display-name>
+    <value-attributes>
+      <type>directory</type>
+    </value-attributes>
   </property>
 
   <property>
@@ -42,6 +48,10 @@
     <property-type>USER</property-type>
     <description>Logsearch user</description>
     <display-name>Logsearch User</display-name>
+    <value-attributes>
+      <type>user</type>
+      <overridable>false</overridable>
+    </value-attributes>
   </property>
 
   <property>

+ 19 - 0
ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/configuration/logsearch-solr-env.xml

@@ -34,6 +34,9 @@
     <value>/var/run/ambari-logsearch-solr</value>
     <description>Solr Process ID Directory</description>
     <display-name>Logsearch Solr pid dir</display-name>
+    <value-attributes>
+      <type>directory</type>
+    </value-attributes>
   </property>
 
   <property>
@@ -41,6 +44,19 @@
     <value>/var/log/ambari-logsearch-solr</value>
     <description>Directory for Solr logs</description>
     <display-name>Logsearch Solr log dir</display-name>
+    <value-attributes>
+      <type>directory</type>
+    </value-attributes>
+  </property>
+
+  <property>
+    <name>logsearch_solr_client_log_dir</name>
+    <value>/var/log/ambari-logsearch-solr-client</value>
+    <description>Directory for Solr client logs</description>
+    <display-name>Logsearch Solr Client log dir</display-name>
+    <value-attributes>
+      <type>directory</type>
+    </value-attributes>
   </property>
 
   <property>
@@ -68,6 +84,9 @@
     <value>/opt/logsearch_solr/data</value>
     <display-name>Logsearch Solr data dir</display-name>
     <description>Directory for storting Solr index. Make sure you have enough disk space</description>
+    <value-attributes>
+      <type>directory</type>
+    </value-attributes>
   </property>
 
   <property>

+ 41 - 1
ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/metainfo.xml

@@ -46,6 +46,15 @@
               <logId>logsearch_perf</logId>
             </log>
           </logs>
+          <dependencies>
+            <dependency>
+              <name>LOGSEARCH/LOGSEARCH_SOLR_CLIENT</name>
+              <scope>host</scope>
+              <auto-deploy>
+                <enabled>true</enabled>
+              </auto-deploy>
+            </dependency>
+          </dependencies>
         </component>
 
         <component>
@@ -55,9 +64,18 @@
           <cardinality>1+</cardinality>
           <versionAdvertised>false</versionAdvertised>
           <commandScript>
-            <script>scripts/solr.py</script>
+            <script>scripts/logsearch_solr.py</script>
             <scriptType>PYTHON</scriptType>
           </commandScript>
+          <dependencies>
+            <dependency>
+              <name>LOGSEARCH/LOGSEARCH_SOLR_CLIENT</name>
+              <scope>host</scope>
+              <auto-deploy>
+                <enabled>true</enabled>
+              </auto-deploy>
+            </dependency>
+          </dependencies>
         </component>
 
         <component>
@@ -79,12 +97,29 @@
           </logs>
         </component>
 
+        <component>
+          <name>LOGSEARCH_SOLR_CLIENT</name>
+          <displayName>Log Search Solr Client</displayName>
+          <category>CLIENT</category>
+          <cardinality>0+</cardinality>
+          <versionAdvertised>false</versionAdvertised>
+          <commandScript>
+            <script>scripts/logsearch_solr_client.py</script>
+            <scriptType>PYTHON</scriptType>
+          </commandScript>
+        </component>
+
       </components>
 
       <osSpecifics>
         <osSpecific>
           <osFamily>redhat7,amazon2015,redhat6,suse11,suse12</osFamily>
           <packages>
+            <package>
+              <name>ambari-logsearch-solr-client</name>
+              <skipUpgrade>true</skipUpgrade>
+              <condition>should_install_logsearch_solr_client</condition>
+            </package>
             <package>
               <name>ambari-logsearch-logfeeder</name>
               <skipUpgrade>true</skipUpgrade>
@@ -104,6 +139,11 @@
         <osSpecific>
           <osFamily>debian7,ubuntu12,ubuntu14</osFamily>
           <packages>
+            <package>
+              <name>ambari-logsearch-solr-client</name>
+              <skipUpgrade>true</skipUpgrade>
+              <condition>should_install_logsearch_solr_client</condition>
+            </package>
             <package>
               <name>ambari-logsearch-logfeeder</name>
               <skipUpgrade>true</skipUpgrade>

+ 4 - 4
ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/scripts/solr.py → ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/scripts/logsearch_solr.py

@@ -21,10 +21,10 @@ from resource_management.core.resources.system import Execute, File
 from resource_management.libraries.functions.format import format
 from resource_management.libraries.functions.check_process_status import check_process_status
 from resource_management.libraries.script.script import Script
-from setup_solr import setup_solr
+from setup_logsearch_solr import setup_logsearch_solr
 
 
-class Solr(Script):
+class LogsearchSolr(Script):
   def install(self, env):
     import params
     env.set_params(params)
@@ -34,7 +34,7 @@ class Solr(Script):
     import params
     env.set_params(params)
 
-    setup_solr()
+    setup_logsearch_solr(name = 'server')
 
   def start(self, env, upgrade_type=None):
     import params
@@ -68,4 +68,4 @@ class Solr(Script):
 
 
 if __name__ == "__main__":
-  Solr().execute()
+  LogsearchSolr().execute()

+ 51 - 0
ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/scripts/logsearch_solr_client.py

@@ -0,0 +1,51 @@
+"""
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+"""
+
+from resource_management.core.exceptions import ClientComponentHasNoStatus
+from resource_management.libraries.script.script import Script
+from setup_logsearch_solr import setup_logsearch_solr
+
+class LogsearchSolrClient(Script):
+
+  def install(self, env):
+    import params
+    env.set_params(params)
+    self.install_packages(env)
+    self.configure(env)
+
+  def configure(self, env, upgrade_type=None):
+    import params
+    env.set_params(params)
+    setup_logsearch_solr(name = 'client')
+
+  def start(self, env, upgrade_type=None):
+    import params
+    env.set_params(params)
+    self.configure(env)
+
+  def stop(self, env, upgrade_type=None):
+    import params
+    env.set_params(params)
+
+  def status(self, env):
+    raise ClientComponentHasNoStatus()
+
+
+if __name__ == "__main__":
+  LogsearchSolrClient().execute()

+ 3 - 0
ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/scripts/params.py

@@ -83,6 +83,7 @@ else:
 # Only supporting SolrCloud mode - so hardcode those options
 solr_cloudmode = 'true'
 solr_dir = '/usr/lib/ambari-logsearch-solr'
+solr_client_dir = '/usr/lib/ambari-logsearch-solr-client'
 solr_bindir = solr_dir + '/bin'
 cloud_scripts = solr_dir + '/server/scripts/cloud-scripts'
 
@@ -123,6 +124,8 @@ else:
 logsearch_solr_user = config['configurations']['logsearch-solr-env']['logsearch_solr_user']
 logsearch_solr_group = config['configurations']['logsearch-solr-env']['logsearch_solr_group']
 logsearch_solr_log_dir = config['configurations']['logsearch-solr-env']['logsearch_solr_log_dir']
+logsearch_solr_client_log_dir = config['configurations']['logsearch-solr-env']['logsearch_solr_client_log_dir']
+logsearch_solr_client_log = format("{logsearch_solr_client_log_dir}/solr-client.log")
 logsearch_solr_log = format("{logsearch_solr_log_dir}/solr-install.log")
 
 solr_env_content = config['configurations']['logsearch-solr-env']['content']

+ 14 - 15
ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/scripts/setup_logsearch.py

@@ -18,6 +18,7 @@ limitations under the License.
 """
 
 import random
+from resource_management.libraries.functions import solr_cloud_util
 from resource_management.core.resources.system import Directory, Execute, File
 from resource_management.libraries.functions.format import format
 from resource_management.core.source import InlineTemplate, Template
@@ -75,29 +76,27 @@ def setup_logsearch():
 
   random_num = random.random()
 
-  upload_configuration_dir_to_zk('hadoop_logs', random_num)
+  upload_conf_set('hadoop_logs', random_num)
 
-  upload_configuration_dir_to_zk('history', random_num)
+  upload_conf_set('history', random_num)
 
-  upload_configuration_dir_to_zk('audit_logs', random_num)
+  upload_conf_set('audit_logs', random_num)
 
   Execute(("chmod", "-R", "ugo+r", format("{logsearch_server_conf}/solr_configsets")),
           sudo=True
           )
 
 
-def upload_configuration_dir_to_zk(config_set, random_num):
+def upload_conf_set(config_set, random_num):
   import params
-
   tmp_config_set_folder = format('{tmp_dir}/solr_config_{config_set}_{random_num}')
-  zk_cli_prefix = format(
-    'export JAVA_HOME={java64_home} ; {cloud_scripts}/zkcli.sh -zkhost {zookeeper_quorum}{logsearch_solr_znode}')
-
-  Execute(format('{zk_cli_prefix} -cmd downconfig -confdir {tmp_config_set_folder} -confname {config_set}'),
-          only_if=format("{zk_cli_prefix} -cmd get /configs/{config_set}"),
-          )
 
-  Execute(format(
-    '{zk_cli_prefix} -cmd upconfig -confdir {logsearch_server_conf}/solr_configsets/{config_set}/conf -confname {config_set}'),
-    not_if=format("test -d {tmp_config_set_folder}"),
-  )
+  solr_cloud_util.upload_configuration_to_zk(
+    zookeeper_quorum=params.zookeeper_hosts,
+    solr_znode=params.logsearch_solr_znode,
+    config_set_dir=format("{logsearch_server_conf}/solr_configsets/{config_set}/conf"),
+    config_set=config_set,
+    tmp_config_set_dir=tmp_config_set_folder,
+    java64_home=params.java64_home,
+    user=params.logsearch_solr_user,
+    group=params.logsearch_solr_group)

+ 103 - 0
ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/scripts/setup_logsearch_solr.py

@@ -0,0 +1,103 @@
+"""
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+"""
+
+from resource_management.core.exceptions import Fail
+from resource_management.core.resources.system import Directory, Execute, File
+from resource_management.core.source import StaticFile
+from resource_management.libraries.functions.format import format
+from resource_management.core.source import InlineTemplate, Template
+
+
+def setup_logsearch_solr(name = None):
+  import params
+
+  if name == 'server':
+    Directory([params.solr_dir, params.logsearch_solr_log_dir, params.logsearch_solr_piddir, params.logsearch_solr_conf,
+               params.logsearch_solr_datadir, params.logsearch_solr_data_resources_dir],
+              mode=0755,
+              cd_access='a',
+              owner=params.logsearch_solr_user,
+              group=params.logsearch_solr_group,
+              create_parents=True
+              )
+
+    File(params.logsearch_solr_log,
+         mode=0644,
+         owner=params.logsearch_solr_user,
+         group=params.logsearch_solr_group,
+         content=''
+         )
+
+    File(format("{logsearch_solr_conf}/logsearch-solr-env.sh"),
+         content=InlineTemplate(params.solr_env_content),
+         mode=0755,
+         owner=params.logsearch_solr_user
+         )
+
+    File(format("{logsearch_solr_datadir}/solr.xml"),
+         content=InlineTemplate(params.solr_xml_content),
+         owner=params.logsearch_solr_user
+         )
+
+    File(format("{logsearch_solr_conf}/log4j.properties"),
+         content=InlineTemplate(params.solr_log4j_content),
+         owner=params.logsearch_solr_user
+         )
+
+    File(format("{logsearch_solr_datadir}/zoo.cfg"),
+         content=Template("zoo.cfg.j2"),
+         owner=params.logsearch_solr_user
+         )
+
+    zk_cli_prefix = format('export JAVA_HOME={java64_home}; {cloud_scripts}/zkcli.sh -zkhost {zookeeper_hosts}')
+    Execute(format('{zk_cli_prefix} -cmd makepath {logsearch_solr_znode}'),
+            not_if=format("{zk_cli_prefix} -cmd get {logsearch_solr_znode}"),
+            ignore_failures=True,
+            user=params.logsearch_solr_user
+            )
+  elif name == 'client':
+    Directory([params.solr_client_dir, params.logsearch_solr_client_log_dir],
+              mode=0755,
+              cd_access='a',
+              owner=params.logsearch_solr_user,
+              group=params.logsearch_solr_group,
+              create_parents=True
+              )
+    solrCliFilename = format("{solr_client_dir}/solrCloudCli.sh")
+    File(solrCliFilename,
+         mode=0755,
+         group=params.logsearch_solr_group,
+         owner=params.logsearch_solr_user,
+         content=StaticFile(solrCliFilename)
+         )
+
+    File(format("{solr_client_dir}/log4j.properties"),
+         content=Template("solr-client-log4j.properties.j2"),
+         owner=params.logsearch_solr_user
+         )
+
+    File(params.logsearch_solr_client_log,
+         mode=0644,
+         owner=params.logsearch_solr_user,
+         group=params.logsearch_solr_group,
+         content=''
+         )
+
+  else :
+    raise Fail('Nor client or server were selected to install.')

+ 0 - 70
ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/scripts/setup_solr.py

@@ -1,70 +0,0 @@
-"""
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-"""
-
-from resource_management.core.resources.system import Directory, Execute, File
-from resource_management.libraries.functions.format import format
-from resource_management.core.source import InlineTemplate, Template
-
-
-def setup_solr():
-  import params
-
-  Directory([params.solr_dir, params.logsearch_solr_log_dir, params.logsearch_solr_piddir, params.logsearch_solr_conf,
-             params.logsearch_solr_datadir, params.logsearch_solr_data_resources_dir],
-            mode=0755,
-            cd_access='a',
-            owner=params.logsearch_solr_user,
-            group=params.logsearch_solr_group,
-            create_parents=True
-            )
-
-  File(params.logsearch_solr_log,
-       mode=0644,
-       owner=params.logsearch_solr_user,
-       group=params.logsearch_solr_group,
-       content=''
-       )
-
-  File(format("{logsearch_solr_conf}/logsearch-solr-env.sh"),
-       content=InlineTemplate(params.solr_env_content),
-       mode=0755,
-       owner=params.logsearch_solr_user
-       )
-
-  File(format("{logsearch_solr_datadir}/solr.xml"),
-       content=InlineTemplate(params.solr_xml_content),
-       owner=params.logsearch_solr_user
-       )
-
-  File(format("{logsearch_solr_conf}/log4j.properties"),
-       content=InlineTemplate(params.solr_log4j_content),
-       owner=params.logsearch_solr_user
-       )
-
-  File(format("{logsearch_solr_datadir}/zoo.cfg"),
-       content=Template("zoo.cfg.j2"),
-       owner=params.logsearch_solr_user
-       )
-
-  zk_cli_prefix = format('export JAVA_HOME={java64_home}; {cloud_scripts}/zkcli.sh -zkhost {zookeeper_hosts}')
-  Execute(format('{zk_cli_prefix} -cmd makepath {logsearch_solr_znode}'),
-          not_if=format("{zk_cli_prefix} -cmd get {logsearch_solr_znode}"),
-          ignore_failures=True,
-          user=params.logsearch_solr_user
-          )

+ 38 - 0
ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/package/templates/solr-client-log4j.properties.j2

@@ -0,0 +1,38 @@
+# Copyright 2011 The Apache Software Foundation
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+log4j.rootLogger=INFO,file,stdout,stderr
+
+log4j.appender.file=org.apache.log4j.RollingFileAppender
+log4j.appender.file.File={{logsearch_solr_client_log}}
+log4j.appender.file.MaxFileSize=80MB
+log4j.appender.file.MaxBackupIndex=60
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{DATE} %5p [%t] %c{1}:%L - %m%n
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Threshold=INFO
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%m%n
+
+log4j.appender.stderr=org.apache.log4j.ConsoleAppender
+log4j.appender.stderr.Threshold=ERROR
+log4j.appender.stderr.Target=System.err
+log4j.appender.stderr.layout=org.apache.log4j.PatternLayout
+log4j.appender.stderr.layout.ConversionPattern=%m%n

+ 1 - 1
ambari-server/src/main/resources/stacks/HDP/2.3/services/LOGSEARCH/role_command_order.json

@@ -1,6 +1,6 @@
 {
   "general_deps" : {
-    "_comment" : "dependencies for all logsearch",
+    "_comment" : "dependencies for logsearch",
     "LOGSEARCH_SOLR-START" : ["ZOOKEEPER_SERVER-START"],
     "LOGSEARCH_SERVER-START": ["LOGSEARCH_SOLR-START"],
     "LOGSEARCH_LOGFEEDER-START": ["LOGSEARCH_SOLR-START", "LOGSEARCH_SERVER-START"]

+ 7 - 18
ambari-server/src/test/python/stacks/2.4/LOGSEARCH/test_logsearch.py

@@ -97,26 +97,15 @@ class TestLogSearch(RMFTestCase):
                               content = InlineTemplate(self.getConfig()['configurations']['logsearch-audit_logs-solrconfig']['content'])
                               )
     
-    self.assertResourceCalledRegexp('^Execute$', '^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr/server/scripts/cloud-scripts/zkcli.sh -zkhost c6401.ambari.apache.org:None/logsearch -cmd downconfig -confdir /tmp/solr_config_hadoop_logs_0.[0-9]* -confname hadoop_logs$',
-                                    only_if = "^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr/server/scripts/cloud-scripts/zkcli.sh -zkhost c6401.ambari.apache.org:None/logsearch -cmd get /configs/hadoop_logs$"
-    )
-    self.assertResourceCalledRegexp('^Execute$', '^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr/server/scripts/cloud-scripts/zkcli.sh -zkhost c6401.ambari.apache.org:None/logsearch -cmd upconfig -confdir /etc/ambari-logsearch-portal/conf/solr_configsets/hadoop_logs/conf -confname hadoop_logs$',
-                                    not_if = "^test -d /tmp/solr_config_hadoop_logs_0.[0-9]*$"
-    )
+    self.assertResourceCalledRegexp('^Execute$', '^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr-client/solrCloudCli.sh -z c6401.ambari.apache.org/logsearch --download-config -d /tmp/solr_config_hadoop_logs_0.[0-9]* -cs hadoop_logs -rt 5 -i 10')
+    self.assertResourceCalledRegexp('^Execute$', '^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr-client/solrCloudCli.sh -z c6401.ambari.apache.org/logsearch --upload-config -d /etc/ambari-logsearch-portal/conf/solr_configsets/hadoop_logs/conf -cs hadoop_logs -rt 5 -i 10')
 
-    self.assertResourceCalledRegexp('^Execute$', '^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr/server/scripts/cloud-scripts/zkcli.sh -zkhost c6401.ambari.apache.org:None/logsearch -cmd downconfig -confdir /tmp/solr_config_history_0.[0-9]* -confname history$',
-                                    only_if = "^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr/server/scripts/cloud-scripts/zkcli.sh -zkhost c6401.ambari.apache.org:None/logsearch -cmd get /configs/history$"
-    )
-    self.assertResourceCalledRegexp('^Execute$', '^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr/server/scripts/cloud-scripts/zkcli.sh -zkhost c6401.ambari.apache.org:None/logsearch -cmd upconfig -confdir /etc/ambari-logsearch-portal/conf/solr_configsets/history/conf -confname history$',
-                                  not_if = "^test -d /tmp/solr_config_history_0.[0-9]*$"
-    )
+    self.assertResourceCalledRegexp('^Execute$', '^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr-client/solrCloudCli.sh -z c6401.ambari.apache.org/logsearch --download-config -d /tmp/solr_config_history_0.[0-9]* -cs history -rt 5 -i 10')
+    self.assertResourceCalledRegexp('^Execute$', '^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr-client/solrCloudCli.sh -z c6401.ambari.apache.org/logsearch --upload-config -d /etc/ambari-logsearch-portal/conf/solr_configsets/history/conf -cs history -rt 5 -i 10')
+
+    self.assertResourceCalledRegexp('^Execute$', '^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr-client/solrCloudCli.sh -z c6401.ambari.apache.org/logsearch --download-config -d /tmp/solr_config_audit_logs_0.[0-9]* -cs audit_logs -rt 5 -i 10')
+    self.assertResourceCalledRegexp('^Execute$', '^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr-client/solrCloudCli.sh -z c6401.ambari.apache.org/logsearch --upload-config -d /etc/ambari-logsearch-portal/conf/solr_configsets/audit_logs/conf -cs audit_logs -rt 5 -i 10')
 
-    self.assertResourceCalledRegexp('^Execute$', '^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr/server/scripts/cloud-scripts/zkcli.sh -zkhost c6401.ambari.apache.org:None/logsearch -cmd downconfig -confdir /tmp/solr_config_audit_logs_0.[0-9]* -confname audit_logs$',
-                                    only_if = "^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr/server/scripts/cloud-scripts/zkcli.sh -zkhost c6401.ambari.apache.org:None/logsearch -cmd get /configs/audit_logs$"
-    )
-    self.assertResourceCalledRegexp('^Execute$', '^export JAVA_HOME=/usr/jdk64/jdk1.7.0_45 ; /usr/lib/ambari-logsearch-solr/server/scripts/cloud-scripts/zkcli.sh -zkhost c6401.ambari.apache.org:None/logsearch -cmd upconfig -confdir /etc/ambari-logsearch-portal/conf/solr_configsets/audit_logs/conf -confname audit_logs$',
-                                    not_if = "^test -d /tmp/solr_config_audit_logs_0.[0-9]*$"
-    )
     self.assertResourceCalled('Execute', ('chmod', '-R', 'ugo+r', '/etc/ambari-logsearch-portal/conf/solr_configsets'),
                               sudo=True
     )

+ 6 - 6
ambari-server/src/test/python/stacks/2.4/LOGSEARCH/test_solr.py

@@ -100,8 +100,8 @@ class TestSolr(RMFTestCase):
       )
   
   def test_configure_default(self):
-    self.executeScript(self.COMMON_SERVICES_PACKAGE_DIR + "/scripts/solr.py",
-                       classname = "Solr",
+    self.executeScript(self.COMMON_SERVICES_PACKAGE_DIR + "/scripts/logsearch_solr.py",
+                       classname = "LogsearchSolr",
                        command = "configure",
                        config_file = "default.json",
                        stack_version = self.STACK_VERSION,
@@ -112,8 +112,8 @@ class TestSolr(RMFTestCase):
     self.assertNoMoreResources()
   
   def test_start_default(self):
-    self.executeScript(self.COMMON_SERVICES_PACKAGE_DIR + "/scripts/solr.py",
-                       classname = "Solr",
+    self.executeScript(self.COMMON_SERVICES_PACKAGE_DIR + "/scripts/logsearch_solr.py",
+                       classname = "LogsearchSolr",
                        command = "start",
                        config_file = "default.json",
                        stack_version = self.STACK_VERSION,
@@ -127,8 +127,8 @@ class TestSolr(RMFTestCase):
     )
   
   def test_stop_default(self):
-    self.executeScript(self.COMMON_SERVICES_PACKAGE_DIR + "/scripts/solr.py",
-                       classname = "Solr",
+    self.executeScript(self.COMMON_SERVICES_PACKAGE_DIR + "/scripts/logsearch_solr.py",
+                       classname = "LogsearchSolr",
                        command = "stop",
                        config_file = "default.json",
                        stack_version = self.STACK_VERSION,

ファイルの差分が大きいため隠しています
+ 1 - 0
ambari-server/src/test/python/stacks/2.4/configs/default.json


+ 13 - 6
ambari-web/app/data/HDP2/site_properties.js

@@ -2194,47 +2194,54 @@ var hdp2properties = [
     "index": 2
   },
   {
-    "name": "logsearch_solr_pid_dir",
+    "name": "logsearch_solr_client_log_dir",
     "serviceName": "LOGSEARCH",
     "filename": "logsearch-solr-env.xml",
     "category": "Advanced logsearch-solr-env",
     "index": 3
   },
   {
-    "name": "logsearch_solr_znode",
+    "name": "logsearch_solr_pid_dir",
     "serviceName": "LOGSEARCH",
     "filename": "logsearch-solr-env.xml",
     "category": "Advanced logsearch-solr-env",
     "index": 4
   },
   {
-    "name": "logsearch_solr_port",
+    "name": "logsearch_solr_znode",
     "serviceName": "LOGSEARCH",
     "filename": "logsearch-solr-env.xml",
     "category": "Advanced logsearch-solr-env",
     "index": 5
   },
   {
-    "name": "logsearch_solr_minmem",
+    "name": "logsearch_solr_port",
     "serviceName": "LOGSEARCH",
     "filename": "logsearch-solr-env.xml",
     "category": "Advanced logsearch-solr-env",
     "index": 6
   },
   {
-    "name": "logsearch_solr_maxmem",
+    "name": "logsearch_solr_minmem",
     "serviceName": "LOGSEARCH",
     "filename": "logsearch-solr-env.xml",
     "category": "Advanced logsearch-solr-env",
     "index": 7
   },
   {
-    "name": "content",
+    "name": "logsearch_solr_maxmem",
     "serviceName": "LOGSEARCH",
     "filename": "logsearch-solr-env.xml",
     "category": "Advanced logsearch-solr-env",
     "index": 8
   },
+  {
+    "name": "content",
+    "serviceName": "LOGSEARCH",
+    "filename": "logsearch-solr-env.xml",
+    "category": "Advanced logsearch-solr-env",
+    "index": 9
+  },
   /*logsearch-solr-log4j*/
   {
     "name": "content",

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません