Esempio n. 1
0
 def module_apply(self, req, body, tenant_id, id):
     """Apply modules to an instance."""
     context = req.environ[wsgi.CONTEXT_KEY]
     instance = models.Instance.load(context, id)
     if not instance:
         raise exception.NotFound(uuid=id)
     self.authorize_instance_action(context, 'module_apply', instance)
     module_ids = [mod['id'] for mod in body.get('modules', [])]
     modules = module_models.Modules.load_by_ids(context, module_ids)
     module_models.Modules.validate(modules, instance.datastore.id,
                                    instance.datastore_version.id)
     module_list = module_views.convert_modules_to_list(modules)
     client = create_guest_client(context, id)
     result_list = client.module_apply(module_list)
     models.Instance.add_instance_modules(context, id, modules)
     return wsgi.Result({'modules': result_list}, 200)
Esempio n. 2
0
 def __init__(self, flavor=None, context=None, flavor_id=None):
     if flavor:
         self.flavor = flavor
         return
     if flavor_id and context:
         try:
             client = create_nova_client(context)
             self.flavor = client.flavors.get(flavor_id)
         except nova_exceptions.NotFound as e:
             raise exception.NotFound(uuid=flavor_id)
         except nova_exceptions.ClientException as e:
             raise exception.TroveError(str(e))
         return
     msg = ("Flavor is not defined, and"
            " context and flavor_id were not specified.")
     raise exception.InvalidModelError(errors=msg)
Esempio n. 3
0
 def load_parameter_by_name(cls,
                            datastore_version_id,
                            config_param_name,
                            show_deleted=False):
     try:
         if show_deleted:
             return DBDatastoreConfigurationParameters.find_by(
                 datastore_version_id=datastore_version_id,
                 name=config_param_name)
         else:
             return DBDatastoreConfigurationParameters.find_by(
                 datastore_version_id=datastore_version_id,
                 name=config_param_name,
                 deleted=False)
     except exception.NotFound:
         raise exception.NotFound(uuid=config_param_name)
Esempio n. 4
0
 def module_list(self, req, tenant_id, id):
     """Return information about modules on an instance."""
     context = req.environ[wsgi.CONTEXT_KEY]
     instance = models.Instance.load(context, id)
     if not instance:
         raise exception.NotFound(uuid=id)
     from_guest = bool(req.GET.get('from_guest', '').lower())
     include_contents = bool(req.GET.get('include_contents', '').lower())
     if from_guest:
         return self._module_list_guest(context,
                                        id,
                                        include_contents=include_contents)
     else:
         return self._module_list(context,
                                  id,
                                  include_contents=include_contents)
Esempio n. 5
0
    def guest_log_action(self, context, log_name, enable, disable,
                         publish, discard):
        if enable and disable:
            raise exception.BadRequest("Cannot enable and disable log '%s'." %
                                       log_name)
        # Enable if we are publishing, unless told to disable
        if publish and not disable:
            enable = True
        LOG.info(_("Processing guest log '%(log)s' "
                 "(enable=%(en)s, disable=%(dis)s, "
                   "publish=%(pub)s, discard=%(disc)s).") %
                 {'log': log_name, 'en': enable, 'dis': disable,
                  'pub': publish, 'disc': discard})
        self.guest_log_context = context
        gl_cache = self.guest_log_cache
        if log_name in gl_cache:
            if ((gl_cache[log_name].type == guest_log.LogType.SYS) and
                    not publish):
                if enable or disable:
                    if enable:
                        action_text = "enable"
                    else:
                        action_text = "disable"
                    raise exception.BadRequest("Cannot %s a SYSTEM log ('%s')."
                                               % (action_text, log_name))
            if gl_cache[log_name].type == guest_log.LogType.USER:
                requires_change = (
                    (gl_cache[log_name].enabled and disable) or
                    (not gl_cache[log_name].enabled and enable))
                if requires_change:
                    restart_required = self.guest_log_enable(
                        context, log_name, disable)
                    if restart_required:
                        self.set_guest_log_status(
                            guest_log.LogStatus.Restart_Required, log_name)
                    gl_cache[log_name].enabled = enable
            log_details = gl_cache[log_name].show()
            if discard:
                log_details = gl_cache[log_name].discard_log()
            if publish:
                log_details = gl_cache[log_name].publish_log()
            LOG.info(_("Details for log '%(log)s': %(det)s") %
                     {'log': log_name, 'det': log_details})
            return log_details

        raise exception.NotFound("Log '%s' is not defined." % log_name)
Esempio n. 6
0
 def module_remove(self, req, tenant_id, id, module_id):
     """Remove module from an instance."""
     context = req.environ[wsgi.CONTEXT_KEY]
     instance = models.Instance.load(context, id)
     if not instance:
         raise exception.NotFound(uuid=id)
     module = module_models.Module.load(context, module_id)
     module_info = module_views.DetailedModuleView(module).data()
     client = create_guest_client(context, id)
     client.module_remove(module_info)
     instance_modules = module_models.InstanceModules.load_all(
         context, instance_id=id, module_id=module_id)
     for instance_module in instance_modules:
         module_models.InstanceModule.delete(context, instance_module)
         LOG.debug("Deleted IM record %s (instance %s, module %s)." %
                   (instance_module.id, id, module_id))
     return wsgi.Result(None, 200)
Esempio n. 7
0
    def guest_log_list(self, req, tenant_id, id):
        """Return all information about all logs for an instance."""
        LOG.debug("Listing logs for tenant %s", tenant_id)
        context = req.environ[wsgi.CONTEXT_KEY]

        try:
            backup_model.verify_swift_auth_token(context)
        except exception.SwiftNotFound:
            raise exception.LogsNotAvailable()

        instance = models.Instance.load(context, id)
        if not instance:
            raise exception.NotFound(uuid=id)
        self.authorize_instance_action(context, 'guest_log_list', instance)
        client = create_guest_client(context, id)
        guest_log_list = client.guest_log_list()
        return wsgi.Result({'logs': guest_log_list}, 200)
Esempio n. 8
0
 def guest_log_action(self, req, body, tenant_id, id):
     """Processes a guest log."""
     LOG.info(_("Processing log for tenant %s"), tenant_id)
     context = req.environ[wsgi.CONTEXT_KEY]
     instance = models.Instance.load(context, id)
     if not instance:
         raise exception.NotFound(uuid=id)
     log_name = body['name']
     enable = body.get('enable', None)
     disable = body.get('disable', None)
     publish = body.get('publish', None)
     discard = body.get('discard', None)
     if enable and disable:
         raise exception.BadRequest(_("Cannot enable and disable log."))
     client = create_guest_client(context, id)
     guest_log = client.guest_log_action(log_name, enable, disable, publish,
                                         discard)
     return wsgi.Result({'log': guest_log}, 200)
Esempio n. 9
0
    def remove_module(cls, driver, module_type, module_id, name, datastore,
                      ds_version):
        datastore = datastore or cls.MODULE_APPLY_TO_ALL
        ds_version = ds_version or cls.MODULE_APPLY_TO_ALL
        module_dir = cls.build_module_dir(module_type, module_id)
        contents_file = cls.build_contents_filename(module_dir)

        if not operating_system.exists(cls.get_result_filename(module_dir)):
            raise exception.NotFound(
                _("Module '%s' has not been applied") % name)
        try:
            removed, message = driver.remove(name, datastore, ds_version,
                                             contents_file)
            cls.remove_module_result(module_dir)
        except Exception:
            LOG.exception(_("Could not remove module '%s'") % name)
            raise
        return removed, message
Esempio n. 10
0
    def guest_log_action(self, context, log_name, enable, disable, publish,
                         discard):
        if enable and disable:
            raise exception.BadRequest("Cannot enable and disable log '%s'." %
                                       log_name)
        # Enable if we are publishing, unless told to disable
        if publish and not disable:
            enable = True
        LOG.debug("Processing guest log '%s' "
                  "(enable=%s, disable=%s, publish=%s, discard=%s)." %
                  (log_name, enable, disable, publish, discard))
        self.guest_log_context = context
        gl_cache = self.guest_log_cache
        response = None
        if log_name in gl_cache:
            if ((gl_cache[log_name].type == guest_log.LogType.SYS)
                    and not publish):
                if enable or disable:
                    if enable:
                        action_text = "enable"
                    else:
                        action_text = "disable"
                    raise exception.BadRequest(
                        "Cannot %s a SYSTEM log ('%s')." %
                        (action_text, log_name))
            if gl_cache[log_name].type == guest_log.LogType.USER:
                requires_change = ((gl_cache[log_name].enabled and disable) or
                                   (not gl_cache[log_name].enabled and enable))
                if requires_change:
                    restart_required = self.guest_log_enable(
                        context, log_name, disable)
                    if restart_required:
                        self.set_guest_log_status(
                            guest_log.LogStatus.Restart_Required, log_name)
                    gl_cache[log_name].enabled = enable
                response = gl_cache[log_name].show()
            if discard:
                response = gl_cache[log_name].discard_log()
            if publish:
                response = gl_cache[log_name].publish_log()
        else:
            raise exception.NotFound("Log '%s' is not defined." % log_name)

        return response
Esempio n. 11
0
 def module_apply(self, req, body, tenant_id, id):
     """Apply modules to an instance."""
     context = req.environ[wsgi.CONTEXT_KEY]
     instance = models.Instance.load(context, id)
     if not instance:
         raise exception.NotFound(uuid=id)
     module_ids = [mod['id'] for mod in body.get('modules', [])]
     modules = module_models.Modules.load_by_ids(context, module_ids)
     module_list = []
     for module in modules:
         module.contents = module_models.Module.deprocess_contents(
             module.contents)
         module_info = module_views.DetailedModuleView(module).data(
             include_contents=True)
         module_list.append(module_info)
     client = create_guest_client(context, id)
     result_list = client.module_apply(module_list)
     models.Instance.add_instance_modules(context, id, modules)
     return wsgi.Result({'modules': result_list}, 200)
Esempio n. 12
0
    def does_configuration_need_restart(self):
        datastore_v = Configuration.load_configuration_datastore_version(
            self.context, self.configuration_id)
        config_items = Configuration.load_items(self.context,
                                                id=self.configuration_id)
        LOG.debug("config_items: %s" % config_items)
        detail_list = DatastoreConfigurationParameters.load_parameters(
            datastore_v.id, show_deleted=True)

        for i in config_items:
            LOG.debug("config item: %s" % i)
            details = Configuration.find_parameter_details(
                i.configuration_key, detail_list)
            LOG.debug("parameter details: %s" % details)
            if not details:
                raise exception.NotFound(uuid=i.configuration_key)
            if bool(details.restart_required):
                return True
        return False
Esempio n. 13
0
def get_db_info(context, id):
    """
    Retrieves an instance of the managed datastore from the persisted
    storage based on the ID and Context
    :param context: the context which owns the instance
    :type context: trove.common.context.TroveContext
    :param id: the unique ID of the instance
    :type id: unicode or str
    :return: a record of the instance as its state exists in persisted storage
    :rtype: trove.instance.models.DBInstance
    """
    if context is None:
        raise TypeError("Argument context not defined.")
    elif id is None:
        raise TypeError("Argument id not defined.")
    try:
        db_info = DBInstance.find_by(context=context, id=id, deleted=False)
    except exception.NotFound:
        raise exception.NotFound(uuid=id)
    return db_info
Esempio n. 14
0
    def assign_configuration(self, configuration_id):
        self._validate_can_perform_assign()

        try:
            configuration = Configuration.load(self.context, configuration_id)
        except exception.ModelNotFoundError:
            raise exception.NotFound(
                message='Configuration group id: %s could not be found.' %
                configuration_id)

        config_ds_v = configuration.datastore_version_id
        inst_ds_v = self.db_info.datastore_version_id
        if (config_ds_v != inst_ds_v):
            raise exception.ConfigurationDatastoreNotMatchInstance(
                config_datastore_version=config_ds_v,
                instance_datastore_version=inst_ds_v)

        config = Configuration(self.context, configuration.id)
        LOG.debug("Config config is %s.", config)
        self.update_db(configuration_id=configuration.id)
        self.update_overrides(config)
Esempio n. 15
0
    def __init__(self, volume_type=None, context=None, volume_type_id=None):
        """
        Initialize the volume type either from the volume_type parameter, or
        by querying cinder using the context provided.
        """

        if volume_type and not (volume_type_id or context):
            self.volume_type = volume_type
        elif volume_type_id and context:
            try:
                client = create_cinder_client(context)
                self.volume_type = client.volume_types.get(volume_type_id)
            except cinder_exception.NotFound:
                raise trove_exception.NotFound(uuid=volume_type_id)
            except cinder_exception.ClientException as ce:
                raise trove_exception.TroveError(str(ce))

            return
        else:
            raise trove_exception.InvalidModelError(
                errors="An invalid set of arguments were provided.")
Esempio n. 16
0
    def assign_configuration(self, configuration_id):
        try:
            configuration = Configuration.load(self.context, configuration_id)
        except exception.ModelNotFoundError:
            raise exception.NotFound(
                message='Configuration group id: %s could not be found'
                % configuration_id)

        config_ds_v = configuration.datastore_version_id
        inst_ds_v = self.db_info.datastore_version_id
        if (config_ds_v != inst_ds_v):
            raise exception.ConfigurationDatastoreNotMatchInstance(
                config_datastore_version=config_ds_v,
                instance_datastore_version=inst_ds_v)

        overrides = Configuration.get_configuration_overrides(
            self.context, configuration.id)

        LOG.info(overrides)

        self.update_overrides(overrides)
        self.update_db(configuration_id=configuration.id)
Esempio n. 17
0
    def create(self, req, body, tenant_id):
        LOG.info("Creating a database instance for tenant '%s'", tenant_id)
        LOG.debug("req : '%s'\n\n", strutils.mask_password(req))
        LOG.debug("body : '%s'\n\n", strutils.mask_password(body))
        context = req.environ[wsgi.CONTEXT_KEY]
        policy.authorize_on_tenant(context, 'instance:create')
        context.notification = notification.DBaaSInstanceCreate(context,
                                                                request=req)

        name = body['instance']['name']
        slave_of_id = body['instance'].get('replica_of')
        replica_count = body['instance'].get('replica_count')
        flavor_ref = body['instance'].get('flavorRef')
        datastore_args = body['instance'].get('datastore', {})
        volume_info = body['instance'].get('volume', {})
        availability_zone = body['instance'].get('availability_zone')
        nics = body['instance'].get('nics', [])
        locality = body['instance'].get('locality')
        region_name = body['instance'].get(
            'region_name', CONF.service_credentials.region_name)
        access = body['instance'].get('access', None)

        if slave_of_id:
            if flavor_ref:
                msg = 'Cannot specify flavor when creating replicas.'
                raise exception.BadRequest(message=msg)
            if datastore_args:
                msg = 'Cannot specify datastore when creating replicas.'
                raise exception.BadRequest(message=msg)
            if volume_info:
                msg = 'Cannot specify volume when creating replicas.'
                raise exception.BadRequest(message=msg)
            if locality:
                msg = 'Cannot specify locality when creating replicas.'
                raise exception.BadRequest(message=msg)
            backup_model.verify_swift_auth_token(context)
        else:
            if replica_count and replica_count > 1:
                msg = (f"Replica count only valid when creating replicas. "
                       f"Cannot create {replica_count} instances.")
                raise exception.BadRequest(message=msg)

        flavor_id = utils.get_id_from_href(flavor_ref)

        if volume_info:
            volume_size = int(volume_info.get('size'))
            volume_type = volume_info.get('type')
        else:
            volume_size = None
            volume_type = None

        if slave_of_id:
            try:
                replica_source = models.DBInstance.find_by(context,
                                                           id=slave_of_id,
                                                           deleted=False)
                flavor_id = replica_source.flavor_id
            except exception.ModelNotFoundError:
                LOG.error(f"Cannot create a replica of {slave_of_id} as that "
                          f"instance could not be found.")
                raise exception.NotFound(uuid=slave_of_id)
            if replica_source.slave_of_id:
                raise exception.Forbidden(
                    f"Cannot create a replica of a replica {slave_of_id}")

            datastore_version = ds_models.DatastoreVersion.load_by_uuid(
                replica_source.datastore_version_id)
            datastore = ds_models.Datastore.load(
                datastore_version.datastore_id)
        else:
            datastore, datastore_version = ds_models.get_datastore_version(
                **datastore_args)

        # If only image_tags is configured in the datastore version, get
        # the image ID using the tags.
        glance_client = clients.create_glance_client(context)
        image_id = common_glance.get_image_id(glance_client,
                                              datastore_version.image_id,
                                              datastore_version.image_tags)
        LOG.info(f'Using image {image_id} for creating instance')

        databases = populate_validated_databases(body['instance'].get(
            'databases', []))
        database_names = [database.get('_name', '') for database in databases]
        users = None
        try:
            users = populate_users(body['instance'].get('users', []),
                                   database_names)
        except ValueError as ve:
            raise exception.BadRequest(message=str(ve))
        if slave_of_id and (databases or users):
            raise exception.ReplicaCreateWithUsersDatabasesError()

        configuration = self._configuration_parse(context, body)
        modules = body['instance'].get('modules')

        # The following operations have their own API calls.
        # We need to make sure the same policies are enforced when
        # creating an instance.
        # i.e. if attaching configuration group to an existing instance is not
        # allowed, it should not be possible to create a new instance with the
        # group attached either
        if configuration:
            policy.authorize_on_tenant(context, 'instance:update')
        if modules:
            policy.authorize_on_tenant(context, 'instance:module_apply')
        if users:
            policy.authorize_on_tenant(context,
                                       'instance:extension:user:create')
        if databases:
            policy.authorize_on_tenant(context,
                                       'instance:extension:database:create')

        if 'restorePoint' in body['instance']:
            backupRef = body['instance']['restorePoint']['backupRef']
            backup_id = utils.get_id_from_href(backupRef)
        else:
            backup_id = None

        # Only 1 nic is allowed as defined in API jsonschema.
        # Use list just for backward compatibility.
        if len(nics) > 0:
            nic = nics[0]
            LOG.info('Checking user provided instance network %s', nic)
            if slave_of_id and nic.get('ip_address'):
                msg = "Cannot specify IP address when creating replicas."
                raise exception.BadRequest(message=msg)
            self._check_nic(context, nic)

        if locality:
            locality_domain = ['affinity', 'anti-affinity']
            locality_domain_msg = ("Invalid locality '%s'. "
                                   "Must be one of ['%s']" %
                                   (locality, "', '".join(locality_domain)))
            if locality not in locality_domain:
                raise exception.BadRequest(message=locality_domain_msg)

        instance = models.Instance.create(context,
                                          name,
                                          flavor_id,
                                          image_id,
                                          databases,
                                          users,
                                          datastore,
                                          datastore_version,
                                          volume_size,
                                          backup_id,
                                          availability_zone,
                                          nics,
                                          configuration,
                                          slave_of_id,
                                          replica_count=replica_count,
                                          volume_type=volume_type,
                                          modules=modules,
                                          locality=locality,
                                          region_name=region_name,
                                          access=access)

        view = views.InstanceDetailView(instance, req=req)
        return wsgi.Result(view.data(), 200)
Esempio n. 18
0
    def create(cls,
               context,
               name,
               flavor_id,
               image_id,
               databases,
               users,
               datastore,
               datastore_version,
               volume_size,
               backup_id,
               availability_zone=None,
               nics=None,
               configuration_id=None,
               slave_of_id=None,
               cluster_config=None):

        datastore_cfg = CONF.get(datastore_version.manager)
        client = create_nova_client(context)
        try:
            flavor = client.flavors.get(flavor_id)
        except nova_exceptions.NotFound:
            raise exception.FlavorNotFound(uuid=flavor_id)

        deltas = {'instances': 1}
        volume_support = datastore_cfg.volume_support
        if volume_support:
            validate_volume_size(volume_size)
            deltas['volumes'] = volume_size
            # Instance volume should have enough space for the backup
            # Backup, and volume sizes are in GBs
            target_size = volume_size
        else:
            target_size = flavor.disk  # local_storage
            if volume_size is not None:
                raise exception.VolumeNotSupported()
            if datastore_cfg.device_path:
                if flavor.ephemeral == 0:
                    raise exception.LocalStorageNotSpecified(flavor=flavor_id)
                target_size = flavor.ephemeral  # ephemeral_Storage

        if backup_id is not None:
            backup_info = Backup.get_by_id(context, backup_id)
            if backup_info.is_running:
                raise exception.BackupNotCompleteError(backup_id=backup_id)

            if backup_info.size > target_size:
                raise exception.BackupTooLarge(backup_size=backup_info.size,
                                               disk_size=target_size)

            if not backup_info.check_swift_object_exist(
                    context,
                    verify_checksum=CONF.verify_swift_checksum_on_restore):
                raise exception.BackupFileNotFound(
                    location=backup_info.location)

            if (backup_info.datastore_version_id
                    and backup_info.datastore.name != datastore.name):
                raise exception.BackupDatastoreMismatchError(
                    datastore1=backup_info.datastore.name,
                    datastore2=datastore.name)

        if slave_of_id:
            replication_support = datastore_cfg.replication_strategy
            if not replication_support:
                raise exception.ReplicationNotSupported(
                    datastore=datastore.name)
            try:
                # looking for replica source
                replica_source = DBInstance.find_by(context,
                                                    id=slave_of_id,
                                                    deleted=False)
                if replica_source.slave_of_id:
                    raise exception.Forbidden(
                        _("Cannot create a replica of a replica %(id)s.") %
                        {'id': slave_of_id})
            except exception.ModelNotFoundError:
                LOG.exception(
                    _("Cannot create a replica of %(id)s "
                      "as that instance could not be found.") %
                    {'id': slave_of_id})
                raise exception.NotFound(uuid=slave_of_id)

        if not nics:
            nics = []
        if CONF.default_neutron_networks:
            nics = [{
                "net-id": net_id
            } for net_id in CONF.default_neutron_networks] + nics

        def _create_resources():

            if cluster_config:
                cluster_id = cluster_config.get("id", None)
                shard_id = cluster_config.get("shard_id", None)
                instance_type = cluster_config.get("instance_type", None)
            else:
                cluster_id = shard_id = instance_type = None

            db_info = DBInstance.create(
                name=name,
                flavor_id=flavor_id,
                tenant_id=context.tenant,
                volume_size=volume_size,
                datastore_version_id=datastore_version.id,
                task_status=InstanceTasks.BUILDING,
                configuration_id=configuration_id,
                slave_of_id=slave_of_id,
                cluster_id=cluster_id,
                shard_id=shard_id,
                type=instance_type)
            LOG.debug("Tenant %(tenant)s created new Trove instance %(db)s.", {
                'tenant': context.tenant,
                'db': db_info.id
            })

            # if a configuration group is associated with an instance,
            # generate an overrides dict to pass into the instance creation
            # method

            config = Configuration(context, configuration_id)
            overrides = config.get_configuration_overrides()
            service_status = InstanceServiceStatus.create(
                instance_id=db_info.id, status=tr_instance.ServiceStatuses.NEW)

            if CONF.trove_dns_support:
                dns_client = create_dns_client(context)
                hostname = dns_client.determine_hostname(db_info.id)
                db_info.hostname = hostname
                db_info.save()

            root_password = None
            if cls.get_root_on_create(
                    datastore_version.manager) and not backup_id:
                root_password = utils.generate_random_password()

            task_api.API(context).create_instance(
                db_info.id, name, flavor, image_id, databases, users,
                datastore_version.manager, datastore_version.packages,
                volume_size, backup_id, availability_zone, root_password, nics,
                overrides, slave_of_id, cluster_config)

            return SimpleInstance(context, db_info, service_status,
                                  root_password)

        return run_with_quotas(context.tenant, deltas, _create_resources)
Esempio n. 19
0
 def load_parameter(cls, config_id):
     try:
         return DBDatastoreConfigurationParameters.find_by(id=config_id,
                                                           deleted=False)
     except exception.NotFound:
         raise exception.NotFound(uuid=config_id)
Esempio n. 20
0
 def load(context, name):
     client = create_nova_client(context)
     try:
         return DetailedHost(client.rdhosts.get(name))
     except nova_exceptions.NotFound:
         raise exception.NotFound(uuid=name)
Esempio n. 21
0
 def _validate_flavor_id(self, id):
     try:
         if int(id) != float(id):
             raise exception.NotFound(uuid=id)
     except ValueError:
         raise exception.NotFound(uuid=id)
Esempio n. 22
0
        def _create_resources():
            # parse the ID from the Ref
            instance_id = utils.get_id_from_href(instance)

            # verify that the instance exists and can perform actions
            from trove.instance.models import Instance
            instance_model = Instance.load(context, instance_id)
            instance_model.validate_can_perform_action()
            cls.validate_can_perform_action(instance_model, 'backup_create')
            cls.verify_swift_auth_token(context)
            if instance_model.cluster_id is not None:
                raise exception.ClusterInstanceOperationNotSupported()

            ds = instance_model.datastore
            ds_version = instance_model.datastore_version
            parent = None
            parent_backup = None
            parent_backup_id = None
            if parent_id:
                # Look up the parent info or fail early if not found or if
                # the user does not have access to the parent.
                if parent_id == 'last':
                    # Need to assign parent_id to a new variable
                    # parent_backup_id, otherwise an unbound variable error
                    # will be thrown
                    parent_backup = Backup.get_last_completed(
                        context, instance_id, True)
                    if parent_backup is None:
                        raise exception.NotFound()
                    parent_backup_id = parent_backup.id
                else:
                    parent_backup_id = parent_id
                _parent = cls.get_by_id(context, parent_backup_id)
                parent = {
                    'id': parent_backup_id,
                    'location': _parent.location,
                    'checksum': _parent.checksum,
                }
            try:
                db_info = DBBackup.create(name=name,
                                          description=description,
                                          tenant_id=context.tenant,
                                          state=BackupState.NEW,
                                          instance_id=instance_id,
                                          parent_id=parent_backup_id,
                                          datastore_version_id=ds_version.id,
                                          deleted=False)
            except exception.InvalidModelError as ex:
                LOG.exception(
                    _("Unable to create backup record for "
                      "instance: %s"), instance_id)
                raise exception.BackupCreationError(str(ex))

            backup_info = {
                'id': db_info.id,
                'name': name,
                'description': description,
                'instance_id': instance_id,
                'backup_type': db_info.backup_type,
                'checksum': db_info.checksum,
                'parent': parent,
                'datastore': ds.name,
                'datastore_version': ds_version.name,
            }
            api.API(context).create_backup(backup_info, instance_id)
            return db_info