novaclient中的源碼簡析

novaclient一個CLI工具,將nova相關命令轉化爲http請求,發送至nova服務!

看了下novaclient代碼,很容易擴展和更改,比葫蘆畫瓢,一會功夫就能完成自己想要的東西,very good!


1,novaclient使用了一個 python中的moudle::agrparse

2,基本本工作流程(nova list)
nova命令執行novaclient.shell.main()方法
main方法獲取shell命令行的參數,解析後,發送請求

3,main方法細節

>>>>parser = self.get_base_parser()   //這裏是一個argparse對象(),向對象中添加基本的參數

>>>>(options, args) = parser.parse_known_args(argv)  返回了兩個對象, options基本的選項參數變量列表, args命令行傳入的args[1:]

>>>>self.extensions = self._discover_extensions(options.os_compute_api_version) //加載基本的擴展,路徑 /novaclinet/v1_1/contrib/****

>>>>subcommand_parser = self.get_subcommand_parser(options.os_compute_api_version)  //獲取子命令解析對象 

    
    def get_subcommand_parser(self, version):
        parser = self.get_base_parser()
        self.subcommands = {}
        subparsers = parser.add_subparsers(metavar='<subcommand>')
        self._find_actions(subparsers, 'novaclient.client.shell_v1_1')
        self._find_actions(subparsers, self)
        for extension in self.extensions:
            self._find_actions(subparsers, extension.module)
        self._add_bash_completion_subparser(subparsers)
        return parser
    def _find_actions(self, subparsers, actions_module):
        for attr in (a for a in dir(actions_module) if a.startswith('do_')):
            command = attr[3:].replace('_', '-')
            callback = getattr(actions_module, attr)
            desc = callback.__doc__ or ''
            action_help = desc.strip()
            arguments = getattr(callback, 'arguments', [])


            subparser = subparsers.add_parser(command,
                help=action_help,
                description=desc,
                add_help=False,
                formatter_class=OpenStackHelpFormatter
            )
            subparser.add_argument('-h', '--help',
                action='help',
                help=argparse.SUPPRESS,
            )
            self.subcommands[command] = subparser
            for (args, kwargs) in arguments:
                subparser.add_argument(*args, **kwargs)
            subparser.set_defaults(func=callback)
    此命令解析::從指定路徑中找出以do_開頭的函數,統統加入parser對象中,指定路徑包括 v1_1.shell.py  self, v1_1/contrib/****
>>>>self.parser = subcommand_parser   //parser是更新後的parse對象

>>>>if options.help or not argv: subcommand_parser.print_help()   return 0  //如果是help命令,就直接打印help信息,返回

>>>>args = subcommand_parser.parse_args(argv) //從argv中解析出並返回一個args對象

>>>>if args.func == self.do_help:self.do_help(args)  return 0    //子命令help,打印子命令的help信息,其中args.func就是找到的具體的函數
@utils.arg('--extra-specs',
           dest='extra_specs',
           action='store_true',
           default=False,
           help=_('Get extra-specs of each flavor.'))
@utils.arg('--all',
           dest='all',
           action='store_true',
           default=False,
           help=_('Display all flavors (Admin only).'))
def do_flavor_list(cs, args):
    if args.all:
        flavors = cs.flavors.list(is_public=None)    調用 v1_1.client.Client().flavors==v1_1.flavor.FlavorManager(),調用Manager的list方法
    else:
        flavors = cs.flavors.list()
    _print_flavor_list(flavors, args.extra_specs)
>>>根據args對象,解析出management_url

        management_url = bypass_url if bypass_url else None
        if not service_type:
            os_compute_api_version = (options.os_compute_api_version or
                                      DEFAULT_OS_COMPUTE_API_VERSION)
            try:
                service_type = DEFAULT_NOVA_SERVICE_TYPE_MAP[
                    os_compute_api_version]
            except KeyError:
                service_type = DEFAULT_NOVA_SERVICE_TYPE_MAP[
                    DEFAULT_OS_COMPUTE_API_VERSION]
            service_type = utils.get_service_type(args.func) or service_type
            print(service_type)
>>>>根據options組件client對象
self.cs = client.Client(options.os_compute_api_version, os_username,
                os_password, os_tenant_name, tenant_id=os_tenant_id,
                auth_url=os_auth_url, insecure=insecure,
                region_name=os_region_name, endpoint_type=endpoint_type,
                extensions=self.extensions, service_type=service_type,
                service_name=service_name, auth_system=os_auth_system,
                auth_plugin=auth_plugin, auth_token=auth_token,
                volume_service_name=volume_service_name,
                timings=args.timings, bypass_url=bypass_url,
                os_cache=os_cache, http_log_debug=options.debug,
                cacert=cacert, timeout=timeout)
>>>>args.func(self.cs, args)   向args.func傳遞client和args參數,執行func

>>>>client對象最終會執行下面的東東
class FlavorManager(base.ManagerWithFind):
    resource_class = Flavor
    is_alphanum_id_allowed = True


    def list(self, detailed=True, is_public=True):
        qparams = {}
        if not is_public:
            qparams['is_public'] = is_public
        query_string = "?%s" % urlutils.urlencode(qparams) if qparams else ""
        detail = ""
        if detailed:
            detail = "/detail"
        return self._list("/flavors%s%s" % (detail, query_string), "flavors")


    def get(self, flavor):
        return self._get("/flavors/%s" % base.getid(flavor), "flavor")


    def delete(self, flavor):
        self._delete("/flavors/%s" % base.getid(flavor))


    def _build_body(self, name, ram, vcpus, disk, id, swap,
                    ephemeral, rxtx_factor, is_public):
        return {
            "flavor": {
                "name": name,
                "ram": ram,
                "vcpus": vcpus,
                "disk": disk,
                "id": id,
                "swap": swap,
                "OS-FLV-EXT-DATA:ephemeral": ephemeral,
                "rxtx_factor": rxtx_factor,
                "os-flavor-access:is_public": is_public,
            }
        }




class Manager(utils.HookableMixin):
    resource_class = None
    cache_lock = threading.RLock()


    def __init__(self, api):
        self.api = api   //client構造時候的那個client對象


    def _list(self, url, response_key, obj_class=None, body=None):
        if body:
            _resp, body = self.api.client.post(url, body=body)
        else:
            _resp, body = self.api.client.get(url)


        if obj_class is None:
            obj_class = self.resource_class    //v1_1.flavors::class Flavor(base.Resource):


        data = body[response_key]   //response_key==flavors
        if isinstance(data, dict):
            try:
                data = data['values']
            except KeyError:
                pass


        with self.completion_cache('human_id', obj_class, mode="w"):
            with self.completion_cache('uuid', obj_class, mode="w"):
                return [obj_class(self, res, loaded=True)
                        for res in data if res]
    def _get(self, url, response_key):
        _resp, body = self.api.client.get(url)
        return self.resource_class(self, body[response_key], loaded=True)


    def _create(self, url, body, response_key, return_raw=False, **kwargs):
        self.run_hooks('modify_body_for_create', body, **kwargs)
        _resp, body = self.api.client.post(url, body=body)
        if return_raw:
            return body[response_key]


        with self.completion_cache('human_id', self.resource_class, mode="a"):
            with self.completion_cache('uuid', self.resource_class, mode="a"):
                return self.resource_class(self, body[response_key])


    def _delete(self, url):
        _resp, _body = self.api.client.delete(url)


    def _update(self, url, body, response_key=None, **kwargs):
        self.run_hooks('modify_body_for_update', body, **kwargs)
        _resp, body = self.api.client.put(url, body=body)
        if body:
            if response_key:
                return self.resource_class(self, body[response_key])
            else:
                return self.resource_class(self, body)




class Flavor(base.Resource):
    HUMAN_ID = True


    def __repr__(self):
        return "<Flavor: %s>" % self.name


    @property
    def ephemeral(self):
        return self._info.get("OS-FLV-EXT-DATA:ephemeral", 'N/A')


    @property
    def is_public(self):
        return self._info.get("os-flavor-access:is_public", 'N/A')


    def get_keys(self):
          _resp, body = self.manager.api.client.get(
                            "/flavors/%s/os-extra_specs" %
                            base.getid(self))
        return body["extra_specs"]


    def set_keys(self, metadata):
        utils.validate_flavor_metadata_keys(metadata.keys())


        body = {'extra_specs': metadata}
        return self.manager._create(
                            "/flavors/%s/os-extra_specs" % base.getid(self),
                            body,
                            "extra_specs",
                            return_raw=True)


    def unset_keys(self, keys):
        for k in keys:
            return self.manager._delete(
                            "/flavors/%s/os-extra_specs/%s" % (
                            base.getid(self), k))


    def delete(self):
        self.manager.delete(self)

發佈了59 篇原創文章 · 獲贊 6 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章