예제 #1
0
    def test_hook_scope(self):
        kv = Storage(':memory:')
        try:
            with kv.hook_scope('install') as rev:
                self.assertEqual(rev, 1)
                kv.set('a', 1)
                raise RuntimeError('x')
        except RuntimeError:
            self.assertEqual(kv.get('a'), None)

        with kv.hook_scope('config-changed') as rev:
            self.assertEqual(rev, 1)
            kv.set('a', 1)
        self.assertEqual(kv.get('a'), 1)

        kv.revision = None

        with kv.hook_scope('start') as rev:
            self.assertEqual(rev, 2)
            kv.set('a', False)
            kv.set('a', True)
        self.assertEqual(kv.get('a'), True)

        # History doesn't decode values by default
        history = [h[:-1] for h in kv.gethistory('a')]
        self.assertEqual(
            history,
            [(1, 'a', '1', 'config-changed'),
             (2, 'a', 'true', 'start')])

        history = [h[:-1] for h in kv.gethistory('a', deserialize=True)]
        self.assertEqual(
            history,
            [(1, 'a', 1, 'config-changed'),
             (2, 'a', True, 'start')])
예제 #2
0
    def test_init_kv_multiple(self):
        with tempfile.NamedTemporaryFile() as fh:
            kv = Storage(fh.name)
            with kv.hook_scope('xyz'):
                kv.set('x', 1)
            kv.close()

            kv = Storage(fh.name)
            with kv.hook_scope('abc'):
                self.assertEqual(kv.get('x'), 1)
            kv.close()
def remember_devices(devs):
    """Add device to local store of ringed devices."""
    d = os.path.dirname(KV_DB_PATH)
    if not os.path.isdir(d):
        mkdir(d)

    kvstore = KVStore(KV_DB_PATH)
    devstore = devstore_safe_load(kvstore.get(key='devices')) or {}
    env_uuid = os.environ['JUJU_ENV_UUID']
    for dev in devs:
        blk_uuid = get_device_blkid("/dev/%s" % (dev))
        key = "%s@%s" % (dev, env_uuid)
        if key in devstore and devstore[key].get('blkid') == blk_uuid:
            log("Device '%s' already in devstore (status:%s)" %
                (dev, devstore[key].get('status')), level=DEBUG)
        else:
            existing = [(k, v) for k, v in devstore.iteritems()
                        if v.get('blkid') == blk_uuid and
                        re.match("^(.+)@(.+)$", k).group(1) == dev]
            if existing:
                log("Device '%s' already in devstore but has a different "
                    "JUJU_ENV_UUID (%s)" %
                    (dev, re.match(".+@(.+)$", existing[0][0]).group(1)),
                    level=WARNING)
            else:
                log("Adding device '%s' with blkid='%s' to devstore" %
                    (blk_uuid, dev),
                    level=DEBUG)
                devstore[key] = {'blkid': blk_uuid, 'status': 'active'}

    if devstore:
        kvstore.set(key='devices', value=json.dumps(devstore))

    kvstore.flush()
    kvstore.close()
예제 #4
0
    def test_update(self):
        kv = Storage(':memory:')
        kv.update({'v_a': 1, 'v_b': 2.2})
        self.assertEqual(kv.getrange('v_'), {'v_a': 1, 'v_b': 2.2})

        kv.update({'a': False, 'b': True}, prefix='x_')
        self.assertEqual(kv.getrange('x_', True), {'a': False, 'b': True})
예제 #5
0
 def test_delta(self):
     kv = Storage(':memory:')
     kv.update({'a': 1, 'b': 2.2}, prefix="x")
     delta = kv.delta({'a': 0, 'c': False}, prefix='x')
     self.assertEqual(
         delta,
         {'a': (1, 0), 'b': (2.2, None), 'c': (None, False)})
     self.assertEqual(delta.a.previous, 1)
     self.assertEqual(delta.a.current, 0)
     self.assertEqual(delta.c.previous, None)
     self.assertEqual(delta.a.current, False)
예제 #6
0
 def test_get_set_unset(self):
     kv = Storage(':memory:')
     kv.hook_scope('test')
     kv.set('hello', 'saucy')
     kv.set('hello', 'world')
     self.assertEqual(kv.get('hello'), 'world')
     kv.flush()
     kv.unset('hello')
     self.assertEqual(kv.get('hello'), None)
     kv.flush(False)
     self.assertEqual(kv.get('hello'), 'world')
예제 #7
0
 def test_keyrange(self):
     kv = Storage(':memory:')
     kv.set('docker.net_mtu', 1)
     kv.set('docker.net_nack', True)
     kv.set('docker.net_type', 'vxlan')
     self.assertEqual(
         kv.getrange('docker'),
         {'docker.net_mtu': 1, 'docker.net_type': 'vxlan',
          'docker.net_nack': True})
     self.assertEqual(
         kv.getrange('docker.', True),
         {'net_mtu': 1, 'net_type': 'vxlan', 'net_nack': True})
예제 #8
0
    def test_unset(self):
        kv = Storage(':memory:')
        with kv.hook_scope('install'):
            kv.set('a', True)
        with kv.hook_scope('start'):
            kv.set('a', False)
        with kv.hook_scope('config-changed'):
            kv.unset('a')
        history = [h[:-1] for h in kv.gethistory('a')]

        self.assertEqual(history, [(1, 'a', 'true', 'install'),
                                   (2, 'a', 'false', 'start'),
                                   (3, 'a', '"DELETED"', "config-changed")])
예제 #9
0
 def test_record(self):
     kv = Storage(':memory:')
     kv.set('config', {'x': 1, 'b': False})
     config = kv.get('config', record=True)
     self.assertEqual(config.b, False)
     self.assertEqual(config.x, 1)
     self.assertEqual(kv.set('config.x', 1), 1)
     try:
         config.z
     except AttributeError:
         pass
     else:
         self.fail('attribute error should fire on nonexistant')
예제 #10
0
    def test_delta_no_previous_and_history(self):
        kv = Storage(':memory:')
        with kv.hook_scope('install'):
            data = {'a': 0, 'c': False}
            delta = kv.delta(data, 'settings.')
            self.assertEqual(delta, {'a': (None, False), 'c': (None, False)})
            kv.update(data, 'settings.')

        with kv.hook_scope('config'):
            data = {'a': 1, 'c': True}
            delta = kv.delta(data, 'settings.')
            self.assertEqual(delta, {'a': (0, 1), 'c': (False, True)})
            kv.update(data, 'settings.')
        # strip the time
        history = [h[:-1] for h in kv.gethistory('settings.a')]
        self.assertEqual(history, [(1, 'settings.a', '0', 'install'),
                                   (2, 'settings.a', '1', 'config')])
예제 #11
0
def remember_devices(devs):
    """Add device to local store of ringed devices."""
    d = os.path.dirname(KV_DB_PATH)
    if not os.path.isdir(d):
        mkdir(d)

    kvstore = KVStore(KV_DB_PATH)
    devstore = devstore_safe_load(kvstore.get(key='devices')) or {}
    env_uuid = os.environ.get('JUJU_ENV_UUID',
                              os.environ.get('JUJU_MODEL_UUID'))
    for dev in devs:
        blk_uuid = get_device_blkid("/dev/%s" % (dev))
        key = "%s@%s" % (dev, env_uuid)
        if key in devstore and devstore[key].get('blkid') == blk_uuid:
            log("Device '%s' already in devstore (status:%s)" %
                (dev, devstore[key].get('status')),
                level=DEBUG)
        else:
            existing = [(k, v) for k, v in devstore.iteritems()
                        if v.get('blkid') == blk_uuid
                        and re.match("^(.+)@(.+)$", k).group(1) == dev]
            if existing:
                log("Device '%s' already in devstore but has a different "
                    "JUJU_[ENV|MODEL]_UUID (%s)" %
                    (dev, re.match(".+@(.+)$", existing[0][0]).group(1)),
                    level=WARNING)
            else:
                log("Adding device '%s' with blkid='%s' to devstore" %
                    (dev, blk_uuid),
                    level=DEBUG)
                devstore[key] = {'blkid': blk_uuid, 'status': 'active'}

    if devstore:
        kvstore.set(key='devices', value=json.dumps(devstore))

    kvstore.flush()
    kvstore.close()
예제 #12
0
def is_device_in_ring(dev, skip_rel_check=False, ignore_deactivated=True):
    """Check if device has been added to the ring.

    First check local KV store then check storage rel with proxy.
    """
    d = os.path.dirname(KV_DB_PATH)
    if not os.path.isdir(d):
        mkdir(d)
        log("Device '%s' does not appear to be in use by Swift" % (dev),
            level=INFO)
        return False

    # First check local KV store
    kvstore = KVStore(KV_DB_PATH)
    devstore = devstore_safe_load(kvstore.get(key='devices'))
    kvstore.close()
    deactivated = []
    if devstore:
        blk_uuid = get_device_blkid("/dev/%s" % (dev))
        env_uuid = os.environ.get('JUJU_ENV_UUID',
                                  os.environ.get('JUJU_MODEL_UUID'))
        masterkey = "%s@%s" % (dev, env_uuid)
        if (masterkey in devstore
                and devstore[masterkey].get('blkid') == blk_uuid
                and devstore[masterkey].get('status') == 'active'):
            log("Device '%s' appears to be in use by Swift (found in local "
                "devstore)" % (dev),
                level=INFO)
            return True

        for key, val in devstore.iteritems():
            if key != masterkey and val.get('blkid') == blk_uuid:
                log("Device '%s' appears to be in use by Swift (found in "
                    "local devstore) but has a different "
                    "JUJU_[ENV|MODEL]_UUID (current=%s, expected=%s). "
                    "This could indicate that the device was added as part of "
                    "a previous deployment and will require manual removal or "
                    "updating if it needs to be reformatted." %
                    (dev, key, masterkey),
                    level=INFO)
                return True

        if ignore_deactivated:
            deactivated = [
                k == masterkey and v.get('blkid') == blk_uuid
                and v.get('status') != 'active'
                for k, v in devstore.iteritems()
            ]

    if skip_rel_check:
        log("Device '%s' does not appear to be in use by swift (searched "
            "local devstore only)" % (dev),
            level=INFO)
        return False

    # Then check swift-storage relation with proxy
    for rid in relation_ids('swift-storage'):
        devstore = relation_get(attribute='device', rid=rid, unit=local_unit())
        if devstore and dev in devstore.split(':'):
            if not ignore_deactivated or dev not in deactivated:
                log("Device '%s' appears to be in use by swift (found on "
                    "proxy relation) but was not found in local devstore so "
                    "will be added to the cache" % (dev),
                    level=INFO)
                remember_devices([dev])
                return True

    log("Device '%s' does not appear to be in use by swift (searched local "
        "devstore and proxy relation)" % (dev),
        level=INFO)
    return False
    def test_configure_amqp(self, mock_config,
                            mock_grant_permissions, mock_create_vhost,
                            mock_create_user, mock_get_rabbit_password,
                            mock_set_ha_mode, mock_is_leader,
                            mock_configure_notification_ttl,
                            mock_configure_ttl):
        config_data = {
            'notification-ttl': 450000,
            'mirroring-queues': True,
        }
        mock_is_leader.return_value = True
        mock_config.side_effect = lambda attribute: config_data.get(attribute)
        tmpdir = tempfile.mkdtemp()
        try:
            db_path = '{}/kv.db'.format(tmpdir)
            rid = 'amqp:1'
            store = Storage(db_path)
            with patch('charmhelpers.core.unitdata._KV', store):
                # Check .set
                with patch.object(store, 'set') as mock_set:
                    rabbitmq_server_relations.configure_amqp('user_foo',
                                                             'vhost_blah', rid)

                    d = {rid: {"username": "******", "vhost": "vhost_blah",
                               "ttl": None, "mirroring-queues": True}}
                    mock_set.assert_has_calls([call(key='amqp_config_tracker',
                                                    value=d)])

                    for m in [mock_grant_permissions, mock_create_vhost,
                              mock_create_user, mock_set_ha_mode]:
                        self.assertTrue(m.called)
                        m.reset_mock()

                # Check .get
                with patch.object(store, 'get') as mock_get:
                    mock_get.return_value = d
                    rabbitmq_server_relations.configure_amqp('user_foo',
                                                             'vhost_blah', rid)
                    mock_set.assert_has_calls([call(key='amqp_config_tracker',
                                                    value=d)])
                    for m in [mock_grant_permissions, mock_create_vhost,
                              mock_create_user, mock_set_ha_mode]:
                        self.assertFalse(m.called)

                # Check invalid relation id
                self.assertRaises(Exception,
                                  rabbitmq_server_relations.configure_amqp,
                                  'user_foo', 'vhost_blah', None, admin=True)

                # Test writing data
                d = {}
                for rid, user in [('amqp:1', 'userA'), ('amqp:2', 'userB')]:
                    rabbitmq_server_relations.configure_amqp(user,
                                                             'vhost_blah', rid)

                    d.update({rid: {"username": user, "vhost": "vhost_blah",
                                    "ttl": None, "mirroring-queues": True}})
                    self.assertEqual(store.get('amqp_config_tracker'), d)

                @rabbitmq_server_relations.validate_amqp_config_tracker
                def fake_configure_amqp(*args, **kwargs):
                    return rabbitmq_server_relations.configure_amqp(*args,
                                                                    **kwargs)

                # Test invalidating data
                mock_is_leader.return_value = False
                d['amqp:2']['stale'] = True
                for rid, user in [('amqp:1', 'userA'), ('amqp:3', 'userC')]:
                    fake_configure_amqp(user, 'vhost_blah', rid)
                    d[rid] = {"username": user, "vhost": "vhost_blah",
                              "ttl": None,
                              "mirroring-queues": True, 'stale': True}
                    # Since this is a dummy case we need to toggle the stale
                    # values.
                    del d[rid]['stale']
                    self.assertEqual(store.get('amqp_config_tracker'), d)
                    d[rid]['stale'] = True

                mock_configure_notification_ttl.assert_not_called()
                mock_configure_ttl.assert_not_called()

                # Test openstack notification workaround
                d = {}
                for rid, user in [('amqp:1', 'userA')]:
                    rabbitmq_server_relations.configure_amqp(
                        user, 'openstack', rid, admin=False,
                        ttlname='heat_expiry',
                        ttlreg='heat-engine-listener|engine_worker', ttl=45000)
                (mock_configure_notification_ttl.
                    assert_called_once_with('openstack', 450000))
                (mock_configure_ttl.
                    assert_called_once_with(
                        'openstack', 'heat_expiry',
                        'heat-engine-listener|engine_worker', 45000))

        finally:
            if os.path.exists(tmpdir):
                shutil.rmtree(tmpdir)
def is_device_in_ring(dev, skip_rel_check=False, ignore_deactivated=True):
    """Check if device has been added to the ring.

    First check local KV store then check storage rel with proxy.
    """
    d = os.path.dirname(KV_DB_PATH)
    if not os.path.isdir(d):
        mkdir(d)
        log("Device '%s' does not appear to be in use by Swift" % (dev),
            level=INFO)
        return False

    # First check local KV store
    kvstore = KVStore(KV_DB_PATH)
    devstore = devstore_safe_load(kvstore.get(key='devices'))
    kvstore.close()
    deactivated = []
    if devstore:
        blk_uuid = get_device_blkid("/dev/%s" % (dev))
        env_uuid = os.environ['JUJU_ENV_UUID']
        masterkey = "%s@%s" % (dev, env_uuid)
        if (masterkey in devstore and
                devstore[masterkey].get('blkid') == blk_uuid and
                devstore[masterkey].get('status') == 'active'):
            log("Device '%s' appears to be in use by Swift (found in local "
                "devstore)" % (dev), level=INFO)
            return True

        for key, val in devstore.iteritems():
            if key != masterkey and val.get('blkid') == blk_uuid:
                log("Device '%s' appears to be in use by Swift (found in "
                    "local devstore) but has a different JUJU_ENV_UUID "
                    "(current=%s, expected=%s). "
                    "This could indicate that the device was added as part of "
                    "a previous deployment and will require manual removal or "
                    "updating if it needs to be reformatted."
                    % (dev, key, masterkey), level=INFO)
                return True

        if ignore_deactivated:
            deactivated = [k == masterkey and v.get('blkid') == blk_uuid and
                           v.get('status') != 'active'
                           for k, v in devstore.iteritems()]

    if skip_rel_check:
        log("Device '%s' does not appear to be in use by swift (searched "
            "local devstore only)" % (dev), level=INFO)
        return False

    # Then check swift-storage relation with proxy
    for rid in relation_ids('swift-storage'):
        devstore = relation_get(attribute='device', rid=rid, unit=local_unit())
        if devstore and dev in devstore.split(':'):
            if not ignore_deactivated or dev not in deactivated:
                log("Device '%s' appears to be in use by swift (found on "
                    "proxy relation) but was not found in local devstore so "
                    "will be added to the cache" % (dev), level=INFO)
                remember_devices([dev])
                return True

    log("Device '%s' does not appear to be in use by swift (searched local "
        "devstore and proxy relation)" % (dev), level=INFO)
    return False
예제 #15
0
 def test_flush_and_close_on_closed(self):
     kv = Storage(':memory:')
     kv.close()
     kv.flush(False)
     kv.close()
예제 #16
0
 def test_debug(self):
     # pure coverage test...
     io = StringIO()
     kv = Storage(':memory:')
     kv.debug(io)
예제 #17
0
 def test_multi_value_set_skips(self):
     # pure coverage test
     kv = Storage(':memory:')
     kv.set('x', 1)
     self.assertEqual(kv.set('x', 1), 1)
예제 #18
0
    def test_configure_amqp(self, mock_grant_permissions, mock_create_vhost,
                            mock_create_user, mock_get_rabbit_password,
                            mock_set_ha_mode, mock_is_leader):
        mock_is_leader.return_value = True
        tmpdir = tempfile.mkdtemp()
        try:
            db_path = '{}/kv.db'.format(tmpdir)
            rid = 'amqp:1'
            store = Storage(db_path)
            with patch('charmhelpers.core.unitdata._KV', store):
                # Check .set
                with patch.object(store, 'set') as mock_set:
                    rabbitmq_server_relations.configure_amqp(
                        'user_foo', 'vhost_blah', rid)

                    d = {
                        rid: {
                            "username": "******",
                            "vhost": "vhost_blah",
                            "mirroring-queues": True
                        }
                    }
                    mock_set.assert_has_calls(
                        [call(key='amqp_config_tracker', value=d)])

                    for m in [
                            mock_grant_permissions, mock_create_vhost,
                            mock_create_user, mock_set_ha_mode
                    ]:
                        self.assertTrue(m.called)
                        m.reset_mock()

                # Check .get
                with patch.object(store, 'get') as mock_get:
                    mock_get.return_value = d
                    rabbitmq_server_relations.configure_amqp(
                        'user_foo', 'vhost_blah', rid)
                    mock_set.assert_has_calls(
                        [call(key='amqp_config_tracker', value=d)])
                    for m in [
                            mock_grant_permissions, mock_create_vhost,
                            mock_create_user, mock_set_ha_mode
                    ]:
                        self.assertFalse(m.called)

                # Check invalid relation id
                self.assertRaises(Exception,
                                  rabbitmq_server_relations.configure_amqp,
                                  'user_foo',
                                  'vhost_blah',
                                  None,
                                  admin=True)

                # Test writing data
                d = {}
                for rid, user in [('amqp:1', 'userA'), ('amqp:2', 'userB')]:
                    rabbitmq_server_relations.configure_amqp(
                        user, 'vhost_blah', rid)

                    d.update({
                        rid: {
                            "username": user,
                            "vhost": "vhost_blah",
                            "mirroring-queues": True
                        }
                    })
                    self.assertEqual(store.get('amqp_config_tracker'), d)

                @rabbitmq_server_relations.validate_amqp_config_tracker
                def fake_configure_amqp(*args, **kwargs):
                    return rabbitmq_server_relations.configure_amqp(
                        *args, **kwargs)

                # Test invalidating data
                mock_is_leader.return_value = False
                d['amqp:2']['stale'] = True
                for rid, user in [('amqp:1', 'userA'), ('amqp:3', 'userC')]:
                    fake_configure_amqp(user, 'vhost_blah', rid)
                    d[rid] = {
                        "username": user,
                        "vhost": "vhost_blah",
                        "mirroring-queues": True,
                        'stale': True
                    }
                    # Since this is a dummy case we need to toggle the stale
                    # values.
                    del d[rid]['stale']
                    self.assertEqual(store.get('amqp_config_tracker'), d)
                    d[rid]['stale'] = True
        finally:
            if os.path.exists(tmpdir):
                shutil.rmtree(tmpdir)
    def test_configure_amqp(self, mock_config,
                            mock_grant_permissions, mock_create_vhost,
                            mock_create_user, mock_get_rabbit_password,
                            mock_set_ha_mode, mock_is_leader,
                            mock_configure_notification_ttl):
        config_data = {
            'notification-ttl': 450000,
            'mirroring-queues': True,
        }
        mock_is_leader.return_value = True
        mock_config.side_effect = lambda attribute: config_data.get(attribute)
        tmpdir = tempfile.mkdtemp()
        try:
            db_path = '{}/kv.db'.format(tmpdir)
            rid = 'amqp:1'
            store = Storage(db_path)
            with patch('charmhelpers.core.unitdata._KV', store):
                # Check .set
                with patch.object(store, 'set') as mock_set:
                    rabbitmq_server_relations.configure_amqp('user_foo',
                                                             'vhost_blah', rid)

                    d = {rid: {"username": "******", "vhost": "vhost_blah",
                               "mirroring-queues": True}}
                    mock_set.assert_has_calls([call(key='amqp_config_tracker',
                                                    value=d)])

                    for m in [mock_grant_permissions, mock_create_vhost,
                              mock_create_user, mock_set_ha_mode]:
                        self.assertTrue(m.called)
                        m.reset_mock()

                # Check .get
                with patch.object(store, 'get') as mock_get:
                    mock_get.return_value = d
                    rabbitmq_server_relations.configure_amqp('user_foo',
                                                             'vhost_blah', rid)
                    mock_set.assert_has_calls([call(key='amqp_config_tracker',
                                                    value=d)])
                    for m in [mock_grant_permissions, mock_create_vhost,
                              mock_create_user, mock_set_ha_mode]:
                        self.assertFalse(m.called)

                # Check invalid relation id
                self.assertRaises(Exception,
                                  rabbitmq_server_relations.configure_amqp,
                                  'user_foo', 'vhost_blah', None, admin=True)

                # Test writing data
                d = {}
                for rid, user in [('amqp:1', 'userA'), ('amqp:2', 'userB')]:
                    rabbitmq_server_relations.configure_amqp(user,
                                                             'vhost_blah', rid)

                    d.update({rid: {"username": user, "vhost": "vhost_blah",
                                    "mirroring-queues": True}})
                    self.assertEqual(store.get('amqp_config_tracker'), d)

                @rabbitmq_server_relations.validate_amqp_config_tracker
                def fake_configure_amqp(*args, **kwargs):
                    return rabbitmq_server_relations.configure_amqp(*args,
                                                                    **kwargs)

                # Test invalidating data
                mock_is_leader.return_value = False
                d['amqp:2']['stale'] = True
                for rid, user in [('amqp:1', 'userA'), ('amqp:3', 'userC')]:
                    fake_configure_amqp(user, 'vhost_blah', rid)
                    d[rid] = {"username": user, "vhost": "vhost_blah",
                              "mirroring-queues": True, 'stale': True}
                    # Since this is a dummy case we need to toggle the stale
                    # values.
                    del d[rid]['stale']
                    self.assertEqual(store.get('amqp_config_tracker'), d)
                    d[rid]['stale'] = True

                mock_configure_notification_ttl.assert_not_called()

                # Test openstack notification workaround
                d = {}
                for rid, user in [('amqp:1', 'userA')]:
                    rabbitmq_server_relations.configure_amqp(user,
                                                             'openstack', rid)
                (mock_configure_notification_ttl.
                    assert_called_once_with('openstack', 450000))

        finally:
            if os.path.exists(tmpdir):
                shutil.rmtree(tmpdir)