diff --git a/INSTALL.md b/INSTALL.md index b0e1a7617b0..37415dc25ec 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -213,6 +213,13 @@ Install needed packages: $ yum install cloud-agent # agent (kvm) $ yum install cloud-usage # usage server +## Installing CloudMonkey CLI + +CloudMonkey is a CLI for Apache CloudStack. It was earlier in `tools/cli` within +the source code but now it has its own repository: + + https://git-wip-us.apache.org/repos/asf?p=cloudstack-cloudmonkey.git + ## Notes If you will be using Xen as your hypervisor, please download [vhd-util](http://download.cloud.com.s3.amazonaws.com/tools/vhd-util) diff --git a/tools/cli/README b/tools/cli/README new file mode 100644 index 00000000000..f8bfebea4ed --- /dev/null +++ b/tools/cli/README @@ -0,0 +1 @@ +Moved to https://git-wip-us.apache.org/repos/asf?p=cloudstack-cloudmonkey.git diff --git a/tools/cli/cloudmonkey/__init__.py b/tools/cli/cloudmonkey/__init__.py deleted file mode 100644 index cf689e79480..00000000000 --- a/tools/cli/cloudmonkey/__init__.py +++ /dev/null @@ -1,23 +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. - -try: - from config import __version__, __description__ - from config import __maintainer__, __maintaineremail__ - from config import __project__, __projecturl__, __projectemail__ -except ImportError, e: - print e diff --git a/tools/cli/cloudmonkey/cachemaker.py b/tools/cli/cloudmonkey/cachemaker.py deleted file mode 100644 index 47749e5ae74..00000000000 --- a/tools/cli/cloudmonkey/cachemaker.py +++ /dev/null @@ -1,181 +0,0 @@ -# -*- coding: utf-8 -*- -# 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. - -try: - import json - import os - import types - - from config import config_fields -except ImportError, e: - import sys - print "ImportError", e - sys.exit(1) - - -def getvalue(dictionary, key): - if key in dictionary: - return dictionary[key] - else: - return None - - -def splitcsvstring(string): - if string is not None: - return filter(lambda x: x.strip() != '', string.split(',')) - else: - return [] - - -def splitverbsubject(string): - idx = 0 - for char in string: - if char.islower(): - idx += 1 - else: - break - return string[:idx].lower(), string[idx:].lower() - - -def savecache(apicache, json_file): - """ - Saves apicache dictionary as json_file, returns dictionary as indented str - """ - if apicache is None or apicache is {}: - return "" - apicachestr = json.dumps(apicache, indent=2) - with open(json_file, 'w') as cache_file: - cache_file.write(apicachestr) - return apicachestr - - -def loadcache(json_file): - """ - Loads json file as dictionary, feeds it to monkeycache and spits result - """ - f = open(json_file, 'r') - data = f.read() - f.close() - try: - apicache = json.loads(data) - except ValueError, e: - print "Error processing json:", json_file, e - return {} - return apicache - - -def monkeycache(apis): - """ - Feed this a dictionary of api bananas, it spits out processed cache - """ - if isinstance(type(apis), types.NoneType) or apis is None: - return {} - - responsekey = filter(lambda x: 'response' in x, apis.keys()) - - if len(responsekey) == 0: - print "[monkeycache] Invalid dictionary, has no response" - return None - if len(responsekey) != 1: - print "[monkeycache] Multiple responsekeys, chosing first one" - - responsekey = responsekey[0] - verbs = set() - cache = {} - cache['count'] = getvalue(apis[responsekey], 'count') - cache['asyncapis'] = [] - - apilist = getvalue(apis[responsekey], 'api') - if apilist is None: - print "[monkeycache] Server response issue, no apis found" - - for api in apilist: - name = getvalue(api, 'name') - verb, subject = splitverbsubject(name) - - apidict = {} - apidict['name'] = name - apidict['description'] = getvalue(api, 'description') - apidict['isasync'] = getvalue(api, 'isasync') - if apidict['isasync']: - cache['asyncapis'].append(name) - apidict['related'] = splitcsvstring(getvalue(api, 'related')) - - required = [] - apiparams = [] - for param in getvalue(api, 'params'): - apiparam = {} - apiparam['name'] = getvalue(param, 'name') - apiparam['description'] = getvalue(param, 'description') - apiparam['required'] = (getvalue(param, 'required') is True) - apiparam['length'] = int(getvalue(param, 'length')) - apiparam['type'] = getvalue(param, 'type') - apiparam['related'] = splitcsvstring(getvalue(param, 'related')) - if apiparam['required']: - required.append(apiparam['name']) - apiparams.append(apiparam) - - apidict['requiredparams'] = required - apidict['params'] = apiparams - if verb not in cache: - cache[verb] = {} - cache[verb][subject] = apidict - verbs.add(verb) - - cache['verbs'] = list(verbs) - return cache - - -def main(json_file): - """ - cachemaker.py creates a precache datastore of all available apis of - CloudStack and dumps the precache dictionary in an - importable python module. This way we cheat on the runtime overhead of - completing commands and help docs. This reduces the overall search and - cache_miss (computation) complexity from O(n) to O(1) for any valid cmd. - """ - f = open("precache.py", "w") - f.write("""# -*- coding: utf-8 -*- -# Auto-generated code by cachemaker.py -# 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.""") - f.write("\napicache = %s" % loadcache(json_file)) - f.close() - -if __name__ == "__main__": - cache_file = config_fields['core']['cache_file'] - print "[cachemaker] Pre-caching using user's cloudmonkey cache", cache_file - if os.path.exists(cache_file): - main(cache_file) - else: - print "[cachemaker] Unable to cache apis, file not found", cache_file - print "[cachemaker] Run cloudmonkey sync to generate cache" diff --git a/tools/cli/cloudmonkey/cloudmonkey.py b/tools/cli/cloudmonkey/cloudmonkey.py deleted file mode 100644 index 13f54ada751..00000000000 --- a/tools/cli/cloudmonkey/cloudmonkey.py +++ /dev/null @@ -1,538 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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. - -try: - import atexit - import cmd - import json - import logging - import os - import pdb - import shlex - import sys - import types - import copy - - from cachemaker import loadcache, savecache, monkeycache, splitverbsubject - from config import __version__, __description__, __projecturl__ - from config import read_config, write_config, config_file - from optparse import OptionParser - from prettytable import PrettyTable - from printer import monkeyprint - from requester import monkeyrequest -except ImportError, e: - print("Import error in %s : %s" % (__name__, e)) - import sys - sys.exit() - -try: - from precache import apicache -except ImportError: - apicache = {'count': 0, 'verbs': [], 'asyncapis': []} - -try: - import readline -except ImportError, e: - print("Module readline not found, autocompletions will fail", e) -else: - import rlcompleter - if 'libedit' in readline.__doc__: - readline.parse_and_bind("bind ^I rl_complete") - else: - readline.parse_and_bind("tab: complete") - -log_fmt = '%(asctime)s - %(filename)s:%(lineno)s - [%(levelname)s] %(message)s' -logger = logging.getLogger(__name__) - - -class CloudMonkeyShell(cmd.Cmd, object): - intro = ("☁ Apache CloudStack 🐵 cloudmonkey " + __version__ + - ". Type help or ? to list commands.\n") - ruler = "=" - config_options = [] - verbs = [] - - def __init__(self, pname, cfile): - self.program_name = pname - self.config_file = cfile - self.config_options = read_config(self.get_attr, self.set_attr, - self.config_file) - self.loadcache() - self.prompt = self.prompt.strip() + " " # Cosmetic fix for prompt - - logging.basicConfig(filename=self.log_file, - level=logging.DEBUG, format=log_fmt) - logger.debug("Loaded config fields:\n%s" % map(lambda x: "%s=%s" % - (x, getattr(self, x)), - self.config_options)) - cmd.Cmd.__init__(self) - - try: - if os.path.exists(self.history_file): - readline.read_history_file(self.history_file) - except IOError, e: - logger.debug("Error: Unable to read history. " + str(e)) - atexit.register(readline.write_history_file, self.history_file) - - def get_attr(self, field): - return getattr(self, field) - - def set_attr(self, field, value): - return setattr(self, field, value) - - def emptyline(self): - pass - - def cmdloop(self, intro=None): - print(self.intro) - while True: - try: - super(CloudMonkeyShell, self).cmdloop(intro="") - self.postloop() - except KeyboardInterrupt: - print("^C") - - def loadcache(self): - if os.path.exists(self.cache_file): - self.apicache = loadcache(self.cache_file) - else: - self.apicache = apicache - if 'verbs' in self.apicache: - self.verbs = self.apicache['verbs'] - - for verb in self.verbs: - def add_grammar(verb): - def grammar_closure(self, args): - if self.pipe_runner("%s %s" % (verb, args)): - return - if ' --help' in args or ' -h' in args: - self.do_help("%s %s" % (verb, args)) - return - try: - args_partition = args.partition(" ") - cmd = self.apicache[verb][args_partition[0]]['name'] - args = args_partition[2] - except KeyError, e: - self.monkeyprint("Error: invalid %s api arg" % verb, e) - return - self.default("%s %s" % (cmd, args)) - return grammar_closure - - grammar_handler = add_grammar(verb) - grammar_handler.__doc__ = "%ss resources" % verb.capitalize() - grammar_handler.__name__ = 'do_' + str(verb) - setattr(self.__class__, grammar_handler.__name__, grammar_handler) - - def monkeyprint(self, *args): - output = "" - try: - for arg in args: - if isinstance(type(arg), types.NoneType): - continue - output += str(arg) - except Exception, e: - print(e) - - if self.color == 'true': - monkeyprint(output) - else: - print(output) - - def print_result(self, result, result_filter=None): - if result is None or len(result) == 0: - return - - def printer_helper(printer, toprow): - if printer: - self.monkeyprint(printer) - return PrettyTable(toprow) - - def print_result_json(result, result_filter=None): - tfilter = {} # temp var to hold a dict of the filters - tresult = copy.deepcopy(result) # dupe the result to filter - if result_filter is not None: - for res in result_filter: - tfilter[res] = 1 - myresults = {} - for okey, oval in result.iteritems(): - if isinstance(oval, dict): - for tkey in x: - if tkey not in tfilter: - try: - del(tresult[okey][x][tkey]) - except: - pass - elif isinstance(oval, list): - for x in range(len(oval)): - if isinstance(oval[x], dict): - for tkey in oval[x]: - if tkey not in tfilter: - try: - del(tresult[okey][x][tkey]) - except: - pass - else: - try: - del(tresult[okey][x]) - except: - pass - print json.dumps(tresult, - sort_keys=True, - indent=2, - separators=(',', ': ')) - - def print_result_tabular(result, result_filter=None): - toprow = None - printer = None - for node in result: - if toprow != node.keys(): - if result_filter is not None and len(result_filter) != 0: - commonkeys = filter(lambda x: x in node.keys(), - result_filter) - if commonkeys != toprow: - toprow = commonkeys - printer = printer_helper(printer, toprow) - else: - toprow = node.keys() - printer = printer_helper(printer, toprow) - row = map(lambda x: node[x], toprow) - if printer and row: - printer.add_row(row) - if printer: - self.monkeyprint(printer) - - def print_result_as_dict(result, result_filter=None): - if self.display == "json": - print_result_json(result, result_filter) - return - - for key in sorted(result.keys(), key=lambda x: - x not in ['id', 'count', 'name'] and x): - if not (isinstance(result[key], list) or - isinstance(result[key], dict)): - self.monkeyprint("%s = %s" % (key, result[key])) - else: - self.monkeyprint(key + ":") - self.print_result(result[key], result_filter) - - def print_result_as_list(result, result_filter=None): - for node in result: - if isinstance(node, dict) and self.display == 'table': - print_result_tabular(result, result_filter) - break - self.print_result(node) - if len(result) > 1: - self.monkeyprint(self.ruler * 80) - - if isinstance(result, dict): - print_result_as_dict(result, result_filter) - elif isinstance(result, list): - print_result_as_list(result, result_filter) - elif isinstance(result, str): - print result - elif not (str(result) is None): - self.monkeyprint(result) - - def make_request(self, command, args={}, isasync=False): - response, error = monkeyrequest(command, args, isasync, - self.asyncblock, logger, - self.host, self.port, - self.apikey, self.secretkey, - self.timeout, self.protocol, self.path) - if error is not None: - self.monkeyprint(error) - return response - - def default(self, args): - if self.pipe_runner(args): - return - - apiname = args.partition(' ')[0] - verb, subject = splitverbsubject(apiname) - - lexp = shlex.shlex(args.strip()) - lexp.whitespace = " " - lexp.whitespace_split = True - lexp.posix = True - args = [] - while True: - next_val = lexp.next() - if next_val is None: - break - args.append(next_val.replace('\x00', '')) - - args_dict = dict(map(lambda x: [x.partition("=")[0], - x.partition("=")[2]], - args[1:])[x] for x in range(len(args) - 1)) - field_filter = None - if 'filter' in args_dict: - field_filter = filter(lambda x: x is not '', - map(lambda x: x.strip(), - args_dict.pop('filter').split(','))) - - missing = [] - if verb in self.apicache and subject in self.apicache[verb]: - missing = filter(lambda x: x not in args_dict.keys(), - self.apicache[verb][subject]['requiredparams']) - - if len(missing) > 0: - self.monkeyprint("Missing arguments: ", ' '.join(missing)) - return - - isasync = False - if 'asyncapis' in self.apicache: - isasync = apiname in self.apicache['asyncapis'] - - result = self.make_request(apiname, args_dict, isasync) - - if result is None: - return - try: - responsekeys = filter(lambda x: 'response' in x, result.keys()) - for responsekey in responsekeys: - self.print_result(result[responsekey], field_filter) - print - except Exception as e: - self.monkeyprint("🙈 Error on parsing and printing", e) - - def completedefault(self, text, line, begidx, endidx): - partitions = line.partition(" ") - verb = partitions[0].strip() - rline = partitions[2].lstrip().partition(" ") - subject = rline[0] - separator = rline[1] - params = rline[2].lstrip() - - if verb not in self.verbs: - return [] - - autocompletions = [] - search_string = "" - - if separator != " ": # Complete verb subjects - autocompletions = self.apicache[verb].keys() - search_string = subject - else: # Complete subject params - autocompletions = map(lambda x: x + "=", - map(lambda x: x['name'], - self.apicache[verb][subject]['params'])) - search_string = text - if self.paramcompletion == 'true': - param = line.split(" ")[-1] - idx = param.find("=") - value = param[idx + 1:] - param = param[:idx] - if len(value) < 36 and idx != -1: - params = self.apicache[verb][subject]['params'] - related = filter(lambda x: x['name'] == param, - params)[0]['related'] - api = min(filter(lambda x: 'list' in x, related), key=len) - response = self.make_request(api, args={'listall': 'true'}) - responsekey = filter(lambda x: 'response' in x, - response.keys())[0] - result = response[responsekey] - uuids = [] - for key in result.keys(): - if isinstance(result[key], list): - for element in result[key]: - if 'id' in element.keys(): - uuids.append(element['id']) - autocompletions = uuids - search_string = value - - if subject != "" and (self.display == "table" or - self.display == "json"): - autocompletions.append("filter=") - return [s for s in autocompletions if s.startswith(search_string)] - - def do_sync(self, args): - """ - Asks cloudmonkey to discovery and sync apis available on user specified - CloudStack host server which has the API discovery plugin, on failure - it rollbacks last datastore or api precached datastore. - """ - response = self.make_request("listApis") - if response is None: - monkeyprint("Failed to sync apis, please check your config?") - monkeyprint("Note: `sync` requires api discovery service enabled" + - " on the CloudStack management server") - return - self.apicache = monkeycache(response) - savecache(self.apicache, self.cache_file) - monkeyprint("%s APIs discovered and cached" % self.apicache["count"]) - self.loadcache() - - def do_api(self, args): - """ - Make raw api calls. Syntax: api =. - - Example: - api listAccount listall=true - """ - if len(args) > 0: - return self.default(args) - else: - self.monkeyprint("Please use a valid syntax") - - def do_set(self, args): - """ - Set config for cloudmonkey. For example, options can be: - host, port, apikey, secretkey, log_file, history_file - You may also edit your ~/.cloudmonkey_config instead of using set. - - Example: - set host 192.168.56.2 - set prompt 🐵 cloudmonkey> - set log_file /var/log/cloudmonkey.log - """ - args = args.strip().partition(" ") - key, value = (args[0], args[2]) - setattr(self, key, value) # keys and attributes should have same names - self.prompt = self.prompt.strip() + " " # prompt fix - write_config(self.get_attr, self.config_file) - - def complete_set(self, text, line, begidx, endidx): - mline = line.partition(" ")[2] - offs = len(mline) - len(text) - return [s[offs:] for s in self.config_options - if s.startswith(mline)] - - def pipe_runner(self, args): - if args.find(' |') > -1: - pname = self.program_name - if '.py' in pname: - pname = "python " + pname - self.do_shell("%s %s" % (pname, args)) - return True - return False - - def do_shell(self, args): - """ - Execute shell commands using shell or ! - - Example: - !ls - shell ls - !for((i=0; i<10; i++)); do cloudmonkey create user account=admin \ - email=test@test.tt firstname=user$i lastname=user$i \ - password=password username=user$i; done - """ - os.system(args) - - def do_help(self, args): - """ - Show help docs for various topics - - Example: - help list - help list users - ?list - ?list users - """ - fields = args.partition(" ") - if fields[2] == "": - cmd.Cmd.do_help(self, args) - else: - verb = fields[0] - subject = fields[2].partition(" ")[0] - if subject in self.apicache[verb]: - api = self.apicache[verb][subject] - helpdoc = "(%s) %s" % (api['name'], api['description']) - if api['isasync']: - helpdoc += "\nThis API is asynchronous." - required = api['requiredparams'] - if len(required) > 0: - helpdoc += "\nRequired params are %s" % ' '.join(required) - helpdoc += "\nParameters\n" + "=" * 10 - for param in api['params']: - helpdoc += "\n%s = (%s) %s" % (param['name'], - param['type'], param['description']) - self.monkeyprint(helpdoc) - else: - self.monkeyprint("Error: no such api (%s) on %s" % - (subject, verb)) - - def complete_help(self, text, line, begidx, endidx): - fields = line.partition(" ") - subfields = fields[2].partition(" ") - - if subfields[1] != " ": - return cmd.Cmd.complete_help(self, text, line, begidx, endidx) - else: - line = fields[2] - text = subfields[2] - return self.completedefault(text, line, begidx, endidx) - - def do_EOF(self, args): - """ - Quit on Ctrl+d or EOF - """ - sys.exit() - - def do_exit(self, args): - """ - Quit CloudMonkey CLI - """ - return self.do_quit(args) - - def do_quit(self, args): - """ - Quit CloudMonkey CLI - """ - self.monkeyprint("Bye!") - return self.do_EOF(args) - - -class MonkeyParser(OptionParser): - def format_help(self, formatter=None): - if formatter is None: - formatter = self.formatter - result = [] - if self.usage: - result.append("Usage: cloudmonkey [options] [cmds] [params]\n\n") - if self.description: - result.append(self.format_description(formatter) + "\n") - result.append(self.format_option_help(formatter)) - result.append("\nTry cloudmonkey [help|?]\n") - return "".join(result) - - -def main(): - parser = MonkeyParser() - parser.add_option("-c", "--config-file", - dest="cfile", default=config_file, - help="config file for cloudmonkey", metavar="FILE") - parser.add_option("-v", "--version", - action="store_true", dest="version", default=False, - help="prints cloudmonkey version information") - - (options, args) = parser.parse_args() - if options.version: - print "cloudmonkey", __version__ - print __description__, "(%s)" % __projecturl__ - sys.exit(0) - - shell = CloudMonkeyShell(sys.argv[0], options.cfile) - if len(args) > 0: - shell.onecmd(' '.join(args)) - else: - shell.cmdloop() - -if __name__ == "__main__": - main() diff --git a/tools/cli/cloudmonkey/config.py b/tools/cli/cloudmonkey/config.py deleted file mode 100644 index 36f7e77ed82..00000000000 --- a/tools/cli/cloudmonkey/config.py +++ /dev/null @@ -1,122 +0,0 @@ -# -*- coding: utf-8 -*- -# 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. - -# Use following rules for versioning: -# - -__version__ = "4.2.0-0" -__description__ = "Command Line Interface for Apache CloudStack" -__maintainer__ = "Rohit Yadav" -__maintaineremail__ = "bhaisaab@apache.org" -__project__ = "The Apache CloudStack Team" -__projectemail__ = "dev@cloudstack.apache.org" -__projecturl__ = "http://cloudstack.apache.org" - -try: - import os - import sys - - from ConfigParser import ConfigParser, SafeConfigParser - from os.path import expanduser -except ImportError, e: - print "ImportError", e - -param_type = ['boolean', 'date', 'float', 'integer', 'short', 'list', - 'long', 'object', 'map', 'string', 'tzdate', 'uuid'] - -iterable_type = ['set', 'list', 'object'] - -config_dir = expanduser('~/.cloudmonkey') -config_file = expanduser(config_dir + '/config') - -# cloudmonkey config fields -config_fields = {'core': {}, 'server': {}, 'user': {}, 'ui': {}} - -# core -config_fields['core']['asyncblock'] = 'true' -config_fields['core']['paramcompletion'] = 'false' -config_fields['core']['cache_file'] = expanduser(config_dir + '/cache') -config_fields['core']['history_file'] = expanduser(config_dir + '/history') -config_fields['core']['log_file'] = expanduser(config_dir + '/log') - -# ui -config_fields['ui']['color'] = 'true' -config_fields['ui']['prompt'] = '> ' -config_fields['ui']['display'] = 'default' - -# server -config_fields['server']['host'] = 'localhost' -config_fields['server']['path'] = '/client/api' -config_fields['server']['port'] = '8080' -config_fields['server']['protocol'] = 'http' -config_fields['server']['timeout'] = '3600' - -# user -config_fields['user']['apikey'] = '' -config_fields['user']['secretkey'] = '' - - -def write_config(get_attr, config_file, first_time=False): - global config_fields - config = ConfigParser() - for section in config_fields.keys(): - config.add_section(section) - for key in config_fields[section].keys(): - if first_time: - config.set(section, key, config_fields[section][key]) - else: - config.set(section, key, get_attr(key)) - with open(config_file, 'w') as cfg: - config.write(cfg) - return config - - -def read_config(get_attr, set_attr, config_file): - global config_fields, config_dir - if not os.path.exists(config_dir): - os.makedirs(config_dir) - - config_options = reduce(lambda x, y: x + y, map(lambda x: - config_fields[x].keys(), config_fields.keys())) - - if os.path.exists(config_file): - config = ConfigParser() - try: - with open(config_file, 'r') as cfg: - config.readfp(cfg) - except IOError, e: - print "Error: config_file not found", e - else: - config = write_config(get_attr, config_file, True) - print "Welcome! Using `set` configure the necessary settings:" - print " ".join(sorted(config_options)) - print "Config file:", config_file - print "After setting up, run the `sync` command to sync apis\n" - - missing_keys = [] - for section in config_fields.keys(): - for key in config_fields[section].keys(): - try: - set_attr(key, config.get(section, key)) - except Exception: - missing_keys.append(key) - - if len(missing_keys) > 0: - print "Please fix `%s` in %s" % (', '.join(missing_keys), config_file) - sys.exit() - - return config_options diff --git a/tools/cli/cloudmonkey/printer.py b/tools/cli/cloudmonkey/printer.py deleted file mode 100644 index 925e765f251..00000000000 --- a/tools/cli/cloudmonkey/printer.py +++ /dev/null @@ -1,120 +0,0 @@ -# -*- coding: utf-8 -*- -# 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. - -try: - from pygments import highlight - from pygments.console import ansiformat - from pygments.formatter import Formatter - from pygments.formatters import Terminal256Formatter - from pygments.lexer import bygroups, include, RegexLexer - from pygments.token import * - - import sys -except ImportError, e: - print e - - -MONKEY_COLORS = { - Token: '', - Whitespace: 'reset', - Text: 'reset', - - Name: 'green', - Operator: 'teal', - Operator.Word: 'lightgray', - String: 'purple', - - Keyword: '_red_', - Error: 'red', - Literal: 'yellow', - Number: 'blue', -} - - -def get_colorscheme(): - return MONKEY_COLORS - - -class MonkeyLexer(RegexLexer): - keywords = ['[a-z]*id', '^[a-z A-Z]*:'] - attributes = ['[Tt]rue', '[Ff]alse'] - params = ['[a-z]*[Nn]ame', 'type', '[Ss]tate'] - - uuid_rgx = r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' - date_rgx = r'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9:]{8}-[0-9]{4}' - - def makelistre(lis): - return r'(' + r'|'.join(lis) + r')' - - tokens = { - 'root': [ - (r' ', Whitespace), - (date_rgx, Number), - (uuid_rgx, Literal), - (r'(?:\b\d+\b(?:-\b\d+|%)?)', Number), - (r'^[-=]*\n', Operator.Word), - (r'Error', Error), - (makelistre(attributes), Literal), - (makelistre(params) + r'( = )(.*)', bygroups(Name, Operator, - String)), - (makelistre(keywords), Keyword), - (makelistre(params), Name), - (r'(^[a-zA-Z]* )(=)', bygroups(Name, Operator)), - (r'\S+', Text), - ] - } - - def analyse_text(text): - npos = text.find('\n') - if npos < 3: - return False - return text[0] == '[' and text[npos - 1] == ']' - - -class MonkeyFormatter(Formatter): - def __init__(self, **options): - Formatter.__init__(self, **options) - self.colorscheme = get_colorscheme() - - def format(self, tokensource, outfile): - return Formatter.format(self, tokensource, outfile) - - def format_unencoded(self, tokensource, outfile): - for ttype, value in tokensource: - color = self.colorscheme.get(ttype) - while color is None: - ttype = ttype[:-1] - color = self.colorscheme.get(ttype) - if color: - spl = value.split('\n') - for line in spl[:-1]: - if line: - outfile.write(ansiformat(color, line)) - outfile.write('\n') - if spl[-1]: - outfile.write(ansiformat(color, spl[-1])) - else: - outfile.write(value) - - -def monkeyprint(text): - fmter = MonkeyFormatter() - lexer = MonkeyLexer() - lexer.encoding = 'utf-8' - fmter.encoding = 'utf-8' - highlight(text, lexer, fmter, sys.stdout) diff --git a/tools/cli/cloudmonkey/requester.py b/tools/cli/cloudmonkey/requester.py deleted file mode 100644 index b06e1fc99e3..00000000000 --- a/tools/cli/cloudmonkey/requester.py +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# 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. - -try: - import base64 - import hashlib - import hmac - import httplib - import json - import os - import pdb - import re - import shlex - import sys - import time - import types - import urllib - import urllib2 - from urllib2 import urlopen, HTTPError, URLError - -except ImportError, e: - print "Import error in %s : %s" % (__name__, e) - import sys - sys.exit() - - -def logger_debug(logger, message): - if logger is not None: - logger.debug(message) - - -def make_request(command, args, logger, host, port, - apikey, secretkey, protocol, path): - response = None - error = None - - if protocol != 'http' and protocol != 'https': - error = "Protocol must be 'http' or 'https'" - return None, error - - if args is None: - args = {} - - args["command"] = command - args["apiKey"] = apikey - args["response"] = "json" - request = zip(args.keys(), args.values()) - request.sort(key=lambda x: x[0].lower()) - - request_url = "&".join(["=".join([r[0], urllib.quote_plus(str(r[1]))]) - for r in request]) - hashStr = "&".join(["=".join([r[0].lower(), - str.lower(urllib.quote_plus(str(r[1]))).replace("+", - "%20")]) for r in request]) - - sig = urllib.quote_plus(base64.encodestring(hmac.new(secretkey, hashStr, - hashlib.sha1).digest()).strip()) - request_url += "&signature=%s" % sig - request_url = "%s://%s:%s%s?%s" % (protocol, host, port, path, request_url) - - try: - logger_debug(logger, "Request sent: %s" % request_url) - connection = urllib2.urlopen(request_url) - response = connection.read() - except HTTPError, e: - error = "%s: %s" % (e.msg, e.info().getheader('X-Description')) - except URLError, e: - error = e.reason - - logger_debug(logger, "Response received: %s" % response) - if error is not None: - logger_debug(logger, "Error: %s" % (error)) - return response, error - - return response, error - - -def monkeyrequest(command, args, isasync, asyncblock, logger, host, port, - apikey, secretkey, timeout, protocol, path): - response = None - error = None - logger_debug(logger, "======== START Request ========") - logger_debug(logger, "Requesting command=%s, args=%s" % (command, args)) - response, error = make_request(command, args, logger, host, port, - apikey, secretkey, protocol, path) - logger_debug(logger, "======== END Request ========\n") - - if error is not None: - return response, error - - def process_json(response): - try: - response = json.loads(str(response)) - except ValueError, e: - error = "Error processing json response, %s" % e - logger_debug(logger, "Error processing json: %s" % e) - - return response - - response = process_json(response) - if response is None: - return response, error - - isasync = isasync and (asyncblock == "true") - responsekey = filter(lambda x: 'response' in x, response.keys())[0] - - if isasync and 'jobid' in response[responsekey]: - jobid = response[responsekey]['jobid'] - command = "queryAsyncJobResult" - request = {'jobid': jobid} - timeout = int(timeout) - pollperiod = 2 - progress = 1 - while timeout > 0: - print '\r' + '.' * progress, - sys.stdout.flush() - time.sleep(pollperiod) - timeout = timeout - pollperiod - progress += 1 - logger_debug(logger, "Job %s to timeout in %ds" % (jobid, timeout)) - response, error = make_request(command, request, logger, - host, port, apikey, secretkey, - protocol, path) - if error is not None: - return response, error - - response = process_json(response) - responsekeys = filter(lambda x: 'response' in x, response.keys()) - - if len(responsekeys) < 1: - continue - - result = response[responsekeys[0]] - jobstatus = result['jobstatus'] - if jobstatus == 2: - jobresult = result["jobresult"] - error = "\rAsync job %s failed\nError %s, %s" % (jobid, - jobresult["errorcode"], jobresult["errortext"]) - return response, error - elif jobstatus == 1: - print "\r" + " " * progress, - return response, error - else: - logger_debug(logger, "We should not arrive here!") - sys.stdout.flush() - - error = "Error: Async query timeout occurred for jobid %s" % jobid - - return response, error diff --git a/tools/cli/pom.xml b/tools/cli/pom.xml deleted file mode 100644 index b4820cd1e36..00000000000 --- a/tools/cli/pom.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - 4.0.0 - cloud-cli - Apache CloudStack cloudmonkey cli - pom - - org.apache.cloudstack - cloud-tools - 4.2.0-SNAPSHOT - ../pom.xml - - - - install - - - org.codehaus.mojo - exec-maven-plugin - 1.2.1 - - - cachemaker - compile - - exec - - - ${basedir}/cloudmonkey - python - - cachemaker.py - - - - - package - compile - - exec - - - ${basedir} - python - - setup.py - sdist - - - - - - - - diff --git a/tools/cli/setup.py b/tools/cli/setup.py deleted file mode 100644 index 4c7b2978b2f..00000000000 --- a/tools/cli/setup.py +++ /dev/null @@ -1,68 +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. - -try: - from setuptools import setup, find_packages -except ImportError: - from distribute_setup import use_setuptools - use_setuptools() - from setuptools import setup, find_packages - -from cloudmonkey import __version__, __description__ -from cloudmonkey import __maintainer__, __maintaineremail__ -from cloudmonkey import __project__, __projecturl__, __projectemail__ - -try: - import readline -except ImportError: - requires.append('readline') - -setup( - name = 'cloudmonkey', - version = __version__, - author = __project__, - author_email = __projectemail__, - maintainer = __maintainer__, - maintainer_email = __maintaineremail__, - url = __projecturl__, - description = __description__, - long_description = "cloudmonkey is a CLI for Apache CloudStack", - platforms = ("Any",), - license = 'ASL 2.0', - packages = find_packages(), - install_requires = [ - 'Pygments>=1.5', - 'prettytable>=0.6', - ], - include_package_data = True, - zip_safe = False, - classifiers = [ - "Development Status :: 5 - Production/Stable", - "Environment :: Console", - "Intended Audience :: Developers", - "Intended Audience :: End Users/Desktop", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Topic :: Software Development :: Testing", - "Topic :: Software Development :: Interpreters", - "Topic :: Utilities", - ], - entry_points=""" - [console_scripts] - cloudmonkey = cloudmonkey.cloudmonkey:main - """, -) diff --git a/tools/pom.xml b/tools/pom.xml index 09961bb28d8..49cf13504b1 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -37,7 +37,6 @@ apidoc marvin - cli devcloud devcloud-kvm