blueprint host-aggregates: xenapi implementation
This commit introduces some clean-up/improvements on the current model and api for host aggregates. It also introduces a first version of the xenapi implementation. More precisely: - it lays out the structure of the virt driver, - it introduces compute and xenapi unit tests coverage, - it deals with join/eject of pool master and slaves, - it fixes xenapi_conn, when used in resource pool configurations More commits to follow (to ensure that VM placement, networking setup, performance metrics work just as well in cases where resource pools are present). However, these may be outside the scope of this blueprint and considered as ad-hoc bug fixes. Change-Id: Ib3cff71160264c5547e1c060d3fd566ad87337cb
This commit is contained in:
@@ -929,6 +929,11 @@ class QuotaError(NovaException):
|
||||
message = _("Quota exceeded") + ": code=%(code)s"
|
||||
|
||||
|
||||
class AggregateError(NovaException):
|
||||
message = _("Aggregate %(aggregate_id)s: action '%(action)s' "
|
||||
"caused an error: %(reason)s.")
|
||||
|
||||
|
||||
class AggregateNotFound(NotFound):
|
||||
message = _("Aggregate %(aggregate_id)s could not be found.")
|
||||
|
||||
|
@@ -3153,6 +3153,8 @@ def _create_service_entries(context, values={'avail_zone1': ['fake_host1',
|
||||
|
||||
|
||||
class ComputeAPIAggrTestCase(test.TestCase):
|
||||
"""This is for unit coverage of aggregate-related methods
|
||||
defined in nova.compute.api."""
|
||||
|
||||
def setUp(self):
|
||||
super(ComputeAPIAggrTestCase, self).setUp()
|
||||
@@ -3164,9 +3166,16 @@ class ComputeAPIAggrTestCase(test.TestCase):
|
||||
def tearDown(self):
|
||||
super(ComputeAPIAggrTestCase, self).tearDown()
|
||||
|
||||
def test_create_invalid_availability_zone(self):
|
||||
"""Ensure InvalidAggregateAction is raised with wrong avail_zone."""
|
||||
self.assertRaises(exception.InvalidAggregateAction,
|
||||
self.api.create_aggregate,
|
||||
self.context, 'fake_aggr', 'fake_avail_zone')
|
||||
|
||||
def test_update_aggregate_metadata(self):
|
||||
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
|
||||
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
|
||||
'fake_availability_zone')
|
||||
'fake_zone')
|
||||
metadata = {'foo_key1': 'foo_value1',
|
||||
'foo_key2': 'foo_value2', }
|
||||
aggr = self.api.update_aggregate_metadata(self.context, aggr['id'],
|
||||
@@ -3178,8 +3187,9 @@ class ComputeAPIAggrTestCase(test.TestCase):
|
||||
|
||||
def test_delete_aggregate(self):
|
||||
"""Ensure we can delete an aggregate."""
|
||||
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
|
||||
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
|
||||
'fake_availability_zone')
|
||||
'fake_zone')
|
||||
self.api.delete_aggregate(self.context, aggr['id'])
|
||||
expected = db.aggregate_get(self.context, aggr['id'],
|
||||
read_deleted='yes')
|
||||
@@ -3188,10 +3198,10 @@ class ComputeAPIAggrTestCase(test.TestCase):
|
||||
|
||||
def test_delete_non_empty_aggregate(self):
|
||||
"""Ensure InvalidAggregateAction is raised when non empty aggregate."""
|
||||
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
|
||||
'fake_availability_zone')
|
||||
_create_service_entries(self.context,
|
||||
{'fake_availability_zone': ['fake_host']})
|
||||
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
|
||||
'fake_availability_zone')
|
||||
self.api.add_host_to_aggregate(self.context, aggr['id'], 'fake_host')
|
||||
self.assertRaises(exception.InvalidAggregateAction,
|
||||
self.api.delete_aggregate, self.context, aggr['id'])
|
||||
@@ -3242,9 +3252,9 @@ class ComputeAPIAggrTestCase(test.TestCase):
|
||||
def test_add_host_to_aggregate_invalid_dismissed_status(self):
|
||||
"""Ensure InvalidAggregateAction is raised when aggregate is
|
||||
deleted."""
|
||||
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
|
||||
aggr = self.api.create_aggregate(self.context,
|
||||
'fake_aggregate', 'fake_zone')
|
||||
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
|
||||
# let's mock the fact that the aggregate is dismissed!
|
||||
status = {'operational_state': aggregate_states.DISMISSED}
|
||||
db.aggregate_update(self.context, aggr['id'], status)
|
||||
@@ -3255,9 +3265,9 @@ class ComputeAPIAggrTestCase(test.TestCase):
|
||||
def test_add_host_to_aggregate_invalid_error_status(self):
|
||||
"""Ensure InvalidAggregateAction is raised when aggregate is
|
||||
in error."""
|
||||
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
|
||||
aggr = self.api.create_aggregate(self.context,
|
||||
'fake_aggregate', 'fake_zone')
|
||||
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
|
||||
# let's mock the fact that the aggregate is in error!
|
||||
status = {'operational_state': aggregate_states.ERROR}
|
||||
db.aggregate_update(self.context, aggr['id'], status)
|
||||
@@ -3267,17 +3277,19 @@ class ComputeAPIAggrTestCase(test.TestCase):
|
||||
|
||||
def test_add_host_to_aggregate_zones_mismatch(self):
|
||||
"""Ensure InvalidAggregateAction is raised when zones don't match."""
|
||||
_create_service_entries(self.context, {'fake_zoneX': ['fake_host']})
|
||||
_create_service_entries(self.context, {'fake_zoneX': ['fake_host1'],
|
||||
'fake_zoneY': ['fake_host2']})
|
||||
aggr = self.api.create_aggregate(self.context,
|
||||
'fake_aggregate', 'fake_zoneY')
|
||||
self.assertRaises(exception.InvalidAggregateAction,
|
||||
self.api.add_host_to_aggregate,
|
||||
self.context, aggr['id'], 'fake_host')
|
||||
self.context, aggr['id'], 'fake_host1')
|
||||
|
||||
def test_add_host_to_aggregate_raise_not_found(self):
|
||||
"""Ensure ComputeHostNotFound is raised when adding invalid host."""
|
||||
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
|
||||
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
|
||||
'fake_availability_zone')
|
||||
'fake_zone')
|
||||
self.assertRaises(exception.ComputeHostNotFound,
|
||||
self.api.add_host_to_aggregate,
|
||||
self.context, aggr['id'], 'invalid_host')
|
||||
@@ -3325,9 +3337,9 @@ class ComputeAPIAggrTestCase(test.TestCase):
|
||||
def test_remove_host_from_aggregate_invalid_dismissed_status(self):
|
||||
"""Ensure InvalidAggregateAction is raised when aggregate is
|
||||
deleted."""
|
||||
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
|
||||
aggr = self.api.create_aggregate(self.context,
|
||||
'fake_aggregate', 'fake_zone')
|
||||
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
|
||||
# let's mock the fact that the aggregate is dismissed!
|
||||
status = {'operational_state': aggregate_states.DISMISSED}
|
||||
db.aggregate_update(self.context, aggr['id'], status)
|
||||
@@ -3338,9 +3350,9 @@ class ComputeAPIAggrTestCase(test.TestCase):
|
||||
def test_remove_host_from_aggregate_invalid_changing_status(self):
|
||||
"""Ensure InvalidAggregateAction is raised when aggregate is
|
||||
changing."""
|
||||
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
|
||||
aggr = self.api.create_aggregate(self.context,
|
||||
'fake_aggregate', 'fake_zone')
|
||||
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
|
||||
# let's mock the fact that the aggregate is changing!
|
||||
status = {'operational_state': aggregate_states.CHANGING}
|
||||
db.aggregate_update(self.context, aggr['id'], status)
|
||||
@@ -3350,13 +3362,85 @@ class ComputeAPIAggrTestCase(test.TestCase):
|
||||
|
||||
def test_remove_host_from_aggregate_raise_not_found(self):
|
||||
"""Ensure ComputeHostNotFound is raised when removing invalid host."""
|
||||
_create_service_entries(self.context, {'fake_zone': ['fake_host']})
|
||||
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
|
||||
'fake_availability_zone')
|
||||
'fake_zone')
|
||||
self.assertRaises(exception.ComputeHostNotFound,
|
||||
self.api.remove_host_from_aggregate,
|
||||
self.context, aggr['id'], 'invalid_host')
|
||||
|
||||
|
||||
class ComputeAggrTestCase(BaseTestCase):
|
||||
"""This is for unit coverage of aggregate-related methods
|
||||
defined in nova.compute.manager."""
|
||||
|
||||
def setUp(self):
|
||||
super(ComputeAggrTestCase, self).setUp()
|
||||
self.context = context.get_admin_context()
|
||||
values = {'name': 'test_aggr',
|
||||
'availability_zone': 'test_zone', }
|
||||
self.aggr = db.aggregate_create(self.context, values)
|
||||
|
||||
def tearDown(self):
|
||||
super(ComputeAggrTestCase, self).tearDown()
|
||||
|
||||
def test_add_aggregate_host(self):
|
||||
def fake_driver_add_to_aggregate(context, aggregate, host):
|
||||
fake_driver_add_to_aggregate.called = True
|
||||
return {"foo": "bar"}
|
||||
self.stubs.Set(self.compute.driver, "add_to_aggregate",
|
||||
fake_driver_add_to_aggregate)
|
||||
|
||||
self.compute.add_aggregate_host(self.context, self.aggr.id, "host")
|
||||
self.assertTrue(fake_driver_add_to_aggregate.called)
|
||||
|
||||
def test_add_aggregate_host_raise_err(self):
|
||||
"""Ensure the undo operation works correctly on add."""
|
||||
def fake_driver_add_to_aggregate(context, aggregate, host):
|
||||
raise exception.AggregateError
|
||||
self.stubs.Set(self.compute.driver, "add_to_aggregate",
|
||||
fake_driver_add_to_aggregate)
|
||||
|
||||
state = {'operational_state': aggregate_states.ACTIVE}
|
||||
db.aggregate_update(self.context, self.aggr.id, state)
|
||||
db.aggregate_host_add(self.context, self.aggr.id, 'fake_host')
|
||||
|
||||
self.assertRaises(exception.AggregateError,
|
||||
self.compute.add_aggregate_host,
|
||||
self.context, self.aggr.id, "fake_host")
|
||||
excepted = db.aggregate_get(self.context, self.aggr.id)
|
||||
self.assertEqual(excepted.operational_state, aggregate_states.ERROR)
|
||||
self.assertEqual(excepted.hosts, [])
|
||||
|
||||
def test_remove_aggregate_host(self):
|
||||
def fake_driver_remove_from_aggregate(context, aggregate, host):
|
||||
fake_driver_remove_from_aggregate.called = True
|
||||
self.assertEqual("host", host, "host")
|
||||
return {"foo": "bar"}
|
||||
self.stubs.Set(self.compute.driver, "remove_from_aggregate",
|
||||
fake_driver_remove_from_aggregate)
|
||||
|
||||
self.compute.remove_aggregate_host(self.context, self.aggr.id, "host")
|
||||
self.assertTrue(fake_driver_remove_from_aggregate.called)
|
||||
|
||||
def test_remove_aggregate_host_raise_err(self):
|
||||
"""Ensure the undo operation works correctly on remove."""
|
||||
def fake_driver_remove_from_aggregate(context, aggregate, host):
|
||||
raise exception.AggregateError
|
||||
self.stubs.Set(self.compute.driver, "remove_from_aggregate",
|
||||
fake_driver_remove_from_aggregate)
|
||||
|
||||
state = {'operational_state': aggregate_states.ACTIVE}
|
||||
db.aggregate_update(self.context, self.aggr.id, state)
|
||||
|
||||
self.assertRaises(exception.AggregateError,
|
||||
self.compute.remove_aggregate_host,
|
||||
self.context, self.aggr.id, "fake_host")
|
||||
excepted = db.aggregate_get(self.context, self.aggr.id)
|
||||
self.assertEqual(excepted.operational_state, aggregate_states.ERROR)
|
||||
self.assertEqual(excepted.hosts, ['fake_host'])
|
||||
|
||||
|
||||
class ComputePolicyTestCase(BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
@@ -267,6 +267,30 @@ class DbApiTestCase(test.TestCase):
|
||||
expected = {uuids[0]: [], uuids[1]: []}
|
||||
self.assertEqual(expected, instance_faults)
|
||||
|
||||
def test_dns_registration(self):
|
||||
domain1 = 'test.domain.one'
|
||||
domain2 = 'test.domain.two'
|
||||
testzone = 'testzone'
|
||||
ctxt = context.get_admin_context()
|
||||
|
||||
db.dnsdomain_register_for_zone(ctxt, domain1, testzone)
|
||||
domain_ref = db.dnsdomain_get(ctxt, domain1)
|
||||
zone = domain_ref.availability_zone
|
||||
scope = domain_ref.scope
|
||||
self.assertEqual(scope, 'private')
|
||||
self.assertEqual(zone, testzone)
|
||||
|
||||
db.dnsdomain_register_for_project(ctxt, domain2,
|
||||
self.project_id)
|
||||
domain_ref = db.dnsdomain_get(ctxt, domain2)
|
||||
project = domain_ref.project_id
|
||||
scope = domain_ref.scope
|
||||
self.assertEqual(project, self.project_id)
|
||||
self.assertEqual(scope, 'public')
|
||||
|
||||
db.dnsdomain_unregister(ctxt, domain1)
|
||||
db.dnsdomain_unregister(ctxt, domain2)
|
||||
|
||||
|
||||
def _get_fake_aggr_values():
|
||||
return {'name': 'fake_aggregate',
|
||||
@@ -351,6 +375,14 @@ class AggregateDBApiTestCase(test.TestCase):
|
||||
db.aggregate_create,
|
||||
self.context, _get_fake_aggr_values())
|
||||
|
||||
def test_aggregate_get(self):
|
||||
"""Ensure we can get aggregate with all its relations."""
|
||||
ctxt = context.get_admin_context()
|
||||
result = _create_aggregate_with_hosts(context=ctxt)
|
||||
expected = db.aggregate_get(ctxt, result.id)
|
||||
self.assertEqual(_get_fake_aggr_hosts(), expected.hosts)
|
||||
self.assertEqual(_get_fake_aggr_metadata(), expected.metadetails)
|
||||
|
||||
def test_aggregate_delete_raise_not_found(self):
|
||||
"""Ensure AggregateNotFound is raised when deleting an aggregate."""
|
||||
ctxt = context.get_admin_context()
|
||||
@@ -541,30 +573,6 @@ class AggregateDBApiTestCase(test.TestCase):
|
||||
db.aggregate_host_delete,
|
||||
ctxt, result.id, _get_fake_aggr_hosts()[0])
|
||||
|
||||
def test_dns_registration(self):
|
||||
domain1 = 'test.domain.one'
|
||||
domain2 = 'test.domain.two'
|
||||
testzone = 'testzone'
|
||||
ctxt = context.get_admin_context()
|
||||
|
||||
db.dnsdomain_register_for_zone(ctxt, domain1, testzone)
|
||||
domain_ref = db.dnsdomain_get(ctxt, domain1)
|
||||
zone = domain_ref.availability_zone
|
||||
scope = domain_ref.scope
|
||||
self.assertEqual(scope, 'private')
|
||||
self.assertEqual(zone, testzone)
|
||||
|
||||
db.dnsdomain_register_for_project(ctxt, domain2,
|
||||
self.project_id)
|
||||
domain_ref = db.dnsdomain_get(ctxt, domain2)
|
||||
project = domain_ref.project_id
|
||||
scope = domain_ref.scope
|
||||
self.assertEqual(project, self.project_id)
|
||||
self.assertEqual(scope, 'public')
|
||||
|
||||
db.dnsdomain_unregister(ctxt, domain1)
|
||||
db.dnsdomain_unregister(ctxt, domain2)
|
||||
|
||||
|
||||
class CapacityTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
|
@@ -401,6 +401,14 @@ class _VirtDriverTestCase(test.TestCase):
|
||||
def test_host_power_action_startup(self):
|
||||
self.connection.host_power_action('a useless argument?', 'startup')
|
||||
|
||||
@catch_notimplementederror
|
||||
def test_add_to_aggregate(self):
|
||||
self.connection.add_to_aggregate(self.ctxt, 'aggregate', 'host')
|
||||
|
||||
@catch_notimplementederror
|
||||
def test_remove_from_aggregate(self):
|
||||
self.connection.remove_from_aggregate(self.ctxt, 'aggregate', 'host')
|
||||
|
||||
|
||||
class AbstractDriverTestCase(_VirtDriverTestCase):
|
||||
def setUp(self):
|
||||
|
@@ -31,6 +31,7 @@ from nova import flags
|
||||
from nova import log as logging
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova.compute import aggregate_states
|
||||
from nova.compute import instance_types
|
||||
from nova.compute import power_state
|
||||
from nova import exception
|
||||
@@ -1741,3 +1742,149 @@ class XenAPISRSelectionTestCase(test.TestCase):
|
||||
expected = helper.safe_find_sr(session)
|
||||
self.assertEqual(session.call_xenapi('pool.get_default_SR', pool_ref),
|
||||
expected)
|
||||
|
||||
|
||||
class XenAPIAggregateTestCase(test.TestCase):
|
||||
"""Unit tests for aggregate operations."""
|
||||
def setUp(self):
|
||||
super(XenAPIAggregateTestCase, self).setUp()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
self.flags(xenapi_connection_url='http://test_url',
|
||||
xenapi_connection_username='test_user',
|
||||
xenapi_connection_password='test_pass',
|
||||
instance_name_template='%d',
|
||||
firewall_driver='nova.virt.xenapi.firewall.'
|
||||
'Dom0IptablesFirewallDriver',
|
||||
host='host')
|
||||
xenapi_fake.reset()
|
||||
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
|
||||
self.context = context.get_admin_context()
|
||||
self.conn = xenapi_conn.get_connection(False)
|
||||
self.fake_metadata = {'master_compute': 'host'}
|
||||
|
||||
def tearDown(self):
|
||||
super(XenAPIAggregateTestCase, self).tearDown()
|
||||
self.stubs.UnsetAll()
|
||||
|
||||
def test_add_to_aggregate_called(self):
|
||||
def fake_add_to_aggregate(context, aggregate, host):
|
||||
fake_add_to_aggregate.called = True
|
||||
self.stubs.Set(self.conn._pool,
|
||||
"add_to_aggregate",
|
||||
fake_add_to_aggregate)
|
||||
|
||||
self.conn.add_to_aggregate(None, None, None)
|
||||
self.assertTrue(fake_add_to_aggregate.called)
|
||||
|
||||
def test_add_to_aggregate_for_first_host_sets_metadata(self):
|
||||
def fake_init_pool(id, name):
|
||||
fake_init_pool.called = True
|
||||
self.stubs.Set(self.conn._pool, "_init_pool", fake_init_pool)
|
||||
|
||||
aggregate = self._aggregate_setup()
|
||||
self.conn._pool.add_to_aggregate(self.context, aggregate, "host")
|
||||
result = db.aggregate_get(self.context, aggregate.id)
|
||||
self.assertTrue(fake_init_pool.called)
|
||||
self.assertDictMatch(self.fake_metadata, result.metadetails)
|
||||
self.assertEqual(aggregate_states.ACTIVE, result.operational_state)
|
||||
|
||||
def test_join_slave(self):
|
||||
"""Ensure join_slave gets called when the request gets to master."""
|
||||
def fake_join_slave(id, compute_uuid, host, url, user, password):
|
||||
fake_join_slave.called = True
|
||||
self.stubs.Set(self.conn._pool, "_join_slave", fake_join_slave)
|
||||
|
||||
aggregate = self._aggregate_setup(hosts=['host', 'host2'],
|
||||
metadata=self.fake_metadata)
|
||||
self.conn._pool.add_to_aggregate(self.context, aggregate, "host2",
|
||||
compute_uuid='fake_uuid',
|
||||
url='fake_url',
|
||||
user='fake_user',
|
||||
passwd='fake_pass',
|
||||
xenhost_uuid='fake_uuid')
|
||||
self.assertTrue(fake_join_slave.called)
|
||||
|
||||
def test_add_to_aggregate_first_host(self):
|
||||
def fake_pool_set_name_label(self, session, pool_ref, name):
|
||||
fake_pool_set_name_label.called = True
|
||||
self.stubs.Set(xenapi_fake.SessionBase, "pool_set_name_label",
|
||||
fake_pool_set_name_label)
|
||||
self.conn._session.call_xenapi("pool.create", {"name": "asdf"})
|
||||
|
||||
values = {"name": 'fake_aggregate',
|
||||
"availability_zone": 'fake_zone'}
|
||||
result = db.aggregate_create(self.context, values)
|
||||
db.aggregate_host_add(self.context, result.id, "host")
|
||||
aggregate = db.aggregate_get(self.context, result.id)
|
||||
self.assertEqual(["host"], aggregate.hosts)
|
||||
self.assertEqual({}, aggregate.metadetails)
|
||||
|
||||
self.conn._pool.add_to_aggregate(self.context, aggregate, "host")
|
||||
self.assertTrue(fake_pool_set_name_label.called)
|
||||
|
||||
def test_remove_from_aggregate_called(self):
|
||||
def fake_remove_from_aggregate(context, aggregate, host):
|
||||
fake_remove_from_aggregate.called = True
|
||||
self.stubs.Set(self.conn._pool,
|
||||
"remove_from_aggregate",
|
||||
fake_remove_from_aggregate)
|
||||
|
||||
self.conn.remove_from_aggregate(None, None, None)
|
||||
self.assertTrue(fake_remove_from_aggregate.called)
|
||||
|
||||
def test_remove_from_empty_aggregate(self):
|
||||
values = {"name": 'fake_aggregate',
|
||||
"availability_zone": 'fake_zone'}
|
||||
result = db.aggregate_create(self.context, values)
|
||||
self.assertRaises(exception.AggregateError,
|
||||
self.conn._pool.remove_from_aggregate,
|
||||
None, result, "test_host")
|
||||
|
||||
def test_remove_slave(self):
|
||||
"""Ensure eject slave gets called."""
|
||||
def fake_eject_slave(id, compute_uuid, host_uuid):
|
||||
fake_eject_slave.called = True
|
||||
self.stubs.Set(self.conn._pool, "_eject_slave", fake_eject_slave)
|
||||
|
||||
self.fake_metadata['host2'] = 'fake_host2_uuid'
|
||||
aggregate = self._aggregate_setup(hosts=['host', 'host2'],
|
||||
metadata=self.fake_metadata)
|
||||
self.conn._pool.remove_from_aggregate(self.context, aggregate, "host2")
|
||||
self.assertTrue(fake_eject_slave.called)
|
||||
|
||||
def test_remove_master_solo(self):
|
||||
"""Ensure metadata are cleared after removal."""
|
||||
def fake_clear_pool(id):
|
||||
fake_clear_pool.called = True
|
||||
self.stubs.Set(self.conn._pool, "_clear_pool", fake_clear_pool)
|
||||
|
||||
aggregate = self._aggregate_setup(aggr_state=aggregate_states.ACTIVE,
|
||||
metadata=self.fake_metadata)
|
||||
self.conn._pool.remove_from_aggregate(self.context, aggregate, "host")
|
||||
result = db.aggregate_get(self.context, aggregate.id)
|
||||
self.assertTrue(fake_clear_pool.called)
|
||||
self.assertDictMatch({}, result.metadetails)
|
||||
self.assertEqual(aggregate_states.ACTIVE, result.operational_state)
|
||||
|
||||
def test_remote_master_non_empty_pool(self):
|
||||
"""Ensure AggregateError is raised if removing the master."""
|
||||
aggregate = self._aggregate_setup(aggr_state=aggregate_states.ACTIVE,
|
||||
hosts=['host', 'host2'],
|
||||
metadata=self.fake_metadata)
|
||||
self.assertRaises(exception.AggregateError,
|
||||
self.conn._pool.remove_from_aggregate,
|
||||
self.context, aggregate, "host")
|
||||
|
||||
def _aggregate_setup(self, aggr_name='fake_aggregate',
|
||||
aggr_zone='fake_zone',
|
||||
aggr_state=aggregate_states.CREATED,
|
||||
hosts=['host'], metadata=None):
|
||||
values = {"name": aggr_name,
|
||||
"availability_zone": aggr_zone,
|
||||
"operational_state": aggr_state, }
|
||||
result = db.aggregate_create(self.context, values)
|
||||
for host in hosts:
|
||||
db.aggregate_host_add(self.context, result.id, host)
|
||||
if metadata:
|
||||
db.aggregate_metadata_add(self.context, result.id, metadata)
|
||||
return db.aggregate_get(self.context, result.id)
|
||||
|
Reference in New Issue
Block a user