Return-Path: X-Original-To: apmail-ambari-commits-archive@www.apache.org Delivered-To: apmail-ambari-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id D518311F62 for ; Wed, 2 Jul 2014 17:36:49 +0000 (UTC) Received: (qmail 38152 invoked by uid 500); 2 Jul 2014 17:36:49 -0000 Delivered-To: apmail-ambari-commits-archive@ambari.apache.org Received: (qmail 38062 invoked by uid 500); 2 Jul 2014 17:36:49 -0000 Mailing-List: contact commits-help@ambari.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: ambari-dev@ambari.apache.org Delivered-To: mailing list commits@ambari.apache.org Received: (qmail 37902 invoked by uid 99); 2 Jul 2014 17:36:49 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 02 Jul 2014 17:36:49 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 4F6EC9940F1; Wed, 2 Jul 2014 17:36:49 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: subin@apache.org To: commits@ambari.apache.org Date: Wed, 02 Jul 2014 17:36:50 -0000 Message-Id: <096829420ce74beaa092b90eb9c2b2df@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [2/4] AMBARI-6176. Integrate python shell into Ambari-shell module(subin) http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/ambari_shell.py ---------------------------------------------------------------------- diff --git a/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/ambari_shell.py b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/ambari_shell.py new file mode 100644 index 0000000..79af1a6 --- /dev/null +++ b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/ambari_shell.py @@ -0,0 +1,412 @@ +#!/usr/bin/env python +# +# 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. + + +import logging +import logging.handlers +import sys +import signal +import json +import time +import pdb +import os +import stat +import cmd +import string +import bz2 +import datetime +# import traceback +import getpass +import argparse +import readline +import ConfigParser +import StringIO +import subprocess +import textwrap + +import utils.displayutils +import utils.osutils +import utils.pluginutils +from ambari_client.ambari_api import AmbariClient + + +configFile = "/etc/ambari-shell/conf/ambari-shell.ini" + + +formatstr = "%(levelname)s %(asctime)s %(filename)s:%(lineno)d - %(message)s" +LOG_MAX_BYTES = 10000000 +LOG_BACKUP = 2 + +SHELL_CONFIG = { + 'clustername': None, + 'hostname': 'localhost', + 'port': 8080, + 'username': 'admin', + 'password': 'admin', + 'display_type': 'table', + 'client': None, + 'configFile': configFile} + +######################################################################## +# +# Utility methods +# +######################################################################## + + +def exit_gracefully(signum, frame): + # restore the original signal handler as otherwise evil things will happen + # in raw_input when CTRL+C is pressed, and our signal handler is not + # re-entrant + signal.signal(signal.SIGINT, original_sigint) + print "\nExiting" + sys.exit(1) + + # restore the exit gracefully handler here + signal.signal(signal.SIGINT, exit_gracefully) + + +def exit_gracefully1(signum, frame): + # restore the original signal handler as otherwise evil things will happen + # in raw_input when CTRL+C is pressed, and our signal handler is not + # re-entrant + signal.signal(signal.SIGQUIT, original_sigint) + print "\nExiting" + sys.exit(1) + + # restore the exit gracefully handler here + signal.signal(signal.SIGQUIT, exit_gracefully) + + +def resolve_config(): + try: + config = ConfigParser.RawConfigParser() + if os.path.exists(configFile): + print "looking from " + configFile + config.read(configFile) + else: + raise Exception("No config found") + except Exception as err: + logging.warn(err) + print "found " + configFile + return config + + +def get_log_level(loglevel): + loglev = loglevel.upper() + if loglev == "DEBUG": + return logging.DEBUG + elif loglev == "INFO": + return logging.INFO + elif loglev == "WARNING": + return logging.WARNING + elif loglev == "CRITICAL": + return logging.CRITICAL + elif loglev == "ERROR": + return logging.ERROR + elif loglev == "FATAL": + return logging.FATAL + else: + return logging.NOTSET + + +def setup_logging(loglevel, logPath="./"): + try: + logging.root + curTimestamp = str(datetime.datetime.now()) + ct = curTimestamp.split('.')[0] + curTime = ct.replace(':', '-') + datee = curTime.split(' ')[0] + timee = curTime.split(' ')[1] + # Set Log directory and log file name. Each run generates a new log + # file + + logFile = logPath + 'ambaricli_' + datee + "_" + timee + '.log' + fh = open(logFile, 'w') + fh.write('*****************************************************\n') + fh.write(' Amabri Python CLI Log\n') + t = ' Timestamp: ' + ct + '\n' + fh.write(t) + fh.write('*****************************************************\n\n\n') + fh.close() + # Set the config for logging + logging.basicConfig( + filename=logFile, + format='%(asctime)s : %(levelname)s: %(message)s', + level=get_log_level(loglevel)) + except IOError as e: + errStr = " I/O error({0}): {1}".format(e.errno, e.strerror) + print errStr + sys.exit(1) + except Exception as exception: + print exception + sys.exit(1) + + +def getLogLevel(configg): + loglevel = "debug" + try: + loglevel = configg.get('python_shell', 'loglevel') + except Exception: + logging.error("No loglevel found ") + return loglevel + return loglevel + + +def getLogPath(configg): + logPath = "./" + try: + logPath = configg.get('python_shell', 'log_folder') + except Exception: + logging.error("No log_folder found") + return logPath + return logPath + + +def getPluginPath(configg): + cliplugin_path = "./" + try: + cliplugin_path = configg.get('python_shell', 'cliplugin_folder') + except Exception: + logging.error("No cliplugin_folder found") + return cliplugin_path + return cliplugin_path + + +def getDefaultPluginPath(configg): + cliplugin_path = "./" + try: + cliplugin_path = configg.get('python_shell', 'default_plugin_folder') + except Exception: + logging.error("No default_plugin_folder found") + return cliplugin_path + return cliplugin_path + + +def getCommandsDict(ppath, cliplugin_path): + default_dictt = utils.pluginutils.getPlugins(ppath) + logging.debug("pluginutils returned default plugins >> %s ", default_dictt) + dictt = utils.pluginutils.getPlugins(cliplugin_path) + logging.debug("pluginutils returned >> %s ", dictt) + if(not set(default_dictt).isdisjoint(set(dictt))): + common_commands = set(default_dictt).intersection(set(dictt)) + common_commands = " & ".join(str(x) for x in common_commands) + logging.error( + "ERROR :plugins folder has duplicate commands already present in default commands") + logging.error(common_commands) + print "ERROR :plugins folder has duplicate command already present in default commands" + print "pls remove following commands from plugin folder >" + str(common_commands) + sys.exit(1) + default_dictt.update(dictt) + return default_dictt + + +class CmdBase(cmd.Cmd): + + """CLI . + """ + + intro = utils.displayutils.shellBanner() + prompt = 'ambari>' + http_proxy = '' + https_proxy = '' + # headers + doc_header = "Commands" + undoc_header = "Other Commands" + + def __init__(self): + cmd.Cmd.__init__(self) + + def do_EOF(self, line): + logging.info("====== do_EOF ======") + return True + + def do_exit(self, line): + logging.info("====== do_exit ======") + return True + + def postloop(self): + logging.info("====== exit ======") + +# def parseline(self, line): +# print 'parseline(%s) =>' % line, +# ret = cmd.Cmd.parseline(self, line) +# print ret +# return ret + + def emptyline(self): + # print 'emptyline()' + # return cmd.Cmd.emptyline(self) + return + + def default(self, line): + """Called on an input line when the command prefix is not recognized. + + If this method is not overridden, it prints an error message and + returns. + + """ + self.stdout.write( + '*** Unknown command *** : %s \n type "help" to list all commands \n' % + line) + + +class AmbariShell(CmdBase): + + COMPONENTS = {} + SERVICES = None + CLUSTERS = None + + def __init__(self): + CmdBase.__init__(self) + self.config = None + self.global_shell_config = SHELL_CONFIG + + def preloop(self): + "Checks if the cluster was pre-defined" + self._set_prompt() + + def _set_prompt(self): + if self.global_shell_config['clustername']: + self.prompt = "ambari-" + \ + str(self.global_shell_config['clustername']) + ">" + logging.debug("found a cluster >" + + str(self.global_shell_config['clustername'])) + else: + self.prompt = 'ambari>' + + def postcmd(self, stop, line): + # print 'postcmd(%s, %s)' % (stop, line) + self._set_prompt() + return cmd.Cmd.postcmd(self, stop, line) + + def setConfig(self, customConfig): + self.config = customConfig + + # core code should begin here + + def generate_output(self, headers, rows): + if self.global_shell_config['display_type'] == "table": + print utils.displayutils.display_table(headers, rows) + + if self.global_shell_config['display_type'] == "csv": + utils.displayutils.createCSV(headers, rows) + + if self.global_shell_config['display_type'] == "xml": + print utils.displayutils.createXML(headers, rows) + +# +# The "main" function + + +def main(): + parser = argparse.ArgumentParser(description='Ambari CLI') + parser.add_argument('-H', '--host', action='store', dest='host') + parser.add_argument( + '-p', + '--port', + action='store', + dest='port', + type=int, + default=8080) + parser.add_argument( + '-u', + '--user', + action='store', + dest='user', + default='admin') + parser.add_argument( + '-c', + '--clustername', + action='store', + dest='clustername') + parser.add_argument( + '--password', + action='store', + dest='password', + default='admin') + parser.add_argument( + '-d', + '--display_type', + action='store', + dest='display_type', + default='table') + parser.add_argument('-r', '--run', action='store', dest='run') + args = parser.parse_args() + + # Check if a username was suplied, if not, prompt the user + if not args.host: + args.host = raw_input("Enter Ambari Server host: ") + + if args.host: + SHELL_CONFIG['hostname'] = args.host + if args.clustername: + SHELL_CONFIG['clustername'] = args.clustername + if args.display_type: + SHELL_CONFIG['display_type'] = args.display_type + + headers_dict = {'X-Requested-By': 'mycompany'} + client = AmbariClient( + SHELL_CONFIG['hostname'], + SHELL_CONFIG['port'], + SHELL_CONFIG['username'], + SHELL_CONFIG['password'], + version=1, + http_header=headers_dict) + SHELL_CONFIG['client'] = client + + # do some plumbing + config = resolve_config() + logPath = getLogPath(config) + loglevel = getLogLevel(config) + cliplugin_path = getPluginPath(config) + dpath = getDefaultPluginPath(config) + setup_logging(loglevel, logPath) + # get ready to create a shell + utils.osutils.doclearScreen() + shell = AmbariShell() + logging.info("cliplugin_folder = %s", getPluginPath(config)) + logging.info("SHELL_CONFIG = %s", str(SHELL_CONFIG)) + # Get all commands + + commands_dictt = getCommandsDict(dpath, cliplugin_path) + for k, v in commands_dictt.items(): + setattr(AmbariShell, str(k), v) + shell.setConfig(config) + + # Check if user is attempting non-interactive shell + if args.run: + print args.run + for command in args.run.split(';'): + shell.onecmd(command) + sys.exit(0) + else: + try: + shell.cmdloop() + except KeyboardInterrupt: + sys.stdout.write("\n") + sys.exit(0) + logging.info("finished") + + +if __name__ == '__main__': + original_sigint = signal.getsignal(signal.SIGINT) + signal.signal(signal.SIGINT, exit_gracefully) + signal.signal(signal.SIGQUIT, exit_gracefully1) + signal.signal(signal.SIGTERM, exit_gracefully1) + main() http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/__init__.py ---------------------------------------------------------------------- diff --git a/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/__init__.py b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/__init__.py new file mode 100644 index 0000000..278df2e --- /dev/null +++ b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/__init__.py @@ -0,0 +1,16 @@ +# +# 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. http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/blueprint.py ---------------------------------------------------------------------- diff --git a/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/blueprint.py b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/blueprint.py new file mode 100644 index 0000000..4d65163 --- /dev/null +++ b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/blueprint.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# +# 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. +import logging +import textwrap + +LOG = logging.getLogger(__name__) http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/connect_cluster.py ---------------------------------------------------------------------- diff --git a/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/connect_cluster.py b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/connect_cluster.py new file mode 100644 index 0000000..c28d493 --- /dev/null +++ b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/connect_cluster.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# +# 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. +import logging +import textwrap + +LOG = logging.getLogger(__name__) + + +def do_connect_cluster(self, cluster): + + if not cluster: + self.help_connect_cluster() + return None + + if not self.CLUSTERS: + clusters = [str(c.cluster_name).encode('ascii', 'ignore') + for c in self.global_shell_config['client'].get_all_clusters()] + self.CLUSTERS = clusters + + if cluster not in self.CLUSTERS: + print "ERROR ! cluster %s not found " % str(cluster) + print " valid clusters are " + str(self.CLUSTERS) + return None + if self.global_shell_config['clustername']: + LOG.debug("old cluster = " + self.global_shell_config['clustername']) + self.global_shell_config['clustername'] = str(cluster) + else: + self.global_shell_config['clustername'] = cluster + self.prompt = "ambari-" + \ + str(self.global_shell_config['clustername']) + ">" + + +def help_connect_cluster(self): + print '\n'.join([' Usage:', ' connect_cluster ']) + + +def do_disconnect_cluster(self, line): + + if self.global_shell_config['clustername']: + LOG.debug("old cluster = " + self.global_shell_config['clustername']) + self.global_shell_config['clustername'] = None + self.prompt = "ambari>" + + +def help_disconnect_cluster(self): + print '\n'.join([' Usage:', ' disconnect_cluster']) + + +def complete_connect_cluster(self, pattern, line, start_index, end_index): + if not self.CLUSTERS: + clusters = [ + (c.cluster_name).encode( + 'ascii', + 'ignore') for c in self.global_shell_config['client'].get_all_clusters()] + self.CLUSTERS = clusters + + LOG.debug( + ("self.CLUSTERS = %s pattern = %s ") % + (str( + self.CLUSTERS), + pattern)) + if pattern: + return [ + c for c in self.CLUSTERS if c.startswith(pattern)] + else: + return self.CLUSTERS http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/create_cluster.py ---------------------------------------------------------------------- diff --git a/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/create_cluster.py b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/create_cluster.py new file mode 100644 index 0000000..f9947d7 --- /dev/null +++ b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/create_cluster.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# +# 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. + + +import logging +import textwrap + + +LOG = logging.getLogger(__name__) + + +def do_create_cluster(self, line): + # logging = self.LOG + print "TODO" + self.prompt = "ambari>" + + +def help_create_cluster(self): + print '\n'.join([' Usage:', ' create_cluster ']) http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/service.py ---------------------------------------------------------------------- diff --git a/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/service.py b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/service.py new file mode 100644 index 0000000..d44769a --- /dev/null +++ b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/service.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +# +# 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. + + +import logging +import textwrap + + +LOG = logging.getLogger(__name__) + + +def do_start_service(self, service): + if not service: + self.help_stop_service() + return None + clustername = self.global_shell_config['clustername'] + if not clustername: + print("Error! No cluster currently selected") + return None + + if self.t_service_action(service=service, action="start"): + print("%s is being started" % (service)) + else: + print("Error! cannot start service") + return + + +def do_stop_service(self, service): + if not service: + self.help_start_service() + return None + + clustername = self.global_shell_config['clustername'] + if not clustername: + print("Error! No cluster currently selected") + return None + + if self.t_service_action(service=service, action="stop"): + print("%s is being stopped" % (service)) + else: + print("Error! cannot stop service") + return + + +def help_stop_service(self): + print textwrap.dedent(""" + Usage: + > stop_service + """) + + +def help_start_service(self): + print textwrap.dedent(""" + Usage: + > start_service + """) + + +def complete_start_service(self, pattern, line, start_index, end_index): + return self.services_complete(pattern, line, start_index, end_index) + + +def complete_stop_service(self, pattern, line, start_index, end_index): + client = self.global_shell_config['client'] + clustername = self.global_shell_config['clustername'] + if not clustername: + return None + else: + if not self.SERVICES: + services = [ + s.service_name for s in client.get_cluster(clustername).get_all_services()] + self.SERVICES = services + + if pattern: + return [s for s in self.SERVICES if s.startswith(pattern)] + else: + return self.SERVICES + + +def services_complete(self, pattern, line, start_index, end_index, append=[]): + client = self.global_shell_config['client'] + clustername = self.global_shell_config['clustername'] + if not clustername: + return None + else: + if not self.SERVICES: + services = [ + s.service_name for s in client.get_cluster(clustername).get_all_services()] + self.SERVICES = services + + if pattern: + return [s for s in self.SERVICES + append if s.startswith(pattern)] + else: + return self.SERVICES + append + + +def t_service_action(self, service, action): + try: + client = self.global_shell_config['client'] + cluster = self.global_shell_config['clustername'] + service = client.get_cluster(cluster).get_service(service) + except Exception: + print("Service not found") + return None + + if action == "start": + service.start(message='started by Ambari python CLI') + if action == "stop": + service.stop(message='stopped by Ambari python CLI') + return True http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/shell_config.py ---------------------------------------------------------------------- diff --git a/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/shell_config.py b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/shell_config.py new file mode 100644 index 0000000..2facd02 --- /dev/null +++ b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/shell_config.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# +# 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. +import logging +import textwrap + +LOG = logging.getLogger(__name__) + + +def do_get_shell_config(self, config_name): + rows = [] + headers = ["KEY", "VAlUE"] + if not config_name: + for i in self.global_shell_config.items(): + rows.append([i[0], i[1]]) + else: + if config_name in self.global_shell_config.keys(): + rows.append([config_name, self.global_shell_config[config_name]]) + + self.generate_output(headers, rows) + + +def do_set_shell_config(self, config=None): + kv = config.split(" ") + if len(kv) != 2: + self.help_set_shell_config() + return + config_name = kv[0] + config_value = kv[1] + if config_name in self.global_shell_config.keys(): + self.global_shell_config[config_name] = config_value + + self.do_get_shell_config(config_name=None) + + +def help_get_shell_config(self): + print textwrap.dedent(""" + Usage: + > get_shell_config get all shell config + """) + + +def help_set_shell_config(self): + print textwrap.dedent(""" + Usage: + > set_shell_config sets shell config + """) + + +def complete_get_shell_config(self, pattern, line, start_index, end_index): + if pattern: + return [ + c for c in self.global_shell_config.keys() if c.startswith(pattern)] + else: + return self.CLUSTERS + + +def complete_set_shell_config(self, pattern, line, start_index, end_index): + if pattern: + return [ + c for c in self.global_shell_config.keys() if c.startswith(pattern)] + else: + return self.CLUSTERS http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/show.py ---------------------------------------------------------------------- diff --git a/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/show.py b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/show.py new file mode 100644 index 0000000..584b40a --- /dev/null +++ b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/plugins/show.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# +# 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. + + +import logging +import textwrap + +LOG = logging.getLogger(__name__) + + +def help_show(self): + print textwrap.dedent(""" + Usage: + > show clusters list clusters + > show hosts list hosts + > show stacks list stacks + > show services list services + > show requests list previous requests + > show blueprints list blueprints + """) + + +def do_show(self, option): + """ + Usage: + > show clusters list clusters + > show hosts list hosts + > show stacks list stacks + > show services list services + > show requests list previous requests + > show blueprints list blueprints + """ + headers = [] + rows = [] + options = [ + "clusters", + "stacks", + "status", + "services", + "hosts", + "requests", + "blueprints"] + + if not option: + self.help_show() + return + + if option not in options: + self.default(option) + return + + client = self.global_shell_config['client'] + clustername = self.global_shell_config['clustername'] + # show clusters + if option == "clusters": + "Display list of clusters on system" + headers = ["CLUSTER NAME"] + clusters = client.get_all_clusters() + for cluster in clusters: + rows.append([cluster.cluster_name]) + + # show clusters + if option == "stacks": + "Display list of stacks on system" + headers = ["STACK NAME"] + stacks = client.get_stacks(True) + for stack in stacks: + rows.append([stack.stack_version]) + + if option == "blueprints": + "Display list of blueprints on system" + headers = ["BLUEPRINT NAME", "VERSION"] + blues = client.get_blueprint() + for b in blues: + rows.append([b.blueprint_name, b.stack_version]) + + if option == "status": + "Display list of stacks on system" + headers = ["HOST_NAME", "ROLE", "STATUS"] + if not clustername: + print("Error! No cluster currently selected") + return + else: + tasks = client.get_task_status(clustername, 2) + for task in tasks: + rows.append([task.host_name, task.role, task.status]) + + # show hosts + if option == "hosts": + "Display a list of hosts avaiable on the system" + headers = ["HOSTNAME", "IP ADDRESS"] + if not clustername: + for host in client.get_all_hosts(): + rows.append([host.host_name, host.ip]) + else: + c = client.get_cluster(clustername) + for host in c.get_all_hosts(): + rows.append([host.host_name, host.ip]) + + if option == "requests": + headers = ["REQUEST-ID", "STATUS"] + if not clustername: + print("Error! No cluster currently selected") + return + else: + c = client.get_cluster(clustername) + for req in client.get_requests(clustername): + rows.append([req.id, req.request_status]) + + # show services + if option == "services": + "Show list of services on the cluster" + headers = ["SERVICE", "STATUS"] + + if not clustername: + print("Error! No cluster currently selected") + return + else: + c = client.get_cluster(clustername) + for service in c.get_all_services(): + rows.append([service.service_name, service.state]) + + self.generate_output(headers, rows) + + +def complete_show(self, pattern, line, start_index, end_index): + show_commands = [ + "clusters", + "hosts", + "services", + "stacks", + "blueprints", + "requests"] + if pattern: + return [c for c in show_commands if c.startswith(pattern)] + else: + return show_commands + +if __name__ == '__main__': + do_show(None, None) http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/__init__.py ---------------------------------------------------------------------- diff --git a/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/__init__.py b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/__init__.py new file mode 100644 index 0000000..278df2e --- /dev/null +++ b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/__init__.py @@ -0,0 +1,16 @@ +# +# 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. http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/displayutils.py ---------------------------------------------------------------------- diff --git a/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/displayutils.py b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/displayutils.py new file mode 100644 index 0000000..3c45e7f --- /dev/null +++ b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/displayutils.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# +# 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. + +import logging +import textwrap +import cStringIO +import operator +from xml.etree import ElementTree as etree +from functools import reduce + + +LOG = logging.getLogger(__name__) + + +class bcolors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + + def disable(self): + self.HEADER = '' + self.OKBLUE = '' + self.OKGREEN = '' + self.WARNING = '' + self.FAIL = '' + + +__header__ = textwrap.dedent(""" + ___ __ _ + / _ | __ _ / / ___ _____(_) + / __ |/ ' \/ _ \/ _ `/ __/ / +/_/ |_/_/_/_/_.__/\_,_/_/ /_/ CLI v%s +""" % str(1)) + + +def shellBanner(): + """ + Prints the CLI Banner. + """ + return __header__ + textwrap.dedent( + """ + ==================================== + Welcome to Ambari python CLI + type 'help' to list all commands.. + ==================================== + """) + + +def createXML(headers, rows): + root = etree.Element('xmloutput') + for r in rows: + for h, relemt in zip(headers, r): + + child = etree.Element(h.lower().replace(' ', '_')) + child.text = str(relemt) + root.append(child) + + # pretty string + s = etree.tostring(root) + return s + + +def createCSV(headers, rows): + headers = [x.lower().replace(' ', '_') for x in headers] + print(','.join(headers)) + for r in rows: + print(','.join(r)) + + +def display_table(headers, rows): + + delimiter = '=' + delimiter1 = ' | ' + output = cStringIO.StringIO() + temp_rows = [tuple(headers)] + rows + row_tuple = [(row,) for row in temp_rows] + # get max width + verticalrows = map(None, *reduce(operator.add, row_tuple)) + + if not rows: + widthList = [len(str(x)) for x in headers] + row_width = sum(widthList) + else: + widthList = [max([len(str(x)) for x in column]) + for column in verticalrows] + row_width = sum(widthList) + header_line = delimiter * \ + (row_width + len(delimiter1) * (len(widthList) - 1)) + + i = 0 + for rr in row_tuple: + for row in rr: + print >> output, delimiter1.join( + [(str(x)).ljust(width) for (x, width) in zip(row, widthList)]) + if i == 0: + print >> output, header_line + i = 9999 + return output.getvalue() + + +if __name__ == '__main__': + print createXML(['STACK NAME', ], [[u'HDP']]) + createCSV(['STACK NAME', ], [[u'HDP']]) + headers = ['First Name', 'Last Name', 'Age'] + data = \ + '''Sam ,Browne,21 + Jhon,Browne,23 + Adam,senio,21''' + rows = [row.strip().split(',') for row in data.splitlines()] + print display_table(headers, rows) http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/osutils.py ---------------------------------------------------------------------- diff --git a/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/osutils.py b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/osutils.py new file mode 100644 index 0000000..8aeaa34 --- /dev/null +++ b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/osutils.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# +# 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. + +import logging +import os +import sys +import platform + +LOG = logging.getLogger(__name__) + + +def clearScreen(operatingSys): + """ + Function to clear the screen + Input : OS + """ + logging.info('Entering..') + + if operatingSys == 'Windows': + cmdClear = 'CLS' + elif operatingSys == 'Linux': + cmdClear = 'clear' + elif operatingSys == 'Darwin': + cmdClear = 'clear' + logging.debug('Running command : %s', cmdClear) + os.system(cmdClear) + logging.info('Exiting..') + + +def getOperatingSystem(): + logging.info('Entering..') + operatingSys = platform.system() + # sprint operatingSys + if not operatingSys: + logging.error('Operating system is NULL.') + return False, '' + else: + logging.debug('Got operating system : %s', operatingSys) + logging.info('Exiting..') + return True, operatingSys + + +def doclearScreen(): + # Determine the OS + result, operatingSys = getOperatingSystem() + if not result: + logging.error('Failed to determine Operating System. Exiting.') + sys.exit(1) + # clear the Screen + clearScreen(operatingSys) + + +if __name__ == '__main__': + pass http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/pluginutils.py ---------------------------------------------------------------------- diff --git a/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/pluginutils.py b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/pluginutils.py new file mode 100644 index 0000000..fdcb97c --- /dev/null +++ b/ambari-shell/ambari-python-shell/src/main/python/ambari_shell/utils/pluginutils.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# +# 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. + +import logging +import os +import sys +import inspect + +LOG = logging.getLogger(__name__) +#------------------------------------------------------------------------------ +''' +Function that searches for all plugins from a file +Input : folder +Output : dict +''' +#------------------------------------------------------------------------------ + + +def get_plugins(module): + logging.debug('[Module: %s]\n' % module.__name__) + + plugin_method_map = {} + count = 0 + + for name in dir(module): + obj = getattr(module, name) + if inspect.isclass(obj): + count += 1 + elif (inspect.ismethod(obj) or inspect.isfunction(obj)): + if obj.__name__.startswith("do_") or obj.__name__.startswith( + "help_") or obj.__name__.startswith("complete_") or obj.__name__.startswith("t_"): + logging.debug("%s ,%s ", obj.__name__, obj) + plugin_method_map.update({obj.__name__: obj}) + count += 1 + elif inspect.isbuiltin(obj): + count += 1 + logging.debug(plugin_method_map) + if count == 0: + logging.debug('(No members)') + + return plugin_method_map + + +def import_modules(dirr): + module_list = [] + for f in os.listdir(os.path.abspath(dirr)): + module_name, ext = os.path.splitext(f) + if ext == '.py' and module_name != "ambari_shell": + logging.debug('imported module: %s' % (module_name)) + module = __import__(module_name) + module_list.append(module) + + return module_list + + +def getPlugins(foldername): + if os.path.isdir(foldername): + sys.path.append(foldername) + logging.debug('%s is a directory!' % (foldername)) + + mod_list = import_modules(foldername) + logging.debug(mod_list) + + plugin_method_map = {} + for m in mod_list: + dictt = get_plugins(m) + if dictt: + plugin_method_map.update(dictt) + + return plugin_method_map + + +def getPluginsFromModules(modulename): + module = __import__(modulename) + logging.debug(module) + + plugin_method_map = {} + dictt = get_plugins(module) + if dictt: + plugin_method_map.update(dictt) + + return plugin_method_map + +if __name__ == "__main__": + print getPlugins("plug") http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-python-shell/src/main/python/setup.py ---------------------------------------------------------------------- diff --git a/ambari-shell/ambari-python-shell/src/main/python/setup.py b/ambari-shell/ambari-python-shell/src/main/python/setup.py new file mode 100755 index 0000000..b67d872 --- /dev/null +++ b/ambari-shell/ambari-python-shell/src/main/python/setup.py @@ -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. + + +from setuptools import setup, find_packages + +from sys import version_info, platform + +if version_info[:2] > (2, 5): + install_requires = [] +else: + install_requires = ['simplejson >= 2.0.0'] + +# Python 2.6 and below requires argparse +if version_info[:2] < (2, 7): + install_requires += ['argparse'] + +install_requires += ['ambari_client'] +setup( + name='ambari_shell', + author_email="ambari-dev@incubator.apache.org", + version="1.6.0-SNAPSHOT", + packages=['ambari_shell'], + install_requires=install_requires, + description='Ambari Python Shell', + license='Apache License 2.0' +) http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/ambari-python-shell/src/packages/tarball/all.xml ---------------------------------------------------------------------- diff --git a/ambari-shell/ambari-python-shell/src/packages/tarball/all.xml b/ambari-shell/ambari-python-shell/src/packages/tarball/all.xml new file mode 100755 index 0000000..0e4f34b --- /dev/null +++ b/ambari-shell/ambari-python-shell/src/packages/tarball/all.xml @@ -0,0 +1,34 @@ + + + + + + dir + + false + + + src/main/python + / + + + http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/assemblies/client.xml ---------------------------------------------------------------------- diff --git a/ambari-shell/assemblies/client.xml b/ambari-shell/assemblies/client.xml new file mode 100644 index 0000000..20670cd --- /dev/null +++ b/ambari-shell/assemblies/client.xml @@ -0,0 +1,20 @@ + + + + http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/pom.xml ---------------------------------------------------------------------- diff --git a/ambari-shell/pom.xml b/ambari-shell/pom.xml index a7e3400..947c9b9 100644 --- a/ambari-shell/pom.xml +++ b/ambari-shell/pom.xml @@ -1,14 +1,23 @@ - + + org.apache.ambari ambari-project @@ -18,106 +27,33 @@ 4.0.0 org.apache.ambari ambari-shell - jar - Ambari Shell + pom 1.3.0-SNAPSHOT + Ambari Shell Ambari Shell - - UTF-8 - org.apache.ambari.shell.AmbariShell - - - - junit - junit - - - org.springframework.shell - spring-shell - - - org.springframework.boot - spring-boot-starter - - - org.springframework.boot - spring-boot-starter-test - - - com.github.lalyos - jfiglet - - - commons-io - commons-io - 2.3 - - - org.apache.ambari - groovy-client - 1.3.0-SNAPSHOT - - - org.mockito - mockito-core - 1.9.5 - test - - - jline - jline - - - org.codehaus.jackson - jackson-mapper-asl - - + + ambari-python-shell + ambari-groovy-shell + - maven-compiler-plugin - 3.0 - - - org.apache.rat - apache-rat-plugin - - - src/main/resources/elephant.txt - src/test/resources/2columns - src/test/resources/3columns - src/test/resources/testBlueprint.json - - - - - test - - check - - - - - maven-assembly-plugin - true + gnu + + assemblies/client.xml + - - - org.springframework.boot - spring-boot-maven-plugin - 1.0.2.RELEASE + build-tarball + prepare-package - repackage + single - - ${start-class} - org.vafer @@ -132,9 +68,7 @@ - ${basedir}/../../ambari-project/src/main/package/deb/control - true - false + ${basedir}/../ambari-project/src/main/package/deb/control http://git-wip-us.apache.org/repos/asf/ambari/blob/c8eceafc/ambari-shell/src/main/java/org/apache/ambari/shell/AmbariShell.java ---------------------------------------------------------------------- diff --git a/ambari-shell/src/main/java/org/apache/ambari/shell/AmbariShell.java b/ambari-shell/src/main/java/org/apache/ambari/shell/AmbariShell.java deleted file mode 100644 index e842620..0000000 --- a/ambari-shell/src/main/java/org/apache/ambari/shell/AmbariShell.java +++ /dev/null @@ -1,111 +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. - */ -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= : Ambari Shell executes commands read from the file.\n\n" + - "Options:\n" + - " --ambari.host= Hostname of the Ambari Server [default: localhost].\n" + - " --ambari.port= Port of the Ambari Server [default: 8080].\n" + - " --ambari.user= Username of the Ambari admin [default: admin].\n" + - " --ambari.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/src/main/java/org/apache/ambari/shell/commands/BasicCommands.java ---------------------------------------------------------------------- diff --git a/ambari-shell/src/main/java/org/apache/ambari/shell/commands/BasicCommands.java b/ambari-shell/src/main/java/org/apache/ambari/shell/commands/BasicCommands.java deleted file mode 100644 index 9babe12..0000000 --- a/ambari-shell/src/main/java/org/apache/ambari/shell/commands/BasicCommands.java +++ /dev/null @@ -1,207 +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. - */ -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/src/main/java/org/apache/ambari/shell/commands/BlueprintCommands.java ---------------------------------------------------------------------- diff --git a/ambari-shell/src/main/java/org/apache/ambari/shell/commands/BlueprintCommands.java b/ambari-shell/src/main/java/org/apache/ambari/shell/commands/BlueprintCommands.java deleted file mode 100644 index 73000d0..0000000 --- a/ambari-shell/src/main/java/org/apache/ambari/shell/commands/BlueprintCommands.java +++ /dev/null @@ -1,198 +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. - */ -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/src/main/java/org/apache/ambari/shell/commands/ClusterCommands.java ---------------------------------------------------------------------- diff --git a/ambari-shell/src/main/java/org/apache/ambari/shell/commands/ClusterCommands.java b/ambari-shell/src/main/java/org/apache/ambari/shell/commands/ClusterCommands.java deleted file mode 100644 index dafdb85..0000000 --- a/ambari-shell/src/main/java/org/apache/ambari/shell/commands/ClusterCommands.java +++ /dev/null @@ -1,294 +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. - */ -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> 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> 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> groups = new HashMap>(); - for (String hostGroup : client.getHostGroups(context.getFocusValue())) { - groups.put(hostGroup, new ArrayList()); - } - this.hostGroups = groups; - } - - private boolean addHostToGroup(String host, String group) { - boolean result = true; - List 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/src/main/java/org/apache/ambari/shell/commands/ElephantCommand.java ---------------------------------------------------------------------- diff --git a/ambari-shell/src/main/java/org/apache/ambari/shell/commands/ElephantCommand.java b/ambari-shell/src/main/java/org/apache/ambari/shell/commands/ElephantCommand.java deleted file mode 100644 index a236054..0000000 --- a/ambari-shell/src/main/java/org/apache/ambari/shell/commands/ElephantCommand.java +++ /dev/null @@ -1,53 +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. - */ -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