You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by su...@apache.org on 2014/07/02 19:36:52 UTC
[4/4] git commit: AMBARI-6176. Integrate python shell into
Ambari-shell module(subin)
AMBARI-6176. Integrate python shell into Ambari-shell module(subin)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/c8eceafc
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/c8eceafc
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/c8eceafc
Branch: refs/heads/trunk
Commit: c8eceafc9efc6fa305519f27c21ab51531c0d7cb
Parents: 2ff2849
Author: Subin <su...@apache.org>
Authored: Wed Jul 2 23:03:34 2014 +0530
Committer: Subin <su...@apache.org>
Committed: Wed Jul 2 23:04:34 2014 +0530
----------------------------------------------------------------------
.../src/main/python/ambari_client/ambari_api.py | 26 +-
.../python/ambari_client/core/http_client.py | 13 +-
.../python/ambari_client/model/blueprint.py | 53 +++
.../src/main/python/ambari_client/model/host.py | 7 +-
.../main/python/ambari_client/model/paths.py | 2 +
.../main/python/ambari_client/model/status.py | 42 +-
.../main/python/ambari_client/model/utils.py | 39 +-
ambari-shell/ambari-groovy-shell/pom.xml | 142 +++++++
.../org/apache/ambari/shell/AmbariShell.java | 111 +++++
.../ambari/shell/commands/BasicCommands.java | 207 ++++++++++
.../shell/commands/BlueprintCommands.java | 198 +++++++++
.../ambari/shell/commands/ClusterCommands.java | 294 +++++++++++++
.../ambari/shell/commands/ElephantCommand.java | 53 +++
.../ambari/shell/commands/HostCommands.java | 119 ++++++
.../ambari/shell/completion/Blueprint.java | 34 ++
.../apache/ambari/shell/completion/Host.java | 34 ++
.../configuration/ConverterConfiguration.java | 142 +++++++
.../shell/configuration/ShellConfiguration.java | 114 +++++
.../shell/converter/BlueprintConverter.java | 58 +++
.../ambari/shell/converter/HostConverter.java | 58 +++
.../shell/customization/AmbariBanner.java | 50 +++
.../shell/customization/AmbariHistory.java | 40 ++
.../shell/customization/AmbariPrompt.java | 43 ++
.../ambari/shell/flash/AbstractFlash.java | 66 +++
.../apache/ambari/shell/flash/FlashService.java | 47 +++
.../apache/ambari/shell/flash/FlashType.java | 39 ++
.../ambari/shell/flash/InstallProgress.java | 79 ++++
.../ambari/shell/model/AmbariContext.java | 159 +++++++
.../org/apache/ambari/shell/model/Focus.java | 53 +++
.../apache/ambari/shell/model/FocusType.java | 55 +++
.../org/apache/ambari/shell/model/Hints.java | 59 +++
.../ambari/shell/support/TableRenderer.java | 121 ++++++
.../src/main/resources/elephant.txt | 8 +
.../shell/commands/BlueprintCommandsTest.java | 128 ++++++
.../shell/commands/ClusterCommandsTest.java | 279 +++++++++++++
.../ambari/shell/commands/HostCommandsTest.java | 66 +++
.../shell/customization/AmbariPromptTest.java | 54 +++
.../ambari/shell/model/AmbariContextTest.java | 66 +++
.../ambari/shell/support/TableRendererTest.java | 54 +++
.../src/test/resources/2columns | 5 +
.../src/test/resources/3columns | 5 +
.../src/test/resources/testBlueprint.json | 48 +++
.../ambari-python-shell/conf/unix/ambari-shell | 62 +++
.../conf/unix/ambari-shell.ini | 19 +
ambari-shell/ambari-python-shell/pom.xml | 237 +++++++++++
.../src/main/package/deb/control/control | 22 +
.../src/main/package/deb/control/postinst | 15 +
.../src/main/package/deb/control/postrm | 15 +
.../src/main/package/deb/control/posttrm | 15 +
.../src/main/package/deb/control/preinst | 15 +
.../src/main/package/deb/control/prerm | 15 +
.../src/main/python/ambari_shell/__init__.py | 16 +
.../main/python/ambari_shell/ambari_shell.py | 412 +++++++++++++++++++
.../python/ambari_shell/plugins/__init__.py | 16 +
.../python/ambari_shell/plugins/blueprint.py | 21 +
.../ambari_shell/plugins/connect_cluster.py | 81 ++++
.../ambari_shell/plugins/create_cluster.py | 34 ++
.../main/python/ambari_shell/plugins/service.py | 125 ++++++
.../python/ambari_shell/plugins/shell_config.py | 77 ++++
.../main/python/ambari_shell/plugins/show.py | 155 +++++++
.../main/python/ambari_shell/utils/__init__.py | 16 +
.../python/ambari_shell/utils/displayutils.py | 128 ++++++
.../main/python/ambari_shell/utils/osutils.py | 69 ++++
.../python/ambari_shell/utils/pluginutils.py | 100 +++++
.../src/main/python/setup.py | 40 ++
.../src/packages/tarball/all.xml | 34 ++
ambari-shell/assemblies/client.xml | 20 +
ambari-shell/pom.xml | 128 ++----
.../org/apache/ambari/shell/AmbariShell.java | 111 -----
.../ambari/shell/commands/BasicCommands.java | 207 ----------
.../shell/commands/BlueprintCommands.java | 198 ---------
.../ambari/shell/commands/ClusterCommands.java | 294 -------------
.../ambari/shell/commands/ElephantCommand.java | 53 ---
.../ambari/shell/commands/HostCommands.java | 119 ------
.../ambari/shell/completion/Blueprint.java | 34 --
.../apache/ambari/shell/completion/Host.java | 34 --
.../configuration/ConverterConfiguration.java | 142 -------
.../shell/configuration/ShellConfiguration.java | 114 -----
.../shell/converter/BlueprintConverter.java | 58 ---
.../ambari/shell/converter/HostConverter.java | 58 ---
.../shell/customization/AmbariBanner.java | 50 ---
.../shell/customization/AmbariHistory.java | 40 --
.../shell/customization/AmbariPrompt.java | 43 --
.../ambari/shell/flash/AbstractFlash.java | 66 ---
.../apache/ambari/shell/flash/FlashService.java | 47 ---
.../apache/ambari/shell/flash/FlashType.java | 39 --
.../ambari/shell/flash/InstallProgress.java | 79 ----
.../ambari/shell/model/AmbariContext.java | 159 -------
.../org/apache/ambari/shell/model/Focus.java | 53 ---
.../apache/ambari/shell/model/FocusType.java | 55 ---
.../org/apache/ambari/shell/model/Hints.java | 59 ---
.../ambari/shell/support/TableRenderer.java | 121 ------
ambari-shell/src/main/resources/elephant.txt | 8 -
.../shell/commands/BlueprintCommandsTest.java | 128 ------
.../shell/commands/ClusterCommandsTest.java | 279 -------------
.../ambari/shell/commands/HostCommandsTest.java | 66 ---
.../shell/customization/AmbariPromptTest.java | 54 ---
.../ambari/shell/model/AmbariContextTest.java | 66 ---
.../ambari/shell/support/TableRendererTest.java | 54 ---
ambari-shell/src/test/resources/2columns | 5 -
ambari-shell/src/test/resources/3columns | 5 -
.../src/test/resources/testBlueprint.json | 48 ---
102 files changed, 5039 insertions(+), 3064 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-client/python-client/src/main/python/ambari_client/ambari_api.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/ambari_api.py b/ambari-client/python-client/src/main/python/ambari_client/ambari_api.py
index 470db36..a79d907 100644
--- a/ambari-client/python-client/src/main/python/ambari_client/ambari_api.py
+++ b/ambari-client/python-client/src/main/python/ambari_client/ambari_api.py
@@ -18,7 +18,7 @@
import logging
from ambari_client.core.http_client import HttpClient
from ambari_client.core.rest_resource import RestResource
-from ambari_client.model import blueprint, stack, cluster, host
+from ambari_client.model import blueprint, stack, cluster, host, status
__docformat__ = "epytext"
@@ -66,6 +66,13 @@ class AmbariClient(RestResource):
if port is None:
port = 8080
+ if not http_header:
+ http_header = {'X-Requested-By': 'pythonclient'}
+ elif 'X-Requested-By' not in http_header.keys():
+ http_header.update({'X-Requested-By': 'pythonclient'})
+ else:
+ pass
+
host_url = "%s://%s:%s/api/v%s" % (protocol, host_name, port, version)
if client is None:
client = HttpClient(host_url, user_name, password)
@@ -224,6 +231,15 @@ class AmbariClient(RestResource):
"""
return cluster._task_status(self, cluster_name, requestid)
+ def get_requests(self, cluster_name, noOfrequest=3):
+ """
+ get components from stack
+ @param version: The HDP version.
+ @param service_name: service name
+ @return: A ConfigModel object
+ """
+ return status._get_N_requests(self, cluster_name, noOfrequest)
+
def get_blueprint(self, blueprint_name=None):
"""
get blueprint
@@ -232,6 +248,14 @@ class AmbariClient(RestResource):
"""
return blueprint.get_blueprint(self, blueprint_name)
+ def get_cluster_blueprint(self, cluster_name):
+ """
+ get blueprint
+ @param cluster_name:cluster_name name.
+ @return: A BlueprintModel object
+ """
+ return blueprint.get_cluster_blueprint(self, cluster_name)
+
def delete_blueprint(self, blueprint_name=None):
"""
get blueprint
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-client/python-client/src/main/python/ambari_client/core/http_client.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/core/http_client.py b/ambari-client/python-client/src/main/python/ambari_client/core/http_client.py
index 299b279..72b3b5f 100644
--- a/ambari-client/python-client/src/main/python/ambari_client/core/http_client.py
+++ b/ambari-client/python-client/src/main/python/ambari_client/core/http_client.py
@@ -103,6 +103,13 @@ class HttpClient(object):
self.c.setopt(pycurl.WRITEFUNCTION, buf.write)
self.c.setopt(pycurl.SSL_VERIFYPEER, 0)
+ # make sure options are reset from previous requests or your GET might
+ # become a DELETE
+ self.c.setopt(pycurl.CUSTOMREQUEST, None)
+ self.c.setopt(pycurl.HTTPGET, 0)
+ self.c.setopt(pycurl.NOBODY, 0)
+ self.c.setopt(pycurl.POST, 0)
+
LOG.debug("invoke : url = " + str(url))
# set http_method
if http_method == "GET":
@@ -121,9 +128,9 @@ class HttpClient(object):
if http_method in ('POST', 'PUT'):
LOG.debug("data..........." + str(payload))
data = json.dumps(payload)
- #data= data.decode('unicode-escape')
- #LOG.debug( "after unicode decode")
- #LOG.debug( data)
+ # data= data.decode('unicode-escape')
+ # LOG.debug( "after unicode decode")
+ # LOG.debug( data)
data = self._to_bytestring(data)
LOG.debug("after _to_bytestring")
LOG.debug(data)
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-client/python-client/src/main/python/ambari_client/model/blueprint.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/model/blueprint.py b/ambari-client/python-client/src/main/python/ambari_client/model/blueprint.py
index 8497dc6..e207a6b 100644
--- a/ambari-client/python-client/src/main/python/ambari_client/model/blueprint.py
+++ b/ambari-client/python-client/src/main/python/ambari_client/model/blueprint.py
@@ -15,6 +15,7 @@
# limitations under the License.
import logging
+import json
from ambari_client.model.base_model import BaseModel, ModelList
from ambari_client.model import paths, utils, status
@@ -46,6 +47,33 @@ def get_blueprint(resource_root, blueprint_name):
"Blueprints")
+def get_cluster_blueprint(resource_root, cluster_name):
+ """
+ Get all Blueprint
+ @param root_resource: The root Resource .
+ @param name: blueprint_name
+ @return: A list of BlueprintModel objects.
+ """
+ resp = resource_root.get(paths.BLUEPRINT_CLUSTER_PATH % cluster_name)
+ result = json.dumps(resp)
+ resp_dictt = json.loads(result)
+ objects = [
+ utils.ModelUtils.create_model(
+ BlueprintHostModel,
+ x,
+ resource_root,
+ "NO_KEY") for x in resp_dictt['host_groups']]
+ LOG.debug(objects)
+
+ bluep = utils.ModelUtils.create_model(
+ BlueprintModel,
+ resp,
+ resource_root,
+ "Blueprints")
+
+ return bluep, ModelList(objects)
+
+
def delete_blueprint(resource_root, blueprint_name):
"""
Delete a blueprint by name
@@ -93,6 +121,31 @@ class BlueprintModel(BaseModel):
return "<<BlueprintModel>> blueprint_name = %s " % (
self.blueprint_name)
+ def _get_cluster_name(self):
+ if self.clusterRef:
+ return self.clusterRef.cluster_name
+ return None
+
+
+class BlueprintHostModel(BaseModel):
+
+ """
+ The BlueprintHostModel class
+ """
+ RO_ATTR = ('blueprint_name',)
+ RW_ATTR = ('name', 'components', 'cardinality')
+ REF_ATTR = ()
+
+ def __init__(self, resource_root, name, components, cardinality):
+ utils.retain_self_helper(BaseModel, **locals())
+
+ def __str__(self):
+ return "<<BlueprintHostModel>> name = %s ,components =%s" % (
+ self.name, str(self.components))
+
+ def to_json(self):
+ pass
+
class BlueprintConfigModel(BaseModel):
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-client/python-client/src/main/python/ambari_client/model/host.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/model/host.py b/ambari-client/python-client/src/main/python/ambari_client/model/host.py
index 05bdf6e..f6c39d6 100644
--- a/ambari-client/python-client/src/main/python/ambari_client/model/host.py
+++ b/ambari-client/python-client/src/main/python/ambari_client/model/host.py
@@ -219,8 +219,11 @@ def _bootstrap_hosts(root_resource, hosts_list, ssh_key, ssh_user):
@param hosts_list list of host_names.
@return: A StatusModel object.
"""
- payload_dic = {"verbose":True, "sshKey": ssh_key.encode(
- 'string_escape') , "hosts":str(hosts_list) , "user": ssh_user }
+ payload_dic = {
+ "verbose": True,
+ "sshKey": ssh_key,
+ "hosts": str(hosts_list),
+ "user": ssh_user}
resp = root_resource.post(
paths.BOOTSTRAP_PATH,
payload_dic,
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-client/python-client/src/main/python/ambari_client/model/paths.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/model/paths.py b/ambari-client/python-client/src/main/python/ambari_client/model/paths.py
index 3643129..e111824 100644
--- a/ambari-client/python-client/src/main/python/ambari_client/model/paths.py
+++ b/ambari-client/python-client/src/main/python/ambari_client/model/paths.py
@@ -40,6 +40,7 @@ HOSTS_ASSIGN_ROLE = "/clusters/%s/hosts?Hosts/host_name=%s"
BOOTSTRAP_PATH = "/bootstrap"
REQUEST_STATUS_PATH = "/clusters/%s/requests/%s?fields=tasks/Tasks/status"
REQUEST_PATH = "clusters/%s/requests/%s"
+REQUEST_N_PATH = "clusters/%s/requests?to=end&page_size=%s&fields=Requests"
CONFIGURATION_PATH = "/clusters/%s/configurations?type=%s&tag=%s"
CONFIGURATION_ALL_PATH = "/clusters/%s/configurations?type=%s"
@@ -57,3 +58,4 @@ TASKS_PATH = "clusters/%s/requests/%s/tasks?fields=*"
BLUEPRINT_ALL_PATH = "blueprints?fields=*"
BLUEPRINT_PATH = "blueprints/%s"
+BLUEPRINT_CLUSTER_PATH = "clusters/%s?format=blueprint"
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-client/python-client/src/main/python/ambari_client/model/status.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/model/status.py b/ambari-client/python-client/src/main/python/ambari_client/model/status.py
index 37ca8fd..8ef1140 100644
--- a/ambari-client/python-client/src/main/python/ambari_client/model/status.py
+++ b/ambari-client/python-client/src/main/python/ambari_client/model/status.py
@@ -15,7 +15,7 @@
# limitations under the License.
import logging
-from ambari_client.model.base_model import BaseModel
+from ambari_client.model.base_model import BaseModel, ModelList
from ambari_client.model import paths, utils
@@ -32,7 +32,7 @@ class StatusModel(BaseModel):
REF_ATTR = ('cluster_name',)
def __init__(self, resource_root, status, requestId=None, message=None):
- #BaseModel.__init__(self, **locals())
+ # BaseModel.__init__(self, **locals())
utils.retain_self_helper(BaseModel, **locals())
def __str__(self):
@@ -72,3 +72,41 @@ class StatusModel(BaseModel):
return self.status
else:
None
+
+
+def _get_N_requests(resource_root, cluster_name, noOfrequest):
+ """
+ Get all services in a cluster.
+ @param cluster_name :Cluster name.
+ @return: A ModelList object.
+ """
+ path = paths.REQUEST_N_PATH % (cluster_name, noOfrequest)
+ dic = resource_root.get(path)
+ return utils.ModelUtils.get_model_list(
+ ModelList,
+ RequestModel,
+ dic,
+ resource_root,
+ "Requests")
+
+
+class RequestModel(BaseModel):
+
+ """
+ The RequestModel class
+ """
+ RO_ATTR = ("request_context",)
+ RW_ATTR = ('id', 'request_status')
+ REF_ATTR = ('cluster_name',)
+
+ def __init__(self, resource_root, id, request_status=None):
+ # BaseModel.__init__(self, **locals())
+ utils.retain_self_helper(BaseModel, **locals())
+
+ def __str__(self):
+ return "<<RequestModel>> id = %s ; request_status = %s" % (
+ self.id, self.request_status)
+
+ def is_error(self):
+ return (
+ self.status != 200 and self.status != 201 and self.status != 202)
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-client/python-client/src/main/python/ambari_client/model/utils.py
----------------------------------------------------------------------
diff --git a/ambari-client/python-client/src/main/python/ambari_client/model/utils.py b/ambari-client/python-client/src/main/python/ambari_client/model/utils.py
index e1f1479..65dd153 100644
--- a/ambari-client/python-client/src/main/python/ambari_client/model/utils.py
+++ b/ambari-client/python-client/src/main/python/ambari_client/model/utils.py
@@ -162,12 +162,20 @@ class ModelUtils(object):
rw_dict = {}
LOG.debug("model_dict = " + str(model_dict))
+ # extract model /keyword
if isinstance(model_dict, dict) and RESOURCE_KEY_WORD in model_dict:
model_dict = model_dict[RESOURCE_KEY_WORD]
- LOG.debug(
- "model_dict has %s ;subset = %s" %
- (RESOURCE_KEY_WORD, str(
- model_dict.items())))
+ if not isinstance(model_dict, list):
+ LOG.debug(
+ "model_dict has %s ;subset = %s" %
+ (RESOURCE_KEY_WORD, str(
+ model_dict.items())))
+ else:
+ LOG.debug(
+ "model_dict is list and has %s ;subset = %s" %
+ (RESOURCE_KEY_WORD, str(
+ model_dict)))
+ # check for Requests
if isinstance(model_dict, dict) and "Requests" in model_dict:
model_dict = model_dict["Requests"]
LOG.debug(
@@ -175,12 +183,17 @@ class ModelUtils(object):
(str(
model_dict.items())))
- for k, v in model_dict.items():
- LOG.debug("key = %s ; value = %s " % (str(k), str(v)))
- if k in model_cls.RW_ATTR:
- LOG.debug(k + " is there in RW_ATTR")
- rw_dict[k] = v
- del model_dict[k]
+ # check for composition i.e list of Models
+ if isinstance(model_dict, list):
+ LOG.debug(
+ "model_dict is list")
+ else:
+ for k, v in model_dict.items():
+ LOG.debug("key = %s ; value = %s " % (str(k), str(v)))
+ if k in model_cls.RW_ATTR:
+ LOG.debug(k + " is there in RW_ATTR")
+ rw_dict[k] = v
+ del model_dict[k]
rw_dict = get_unicode_kw(rw_dict)
obj = model_cls(resource_root, **rw_dict)
@@ -238,7 +251,7 @@ def get_REF_object(ref_class_name):
def get_unicode(v):
- #import unicodedata
+ # import unicodedata
if v:
if isinstance(v, unicode):
v = unicodedata.normalize('NFKD', v).encode('ascii', 'ignore')
@@ -249,8 +262,8 @@ def get_unicode(v):
def retain_self_helper(memclass, self=None, **kwargs):
- # print locals()
- #from ambari_client.model.base_model import BaseModel
+ # print locals()
+ # from ambari_client.model.base_model import BaseModel
memclass.__init__(self, **kwargs)
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/pom.xml b/ambari-shell/ambari-groovy-shell/pom.xml
new file mode 100644
index 0000000..a1acf8b
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/pom.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0"?>
+<!-- Licensed 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.
+ See accompanying LICENSE file. -->
+<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/maven-v4_0_0.xsd">
+ <parent>
+ <groupId>org.apache.ambari</groupId>
+ <artifactId>ambari-shell</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../../ambari-shell</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.ambari</groupId>
+ <artifactId>ambari-groovy-shell</artifactId>
+ <packaging>jar</packaging>
+ <name>Ambari Groovy Shell</name>
+ <version>1.3.0-SNAPSHOT</version>
+ <description>Ambari Groovy Shell</description>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <start-class>org.apache.ambari.shell.AmbariShell</start-class>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.shell</groupId>
+ <artifactId>spring-shell</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.github.lalyos</groupId>
+ <artifactId>jfiglet</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ambari</groupId>
+ <artifactId>groovy-client</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>jline</groupId>
+ <artifactId>jline</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.0</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>src/main/resources/elephant.txt</exclude>
+ <exclude>src/test/resources/2columns</exclude>
+ <exclude>src/test/resources/3columns</exclude>
+ <exclude>src/test/resources/testBlueprint.json</exclude>
+ </excludes>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>test</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <skipAssembly>true</skipAssembly>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <version>1.0.2.RELEASE</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <mainClass>${start-class}</mainClass>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.vafer</groupId>
+ <artifactId>jdeb</artifactId>
+ <version>1.0.1</version>
+ <executions>
+ <execution>
+ <phase>none</phase>
+ <goals>
+ <goal>jdeb</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <controlDir>${basedir}/../../ambari-project/src/main/package/deb/control</controlDir>
+ <skip>true</skip>
+ <submodules>false</submodules>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/AmbariShell.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/AmbariShell.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/AmbariShell.java
new file mode 100644
index 0000000..e842620
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/AmbariShell.java
@@ -0,0 +1,111 @@
+/**
+ * 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.shell;
+
+import org.apache.ambari.groovy.client.AmbariClient;
+import org.apache.ambari.shell.model.AmbariContext;
+import org.apache.ambari.shell.model.Hints;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.shell.CommandLine;
+import org.springframework.shell.core.JLineShellComponent;
+import org.springframework.shell.event.ShellStatus;
+import org.springframework.shell.event.ShellStatusListener;
+
+/**
+ * Shell bootstrap.
+ */
+@Configuration
+@ComponentScan(basePackageClasses = {AmbariShell.class})
+public class AmbariShell implements CommandLineRunner, ShellStatusListener {
+
+ @Autowired
+ private CommandLine commandLine;
+ @Autowired
+ private JLineShellComponent shell;
+ @Autowired
+ private AmbariContext context;
+ @Autowired
+ private AmbariClient client;
+
+ @Override
+ public void run(String... arg) throws Exception {
+ String[] shellCommandsToExecute = commandLine.getShellCommandsToExecute();
+ if (shellCommandsToExecute != null) {
+ for (String cmd : shellCommandsToExecute) {
+ if (!shell.executeScriptLine(cmd)) {
+ break;
+ }
+ }
+ System.exit(0);
+ } else {
+ shell.addShellStatusListener(this);
+ shell.start();
+ shell.promptLoop();
+ shell.waitForComplete();
+ }
+ }
+
+ @Override
+ public void onShellStatusChange(ShellStatus oldStatus, ShellStatus newStatus) {
+ if (newStatus.getStatus() == ShellStatus.Status.STARTED) {
+ try {
+ String cluster = client.getClusterName();
+ boolean available = client.isBlueprintAvailable();
+ if (cluster == null) {
+ if (available) {
+ context.setHint(Hints.BUILD_CLUSTER);
+ } else {
+ context.setHint(Hints.ADD_BLUEPRINT);
+ }
+ } else {
+ context.setHint(Hints.PROGRESS);
+ }
+ context.setCluster(cluster);
+ context.setBlueprintsAvailable(available);
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ shell.executeCommand("quit");
+ }
+ }
+ }
+
+
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ System.out.println(
+ "\nAmbari Shell: Interactive command line tool for managing Apache Ambari.\n\n" +
+ "Usage:\n" +
+ " java -jar ambari-shell.jar : Starts Ambari Shell in interactive mode.\n" +
+ " java -jar ambari-shell.jar --cmdfile=<FILE> : Ambari Shell executes commands read from the file.\n\n" +
+ "Options:\n" +
+ " --ambari.host=<HOSTNAME> Hostname of the Ambari Server [default: localhost].\n" +
+ " --ambari.port=<PORT> Port of the Ambari Server [default: 8080].\n" +
+ " --ambari.user=<USER> Username of the Ambari admin [default: admin].\n" +
+ " --ambari.password=<PASSWORD> Password of the Ambari admin [default: admin].\n\n" +
+ "Note:\n" +
+ " At least one option is mandatory."
+ );
+ System.exit(1);
+ }
+ new SpringApplicationBuilder(AmbariShell.class).showBanner(false).run(args);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/BasicCommands.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/BasicCommands.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/BasicCommands.java
new file mode 100644
index 0000000..9babe12
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/BasicCommands.java
@@ -0,0 +1,207 @@
+/**
+ * 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.shell.commands;
+
+import static org.apache.ambari.shell.support.TableRenderer.renderMapValueMap;
+import static org.apache.ambari.shell.support.TableRenderer.renderSingleMap;
+
+import org.apache.ambari.groovy.client.AmbariClient;
+import org.apache.ambari.shell.model.AmbariContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.shell.core.CommandMarker;
+import org.springframework.shell.core.annotation.CliAvailabilityIndicator;
+import org.springframework.shell.core.annotation.CliCommand;
+import org.springframework.shell.core.annotation.CliOption;
+import org.springframework.stereotype.Component;
+
+/**
+ * Basic commands used in the shell. Delegating the commands
+ * to the Ambari Server via a Groovy based client.
+ *
+ * @see org.apache.ambari.groovy.client.AmbariClient
+ */
+@Component
+public class BasicCommands implements CommandMarker {
+
+ private AmbariClient client;
+ private AmbariContext context;
+
+ @Autowired
+ public BasicCommands(AmbariClient client, AmbariContext context) {
+ this.client = client;
+ this.context = context;
+ }
+
+ /**
+ * Checks whether the tasks command is available or not.
+ *
+ * @return true if its available false otherwise
+ */
+ @CliAvailabilityIndicator("tasks")
+ public boolean isTasksCommandAvailable() {
+ return context.isConnectedToCluster();
+ }
+
+ /**
+ * Prints the tasks of the Ambari Server.
+ *
+ * @param id id of the request
+ * @return task list
+ */
+ @CliCommand(value = "tasks", help = "Lists the Ambari tasks")
+ public String tasks(
+ @CliOption(key = "id", mandatory = false, help = "Id of the request; default is: 1", unspecifiedDefaultValue = "1") String id) {
+ return renderSingleMap(client.getTaskMap(id), "TASK", "STATUS");
+ }
+
+ /**
+ * Checks whether the service list command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("services list")
+ public boolean isServiceListCommandAvailable() {
+ return context.isConnectedToCluster();
+ }
+
+ /**
+ * Prints the available service list of the Ambari Server.
+ *
+ * @return service list
+ */
+ @CliCommand(value = "services list", help = "Lists the available services")
+ public String servicesList() {
+ return renderSingleMap(client.getServicesMap(), "SERVICE", "STATE");
+ }
+
+ /**
+ * Checks whether the service components command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("services components")
+ public boolean isServiceComponentsCommandAvailable() {
+ return context.isConnectedToCluster();
+ }
+
+ /**
+ * Prints the service components of the Ambari Server.
+ *
+ * @return service component list
+ */
+ @CliCommand(value = "services components", help = "Lists all services with their components")
+ public String serviceComponents() {
+ return renderMapValueMap(client.getServiceComponentsMap(), "SERVICE", "COMPONENT", "STATE");
+ }
+
+ /**
+ * Checks whether the debug on command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("debug on")
+ public boolean isDebugOnCommandAvailable() {
+ return !client.isDebugEnabled();
+ }
+
+ /**
+ * Turns the debug on. From now on users will see the URLs of the API calls.
+ *
+ * @return status message
+ */
+ @CliCommand(value = "debug on", help = "Shows the URL of the API calls")
+ public String debugOn() {
+ client.setDebugEnabled(true);
+ return "debug enabled";
+ }
+
+ /**
+ * Checks whether the debug off command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("debug off")
+ public boolean isDebugOffCommandAvailable() {
+ return client.isDebugEnabled();
+ }
+
+ /**
+ * Turns the debug off. URLs are not visible anymore.
+ *
+ * @return status message
+ */
+ @CliCommand(value = "debug off", help = "Stops showing the URL of the API calls")
+ public String debugOff() {
+ client.setDebugEnabled(false);
+ return "debug disabled";
+ }
+
+ /**
+ * Checks whether the hint command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("hint")
+ public boolean isHintCommandAvailable() {
+ return true;
+ }
+
+ /**
+ * Provides some hints what you can do in the current context.
+ *
+ * @return hint message
+ */
+ @CliCommand(value = "hint", help = "Shows some hints")
+ public String hint() {
+ return context.getHint();
+ }
+
+ @CliAvailabilityIndicator("services stop")
+ public boolean isServiceStopCommandAvailable() {
+ return context.isConnectedToCluster();
+ }
+
+ @CliCommand(value = "services stop", help = "Stops all the running services")
+ public String stopServices() {
+ String message;
+ try {
+ client.stopAllServices();
+ message = "Stopping all services..";
+ } catch (Exception e) {
+ message = "Cannot stop services";
+ }
+ return String.format("%s\n\n%s", message, servicesList());
+ }
+
+ @CliAvailabilityIndicator("services start")
+ public boolean isServiceStartCommandAvailable() {
+ return context.isConnectedToCluster();
+ }
+
+ @CliCommand(value = "services start", help = "Starts all the services")
+ public String startServices() {
+ String message;
+ try {
+ client.startAllServices();
+ message = "Starting all services..";
+ } catch (Exception e) {
+ message = "Cannot start services";
+ }
+ return String.format("%s\n\n%s", message, servicesList());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/BlueprintCommands.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/BlueprintCommands.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/BlueprintCommands.java
new file mode 100644
index 0000000..73000d0
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/BlueprintCommands.java
@@ -0,0 +1,198 @@
+/**
+ * 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.shell.commands;
+
+import static org.apache.ambari.shell.support.TableRenderer.renderMultiValueMap;
+import static org.apache.ambari.shell.support.TableRenderer.renderSingleMap;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.ambari.groovy.client.AmbariClient;
+import org.apache.ambari.shell.completion.Blueprint;
+import org.apache.ambari.shell.model.AmbariContext;
+import org.apache.ambari.shell.model.Hints;
+import org.apache.commons.io.IOUtils;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.shell.core.CommandMarker;
+import org.springframework.shell.core.annotation.CliAvailabilityIndicator;
+import org.springframework.shell.core.annotation.CliCommand;
+import org.springframework.shell.core.annotation.CliOption;
+import org.springframework.stereotype.Component;
+
+/**
+ * Blueprint related commands used in the shell.
+ *
+ * @see org.apache.ambari.groovy.client.AmbariClient
+ */
+@Component
+public class BlueprintCommands implements CommandMarker {
+
+ private AmbariClient client;
+ private AmbariContext context;
+ private ObjectMapper jsonMapper;
+
+ @Autowired
+ public BlueprintCommands(AmbariClient client, AmbariContext context, ObjectMapper jsonMapper) {
+ this.client = client;
+ this.context = context;
+ this.jsonMapper = jsonMapper;
+ }
+
+ /**
+ * Checks whether the blueprints command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("blueprint list")
+ public boolean isBlueprintListCommandAvailable() {
+ return context.areBlueprintsAvailable();
+ }
+
+ /**
+ * Prints all the blueprints.
+ *
+ * @return list of blueprints
+ */
+ @CliCommand(value = "blueprint list", help = "Lists all known blueprints")
+ public String listBlueprints() {
+ return renderSingleMap(client.getBlueprintsMap(), "BLUEPRINT", "STACK");
+ }
+
+ /**
+ * Checks whether the blueprint show command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator(value = "blueprint show")
+ public boolean isBlueprintShowCommandAvailable() {
+ return context.areBlueprintsAvailable();
+ }
+
+ /**
+ * Shows the requested blueprint's details.
+ *
+ * @param id id of the blueprint
+ * @return blueprint as formatted table
+ */
+ @CliCommand(value = "blueprint show", help = "Shows the blueprint by its id")
+ public String showBlueprint(
+ @CliOption(key = "id", mandatory = true, help = "Id of the blueprint") Blueprint id) {
+ return renderMultiValueMap(client.getBlueprintMap(id.getName()), "HOSTGROUP", "COMPONENT");
+ }
+
+ /**
+ * Checks whether the blueprint add command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator(value = "blueprint add")
+ public boolean isBlueprintAddCommandAvailable() {
+ return true;
+ }
+
+ /**
+ * Adds a blueprint to the Ambari server either through an URL or from a file.
+ * If both specified the file takes precedence.
+ *
+ * @param url -optional, URL containing the blueprint json
+ * @param file - optional, file containing the blueprint json
+ * @return status message
+ */
+ @CliCommand(value = "blueprint add", help = "Add a new blueprint with either --url or --file")
+ public String addBlueprint(
+ @CliOption(key = "url", mandatory = false, help = "URL of the blueprint to download from") String url,
+ @CliOption(key = "file", mandatory = false, help = "File which contains the blueprint") File file) {
+ String message;
+ try {
+ String json = file == null ? readContent(url) : readContent(file);
+ if (json != null) {
+ client.addBlueprint(json);
+ context.setHint(Hints.BUILD_CLUSTER);
+ context.setBlueprintsAvailable(true);
+ message = String.format("Blueprint: '%s' has been added", getBlueprintName(json));
+ } else {
+ message = "No blueprint specified";
+ }
+ } catch (Exception e) {
+ message = "Cannot add blueprint: " + e.getMessage();
+ }
+ return message;
+ }
+
+ /**
+ * Checks whether the blueprint defaults command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator(value = "blueprint defaults")
+ public boolean isBlueprintDefaultsAddCommandAvailable() {
+ return !context.areBlueprintsAvailable();
+ }
+
+ /**
+ * Adds two default blueprints to the Ambari server.
+ *
+ * @return status message
+ */
+ @CliCommand(value = "blueprint defaults", help = "Adds the default blueprints to Ambari")
+ public String addBlueprint() {
+ String message = "Default blueprints added";
+ try {
+ client.addDefaultBlueprints();
+ context.setHint(Hints.BUILD_CLUSTER);
+ context.setBlueprintsAvailable(true);
+ } catch (Exception e) {
+ message = "Failed to add the default blueprints: " + e.getMessage();
+ }
+ return message;
+ }
+
+ private String readContent(File file) {
+ String content = null;
+ try {
+ content = IOUtils.toString(new FileInputStream(file));
+ } catch (IOException e) {
+ // not important
+ }
+ return content;
+ }
+
+ private String readContent(String url) {
+ String content = null;
+ try {
+ content = IOUtils.toString(new URL(url));
+ } catch (IOException e) {
+ // not important
+ }
+ return content;
+ }
+
+ private String getBlueprintName(String json) {
+ String result = "";
+ try {
+ result = jsonMapper.readTree(json.getBytes()).get("Blueprints").get("blueprint_name").asText();
+ } catch (IOException e) {
+ // not important
+ }
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/ClusterCommands.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/ClusterCommands.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/ClusterCommands.java
new file mode 100644
index 0000000..dafdb85
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/ClusterCommands.java
@@ -0,0 +1,294 @@
+/**
+ * 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.shell.commands;
+
+import static org.apache.ambari.shell.support.TableRenderer.renderMultiValueMap;
+import static org.apache.ambari.shell.support.TableRenderer.renderSingleMap;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ambari.groovy.client.AmbariClient;
+import org.apache.ambari.shell.completion.Blueprint;
+import org.apache.ambari.shell.completion.Host;
+import org.apache.ambari.shell.flash.FlashService;
+import org.apache.ambari.shell.model.AmbariContext;
+import org.apache.ambari.shell.model.FocusType;
+import org.apache.ambari.shell.model.Hints;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.shell.core.CommandMarker;
+import org.springframework.shell.core.annotation.CliAvailabilityIndicator;
+import org.springframework.shell.core.annotation.CliCommand;
+import org.springframework.shell.core.annotation.CliOption;
+import org.springframework.stereotype.Component;
+
+import groovyx.net.http.HttpResponseException;
+
+/**
+ * Cluster related commands used in the shell.
+ *
+ * @see org.apache.ambari.groovy.client.AmbariClient
+ */
+@Component
+public class ClusterCommands implements CommandMarker {
+
+ private AmbariClient client;
+ private AmbariContext context;
+ private FlashService flashService;
+ private Map<String, List<String>> hostGroups;
+
+ @Autowired
+ public ClusterCommands(AmbariClient client, AmbariContext context, FlashService flashService) {
+ this.client = client;
+ this.context = context;
+ this.flashService = flashService;
+ }
+
+ /**
+ * Checks whether the cluster build command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("cluster build")
+ public boolean isClusterBuildCommandAvailable() {
+ return !context.isConnectedToCluster() && !context.isFocusOnClusterBuild() && context.areBlueprintsAvailable();
+ }
+
+ /**
+ * Sets the focus on cluster building. Takes a blueprint id, if it does not exists it wont focus.
+ * After focus the users are able to assign hosts to host groups.
+ *
+ * @param id id of the blueprint
+ * @return prints the blueprint as formatted table if exists, otherwise error message
+ */
+ @CliCommand(value = "cluster build", help = "Starts to build a cluster")
+ public String buildCluster(
+ @CliOption(key = "blueprint", mandatory = true, help = "Id of the blueprint, use 'blueprints' command to see the list") Blueprint id) {
+ String message;
+ String blueprint = id.getName();
+ if (client.doesBlueprintExist(blueprint)) {
+ context.setFocus(blueprint, FocusType.CLUSTER_BUILD);
+ context.setHint(Hints.ASSIGN_HOSTS);
+ message = String.format("%s\n%s",
+ renderSingleMap(client.getHostNames(), "HOSTNAME", "STATE"),
+ renderMultiValueMap(client.getBlueprintMap(blueprint), "HOSTGROUP", "COMPONENT"));
+ createNewHostGroups();
+ } else {
+ message = "Not a valid blueprint id";
+ }
+ return message;
+ }
+
+ /**
+ * Checks whether the cluster assign command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("cluster assign")
+ public boolean isAssignCommandAvailable() {
+ return context.isFocusOnClusterBuild();
+ }
+
+ /**
+ * Assign hosts to host groups provided in the blueprint.
+ *
+ * @param host host to assign
+ * @param group which host group to
+ * @return status message
+ */
+ @CliCommand(value = "cluster assign", help = "Assign host to host group")
+ public String assign(
+ @CliOption(key = "host", mandatory = true, help = "Fully qualified host name") Host host,
+ @CliOption(key = "hostGroup", mandatory = true, help = "Host group which to assign the host") String group) {
+ String message;
+ String hostName = host.getName();
+ if (client.getHostNames().keySet().contains(hostName)) {
+ if (addHostToGroup(hostName, group)) {
+ context.setHint(Hints.CREATE_CLUSTER);
+ message = String.format("%s has been added to %s", hostName, group);
+ } else {
+ message = String.format("%s is not a valid host group", group);
+ }
+ } else {
+ message = String.format("%s is not a valid hostname", hostName);
+ }
+ return message;
+ }
+
+ /**
+ * Checks whether the cluster auto command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator(value = "cluster autoAssign")
+ public boolean isClusterAutoAssignAvailable() {
+ return context.isFocusOnClusterBuild() && !isHostAssigned();
+ }
+
+ /**
+ * Tries to auto associate hosts to host groups.
+ *
+ * @return prints the auto assignments
+ */
+ @CliCommand(value = "cluster autoAssign", help = "Automatically assigns hosts to different host groups base on the provided strategy")
+ public String autoAssign() {
+ Map<String, List<String>> assignments = client.recommendAssignments(context.getFocusValue());
+ if (!assignments.isEmpty()) {
+ hostGroups = assignments;
+ context.setHint(Hints.CREATE_CLUSTER);
+ }
+ return showAssignments();
+ }
+
+ /**
+ * Checks whether the cluster preview command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("cluster preview")
+ public boolean isClusterPreviewCommandAvailable() {
+ return context.isFocusOnClusterBuild() && isHostAssigned();
+ }
+
+ /**
+ * Shows the currently assigned hosts.
+ *
+ * @return formatted host - host group table
+ */
+ @CliCommand(value = "cluster preview", help = "Shows the currently assigned hosts")
+ public String showAssignments() {
+ return renderMultiValueMap(hostGroups, "HOSTGROUP", "HOST");
+ }
+
+ /**
+ * Checks whether the cluster create command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("cluster create")
+ public boolean isCreateClusterCommandAvailable() {
+ return context.isFocusOnClusterBuild() && isHostAssigned();
+ }
+
+ /**
+ * Creates a new cluster based on the provided host - host group associations and the selected blueprint.
+ * If the cluster creation fails, deletes the cluster.
+ *
+ * @return status message
+ */
+ @CliCommand(value = "cluster create", help = "Create a cluster based on current blueprint and assigned hosts")
+ public String createCluster(
+ @CliOption(key = "exitOnFinish", mandatory = false, help = "Quits the shell when the cluster creation finishes") Boolean exit) {
+ String message = "Successfully created the cluster";
+ String blueprint = context.getFocusValue();
+ try {
+ client.createCluster(blueprint, blueprint, hostGroups);
+ context.setCluster(blueprint);
+ context.resetFocus();
+ context.setHint(Hints.PROGRESS);
+ flashService.showInstallProgress(exit == null ? false : exit);
+ } catch (HttpResponseException e) {
+ createNewHostGroups();
+ message = "Failed to create the cluster: " + e.getMessage();
+ try {
+ deleteCluster(blueprint);
+ } catch (HttpResponseException e1) {
+ message += ". Failed to cleanup cluster creation: " + e1.getMessage();
+ }
+ }
+ return message;
+ }
+
+ /**
+ * Checks whether the cluster delete command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("cluster delete")
+ public boolean isDeleteClusterCommandAvailable() {
+ return context.isConnectedToCluster();
+ }
+
+ /**
+ * Deletes the cluster.
+ *
+ * @return status message
+ */
+ @CliCommand(value = "cluster delete", help = "Delete the cluster")
+ public String deleteCluster() {
+ String message = "Successfully deleted the cluster";
+ try {
+ deleteCluster(context.getCluster());
+ } catch (HttpResponseException e) {
+ message = "Could not delete the cluster: " + e.getMessage();
+ }
+ return message;
+ }
+
+ /**
+ * Checks whether the cluster reset command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator(value = "cluster reset")
+ public boolean isClusterResetCommandAvailable() {
+ return context.isFocusOnClusterBuild() && isHostAssigned();
+ }
+
+ @CliCommand(value = "cluster reset", help = "Clears the host - host group assignments")
+ public void reset() {
+ context.setHint(Hints.ASSIGN_HOSTS);
+ createNewHostGroups();
+ }
+
+ private void deleteCluster(String id) throws HttpResponseException {
+ client.deleteCluster(id);
+ }
+
+ private void createNewHostGroups() {
+ Map<String, List<String>> groups = new HashMap<String, List<String>>();
+ for (String hostGroup : client.getHostGroups(context.getFocusValue())) {
+ groups.put(hostGroup, new ArrayList<String>());
+ }
+ this.hostGroups = groups;
+ }
+
+ private boolean addHostToGroup(String host, String group) {
+ boolean result = true;
+ List<String> hosts = hostGroups.get(group);
+ if (hosts == null) {
+ result = false;
+ } else {
+ hosts.add(host);
+ }
+ return result;
+ }
+
+ private boolean isHostAssigned() {
+ boolean result = false;
+ for (String group : hostGroups.keySet()) {
+ if (!hostGroups.get(group).isEmpty()) {
+ result = true;
+ break;
+ }
+ }
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/ElephantCommand.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/ElephantCommand.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/ElephantCommand.java
new file mode 100644
index 0000000..a236054
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/ElephantCommand.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.shell.commands;
+
+import java.io.IOException;
+
+import org.apache.commons.io.IOUtils;
+import org.springframework.shell.core.CommandMarker;
+import org.springframework.shell.core.annotation.CliAvailabilityIndicator;
+import org.springframework.shell.core.annotation.CliCommand;
+import org.springframework.stereotype.Component;
+
+/**
+ * Draws an elephant to the console.
+ */
+@Component
+public class ElephantCommand implements CommandMarker {
+
+ /**
+ * Checks whether the hello command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("hello")
+ public boolean isCommandAvailable() {
+ return true;
+ }
+
+ /**
+ * Prints an elephant to the console.
+ *
+ * @return elephant
+ */
+ @CliCommand(value = "hello", help = "Prints a simple elephant to the console")
+ public String elephant() throws IOException {
+ return IOUtils.toString(getClass().getResourceAsStream("/elephant.txt"));
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/HostCommands.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/HostCommands.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/HostCommands.java
new file mode 100644
index 0000000..dd3c775
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/HostCommands.java
@@ -0,0 +1,119 @@
+/**
+ * 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.shell.commands;
+
+import static org.apache.ambari.shell.support.TableRenderer.renderSingleMap;
+
+import org.apache.ambari.groovy.client.AmbariClient;
+import org.apache.ambari.shell.completion.Host;
+import org.apache.ambari.shell.model.AmbariContext;
+import org.apache.ambari.shell.model.FocusType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.shell.core.CommandMarker;
+import org.springframework.shell.core.annotation.CliAvailabilityIndicator;
+import org.springframework.shell.core.annotation.CliCommand;
+import org.springframework.shell.core.annotation.CliOption;
+import org.springframework.stereotype.Component;
+
+/**
+ * Host related commands used in the shell.
+ *
+ * @see org.apache.ambari.groovy.client.AmbariClient
+ */
+@Component
+public class HostCommands implements CommandMarker {
+
+ private AmbariClient client;
+ private AmbariContext context;
+
+ @Autowired
+ public HostCommands(AmbariClient client, AmbariContext context) {
+ this.client = client;
+ this.context = context;
+ }
+
+ /**
+ * Checks whether the host list command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("host list")
+ public boolean isHostsCommandAvailable() {
+ return true;
+ }
+
+ /**
+ * Prints the available hosts of the Ambari Server.
+ *
+ * @return host list
+ */
+ @CliCommand(value = "host list", help = "Lists the available hosts")
+ public String hosts() {
+ return client.showHostList();
+ }
+
+ /**
+ * Checks whether the host focus command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("host focus")
+ public boolean isFocusHostCommandAvailable() {
+ return context.isConnectedToCluster();
+ }
+
+ /**
+ * Sets the focus to the specified host.
+ *
+ * @param host the host to set the focus to
+ * @return status message
+ */
+ @CliCommand(value = "host focus", help = "Sets the useHost to the specified host")
+ public String focusHost(
+ @CliOption(key = "host", mandatory = true, help = "hostname") Host host) {
+ String message;
+ String hostName = host.getName();
+ if (client.getHostNames().keySet().contains(hostName)) {
+ context.setFocus(hostName, FocusType.HOST);
+ message = "Focus set to: " + hostName;
+ } else {
+ message = hostName + " is not a valid host name";
+ }
+ return message;
+ }
+
+ /**
+ * Checks whether the host components command is available or not.
+ *
+ * @return true if available false otherwise
+ */
+ @CliAvailabilityIndicator("host components")
+ public boolean isHostComponentsCommandAvailable() {
+ return context.isFocusOnHost();
+ }
+
+ /**
+ * Prints the components which belongs to the host being focused on.
+ *
+ * @return list of host components
+ */
+ @CliCommand(value = "host components", help = "Lists the components assigned to the selected host")
+ public String hostComponents() {
+ return renderSingleMap(client.getHostComponentsMap(context.getFocusValue()), "COMPONENT", "STATE");
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/completion/Blueprint.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/completion/Blueprint.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/completion/Blueprint.java
new file mode 100644
index 0000000..4eec7b1
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/completion/Blueprint.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.shell.completion;
+
+/**
+ * Wrapper class for TAB completion to blueprint names.
+ */
+public class Blueprint {
+
+ private final String name;
+
+ public Blueprint(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/completion/Host.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/completion/Host.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/completion/Host.java
new file mode 100644
index 0000000..f64e97b
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/completion/Host.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.shell.completion;
+
+/**
+ * Wrapper class for TAB completion to host names.
+ */
+public class Host {
+
+ private final String name;
+
+ public Host(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/configuration/ConverterConfiguration.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/configuration/ConverterConfiguration.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/configuration/ConverterConfiguration.java
new file mode 100644
index 0000000..049c52f
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/configuration/ConverterConfiguration.java
@@ -0,0 +1,142 @@
+/**
+ * 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.shell.configuration;
+
+import org.apache.ambari.groovy.client.AmbariClient;
+import org.apache.ambari.shell.converter.BlueprintConverter;
+import org.apache.ambari.shell.converter.HostConverter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.shell.converters.AvailableCommandsConverter;
+import org.springframework.shell.converters.BigDecimalConverter;
+import org.springframework.shell.converters.BigIntegerConverter;
+import org.springframework.shell.converters.BooleanConverter;
+import org.springframework.shell.converters.CharacterConverter;
+import org.springframework.shell.converters.DateConverter;
+import org.springframework.shell.converters.DoubleConverter;
+import org.springframework.shell.converters.EnumConverter;
+import org.springframework.shell.converters.FloatConverter;
+import org.springframework.shell.converters.IntegerConverter;
+import org.springframework.shell.converters.LocaleConverter;
+import org.springframework.shell.converters.LongConverter;
+import org.springframework.shell.converters.ShortConverter;
+import org.springframework.shell.converters.SimpleFileConverter;
+import org.springframework.shell.converters.StaticFieldConverterImpl;
+import org.springframework.shell.converters.StringConverter;
+import org.springframework.shell.core.Converter;
+
+/**
+ * Configures the converters used by the shell.
+ */
+@Configuration
+public class ConverterConfiguration {
+
+ @Autowired
+ private AmbariClient client;
+
+ @Bean
+ Converter simpleFileConverter() {
+ return new SimpleFileConverter();
+ }
+
+ @Bean
+ Converter stringConverter() {
+ return new StringConverter();
+ }
+
+ @Bean
+ Converter availableCommandsConverter() {
+ return new AvailableCommandsConverter();
+ }
+
+ @Bean
+ Converter bigDecimalConverter() {
+ return new BigDecimalConverter();
+ }
+
+ @Bean
+ Converter bigIntegerConverter() {
+ return new BigIntegerConverter();
+ }
+
+ @Bean
+ Converter booleanConverter() {
+ return new BooleanConverter();
+ }
+
+ @Bean
+ Converter characterConverter() {
+ return new CharacterConverter();
+ }
+
+ @Bean
+ Converter dateConverter() {
+ return new DateConverter();
+ }
+
+ @Bean
+ Converter doubleConverter() {
+ return new DoubleConverter();
+ }
+
+ @Bean
+ Converter enumConverter() {
+ return new EnumConverter();
+ }
+
+ @Bean
+ Converter floatConverter() {
+ return new FloatConverter();
+ }
+
+ @Bean
+ Converter integerConverter() {
+ return new IntegerConverter();
+ }
+
+ @Bean
+ Converter localeConverter() {
+ return new LocaleConverter();
+ }
+
+ @Bean
+ Converter longConverter() {
+ return new LongConverter();
+ }
+
+ @Bean
+ Converter shortConverter() {
+ return new ShortConverter();
+ }
+
+ @Bean
+ Converter staticFieldConverterImpl() {
+ return new StaticFieldConverterImpl();
+ }
+
+ @Bean
+ Converter blueprintConverter() {
+ return new BlueprintConverter(client);
+ }
+
+ @Bean
+ Converter hostConverter() {
+ return new HostConverter(client);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/configuration/ShellConfiguration.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/configuration/ShellConfiguration.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/configuration/ShellConfiguration.java
new file mode 100644
index 0000000..4c493b0
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/configuration/ShellConfiguration.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.shell.configuration;
+
+import org.apache.ambari.groovy.client.AmbariClient;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
+import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean;
+import org.springframework.shell.CommandLine;
+import org.springframework.shell.SimpleShellCommandLineOptions;
+import org.springframework.shell.commands.ExitCommands;
+import org.springframework.shell.commands.HelpCommands;
+import org.springframework.shell.commands.ScriptCommands;
+import org.springframework.shell.commands.VersionCommands;
+import org.springframework.shell.core.CommandMarker;
+import org.springframework.shell.core.JLineShellComponent;
+import org.springframework.shell.plugin.HistoryFileNameProvider;
+import org.springframework.shell.plugin.support.DefaultHistoryFileNameProvider;
+
+/**
+ * Spring bean definitions.
+ */
+@Configuration
+public class ShellConfiguration {
+
+ @Value("${ambari.host:localhost}")
+ private String host;
+
+ @Value("${ambari.port:8080}")
+ private String port;
+
+ @Value("${ambari.user:admin}")
+ private String user;
+
+ @Value("${ambari.password:admin}")
+ private String password;
+
+ @Value("${cmdfile:}")
+ private String cmdFile;
+
+ @Bean
+ AmbariClient createAmbariClient() {
+ return new AmbariClient(host, port, user, password);
+ }
+
+ @Bean
+ static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
+ return new PropertySourcesPlaceholderConfigurer();
+ }
+
+ @Bean
+ HistoryFileNameProvider defaultHistoryFileNameProvider() {
+ return new DefaultHistoryFileNameProvider();
+ }
+
+ @Bean(name = "shell")
+ JLineShellComponent shell() {
+ return new JLineShellComponent();
+ }
+
+ @Bean
+ CommandLine commandLine() throws Exception {
+ String[] args = cmdFile.length() > 0 ? new String[]{"--cmdfile", cmdFile} : new String[0];
+ return SimpleShellCommandLineOptions.parseCommandLine(args);
+ }
+
+ @Bean
+ ThreadPoolExecutorFactoryBean getThreadPoolExecutorFactoryBean() {
+ return new ThreadPoolExecutorFactoryBean();
+ }
+
+ @Bean
+ ObjectMapper getObjectMapper() {
+ return new ObjectMapper();
+ }
+
+ @Bean
+ CommandMarker exitCommand() {
+ return new ExitCommands();
+ }
+
+ @Bean
+ CommandMarker versionCommands() {
+ return new VersionCommands();
+ }
+
+ @Bean
+ CommandMarker helpCommands() {
+ return new HelpCommands();
+ }
+
+ @Bean
+ CommandMarker scriptCommands() {
+ return new ScriptCommands();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/converter/BlueprintConverter.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/converter/BlueprintConverter.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/converter/BlueprintConverter.java
new file mode 100644
index 0000000..7984e7f
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/converter/BlueprintConverter.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.shell.converter;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ambari.groovy.client.AmbariClient;
+import org.apache.ambari.shell.completion.Blueprint;
+import org.springframework.shell.core.Completion;
+import org.springframework.shell.core.Converter;
+import org.springframework.shell.core.MethodTarget;
+
+/**
+ * Converter used to complete blueprint names.
+ */
+public class BlueprintConverter implements Converter<Blueprint> {
+
+ private AmbariClient client;
+
+ public BlueprintConverter(AmbariClient client) {
+ this.client = client;
+ }
+
+ @Override
+ public boolean supports(Class<?> type, String optionContext) {
+ return Blueprint.class.isAssignableFrom(type);
+ }
+
+ @Override
+ public Blueprint convertFromText(String value, Class<?> targetType, String optionContext) {
+ return new Blueprint(value);
+ }
+
+ @Override
+ public boolean getAllPossibleValues(List<Completion> completions, Class<?> targetType, String existingData, String optionContext, MethodTarget target) {
+ Set<String> blueprints = client.getBlueprintsMap().keySet();
+ for (String blueprint : blueprints) {
+ completions.add(new Completion(blueprint));
+ }
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/converter/HostConverter.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/converter/HostConverter.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/converter/HostConverter.java
new file mode 100644
index 0000000..39aa6e9
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/converter/HostConverter.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.shell.converter;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ambari.groovy.client.AmbariClient;
+import org.apache.ambari.shell.completion.Host;
+import org.springframework.shell.core.Completion;
+import org.springframework.shell.core.Converter;
+import org.springframework.shell.core.MethodTarget;
+
+/**
+ * Converter used to complete host names.
+ */
+public class HostConverter implements Converter<Host> {
+
+ private AmbariClient client;
+
+ public HostConverter(AmbariClient client) {
+ this.client = client;
+ }
+
+ @Override
+ public boolean supports(Class<?> type, String optionContext) {
+ return Host.class.isAssignableFrom(type);
+ }
+
+ @Override
+ public Host convertFromText(String value, Class<?> targetType, String optionContext) {
+ return new Host(value);
+ }
+
+ @Override
+ public boolean getAllPossibleValues(List<Completion> completions, Class<?> targetType, String existingData, String optionContext, MethodTarget target) {
+ Set<String> hosts = client.getHostNames().keySet();
+ for (String host : hosts) {
+ completions.add(new Completion(host));
+ }
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/customization/AmbariBanner.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/customization/AmbariBanner.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/customization/AmbariBanner.java
new file mode 100644
index 0000000..f97ef59
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/customization/AmbariBanner.java
@@ -0,0 +1,50 @@
+/**
+ * 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.shell.customization;
+
+import org.springframework.shell.plugin.BannerProvider;
+import org.springframework.stereotype.Component;
+
+import com.github.lalyos.jfiglet.FigletFont;
+
+/**
+ * Prints the banner when the user starts the shell.
+ */
+@Component
+public class AmbariBanner implements BannerProvider {
+
+ @Override
+ public String getProviderName() {
+ return "AmbariShell";
+ }
+
+ @Override
+ public String getBanner() {
+ return FigletFont.convertOneLine("AmbariShell");
+ }
+
+ @Override
+ public String getVersion() {
+ return getClass().getPackage().getImplementationVersion();
+ }
+
+ @Override
+ public String getWelcomeMessage() {
+ return "Welcome to Ambari Shell. For command and param completion press TAB, for assistance type 'hint'.";
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/customization/AmbariHistory.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/customization/AmbariHistory.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/customization/AmbariHistory.java
new file mode 100644
index 0000000..c23fc9a
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/customization/AmbariHistory.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.shell.customization;
+
+import org.springframework.shell.plugin.HistoryFileNameProvider;
+import org.springframework.stereotype.Component;
+
+/**
+ * Specifies the name of the Ambari command log. Later this log can be used
+ * to re-execute the commands with either the --cmdfile option at startup
+ * or with the script --file command.
+ */
+@Component
+public class AmbariHistory implements HistoryFileNameProvider {
+
+ @Override
+ public String getHistoryFileName() {
+ return "ambari-log.ash";
+ }
+
+ @Override
+ public String getProviderName() {
+ return "AmbariShell";
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/customization/AmbariPrompt.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/customization/AmbariPrompt.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/customization/AmbariPrompt.java
new file mode 100644
index 0000000..b91e76e
--- /dev/null
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/customization/AmbariPrompt.java
@@ -0,0 +1,43 @@
+/**
+ * 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.shell.customization;
+
+import org.apache.ambari.shell.model.AmbariContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.shell.plugin.PromptProvider;
+import org.springframework.stereotype.Component;
+
+/**
+ * Manages the text of the shell's prompt.
+ */
+@Component
+public class AmbariPrompt implements PromptProvider {
+
+ @Autowired
+ private AmbariContext context;
+
+ @Override
+ public String getProviderName() {
+ return AmbariPrompt.class.getSimpleName();
+ }
+
+ @Override
+ public String getPrompt() {
+ return context.getPrompt();
+ }
+}