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, }
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, }
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, }
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, }
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)
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, }
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)
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)
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)
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)
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)
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
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)
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)
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)
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)
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, )
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)
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)
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 }
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 }
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 }
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
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, }
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
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
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, }
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