mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-11-04 00:02:37 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			467 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			467 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#!/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
 | 
						|
 | 
						|
    from cachemaker import loadcache, savecache, monkeycache, splitverbsubject
 | 
						|
    from config import __version__, cache_file
 | 
						|
    from config import read_config, write_config
 | 
						|
    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 = {}
 | 
						|
 | 
						|
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 = "="
 | 
						|
    cache_file = cache_file
 | 
						|
    config_options = []
 | 
						|
    verbs = []
 | 
						|
 | 
						|
    def __init__(self, pname):
 | 
						|
        self.program_name = pname
 | 
						|
        self.config_options = read_config(self.get_attr, self.set_attr)
 | 
						|
        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_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):
 | 
						|
            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:
 | 
						|
                # Tabular print if it's a list of dict and tabularize is true
 | 
						|
                if isinstance(node, dict) and self.tabularize == 'true':
 | 
						|
                    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 self.tabularize == "true" and subject != "":
 | 
						|
            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")
 | 
						|
        self.apicache = monkeycache(response)
 | 
						|
        if response is None:
 | 
						|
            monkeyprint("Failed to sync apis, check your config")
 | 
						|
            return
 | 
						|
        savecache(self.apicache, self.cache_file)
 | 
						|
        self.loadcache()
 | 
						|
 | 
						|
    def do_api(self, args):
 | 
						|
        """
 | 
						|
        Make raw api calls. Syntax: api <apiName> <args>=<values>.
 | 
						|
 | 
						|
        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)
 | 
						|
 | 
						|
    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 <command> or !<command>
 | 
						|
 | 
						|
        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)
 | 
						|
 | 
						|
 | 
						|
def main():
 | 
						|
    shell = CloudMonkeyShell(sys.argv[0])
 | 
						|
    if len(sys.argv) > 1:
 | 
						|
        shell.onecmd(' '.join(sys.argv[1:]))
 | 
						|
    else:
 | 
						|
        shell.cmdloop()
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    main()
 |