另一个地址:http://neuromancer.sinaapp.com/?p=64
从nova client的入口查看
cat /usr/bin/nova
cat /usr/bin/nova #!/usr/bin/python # PBR Generated from ‘console_scripts‘ import sys from novaclient.shell import main if __name__ == "__main__": sys.exit(main())
其中导入了novaclient.shell这个文件中导入了main方法,进入novaclient.shell.py查看
def main(): try: OpenStackComputeShell().main(map(strutils.safe_decode, sys.argv[1:])) except Exception as e: logger.debug(e, exc_info=1) print("ERROR: %s" % strutils.safe_encode(six.text_type(e)), file=sys.stderr) sys.exit(1) OpenStackComputeShell.main() 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, 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)
self.cs是从client中创建出的一个Client实例,进入novaclient.client.py查看这个实例的具体方法
def get_client_class(version): version_map = { ‘1.1‘: ‘novaclient.v1_1.client.Client‘, ‘2‘: ‘novaclient.v1_1.client.Client‘, ‘3‘: ‘novaclient.v3.client.Client‘, } try: client_path = version_map[str(version)] except (KeyError, ValueError): msg = "Invalid client version ‘%s‘. must be one of: %s" % ( (version, ‘, ‘.join(version_map.keys()))) raise exceptions.UnsupportedVersion(msg) return utils.import_class(client_path) def Client(version, *args, **kwargs): client_class = get_client_class(version) return client_class(*args, **kwargs)
用的是v1_1这个版本的api,对应的是novaclient.v1_1.client.py里的Client类
class Client(object): """ Top-level object to access the OpenStack Compute API. Create an instance with your creds:: >>> client = Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) Then call methods on its managers:: >>> client.servers.list() ... >>> client.flavors.list() ... """
注释里讲了怎么使用python命令行调用nova的client
client里给流入的指令分了很多类,以flavors为例,看nova flavor-list这个命令的流程
self.flavors = flavors.FlavorManager(self) flavors.list() 进入novaclient.v1_1.flavors.py class FlavorManager(base.ManagerWithFind): """ Manage :class:`Flavor` resources. """ resource_class = Flavor is_alphanum_id_allowed = True def list(self, detailed=True, is_public=True): """ Get a list of all flavors. :rtype: list of :class:`Flavor`. """ qparams = {} # is_public is ternary - None means give all flavors. # By default Nova assumes True and gives admins public flavors # and flavors from their own projects only. 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")
self._list进入novaclient.base.py
class Manager(utils.HookableMixin): """ Managers interact with a particular type of API (servers, flavors, images, etc.) and provide CRUD operations for them. """ resource_class = None def __init__(self, api): self.api = api 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 data = body[response_key] # NOTE(ja): keystone returns values as list as {‘values‘: [ ... ]} # unlike other services which just return the list... 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]
novaclient.v1_1.flavors.py里FlavorManager的resource_class = Flavor即class Flavor(base.Resource)
所以最后obj_class为Flavor
调用api的过程:
if body: _resp, body = self.api.client.post(url, body=body) else: _resp, body = self.api.client.get(url)
通过self.api到了nova的api里nova.api.openstack.compute.__init__.py if init_only is None or ‘flavors‘ in init_only: self.resources[‘flavors‘] = flavors.create_resource() mapper.resource("flavor", "flavors", controller=self.resources[‘flavors‘], collection={‘detail‘: ‘GET‘}, member={‘action‘: ‘POST‘})找到nova.api.openstack.flavors.py
@wsgi.serializers(xml=MinimalFlavorsTemplate) def index(self, req): """Return all flavors in brief.""" limited_flavors = self._get_flavors(req) return self._view_builder.index(req, limited_flavors)
它最后会返回一个存放flavors信息的字典,这些原始数据经过提取和加工,最后在终端被打印出来
nova.api.openstack.compute.views.flavors.py
def _list_view(self, func, request, flavors): """Provide a view for a list of flavors.""" flavor_list = [func(request, flavor)["flavor"] for flavor in flavors] flavors_links = self._get_collection_links(request, flavors, self._collection_name, "flavorid") flavors_dict = dict(flavors=flavor_list) if flavors_links: flavors_dict["flavors_links"] = flavors_links return flavors_dict
添加一个新的client流程:
功能:快速备份虚拟机,三个参数,虚拟机uuid、备份的名字、备份的描述,调用地方和方法如下:
curl -i http://<nova_ip>:8774/v2/<tenant_id>/servers/<user_id>/backup_instance -X POST -H "X-Auth-Project-Id: admin" -H "Content-Type: application/json" -H "User-Agent: python-novaclient" -H "Accept: application/json" -H "X-Auth-Token: <token>" -d ‘{"name" : "backup", "description" : "backup‘s description"}‘
novaclient.shell.py
class OpenStackComputeShell(object) 的get_subcommand_parser 方法里指定了actions_module
转到novaclient.v1_1.shell.py
增加一个新的方法,装饰器里是需要的参数,有顺序,和执行时的参数顺序一致
@utils.arg(‘server‘, metavar=‘<server>‘, help=‘ID of server.‘) @utils.arg(‘displayname‘, metavar=‘<name>‘, help=‘Display name for backup.‘) @utils.arg(‘description‘, default=None, metavar=‘<description>‘, help=‘Description for backup.(Default None)‘) def do_backup_instance(cs, args): """Make a quick backup for instance.""" cs.servers.backup_instance(args.server, args.displayname, args.description)
这个功能是加在servers部分里的,转到novaclient.v1_1.servers.py
在ServerManager类里添加
def backup_instance(self, server, backup_name, backup_description): """ Backup a server instance quickly. :param server: The :class:`Server` (or its ID) to share onto. :param backup_name: Name of the backup image :param backup_description: The backup description """ body = {‘name‘: backup_name, ‘description‘: backup_description} response_key = "id" return self._create("/servers/%s/backup_instance" % base.getid(server), body, response_key, return_raw=True)
response_key是指返回数据里的key,这里返回的数据是{‘id‘: "*****"},所以response_key = "id"
因为这个api返回的是一个json字符串,不能通过novaclient.base.py里Manager类里的方法把数据提取出来(它需要字典),于是把return_raw这个参数设置为True
然后就可以在nova的命令行里看到这个新的功能了:nova backup-instance;使用方法:nova backup-instance <server_id> <bak_name> <bak_description>