Openstack虛擬機器建立中的RPC呼叫
阿新 • • 發佈:2019-01-31
虛擬機器建立過程如圖1-1
圖1-1
如上圖,nova-api在收到客戶端建立虛擬機器的HTTP請求後,通過RPC呼叫nova.conductor.manager.ComputeTaskManager中的build_instances()方法。nova-conductor會在build_instance()生成request_spec字典,其中包括了詳細的虛擬機器資訊,nova-scheduler會根據這些資訊為虛擬機器選擇一個最佳的主機,然後nova-conductor再通過RPC呼叫nova-compute建立虛擬機器。虛擬機器的建立過程即nova中各元件的RPC呼叫過程。
程式碼分析
nova-api接收到建立虛擬機器的http請求之後,通過nova-api呼叫compute.API的create方法來建立虛擬機器
#nova/api/openstack/compute/servers.py
from nova import compute
class ServersController(wsgi.Controller):
def __init__(self, **kwargs):
...
super(ServersController, self).__init__(**kwargs)
self.compute_api = compute.API(skip_policy_check=True )
#呼叫compute.API
def create(self, req, body):
#為特定使用者建立虛擬機器
context = req.environ['nova.context']
server_dict = body['server']
password = self._get_server_admin_password(server_dict)
name = common.normalize_name(server_dict['name'])
#要建立例項的各項引數
create_kwargs = {}
try :
inst_type = flavors.get_flavor_by_flavor_id(
flavor_id, ctxt=context, read_deleted="no")
#呼叫compute.API的create方法進行虛擬機器建立
(instances, resv_id) = self.compute_api.create(context,
inst_type,
image_uuid,
display_name=name,
display_description=name,
metadata=server_dict.get('metadata', {}),
admin_password=password,
requested_networks=requested_networks,
check_server_group_quota=True,
**create_kwargs)
...
API的create方法返回值呼叫APi的_creat_instance()
class API(base.Base):
def compute_task_api(self):
if self._compute_task_api is None:
# TODO(alaski): Remove calls into here from conductor manager so
# that this isn't necessary. #1180540
#呼叫conductor的ComputeTaskAPI類
from nova import conductor
self._compute_task_api = conductor.ComputeTaskAPI()
return self._compute_task_api
def _create_instance(self, context, instance_type,
image_href, kernel_id, ramdisk_id,
min_count, max_count,
display_name, display_description,
key_name, key_data, security_groups,
availability_zone, user_data, metadata,
injected_files, admin_password,
access_ip_v4, access_ip_v6,
requested_networks, config_drive,
block_device_mapping, auto_disk_config,
reservation_id=None, scheduler_hints=None,
legacy_bdm=True, shutdown_terminate=False,
check_server_group_quota=False):
# self._compute_task_api = conductor.ComputeTaskAPI() 呼叫呼叫conductor的ComputeTaskAPI類的build_instances方法
self.compute_task_api.build_instances(context,
instances=instances, image=boot_meta,
filter_properties=filter_properties,
admin_password=admin_password,
injected_files=injected_files,
requested_networks=requested_networks,
security_groups=security_groups,
block_device_mapping=block_device_mapping,
legacy_bdm=False)
return (instances, reservation_id)
ComputeTaskAPI中build_instances方法的實現
#/nova/compute/api.py
class ComputeTaskAPI(object):
"""ComputeTask API that queues up compute tasks for nova-conductor."""
def __init__(self):
self.conductor_compute_rpcapi = rpcapi.ComputeTaskAPI()
#雲主機調整大小
def resize_instance(self, context, instance, extra_instance_updates,
scheduler_hint, flavor, reservations,
clean_shutdown=True):
# NOTE(comstud): 'extra_instance_updates' is not used here but is
# needed for compatibility with the cells_rpcapi version of this
# method.
self.conductor_compute_rpcapi.migrate_server(
context, instance, scheduler_hint, live=False, rebuild=False,
flavor=flavor, block_migration=None, disk_over_commit=None,
reservations=reservations, clean_shutdown=clean_shutdown)
#rpc呼叫
def live_migrate_instance(self, context, instance, host_name,
block_migration, disk_over_commit):
scheduler_hint = {'host': host_name}
self.conductor_compute_rpcapi.migrate_server(
context, instance, scheduler_hint, True, False, None,
block_migration, disk_over_commit, None)
#建立雲主機,呼叫rpcapi.ComputeTaskAPI()的build_instances方法
def build_instances(self, context, instances, image, filter_properties,
admin_password, injected_files, requested_networks,
security_groups, block_device_mapping, legacy_bdm=True):
self.conductor_compute_rpcapi.build_instances(context,
instances=instances, image=image,
filter_properties=filter_properties,
admin_password=admin_password, injected_files=injected_files,
requested_networks=requested_networks,
security_groups=security_groups,
block_device_mapping=block_device_mapping,
legacy_bdm=legacy_bdm)
def unshelve_instance(self, context, instance):
self.conductor_compute_rpcapi.unshelve_instance(context,
instance=instance)
#重建雲主機
def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
injected_files, new_pass, orig_sys_metadata,
bdms, recreate=False, on_shared_storage=False,
preserve_ephemeral=False, host=None, kwargs=None):
# kwargs unused but required for cell compatibility
self.conductor_compute_rpcapi.rebuild_instance(context,
instance=instance,
new_pass=new_pass,
injected_files=injected_files,
image_ref=image_ref,
orig_image_ref=orig_image_ref,
orig_sys_metadata=orig_sys_metadata,
bdms=bdms,
recreate=recreate,
on_shared_storage=on_shared_storage,
preserve_ephemeral=preserve_ephemeral,
host=host)
到了conductor.rpcapi
# nova/conductor/rpcapi.py
class ComputeTaskAPI(object):
"""Client side of the conductor 'compute' namespaced RPC API"""
def build_instances(self, context, instances, image, filter_properties,
admin_password, injected_files, requested_networks,
security_groups, block_device_mapping, legacy_bdm=True):
image_p = jsonutils.to_primitive(image)
version = '1.10'
if not self.client.can_send_version(version):
version = '1.9'
if 'instance_type' in filter_properties:
flavor = filter_properties['instance_type']
flavor_p = objects_base.obj_to_primitive(flavor)
filter_properties = dict(filter_properties,
instance_type=flavor_p)
kw = {'instances': instances, 'image': image_p,
'filter_properties': filter_properties,
'admin_password': admin_password,
'injected_files': injected_files,
'requested_networks': requested_networks,
'security_groups': security_groups}
if not self.client.can_send_version(version):
version = '1.8'
kw['requested_networks'] = kw['requested_networks'].as_tuples()
if not self.client.can_send_version('1.7'):
version = '1.5'
bdm_p = objects_base.obj_to_primitive(block_device_mapping)
kw.update({'block_device_mapping': bdm_p,
'legacy_bdm': legacy_bdm})
cctxt = self.client.prepare(version=version)
#RPC主要用於非同步形式,比如下面就用於雙肩虛擬機器,在建立過程,可能需要比較長的時間
#如果使用RPC call顯然對效能有很大影響。cast()的第二個引數是RPC呼叫的函式名,後面的引數
#將作為引數傳遞給函式。
#關於同步非同步阻塞非阻塞請看 http://blog.csdn.net/moolight_shadow/article/details/70212874
# 這兒就呼叫上面的build_instances方法進行RPC通訊。把建立請求扔到訊息佇列中
cctxt.cast(context, 'build_instances', **kw)
cast() 方法傳送出去的 rpc 請求是沒有回覆的。所以建立虛擬機器的請求在_create_instance()
一步直接返回了,此時虛擬機器並沒有建立完畢,但是建立的請求已成功發往了 nova-conductor。後續虛擬機器的建立情況通過虛擬機器的狀態反映。
nova-conductor 提供了 build_instances() 這個 rpc 方法,所以它一直在緊切注視著 rabbitmq 訊息佇列。當看到有一個請求指向自己時,它就撿起了這個請求,準備進行 build_instances() 操作。進入 nova/conductor/manager.py 這裡,做的事情非常明顯,傳送一個請求到 nova-scheduler,得到一個選好的執行 nova-compute的主機,然後將請求發給 nova-compute。
在nova-conductor程序中進行處理
class ComputeTaskManager(base.Base):
def build_instances(self, context, instances, image, filter_properties,
admin_password, injected_files, requested_networks,
security_groups, block_device_mapping=None, legacy_bdm=True):
# TODO(ndipanov): Remove block_device_mapping and legacy_bdm in version
# 2.0 of the RPC API.
request_spec = scheduler_utils.build_request_spec(context, image,
instances)
for (instance, host) in itertools.izip(instances, hosts):
try:
instance.refresh()
except (exception.InstanceNotFound,
exception.InstanceInfoCacheNotFound):
LOG.debug('Instance deleted during build', instance=instance)
continue
local_filter_props = copy.deepcopy(filter_properties)
scheduler_utils.populate_filter_properties(local_filter_props,
host)
# The block_device_mapping passed from the api doesn't contain
# instance specific information
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
context, instance.uuid)
self.compute_rpcapi.build_and_run_instance(context,
instance=instance, host=host['host'], image=image,
request_spec=request_spec,
filter_properties=local_filter_props,
admin_password=admin_password,
injected_files=injected_files,
requested_networks=requested_networks,
security_groups=security_groups,
block_device_mapping=bdms, node=host['nodename'],
limits=host['limits'])