nova --debug list
1. 在已经配置好环境变量的终端中,调用过程,可见首先经过认证过程,在获取合法得token之后调用Nova API 中server/detail来获取各个server list.
park@park-ThinkPad-T420:~/openstack/devstack$ nova --debug list
DEBUG (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 settings
518 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 339
DEBUG (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/tokens
DEBUG (connectionpool:362) "POST /v2.0/tokens HTTP/1.1" 401 136
DEBUG (session:377) Request returned failure status: 401
DEBUG (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)
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算是构建完成。