深挖Openstack Nova - 例項建立(2)

self_blank發表於2018-06-04

--------------- 緊接上篇 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)複製程式碼



(未完待續...)

相關文章