Horizon前臺界面用於接受用戶的輸入或動做(action),而後將這些參數構形成RESTful API(https://developer.openstack.org/api-ref/compute/#create-server),並經過novaclient發出。Nova-API負責接受該RESTFul API請求,經過RPC調用將消息發送到RabbitMQ,Nova-Compute訂閱了該消息,接受消息後隨機調用Libvirt API,指揮KVM-QEMU去真正建立虛擬機。node
對虛擬機的增刪改查(前臺)操做,發出的url請求都會被如下模塊正則匹配到:python
urlpatterns = patterns( VIEW_MOD, url(r'^$', views.IndexView.as_view(), name='index'), url(r'^filter/(?P<vm_state>[^/]+)/$', views.IndexView.as_view(), name='filter'), url(r'^create$', views.CreateInstanceView.as_view(), name='create'), url(INSTANCES % 'update', views.UpdateView.as_view(), name='update'), url(INSTANCES % 'rebuild', views.RebuildView.as_view(), name='rebuild'),
拿建立虛擬機的操做爲例,有一系列的步驟(workflow)須要用戶選擇和操做,目的是拼接足夠多的(建立虛擬機所必須的)參數:ajax
class CreateInstanceView(workflows.WorkflowView): workflow_class = project_workflows.CreateInstance ajax_template_name = constants.INSTANCE_CREATE_TEMPLATE success_url = reverse_lazy(constants.INSTANCES_INDEX_URL)
默認的workflow爲:json
class CreateInstance(workflows.Workflow): slug = "create_instance" name = _("Create Instance") finalize_button_name = _("Create") success_message = _('Launched %(count)s named "%(name)s".') failure_message = _('Unable to launch %(count)s named "%(name)s".') success_url = constants.INSTANCES_INDEX_URL wizard = True default_steps = ( SelectImages, CustomFlavors, SelectHost, SelectNetworks, SetInstanceDetails )
@sensitive_variables('context') def handle(self, request, context): custom_script = context.get('script_data', '') vcpus = int(context.get('vcpus', 0)) ram = int(context.get('ram', 0)) * 1024 disk = int(float(context.get('disk', 0))) count = int(context.get('count', 0)) pool = context.get('pool', None) try: rets = api.nova.server_create(request, context['name'], image_id, flavor_id, None, normalize_newlines(custom_script), context['groups'], meta=meta, fips=fips)
workflow走完以後,就執行handle方法了,Horizon中有一個api python包,裏面有nova,keystone,glance等的api,這些api都是對novaclient、keystoneclient、glanceclient等的封裝。api
@write_log("Create Server") def server_create(request, name, image, flavor, key_name, user_data, security_groups, block_device_mapping=None, block_device_mapping_v2=None, nics=None, availability_zone=None, instance_count=1, admin_pass=None, disk_config=None, config_drive=None, meta=None, host=None, fips=None): return Server(novaclient(request).servers.create( name, image, flavor, userdata=user_data, security_groups=security_groups, key_name=key_name, block_device_mapping=block_device_mapping, block_device_mapping_v2=block_device_mapping_v2, nics=nics, availability_zone=availability_zone, min_count=instance_count, admin_pass=admin_pass, disk_config=disk_config, config_drive=config_drive, meta=meta, fips=fips), request)
novaclient的create方法得到須要的參數後,構造Restful api,併發送到Nova服務中,由nova-api負責接收。網絡
在Nova組件中,nova/api/openstack/compute/servers.py(nova-api)的Controller負責接受請求並進行處理。併發
class Controller(wsgi.Controller): def create(self, req, body): try: _get_inst_type = flavors.get_flavor_by_flavor_id inst_type = _get_inst_type(flavor_id, ctxt=context, read_deleted="no") (instances, resv_id) = self.compute_api.create(context, inst_type, image_uuid, display_name=name, display_description=name, key_name=key_name, metadata=server_dict.get('metadata', {}), config_drive=config_drive, block_device_mapping=block_device_mapping, auto_disk_config=auto_disk_config, scheduler_hints=scheduler_hints, legacy_bdm=legacy_bdm, check_server_group_quota=check_server_group_quota, floating_ips=floating_ips) return self._add_location(robj) def _delete(self, context, req, instance_uuid): instance = self._get_server(context, req, instance_uuid) if CONF.reclaim_instance_interval: try: self.compute_api.soft_delete(context, instance) except exception.InstanceInvalidState: self.compute_api.delete(context, instance) else: self.compute_api.delete(context, instance)
以後,會調用Nova Compute的api模塊的相應方法:app
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, floating_ips=None): 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, floating_ips=floating_ips) return (instances, reservation_id)
而後,會調用Nova-Conductor API中的ComputeTaskAPI相應方法:dom
def build_instances(self, context, instances, image, filter_properties, admin_password, injected_files, requested_networks, security_groups, block_device_mapping, legacy_bdm=True, floating_ips=None): 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, floating_ips=floating_ips)
以後,又會調用Nova-Conductor模塊RPCAPI中的相應方法:ui
def build_instances(self, context, instances, image, filter_properties, admin_password, injected_files, requested_networks, security_groups, block_device_mapping, legacy_bdm=True, floating_ips=None): image_p = jsonutils.to_primitive(image) 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, 'floating_ips': floating_ips} version = '1.9' if not self.client.can_send_version('1.9'): version = '1.8' kw['requested_networks'] = kw['requested_networks'].as_tuples() if not self.client.can_send_version('1.7'): version = '1.5' kw.update({'block_device_mapping': block_device_mapping, 'legacy_bdm': legacy_bdm}) cctxt = self.client.prepare(version=version) cctxt.cast(context, 'build_instances', **kw)
該方法會獲取messaging模塊中的RPCClient,而後經過cast的方式將消息發送到RabbitMQ中。
最終,nova-compute訂閱了RabbitMQ的消息,接受到建立虛擬機的消息。建立虛擬機的操做,由/nova/compute/manager.py ComputeManager中的_do_build_and_run_instance方法完成。
def _do_build_and_run_instance(self, context, instance, image, request_spec, filter_properties, admin_password, injected_files, requested_networks, security_groups, block_device_mapping, node=None, limits=None, floating_ip=None): try: self._build_and_run_instance(context, instance, image, decoded_files, admin_password, requested_networks, security_groups, block_device_mapping, node, limits, filter_properties, floating_ip=floating_ip)
def _build_and_run_instance(self, context, instance, image, injected_files, admin_password, requested_networks, security_groups, block_device_mapping, node, limits, filter_properties, floating_ip=None): try: self.driver.spawn(context, instance, image, injected_files, admin_password, network_info=network_info, block_device_info=block_device_info)
最後,調用Libvirt(Driver)的api接口,完成建立虛擬機的操做。/nova/virt/libvirt/driver spawn方法爲建立本地鏡像,獲取虛機xml文件,建立domain:
def spawn(self, context, instance, image_meta, injected_files, admin_password, network_info=None, block_device_info=None): disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type, instance, block_device_info, image_meta) self._create_image(context, instance, disk_info['mapping'], network_info=network_info, block_device_info=block_device_info, files=injected_files, admin_pass=admin_password) xml = self._get_guest_xml(context, instance, network_info, disk_info, image_meta, block_device_info=block_device_info, write_to_disk=True) self._create_domain_and_network(context, xml, instance, network_info, block_device_info, disk_info=disk_info) LOG.debug("Instance is running", instance=instance)