def test_snapshot_detail(self, get_all_snapshots, snapshot_get_by_id, volume_get_by_id, snapshot_metadata_get): snapshot = { 'id': UUID, 'volume_id': 1, 'status': 'available', 'volume_size': 100, 'display_name': 'Default name', 'display_description': 'Default description', 'expected_attrs': ['metadata'] } ctx = context.RequestContext('admin', 'fake', True) snapshot_obj = fake_snapshot.fake_snapshot_obj(ctx, **snapshot) fake_volume_obj = fake_volume.fake_volume_obj(ctx) snapshot_get_by_id.return_value = snapshot_obj volume_get_by_id.return_value = fake_volume_obj snapshots = objects.SnapshotList(objects=[snapshot_obj]) get_all_snapshots.return_value = snapshots req = fakes.HTTPRequest.blank('/v2/snapshots/detail') resp_dict = self.controller.detail(req) self.assertIn('snapshots', resp_dict) resp_snapshots = resp_dict['snapshots'] self.assertEqual(len(resp_snapshots), 1) resp_snapshot = resp_snapshots.pop() self.assertEqual(resp_snapshot['id'], UUID)
def test_snapshot_detail(self, get_all_snapshots, snapshot_get_by_id, volume_get_by_id, snapshot_metadata_get): snapshot = { 'id': UUID, 'volume_id': fake.VOLUME_ID, 'status': fields.SnapshotStatus.AVAILABLE, 'volume_size': 100, 'display_name': 'Default name', 'display_description': 'Default description', 'expected_attrs': ['metadata'] } ctx = context.RequestContext(fake.PROJECT_ID, fake.USER_ID, True) snapshot_obj = fake_snapshot.fake_snapshot_obj(ctx, **snapshot) fake_volume_obj = fake_volume.fake_volume_obj(ctx) snapshot_get_by_id.return_value = snapshot_obj volume_get_by_id.return_value = fake_volume_obj snapshots = objects.SnapshotList(objects=[snapshot_obj]) get_all_snapshots.return_value = snapshots req = fakes.HTTPRequest.blank('/v2/snapshots/detail') resp_dict = self.controller.detail(req) self.assertIn('snapshots', resp_dict) resp_snapshots = resp_dict['snapshots'] self.assertEqual(1, len(resp_snapshots)) self.assertIn('updated_at', resp_snapshots[0]) resp_snapshot = resp_snapshots.pop() self.assertEqual(UUID, resp_snapshot['id'])
def delete_cgsnapshot(self, context, cgsnapshot, snapshots): """Deletes a cgsnapshot. If profile isn't found return success. If failed to delete the replay (the snapshot) then raise an exception. :param context: the context of the caller. :param cgsnapshot: Information about the snapshot to delete. :return: Updated model_update, snapshots. :raises: VolumeBackendAPIException. """ cgid = cgsnapshot['consistencygroup_id'] snapshotid = cgsnapshot['id'] with self._client.open_connection() as api: profile = api.find_replay_profile(cgid) if profile: LOG.info(_LI('Deleting snapshot %(ss)s from %(pro)s'), {'ss': snapshotid, 'pro': profile}) if not api.delete_cg_replay(profile, snapshotid): raise exception.VolumeBackendAPIException( _('Unable to delete Consistency Group snapshot %s') % snapshotid) snapshots = objects.SnapshotList().get_all_for_cgsnapshot( context, snapshotid) for snapshot in snapshots: snapshot.status = 'deleted' model_update = {'status': 'deleted'} return model_update, snapshots
def create_cgsnapshot(self, context, cgsnapshot, snapshots): """Takes a snapshot of the consistency group. :param context: the context of the caller. :param cgsnapshot: Information about the snapshot to take. :return: Updated model_update, snapshots. :raises: VolumeBackendAPIException. """ cgid = cgsnapshot['consistencygroup_id'] snapshotid = cgsnapshot['id'] with self._client.open_connection() as api: profile = api.find_replay_profile(cgid) if profile: LOG.debug('profile %s replayid %s', profile, snapshotid) if api.snap_cg_replay(profile, snapshotid, 0): snapshots = objects.SnapshotList().get_all_for_cgsnapshot( context, snapshotid) for snapshot in snapshots: snapshot.status = 'available' model_update = {'status': 'available'} return model_update, snapshots # That didn't go well. Tell them why. Then bomb out. LOG.error(_LE('Failed to snap Consistency Group %s'), cgid) else: LOG.error(_LE('Cannot find Consistency Group %s'), cgid) raise exception.VolumeBackendAPIException( _('Unable to snap Consistency Group %s') % cgid)
def test_save_with_snapshots(self): db_volume = fake_volume.fake_db_volume() volume = objects.Volume._from_db_object(self.context, objects.Volume(), db_volume) volume.display_name = 'foobar' volume.snapshots = objects.SnapshotList() self.assertRaises(exception.ObjectActionError, volume.save)
def _from_db_object(context, volume, db_volume, expected_attrs=None): if expected_attrs is None: expected_attrs = [] for name, field in volume.fields.items(): if name in Volume.OPTIONAL_FIELDS: continue value = db_volume.get(name) if isinstance(field, fields.IntegerField): value = value or 0 volume[name] = value # Get data from db_volume object that was queried by joined query # from DB if 'metadata' in expected_attrs: metadata = db_volume.get('volume_metadata', []) volume.metadata = {item['key']: item['value'] for item in metadata} if 'admin_metadata' in expected_attrs: metadata = db_volume.get('volume_admin_metadata', []) volume.admin_metadata = { item['key']: item['value'] for item in metadata } if 'glance_metadata' in expected_attrs: metadata = db_volume.get('volume_glance_metadata', []) volume.glance_metadata = { item['key']: item['value'] for item in metadata } if 'volume_type' in expected_attrs: db_volume_type = db_volume.get('volume_type') if db_volume_type: vt_expected_attrs = [] if 'volume_type.extra_specs' in expected_attrs: vt_expected_attrs.append('extra_specs') volume.volume_type = objects.VolumeType._from_db_object( context, objects.VolumeType(), db_volume_type, expected_attrs=vt_expected_attrs) if 'volume_attachment' in expected_attrs: attachments = base.obj_make_list( context, objects.VolumeAttachmentList(context), objects.VolumeAttachment, db_volume.get('volume_attachment')) volume.volume_attachment = attachments if 'consistencygroup' in expected_attrs: consistencygroup = objects.ConsistencyGroup(context) consistencygroup._from_db_object(context, consistencygroup, db_volume['consistencygroup']) volume.consistencygroup = consistencygroup if 'snapshots' in expected_attrs: snapshots = base.obj_make_list(context, objects.SnapshotList(context), objects.Snapshot, db_volume['snapshots']) volume.snapshots = snapshots volume._context = context volume.obj_reset_changes() return volume
def create_cgsnapshot(self, context, cgsnapshot): """Creates a consistency group snapshot.""" client = self._login() try: # TODO(aorourke): Can't eliminate the DB calls here due to CG API. # Will fix in M release snapshots = objects.SnapshotList().get_all_for_cgsnapshot( context, cgsnapshot['id']) snap_set = [] snapshot_base_name = "snapshot-" + cgsnapshot['id'] for i, snapshot in enumerate(snapshots): volume = snapshot.volume volume_name = volume['name'] try: volume_info = client.getVolumeByName(volume_name) except Exception as ex: error = six.text_type(ex) LOG.error( _LE("Could not find volume with name %(name)s. " "Error: %(error)s"), { 'name': volume_name, 'error': error }) raise exception.VolumeBackendAPIException(data=error) volume_id = volume_info['id'] snapshot_name = snapshot_base_name + "-" + six.text_type(i) snap_set_member = { 'volumeName': volume_name, 'volumeId': volume_id, 'snapshotName': snapshot_name } snap_set.append(snap_set_member) snapshot.status = 'available' source_volume_id = snap_set[0]['volumeId'] optional = {'inheritAccess': True} description = cgsnapshot.get('description', None) if description: optional['description'] = description try: client.createSnapshotSet(source_volume_id, snap_set, optional) except Exception as ex: error = six.text_type(ex) LOG.error(_LE("Could not create snapshot set. Error: '%s'"), error) raise exception.VolumeBackendAPIException(data=error) except Exception as ex: raise exception.VolumeBackendAPIException(data=six.text_type(ex)) finally: self._logout(client) model_update = {'status': 'available'} return model_update, snapshots
def snapshots(self): # Lazy loading if self._snapshots is None: self._snapshots = self.persistence.get_snapshots(volume_id=self.id) for snap in self._snapshots: snap.volume = self ovos = [snap._ovo for snap in self._snapshots] self._ovo.snapshots = cinder_objs.SnapshotList(objects=ovos) self._ovo.obj_reset_changes(('snapshots', )) return self._snapshots
def delete_cgsnapshot(self, context, cgsnapshot, snapshots): """Deletes a cgsnapshot.""" self.client.req('snapshot-sets', 'DELETE', name=self._get_cgsnap_name(cgsnapshot), ver='v2') snapshots = objects.SnapshotList().get_all_for_cgsnapshot( context, cgsnapshot['id']) for snapshot in snapshots: snapshot.status = 'deleted' model_update = {'status': cgsnapshot.status} return model_update, snapshots
def create_cgsnapshot(self, context, cgsnapshot, snapshots): """Creates a cgsnapshot.""" data = {'consistency-group-id': cgsnapshot['consistencygroup_id'], 'snapshot-set-name': self._get_cgsnap_name(cgsnapshot)} self.client.req('snapshots', 'POST', data, ver='v2') snapshots = objects.SnapshotList().get_all_for_cgsnapshot( context, cgsnapshot['id']) for snapshot in snapshots: snapshot.status = 'available' model_update = {'status': 'available'} return model_update, snapshots
def create_cgsnapshot(self, context, cgsnapshot): """Creates a cgsnapshot.""" cg_id = cgsnapshot.consistencygroup_id pgroup_name = self._get_pgroup_name_from_id(cg_id) pgsnap_suffix = self._get_pgroup_snap_suffix(cgsnapshot) self._array.create_pgroup_snapshot(pgroup_name, suffix=pgsnap_suffix) snapshots = objects.SnapshotList().get_all_for_cgsnapshot( context, cgsnapshot.id) for snapshot in snapshots: snapshot.status = 'available' model_update = {'status': 'available'} return model_update, snapshots
def test_obj_load_attr(self, snapshotlist_get_for_cgs, consistencygroup_get_by_id): cgsnapshot = objects.CGSnapshot._from_db_object( self.context, objects.CGSnapshot(), fake_cgsnapshot) # Test consistencygroup lazy-loaded field consistencygroup = objects.ConsistencyGroup(context=self.context, id=2) consistencygroup_get_by_id.return_value = consistencygroup self.assertEqual(consistencygroup, cgsnapshot.consistencygroup) consistencygroup_get_by_id.assert_called_once_with( self.context, cgsnapshot.consistencygroup_id) # Test snapshots lazy-loaded field snapshots_objs = [objects.Snapshot(context=self.context, id=i) for i in [3, 4, 5]] snapshots = objects.SnapshotList(context=self.context, objects=snapshots_objs) snapshotlist_get_for_cgs.return_value = snapshots self.assertEqual(snapshots, cgsnapshot.snapshots) snapshotlist_get_for_cgs.assert_called_once_with( self.context, cgsnapshot.id)
def test_obj_load_attr(self, snapshotlist_get_for_cgs, group_get_by_id): group_snapshot = objects.GroupSnapshot._from_db_object( self.context, objects.GroupSnapshot(), fake_group_snapshot) # Test group lazy-loaded field group = objects.Group(context=self.context, id=fake.GROUP_ID) group_get_by_id.return_value = group self.assertEqual(group, group_snapshot.group) group_get_by_id.assert_called_once_with(self.context, group_snapshot.group_id) # Test snapshots lazy-loaded field snapshots_objs = [ objects.Snapshot(context=self.context, id=i) for i in [fake.SNAPSHOT_ID, fake.SNAPSHOT2_ID, fake.SNAPSHOT3_ID] ] snapshots = objects.SnapshotList(context=self.context, objects=snapshots_objs) snapshotlist_get_for_cgs.return_value = snapshots self.assertEqual(snapshots, group_snapshot.snapshots) snapshotlist_get_for_cgs.assert_called_once_with( self.context, group_snapshot.id)
def delete_cgsnapshot(self, context, cgsnapshot): """Deletes a consistency group snapshot.""" client = self._login() try: snap_name_base = "snapshot-" + cgsnapshot['id'] # TODO(aorourke): Can't eliminate the DB calls here due to CG API. # Will fix in M release snapshots = objects.SnapshotList().get_all_for_cgsnapshot( context, cgsnapshot['id']) for i, snapshot in enumerate(snapshots): try: snap_name = snap_name_base + "-" + six.text_type(i) snap_info = client.getSnapshotByName(snap_name) client.deleteSnapshot(snap_info['id']) except hpexceptions.HTTPNotFound: LOG.error( _LE("Snapshot did not exist. It will not be " "deleted.")) except hpexceptions.HTTPServerError as ex: in_use_msg = ('cannot be deleted because it is a clone ' 'point') if in_use_msg in ex.get_description(): raise exception.SnapshotIsBusy(snapshot_name=snap_name) raise exception.VolumeBackendAPIException( data=six.text_type(ex)) except Exception as ex: raise exception.VolumeBackendAPIException(data=six.text_type(ex)) finally: self._logout(client) model_update = {'status': cgsnapshot['status']} return model_update, snapshots
def test_obj_load_attr(self, mock_sl_get_all_for_volume, mock_cg_get_by_id, mock_va_get_all_by_vol, mock_vt_get_by_id, mock_admin_metadata_get, mock_glance_metadata_get, mock_metadata_get): volume = objects.Volume._from_db_object(self.context, objects.Volume(), fake_volume.fake_db_volume()) # Test metadata lazy-loaded field metadata = {'foo': 'bar'} mock_metadata_get.return_value = metadata self.assertEqual(metadata, volume.metadata) mock_metadata_get.assert_called_once_with(self.context, volume.id) # Test glance_metadata lazy-loaded field glance_metadata = [{'key': 'foo', 'value': 'bar'}] mock_glance_metadata_get.return_value = glance_metadata self.assertEqual({'foo': 'bar'}, volume.glance_metadata) mock_glance_metadata_get.assert_called_once_with( self.context, volume.id) # Test volume_type lazy-loaded field # Case1. volume.volume_type_id = None self.assertIsNone(volume.volume_type) # Case2. volume2.volume_type_id = 1 fake2 = fake_volume.fake_db_volume() fake2.update({'volume_type_id': fake.volume_id}) volume2 = objects.Volume._from_db_object(self.context, objects.Volume(), fake2) volume_type = objects.VolumeType(context=self.context, id=fake.volume_type_id) mock_vt_get_by_id.return_value = volume_type self.assertEqual(volume_type, volume2.volume_type) mock_vt_get_by_id.assert_called_once_with(self.context, volume2.volume_type_id) # Test consistencygroup lazy-loaded field consistencygroup = objects.ConsistencyGroup( context=self.context, id=fake.consistency_group_id) mock_cg_get_by_id.return_value = consistencygroup self.assertEqual(consistencygroup, volume.consistencygroup) mock_cg_get_by_id.assert_called_once_with(self.context, volume.consistencygroup_id) # Test snapshots lazy-loaded field snapshots = objects.SnapshotList(context=self.context, id=fake.snapshot_id) mock_sl_get_all_for_volume.return_value = snapshots self.assertEqual(snapshots, volume.snapshots) mock_sl_get_all_for_volume.assert_called_once_with( self.context, volume.id) # Test volume_attachment lazy-loaded field va_objs = [ objects.VolumeAttachment(context=self.context, id=i) for i in [fake.object_id, fake.object2_id, fake.object3_id] ] va_list = objects.VolumeAttachmentList(context=self.context, objects=va_objs) mock_va_get_all_by_vol.return_value = va_list self.assertEqual(va_list, volume.volume_attachment) mock_va_get_all_by_vol.assert_called_once_with(self.context, volume.id) # Test admin_metadata lazy-loaded field - user context adm_metadata = {'bar': 'foo'} mock_admin_metadata_get.return_value = adm_metadata self.assertEqual({}, volume.admin_metadata) self.assertFalse(mock_admin_metadata_get.called) # Test admin_metadata lazy-loaded field - admin context adm_context = self.context.elevated() volume = objects.Volume._from_db_object(adm_context, objects.Volume(), fake_volume.fake_db_volume()) adm_metadata = {'bar': 'foo'} mock_admin_metadata_get.return_value = adm_metadata self.assertEqual(adm_metadata, volume.admin_metadata) mock_admin_metadata_get.assert_called_once_with(adm_context, volume.id)
def _from_db_object(cls, context, volume, db_volume, expected_attrs=None): if expected_attrs is None: expected_attrs = [] for name, field in volume.fields.items(): if name in cls.OPTIONAL_FIELDS: continue value = db_volume.get(name) if isinstance(field, fields.IntegerField): value = value or 0 volume[name] = value # Get data from db_volume object that was queried by joined query # from DB if 'metadata' in expected_attrs: metadata = db_volume.get('volume_metadata', []) volume.metadata = {item['key']: item['value'] for item in metadata} if 'admin_metadata' in expected_attrs: metadata = db_volume.get('volume_admin_metadata', []) volume.admin_metadata = {item['key']: item['value'] for item in metadata} if 'glance_metadata' in expected_attrs: metadata = db_volume.get('volume_glance_metadata', []) volume.glance_metadata = {item['key']: item['value'] for item in metadata} if 'volume_type' in expected_attrs: db_volume_type = db_volume.get('volume_type') if db_volume_type: vt_expected_attrs = [] if 'volume_type.extra_specs' in expected_attrs: vt_expected_attrs.append('extra_specs') volume.volume_type = objects.VolumeType._from_db_object( context, objects.VolumeType(), db_volume_type, expected_attrs=vt_expected_attrs) if 'volume_attachment' in expected_attrs: attachments = base.obj_make_list( context, objects.VolumeAttachmentList(context), objects.VolumeAttachment, db_volume.get('volume_attachment')) volume.volume_attachment = attachments if volume.consistencygroup_id and 'consistencygroup' in expected_attrs: consistencygroup = objects.ConsistencyGroup(context) consistencygroup._from_db_object(context, consistencygroup, db_volume['consistencygroup']) volume.consistencygroup = consistencygroup if 'snapshots' in expected_attrs: snapshots = base.obj_make_list( context, objects.SnapshotList(context), objects.Snapshot, db_volume['snapshots']) volume.snapshots = snapshots if 'cluster' in expected_attrs: db_cluster = db_volume.get('cluster') # If this volume doesn't belong to a cluster the cluster field in # the ORM instance will have value of None. if db_cluster: volume.cluster = objects.Cluster(context) objects.Cluster._from_db_object(context, volume.cluster, db_cluster) else: volume.cluster = None if volume.group_id and 'group' in expected_attrs: group = objects.Group(context) group._from_db_object(context, group, db_volume['group']) volume.group = group volume._context = context volume.obj_reset_changes() return volume
def __init__(self, backend_or_vol, pool_name=None, **kwargs): # Accept backend name for convenience if isinstance(backend_or_vol, six.string_types): backend_name = backend_or_vol backend_or_vol = self._get_backend(backend_or_vol) elif isinstance(backend_or_vol, self.backend_class): backend_name = backend_or_vol.id elif isinstance(backend_or_vol, Volume): backend_str, pool = backend_or_vol._ovo.host.split('#') backend_name = backend_str.split('@')[-1] pool_name = pool_name or pool for key in backend_or_vol._ovo.fields: if (backend_or_vol._ovo.obj_attr_is_set(key) and key not in self._ignore_keys): kwargs.setdefault(key, getattr(backend_or_vol._ovo, key)) if backend_or_vol.volume_type: kwargs.setdefault('extra_specs', backend_or_vol.volume_type.extra_specs) if backend_or_vol.volume_type.qos_specs: kwargs.setdefault( 'qos_specs', backend_or_vol.volume_type.qos_specs.specs) backend_or_vol = backend_or_vol.backend if '__ovo' not in kwargs: kwargs[CONNECTIONS_OVO_FIELD] = (cinder_objs.VolumeAttachmentList( context=self.CONTEXT)) kwargs['snapshots'] = (cinder_objs.SnapshotList( context=self.CONTEXT)) self._snapshots = [] self._connections = [] qos_specs = kwargs.pop('qos_specs', None) extra_specs = kwargs.pop('extra_specs', {}) super(Volume, self).__init__(backend_or_vol, **kwargs) self._populate_data() self.local_attach = None # If we overwrote the host, then we ignore pool_name and don't set a # default value or copy the one from the source either. if 'host' not in kwargs and '__ovo' not in kwargs: # TODO(geguileo): Add pool support pool_name = pool_name or backend_or_vol.pool_names[0] self._ovo.host = ('%s@%s#%s' % (cfg.CONF.host, backend_name, pool_name)) if qos_specs or extra_specs: if qos_specs: qos_specs = cinder_objs.QualityOfServiceSpecs( id=self.id, name=self.id, consumer='back-end', specs=qos_specs) qos_specs_id = self.id else: qos_specs = qos_specs_id = None self._ovo.volume_type = cinder_objs.VolumeType( context=self.CONTEXT, is_public=True, id=self.id, name=self.id, qos_specs_id=qos_specs_id, extra_specs=extra_specs, qos_specs=qos_specs) self._ovo.volume_type_id = self.id