def test_work_cleanup(self, get_mock, vol_clean_mock, sch_clean_mock): args = dict(service_id=1, cluster_name='cluster_name', host='host', binary=constants.VOLUME_BINARY, is_up=False, disabled=True, resource_id=fake.VOLUME_ID, resource_type='Volume') cluster = objects.Cluster(id=1, name=args['cluster_name'], binary=constants.VOLUME_BINARY) services = [objects.Service(self.context, id=2, host='hostname', cluster_name=cluster.name, binary=constants.VOLUME_BINARY, cluster=cluster), objects.Service(self.context, id=3, host='hostname', cluster_name=None, binary=constants.SCHEDULER_BINARY), objects.Service(self.context, id=4, host='hostname', cluster_name=None, binary=constants.VOLUME_BINARY)] get_mock.return_value = services cleanup_request = objects.CleanupRequest(self.context, **args) res = self.manager.work_cleanup(self.context, cleanup_request) self.assertEqual((services[:2], services[2:]), res) self.assertEqual(1, vol_clean_mock.call_count) self.assertEqual(1, sch_clean_mock.call_count)
def test_failover_api_success_multiple_results(self, failover_mock): """Succeed to failover multiple services for the same backend.""" rep_field = fields.ReplicationStatus cluster_name = 'mycluster@backend1' cluster = objects.Cluster(self.context, name=cluster_name, replication_status=rep_field.ENABLED, binary=constants.VOLUME_BINARY) cluster.create() services = [ objects.Service(self.context, host='host1@backend1', cluster_name=cluster_name, replication_status=rep_field.ENABLED, binary=constants.VOLUME_BINARY), objects.Service(self.context, host='host2@backend1', cluster_name=cluster_name, replication_status=rep_field.ENABLED, binary=constants.VOLUME_BINARY), ] services[0].create() services[1].create() self.volume_api.failover(self.context, None, cluster_name, mock.sentinel.secondary_id) for service in services + [cluster]: self.assertEqual(rep_field.ENABLED, service.replication_status) service.refresh() self.assertEqual(rep_field.FAILING_OVER, service.replication_status) failover_mock.assert_called_once_with(self.context, mock.ANY, mock.sentinel.secondary_id) self.assertEqual(services[0].id, failover_mock.call_args[0][1].id)
def _from_db_object(context, service, db_service, expected_attrs=None): expected_attrs = expected_attrs or [] for name, field in service.fields.items(): if ((name == 'uuid' and not db_service.get(name)) or name in service.OPTIONAL_FIELDS): continue value = db_service.get(name) if isinstance(field, fields.IntegerField): value = value or 0 elif isinstance(field, fields.DateTimeField): value = value or None service[name] = value service._context = context if 'cluster' in expected_attrs: db_cluster = db_service.get('cluster') # If this service doesn't belong to a cluster the cluster field in # the ORM instance will have value of None. if db_cluster: service.cluster = objects.Cluster(context) objects.Cluster._from_db_object(context, service.cluster, db_cluster) else: service.cluster = None service.obj_reset_changes() return service
def _ensure_cluster_exists(self, context, service): if self.cluster: try: cluster = objects.Cluster.get_by_id(context, None, name=self.cluster, binary=self.binary) # If the cluster already exists, then the service replication # fields must match those of the cluster unless the service # is in error status. error_states = (fields.ReplicationStatus.ERROR, fields.ReplicationStatus.FAILOVER_ERROR) if service.replication_status not in error_states: for attr in ('replication_status', 'active_backend_id', 'frozen'): if getattr(service, attr) != getattr(cluster, attr): setattr(service, attr, getattr(cluster, attr)) except exception.ClusterNotFound: # Since the cluster didn't exist, we copy replication fields # from the service. cluster = objects.Cluster( context=context, name=self.cluster, binary=self.binary, disabled=service.disabled, replication_status=service.replication_status, active_backend_id=service.active_backend_id, frozen=service.frozen) try: cluster.create() # Race condition occurred and another service created the # cluster, so we can continue as it already exists. except exception.ClusterExists: pass
def test_cleanup_destination_volume_cluster_cache_hit(self): cluster = objects.Cluster(id=1, name='mycluster', binary=constants.VOLUME_BINARY) service = objects.Service(id=2, host='hostname', cluster_name=cluster.name, binary=constants.VOLUME_BINARY) cluster_cache = {'cinder-volume': {'mycluster': cluster}} result = self.manager._cleanup_destination(cluster_cache, service) expected = self.manager.volume_api.do_cleanup, cluster, cluster.name self.assertEqual(expected, result)
def tests_obj_make_compatible(self, version): new_fields = {'replication_status': 'error', 'frozen': True, 'active_backend_id': 'replication'} cluster = objects.Cluster(self.context, **new_fields) primitive = cluster.obj_to_primitive(version) converted_cluster = objects.Cluster.obj_from_primitive(primitive) for key, value in new_fields.items(): if version == '1.0': self.assertFalse(converted_cluster.obj_attr_is_set(key)) else: self.assertEqual(value, getattr(converted_cluster, key))
def test_lazy_loading_cluster_field(self, cluster_get): cluster_orm = fake_cluster.fake_cluster_orm(name='mycluster') cluster_get.return_value = cluster_orm cluster = objects.Cluster._from_db_object(self.context, objects.Cluster(), cluster_orm) service = fake_service.fake_service_obj(self.context, cluster_name='mycluster') self.assertEqual(cluster, service.cluster) cluster_get.assert_called_once_with(self.context, None, name='mycluster')
def test_cleanup_destination_volume_cluster_cache_miss(self, get_mock): cluster = objects.Cluster(id=1, name='mycluster', binary=constants.VOLUME_BINARY) service = objects.Service(self.context, id=2, host='hostname', cluster_name=cluster.name, binary=constants.VOLUME_BINARY) get_mock.return_value = cluster cluster_cache = collections.defaultdict(dict) result = self.manager._cleanup_destination(cluster_cache, service) expected = self.manager.volume_api.do_cleanup, cluster, cluster.name self.assertEqual(expected, result)
def test_obj_make_compatible_cluster_added(self, version): extra_data = {'cluster_name': 'cluster_name', 'cluster': objects.Cluster()} volume = objects.Volume(self.context, host='host', **extra_data) serializer = ovo_base.CinderObjectSerializer(version) primitive = serializer.serialize_entity(self.context, volume) converted_volume = objects.Volume.obj_from_primitive(primitive) is_set = version == '1.7' for key in extra_data: self.assertEqual(is_set, converted_volume.obj_attr_is_set(key)) self.assertEqual('host', converted_volume.host)
def test_ensure_cluster_exists_cluster_exists_non_relicated(self, get_mock): cluster = objects.Cluster( name='cluster_name', active_backend_id=None, frozen=False, replication_status=fields.ReplicationStatus.NOT_CAPABLE) get_mock.return_value = cluster app = service.Service.create(host=self.host, binary=self.binary, topic=self.topic) svc = objects.Service.get_by_id(self.ctxt, app.service_id) app.cluster = cluster.name app._ensure_cluster_exists(self.ctxt, svc) get_mock.assert_called_once_with(self.ctxt, None, name=cluster.name, binary=app.binary) self.assertEqual({}, svc.cinder_obj_get_changes())
def test_ensure_cluster_exists_cluster_change(self, get_mock): """We copy replication fields from the cluster to the service.""" changes = dict(replication_status=fields.ReplicationStatus.FAILED_OVER, active_backend_id='secondary', frozen=True) cluster = objects.Cluster(name='cluster_name', **changes) get_mock.return_value = cluster app = service.Service.create(host=self.host, binary=self.binary, topic=self.topic) svc = objects.Service.get_by_id(self.ctxt, app.service_id) app.cluster = cluster.name app._ensure_cluster_exists(self.ctxt, svc) get_mock.assert_called_once_with(self.ctxt, None, name=cluster.name, binary=app.binary) self.assertEqual(changes, svc.cinder_obj_get_changes())
def test_failover_api_fail_multiple_results_not_updated( self, failover_mock): """Fail if none of the services could be updated.""" rep_field = fields.ReplicationStatus cluster_name = 'mycluster@backend1' cluster = objects.Cluster(self.context, name=cluster_name, replication_status=rep_field.ENABLED, binary=constants.VOLUME_BINARY) cluster.create() down_time = timeutils.datetime.datetime(1970, 1, 1) services = [ # This service is down objects.Service(self.context, host='host1@backend1', cluster_name=cluster_name, replication_status=rep_field.ENABLED, created_at=down_time, updated_at=down_time, modified_at=down_time, binary=constants.VOLUME_BINARY), # This service is not with the right replication status objects.Service(self.context, host='host2@backend1', cluster_name=cluster_name, replication_status=rep_field.ERROR, binary=constants.VOLUME_BINARY), ] services[0].create() services[1].create() self.assertRaises(exception.InvalidInput, self.volume_api.failover, self.context, None, cluster_name, mock.sentinel.secondary_id) for service in services: svc = objects.Service.get_by_id(self.context, service.id) self.assertEqual(service.replication_status, svc.replication_status) cluster.refresh() self.assertEqual(rep_field.ENABLED, cluster.replication_status) failover_mock.assert_not_called()
def _ensure_cluster_exists(self, context, disabled=None): if self.cluster: try: objects.Cluster.get_by_id(context, None, name=self.cluster, binary=self.binary) except exception.ClusterNotFound: cluster = objects.Cluster(context=context, name=self.cluster, binary=self.binary) # If disabled has been specified overwrite default value if disabled is not None: cluster.disabled = disabled try: cluster.create() # Race condition occurred and another service created the # cluster, so we can continue as it already exists. except exception.ClusterExists: pass
def _from_db_object(cls, context, consistencygroup, db_consistencygroup, expected_attrs=None): if expected_attrs is None: expected_attrs = [] for name, field in consistencygroup.fields.items(): if name in cls.OPTIONAL_FIELDS: continue value = db_consistencygroup.get(name) setattr(consistencygroup, name, value) if 'cgsnapshots' in expected_attrs: cgsnapshots = base.obj_make_list( context, objects.CGSnapshotList(context), objects.CGSnapshot, db_consistencygroup['cgsnapshots']) consistencygroup.cgsnapshots = cgsnapshots if 'volumes' in expected_attrs: volumes = base.obj_make_list(context, objects.VolumeList(context), objects.Volume, db_consistencygroup['volumes']) consistencygroup.volumes = volumes if 'cluster' in expected_attrs: db_cluster = db_consistencygroup.get('cluster') # If this consistency group doesn't belong to a cluster the cluster # field in the ORM instance will have value of None. if db_cluster: consistencygroup.cluster = objects.Cluster(context) objects.Cluster._from_db_object(context, consistencygroup.cluster, db_cluster) else: consistencygroup.cluster = None consistencygroup._context = context consistencygroup.obj_reset_changes() return consistencygroup
def _from_db_object(context, service, db_service, expected_attrs=None): expected_attrs = expected_attrs or [] for name, field in service.fields.items(): if ((name == 'uuid' and not db_service.get(name)) or name in service.OPTIONAL_FIELDS): continue value = db_service.get(name) if isinstance(field, fields.IntegerField): value = value or 0 elif isinstance(field, fields.DateTimeField): value = value or None service[name] = value service._context = context if 'cluster' in expected_attrs: db_cluster = db_service.get('cluster') # If this service doesn't belong to a cluster the cluster field in # the ORM instance will have value of None. if db_cluster: service.cluster = objects.Cluster(context) objects.Cluster._from_db_object(context, service.cluster, db_cluster) else: service.cluster = None service.obj_reset_changes() # TODO(jdg): Remove in S when we're sure all Services have UUID in db if 'uuid' not in service: service.uuid = uuidutils.generate_uuid() LOG.debug('Generated UUID %(uuid)s for service %(id)i', dict(uuid=service.uuid, id=service.id)) service.save() return service
def fake_cluster_ovo(context, **updates): """Create a fake Cluster versioned object.""" return objects.Cluster._from_db_object(context, objects.Cluster(), fake_cluster_orm(**updates))
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 test_create(self, cluster_create_mock): cluster = objects.Cluster(context=self.context, name='cluster_name') cluster.create() self.assertEqual(self.cluster.id, cluster.id) cluster_create_mock.assert_called_once_with(self.context, {'name': 'cluster_name'})