1. 程式人生 > >Nova API服務 之 建立虛擬機器流程(1)

Nova API服務 之 建立虛擬機器流程(1)

本小節,將以虛擬機器建立請求為例,分析底層Controller類的HTTP請求處理方法。

一、回憶總結

1、處理HTTP請求的核心工作都在底層Controller物件中定義。Resource物件在底層Controller物件的基礎上,實現

據的轉化工作。

2、Resource物件首相將傳入的物件反序列化。然後將資料交給底層Controller物件中相應的處理方法處理。最後將

Controller物件處理方法返回的結果序列化。

二、建立虛擬機器流程

1、以建立虛擬機器請求為例,分析底層controller物件的定義。底層controller類中,處理虛擬機器建立請求的方法是

create方法。

2、servers.Controller類的create方法

每個資源都對應一個底層的Controller類。servers資源的底層Controller類定義在servers包中。處理虛擬機器建立

求的方法為create方法。定義如下:

class Controller(wsgi.Controller):

    _view_builder_class = views_servers.ViewBuilder
	@wsgi.response(202)
	@wsgi.serialozers(xml=FullServerTemplate
	@wsgi.deserialozers(xml=CreateDeserializer)
    def create(self, req, body):
        #為給定的使用者建立新的虛擬機器  檢查HTTP訊息體是否合法
        if not self.is_valid_body(body, 'server'):
            raise exc.HTTPUnprocessableEntity()
		#獲取客戶端傳入的虛擬機器引數
        context = req.environ['nova.context']
        server_dict = body['server']
		
        ...
		#獲取和檢查虛擬機器名
        name = server_dict['name']
        self._validate_server_name(name)
        name = name.strip()
		#獲取虛擬機器的磁碟映象uuid
        image_uuid = self._image_from_req_data(body)

        ....
		
		# 獲取客戶端需求的網路
        requested_networks = self._determine_requested_networks(server_dict)
		#獲取和驗證客戶端指定的虛擬機器IP
        (access_ip_v4, ) = server_dict.get('accessIPv4'),
        if access_ip_v4 is not None:
            self._validate_access_ipv4(access_ip_v4)

        (access_ip_v6, ) = server_dict.get('accessIPv6'),
        if access_ip_v6 is not None:
            self._validate_access_ipv6(access_ip_v6)
		#獲取虛擬機器規格ID
        flavor_id = self._flavor_id_from_req_data(body)

        # optional openstack extensions:
        key_name = self._extract(server_dict, 'os-keypairs', 'key_name')
        availability_zone = self._extract(server_dict, 'os-availability-zone',
                                          'availability_zone')
        user_data = self._extract(server_dict, 'os-user-data', 'user_data')
        self._validate_user_data(user_data)

        image_uuid_specified = bool(image_uuid)
        legacy_bdm, block_device_mapping = self._extract_bdm(server_dict,
            image_uuid_specified)

        ret_resv_id = False
        # min_count and max_count are optional.  If they exist, they may come
        # in as strings.  Verify that they are valid integers and > 0.
        # Also, we want to default 'min_count' to 1, and default
        # 'max_count' to be 'min_count'.
        min_count = 1
        max_count = 1
        if self.ext_mgr.is_loaded('os-multiple-create'):
            ret_resv_id = server_dict.get('return_reservation_id', False)
            min_count = server_dict.get('min_count', 1)
            max_count = server_dict.get('max_count', min_count)

        try:
            min_count = utils.validate_integer(
                min_count, "min_count", min_value=1)
            max_count = utils.validate_integer(
                max_count, "max_count", min_value=1)
        except exception.InvalidInput as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        if min_count > max_count:
            msg = _('min_count must be <= max_count')
            raise exc.HTTPBadRequest(explanation=msg)

        auto_disk_config = False
        if self.ext_mgr.is_loaded('OS-DCF'):
            auto_disk_config = server_dict.get('auto_disk_config')

        scheduler_hints = {}
        if self.ext_mgr.is_loaded('OS-SCH-HNT'):
            scheduler_hints = server_dict.get('scheduler_hints', {})

        check_server_group_quota = self.ext_mgr.is_loaded(
                'os-server-group-quotas')
        try:
            #獲取虛擬機器規格資訊
			_get_inst_type = flavors.get_flavor_by_flavor_id
            inst_type = _get_inst_type(flavor_id, ctxt=context,
                                       read_deleted="no")
			#呼叫compute API建立虛擬機器
            (instances, resv_id) = self.compute_api.create(context,
                        ...)
        ...
        except UnicodeDecodeError as error:
        ...
		#將虛擬機器資訊轉化為字典
        server = self._view_builder.create(req, instances[0])

        if CONF.enable_instance_password:
            server['server']['adminPass'] = password
		#將虛擬機器資訊封裝成ResponseObject物件
        robj = wsgi.ResponseObject(server)
		#新增訪問當前虛擬機器資源的url
        return self._add_location(robj)
	
(1)、這個類繼承自wsgi.Controller,該類位於nova/nova/api/openstack/wsgi.py。首先為create方法指定了序列

化物件、反序列化物件和預設的HTTP Code。接著我們進入create方法,來分析程式碼。

先來看看context = req.eviron['nova.context']究竟拿到了什麼資訊,這個上下文資訊物件是由類

nova.context.RequestContext例項化而來。我們來把獲取到的context打印出來,這樣方便大家理解。

user_id = afc380206e2549ad930396d9050d20cf  
project_id = 0e492e86f22e4d19bd523f1e7ca64566  
roles = [u'admin', u'KeystoneAdmin', u'KeystoneServiceAdmin']  
read_deleted = no  
remote_address = 172.21.6.145  
timestamp = 2013-06-23 16:36:37.399405  
request_id = req-f0255b14-833d-4fff-b973-23c35f70ddda  
auth_token = 7e7bb3cf84ab43269010bb55410064b3  
service_catalog = [{u'endpoints': [{u'adminURL': u'http://172.21.5.161:8776/v1/0e492e86f22e4d19bd523f1e7ca64566', 
                    u'region': u'RegionOne', u'id': u'753a1ad55e91469794e2eb7ac4c3df92',
                    u'internalURL': u'http://172.21.5.161:8776/v1/0e492e86f22e4d19bd523f1e7ca64566',
                    u'publicURL': u'http://172.21.6.145:8776/v1/0e492e86f22e4d19bd523f1e7ca64566'}],
                    u'endpoints_links': [], u'type': u'volume', u'name': u'cinder'}]  
instance_lock_checked = False  
quota_class = None  
user_name = admin  
project_name = admin  
is_admin = True

(2) is_valid_body方法

該方法檢查客戶端傳入的訊息體是否合法,這個方法位於Controller類的基類wsgi.Controller中。is_valid_body方

法首先檢查訊息體中是否含有server欄位,然後檢查server欄位的內容是否為字典。

Nova中,所有的訊息(包括客戶端傳給Nova API伺服器的訊息,以及伺服器返回給客戶端的訊息)都是以資源名作為

鍵值的{key:value}對。

接下來的一系列方法都是獲取並驗證客戶端傳入的虛擬機器引數。其中,server_dict儲存了虛擬機器引數,包括虛擬機器

名、映象uuid、需要的網路、固定ip、虛擬機器規格等等。

(3) _validate_server_name方法

  該方法檢查虛擬機器名的長度是否越界。

(4) _validate_access_ipv4&_validate_access_ipv6方法

  該方法驗證傳入的固定ip格式是否合法。

(5) _favor_id_from_req_data方法

該方法對HTTP請求中包含的虛擬機器規格id進行過濾解析。所謂規格id就是url包含的id或者uuid的值,如

http://www.foo.com/bar/123?q=4,該方法則返回123。

(6) get_instance_type_get_by_flavor_id方法

該方法位於nova/nova/compute/instance_types.py中,該方法呼叫了nova/nova/db/api.py下的

instance_type_get_by_flavor_id方法,其功能是根據虛擬機器規格id,從資料庫中查詢對應規格資訊後返回。

(7) 呼叫Nova Compute的API處理虛擬機器建立請求。 

  __init__方法:self.compute_api = compute.API()

 Compute API的create方法會返回一個建立虛擬機器資訊列表。列表中的每個元素都是資料庫Model物件。資料庫

Model物件是Nova與資料庫互動的資料格式,不可序列化。

為了便於顯示以及序列化,self._view_builder.create物件的create方法將Model物件的資料轉換為字典。

(8) Nova中,每個底層的Controller物件的_view_builder物件型別都是由_view_builder_class類成員變數指定。

對於server資源的底層Controller物件,其_view_builder_class類成員變數的值為views_servers.ViewBuilder:

_view_builder_class = views_servers.ViewBuilder. 

class ViewBuilder(common.ViewBuilder):
    def create(self, request, instance):
        """View that should be returned when an instance is created."""
        return {
            "server": {
                "id": instance["uuid"],
                "links": self._get_links(request,
                                         instance["uuid"],
                                         self._collection_name),
            },
        }

create方法返回的是一個關鍵字為server的鍵值對。返回結果中,包含了虛擬機器的uuid,以及虛擬機器的url連結。

三、總結:

Nova API中底層Controller物件定義的HTTP請求的處理方法的小結。

1、檢查客戶端傳入的引數是否合法有效

2、呼叫nova其他子服務的API處理客戶端的HTTP請求。

3、將Nova其他子服務的API返回結果轉化為視覺化的字典。(反序列化)