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)