예제 #1
0
 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')
예제 #2
0
    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)
예제 #3
0
 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)
예제 #4
0
 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)
예제 #5
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)
예제 #6
0
 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)
예제 #7
0
 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')
예제 #8
0
 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')
예제 #9
0
    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)
예제 #10
0
    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)