当前位置: 代码迷 >> 电脑整机及配件 >> Openstack Nova(9)-Instance 创建(Computer API & Conductor )
  详细解决方案

Openstack Nova(9)-Instance 创建(Computer API & Conductor )

热度:746   发布时间:2016-04-29 02:40:14.0
Openstack Nova(九)----Instance 创建(Computer API & Conductor )

在前一章中, 学习了所有的流水线处理。最后通过路由的设置,会把创建Instance的请求映射到servers.py的Controller的create方法。这中间会有不少的代码, 但都是一些常用的逻辑, 这里不进行讲述。

Controller/Create

这是一段比较长的代码, 但是做的事情还是比较简单。就是对各种参数的合法性检查。

@wsgi.response(202)    @wsgi.serializers(xml=FullServerTemplate)    @wsgi.deserializers(xml=CreateDeserializer)    def create(self, req, body):        """Creates a new server for a given user."""        if not self.is_valid_body(body, 'server'):            raise exc.HTTPUnprocessableEntity()        #由前面的流水线生成的上下文环境        context = req.environ['nova.context']        #server的全部参数        server_dict = body['server']        #Instance的password        password = self._get_server_admin_password(server_dict)        #Instance的名称,这个不能为空        if 'name' not in server_dict:            msg = _("Server name is not defined")            raise exc.HTTPBadRequest(explanation=msg)        name = server_dict['name']        #验证name的长度1-255        self._validate_server_name(name)        name = name.strip()        #从当前参数中,取出image标识,有可能为空        image_uuid = self._image_from_req_data(body)        personality = server_dict.get('personality')        config_drive = None        if self.ext_mgr.is_loaded('os-config-drive'):            config_drive = server_dict.get('config_drive')        injected_files = []        if personality:            #需要注入的文件            injected_files = self._get_injected_files(personality)        #安全组配置        sg_names = []        if self.ext_mgr.is_loaded('os-security-groups'):            security_groups = server_dict.get('security_groups')            if security_groups is not None:                sg_names = [sg['name'] for sg in security_groups                            if sg.get('name')]        if not sg_names:            sg_names.append('default')        sg_names = list(set(sg_names))        #网络配置        requested_networks = None        if (self.ext_mgr.is_loaded('os-networks')                or utils.is_neutron()):            requested_networks = server_dict.get('networks')        if requested_networks is not None:            if not isinstance(requested_networks, list):                expl = _('Bad networks format')                raise exc.HTTPBadRequest(explanation=expl)            requested_networks = self._get_requested_networks(                requested_networks)        #ipv4及有效性检查,只是针对IP地址本身,不会考虑其它单元的IP地址        (access_ip_v4, ) = server_dict.get('accessIPv4'),        if access_ip_v4 is not None:            self._validate_access_ipv4(access_ip_v4)        #ipv6及有效性检查,只是针对IP地址本身,不会考虑其它单元的IP地址        (access_ip_v6, ) = server_dict.get('accessIPv6'),        if access_ip_v6 is not None:            self._validate_access_ipv6(access_ip_v6)        try:            #Instance的配置信息, 这个必须有            flavor_id = self._flavor_id_from_req_data(body)        except ValueError as error:            msg = _("Invalid flavorRef provided.")            raise exc.HTTPBadRequest(explanation=msg)        # optional openstack extensions:        key_name = None        if self.ext_mgr.is_loaded('os-keypairs'):            key_name = server_dict.get('key_name')        user_data = None        if self.ext_mgr.is_loaded('os-user-data'):            user_data = server_dict.get('user_data')        self._validate_user_data(user_data)        #zone信息, 用来对Instance进行分区,分区基于HOST        availability_zone = None        if self.ext_mgr.is_loaded('os-availability-zone'):            availability_zone = server_dict.get('availability_zone')        #硬盘信息, 可以做为启动盘        block_device_mapping = None        block_device_mapping_v2 = None        legacy_bdm = True        if self.ext_mgr.is_loaded('os-volumes'):            block_device_mapping = server_dict.get('block_device_mapping', [])            for bdm in block_device_mapping:                try:                    block_device.validate_device_name(bdm.get("device_name"))                    block_device.validate_and_default_volume_size(bdm)                except exception.InvalidBDMFormat as e:                    raise exc.HTTPBadRequest(explanation=e.format_message())                if 'delete_on_termination' in bdm:                    bdm['delete_on_termination'] = strutils.bool_from_string(                        bdm['delete_on_termination'])            if self.ext_mgr.is_loaded('os-block-device-mapping-v2-boot'):                # Consider the new data format for block device mapping                block_device_mapping_v2 = server_dict.get(                    'block_device_mapping_v2', [])                # NOTE (ndipanov):  Disable usage of both legacy and new                #                   block device format in the same request                if block_device_mapping and block_device_mapping_v2:                    expl = _('Using different block_device_mapping syntaxes '                             'is not allowed in the same request.')                    raise exc.HTTPBadRequest(explanation=expl)                # Assume legacy format                legacy_bdm = not bool(block_device_mapping_v2)                try:                    block_device_mapping_v2 = [                        block_device.BlockDeviceDict.from_api(bdm_dict)                        for bdm_dict in block_device_mapping_v2]                except exception.InvalidBDMFormat as e:                    raise exc.HTTPBadRequest(explanation=e.format_message())        block_device_mapping = (block_device_mapping or                                block_device_mapping_v2)        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', {})        try:            _get_inst_type = flavors.get_flavor_by_flavor_id            inst_type = _get_inst_type(flavor_id, ctxt=context,                                       read_deleted="no")#使用compute_api去创建Instance            (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', {}),                            access_ip_v4=access_ip_v4,                            access_ip_v6=access_ip_v6,                            injected_files=injected_files,                            admin_password=password,                            min_count=min_count,                            max_count=max_count,                            requested_networks=requested_networks,                            security_group=sg_names,                            user_data=user_data,                            availability_zone=availability_zone,                            config_drive=config_drive,                            block_device_mapping=block_device_mapping,                            auto_disk_config=auto_disk_config,                            scheduler_hints=scheduler_hints,                            legacy_bdm=legacy_bdm)        except exception.QuotaError as error:            raise exc.HTTPRequestEntityTooLarge(                explanation=error.format_message(),                headers={'Retry-After': 0})        except exception.InvalidMetadataSize as error:            raise exc.HTTPRequestEntityTooLarge(                explanation=error.format_message())        except exception.ImageNotFound as error:            msg = _("Can not find requested image")            raise exc.HTTPBadRequest(explanation=msg)        except exception.FlavorNotFound as error:            msg = _("Invalid flavorRef provided.")            raise exc.HTTPBadRequest(explanation=msg)        except exception.KeypairNotFound as error:            msg = _("Invalid key_name provided.")            raise exc.HTTPBadRequest(explanation=msg)        except exception.ConfigDriveInvalidValue:            msg = _("Invalid config_drive provided.")            raise exc.HTTPBadRequest(explanation=msg)        except rpc_common.RemoteError as err:            msg = "%(err_type)s: %(err_msg)s" % {'err_type': err.exc_type,                                                 'err_msg': err.value}            raise exc.HTTPBadRequest(explanation=msg)        except UnicodeDecodeError as error:            msg = "UnicodeError: %s" % unicode(error)            raise exc.HTTPBadRequest(explanation=msg)        except (exception.ImageNotActive,                exception.InstanceTypeDiskTooSmall,                exception.InstanceTypeMemoryTooSmall,                exception.InstanceTypeNotFound,                exception.InvalidMetadata,                exception.InvalidRequest,                exception.MultiplePortsNotApplicable,                exception.PortNotFound,                exception.SecurityGroupNotFound,                exception.InvalidBDM) as error:            raise exc.HTTPBadRequest(explanation=error.format_message())        except exception.PortInUse as error:            raise exc.HTTPConflict(explanation=error.format_message())        # If the caller wanted a reservation_id, return it        if ret_resv_id:            return wsgi.ResponseObject({'reservation_id': resv_id},                                       xml=ServerMultipleCreateTemplate)        req.cache_db_instances(instances)        server = self._view_builder.create(req, instances[0])        if CONF.enable_instance_password:            server['server']['adminPass'] = password        robj = wsgi.ResponseObject(server)        return self._add_location(robj)

compute_api

为了简化问题, 这里假设系统中没有使用cells, 那么对应的computer_api就是 nova/compute/api.py的API类。

create

@hooks.add_hook("create_instance")    def create(self, context, instance_type,               image_href, kernel_id=None, ramdisk_id=None,               min_count=None, max_count=None,               display_name=None, display_description=None,               key_name=None, key_data=None, security_group=None,               availability_zone=None, user_data=None, metadata=None,               injected_files=None, admin_password=None,               block_device_mapping=None, access_ip_v4=None,               access_ip_v6=None, requested_networks=None, config_drive=None,               auto_disk_config=None, scheduler_hints=None, legacy_bdm=True):        """        Provision instances, sending instance information to the        scheduler.  The scheduler will determine where the instance(s)        go and will handle creating the DB entries.        Returns a tuple of (instances, reservation_id)        """        #创建规则检查        self._check_create_policies(context, availability_zone,                requested_networks, block_device_mapping)        if requested_networks and max_count > 1 and utils.is_neutron():            self._check_multiple_instances_neutron_ports(requested_networks)        #调用自己的_create_instance        return self._create_instance(                               context, instance_type,                               image_href, kernel_id, ramdisk_id,                               min_count, max_count,                               display_name, display_description,                               key_name, key_data, security_group,                               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,                               scheduler_hints=scheduler_hints,                               legacy_bdm=legacy_bdm)

_create_instance

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):        """Verify all the input parameters regardless of the provisioning        strategy being performed and schedule the instance(s) for        creation.        """        # Normalize and setup some parameters        if reservation_id is None:            reservation_id = utils.generate_uid('r')        security_groups = security_groups or ['default']        min_count = min_count or 1        max_count = max_count or min_count        block_device_mapping = block_device_mapping or []        if not instance_type:            instance_type = flavors.get_default_flavor()        if image_href:            image_id, boot_meta = self._get_image(context, image_href)        else:            image_id = None            boot_meta = {}            boot_meta['properties'] = \                self._get_bdm_image_metadata(context,                    block_device_mapping, legacy_bdm)        self._check_auto_disk_config(image=boot_meta,                                     auto_disk_config=auto_disk_config)        handle_az = self._handle_availability_zone        availability_zone, forced_host, forced_node = handle_az(context,                                                            availability_zone)        base_options = self._validate_and_build_base_options(context,                instance_type, boot_meta, image_href, image_id, kernel_id,                ramdisk_id, display_name, display_description,                key_name, key_data, security_groups, availability_zone,                forced_host, user_data, metadata, injected_files, access_ip_v4,                access_ip_v6, requested_networks, config_drive,                block_device_mapping, auto_disk_config, reservation_id)        block_device_mapping = self._check_and_transform_bdm(            base_options, boot_meta, min_count, max_count,            block_device_mapping, legacy_bdm)        #创建数据库记录        instances = self._provision_instances(context, instance_type,                min_count, max_count, base_options, boot_meta, security_groups,                block_device_mapping)        #创建过滤条件        filter_properties = self._build_filter_properties(context,                scheduler_hints, forced_host, forced_node, instance_type)        #更新Instance状态        for instance in instances:            self._record_action_start(context, instance,                                      instance_actions.CREATE)        #使用compute_task_api 去创建Instance        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)

compute_task_api(conductor)/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)

conductor_compute_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)

conductor_compute_rpcapi(conductor/rpcapi/ConductorAPI)

def build_instances(self, context, instances, image, filter_properties,            admin_password, injected_files, requested_networks,            security_groups, block_device_mapping, legacy_bdm=True):        instances_p = [jsonutils.to_primitive(inst) for inst in instances]        image_p = jsonutils.to_primitive(image)        cctxt = self.client.prepare(version='1.5')        cctxt.cast(context, 'build_instances',                   instances=instances_p, image=image_p,                   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)

至此, Conductor发送RPC消息给端的Conductor, 然后再交给Scheduler去处理。

但是这里还有一个比较重要的过程,就是RPC机制,下一章中将继续处理。

  相关解决方案