blueprint host-aggregates: OSAPI/virt integration, via nova.compute.api

This commit introduces the first cut of integration between the OSAPI
Admin extensions for host aggregates and the virt layer.

This is part of a series of commits that have started with change:

https://review.openstack.org/#change,3035

Change-Id: I75d8b616e3b8f8cef75d40d937e0dce9f29b16db
This commit is contained in:
Armando Migliaccio
2012-01-18 19:47:36 +00:00
parent 7ee649d0e5
commit f80a7409d0
3 changed files with 228 additions and 1 deletions

View File

@@ -261,6 +261,11 @@ class InvalidParameterValue(Invalid):
message = _("%(err)s")
class InvalidAggregateAction(Invalid):
message = _("Cannot perform action '%(action)s' on aggregate "
"%(aggregate_id)s. Reason: %(reason)s.")
class InstanceInvalidState(Invalid):
message = _("Instance %(instance_uuid)s in %(attr)s %(state)s. Cannot "
"%(method)s while the instance is in this state.")

View File

@@ -31,6 +31,7 @@ import nova
import nova.common.policy
from nova import compute
import nova.compute.api
from nova.compute import aggregate_states
from nova.compute import instance_types
from nova.compute import manager as compute_manager
from nova.compute import power_state
@@ -3011,6 +3012,216 @@ class ComputeAPITestCase(BaseTestCase):
db.instance_destroy(self.context, instance['id'])
def fake_rpc_method(context, topic, msg, do_cast=True):
pass
def _create_service_entries(context, values={'avail_zone1': ['fake_host1',
'fake_host2'],
'avail_zone2': ['fake_host3'], }):
for avail_zone, hosts in values.iteritems():
for host in hosts:
db.service_create(context,
{'host': host,
'binary': 'nova-compute',
'topic': 'compute',
'report_count': 0,
'availability_zone': avail_zone})
return values
class ComputeAPIAggrTestCase(test.TestCase):
def setUp(self):
super(ComputeAPIAggrTestCase, self).setUp()
self.api = compute.AggregateAPI()
self.context = context.get_admin_context()
self.stubs.Set(rpc, 'call', fake_rpc_method)
self.stubs.Set(rpc, 'cast', fake_rpc_method)
def tearDown(self):
super(ComputeAPIAggrTestCase, self).tearDown()
def test_update_aggregate_metadata(self):
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
'fake_availability_zone')
metadata = {'foo_key1': 'foo_value1',
'foo_key2': 'foo_value2', }
aggr = self.api.update_aggregate_metadata(self.context, aggr['id'],
metadata)
metadata['foo_key1'] = None
expected = self.api.update_aggregate_metadata(self.context,
aggr['id'], metadata)
self.assertDictMatch(expected['metadata'], {'foo_key2': 'foo_value2'})
def test_delete_aggregate(self):
"""Ensure we can delete an aggregate."""
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
'fake_availability_zone')
self.api.delete_aggregate(self.context, aggr['id'])
expected = db.aggregate_get(self.context, aggr['id'],
read_deleted='yes')
self.assertNotEqual(aggr['operational_state'],
expected['operational_state'])
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']})
self.api.add_host_to_aggregate(self.context, aggr['id'], 'fake_host')
self.assertRaises(exception.InvalidAggregateAction,
self.api.delete_aggregate, self.context, aggr['id'])
def test_add_host_to_aggregate(self):
"""Ensure we can add a host to an aggregate."""
values = _create_service_entries(self.context)
fake_zone = values.keys()[0]
fake_host = values[fake_zone][0]
aggr = self.api.create_aggregate(self.context,
'fake_aggregate', fake_zone)
aggr = self.api.add_host_to_aggregate(self.context,
aggr['id'], fake_host)
self.assertEqual(aggr['operational_state'], aggregate_states.CHANGING)
def test_add_host_to_aggregate_multiple(self):
"""Ensure we can add multiple hosts to an aggregate."""
values = _create_service_entries(self.context)
fake_zone = values.keys()[0]
aggr = self.api.create_aggregate(self.context,
'fake_aggregate', fake_zone)
# let's mock the fact that the aggregate is active already!
status = {'operational_state': aggregate_states.ACTIVE}
db.aggregate_update(self.context, aggr['id'], status)
for host in values[fake_zone]:
aggr = self.api.add_host_to_aggregate(self.context,
aggr['id'], host)
self.assertEqual(len(aggr['hosts']), len(values[fake_zone]))
self.assertEqual(aggr['operational_state'],
aggregate_states.ACTIVE)
def test_add_host_to_aggregate_invalid_changing_status(self):
"""Ensure InvalidAggregateAction is raised when adding host while
aggregate is not ready."""
values = _create_service_entries(self.context)
fake_zone = values.keys()[0]
fake_host = values[fake_zone][0]
aggr = self.api.create_aggregate(self.context,
'fake_aggregate', fake_zone)
aggr = self.api.add_host_to_aggregate(self.context,
aggr['id'], fake_host)
self.assertEqual(aggr['operational_state'],
aggregate_states.CHANGING)
self.assertRaises(exception.InvalidAggregateAction,
self.api.add_host_to_aggregate, self.context,
aggr['id'], fake_host)
def test_add_host_to_aggregate_invalid_dismissed_status(self):
"""Ensure InvalidAggregateAction is raised when aggregate is
deleted."""
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)
self.assertRaises(exception.InvalidAggregateAction,
self.api.add_host_to_aggregate, self.context,
aggr['id'], 'fake_host')
def test_add_host_to_aggregate_invalid_error_status(self):
"""Ensure InvalidAggregateAction is raised when aggregate is
in error."""
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)
self.assertRaises(exception.InvalidAggregateAction,
self.api.add_host_to_aggregate, self.context,
aggr['id'], 'fake_host')
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']})
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')
def test_add_host_to_aggregate_raise_not_found(self):
"""Ensure ComputeHostNotFound is raised when adding invalid host."""
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
'fake_availability_zone')
self.assertRaises(exception.ComputeHostNotFound,
self.api.add_host_to_aggregate,
self.context, aggr['id'], 'invalid_host')
def test_remove_host_from_aggregate_active(self):
"""Ensure we can remove a host from an aggregate."""
values = _create_service_entries(self.context)
fake_zone = values.keys()[0]
aggr = self.api.create_aggregate(self.context,
'fake_aggregate', fake_zone)
# let's mock the fact that the aggregate is active already!
status = {'operational_state': aggregate_states.ACTIVE}
db.aggregate_update(self.context, aggr['id'], status)
for host in values[fake_zone]:
aggr = self.api.add_host_to_aggregate(self.context,
aggr['id'], host)
expected = self.api.remove_host_from_aggregate(self.context,
aggr['id'],
values[fake_zone][0])
self.assertEqual(len(aggr['hosts']) - 1, len(expected['hosts']))
self.assertEqual(expected['operational_state'],
aggregate_states.ACTIVE)
def test_remove_host_from_aggregate_error(self):
"""Ensure we can remove a host from an aggregate even if in error."""
values = _create_service_entries(self.context)
fake_zone = values.keys()[0]
aggr = self.api.create_aggregate(self.context,
'fake_aggregate', fake_zone)
# let's mock the fact that the aggregate is ready!
status = {'operational_state': aggregate_states.ACTIVE}
db.aggregate_update(self.context, aggr['id'], status)
for host in values[fake_zone]:
aggr = self.api.add_host_to_aggregate(self.context,
aggr['id'], host)
# let's mock the fact that the aggregate is in error!
status = {'operational_state': aggregate_states.ERROR}
expected = self.api.remove_host_from_aggregate(self.context,
aggr['id'],
values[fake_zone][0])
self.assertEqual(len(aggr['hosts']) - 1, len(expected['hosts']))
self.assertEqual(expected['operational_state'],
aggregate_states.ACTIVE)
def test_remove_host_from_aggregate_invalid_dismissed_status(self):
"""Ensure InvalidAggregateAction is raised when aggregate is
deleted."""
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)
self.assertRaises(exception.InvalidAggregateAction,
self.api.remove_host_from_aggregate, self.context,
aggr['id'], 'fake_host')
def test_remove_host_from_aggregate_raise_not_found(self):
"""Ensure ComputeHostNotFound is raised when removing invalid host."""
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
'fake_availability_zone')
self.assertRaises(exception.ComputeHostNotFound,
self.api.remove_host_from_aggregate,
self.context, aggr['id'], 'invalid_host')
class ComputePolicyTestCase(BaseTestCase):
def setUp(self):

View File

@@ -304,7 +304,7 @@ class AggregateDBApiTestCase(test.TestCase):
def test_aggregate_create(self):
"""Ensure aggregate can be created with no metadata."""
result = _create_aggregate(metadata=None)
self.assertEqual(result['operational_state'], 'building')
self.assertEqual(result['operational_state'], 'created')
def test_aggregate_create_raise_exist_exc(self):
"""Ensure aggregate names are distinct."""
@@ -476,6 +476,17 @@ class AggregateDBApiTestCase(test.TestCase):
expected = db.aggregate_host_get_all(ctxt, result.id)
self.assertEqual(_get_fake_aggr_hosts(), expected)
def test_aggregate_host_add_deleted(self):
"""Ensure we can add a host that was previously deleted."""
ctxt = context.get_admin_context()
result = _create_aggregate_with_hosts(context=ctxt, metadata=None)
host = _get_fake_aggr_hosts()[0]
db.aggregate_host_delete(ctxt, result.id, host)
db.aggregate_host_add(ctxt, result.id, host)
expected = db.aggregate_host_get_all(ctxt, result.id,
read_deleted='no')
self.assertEqual(len(expected), 1)
def test_aggregate_host_add_duplicate_raise_conflict(self):
"""Ensure we cannot add host to distinct aggregates."""
ctxt = context.get_admin_context()