阅读 368

深挖Openstack Nova - 实例创建(2)

--------------- 紧接上篇 nova实例创建(1)--------------------


3. 分析show方法

在2.1中的image = self.image_service.show(context, internal_id)中,由于S3是EC2的存储平台,所以调用/nova/image/s3.py的show方法

def show(self, context, image_id):
    # 参数image_id:实例镜像的ID值,是由ec2_id值变换格式后而来
    # id_to_glance_id:根据实例镜像image_id,查询数据库,找到匹配的S3Image的表信息
    # 获取它的S3Image.uuid并返回,赋值给image_uuid
    image_uuid = ec2utils.id_to_glance_id(context, image_id)

    # 这里的service是获取的service或者是GlanceImageService的对象
    #(由glance.get_default_image_service()而来)
    iamge = self.service.show(context, image_uuid)

    # 转换镜像中的image_uuid到image_id
    # 更新image当中的相关属性,返回更新后的image数据
    return self._translate_uuid_to_id(context, image)复制代码

其中,在image = self.service.show(context, image_uuid)中可查到:

self.service = service or glance.get_default_image_service()复制代码

这里的get_default_image_service()对应的是/nova/image/glance.py文件。

可以看到,这里的service获取的类GlanceImageService的实例对象,调用的是该对象里面的show方法:

# 以字典的形式返回给定image_id的镜像image数据
# 调用glance客户端,获取image_id指定的镜像元数据(此时为JSON格式)
# 转换从glance下载的镜像数据为python可处理的字典格式
def show(self, context, image_id, include_location=False,
        show_deleted=True)复制代码


3.1 设置glance客户端的版本

# glance客户端有两个版本
# 初始版本为1
version = 1
# include_locations:可选参数
# 决定image service API是否支持返回的镜像字典信息中包括地址
# 如果支持,选择版本为2的glance客户端
if include_locations:
    version = 2
复制代码


3.2 调用call方法去连接glance客户端,获取image数据

# call:调用一个glance客户端的对象,调用其中的get方法,获取镜像image元数据
# 这个方法尝试一定次数连接glance,从glance客户端下载image
# 如果连接成功,这时会获取glance客户端对象
try:
    image = self._client.call(context, version, 'get', image_id)
except Exception:
    _reraise_translated_image_exception(image_id)
复制代码


3.3 获取image数据后,做正确性验证

# show_deleted:可选参数,当参数为true,表示处于已删除状态的image也显示出来
# 检测image是否被删除
if not show_deleted and getattr(image, 'deleted', False):
    raise exception.ImageNotFound(image_id=image_id)

# 检测image的可用性
if not _is_image_available(context, image):
    raise exception.ImageNotFound(image_id=image_id)
复制代码


3.4 image数据格式转换

# 把通过glance客户端获取的镜像image元数据转换为python可处理的数据格式(原为JSON格式)
image = _translate_form_glance(iamge,
                                include_locations=include_locations)复制代码


3.5 处理返回的image数据中location地址信息,最后返回image

# 通过镜像image拼装location地址
if include_locations:
    locations = image.get('locations', None) or []
    du = image.get('direct_url', None)
    if du:
        locations.append({'url': du, 'metadata': {}})
    iamge['locations'] = locations

return image复制代码


4. 分析call方法

从3.2的image = self._client.call(context, version, 'get', image_id)可知通过call方法去连接glance客户端,从而获取镜像image元数据

# 调用一个glance客户端的对象,调用其中的get方法,获取image镜像
# 尝试一定次数连接glance,如果连接成功,则会获取glance客户端对象,并返回client.images.get
# 如果到达最大尝试连接次数都没有连接成功,则会抛出异常
def call(self, context, version, method, *args, **kwargs):复制代码


4.1 定义异常的类型

# 各种异常
retry_excs = (glanceclient.exc.ServiceUnavailable,
        glanceclient.exc.InvalidEndpoint,
        glanceclient.exc.CommunicatinError)复制代码


4.2 设置重试次数

# 定义当从glance下载image镜像时,重试的次数
# 默认为0,表示会重试无数次
retries = CONF.glance.num_retries
if retries < 0:
    LOG.warning(_LW("Treating negative config value (%(retries)s) for "
                    "glance.num_retries" as 0."),
                {'retries': retries})
    retries = 0
# 加1是为了避免无数次下载尝试的可能性,从而定位为1次重试机会
num_attempts = retries + 1复制代码


4.3 循环尝试获取glance客户端对象

for attempt in range(1, num_attempts + 1):
    # 得到glance客户端对象,如果没有定义,则新建立一个客户端对象
    client = self.client or self._create_onetime_client(context,
                                                        version)
    # 传进来的method是get,所有返回的是getattr
    # 调用的是客户端对象类中的get方法,获取image镜像
    try:
        return getattr(client.images, method)(*args, **kwargs)复制代码

其中,self._create_onetime_client(context, version)表示的是尝试建立并返回一个正确的glanceclient.Client客户端:

# 建立并返回一个正确的glanceclient.Client客户端,它会被用于一次call
def _create_onetime_client(self, context, version):
    # 返回的是不断循环的api_servers列表中的元素(因为执行了:itertools.cycle)
    if self.api_servers is None:
        self.api_servers = get_api_servers()

    # 从api_servers中随机获取某一个元素,进而获取一组host、post、use_ssl的值
    # 用于后续建立glance客户端
    self.host, self.post, self.use_ssl = next(self.api_servers)
    return _create_glance_client(context,
                                 self.host, self.port,
                                 self.use_ssl, version)复制代码

深入_create_glance_client方法,该方法实现了实例化一个正确的新的glanceclient.Client对象

# 实例化一个正确的新的glanceclient.Client对象
# 在python-glanceclient中一共定义了两个版本的客户端:
# glanceclient.v1和glanceclient.v2
def _create_glance_client(context, host, post, use_ssl, version=1):复制代码

(1)决定scheme类型是采用http还是https

params = {}
if use_ssl:
    # https协议是由SSL-HTTP协议构建的
    scheme = 'https'
    # 定义是否执行不安全的SSL协议(https)
    params['insecure'] = CONF.glance.api_insecure
    params['ssl_comopression'] = False
    sslutils.is_enabled(CONF)
    if CONF.ssl.cert_file:
        params['cert_file'] = CONF.ssl.cert_file
    if CONF.ssl.key_file:
        params['key_file'] = CONF.ssl.key_file
    if CONF.ssl.ca_file:
        params['cacert'] = CONF.ssl.ca_file
else:
    scheme = 'http'
复制代码

(2)定义身份认证策略

# 定义身份认证所使用的策略是noauth或者是keystone
if CONF.auth_strategy == 'keystone':
    params['token'] = context.auth_token
    params['identity_headers'] = generate_identity_headers(context)
复制代码

(3)判断host是否需要转换为ipv6格式

# 转换ipv6格式:用[]包围住
if netutils.is_valid_ipv6(host):
    host = '[%s]' % host
复制代码

(4)组装合适的URL形式,并调用glanceclient.Client去获取数据

# endpoint:访问glance api服务的端点
# 组成能够访问glance api service的URL形式
endpoint = '%s://%s:%s' % (scheme, host, post)
# 获取正确的glanceclient.version.client.Client类
return glanceclient.Client(str(version), endpoint, **params)复制代码


4.4 对循环过程中出现的异常做处理

except retry_excs as e:
    host = self.host
    port = self.port

    # 根据重试次数区分不同的错误信息
    if attempt < num_attempts:
        extra = "retrying"
    else:
        extra = 'done trying'

    LOG.exception(_LE("Error contacting glance server "
                      "'%(host)s:%(port)s' for '%(method)s', "
                      "%(extra)s."),
                 {'host': host, 'port': port,
                  'method': method, 'extra': extra})
复制代码


4.5 处理当达到最大连接尝试数的情况

# 如果达到了最大的尝试下载次数,则会抛出异常,提示glance连接失败
if attempt == num_attempts:
    raise exception.GlanceConnectionFailed(
            host=host, port=port, reason=six.text_type(e))
time.sleep(1)复制代码



(未完待续...)

文章分类
后端
文章标签