个人见解,可能会有错误。刚刚开始学习nova,就先从api入手学习吧。
1、D:\代码\python-novaclient\python-novaclient\novaclient\v1_1\shell.py,最上边怎么调用不太清楚了,先从这里开始。
def do_list(cs, args):
"""List active servers."""
imageid = None
flavorid = None
#查找镜像
if args.image:
imageid = _find_image(cs, args.image).id
#查找套餐类型
if args.flavor:
flavorid = _find_flavor(cs, args.flavor).id
search_opts = {
'all_tenants': args.all_tenants,
'reservation_id': args.reservation_id,
'ip': args.ip,
'ip6': args.ip6,
'name': args.name,
'image': imageid,
'flavor': flavorid,
'status': args.status,
'tenant_id': args.tenant, #租赁人id
'host': args.host,
'instance_name': args.instance_name}
#过滤条件
filters = {'flavor': lambda f: f['id'],
'security_groups': utils._format_security_groups}
#存放过滤后的结果
formatters = {}
#formatters 字典的关键字
field_titles = []
if args.fields:
for field in args.fields.split(','):
#让field 格式化成某种字符串
field_title, formatter = utils._make_field_formatter(field,
filters)
field_titles.append(field_title)
formatters[field_title] = formatter
id_col = 'ID'
#同一目录下 servers.py里面的list方法,下一个函数讲解这里。
#这里的返回值是server 的列表
servers = cs.servers.list(search_opts=search_opts)
cOnvert= [('OS-EXT-SRV-ATTR:host', 'host'),
('OS-EXT-STS:task_state', 'task_state'),
('OS-EXT-SRV-ATTR:instance_name', 'instance_name'),
('hostId', 'host_id')]
_translate_keys(servers, convert)
#这个columns 应该是列的意思,和打印有关吧。
if field_titles:
columns = [id_col] + field_titles
else:
columns = [id_col, 'Name', 'Status', 'Networks']
formatters['Networks'] = utils._format_servers_list_networks
#这里负责具体输出nova list 命令的输出。
utils.print_list(servers, columns,
formatters, sortby_index=1)
def list(self, detailed=True, search_opts=None):
"""
# detailed 负责是否返回详细服务器信息,reservation_id仅仅返回对应id的。
Get a list of servers.
Optional detailed returns details server info.
Optional reservation_id only returns instances with that
reservation_id.
:rtype: list of :class:`Server`
"""
#search_opt 的内容见上一个函数的绿色字体,是一个字典。
if search_opts is None:
search_opts = {}
qparams = {}
#这里吧search_opt 的字典内容拷贝到qparams
for opt, val in search_opts.iteritems():
if val:
qparams[opt] = val
#这里把我们的选项进行url编码,因为要发送http请求嘛
query_string = "?%s" % urllib.urlencode(qparams) if qparams else ""
detail = ""
if detailed:
detail = "/detail"
#这里继续调用_list方法,该函数下边继续分析。这个函数第一个参数是url,第二个参数是response_key
return self._list("/servers%s%s" % (detail, query_string), "servers")
def _list(self, url, response_key, obj_class=None, body=None):
#根据body是否为空调用post或者get方法,这两个函数都调用一个函数,只不过传参不太一样---_cs_request
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
#参考上边的传参,可以发现这个response_key的内容应该是“servers”
#到这里我们获取了数据。
data = body[response_key]
# NOTE(ja): keystone returns values as list as {'values': [ ... ]}
# unlike other services which just return the list...
#上边的注释讲的很清楚,如果data是个字典,具体的server的内容是里面的对应key='values'的字段值,结果是一个list
if isinstance(data, dict):
try:
data = data['values']
except KeyError:
pass
#这里没有仔细看了,最后一句话是返回结果。with语句和try finally 一样能够处理异常情况的上下文清理工作。
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 post(self, url, **kwargs):
return self._cs_request(url, 'POST', **kwargs)
5、D:\代码\python-novaclient\python-novaclient\novaclient\client.py ---- _cs_request
# 客户端向服务端发送请求序列。
def _cs_request(self, url, method, **kwargs):
if not self.management_url:
self.authenticate()
# Perform the request once. If we get a 401 back then it
# might be because the auth token expired, so try to
# re-authenticate and try again. If it still fails, bail.
try:
#headers 如果没有这个关键就设置为空,然后给headers字典的属性 ‘X-Auth-Token’赋值
kwargs.setdefault('headers', {})['X-Auth-Token'] = self.auth_token
if self.projectid:
kwargs['headers']['X-Auth-Project-Id'] = self.projectid
#这里进一步发送消息。
resp, body = self._time_request(self.management_url + url, method,
**kwargs)
return resp, body
except exceptions.Unauthorized as e:
try:
self.authenticate()
kwargs['headers']['X-Auth-Token'] = self.auth_token
resp, body = self._time_request(self.management_url + url,
method, **kwargs)
return resp, body
except exceptions.Unauthorized:
raise e
def _time_request(self, url, method, **kwargs):
start_time = time.time()
resp, body = self.request(url, method, **kwargs)
self.times.append(("%s %s" % (method, url),
start_time, time.time()))
return resp, body
def request(self, url, method, **kwargs):
kwargs.setdefault('headers', kwargs.get('headers', {}))
kwargs['headers']['User-Agent'] = self.USER_AGENT
kwargs['headers']['Accept'] = 'application/json'
if 'body' in kwargs:
kwargs['headers']['Content-Type'] = 'application/json'
kwargs['data'] = json.dumps(kwargs['body'])
del kwargs['body']
if self.timeout is not None:
kwargs.setdefault('timeout', self.timeout)
# 打印请求部分
self.http_log_req((url, method,), kwargs)
#这里发送http请求并得到相应的response,到此大功告成。
resp =self.http.request(
method,
url,
verify=self.verify_cert,
**kwargs)
#打印相应部分
self.http_log_resp(resp)
#检查响应的内容。
if resp.text:
# TODO(dtroyer): verify the note below in a requests context
# NOTE(alaski): Because force_exceptions_to_status_code=True
# httplib2 returns a connection refused event as a 400 response.
# To determine if it is a bad request or refused connection we need
# to check the body. httplib2 tests check for 'Connection refused'
# or 'actively refused' in the body, so that's what we'll do.
if resp.status_code == 400:
if ('Connection refused' in resp.text or
'actively refused' in resp.text):
raise exceptions.ConnectionRefused(resp.text)
try:
body = json.loads(resp.text)
except ValueError:
pass
body = None
else:
body = None
if resp.status_code >= 400:
raise exceptions.from_response(resp, body, url, method)
return resp, body