def test_re_raise(self): try: with undo_transaction(UndoFrameworkTest.Foo(), ValueError, re_raise_exception=True): raise ValueError() except ValueError: return self.fail('no exception')
def save(self, *args, **kwargs): """ This method implements three purposes. 1. Implements the functionality originally done by django (e.g. setting id on self) 2. Modify the Ceph state-machine in a sane way. 3. Providing a RESTful API. """ api = self.mon_api(self.cluster.fsid) if self.id is None: raise ValidationError('Creating OSDs is not supported.') with undo_transaction(api, re_raise_exception=True) as api: diff, original = self.get_modified_fields() for key, value in diff.items(): if key == 'in_state': if value: api.osd_in(self.name) else: api.osd_out(self.name) elif key == 'reweight': api.osd_crush_reweight(self.name, value, original.reweight) else: logger.warning( 'Tried to set "{}" to "{}" on osd "{}", which is not ' 'supported'.format(key, value, self.name)) super(CephOsd, self).save(*args, **kwargs)
def test_excption_in_func(self): foo = UndoFrameworkTest.Foo() foo.add(100) with undo_transaction(foo, ZeroDivisionError): foo.add(4) foo.div() self.fail('div by 0') self.assertEqual(foo.val, 100)
def test_undoable_undo(self): with undo_transaction(UndoFrameworkTest.Foo(), NotImplementedError) as foo: self.assertEqual(foo.val, 0) foo.add(4) self.assertEqual(foo.val, 4) self.assertEqual(foo.minus(1), 3) self.assertEqual(foo.val, 3) raise NotImplementedError() self.assertEqual(foo.val, 0)
def test_success(self): foo = UndoFrameworkTest.Foo() foo.add(100) self.assertEqual(foo.val, 100) with undo_transaction(foo, NotImplementedError): self.assertEqual(foo.val, 100) self.assertEqual(foo.add(4), 104) self.assertEqual(foo.val, 104) foo.add(2) self.assertEqual(foo.val, 106) self.assertEqual(foo.val, 106)
def test_exception(self): foo = UndoFrameworkTest.Foo() foo.add(100) with undo_transaction(foo, NotImplementedError): self.assertEqual(foo.val, 100) foo.add(4) self.assertEqual(foo.val, 104) foo.add(2) self.assertEqual(foo.val, 106) raise NotImplementedError() self.assertEqual(foo.val, 100)
def test_unknown_exception(self): foo = UndoFrameworkTest.Foo() try: with undo_transaction(foo, NotImplementedError): self.assertEqual(foo.val, 0) foo.add(4) self.assertEqual(foo.val, 4) raise ValueError() except ValueError: self.assertEqual(foo.val, 4) return self.fail('no exception')
def test_broken_undo(self): foo = UndoFrameworkTest.Foo() try: with undo_transaction(foo, NotImplementedError): foo.add(4) self.assertEqual(foo.val, 4) foo.multi(0) self.assertEqual(foo.val, 0) raise NotImplementedError() except NotImplementedError: self.fail('wrong type') except ZeroDivisionError: return self.fail('no exception')
def save(self, *args, **kwargs): """ This method implements three purposes. 1. Implements the functionality originally done by django (e.g. setting id on self) 2. Modify the Ceph state-machine in a sane way. 3. Providing a RESTful API. """ if self.cluster is None: self.cluster = CephPool.objects.nodb_context.cluster insert = getattr(self, 'id', None) is None with undo_transaction(self.mon_api(), exception_type=(ExternalCommandError, NotSupportedError), re_raise_exception=True) as api: if insert: api.osd_pool_create( self.name, self.pg_num, self.pg_num, # second pg_num is in fact pgp_num, but we don't want to allow # different values here. self.type, self.erasure_code_profile.name if (self.erasure_code_profile and self.type == 'erasure') else None) diff, original = (self.get_modified_fields( name=self.name) if insert else self.get_modified_fields()) self.set_read_only_fields(original) if insert: for attr, value in diff.items(): if not hasattr(self, attr): setattr(self, attr, value) self._task_queue = ceph.tasks.track_pg_creation.delay( self.cluster.fsid, self.id, 0, self.pg_num) if not insert and 'cluster' in diff: raise ValueError({'cluster': ["Cluster cannot be changed."]}) def schwartzian_transform(obj): key, val = obj if key == 'tier_of_id': return (1 if val is None else -1), obj # move to start or end. return 0, obj for key, value in sorted(diff.items(), key=schwartzian_transform): if key == 'pg_num': if not insert: api.osd_pool_set(self.name, "pg_num", value, undo_previous_value=original.pg_num) api.osd_pool_set(self.name, "pgp_num", value, undo_previous_value=original.pg_num) elif key == 'cache_mode': api.osd_tier_cache_mode( self.name, value, undo_previous_mode=original.cache_mode) elif key == 'tier_of_id': if self.tier_of is None: tier_of_target = original.tier_of api.osd_tier_remove(tier_of_target.name, self.name) else: tier_of_target = self.tier_of api.osd_tier_add(tier_of_target.name, self.name) elif key == 'read_tier_id': if self.read_tier is None: read_tier_target = original.read_tier api.osd_tier_remove_overlay( self.name, undo_previous_overlay=read_tier_target.name) else: read_tier_target = self.read_tier api.osd_tier_set_overlay(self.name, read_tier_target.name) elif key == 'flags': for flag in value: if flag == 'allow_ec_overwrites' or flag == 'ec_overwrites': api.osd_pool_set(self.name, 'allow_ec_overwrites', 'true') else: msg = 'Unknown flag \'{}\'.'.format(flag) logger.warning(msg) raise NotSupportedError(msg) elif key == 'compression_required_ratio': api.osd_pool_set(self.name, key, str(value), undo_previous_value=str( getattr(original, key))) elif key == 'application_metadata': for app in set(original.application_metadata) - set(value): api.osd_pool_application_disable(self.name, app) for app in set(value) - set(original.application_metadata): api.osd_pool_application_enable(self.name, app) elif key == 'crush_ruleset': logger.info('Setting crush_ruleset` is not yet supported.') elif self.type == 'replicated' and key not in \ ['name', 'erasure_code_profile_id'] and value is not None: api.osd_pool_set(self.name, key, value, undo_previous_value=getattr( original, key)) elif self.type == 'erasure' and key not in ['name', 'size', 'min_size'] \ and value is not None: api.osd_pool_set(self.name, key, value, undo_previous_value=getattr( original, key)) else: logger.warning( 'Tried to set "{}" to "{}" on pool "{}" aka "{}", which is not ' 'supported'.format(key, value, self.id, self.name)) super(CephPool, self).save(*args, **kwargs)
def save(self, *args, **kwargs): """ This method implements three purposes. 1. Implements the functionality originally done by django (e.g. setting id on self) 2. Modify the Ceph state-machine in a sane way. 3. Providing a RESTful API. """ insert = self._state.adding # there seems to be no id field. if not hasattr(self, 'features') or not isinstance( self.features, list): self.features = [] api = self.rbd_api() with undo_transaction(api, re_raise_exception=True, exception_type=CephRbd.DoesNotExist): if insert: order = None data_pool_name = self.data_pool.name if self.data_pool else None if self.obj_size is not None and self.obj_size > 0: order = int(round(math.log(float(self.obj_size), 2))) api.create(self.pool.name, self.name, self.size, features=self.features, old_format=self.old_format, order=order, stripe_unit=self.stripe_unit, stripe_count=self.stripe_count, data_pool_name=data_pool_name) self.id = CephRbd.make_key(self.pool, self.name) diff, original = self.get_modified_fields() self.set_read_only_fields(original) if insert: self.features = original.features for key, value in diff.items(): if key == 'size': assert not insert api.image_resize(self.pool.name, self.name, value) elif key == 'features': def feature_index(feature): features_ordered = [ 'deep-flatten', 'layering', 'stripingv2', 'exclusive-lock', 'object-map', 'journaling', 'fast-diff', 'data-pool' ] if feature not in features_ordered: raise ValidationError( 'Unexpected RBD feature: {}.'.format(feature)) return features_ordered.index(feature) if not insert: features_to_disable = set( original.features).difference(set(value)) for feature in sorted(features_to_disable, key=feature_index, reverse=True): api.image_set_feature(self.pool.name, self.name, feature, False) features_to_enable = set(value).difference( set(original.features)) for feature in sorted(features_to_enable, key=feature_index, reverse=False): api.image_set_feature(self.pool.name, self.name, feature, True) else: logger.warning( 'Tried to set features, but they should already match. {} ' '!= {}'.format(original.features, value)) else: logger.warning( 'Tried to set "{}" to "{}" on rbd "{}", which is not ' 'supported'.format(key, value, self.name)) super(CephRbd, self).save(*args, **kwargs)