Merge "Add hardware types to the hash ring"
This commit is contained in:
commit
bd29a4d39d
@ -50,6 +50,7 @@ class HashRingManager(object):
|
||||
def _load_hash_rings(self):
|
||||
rings = {}
|
||||
d2c = self.dbapi.get_active_driver_dict()
|
||||
d2c.update(self.dbapi.get_active_hardware_type_dict())
|
||||
|
||||
for driver_name, hosts in d2c.items():
|
||||
rings[driver_name] = hashring.HashRing(
|
||||
|
@ -530,6 +530,19 @@ class Connection(object):
|
||||
driverB: set([host2, host3])}
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_active_hardware_type_dict(self):
|
||||
"""Retrieve hardware types for the registered and active conductors.
|
||||
|
||||
:returns: A dict which maps hardware type names to the set of hosts
|
||||
which support them. For example:
|
||||
|
||||
::
|
||||
|
||||
{hardware-type-a: set([host1, host2]),
|
||||
hardware-type-b: set([host2, host3])}
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_offline_conductors(self):
|
||||
"""Get a list conductor hostnames that are offline (dead).
|
||||
|
@ -177,6 +177,16 @@ def _paginate_query(model, limit=None, marker=None, sort_key=None,
|
||||
return query.all()
|
||||
|
||||
|
||||
def _filter_active_conductors(query, interval=None):
|
||||
if interval is None:
|
||||
interval = CONF.conductor.heartbeat_timeout
|
||||
limit = timeutils.utcnow() - datetime.timedelta(seconds=interval)
|
||||
|
||||
query = (query.filter(models.Conductor.online.is_(True))
|
||||
.filter(models.Conductor.updated_at >= limit))
|
||||
return query
|
||||
|
||||
|
||||
class Connection(api.Connection):
|
||||
"""SqlAlchemy connection."""
|
||||
|
||||
@ -782,11 +792,8 @@ class Connection(api.Connection):
|
||||
if interval is None:
|
||||
interval = CONF.conductor.heartbeat_timeout
|
||||
|
||||
limit = timeutils.utcnow() - datetime.timedelta(seconds=interval)
|
||||
result = (model_query(models.Conductor)
|
||||
.filter_by(online=True)
|
||||
.filter(models.Conductor.updated_at >= limit)
|
||||
.all())
|
||||
query = model_query(models.Conductor)
|
||||
result = _filter_active_conductors(query, interval=interval)
|
||||
|
||||
# build mapping of drivers to the set of hosts which support them
|
||||
d2c = collections.defaultdict(set)
|
||||
@ -795,6 +802,17 @@ class Connection(api.Connection):
|
||||
d2c[driver].add(row['hostname'])
|
||||
return d2c
|
||||
|
||||
def get_active_hardware_type_dict(self):
|
||||
query = (model_query(models.ConductorHardwareInterfaces,
|
||||
models.Conductor)
|
||||
.join(models.Conductor))
|
||||
result = _filter_active_conductors(query)
|
||||
|
||||
d2c = collections.defaultdict(set)
|
||||
for iface_row, cdr_row in result:
|
||||
d2c[iface_row['hardware_type']].add(cdr_row['hostname'])
|
||||
return d2c
|
||||
|
||||
def get_offline_conductors(self):
|
||||
interval = CONF.conductor.heartbeat_timeout
|
||||
limit = timeutils.utcnow() - datetime.timedelta(seconds=interval)
|
||||
|
@ -31,20 +31,28 @@ class HashRingManagerTestCase(db_base.DbTestCase):
|
||||
self.ring_manager = hash_ring.HashRingManager()
|
||||
|
||||
def register_conductors(self):
|
||||
self.dbapi.register_conductor({
|
||||
c1 = self.dbapi.register_conductor({
|
||||
'hostname': 'host1',
|
||||
'drivers': ['driver1', 'driver2'],
|
||||
})
|
||||
self.dbapi.register_conductor({
|
||||
c2 = self.dbapi.register_conductor({
|
||||
'hostname': 'host2',
|
||||
'drivers': ['driver1'],
|
||||
})
|
||||
for c in (c1, c2):
|
||||
self.dbapi.register_conductor_hardware_interfaces(
|
||||
c.id, 'hardware-type', 'deploy', ['iscsi', 'direct'], 'iscsi')
|
||||
|
||||
def test_hash_ring_manager_get_ring_success(self):
|
||||
self.register_conductors()
|
||||
ring = self.ring_manager['driver1']
|
||||
self.assertEqual(sorted(['host1', 'host2']), sorted(ring.nodes))
|
||||
|
||||
def test_hash_ring_manager_hardware_type_success(self):
|
||||
self.register_conductors()
|
||||
ring = self.ring_manager['hardware-type']
|
||||
self.assertEqual(sorted(['host1', 'host2']), sorted(ring.nodes))
|
||||
|
||||
def test_hash_ring_manager_driver_not_found(self):
|
||||
self.register_conductors()
|
||||
self.assertRaises(exception.DriverNotFound,
|
||||
|
@ -40,9 +40,16 @@ class DbConductorTestCase(base.DbTestCase):
|
||||
self.dbapi.register_conductor(c)
|
||||
self.dbapi.register_conductor(c, update_existing=True)
|
||||
|
||||
def _create_test_cdr(self, **kwargs):
|
||||
def _create_test_cdr(self, hardware_types=None, **kwargs):
|
||||
hardware_types = hardware_types or []
|
||||
c = utils.get_test_conductor(**kwargs)
|
||||
return self.dbapi.register_conductor(c)
|
||||
cdr = self.dbapi.register_conductor(c)
|
||||
for ht in hardware_types:
|
||||
self.dbapi.register_conductor_hardware_interfaces(cdr.id, ht,
|
||||
'power',
|
||||
['ipmi', 'fake'],
|
||||
'ipmi')
|
||||
return cdr
|
||||
|
||||
def test_register_conductor_hardware_interfaces(self):
|
||||
c = self._create_test_cdr()
|
||||
@ -187,7 +194,7 @@ class DbConductorTestCase(base.DbTestCase):
|
||||
def test_get_active_driver_dict_one_host_one_driver(self, mock_utcnow):
|
||||
h = 'fake-host'
|
||||
d = 'fake-driver'
|
||||
expected = {d: set([h])}
|
||||
expected = {d: {h}}
|
||||
|
||||
mock_utcnow.return_value = datetime.datetime.utcnow()
|
||||
self._create_test_cdr(hostname=h, drivers=[d])
|
||||
@ -199,7 +206,7 @@ class DbConductorTestCase(base.DbTestCase):
|
||||
h = 'fake-host'
|
||||
d1 = 'driver-one'
|
||||
d2 = 'driver-two'
|
||||
expected = {d1: set([h]), d2: set([h])}
|
||||
expected = {d1: {h}, d2: {h}}
|
||||
|
||||
mock_utcnow.return_value = datetime.datetime.utcnow()
|
||||
self._create_test_cdr(hostname=h, drivers=[d1, d2])
|
||||
@ -211,7 +218,7 @@ class DbConductorTestCase(base.DbTestCase):
|
||||
h1 = 'host-one'
|
||||
h2 = 'host-two'
|
||||
d = 'fake-driver'
|
||||
expected = {d: set([h1, h2])}
|
||||
expected = {d: {h1, h2}}
|
||||
|
||||
mock_utcnow.return_value = datetime.datetime.utcnow()
|
||||
self._create_test_cdr(id=1, hostname=h1, drivers=[d])
|
||||
@ -226,7 +233,7 @@ class DbConductorTestCase(base.DbTestCase):
|
||||
h3 = 'host-three'
|
||||
d1 = 'driver-one'
|
||||
d2 = 'driver-two'
|
||||
expected = {d1: set([h1, h2]), d2: set([h2, h3])}
|
||||
expected = {d1: {h1, h2}, d2: {h2, h3}}
|
||||
|
||||
mock_utcnow.return_value = datetime.datetime.utcnow()
|
||||
self._create_test_cdr(id=1, hostname=h1, drivers=[d1])
|
||||
@ -254,16 +261,114 @@ class DbConductorTestCase(base.DbTestCase):
|
||||
|
||||
# verify that old-host does not show up in current list
|
||||
one_minute = 60
|
||||
expected = {d: set([h2]), d2: set([h2])}
|
||||
expected = {d: {h2}, d2: {h2}}
|
||||
result = self.dbapi.get_active_driver_dict(interval=one_minute)
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
# change the interval, and verify that old-host appears
|
||||
two_minute = one_minute * 2
|
||||
expected = {d: set([h1, h2]), d1: set([h1]), d2: set([h2])}
|
||||
expected = {d: {h1, h2}, d1: {h1}, d2: {h2}}
|
||||
result = self.dbapi.get_active_driver_dict(interval=two_minute)
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch.object(timeutils, 'utcnow', autospec=True)
|
||||
def test_get_active_hardware_type_dict_one_host_no_ht(self, mock_utcnow):
|
||||
h = 'fake-host'
|
||||
expected = {}
|
||||
|
||||
mock_utcnow.return_value = datetime.datetime.utcnow()
|
||||
self._create_test_cdr(hostname=h, drivers=[], hardware_types=[])
|
||||
result = self.dbapi.get_active_hardware_type_dict()
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch.object(timeutils, 'utcnow', autospec=True)
|
||||
def test_get_active_hardware_type_dict_one_host_one_ht(self, mock_utcnow):
|
||||
h = 'fake-host'
|
||||
ht = 'hardware-type'
|
||||
expected = {ht: {h}}
|
||||
|
||||
mock_utcnow.return_value = datetime.datetime.utcnow()
|
||||
self._create_test_cdr(hostname=h, drivers=[], hardware_types=[ht])
|
||||
result = self.dbapi.get_active_hardware_type_dict()
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch.object(timeutils, 'utcnow', autospec=True)
|
||||
def test_get_active_hardware_type_dict_one_host_many_ht(self, mock_utcnow):
|
||||
h = 'fake-host'
|
||||
ht1 = 'hardware-type'
|
||||
ht2 = 'another-hardware-type'
|
||||
expected = {ht1: {h}, ht2: {h}}
|
||||
|
||||
mock_utcnow.return_value = datetime.datetime.utcnow()
|
||||
self._create_test_cdr(hostname=h, drivers=[],
|
||||
hardware_types=[ht1, ht2])
|
||||
result = self.dbapi.get_active_hardware_type_dict()
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch.object(timeutils, 'utcnow', autospec=True)
|
||||
def test_get_active_hardware_type_dict_many_host_one_ht(self, mock_utcnow):
|
||||
h1 = 'host-one'
|
||||
h2 = 'host-two'
|
||||
ht = 'hardware-type'
|
||||
expected = {ht: {h1, h2}}
|
||||
|
||||
mock_utcnow.return_value = datetime.datetime.utcnow()
|
||||
self._create_test_cdr(id=1, hostname=h1, drivers=[],
|
||||
hardware_types=[ht])
|
||||
self._create_test_cdr(id=2, hostname=h2, drivers=[],
|
||||
hardware_types=[ht])
|
||||
result = self.dbapi.get_active_hardware_type_dict()
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch.object(timeutils, 'utcnow', autospec=True)
|
||||
def test_get_active_hardware_type_dict_many_host_many_ht(self,
|
||||
mock_utcnow):
|
||||
h1 = 'host-one'
|
||||
h2 = 'host-two'
|
||||
ht1 = 'hardware-type'
|
||||
ht2 = 'another-hardware-type'
|
||||
expected = {ht1: {h1, h2}, ht2: {h1, h2}}
|
||||
|
||||
mock_utcnow.return_value = datetime.datetime.utcnow()
|
||||
self._create_test_cdr(id=1, hostname=h1, drivers=[],
|
||||
hardware_types=[ht1, ht2])
|
||||
self._create_test_cdr(id=2, hostname=h2, drivers=[],
|
||||
hardware_types=[ht1, ht2])
|
||||
result = self.dbapi.get_active_hardware_type_dict()
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch.object(timeutils, 'utcnow', autospec=True)
|
||||
def test_get_active_hardware_type_dict_with_old_conductor(self,
|
||||
mock_utcnow):
|
||||
past = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
present = past + datetime.timedelta(minutes=2)
|
||||
|
||||
ht = 'hardware-type'
|
||||
|
||||
h1 = 'old-host'
|
||||
ht1 = 'old-hardware-type'
|
||||
mock_utcnow.return_value = past
|
||||
self._create_test_cdr(id=1, hostname=h1, drivers=[],
|
||||
hardware_types=[ht, ht1])
|
||||
|
||||
h2 = 'new-host'
|
||||
ht2 = 'new-hardware-type'
|
||||
mock_utcnow.return_value = present
|
||||
self._create_test_cdr(id=2, hostname=h2, drivers=[],
|
||||
hardware_types=[ht, ht2])
|
||||
|
||||
# verify that old-host does not show up in current list
|
||||
self.config(heartbeat_timeout=60, group='conductor')
|
||||
expected = {ht: {h2}, ht2: {h2}}
|
||||
result = self.dbapi.get_active_hardware_type_dict()
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
# change the heartbeat timeout, and verify that old-host appears
|
||||
self.config(heartbeat_timeout=120, group='conductor')
|
||||
expected = {ht: {h1, h2}, ht1: {h1}, ht2: {h2}}
|
||||
result = self.dbapi.get_active_hardware_type_dict()
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch.object(timeutils, 'utcnow', autospec=True)
|
||||
def test_get_offline_conductors(self, mock_utcnow):
|
||||
self.config(heartbeat_timeout=60, group='conductor')
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Using a dynamic driver in a node's driver field is now possible, though
|
||||
customizing the interfaces is not yet exposed in the REST API.
|
Loading…
x
Reference in New Issue
Block a user