Example #1
0
def get_backend_qos_specs(volume):
    type_id = volume.volume_type_id
    if type_id is None:
        return None

    qos_specs = volume_types.get_volume_type_qos_specs(type_id)
    if qos_specs is None:
        return None

    qos_specs = qos_specs['qos_specs']
    if qos_specs is None:
        return None

    consumer = qos_specs['consumer']
    # Front end QoS specs are handled by nova. We ignore them here.
    if consumer not in BACKEND_QOS_CONSUMERS:
        return None

    max_iops = qos_specs['specs'].get(QOS_MAX_IOPS)
    max_bws = qos_specs['specs'].get(QOS_MAX_BWS)
    if max_iops is None and max_bws is None:
        return None

    return {
        'id': qos_specs['id'],
        QOS_MAX_IOPS: max_iops,
        QOS_MAX_BWS: max_bws,
    }
Example #2
0
def get_backend_qos_specs(volume):
    type_id = volume.volume_type_id
    if type_id is None:
        return None

    # Use the provided interface to avoid permission issue
    qos_specs = volume_types.get_volume_type_qos_specs(type_id)
    if qos_specs is None:
        return None

    qos_specs = qos_specs['qos_specs']
    if qos_specs is None:
        return None

    consumer = qos_specs['consumer']
    # Front end QoS specs are handled by nova. We ignore them here.
    if consumer not in common.BACKEND_QOS_CONSUMERS:
        return None

    max_iops = qos_specs['specs'].get(common.QOS_MAX_IOPS)
    max_bws = qos_specs['specs'].get(common.QOS_MAX_BWS)
    if max_iops is None and max_bws is None:
        return None

    return {
        'id': qos_specs['id'],
        common.QOS_MAX_IOPS: max_iops,
        common.QOS_MAX_BWS: max_bws,
    }
Example #3
0
def get_backend_qos_specs(volume):
    type_id = volume.volume_type_id
    if type_id is None:
        return None

    qos_specs = volume_types.get_volume_type_qos_specs(type_id)
    if qos_specs is None:
        return None

    qos_specs = qos_specs['qos_specs']
    if qos_specs is None:
        return None

    consumer = qos_specs['consumer']
    # Front end QoS specs are handled by nova. We ignore them here.
    if consumer not in BACKEND_QOS_CONSUMERS:
        return None

    max_iops = qos_specs['specs'].get(QOS_MAX_IOPS)
    max_bws = qos_specs['specs'].get(QOS_MAX_BWS)
    if max_iops is None and max_bws is None:
        return None

    return {
        'id': qos_specs['id'],
        QOS_MAX_IOPS: max_iops,
        QOS_MAX_BWS: max_bws,
    }
Example #4
0
    def execute(self, context, size, snapshot, image_id, source_volume,
                availability_zone, volume_type, metadata,
                key_manager, backup_source_volume):

        utils.check_exclusive_options(snapshot=snapshot,
                                      imageRef=image_id,
                                      source_volume=source_volume)
        policy.enforce_action(context, ACTION)

        # TODO(harlowja): what guarantee is there that the snapshot or source
        # volume will remain available after we do this initial verification??
        snapshot_id = self._extract_snapshot(snapshot)
        source_volid = self._extract_source_volume(source_volume)
        size = self._extract_size(size, source_volume, snapshot)

        self._check_image_metadata(context, image_id, size)

        availability_zone = self._extract_availability_zone(availability_zone,
                                                            snapshot,
                                                            source_volume)

        # TODO(joel-coffman): This special handling of snapshots to ensure that
        # their volume type matches the source volume is too convoluted. We
        # should copy encryption metadata from the encrypted volume type to the
        # volume upon creation and propagate that information to each snapshot.
        # This strategy avoid any dependency upon the encrypted volume type.
        if not volume_type and not source_volume and not snapshot:
            volume_type = volume_types.get_default_volume_type()

        volume_type_id = self._get_volume_type_id(volume_type,
                                                  source_volume, snapshot,
                                                  backup_source_volume)

        encryption_key_id = self._get_encryption_key_id(key_manager,
                                                        context,
                                                        volume_type_id,
                                                        snapshot,
                                                        source_volume,
                                                        backup_source_volume)

        specs = {}
        if volume_type_id:
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            specs = qos_specs['qos_specs']
        if not specs:
            # to make sure we don't pass empty dict
            specs = None

        self._check_metadata_properties(metadata)

        return {
            'size': size,
            'snapshot_id': snapshot_id,
            'source_volid': source_volid,
            'availability_zone': availability_zone,
            'volume_type': volume_type,
            'volume_type_id': volume_type_id,
            'encryption_key_id': encryption_key_id,
            'qos_specs': specs,
        }
Example #5
0
def associate_qos_with_type(context, specs_id, type_id):
    """Associate qos_specs with volume type.

    Associate target qos specs with specific volume type. Would raise
    following exceptions:
        VolumeTypeNotFound  - if volume type doesn't exist;
        QoSSpecsNotFound  - if qos specs doesn't exist;
        InvalidVolumeType  - if volume type is already associated with
                             qos specs other than given one.
        QoSSpecsAssociateFailed -  if there was general DB error
    :param specs_id: qos specs ID to associate with
    :param type_id: volume type ID to associate with
    """
    try:
        get_qos_specs(context, specs_id)
        res = volume_types.get_volume_type_qos_specs(type_id)
        if res.get('qos_specs', None):
            if res['qos_specs'].get('id') != specs_id:
                msg = (_("Type %(type_id)s is already associated with another "
                         "qos specs: %(qos_specs_id)s") %
                       {'type_id': type_id,
                        'qos_specs_id': res['qos_specs']['id']})
                raise exception.InvalidVolumeType(reason=msg)
        else:
            db.qos_specs_associate(context, specs_id, type_id)
    except db_exc.DBError as e:
        LOG.exception(_('DB error: %s') % e)
        LOG.warn(_('Failed to associate qos specs '
                   '%(id)s with type: %(vol_type_id)s') %
                 dict(id=specs_id, vol_type_id=type_id))
        raise exception.QoSSpecsAssociateFailed(specs_id=specs_id,
                                                type_id=type_id)
Example #6
0
    def execute(self, context, size, snapshot, image_id, source_volume,
                availability_zone, volume_type, metadata,
                key_manager, backup_source_volume):

        utils.check_exclusive_options(snapshot=snapshot,
                                      imageRef=image_id,
                                      source_volume=source_volume)
        policy.enforce_action(context, ACTION)

        # TODO(harlowja): what guarantee is there that the snapshot or source
        # volume will remain available after we do this initial verification??
        snapshot_id = self._extract_snapshot(snapshot)
        source_volid = self._extract_source_volume(source_volume)
        size = self._extract_size(size, source_volume, snapshot)

        self._check_image_metadata(context, image_id, size)

        availability_zone = self._extract_availability_zone(availability_zone,
                                                            snapshot,
                                                            source_volume)

        # TODO(joel-coffman): This special handling of snapshots to ensure that
        # their volume type matches the source volume is too convoluted. We
        # should copy encryption metadata from the encrypted volume type to the
        # volume upon creation and propagate that information to each snapshot.
        # This strategy avoid any dependency upon the encrypted volume type.
        if not volume_type and not source_volume and not snapshot:
            volume_type = volume_types.get_default_volume_type()

        volume_type_id = self._get_volume_type_id(volume_type,
                                                  source_volume, snapshot,
                                                  backup_source_volume)

        encryption_key_id = self._get_encryption_key_id(key_manager,
                                                        context,
                                                        volume_type_id,
                                                        snapshot,
                                                        source_volume,
                                                        backup_source_volume)

        specs = {}
        if volume_type_id:
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            specs = qos_specs['qos_specs']
        if not specs:
            # to make sure we don't pass empty dict
            specs = None

        self._check_metadata_properties(metadata)

        return {
            'size': size,
            'snapshot_id': snapshot_id,
            'source_volid': source_volid,
            'availability_zone': availability_zone,
            'volume_type': volume_type,
            'volume_type_id': volume_type_id,
            'encryption_key_id': encryption_key_id,
            'qos_specs': specs,
        }
Example #7
0
def associate_qos_with_type(context, specs_id, type_id):
    """Associate qos_specs with volume type.

    Associate target qos specs with specific volume type. Would raise
    following exceptions:
        VolumeTypeNotFound  - if volume type doesn't exist;
        QoSSpecsNotFound  - if qos specs doesn't exist;
        InvalidVolumeType  - if volume type is already associated with
                             qos specs other than given one.
        QoSSpecsAssociateFailed -  if there was general DB error
    :param specs_id: qos specs ID to associate with
    :param type_id: volume type ID to associate with
    """
    try:
        get_qos_specs(context, specs_id)
        res = volume_types.get_volume_type_qos_specs(type_id)
        if res.get('qos_specs', None):
            if res['qos_specs'].get('id') != specs_id:
                msg = (_("Type %(type_id)s is already associated with another "
                         "qos specs: %(qos_specs_id)s") %
                       {'type_id': type_id,
                        'qos_specs_id': res['qos_specs']['id']})
                raise exception.InvalidVolumeType(reason=msg)
        else:
            db.qos_specs_associate(context, specs_id, type_id)
    except db_exc.DBError:
        LOG.exception(_LE('DB error:'))
        LOG.warning(_LW('Failed to associate qos specs '
                        '%(id)s with type: %(vol_type_id)s'),
                    dict(id=specs_id, vol_type_id=type_id))
        raise exception.QoSSpecsAssociateFailed(specs_id=specs_id,
                                                type_id=type_id)
Example #8
0
def associate_qos_with_type(context, specs_id, type_id):
    """Associate qos_specs with volume type.

    Associate target qos specs with specific volume type.

    :param specs_id: qos specs ID to associate with
    :param type_id: volume type ID to associate with
    :raises VolumeTypeNotFound: if volume type doesn't exist
    :raises QoSSpecsNotFound: if qos specs doesn't exist
    :raises InvalidVolumeType: if volume type is already associated
                               with qos specs other than given one.
    :raises QoSSpecsAssociateFailed: if there was general DB error
    """
    try:
        get_qos_specs(context, specs_id)
        res = volume_types.get_volume_type_qos_specs(type_id)
        if res.get("qos_specs", None):
            if res["qos_specs"].get("id") != specs_id:
                msg = _("Type %(type_id)s is already associated with another " "qos specs: %(qos_specs_id)s") % {
                    "type_id": type_id,
                    "qos_specs_id": res["qos_specs"]["id"],
                }
                raise exception.InvalidVolumeType(reason=msg)
        else:
            db.qos_specs_associate(context, specs_id, type_id)
    except db_exc.DBError:
        LOG.exception(_LE("DB error:"))
        LOG.warning(
            _LW("Failed to associate qos specs " "%(id)s with type: %(vol_type_id)s"),
            dict(id=specs_id, vol_type_id=type_id),
        )
        raise exception.QoSSpecsAssociateFailed(specs_id=specs_id, type_id=type_id)
Example #9
0
    def test_get_volume_type_qos_specs(self):
        qos_ref = qos_specs.create(self.ctxt, "qos-specs-1", {"k1": "v1", "k2": "v2", "k3": "v3"})
        type_ref = volume_types.create(self.ctxt, "type1", {"key2": "val2", "key3": "val3"})
        res = volume_types.get_volume_type_qos_specs(type_ref["id"])
        self.assertIsNone(res["qos_specs"])
        qos_specs.associate_qos_with_type(self.ctxt, qos_ref["id"], type_ref["id"])

        expected = {
            "qos_specs": {
                "id": qos_ref["id"],
                "name": "qos-specs-1",
                "consumer": "back-end",
                "specs": {"k1": "v1", "k2": "v2", "k3": "v3"},
            }
        }
        res = volume_types.get_volume_type_qos_specs(type_ref["id"])
        self.assertDictMatch(expected, res)
    def test_get_volume_type_qos_specs(self):
        qos_ref = qos_specs.create(self.ctxt, 'qos-specs-1', {'k1': 'v1',
                                                              'k2': 'v2',
                                                              'k3': 'v3'})
        type_ref = volume_types.create(self.ctxt, "type1", {"key2": "val2",
                                                  "key3": "val3"})
        res = volume_types.get_volume_type_qos_specs(type_ref['id'])
        self.assertEquals(res['qos_specs'], {})
        qos_specs.associate_qos_with_type(self.ctxt,
                                          qos_ref['id'],
                                          type_ref['id'])

        expected = {'qos_specs': {'consumer': 'back-end',
                                  'k1': 'v1',
                                  'k2': 'v2',
                                  'k3': 'v3'}}
        res = volume_types.get_volume_type_qos_specs(type_ref['id'])
        self.assertDictMatch(expected, res)
Example #11
0
 def _update_volume_info_from_volume_type(self, volume_info, volume_type_id):
     if not volume_type_id:
         return
     else:
         volume_type = volume_types.get_volume_type(context.get_admin_context(), volume_type_id)
         extra_specs = volume_type.get("extra_specs")
         self._update_volume_info_from_extra_specs(volume_info, extra_specs)
         qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
         self._update_volume_info_from_qos_specs(volume_info, qos_specs)
Example #12
0
    def test_get_volume_type_qos_specs(self):
        qos_ref = qos_specs.create(self.ctxt, 'qos-specs-1', {'k1': 'v1',
                                                              'k2': 'v2',
                                                              'k3': 'v3'})
        type_ref = volume_types.create(self.ctxt, "type1", {"key2": "val2",
                                                  "key3": "val3"})
        res = volume_types.get_volume_type_qos_specs(type_ref['id'])
        self.assertEquals(res['qos_specs'], {})
        qos_specs.associate_qos_with_type(self.ctxt,
                                          qos_ref['id'],
                                          type_ref['id'])

        expected = {'qos_specs': {'consumer': 'back-end',
                                  'k1': 'v1',
                                  'k2': 'v2',
                                  'k3': 'v3'}}
        res = volume_types.get_volume_type_qos_specs(type_ref['id'])
        self.assertDictMatch(expected, res)
Example #13
0
    def initialize_connection(self, context, volume_id, connector):
        """Prepare volume for connection from host represented by connector.

        This method calls the driver initialize_connection and returns
        it to the caller.  The connector parameter is a dictionary with
        information about the host that will connect to the volume in the
        following format::

            {
                'ip': ip,
                'initiator': initiator,
            }

        ip: the ip address of the connecting machine

        initiator: the iscsi initiator name of the connecting machine.
        This can be None if the connecting machine does not support iscsi
        connections.

        driver is responsible for doing any necessary security setup and
        returning a connection_info dictionary in the following format::

            {
                'driver_volume_type': driver_volume_type,
                'data': data,
            }

        driver_volume_type: a string to identify the type of volume.  This
                           can be used by the calling code to determine the
                           strategy for connecting to the volume. This could
                           be 'iscsi', 'rbd', 'sheepdog', etc.

        data: this is the data that the calling code will use to connect
              to the volume. Keep in mind that this will be serialized to
              json in various places, so it should not contain any non-json
              data types.
        """
        volume_ref = self.db.volume_get(context, volume_id)
        self.driver.validate_connector(connector)
        conn_info = self.driver.initialize_connection(volume_ref, connector)

        # Add qos_specs to connection info
        typeid = volume_ref['volume_type_id']
        specs = {}
        if typeid:
            res = volume_types.get_volume_type_qos_specs(typeid)
            specs = res['qos_specs']

        # Don't pass qos_spec as empty dict
        qos_spec = dict(qos_spec=specs if specs else None)

        conn_info['data'].update(qos_spec)

        return conn_info
Example #14
0
 def _update_volume_info_from_volume_type(self, volume_info,
                                          volume_type_id):
     if not volume_type_id:
         return
     else:
         volume_type = volume_types.get_volume_type(
             context.get_admin_context(), volume_type_id)
         extra_specs = volume_type.get('extra_specs')
         self._update_volume_info_from_extra_specs(volume_info, extra_specs)
         qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
         self._update_volume_info_from_qos_specs(volume_info, qos_specs)
Example #15
0
    def _cast_create_consistencygroup(self, context, group_id,
                                      request_spec_list,
                                      filter_properties_list):

        try:
            for request_spec in request_spec_list:
                volume_type = request_spec.get('volume_type', None)
                volume_type_id = None
                if volume_type:
                    volume_type_id = volume_type.get('id', None)

                specs = {}
                if volume_type_id:
                    qos_specs = volume_types.get_volume_type_qos_specs(
                        volume_type_id)
                    specs = qos_specs['qos_specs']
                if not specs:
                    # to make sure we don't pass empty dict
                    specs = None

                volume_properties = {
                    'size': 0,  # Need to populate size for the scheduler
                    'user_id': context.user_id,
                    'project_id': context.project_id,
                    'status': 'creating',
                    'attach_status': 'detached',
                    'encryption_key_id': request_spec.get('encryption_key_id',
                                                          None),
                    'display_description': request_spec.get('description',
                                                            None),
                    'display_name': request_spec.get('name', None),
                    'volume_type_id': volume_type_id,
                }

                request_spec['volume_properties'] = volume_properties
                request_spec['qos_specs'] = specs

        except Exception:
            with excutils.save_and_reraise_exception():
                try:
                    self.db.consistencygroup_destroy(context, group_id)
                finally:
                    LOG.error(_LE("Error occurred when building "
                                  "request spec list for consistency group "
                                  "%s."), group_id)

        # Cast to the scheduler and let it handle whatever is needed
        # to select the target host for this group.
        self.scheduler_rpcapi.create_consistencygroup(
            context,
            CONF.volume_topic,
            group_id,
            request_spec_list=request_spec_list,
            filter_properties_list=filter_properties_list)
Example #16
0
    def _cast_create_consistencygroup(self, context, group,
                                      request_spec_list,
                                      filter_properties_list):

        try:
            for request_spec in request_spec_list:
                volume_type = request_spec.get('volume_type', None)
                volume_type_id = None
                if volume_type:
                    volume_type_id = volume_type.get('id', None)

                specs = {}
                if volume_type_id:
                    qos_specs = volume_types.get_volume_type_qos_specs(
                        volume_type_id)
                    specs = qos_specs['qos_specs']
                if not specs:
                    # to make sure we don't pass empty dict
                    specs = None

                volume_properties = {
                    'size': 0,  # Need to populate size for the scheduler
                    'user_id': context.user_id,
                    'project_id': context.project_id,
                    'status': 'creating',
                    'attach_status': 'detached',
                    'encryption_key_id': request_spec.get('encryption_key_id',
                                                          None),
                    'display_description': request_spec.get('description',
                                                            None),
                    'display_name': request_spec.get('name', None),
                    'volume_type_id': volume_type_id,
                }

                request_spec['volume_properties'] = volume_properties
                request_spec['qos_specs'] = specs

        except Exception:
            with excutils.save_and_reraise_exception():
                try:
                    group.destroy()
                finally:
                    LOG.error(_LE("Error occurred when building "
                                  "request spec list for consistency group "
                                  "%s."), group.id)

        # Cast to the scheduler and let it handle whatever is needed
        # to select the target host for this group.
        self.scheduler_rpcapi.create_consistencygroup(
            context,
            CONF.volume_topic,
            group,
            request_spec_list=request_spec_list,
            filter_properties_list=filter_properties_list)
Example #17
0
    def test_get_volume_type_qos_specs(self):
        qos_ref = qos_specs.create(self.ctxt, 'qos-specs-1', {'k1': 'v1',
                                                              'k2': 'v2',
                                                              'k3': 'v3'})
        type_ref = volume_types.create(self.ctxt, "type1", {"key2": "val2",
                                                            "key3": "val3"})
        res = volume_types.get_volume_type_qos_specs(type_ref['id'])
        self.assertIsNone(res['qos_specs'])
        qos_specs.associate_qos_with_type(self.ctxt,
                                          qos_ref['id'],
                                          type_ref['id'])

        expected = {'qos_specs': {'id': qos_ref['id'],
                                  'name': 'qos-specs-1',
                                  'consumer': 'back-end',
                                  'specs': {
                                      'k1': 'v1',
                                      'k2': 'v2',
                                      'k3': 'v3'}}}
        res = volume_types.get_volume_type_qos_specs(type_ref['id'])
        specs = db.qos_specs_get(self.ctxt, qos_ref['id'])
        expected['qos_specs']['created_at'] = specs['created_at']
        self.assertDictEqual(expected, res)
Example #18
0
    def _cast_create_consistencygroup(self, context, group, request_spec_list, filter_properties_list):

        try:
            for request_spec in request_spec_list:
                volume_type = request_spec.get("volume_type", None)
                volume_type_id = None
                if volume_type:
                    volume_type_id = volume_type.get("id", None)

                specs = {}
                if volume_type_id:
                    qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
                    specs = qos_specs["qos_specs"]
                if not specs:
                    # to make sure we don't pass empty dict
                    specs = None

                volume_properties = {
                    "size": 0,  # Need to populate size for the scheduler
                    "user_id": context.user_id,
                    "project_id": context.project_id,
                    "status": "creating",
                    "attach_status": "detached",
                    "encryption_key_id": request_spec.get("encryption_key_id", None),
                    "display_description": request_spec.get("description", None),
                    "display_name": request_spec.get("name", None),
                    "volume_type_id": volume_type_id,
                }

                request_spec["volume_properties"] = volume_properties
                request_spec["qos_specs"] = specs

        except Exception:
            with excutils.save_and_reraise_exception():
                try:
                    group.destroy()
                finally:
                    LOG.error(
                        _LE("Error occurred when building " "request spec list for consistency group " "%s."), group.id
                    )

        # Cast to the scheduler and let it handle whatever is needed
        # to select the target host for this group.
        self.scheduler_rpcapi.create_consistencygroup(
            context,
            CONF.volume_topic,
            group,
            request_spec_list=request_spec_list,
            filter_properties_list=filter_properties_list,
        )
Example #19
0
    def test_get_volume_type_qos_specs(self):
        qos_ref = qos_specs.create(self.ctxt, 'qos-specs-1', {'k1': 'v1',
                                                              'k2': 'v2',
                                                              'k3': 'v3'})
        type_ref = volume_types.create(self.ctxt, "type1", {"key2": "val2",
                                                            "key3": "val3"})
        res = volume_types.get_volume_type_qos_specs(type_ref['id'])
        self.assertIsNone(res['qos_specs'])
        qos_specs.associate_qos_with_type(self.ctxt,
                                          qos_ref['id'],
                                          type_ref['id'])

        expected = {'qos_specs': {'id': qos_ref['id'],
                                  'name': 'qos-specs-1',
                                  'consumer': 'back-end',
                                  'specs': {
                                      'k1': 'v1',
                                      'k2': 'v2',
                                      'k3': 'v3'}}}
        res = volume_types.get_volume_type_qos_specs(type_ref['id'])
        specs = db.qos_specs_get(self.ctxt, qos_ref['id'])
        expected['qos_specs']['created_at'] = specs['created_at']
        self.assertDictEqual(expected, res)
Example #20
0
 def create_volume(self, volume):
     """Driver entry point for creating a new volume."""
     try:
         qos_specs = None
         name, description, size = self.get_hedvig_volume_details(volume)
         vol_type_id = volume.volume_type_id
         if vol_type_id is not None:
             qos = volume_types.get_volume_type_qos_specs(vol_type_id)
             qos_specs = qos['qos_specs']
         self.hedvig_create_virtualdisk(name, description, size, qos_specs)
     except exception.VolumeDriverException:
         msg = _(
             'Failed to create volume %s. Rest API failed') % volume.name
         LOG.exception(msg)
         raise exception.VolumeDriverException(msg)
     except Exception:
         msg = _('Failed to create volume: %s') % volume.name
         LOG.exception(msg)
         raise exception.VolumeDriverException(msg)
Example #21
0
    def initialize_connection(self, context, volume_id, connector):
        """Prepare volume for connection from host represented by connector.

        This method calls the driver initialize_connection and returns
        it to the caller.  The connector parameter is a dictionary with
        information about the host that will connect to the volume in the
        following format::

            {
                'ip': ip,
                'initiator': initiator,
            }

        ip: the ip address of the connecting machine

        initiator: the iscsi initiator name of the connecting machine.
        This can be None if the connecting machine does not support iscsi
        connections.

        driver is responsible for doing any necessary security setup and
        returning a connection_info dictionary in the following format::

            {
                'driver_volume_type': driver_volume_type,
                'data': data,
            }

        driver_volume_type: a string to identify the type of volume.  This
                           can be used by the calling code to determine the
                           strategy for connecting to the volume. This could
                           be 'iscsi', 'rbd', 'sheepdog', etc.

        data: this is the data that the calling code will use to connect
              to the volume. Keep in mind that this will be serialized to
              json in various places, so it should not contain any non-json
              data types.
        """
        volume = self.db.volume_get(context, volume_id)
        self.driver.validate_connector(connector)
        conn_info = self.driver.initialize_connection(volume, connector)

        # Add qos_specs to connection info
        typeid = volume['volume_type_id']
        specs = None
        if typeid:
            res = volume_types.get_volume_type_qos_specs(typeid)
            qos = res['qos_specs']
            # only pass qos_specs that is designated to be consumed by
            # front-end, or both front-end and back-end.
            if qos and qos.get('consumer') in ['front-end', 'both']:
                specs = qos.get('specs')

        qos_spec = dict(qos_specs=specs)
        conn_info['data'].update(qos_spec)

        # Add access_mode to connection info
        volume_metadata = self.db.volume_admin_metadata_get(context.elevated(),
                                                            volume_id)
        if conn_info['data'].get('access_mode') is None:
            access_mode = volume_metadata.get('attached_mode')
            if access_mode is None:
                # NOTE(zhiyan): client didn't call 'os-attach' before
                access_mode = ('ro'
                               if volume_metadata.get('readonly') == 'True'
                               else 'rw')
            conn_info['data']['access_mode'] = access_mode
        return conn_info
    def execute(self, context, size, snapshot, image_id, source_volume,
                availability_zone, volume_type, metadata, key_manager,
                consistencygroup, cgsnapshot, group):

        utils.check_exclusive_options(snapshot=snapshot,
                                      imageRef=image_id,
                                      source_volume=source_volume)
        policy.enforce_action(context, ACTION)

        # TODO(harlowja): what guarantee is there that the snapshot or source
        # volume will remain available after we do this initial verification??
        snapshot_id = self._extract_snapshot(snapshot)
        source_volid = self._extract_source_volume(source_volume)
        size = self._extract_size(size, source_volume, snapshot)
        consistencygroup_id = self._extract_consistencygroup(consistencygroup)
        cgsnapshot_id = self._extract_cgsnapshot(cgsnapshot)
        group_id = self._extract_group(group)

        image_meta = self._get_image_metadata(context,
                                              image_id,
                                              size)

        availability_zone, refresh_az = self._extract_availability_zone(
            availability_zone, snapshot, source_volume, group)

        # TODO(joel-coffman): This special handling of snapshots to ensure that
        # their volume type matches the source volume is too convoluted. We
        # should copy encryption metadata from the encrypted volume type to the
        # volume upon creation and propagate that information to each snapshot.
        # This strategy avoids any dependency upon the encrypted volume type.
        def_vol_type = volume_types.get_default_volume_type()
        if not volume_type and not source_volume and not snapshot:
            image_volume_type = self._get_image_volume_type(context, image_id)
            volume_type = (image_volume_type if image_volume_type else
                           def_vol_type)

        volume_type_id = self._get_volume_type_id(volume_type,
                                                  source_volume, snapshot)

        encryption_key_id = self._get_encryption_key_id(
            key_manager,
            context,
            volume_type_id,
            snapshot,
            source_volume,
            image_meta)

        specs = {}
        if volume_type_id:
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            if qos_specs['qos_specs']:
                specs = qos_specs['qos_specs'].get('specs', {})

            # Determine default replication status
            extra_specs = volume_types.get_volume_type_extra_specs(
                volume_type_id)
        if not specs:
            # to make sure we don't pass empty dict
            specs = None
            extra_specs = None

        if vol_utils.is_replicated_spec(extra_specs):
            replication_status = fields.ReplicationStatus.ENABLED
        else:
            replication_status = fields.ReplicationStatus.DISABLED

        return {
            'size': size,
            'snapshot_id': snapshot_id,
            'source_volid': source_volid,
            'availability_zone': availability_zone,
            'volume_type': volume_type,
            'volume_type_id': volume_type_id,
            'encryption_key_id': encryption_key_id,
            'qos_specs': specs,
            'consistencygroup_id': consistencygroup_id,
            'cgsnapshot_id': cgsnapshot_id,
            'group_id': group_id,
            'replication_status': replication_status,
            'refresh_az': refresh_az
        }
Example #23
0
    def execute(self,
                context,
                size,
                snapshot,
                image_id,
                source_volume,
                availability_zone,
                volume_type,
                metadata,
                key_manager,
                consistencygroup,
                cgsnapshot,
                group,
                group_snapshot,
                backup,
                multiattach=False):

        utils.check_exclusive_options(snapshot=snapshot,
                                      imageRef=image_id,
                                      source_volume=source_volume,
                                      backup=backup)
        context.authorize(policy.CREATE_POLICY)

        # TODO(harlowja): what guarantee is there that the snapshot or source
        # volume will remain available after we do this initial verification??
        snapshot_id = self._extract_snapshot(snapshot)
        source_volid = self._extract_source_volume(source_volume)
        backup_id = self._extract_backup(backup)
        size = self._extract_size(size, source_volume, snapshot, backup)
        consistencygroup_id = self._extract_consistencygroup(consistencygroup)
        cgsnapshot_id = self._extract_cgsnapshot(cgsnapshot)
        group_id = self._extract_group(group)

        image_meta = self._get_image_metadata(context, image_id, size)

        image_properties = image_meta.get('properties',
                                          {}) if image_meta else {}
        image_volume_type = image_properties.get(
            'cinder_img_volume_type', None) if image_properties else None

        volume_type = self._get_volume_type(context, volume_type,
                                            source_volume, snapshot,
                                            image_volume_type)

        volume_type_id = volume_type.get('id') if volume_type else None

        availability_zones, refresh_az = self._extract_availability_zones(
            availability_zone,
            snapshot,
            source_volume,
            group,
            volume_type=volume_type)

        encryption_key_id = self._get_encryption_key_id(
            key_manager, context, volume_type_id, snapshot, source_volume,
            image_meta)  # new key id that's been cloned already

        if volume_type_id:
            volume_type = objects.VolumeType.get_by_name_or_id(
                context, volume_type_id)
            extra_specs = volume_type.get('extra_specs', {})
            # NOTE(tommylikehu): Although the parameter `multiattach` from
            # create volume API is deprecated now, we still need to consider
            # it when multiattach is not enabled in volume type.
            multiattach = (extra_specs.get('multiattach', '') == '<is> True'
                           or multiattach)
            if multiattach and encryption_key_id:
                msg = _('Multiattach cannot be used with encrypted volumes.')
                raise exception.InvalidVolume(reason=msg)

        if multiattach:
            context.authorize(policy.MULTIATTACH_POLICY)

        specs = {}
        if volume_type_id:
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            if qos_specs['qos_specs']:
                specs = qos_specs['qos_specs'].get('specs', {})

            # Determine default replication status
            extra_specs = volume_types.get_volume_type_extra_specs(
                volume_type_id)
        if not specs:
            # to make sure we don't pass empty dict
            specs = None
            extra_specs = None

        if volume_utils.is_replicated_spec(extra_specs):
            replication_status = fields.ReplicationStatus.ENABLED
        else:
            replication_status = fields.ReplicationStatus.DISABLED

        return {
            'size': size,
            'snapshot_id': snapshot_id,
            'source_volid': source_volid,
            'volume_type': volume_type,
            'volume_type_id': volume_type_id,
            'encryption_key_id': encryption_key_id,
            'qos_specs': specs,
            'consistencygroup_id': consistencygroup_id,
            'cgsnapshot_id': cgsnapshot_id,
            'group_id': group_id,
            'replication_status': replication_status,
            'refresh_az': refresh_az,
            'backup_id': backup_id,
            'multiattach': multiattach,
            'availability_zones': availability_zones
        }
Example #24
0
    def execute(self, context, size, snapshot, image_id, source_volume,
                availability_zone, volume_type, metadata, key_manager,
                consistencygroup, cgsnapshot, group, group_snapshot, backup,
                multiattach=False):

        utils.check_exclusive_options(snapshot=snapshot,
                                      imageRef=image_id,
                                      source_volume=source_volume,
                                      backup=backup)
        context.authorize(policy.CREATE_POLICY)

        # TODO(harlowja): what guarantee is there that the snapshot or source
        # volume will remain available after we do this initial verification??
        snapshot_id = self._extract_snapshot(snapshot)
        source_volid = self._extract_source_volume(source_volume)
        backup_id = self._extract_backup(backup)
        size = self._extract_size(size, source_volume, snapshot, backup)
        consistencygroup_id = self._extract_consistencygroup(consistencygroup)
        cgsnapshot_id = self._extract_cgsnapshot(cgsnapshot)
        group_id = self._extract_group(group)

        image_meta = self._get_image_metadata(context,
                                              image_id,
                                              size)

        image_properties = image_meta.get(
            'properties', {}) if image_meta else {}
        image_volume_type = image_properties.get(
            'cinder_img_volume_type', None) if image_properties else None

        volume_type = self._get_volume_type(
            context, volume_type, source_volume, snapshot, image_volume_type)

        volume_type_id = volume_type.get('id') if volume_type else None

        availability_zones, refresh_az = self._extract_availability_zones(
            availability_zone, snapshot, source_volume, group,
            volume_type=volume_type)

        encryption_key_id = self._get_encryption_key_id(
            key_manager,
            context,
            volume_type_id,
            snapshot,
            source_volume,
            image_meta)

        if volume_type_id:
            volume_type = objects.VolumeType.get_by_name_or_id(
                context, volume_type_id)
            extra_specs = volume_type.get('extra_specs', {})
            # NOTE(tommylikehu): Although the parameter `multiattach` from
            # create volume API is deprecated now, we still need to consider
            # it when multiattach is not enabled in volume type.
            multiattach = (extra_specs.get(
                'multiattach', '') == '<is> True' or multiattach)
            if multiattach and encryption_key_id:
                msg = _('Multiattach cannot be used with encrypted volumes.')
                raise exception.InvalidVolume(reason=msg)

        if multiattach:
            context.authorize(policy.MULTIATTACH_POLICY)

        specs = {}
        if volume_type_id:
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            if qos_specs['qos_specs']:
                specs = qos_specs['qos_specs'].get('specs', {})

            # Determine default replication status
            extra_specs = volume_types.get_volume_type_extra_specs(
                volume_type_id)
        if not specs:
            # to make sure we don't pass empty dict
            specs = None
            extra_specs = None

        if vol_utils.is_replicated_spec(extra_specs):
            replication_status = fields.ReplicationStatus.ENABLED
        else:
            replication_status = fields.ReplicationStatus.DISABLED

        return {
            'size': size,
            'snapshot_id': snapshot_id,
            'source_volid': source_volid,
            'volume_type': volume_type,
            'volume_type_id': volume_type_id,
            'encryption_key_id': encryption_key_id,
            'qos_specs': specs,
            'consistencygroup_id': consistencygroup_id,
            'cgsnapshot_id': cgsnapshot_id,
            'group_id': group_id,
            'replication_status': replication_status,
            'refresh_az': refresh_az,
            'backup_id': backup_id,
            'multiattach': multiattach,
            'availability_zones': availability_zones
        }
Example #25
0
    def initialize_connection(self, context, volume_id, connector):
        """Prepare volume for connection from host represented by connector.

        This method calls the driver initialize_connection and returns
        it to the caller.  The connector parameter is a dictionary with
        information about the host that will connect to the volume in the
        following format::

            {
                'ip': ip,
                'initiator': initiator,
            }

        ip: the ip address of the connecting machine

        initiator: the iscsi initiator name of the connecting machine.
        This can be None if the connecting machine does not support iscsi
        connections.

        driver is responsible for doing any necessary security setup and
        returning a connection_info dictionary in the following format::

            {
                'driver_volume_type': driver_volume_type,
                'data': data,
            }

        driver_volume_type: a string to identify the type of volume.  This
                           can be used by the calling code to determine the
                           strategy for connecting to the volume. This could
                           be 'iscsi', 'rbd', 'sheepdog', etc.

        data: this is the data that the calling code will use to connect
              to the volume. Keep in mind that this will be serialized to
              json in various places, so it should not contain any non-json
              data types.
        """
        volume = self.db.volume_get(context, volume_id)
        self.driver.validate_connector(connector)
        conn_info = self.driver.initialize_connection(volume, connector)

        # Add qos_specs to connection info
        typeid = volume["volume_type_id"]
        specs = {}
        if typeid:
            res = volume_types.get_volume_type_qos_specs(typeid)
            specs = res["qos_specs"]

        # Don't pass qos_spec as empty dict
        qos_spec = dict(qos_spec=specs if specs else None)

        conn_info["data"].update(qos_spec)

        # Add access_mode to connection info
        volume_metadata = self.db.volume_admin_metadata_get(context.elevated(), volume_id)
        if conn_info["data"].get("access_mode") is None:
            access_mode = volume_metadata.get("attached_mode")
            if access_mode is None:
                # NOTE(zhiyan): client didn't call 'os-attach' before
                access_mode = "ro" if volume_metadata.get("readonly") == "True" else "rw"
            conn_info["data"]["access_mode"] = access_mode
        return conn_info
Example #26
0
    def execute(
        self,
        context,
        size,
        snapshot,
        image_id,
        source_volume,
        availability_zone,
        volume_type,
        metadata,
        key_manager,
        backup_source_volume,
        source_replica,
        consistencygroup,
    ):

        utils.check_exclusive_options(snapshot=snapshot, imageRef=image_id, source_volume=source_volume)
        policy.enforce_action(context, ACTION)

        # TODO(harlowja): what guarantee is there that the snapshot or source
        # volume will remain available after we do this initial verification??
        snapshot_id = self._extract_snapshot(snapshot)
        source_volid = self._extract_source_volume(source_volume)
        source_replicaid = self._extract_source_replica(source_replica)
        size = self._extract_size(size, source_volume, snapshot)
        consistencygroup_id = self._extract_consistencygroup(consistencygroup)

        self._check_image_metadata(context, image_id, size)

        availability_zone = self._extract_availability_zone(availability_zone, snapshot, source_volume)

        # TODO(joel-coffman): This special handling of snapshots to ensure that
        # their volume type matches the source volume is too convoluted. We
        # should copy encryption metadata from the encrypted volume type to the
        # volume upon creation and propagate that information to each snapshot.
        # This strategy avoid any dependency upon the encrypted volume type.
        def_vol_type = volume_types.get_default_volume_type()
        if not volume_type and not source_volume and not snapshot:
            volume_type = def_vol_type

        # When creating a clone of a replica (replication test), we can't
        # use the volume type of the replica, therefore, we use the default.
        # NOTE(ronenkat): this assumes the default type is not replicated.
        if source_replicaid:
            volume_type = def_vol_type

        volume_type_id = self._get_volume_type_id(volume_type, source_volume, snapshot, backup_source_volume)

        encryption_key_id = self._get_encryption_key_id(
            key_manager, context, volume_type_id, snapshot, source_volume, backup_source_volume
        )

        specs = {}
        if volume_type_id:
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            specs = qos_specs["qos_specs"]
        if not specs:
            # to make sure we don't pass empty dict
            specs = None

        self._check_metadata_properties(metadata)

        return {
            "size": size,
            "snapshot_id": snapshot_id,
            "source_volid": source_volid,
            "availability_zone": availability_zone,
            "volume_type": volume_type,
            "volume_type_id": volume_type_id,
            "encryption_key_id": encryption_key_id,
            "qos_specs": specs,
            "source_replicaid": source_replicaid,
            "consistencygroup_id": consistencygroup_id,
        }
Example #27
0
    def initialize_connection(self, context, volume_id, connector):
        """Prepare volume for connection from host represented by connector.

        This method calls the driver initialize_connection and returns
        it to the caller.  The connector parameter is a dictionary with
        information about the host that will connect to the volume in the
        following format::

            {
                'ip': ip,
                'initiator': initiator,
            }

        ip: the ip address of the connecting machine

        initiator: the iscsi initiator name of the connecting machine.
        This can be None if the connecting machine does not support iscsi
        connections.

        driver is responsible for doing any necessary security setup and
        returning a connection_info dictionary in the following format::

            {
                'driver_volume_type': driver_volume_type,
                'data': data,
            }

        driver_volume_type: a string to identify the type of volume.  This
                           can be used by the calling code to determine the
                           strategy for connecting to the volume. This could
                           be 'iscsi', 'rbd', 'sheepdog', etc.

        data: this is the data that the calling code will use to connect
              to the volume. Keep in mind that this will be serialized to
              json in various places, so it should not contain any non-json
              data types.
        """
        volume = self.db.volume_get(context, volume_id)
        self.driver.validate_connector(connector)
        conn_info = self.driver.initialize_connection(volume, connector)

        # Add qos_specs to connection info
        typeid = volume['volume_type_id']
        specs = {}
        if typeid:
            res = volume_types.get_volume_type_qos_specs(typeid)
            specs = res['qos_specs']

        # Don't pass qos_spec as empty dict
        qos_spec = dict(qos_spec=specs if specs else None)

        conn_info['data'].update(qos_spec)

        # Add access_mode to connection info
        volume_metadata = self.db.volume_admin_metadata_get(context.elevated(),
                                                            volume_id)
        if conn_info['data'].get('access_mode') is None:
            access_mode = volume_metadata.get('attached_mode')
            if access_mode is None:
                # NOTE(zhiyan): client didn't call 'os-attach' before
                access_mode = ('ro'
                               if volume_metadata.get('readonly') == 'True'
                               else 'rw')
            conn_info['data']['access_mode'] = access_mode
        return conn_info
Example #28
0
    def initialize_connection(self, context, volume_id, connector):
        """Prepare volume for connection from host represented by connector.

        This method calls the driver initialize_connection and returns
        it to the caller.  The connector parameter is a dictionary with
        information about the host that will connect to the volume in the
        following format::

            {
                'ip': ip,
                'initiator': initiator,
            }

        ip: the ip address of the connecting machine

        initiator: the iscsi initiator name of the connecting machine.
        This can be None if the connecting machine does not support iscsi
        connections.

        driver is responsible for doing any necessary security setup and
        returning a connection_info dictionary in the following format::

            {
                'driver_volume_type': driver_volume_type,
                'data': data,
            }

        driver_volume_type: a string to identify the type of volume.  This
                           can be used by the calling code to determine the
                           strategy for connecting to the volume. This could
                           be 'iscsi', 'rbd', 'sheepdog', etc.

        data: this is the data that the calling code will use to connect
              to the volume. Keep in mind that this will be serialized to
              json in various places, so it should not contain any non-json
              data types.
        """
        # NOTE(flaper87): Verify the driver is enabled
        # before going forward. The exception will be caught
        # and the volume status updated.
        utils.require_driver_initialized(self.driver)

        volume = self.db.volume_get(context, volume_id)
        self.driver.validate_connector(connector)
        instance_uuid = connector.get('instance_uuid', None)
        self._check_attach_same_instance(context, volume, instance_uuid)
        conn_info = self.driver.initialize_connection(volume, connector)

        # Add qos_specs to connection info
        typeid = volume['volume_type_id']
        specs = None
        if typeid:
            res = volume_types.get_volume_type_qos_specs(typeid)
            qos = res['qos_specs']
            # only pass qos_specs that is designated to be consumed by
            # front-end, or both front-end and back-end.
            if qos and qos.get('consumer') in ['front-end', 'both']:
                specs = qos.get('specs')

        qos_spec = dict(qos_specs=specs)
        conn_info['data'].update(qos_spec)

        # Add access_mode to connection info
        volume_metadata = self.db.volume_admin_metadata_get(context.elevated(),
                                                            volume_id)
        if conn_info['data'].get('access_mode') is None:
            access_mode = volume_metadata.get('attached_mode')
            if access_mode is None:
                # NOTE(zhiyan): client didn't call 'os-attach' before
                access_mode = ('ro'
                               if volume_metadata.get('readonly') == 'True'
                               else 'rw')
            conn_info['data']['access_mode'] = access_mode
        return conn_info
Example #29
0
    def execute(self, context, size, snapshot, image_id, source_volume,
                availability_zone, volume_type, metadata, key_manager,
                source_replica, consistencygroup, cgsnapshot):

        utils.check_exclusive_options(snapshot=snapshot,
                                      imageRef=image_id,
                                      source_volume=source_volume)
        policy.enforce_action(context, ACTION)

        # TODO(harlowja): what guarantee is there that the snapshot or source
        # volume will remain available after we do this initial verification??
        snapshot_id = self._extract_snapshot(snapshot)
        source_volid = self._extract_source_volume(source_volume)
        source_replicaid = self._extract_source_replica(source_replica)
        size = self._extract_size(size, source_volume, snapshot)
        consistencygroup_id = self._extract_consistencygroup(consistencygroup)
        cgsnapshot_id = self._extract_cgsnapshot(cgsnapshot)

        self._check_image_metadata(context, image_id, size)

        availability_zone = self._extract_availability_zone(availability_zone,
                                                            snapshot,
                                                            source_volume)

        # TODO(joel-coffman): This special handling of snapshots to ensure that
        # their volume type matches the source volume is too convoluted. We
        # should copy encryption metadata from the encrypted volume type to the
        # volume upon creation and propagate that information to each snapshot.
        # This strategy avoids any dependency upon the encrypted volume type.
        def_vol_type = volume_types.get_default_volume_type()
        if not volume_type and not source_volume and not snapshot:
            image_volume_type = self._get_image_volume_type(context, image_id)
            volume_type = (image_volume_type if image_volume_type else
                           def_vol_type)

        # When creating a clone of a replica (replication test), we can't
        # use the volume type of the replica, therefore, we use the default.
        # NOTE(ronenkat): this assumes the default type is not replicated.
        if source_replicaid:
            volume_type = def_vol_type

        volume_type_id = self._get_volume_type_id(volume_type,
                                                  source_volume, snapshot)

        if image_id and volume_types.is_encrypted(context, volume_type_id):
            msg = _('Create encrypted volumes with type %(type)s '
                    'from image %(image)s is not supported.')
            msg = msg % {'type': volume_type_id,
                         'image': image_id, }
            raise exception.InvalidInput(reason=msg)

        encryption_key_id = self._get_encryption_key_id(key_manager,
                                                        context,
                                                        volume_type_id,
                                                        snapshot,
                                                        source_volume)

        specs = {}
        if volume_type_id:
            qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
            if qos_specs['qos_specs']:
                specs = qos_specs['qos_specs'].get('specs', {})
        if not specs:
            # to make sure we don't pass empty dict
            specs = None

        utils.check_metadata_properties(metadata)

        return {
            'size': size,
            'snapshot_id': snapshot_id,
            'source_volid': source_volid,
            'availability_zone': availability_zone,
            'volume_type': volume_type,
            'volume_type_id': volume_type_id,
            'encryption_key_id': encryption_key_id,
            'qos_specs': specs,
            'source_replicaid': source_replicaid,
            'consistencygroup_id': consistencygroup_id,
            'cgsnapshot_id': cgsnapshot_id,
        }
Example #30
0
    def initialize_connection(self, context, volume_id, connector):
        """Prepare volume for connection from host represented by connector.

        This method calls the driver initialize_connection and returns
        it to the caller.  The connector parameter is a dictionary with
        information about the host that will connect to the volume in the
        following format::

            {
                'ip': ip,
                'initiator': initiator,
            }

        ip: the ip address of the connecting machine

        initiator: the iscsi initiator name of the connecting machine.
        This can be None if the connecting machine does not support iscsi
        connections.

        driver is responsible for doing any necessary security setup and
        returning a connection_info dictionary in the following format::

            {
                'driver_volume_type': driver_volume_type,
                'data': data,
            }

        driver_volume_type: a string to identify the type of volume.  This
                           can be used by the calling code to determine the
                           strategy for connecting to the volume. This could
                           be 'iscsi', 'rbd', 'sheepdog', etc.

        data: this is the data that the calling code will use to connect
              to the volume. Keep in mind that this will be serialized to
              json in various places, so it should not contain any non-json
              data types.
        """
        # NOTE(flaper87): Verify the driver is enabled
        # before going forward. The exception will be caught
        # and the volume status updated.
        utils.require_driver_initialized(self.driver)

        volume = self.db.volume_get(context, volume_id)
        self.driver.validate_connector(connector)
        try:
            conn_info = self.driver.initialize_connection(volume, connector)
        except Exception as err:
            err_msg = _("Unable to fetch connection information from " "backend: %(err)s") % {"err": str(err)}
            LOG.error(err_msg)
            raise exception.VolumeBackendAPIException(data=err_msg)

        # Add qos_specs to connection info
        typeid = volume["volume_type_id"]
        specs = None
        if typeid:
            res = volume_types.get_volume_type_qos_specs(typeid)
            qos = res["qos_specs"]
            # only pass qos_specs that is designated to be consumed by
            # front-end, or both front-end and back-end.
            if qos and qos.get("consumer") in ["front-end", "both"]:
                specs = qos.get("specs")

        qos_spec = dict(qos_specs=specs)
        conn_info["data"].update(qos_spec)

        # Add access_mode to connection info
        volume_metadata = self.db.volume_admin_metadata_get(context.elevated(), volume_id)
        if conn_info["data"].get("access_mode") is None:
            access_mode = volume_metadata.get("attached_mode")
            if access_mode is None:
                # NOTE(zhiyan): client didn't call 'os-attach' before
                access_mode = "ro" if volume_metadata.get("readonly") == "True" else "rw"
            conn_info["data"]["access_mode"] = access_mode
        return conn_info