--------------- 緊接上篇 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)複製程式碼
(未完待續...)