novaclient代码解析之----novaclient创建过程

来源:互联网 发布:数据可视化培训 编辑:程序博客网 时间:2024/06/06 08:44

nova --debug list

1. 在已经配置好环境变量的终端中,调用过程,可见首先经过认证过程,在获取合法得token之后调用Nova API 中server/detail来获取各个server list.

park@park-ThinkPad-T420:~/openstack/devstack$ nova --debug listDEBUG (session:186) REQ: curl -g -i --cacert "/opt/stack/data/CA/int-ca/ca-chain.pem" -X GET http://localhost:5000/v2.0 -H "Accept: application/json" -H "User-Agent: python-keystoneclient"RESP BODY: {"version": {"status": "stable", "updated": "2014-04-17T00:00:00Z", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.identity-v2.0+json"}], "id": "v2.0", "links": [{"href": "http://localhost:5000/v2.0/", "rel": "self"}, {"href": "http://docs.openstack.org/", "type": "text/html", "rel": "describedby"}]}}DEBUG (session:186) REQ: curl -g -i --cacert "/opt/stack/data/CA/int-ca/ca-chain.pem" -X GET http://xxxxx:8774/v2/e46fc3bb07da4987831fc4fe65d532ea/servers/detail -H "User-Agent: python-novaclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}067cafad6460a7d21af1f66c4711c78d666d6866"RESP BODY: {"servers": []}+----+------+--------+------------+-------------+----------+| ID | Name | Status | Task State | Power State | Networks |+----+------+--------+------------+-------------+----------++----+------+--------+------------+-------------+----------+

2. novaclient在哪里呢?他是怎么运行起来的呢?代码是最好的答案。

$ vim /usr/local/bin/nova

1 #!/usr/bin/python  2   3 # -*- coding: utf-8 -*-  4 import re  5 import sys  6   7 from novaclient.shell import main  8 ...
见第7行,继续追踪novaclient.shell.main

$ vim /home/park/python-novaclient/novaclient/shell.py, 

813 def main():814     try:815         argv = [encodeutils.safe_decode(a) for a in sys.argv[1:]]816         OpenStackComputeShell().main(argv)
816行,继续找这个main method, 还好继续在这个文件中....

516     def main(self, argv):517         # Parse args once to find version and debug settings518         parser = self.get_base_parser()519         (options, args) = parser.parse_known_args(argv)520         self.setup_debugging(options.debug)...
靠谱了,开始解析变量和参数了。没错,这里就是novaclient真正的入口。


按照上面的调用关系,我关心两个问题:

1. novaclient是如何根据环境变量和参数进行用户认证的?

2. novaclient各种具体命令是如何和nova API进行交互的呢?

第一个用户认证问题这里不想详细描述,有兴趣同学可以利用下面命令逐一测试


在一个未设置相关环境变量得终端中输入
$ nova --debug --os-username abc --os-project-name 123 --os-auth-url http://localhost:5000/v2.0 list
正常情况下应该会有如下错误,不再一一阐述。


Namespace(all_tenants=0, bypass_url='', debug=True, deleted=False, endpoint_type='publicURL', fields=None, flavor=None, func=<function do_list at 0x7fa3fd8856e0>, help=False, host=None, image=None, insecure=False, instance_name=None, ip=None, ip6=None, minimal=False, name=None, os_auth_system='', os_auth_token='', os_auth_url=u'http://<span style="font-family: Arial, Helvetica, sans-serif;">localhost</span><span style="font-family: Arial, Helvetica, sans-serif;">:5000/v2.0', os_cacert=None, os_cache=False, os_cert=None, os_compute_api_version='1.1', os_domain_id=None, os_domain_name=None, os_key=None, os_password='', os_project_domain_id=None, os_project_domain_name=None, os_project_id=None, os_project_name=u'123', os_region_name='', os_tenant_id='', os_tenant_name='', os_trust_id=None, os_user_domain_id=None, os_user_domain_name=None, os_user_id=None, os_username=u'abc', reservation_id=None, service_name='', service_type=None, sort=None, status=None, tenant=None, timeout=600, timings=False, user=None, volume_service_name='')</span>OS Password: DEBUG (session:186) REQ: curl -g -i -X GET http://<span style="font-family: Arial, Helvetica, sans-serif;">localhost</span>:5000/v2.0 -H "Accept: application/json" -H "User-Agent: python-keystoneclient"INFO (connectionpool:188) Starting new HTTP connection (1): <span style="font-family: Arial, Helvetica, sans-serif;">localhost</span>DEBUG (connectionpool:362) "GET /v2.0 HTTP/1.1" 200 339DEBUG (session:214) RESP: [200] content-length: 339 vary: X-Auth-Token keep-alive: timeout=5, max=100 server: Apache/2.4.7 (Ubuntu) connection: Keep-Alive date: Wed, 28 Jan 2015 08:22:41 GMT content-type: application/json RESP BODY: {"version": {"status": "stable", "updated": "2014-04-17T00:00:00Z", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.identity-v2.0+json"}], "id": "v2.0", "links": [{"href": "http://localhost:5000/v2.0/", "rel": "self"}, {"href": "http://docs.openstack.org/", "type": "text/html", "rel": "describedby"}]}}DEBUG (v2:76) Making authentication request to http://localhost:5000/v2.0/tokensDEBUG (connectionpool:362) "POST /v2.0/tokens HTTP/1.1" 401 136DEBUG (session:377) Request returned failure status: 401DEBUG (shell:910) Authentication failure: Could not find user: abc (Disable debug mode to suppress these details.) (HTTP 401)Traceback (most recent call last):  File "/opt/stack/python-novaclient/novaclient/shell.py", line 907, in main    OpenStackComputeShell().main(argv)  File "/opt/stack/python-novaclient/novaclient/shell.py", line 834, in main    args.func(self.cs, args)  File "/opt/stack/python-novaclient/novaclient/v1_1/shell.py", line 1377, in do_list    sort_dirs=sort_dirs)  File "/opt/stack/python-novaclient/novaclient/v1_1/servers.py", line 620, in list    return self._list("/servers%s%s" % (detail, query_string), "servers")  File "/opt/stack/python-novaclient/novaclient/base.py", line 66, in _list    _resp, body = self.api.client.get(url)  File "/opt/stack/python-keystoneclient/keystoneclient/adapter.py", line 130, in get    return self.request(url, 'GET', **kwargs)  File "/opt/stack/python-novaclient/novaclient/client.py", line 152, in request    **kwargs)  File "/opt/stack/python-keystoneclient/keystoneclient/adapter.py", line 166, in request    resp = super(LegacyJsonAdapter, self).request(*args, **kwargs)  File "/opt/stack/python-keystoneclient/keystoneclient/adapter.py", line 89, in request    return self.session.request(url, method, **kwargs)  File "/opt/stack/python-keystoneclient/keystoneclient/utils.py", line 318, in inner    return func(*args, **kwargs)  File "/opt/stack/python-keystoneclient/keystoneclient/session.py", line 298, in request    token = self.get_token(auth)  File "/opt/stack/python-keystoneclient/keystoneclient/session.py", line 577, in get_token    _("Authentication failure: %s") % exc)AuthorizationFailure: Authentication failure: Could not find user: abc (Disable debug mode to suppress these details.) (HTTP 401)ERROR (AuthorizationFailure): Authentication failure: Could not find user: abc (Disable debug mode to suppress these details.) (HTTP 401)

继续看main函数,

756         self.cs = client.Client(757             options.os_compute_api_version,758             os_username, os_password, os_tenant_name,759             tenant_id=os_tenant_id, user_id=os_user_id,760             auth_url=os_auth_url, insecure=insecure,761             region_name=os_region_name, endpoint_type=endpoint_type,762             extensions=self.extensions, service_type=service_type,763             service_name=service_name, auth_system=os_auth_system,764             auth_plugin=auth_plugin, auth_token=auth_token,765             volume_service_name=volume_service_name,766             timings=args.timings, bypass_url=bypass_url,767             os_cache=os_cache, http_log_debug=options.debug,768             cacert=cacert, timeout=timeout,769             session=keystone_session, auth=keystone_auth)770 

一直到这里之前,novaclient都在纠缠一些变量的获取,这里是生成了一个Client的对象,这个对象是干嘛的呢?

$ vim /home/park/python-novaclient/novaclient/client.py, 

def get_client_class(version):    version_map = {        '1.1': 'novaclient.v2.client.Client',        '2': 'novaclient.v2.client.Client',        '3': 'novaclient.v2.client.Client',    }    try:        client_path = version_map[str(version)]    except (KeyError, ValueError):        msg = _("Invalid client version '%(version)s'. must be one of: "                "%(keys)s") % {'version': version,                               'keys': ', '.join(version_map.keys())}        raise exceptions.UnsupportedVersion(msg)    return importutils.import_class(client_path)def Client(version, *args, **kwargs):    client_class = get_client_class(version)    return client_class(*args, **kwargs)
原来是为了找到和version相匹配的Client,所有的都指向了V2,我们来看看这个v2.client都干了什么。

$ vim /home/park/python-novaclient/novaclient/V2/client.py, 

    def __init__(self, username=None, api_key=None, project_id=None,                 auth_url=None, insecure=False, timeout=None,                 proxy_tenant_id=None, proxy_token=None, region_name=None,                 endpoint_type='publicURL', extensions=None,                 service_type='compute', service_name=None,                 volume_service_name=None, timings=False, bypass_url=None,                 os_cache=False, no_cache=True, http_log_debug=False,                 auth_system='keystone', auth_plugin=None, auth_token=None,                 cacert=None, tenant_id=None, user_id=None,                 connection_pool=False, session=None, auth=None,                 **kwargs):        """        :param str username: Username        :param str api_key: API Key        :param str project_id: Project ID        :param str auth_url: Auth URL        :param bool insecure: Allow insecure        :param float timeout: API timeout, None or 0 disables        :param str proxy_tenant_id: Tenant ID        :param str proxy_token: Proxy Token        :param str region_name: Region Name        :param str endpoint_type: Endpoint Type        :param str extensions: Exensions        :param str service_type: Service Type        :param str service_name: Service Name        :param str volume_service_name: Volume Service Name        :param bool timings: Timings        :param str bypass_url: Bypass URL        :param bool os_cache: OS cache        :param bool no_cache: No cache        :param bool http_log_debug: Enable debugging for HTTP connections        :param str auth_system: Auth system        :param str auth_plugin: Auth plugin        :param str auth_token: Auth token        :param str cacert: cacert        :param str tenant_id: Tenant ID        :param str user_id: User ID        :param bool connection_pool: Use a connection pool        :param str session: Session        :param str auth: Auth        """        # FIXME(comstud): Rename the api_key argument above when we        # know it's not being used as keyword argument        # NOTE(cyeoh): In the novaclient context (unlike Nova) the        # project_id is not the same as the tenant_id. Here project_id        # is a name (what the Nova API often refers to as a project or        # tenant name) and tenant_id is a UUID (what the Nova API        # often refers to as a project_id or tenant_id).        password = api_key        self.projectid = project_id        self.tenant_id = tenant_id        self.user_id = user_id        self.flavors = flavors.FlavorManager(self)        self.flavor_access = flavor_access.FlavorAccessManager(self)        self.images = images.ImageManager(self)        self.limits = limits.LimitsManager(self)        self.servers = servers.ServerManager(self)        self.versions = versions.VersionManager(self)        # extensions        self.agents = agents.AgentsManager(self)        self.dns_domains = floating_ip_dns.FloatingIPDNSDomainManager(self)        self.dns_entries = floating_ip_dns.FloatingIPDNSEntryManager(self)        self.cloudpipe = cloudpipe.CloudpipeManager(self)        self.certs = certs.CertificateManager(self)        self.floating_ips = floating_ips.FloatingIPManager(self)        self.floating_ip_pools = floating_ip_pools.FloatingIPPoolManager(self)        self.fping = fping.FpingManager(self)        self.volumes = volumes.VolumeManager(self)        self.volume_snapshots = volume_snapshots.SnapshotManager(self)        self.volume_types = volume_types.VolumeTypeManager(self)        self.keypairs = keypairs.KeypairManager(self)        self.networks = networks.NetworkManager(self)        self.quota_classes = quota_classes.QuotaClassSetManager(self)        self.quotas = quotas.QuotaSetManager(self)        self.security_groups = security_groups.SecurityGroupManager(self)        self.security_group_rules = \            security_group_rules.SecurityGroupRuleManager(self)        self.security_group_default_rules = \            security_group_default_rules.SecurityGroupDefaultRuleManager(self)        self.usage = usage.UsageManager(self)        self.virtual_interfaces = \            virtual_interfaces.VirtualInterfaceManager(self)        self.aggregates = aggregates.AggregateManager(self)        self.hosts = hosts.HostManager(self)        self.hypervisors = hypervisors.HypervisorManager(self)        self.hypervisor_stats = hypervisors.HypervisorStatsManager(self)        self.services = services.ServiceManager(self)        self.fixed_ips = fixed_ips.FixedIPsManager(self)        self.floating_ips_bulk = floating_ips_bulk.FloatingIPBulkManager(self)        self.os_cache = os_cache or not no_cache        self.availability_zones = \            availability_zones.AvailabilityZoneManager(self)        self.server_groups = server_groups.ServerGroupsManager(self)        # Add in any extensions...        if extensions:            for extension in extensions:                if extension.manager_class:                    setattr(self, extension.name,                            extension.manager_class(self))        self.client = client._construct_http_client(            username=username,            password=password,            user_id=user_id,            project_id=project_id,            tenant_id=tenant_id,            auth_url=auth_url,            auth_token=auth_token,            insecure=insecure,            timeout=timeout,            auth_system=auth_system,            auth_plugin=auth_plugin,            proxy_token=proxy_token,            proxy_tenant_id=proxy_tenant_id,            region_name=region_name,            endpoint_type=endpoint_type,            service_type=service_type,            service_name=service_name,            volume_service_name=volume_service_name,            timings=timings,            bypass_url=bypass_url,            os_cache=self.os_cache,            http_log_debug=http_log_debug,            cacert=cacert,            connection_pool=connection_pool,            session=session,            auth=auth,            **kwargs)
构建基础信息,extension信息等,extension信息指向了一些manager,这里以后再阐述。

最后,通过self.client = client._construct_http_client创建了一个基于http的client,到这里client已经具备了http和api通信的能力了。

但是仍然没有解决上面提到的第二个问题,具体命令是如何执行的呢?回到novaclient/shell.py中:

在创建Client之后会找到下面一小行代码

args.func(self.cs, args)
这里才是真正执行具体命令的接口。

至此,一个完整的Client算是构建完成。




0 0
原创粉丝点击