From 9f4534ab584faeee1e24d4c1bb38a2b194f24626 Mon Sep 17 00:00:00 2001 From: Joe Gordon Date: Thu, 2 Aug 2012 17:44:18 -0700 Subject: Enable Aggregate based availability zones Instead of implementing availability zones in the service table, availability zones are implemented using general aggregate metadata. This patch does not remove availability zones from the service table, a latter patch will do that. * In theory supports a single compute node in multiple availability zones * Drop availability_zone column from Aggregate table (is now a property) * map aggregate metadata 'availability_zone' so API does not change Implements blueprint aggregate-based-availability-zones Change-Id: I2a2ac5bfaa526d639dff5efa392c051347dbd9bb --- nova/tests/api/ec2/test_cloud.py | 10 +- .../openstack/compute/contrib/test_aggregates.py | 16 +-- nova/tests/compute/test_compute.py | 28 +---- nova/tests/conductor/test_conductor.py | 2 +- .../aggregate-update-post-resp.json.tpl | 4 +- .../aggregate-update-post-resp.xml.tpl | 4 +- .../aggregates-add-host-post-resp.json.tpl | 4 +- .../aggregates-add-host-post-resp.xml.tpl | 4 +- .../os-aggregates/aggregates-get-resp.json.tpl | 4 +- .../os-aggregates/aggregates-get-resp.xml.tpl | 4 +- .../aggregates-list-get-resp.json.tpl | 4 +- .../os-aggregates/aggregates-list-get-resp.xml.tpl | 4 +- .../aggregates-metadata-post-resp.json.tpl | 1 + .../aggregates-metadata-post-resp.xml.tpl | 1 + .../aggregates-remove-host-post-resp.json.tpl | 4 +- .../aggregates-remove-host-post-resp.xml.tpl | 4 +- nova/tests/scheduler/test_host_filters.py | 7 +- nova/tests/test_db_api.py | 123 ++++++++++----------- nova/tests/test_migrations.py | 34 ++++++ nova/tests/test_xenapi.py | 19 ++-- 20 files changed, 155 insertions(+), 126 deletions(-) (limited to 'nova/tests') diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py index d452c18cb..429746dac 100644 --- a/nova/tests/api/ec2/test_cloud.py +++ b/nova/tests/api/ec2/test_cloud.py @@ -710,8 +710,16 @@ class CloudTestCase(test.TestCase): 'topic': 'compute', 'report_count': 0, 'availability_zone': "zone2"}) + # Aggregate based zones + agg = db.aggregate_create(self.context, + {'name': 'agg1'}, {'availability_zone': 'aggzones'}) + db.aggregate_host_add(self.context, agg.id, 'host2_zones') result = self.cloud.describe_availability_zones(self.context) - self.assertEqual(len(result['availabilityZoneInfo']), 3) + self.assertEqual(len(result['availabilityZoneInfo']), 4) + admin_ctxt = context.get_admin_context(read_deleted="no") + result = self.cloud.describe_availability_zones(admin_ctxt, + zone_name='verbose') + self.assertEqual(len(result['availabilityZoneInfo']), 18) db.service_destroy(self.context, service1['id']) db.service_destroy(self.context, service2['id']) diff --git a/nova/tests/api/openstack/compute/contrib/test_aggregates.py b/nova/tests/api/openstack/compute/contrib/test_aggregates.py index 0f60b8128..c57d6a91b 100644 --- a/nova/tests/api/openstack/compute/contrib/test_aggregates.py +++ b/nova/tests/api/openstack/compute/contrib/test_aggregates.py @@ -123,7 +123,7 @@ class AggregateTestCase(test.TestCase): def test_create_with_extra_invalid_arg(self): self.assertRaises(exc.HTTPBadRequest, self.controller.create, self.req, dict(name="test", - availablity_zone="nova1", + availability_zone="nova1", foo='bar')) def test_show(self): @@ -183,9 +183,7 @@ class AggregateTestCase(test.TestCase): return AGGREGATE self.stubs.Set(self.controller.api, "update_aggregate", stub_update_aggregate) - result = self.controller.update(self.req, "1", body=body) - self.assertEqual(AGGREGATE, result["aggregate"]) def test_update_with_no_updates(self): @@ -261,18 +259,6 @@ class AggregateTestCase(test.TestCase): self.req, "bogus_aggregate", body={"add_host": {"host": "host1"}}) - def test_add_host_with_host_in_wrong_availability_zone(self): - def stub_add_host_to_aggregate(context, aggregate, host): - raise exception.InvalidAggregateAction(action='create_aggregate', - aggregate_id="'N/A'", - reason='wrong zone') - self.stubs.Set(self.controller.api, "add_host_to_aggregate", - stub_add_host_to_aggregate) - - self.assertRaises(exc.HTTPConflict, self.controller.action, - self.req, "bogus_aggregate", - body={"add_host": {"host": "host1"}}) - def test_add_host_with_missing_host(self): self.assertRaises(exc.HTTPBadRequest, self.controller.action, self.req, "1", body={"asdf": "asdf"}) diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index dabb8bb89..823eeaf4e 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -5399,15 +5399,8 @@ class ComputeAPIAggrTestCase(BaseTestCase): self.stubs.Set(rpc, 'call', fake_rpc_method) self.stubs.Set(rpc, 'cast', fake_rpc_method) - 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): """Ensure metadata can be updated""" - _create_service_entries(self.context, {'fake_zone': ['fake_host']}) aggr = self.api.create_aggregate(self.context, 'fake_aggregate', 'fake_zone') metadata = {'foo_key1': 'foo_value1', @@ -5418,11 +5411,11 @@ class ComputeAPIAggrTestCase(BaseTestCase): expected = self.api.update_aggregate_metadata(self.context, aggr['id'], metadata) self.assertThat(expected['metadata'], - matchers.DictMatches({'foo_key2': 'foo_value2'})) + matchers.DictMatches({'availability_zone': 'fake_zone', + 'foo_key2': 'foo_value2'})) 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_zone') self.api.delete_aggregate(self.context, aggr['id']) @@ -5463,19 +5456,8 @@ class ComputeAPIAggrTestCase(BaseTestCase): aggr['id'], host) self.assertEqual(len(aggr['hosts']), len(values[fake_zone])) - 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_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_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_zone') self.assertRaises(exception.ComputeHostNotFound, @@ -5526,9 +5508,9 @@ class ComputeAggrTestCase(BaseTestCase): 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) + values = {'name': 'test_aggr'} + az = {'availability_zone': 'test_zone'} + self.aggr = db.aggregate_create(self.context, values, metadata=az) def test_add_aggregate_host(self): def fake_driver_add_to_aggregate(context, aggregate, host, **_ignore): diff --git a/nova/tests/conductor/test_conductor.py b/nova/tests/conductor/test_conductor.py index dcbafec9e..fd87e420b 100644 --- a/nova/tests/conductor/test_conductor.py +++ b/nova/tests/conductor/test_conductor.py @@ -129,7 +129,7 @@ class _BaseTestCase(object): def _setup_aggregate_with_host(self): aggregate_ref = db.aggregate_create(self.context.elevated(), - {'name': 'foo', 'availability_zone': 'foo'}) + {'name': 'foo'}, metadata={'availability_zone': 'foo'}) self.conductor.aggregate_host_add(self.context, aggregate_ref, 'bar') diff --git a/nova/tests/integrated/api_samples/os-aggregates/aggregate-update-post-resp.json.tpl b/nova/tests/integrated/api_samples/os-aggregates/aggregate-update-post-resp.json.tpl index 89a48ee57..119f78ad2 100644 --- a/nova/tests/integrated/api_samples/os-aggregates/aggregate-update-post-resp.json.tpl +++ b/nova/tests/integrated/api_samples/os-aggregates/aggregate-update-post-resp.json.tpl @@ -6,7 +6,9 @@ "deleted_at": null, "hosts": [], "id": 1, - "metadata": {}, + "metadata": { + "availability_zone": "nova2" + }, "name": "newname", "updated_at": "%(timestamp)s" } diff --git a/nova/tests/integrated/api_samples/os-aggregates/aggregate-update-post-resp.xml.tpl b/nova/tests/integrated/api_samples/os-aggregates/aggregate-update-post-resp.xml.tpl index 3f72a0b43..071e1c43a 100644 --- a/nova/tests/integrated/api_samples/os-aggregates/aggregate-update-post-resp.xml.tpl +++ b/nova/tests/integrated/api_samples/os-aggregates/aggregate-update-post-resp.xml.tpl @@ -8,5 +8,7 @@ None 1 - + + nova2 + diff --git a/nova/tests/integrated/api_samples/os-aggregates/aggregates-add-host-post-resp.json.tpl b/nova/tests/integrated/api_samples/os-aggregates/aggregates-add-host-post-resp.json.tpl index ee0ea6c3d..b311bb18e 100644 --- a/nova/tests/integrated/api_samples/os-aggregates/aggregates-add-host-post-resp.json.tpl +++ b/nova/tests/integrated/api_samples/os-aggregates/aggregates-add-host-post-resp.json.tpl @@ -8,7 +8,9 @@ "%(compute_host)s" ], "id": 1, - "metadata": {}, + "metadata": { + "availability_zone": "nova" + }, "name": "name", "updated_at": null } diff --git a/nova/tests/integrated/api_samples/os-aggregates/aggregates-add-host-post-resp.xml.tpl b/nova/tests/integrated/api_samples/os-aggregates/aggregates-add-host-post-resp.xml.tpl index 82a0401ad..a45a01789 100644 --- a/nova/tests/integrated/api_samples/os-aggregates/aggregates-add-host-post-resp.xml.tpl +++ b/nova/tests/integrated/api_samples/os-aggregates/aggregates-add-host-post-resp.xml.tpl @@ -10,5 +10,7 @@ None 1 - + + nova + diff --git a/nova/tests/integrated/api_samples/os-aggregates/aggregates-get-resp.json.tpl b/nova/tests/integrated/api_samples/os-aggregates/aggregates-get-resp.json.tpl index 8ce7d9c40..6b94465c4 100644 --- a/nova/tests/integrated/api_samples/os-aggregates/aggregates-get-resp.json.tpl +++ b/nova/tests/integrated/api_samples/os-aggregates/aggregates-get-resp.json.tpl @@ -6,7 +6,9 @@ "deleted_at": null, "hosts": [], "id": 1, - "metadata": {}, + "metadata": { + "availability_zone": "nova" + }, "name": "name", "updated_at": null } diff --git a/nova/tests/integrated/api_samples/os-aggregates/aggregates-get-resp.xml.tpl b/nova/tests/integrated/api_samples/os-aggregates/aggregates-get-resp.xml.tpl index 56f0dd3e8..d59d10a84 100644 --- a/nova/tests/integrated/api_samples/os-aggregates/aggregates-get-resp.xml.tpl +++ b/nova/tests/integrated/api_samples/os-aggregates/aggregates-get-resp.xml.tpl @@ -8,5 +8,7 @@ None 1 - + + nova + diff --git a/nova/tests/integrated/api_samples/os-aggregates/aggregates-list-get-resp.json.tpl b/nova/tests/integrated/api_samples/os-aggregates/aggregates-list-get-resp.json.tpl index f373f02f7..bed47e730 100644 --- a/nova/tests/integrated/api_samples/os-aggregates/aggregates-list-get-resp.json.tpl +++ b/nova/tests/integrated/api_samples/os-aggregates/aggregates-list-get-resp.json.tpl @@ -7,7 +7,9 @@ "deleted_at": null, "hosts": [], "id": 1, - "metadata": {}, + "metadata": { + "availability_zone": "nova" + }, "name": "name", "updated_at": null } diff --git a/nova/tests/integrated/api_samples/os-aggregates/aggregates-list-get-resp.xml.tpl b/nova/tests/integrated/api_samples/os-aggregates/aggregates-list-get-resp.xml.tpl index 417b1016f..0a6173a0b 100644 --- a/nova/tests/integrated/api_samples/os-aggregates/aggregates-list-get-resp.xml.tpl +++ b/nova/tests/integrated/api_samples/os-aggregates/aggregates-list-get-resp.xml.tpl @@ -9,6 +9,8 @@ None 1 - + + nova + diff --git a/nova/tests/integrated/api_samples/os-aggregates/aggregates-metadata-post-resp.json.tpl b/nova/tests/integrated/api_samples/os-aggregates/aggregates-metadata-post-resp.json.tpl index 058a1ecf5..f34932617 100644 --- a/nova/tests/integrated/api_samples/os-aggregates/aggregates-metadata-post-resp.json.tpl +++ b/nova/tests/integrated/api_samples/os-aggregates/aggregates-metadata-post-resp.json.tpl @@ -7,6 +7,7 @@ "hosts": [], "id": 1, "metadata": { + "availability_zone": "nova", "key": "value" }, "name": "name", diff --git a/nova/tests/integrated/api_samples/os-aggregates/aggregates-metadata-post-resp.xml.tpl b/nova/tests/integrated/api_samples/os-aggregates/aggregates-metadata-post-resp.xml.tpl index 9bbd1f0bd..5b229cfc9 100644 --- a/nova/tests/integrated/api_samples/os-aggregates/aggregates-metadata-post-resp.xml.tpl +++ b/nova/tests/integrated/api_samples/os-aggregates/aggregates-metadata-post-resp.xml.tpl @@ -10,5 +10,6 @@ 1 value + nova diff --git a/nova/tests/integrated/api_samples/os-aggregates/aggregates-remove-host-post-resp.json.tpl b/nova/tests/integrated/api_samples/os-aggregates/aggregates-remove-host-post-resp.json.tpl index 8ce7d9c40..6b94465c4 100644 --- a/nova/tests/integrated/api_samples/os-aggregates/aggregates-remove-host-post-resp.json.tpl +++ b/nova/tests/integrated/api_samples/os-aggregates/aggregates-remove-host-post-resp.json.tpl @@ -6,7 +6,9 @@ "deleted_at": null, "hosts": [], "id": 1, - "metadata": {}, + "metadata": { + "availability_zone": "nova" + }, "name": "name", "updated_at": null } diff --git a/nova/tests/integrated/api_samples/os-aggregates/aggregates-remove-host-post-resp.xml.tpl b/nova/tests/integrated/api_samples/os-aggregates/aggregates-remove-host-post-resp.xml.tpl index 56f0dd3e8..d59d10a84 100644 --- a/nova/tests/integrated/api_samples/os-aggregates/aggregates-remove-host-post-resp.xml.tpl +++ b/nova/tests/integrated/api_samples/os-aggregates/aggregates-remove-host-post-resp.xml.tpl @@ -8,5 +8,7 @@ None 1 - + + nova + diff --git a/nova/tests/scheduler/test_host_filters.py b/nova/tests/scheduler/test_host_filters.py index 07a1bc2b8..b08da6baa 100644 --- a/nova/tests/scheduler/test_host_filters.py +++ b/nova/tests/scheduler/test_host_filters.py @@ -748,8 +748,11 @@ class HostFiltersTestCase(test.TestCase): def _create_aggregate_with_host(self, name='fake_aggregate', metadata=None, hosts=['host1']): - values = {'name': name, - 'availability_zone': 'fake_avail_zone', } + values = {'name': name} + if metadata: + metadata['availability_zone'] = 'fake_avail_zone' + else: + metadata = {'availability_zone': 'fake_avail_zone'} result = db.aggregate_create(self.context.elevated(), values, metadata) for host in hosts: db.aggregate_host_add(self.context.elevated(), result['id'], host) diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py index a17113a42..1a4509011 100644 --- a/nova/tests/test_db_api.py +++ b/nova/tests/test_db_api.py @@ -765,13 +765,13 @@ class DbApiTestCase(test.TestCase): def _get_fake_aggr_values(): - return {'name': 'fake_aggregate', - 'availability_zone': 'fake_avail_zone', } + return {'name': 'fake_aggregate'} def _get_fake_aggr_metadata(): return {'fake_key1': 'fake_value1', - 'fake_key2': 'fake_value2'} + 'fake_key2': 'fake_value2', + 'availability_zone': 'fake_avail_zone'} def _get_fake_aggr_hosts(): @@ -802,28 +802,26 @@ class AggregateDBApiTestCase(test.TestCase): self.project_id = 'fake' self.context = context.RequestContext(self.user_id, self.project_id) - def test_aggregate_create(self): - """Ensure aggregate can be created with no metadata.""" + def test_aggregate_create_no_metadata(self): result = _create_aggregate(metadata=None) self.assertEquals(result['name'], 'fake_aggregate') def test_aggregate_create_avoid_name_conflict(self): - """Test we can avoid conflict on deleted aggregates.""" r1 = _create_aggregate(metadata=None) db.aggregate_delete(context.get_admin_context(), r1['id']) - values = {'name': r1['name'], 'availability_zone': 'new_zone'} - r2 = _create_aggregate(values=values) + values = {'name': r1['name']} + metadata = {'availability_zone': 'new_zone'} + r2 = _create_aggregate(values=values, metadata=metadata) self.assertEqual(r2['name'], values['name']) - self.assertEqual(r2['availability_zone'], values['availability_zone']) + self.assertEqual(r2['availability_zone'], + metadata['availability_zone']) def test_aggregate_create_raise_exist_exc(self): - """Ensure aggregate names are distinct.""" _create_aggregate(metadata=None) self.assertRaises(exception.AggregateNameExists, _create_aggregate, metadata=None) def test_aggregate_get_raise_not_found(self): - """Ensure AggregateNotFound is raised when getting an aggregate.""" ctxt = context.get_admin_context() # this does not exist! aggregate_id = 1 @@ -832,7 +830,6 @@ class AggregateDBApiTestCase(test.TestCase): ctxt, aggregate_id) def test_aggregate_metadata_get_raise_not_found(self): - """Ensure AggregateNotFound is raised when getting metadata.""" ctxt = context.get_admin_context() # this does not exist! aggregate_id = 1 @@ -841,7 +838,6 @@ class AggregateDBApiTestCase(test.TestCase): ctxt, aggregate_id) def test_aggregate_create_with_metadata(self): - """Ensure aggregate can be created with metadata.""" ctxt = context.get_admin_context() result = _create_aggregate(context=ctxt) expected_metadata = db.aggregate_metadata_get(ctxt, result['id']) @@ -849,25 +845,25 @@ class AggregateDBApiTestCase(test.TestCase): matchers.DictMatches(_get_fake_aggr_metadata())) def test_aggregate_create_delete_create_with_metadata(self): - """Ensure aggregate metadata is deleted bug 1052479.""" + #test for bug 1052479 ctxt = context.get_admin_context() result = _create_aggregate(context=ctxt) expected_metadata = db.aggregate_metadata_get(ctxt, result['id']) self.assertThat(expected_metadata, matchers.DictMatches(_get_fake_aggr_metadata())) db.aggregate_delete(ctxt, result['id']) - result = _create_aggregate(metadata=None) + result = _create_aggregate(metadata={'availability_zone': + 'fake_avail_zone'}) expected_metadata = db.aggregate_metadata_get(ctxt, result['id']) - self.assertEqual(expected_metadata, {}) + self.assertEqual(expected_metadata, {'availability_zone': + 'fake_avail_zone'}) def test_aggregate_create_low_privi_context(self): - """Ensure right context is applied when creating aggregate.""" self.assertRaises(exception.AdminRequired, 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']) @@ -875,20 +871,16 @@ class AggregateDBApiTestCase(test.TestCase): self.assertEqual(_get_fake_aggr_metadata(), expected['metadetails']) def test_aggregate_get_by_host(self): - """Ensure we can get aggregates by host.""" ctxt = context.get_admin_context() - values = {'name': 'fake_aggregate2', - 'availability_zone': 'fake_avail_zone', } + values = {'name': 'fake_aggregate2'} a1 = _create_aggregate_with_hosts(context=ctxt) a2 = _create_aggregate_with_hosts(context=ctxt, values=values) r1 = db.aggregate_get_by_host(ctxt, 'foo.openstack.org') self.assertEqual([a1['id'], a2['id']], [x['id'] for x in r1]) def test_aggregate_get_by_host_with_key(self): - """Ensure we can get aggregates by host.""" ctxt = context.get_admin_context() - values = {'name': 'fake_aggregate2', - 'availability_zone': 'fake_avail_zone', } + values = {'name': 'fake_aggregate2'} a1 = _create_aggregate_with_hosts(context=ctxt, metadata={'goodkey': 'good'}) a2 = _create_aggregate_with_hosts(context=ctxt, values=values) @@ -896,13 +888,10 @@ class AggregateDBApiTestCase(test.TestCase): r1 = db.aggregate_get_by_host(ctxt, 'foo.openstack.org', key='goodkey') self.assertEqual([a1['id']], [x['id'] for x in r1]) - def test_aggregate_metdata_get_by_host(self): - """Ensure we can get aggregates by host.""" + def test_aggregate_metadata_get_by_host(self): ctxt = context.get_admin_context() - values = {'name': 'fake_aggregate2', - 'availability_zone': 'fake_avail_zone', } - values2 = {'name': 'fake_aggregate3', - 'availability_zone': 'fake_avail_zone', } + values = {'name': 'fake_aggregate2'} + values2 = {'name': 'fake_aggregate3'} a1 = _create_aggregate_with_hosts(context=ctxt) a2 = _create_aggregate_with_hosts(context=ctxt, values=values) a3 = _create_aggregate_with_hosts(context=ctxt, values=values2, @@ -911,13 +900,10 @@ class AggregateDBApiTestCase(test.TestCase): self.assertEqual(r1['fake_key1'], set(['fake_value1'])) self.assertFalse('badkey' in r1) - def test_aggregate_metdata_get_by_host_with_key(self): - """Ensure we can get aggregates by host.""" + def test_aggregate_metadata_get_by_host_with_key(self): ctxt = context.get_admin_context() - values = {'name': 'fake_aggregate2', - 'availability_zone': 'fake_avail_zone', } - values2 = {'name': 'fake_aggregate3', - 'availability_zone': 'fake_avail_zone', } + values = {'name': 'fake_aggregate2'} + values2 = {'name': 'fake_aggregate3'} a1 = _create_aggregate_with_hosts(context=ctxt) a2 = _create_aggregate_with_hosts(context=ctxt, values=values) a3 = _create_aggregate_with_hosts(context=ctxt, values=values2, @@ -932,14 +918,24 @@ class AggregateDBApiTestCase(test.TestCase): key='good') self.assertFalse('good' in r2) + def test_aggregate_host_get_by_metadata_key(self): + ctxt = context.get_admin_context() + values = {'name': 'fake_aggregate2'} + values2 = {'name': 'fake_aggregate3'} + a1 = _create_aggregate_with_hosts(context=ctxt) + a2 = _create_aggregate_with_hosts(context=ctxt, values=values) + a3 = _create_aggregate_with_hosts(context=ctxt, values=values2, + hosts=['foo.openstack.org'], metadata={'good': 'value'}) + r1 = db.aggregate_host_get_by_metadata_key(ctxt, key='good') + self.assertEqual(r1, {'foo.openstack.org': set(['value'])}) + self.assertFalse('fake_key1' in r1) + def test_aggregate_get_by_host_not_found(self): - """Ensure AggregateHostNotFound is raised with unknown host.""" ctxt = context.get_admin_context() _create_aggregate_with_hosts(context=ctxt) self.assertEqual([], db.aggregate_get_by_host(ctxt, 'unknown_host')) def test_aggregate_delete_raise_not_found(self): - """Ensure AggregateNotFound is raised when deleting an aggregate.""" ctxt = context.get_admin_context() # this does not exist! aggregate_id = 1 @@ -948,7 +944,6 @@ class AggregateDBApiTestCase(test.TestCase): ctxt, aggregate_id) def test_aggregate_delete(self): - """Ensure we can delete an aggregate.""" ctxt = context.get_admin_context() result = _create_aggregate(context=ctxt, metadata=None) db.aggregate_delete(ctxt, result['id']) @@ -959,9 +954,10 @@ class AggregateDBApiTestCase(test.TestCase): self.assertEqual(aggregate['deleted'], True) def test_aggregate_update(self): - """Ensure an aggregate can be updated.""" ctxt = context.get_admin_context() - result = _create_aggregate(context=ctxt, metadata=None) + result = _create_aggregate(context=ctxt, metadata={'availability_zone': + 'fake_avail_zone'}) + self.assertEqual(result.availability_zone, 'fake_avail_zone') new_values = _get_fake_aggr_values() new_values['availability_zone'] = 'different_avail_zone' updated = db.aggregate_update(ctxt, 1, new_values) @@ -969,18 +965,20 @@ class AggregateDBApiTestCase(test.TestCase): updated['availability_zone']) def test_aggregate_update_with_metadata(self): - """Ensure an aggregate can be updated with metadata.""" ctxt = context.get_admin_context() result = _create_aggregate(context=ctxt, metadata=None) values = _get_fake_aggr_values() values['metadata'] = _get_fake_aggr_metadata() + values['availability_zone'] = 'different_avail_zone' db.aggregate_update(ctxt, 1, values) expected = db.aggregate_metadata_get(ctxt, result['id']) - self.assertThat(_get_fake_aggr_metadata(), + updated = db.aggregate_get(ctxt, result['id']) + self.assertThat(values['metadata'], matchers.DictMatches(expected)) + self.assertNotEqual(result.availability_zone, + updated.availability_zone) def test_aggregate_update_with_existing_metadata(self): - """Ensure an aggregate can be updated with existing metadata.""" ctxt = context.get_admin_context() result = _create_aggregate(context=ctxt) values = _get_fake_aggr_values() @@ -991,7 +989,6 @@ class AggregateDBApiTestCase(test.TestCase): self.assertThat(values['metadata'], matchers.DictMatches(expected)) def test_aggregate_update_raise_not_found(self): - """Ensure AggregateNotFound is raised when updating an aggregate.""" ctxt = context.get_admin_context() # this does not exist! aggregate_id = 1 @@ -1000,26 +997,22 @@ class AggregateDBApiTestCase(test.TestCase): db.aggregate_update, ctxt, aggregate_id, new_values) def test_aggregate_get_all(self): - """Ensure we can get all aggregates.""" ctxt = context.get_admin_context() counter = 3 for c in xrange(counter): _create_aggregate(context=ctxt, - values={'name': 'fake_aggregate_%d' % c, - 'availability_zone': 'fake_avail_zone'}, + values={'name': 'fake_aggregate_%d' % c}, metadata=None) results = db.aggregate_get_all(ctxt) self.assertEqual(len(results), counter) def test_aggregate_get_all_non_deleted(self): - """Ensure we get only non-deleted aggregates.""" ctxt = context.get_admin_context() add_counter = 5 remove_counter = 2 aggregates = [] for c in xrange(1, add_counter): - values = {'name': 'fake_aggregate_%d' % c, - 'availability_zone': 'fake_avail_zone'} + values = {'name': 'fake_aggregate_%d' % c} aggregates.append(_create_aggregate(context=ctxt, values=values, metadata=None)) for c in xrange(1, remove_counter): @@ -1028,7 +1021,6 @@ class AggregateDBApiTestCase(test.TestCase): self.assertEqual(len(results), add_counter - remove_counter) def test_aggregate_metadata_add(self): - """Ensure we can add metadata for the aggregate.""" ctxt = context.get_admin_context() result = _create_aggregate(context=ctxt, metadata=None) metadata = _get_fake_aggr_metadata() @@ -1037,7 +1029,6 @@ class AggregateDBApiTestCase(test.TestCase): self.assertThat(metadata, matchers.DictMatches(expected)) def test_aggregate_metadata_update(self): - """Ensure we can update metadata for the aggregate.""" ctxt = context.get_admin_context() result = _create_aggregate(context=ctxt) metadata = _get_fake_aggr_metadata() @@ -1050,7 +1041,6 @@ class AggregateDBApiTestCase(test.TestCase): self.assertThat(metadata, matchers.DictMatches(expected)) def test_aggregate_metadata_delete(self): - """Ensure we can delete metadata for the aggregate.""" ctxt = context.get_admin_context() result = _create_aggregate(context=ctxt, metadata=None) metadata = _get_fake_aggr_metadata() @@ -1060,8 +1050,17 @@ class AggregateDBApiTestCase(test.TestCase): del metadata[metadata.keys()[0]] self.assertThat(metadata, matchers.DictMatches(expected)) + def test_aggregate_remove_availability_zone(self): + ctxt = context.get_admin_context() + result = _create_aggregate(context=ctxt, metadata={'availability_zone': + 'fake_avail_zone'}) + db.aggregate_metadata_delete(ctxt, result.id, 'availability_zone') + expected = db.aggregate_metadata_get(ctxt, result.id) + aggregate = db.aggregate_get(ctxt, result.id) + self.assertEquals(aggregate.availability_zone, None) + self.assertThat({}, matchers.DictMatches(expected)) + def test_aggregate_metadata_delete_raise_not_found(self): - """Ensure AggregateMetadataNotFound is raised when deleting.""" ctxt = context.get_admin_context() result = _create_aggregate(context=ctxt) self.assertRaises(exception.AggregateMetadataNotFound, @@ -1069,14 +1068,12 @@ class AggregateDBApiTestCase(test.TestCase): ctxt, result['id'], 'foo_key') def test_aggregate_host_add(self): - """Ensure we can add host to the aggregate.""" ctxt = context.get_admin_context() result = _create_aggregate_with_hosts(context=ctxt, metadata=None) 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.""" + def test_aggregate_host_re_add(self): ctxt = context.get_admin_context() result = _create_aggregate_with_hosts(context=ctxt, metadata=None) host = _get_fake_aggr_hosts()[0] @@ -1086,19 +1083,16 @@ class AggregateDBApiTestCase(test.TestCase): self.assertEqual(len(expected), 1) def test_aggregate_host_add_duplicate_works(self): - """Ensure we can add host to distinct aggregates.""" ctxt = context.get_admin_context() r1 = _create_aggregate_with_hosts(context=ctxt, metadata=None) r2 = _create_aggregate_with_hosts(ctxt, - values={'name': 'fake_aggregate2', - 'availability_zone': 'fake_avail_zone2', }, - metadata=None) + values={'name': 'fake_aggregate2'}, + metadata={'availability_zone': 'fake_avail_zone2'}) h1 = db.aggregate_host_get_all(ctxt, r1['id']) h2 = db.aggregate_host_get_all(ctxt, r2['id']) self.assertEqual(h1, h2) def test_aggregate_host_add_duplicate_raise_exist_exc(self): - """Ensure we cannot add host to the same aggregate.""" ctxt = context.get_admin_context() result = _create_aggregate_with_hosts(context=ctxt, metadata=None) self.assertRaises(exception.AggregateHostExists, @@ -1106,7 +1100,6 @@ class AggregateDBApiTestCase(test.TestCase): ctxt, result['id'], _get_fake_aggr_hosts()[0]) def test_aggregate_host_add_raise_not_found(self): - """Ensure AggregateFound when adding a host.""" ctxt = context.get_admin_context() # this does not exist! aggregate_id = 1 @@ -1116,7 +1109,6 @@ class AggregateDBApiTestCase(test.TestCase): ctxt, aggregate_id, host) def test_aggregate_host_delete(self): - """Ensure we can add host to the aggregate.""" ctxt = context.get_admin_context() result = _create_aggregate_with_hosts(context=ctxt, metadata=None) db.aggregate_host_delete(ctxt, result['id'], @@ -1125,7 +1117,6 @@ class AggregateDBApiTestCase(test.TestCase): self.assertEqual(0, len(expected)) def test_aggregate_host_delete_raise_not_found(self): - """Ensure AggregateHostNotFound is raised when deleting a host.""" ctxt = context.get_admin_context() result = _create_aggregate(context=ctxt) self.assertRaises(exception.AggregateHostNotFound, diff --git a/nova/tests/test_migrations.py b/nova/tests/test_migrations.py index 125b2fe36..bcd858d96 100644 --- a/nova/tests/test_migrations.py +++ b/nova/tests/test_migrations.py @@ -297,3 +297,37 @@ class TestMigrations(test.TestCase): self.assertEqual(version, migration_api.db_version(engine, TestMigrations.REPOSITORY)) + + def test_migration_146(self): + name = 'name' + az = 'custom_az' + + def _145_check(): + agg = aggregates.select(aggregates.c.id == 1).execute().first() + self.assertEqual(name, agg.name) + self.assertEqual(az, agg.availability_zone) + + for key, engine in self.engines.items(): + migration_api.version_control(engine, TestMigrations.REPOSITORY, + migration.INIT_VERSION) + migration_api.upgrade(engine, TestMigrations.REPOSITORY, 145) + metadata = sqlalchemy.schema.MetaData() + metadata.bind = engine + aggregates = sqlalchemy.Table('aggregates', metadata, + autoload=True) + + aggregates.insert().values(id=1, availability_zone=az, + aggregate_name=1, name=name).execute() + + _145_check() + + migration_api.upgrade(engine, TestMigrations.REPOSITORY, 146) + + aggregate_metadata = sqlalchemy.Table('aggregate_metadata', + metadata, autoload=True) + metadata = aggregate_metadata.select(aggregate_metadata.c. + aggregate_id == 1).execute().first() + self.assertEqual(az, metadata['value']) + + migration_api.downgrade(engine, TestMigrations.REPOSITORY, 145) + _145_check() diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 8b57dfef4..64659a21f 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -2222,11 +2222,12 @@ class XenAPIAggregateTestCase(stubs.XenAPITestBase): self.compute = importutils.import_object(CONF.compute_manager) self.api = compute_api.AggregateAPI() values = {'name': 'test_aggr', - 'availability_zone': 'test_zone', - 'metadata': {pool_states.POOL_FLAG: 'XenAPI'}} + 'metadata': {'availability_zone': 'test_zone', + pool_states.POOL_FLAG: 'XenAPI'}} self.aggr = db.aggregate_create(self.context, values) self.fake_metadata = {pool_states.POOL_FLAG: 'XenAPI', 'master_compute': 'host', + 'availability_zone': 'fake_zone', pool_states.KEY: pool_states.ACTIVE, 'host': xenapi_fake.get_record('host', host_ref)['uuid']} @@ -2306,9 +2307,10 @@ class XenAPIAggregateTestCase(stubs.XenAPITestBase): self.conn._session.call_xenapi("pool.create", {"name": "asdf"}) values = {"name": 'fake_aggregate', - "availability_zone": 'fake_zone'} + 'metadata': {'availability_zone': 'fake_zone'}} result = db.aggregate_create(self.context, values) - metadata = {pool_states.POOL_FLAG: "XenAPI", + metadata = {'availability_zone': 'fake_zone', + pool_states.POOL_FLAG: "XenAPI", pool_states.KEY: pool_states.CREATED} db.aggregate_metadata_add(self.context, result['id'], metadata) @@ -2358,7 +2360,8 @@ class XenAPIAggregateTestCase(stubs.XenAPITestBase): 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.assertThat({pool_states.POOL_FLAG: 'XenAPI', + self.assertThat({'availability_zone': 'fake_zone', + pool_states.POOL_FLAG: 'XenAPI', pool_states.KEY: pool_states.ACTIVE}, matchers.DictMatches(result['metadetails'])) @@ -2375,9 +2378,9 @@ class XenAPIAggregateTestCase(stubs.XenAPITestBase): aggr_zone='fake_zone', aggr_state=pool_states.CREATED, hosts=['host'], metadata=None): - values = {"name": aggr_name, - "availability_zone": aggr_zone} - result = db.aggregate_create(self.context, values) + values = {"name": aggr_name} + result = db.aggregate_create(self.context, values, + metadata={'availability_zone': aggr_zone}) pool_flag = {pool_states.POOL_FLAG: "XenAPI", pool_states.KEY: aggr_state} db.aggregate_metadata_add(self.context, result['id'], pool_flag) -- cgit