diff options
author | Joe Gordon <jogo@cloudscaling.com> | 2012-08-02 17:44:18 -0700 |
---|---|---|
committer | Joe Gordon <jogo@cloudscaling.com> | 2013-01-08 14:00:41 -0800 |
commit | 9f4534ab584faeee1e24d4c1bb38a2b194f24626 (patch) | |
tree | 969f294fcb40434e4432bf4e8eaefc96ced17284 | |
parent | 61e9e0112bf2fd09892a5e1974d4f454ea2a4469 (diff) | |
download | nova-9f4534ab584faeee1e24d4c1bb38a2b194f24626.tar.gz nova-9f4534ab584faeee1e24d4c1bb38a2b194f24626.tar.xz nova-9f4534ab584faeee1e24d4c1bb38a2b194f24626.zip |
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
39 files changed, 355 insertions, 191 deletions
diff --git a/doc/api_samples/os-aggregates/aggregate-update-post-resp.json b/doc/api_samples/os-aggregates/aggregate-update-post-resp.json index 81869e730..6636f0a17 100644 --- a/doc/api_samples/os-aggregates/aggregate-update-post-resp.json +++ b/doc/api_samples/os-aggregates/aggregate-update-post-resp.json @@ -1,13 +1,15 @@ { "aggregate": { "availability_zone": "nova2", - "created_at": "2012-10-01T18:50:27.781065", + "created_at": "2012-12-04T12:04:27.075065", "deleted": false, "deleted_at": null, "hosts": [], "id": 1, - "metadata": {}, + "metadata": { + "availability_zone": "nova2" + }, "name": "newname", - "updated_at": "2012-10-01T18:50:27.791392" + "updated_at": "2012-12-04T12:04:27.242597" } }
\ No newline at end of file diff --git a/doc/api_samples/os-aggregates/aggregate-update-post-resp.xml b/doc/api_samples/os-aggregates/aggregate-update-post-resp.xml index ad9498aa0..25227669b 100644 --- a/doc/api_samples/os-aggregates/aggregate-update-post-resp.xml +++ b/doc/api_samples/os-aggregates/aggregate-update-post-resp.xml @@ -3,10 +3,12 @@ <name>newname</name> <availability_zone>nova2</availability_zone> <deleted>False</deleted> - <created_at>2012-10-01 18:50:35.506667</created_at> - <updated_at>2012-10-01 18:50:35.517397</updated_at> + <created_at>2012-12-04 12:04:30.245284</created_at> + <updated_at>2012-12-04 12:04:30.357795</updated_at> <hosts/> <deleted_at>None</deleted_at> <id>1</id> - <metadata/> + <metadata> + <availability_zone>nova2</availability_zone> + </metadata> </aggregate>
\ No newline at end of file diff --git a/doc/api_samples/os-aggregates/aggregates-add-host-post-resp.json b/doc/api_samples/os-aggregates/aggregates-add-host-post-resp.json index 518f4176a..1f7918ba8 100644 --- a/doc/api_samples/os-aggregates/aggregates-add-host-post-resp.json +++ b/doc/api_samples/os-aggregates/aggregates-add-host-post-resp.json @@ -1,14 +1,16 @@ { "aggregate": { "availability_zone": "nova", - "created_at": "2012-10-01T18:50:27.511586", + "created_at": "2012-12-04T12:04:24.399784", "deleted": false, "deleted_at": null, "hosts": [ - "581d29b9e3504d8a895caddb13839b15" + "0438c6a4e8d841ad823b801d681f4680" ], "id": 1, - "metadata": {}, + "metadata": { + "availability_zone": "nova" + }, "name": "name", "updated_at": null } diff --git a/doc/api_samples/os-aggregates/aggregates-add-host-post-resp.xml b/doc/api_samples/os-aggregates/aggregates-add-host-post-resp.xml index a4c9de5fd..ad11f3859 100644 --- a/doc/api_samples/os-aggregates/aggregates-add-host-post-resp.xml +++ b/doc/api_samples/os-aggregates/aggregates-add-host-post-resp.xml @@ -3,12 +3,14 @@ <name>name</name> <availability_zone>nova</availability_zone> <deleted>False</deleted> - <created_at>2012-10-01 18:50:35.236556</created_at> + <created_at>2012-12-04 12:04:27.574038</created_at> <updated_at>None</updated_at> <hosts> - <host>7c9e00dbca5e4fb88538b021c0f933a5</host> + <host>392adba19dd449179804eaff16ff4a97</host> </hosts> <deleted_at>None</deleted_at> <id>1</id> - <metadata/> + <metadata> + <availability_zone>nova</availability_zone> + </metadata> </aggregate>
\ No newline at end of file diff --git a/doc/api_samples/os-aggregates/aggregates-get-resp.json b/doc/api_samples/os-aggregates/aggregates-get-resp.json index cde446e51..101a6584d 100644 --- a/doc/api_samples/os-aggregates/aggregates-get-resp.json +++ b/doc/api_samples/os-aggregates/aggregates-get-resp.json @@ -1,13 +1,15 @@ { "aggregate": { "availability_zone": "nova", - "created_at": "2012-10-01T18:50:27.048605", + "created_at": "2012-11-16T06:22:23.032493", "deleted": false, "deleted_at": null, "hosts": [], "id": 1, - "metadata": {}, + "metadata": { + "availability_zone": "nova" + }, "name": "name", "updated_at": null } -}
\ No newline at end of file +} diff --git a/doc/api_samples/os-aggregates/aggregates-get-resp.xml b/doc/api_samples/os-aggregates/aggregates-get-resp.xml index be1349bd2..431e59cf4 100644 --- a/doc/api_samples/os-aggregates/aggregates-get-resp.xml +++ b/doc/api_samples/os-aggregates/aggregates-get-resp.xml @@ -3,10 +3,12 @@ <name>name</name> <availability_zone>nova</availability_zone> <deleted>False</deleted> - <created_at>2012-10-01 18:50:34.764838</created_at> + <created_at>2012-11-16 06:22:25.587739</created_at> <updated_at>None</updated_at> <hosts/> <deleted_at>None</deleted_at> <id>1</id> - <metadata/> -</aggregate>
\ No newline at end of file + <metadata> + <availability_zone>nova</availability_zone> + </metadata> +</aggregate> diff --git a/doc/api_samples/os-aggregates/aggregates-list-get-resp.json b/doc/api_samples/os-aggregates/aggregates-list-get-resp.json index 75b412b53..53d278c63 100644 --- a/doc/api_samples/os-aggregates/aggregates-list-get-resp.json +++ b/doc/api_samples/os-aggregates/aggregates-list-get-resp.json @@ -2,12 +2,14 @@ "aggregates": [ { "availability_zone": "nova", - "created_at": "2012-10-01T18:50:27.252869", + "created_at": "2012-11-16T06:22:23.361359", "deleted": false, "deleted_at": null, "hosts": [], "id": 1, - "metadata": {}, + "metadata": { + "availability_zone": "nova" + }, "name": "name", "updated_at": null } diff --git a/doc/api_samples/os-aggregates/aggregates-list-get-resp.xml b/doc/api_samples/os-aggregates/aggregates-list-get-resp.xml index c5590855b..8d92e1466 100644 --- a/doc/api_samples/os-aggregates/aggregates-list-get-resp.xml +++ b/doc/api_samples/os-aggregates/aggregates-list-get-resp.xml @@ -4,11 +4,13 @@ <name>name</name> <availability_zone>nova</availability_zone> <deleted>False</deleted> - <created_at>2012-10-01 18:50:34.970677</created_at> + <created_at>2012-11-16 06:22:25.935099</created_at> <updated_at>None</updated_at> <hosts/> <deleted_at>None</deleted_at> <id>1</id> - <metadata/> + <metadata> + <availability_zone>nova</availability_zone> + </metadata> </aggregate> -</aggregates>
\ No newline at end of file +</aggregates> diff --git a/doc/api_samples/os-aggregates/aggregates-metadata-post-resp.json b/doc/api_samples/os-aggregates/aggregates-metadata-post-resp.json index dc4806a4f..33b4702ef 100644 --- a/doc/api_samples/os-aggregates/aggregates-metadata-post-resp.json +++ b/doc/api_samples/os-aggregates/aggregates-metadata-post-resp.json @@ -1,12 +1,13 @@ { "aggregate": { "availability_zone": "nova", - "created_at": "2012-10-01T18:50:26.604176", + "created_at": "2012-11-16T06:22:22.342791", "deleted": false, "deleted_at": null, "hosts": [], "id": 1, "metadata": { + "availability_zone": "nova", "key": "value" }, "name": "name", diff --git a/doc/api_samples/os-aggregates/aggregates-metadata-post-resp.xml b/doc/api_samples/os-aggregates/aggregates-metadata-post-resp.xml index 7eeefb8b7..5e2193d06 100644 --- a/doc/api_samples/os-aggregates/aggregates-metadata-post-resp.xml +++ b/doc/api_samples/os-aggregates/aggregates-metadata-post-resp.xml @@ -3,12 +3,13 @@ <name>name</name> <availability_zone>nova</availability_zone> <deleted>False</deleted> - <created_at>2012-10-01 18:50:34.313003</created_at> + <created_at>2012-11-16 06:22:24.864471</created_at> <updated_at>None</updated_at> <hosts/> <deleted_at>None</deleted_at> <id>1</id> <metadata> <key>value</key> + <availability_zone>nova</availability_zone> </metadata> </aggregate>
\ No newline at end of file diff --git a/doc/api_samples/os-aggregates/aggregates-remove-host-post-resp.json b/doc/api_samples/os-aggregates/aggregates-remove-host-post-resp.json index 497fcb7fb..ba9d4e00a 100644 --- a/doc/api_samples/os-aggregates/aggregates-remove-host-post-resp.json +++ b/doc/api_samples/os-aggregates/aggregates-remove-host-post-resp.json @@ -1,12 +1,14 @@ { "aggregate": { "availability_zone": "nova", - "created_at": "2012-10-01T18:50:27.511586", + "created_at": "2012-12-04T12:04:26.557909", "deleted": false, "deleted_at": null, "hosts": [], "id": 1, - "metadata": {}, + "metadata": { + "availability_zone": "nova" + }, "name": "name", "updated_at": null } diff --git a/doc/api_samples/os-aggregates/aggregates-remove-host-post-resp.xml b/doc/api_samples/os-aggregates/aggregates-remove-host-post-resp.xml index dc8a55330..33dce2838 100644 --- a/doc/api_samples/os-aggregates/aggregates-remove-host-post-resp.xml +++ b/doc/api_samples/os-aggregates/aggregates-remove-host-post-resp.xml @@ -3,10 +3,12 @@ <name>name</name> <availability_zone>nova</availability_zone> <deleted>False</deleted> - <created_at>2012-10-01 18:50:35.236556</created_at> + <created_at>2012-12-04 12:04:29.722109</created_at> <updated_at>None</updated_at> <hosts/> <deleted_at>None</deleted_at> <id>1</id> - <metadata/> + <metadata> + <availability_zone>nova</availability_zone> + </metadata> </aggregate>
\ No newline at end of file diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index d40f25c4d..208574903 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -257,12 +257,18 @@ class CloudController(object): if not zone in available_zones: available_zones.append(zone) + # aggregate based availability_zones + metadata = db.aggregate_host_get_by_metadata_key(context, + key='availability_zone') + for zone_set in metadata.values(): + for zone in zone_set: + if zone not in available_zones: + available_zones.append(zone) not_available_zones = [] for zone in [service.availability_zone for service in disabled_services if not service['availability_zone'] in available_zones]: if not zone in not_available_zones: not_available_zones.append(zone) - return (available_zones, not_available_zones) def _describe_availability_zones(self, context, **kwargs): @@ -294,6 +300,15 @@ class CloudController(object): host_services.setdefault(service['host'], []) host_services[service['host']].append(service) + # aggregate based available_zones + metadata = db.aggregate_host_get_by_metadata_key(context, + key='availability_zone') + # metdata: {machine: set( az1, az2 )} + for host, zones in metadata.items(): + for zone in zones: + zone_hosts.setdefault(zone, []) + if host not in zone_hosts[zone]: + zone_hosts[zone].append(host) result = [] for zone in available_zones: diff --git a/nova/compute/api.py b/nova/compute/api.py index abbc0bd92..4ac04e790 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -2186,22 +2186,15 @@ class AggregateAPI(base.Base): def create_aggregate(self, context, aggregate_name, availability_zone): """Creates the model for the aggregate.""" - zones = [s['availability_zone'] for s in - self.db.service_get_all_by_topic(context, - CONF.compute_topic)] - if availability_zone in zones: - values = {"name": aggregate_name, - "availability_zone": availability_zone} - aggregate = self.db.aggregate_create(context, values) - aggregate = self._get_aggregate_info(context, aggregate) - # To maintain the same API result as before. - del aggregate['hosts'] - del aggregate['metadata'] - return aggregate - else: - raise exception.InvalidAggregateAction(action='create_aggregate', - aggregate_id="'N/A'", - reason='invalid zone') + + values = {"name": aggregate_name} + aggregate = self.db.aggregate_create(context, values, + metadata={'availability_zone': availability_zone}) + aggregate = self._get_aggregate_info(context, aggregate) + # To maintain the same API result as before. + del aggregate['hosts'] + del aggregate['metadata'] + return aggregate def get_aggregate(self, context, aggregate_id): """Get an aggregate by id.""" diff --git a/nova/db/api.py b/nova/db/api.py index 1322c29e9..8d93701c8 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1543,6 +1543,14 @@ def aggregate_metadata_get_by_host(context, host, key=None): return IMPL.aggregate_metadata_get_by_host(context, host, key) +def aggregate_host_get_by_metadata_key(context, key): + """Get hosts with a specific metadata key metadata for all aggregates. + + Returns a dictionary where each key is a hostname and each value is the + key value""" + return IMPL.aggregate_host_get_by_metadata_key(context, key) + + def aggregate_update(context, aggregate_id, values): """Update the attributes of an aggregates. If values contains a metadata key, it updates the aggregate metadata too.""" diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 57004be58..f89ebfaa3 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -4256,12 +4256,13 @@ def _aggregate_get_query(context, model_class, id_field=None, id=None, @require_admin_context def aggregate_create(context, values, metadata=None): session = get_session() - aggregate = _aggregate_get_query(context, - models.Aggregate, - models.Aggregate.name, - values['name'], - session=session, - read_deleted='no').first() + query = _aggregate_get_query(context, + models.Aggregate, + models.Aggregate.name, + values['name'], + session=session, + read_deleted='no') + aggregate = query.options(joinedload('_metadata')).first() if not aggregate: aggregate = models.Aggregate() aggregate.update(values) @@ -4274,15 +4275,16 @@ def aggregate_create(context, values, metadata=None): raise exception.AggregateNameExists(aggregate_name=values['name']) if metadata: aggregate_metadata_add(context, aggregate.id, metadata) - return aggregate + return aggregate_get(context, aggregate.id) @require_admin_context def aggregate_get(context, aggregate_id): - aggregate = _aggregate_get_query(context, - models.Aggregate, - models.Aggregate.id, - aggregate_id).first() + query = _aggregate_get_query(context, + models.Aggregate, + models.Aggregate.id, + aggregate_id) + aggregate = query.options(joinedload('_metadata')).first() if not aggregate: raise exception.AggregateNotFound(aggregate_id=aggregate_id) @@ -4314,18 +4316,38 @@ def aggregate_metadata_get_by_host(context, host, key=None): for agg in rows: for kv in agg._metadata: metadata[kv['key']].add(kv['value']) - return metadata + return dict(metadata) + + +@require_admin_context +def aggregate_host_get_by_metadata_key(context, key): + query = model_query(context, models.Aggregate).join( + "_metadata").filter(models.AggregateMetadata.key == key) + rows = query.all() + metadata = collections.defaultdict(set) + for agg in rows: + for agghost in agg._hosts: + metadata[agghost.host].add(agg._metadata[0]['value']) + return dict(metadata) @require_admin_context def aggregate_update(context, aggregate_id, values): session = get_session() - aggregate = _aggregate_get_query(context, + aggregate = (_aggregate_get_query(context, models.Aggregate, models.Aggregate.id, aggregate_id, - session=session).first() + session=session). + options(joinedload('_metadata')).first()) + if aggregate: + if "availability_zone" in values: + az = values.pop('availability_zone') + if 'metadata' not in values: + values['metadata'] = {'availability_zone': az} + else: + values['metadata']['availability_zone'] = az metadata = values.get('metadata') if metadata is not None: aggregate_metadata_add(context, @@ -4336,7 +4358,7 @@ def aggregate_update(context, aggregate_id, values): aggregate.update(values) aggregate.save(session=session) values['metadata'] = metadata - return aggregate + return aggregate_get(context, aggregate.id) else: raise exception.AggregateNotFound(aggregate_id=aggregate_id) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/146_aggregate_zones.py b/nova/db/sqlalchemy/migrate_repo/versions/146_aggregate_zones.py new file mode 100644 index 000000000..04f31ce5f --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/146_aggregate_zones.py @@ -0,0 +1,57 @@ +# Copyright 2012 OpenStack LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import String, Column, MetaData, Table, delete, select + +from nova.openstack.common import log as logging + +LOG = logging.getLogger(__name__) + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + aggregates = Table('aggregates', meta, autoload=True) + aggregate_metadata = Table('aggregate_metadata', meta, autoload=True) + # migrate data + record_list = list(aggregates.select().execute()) + for rec in record_list: + row = aggregate_metadata.insert() + row.execute({'created_at': rec['created_at'], + 'updated_at': rec['updated_at'], + 'deleted_at': rec['deleted_at'], + 'deleted': rec['deleted'], + 'key': 'availability_zone', + 'value': rec['availability_zone'], + 'aggregate_id': rec['id'], + }) + aggregates.drop_column('availability_zone') + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + aggregates = Table('aggregates', meta, autoload=True) + aggregate_metadata = Table('aggregate_metadata', meta, autoload=True) + availability_zone = Column('availability_zone', String(255)) + aggregates.create_column(availability_zone) + # migrate data + aggregates.update().values(availability_zone=select( + [aggregate_metadata.c.value]).where(aggregates.c.id == + aggregate_metadata.c.aggregate_id).where(aggregate_metadata.c.key == + 'availability_zone')).execute() + delete(aggregate_metadata, aggregate_metadata.c.key == 'availability_zone') + aggregates.c.availability_zone.alter(nullable=False) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index cdd140b6e..8a161efdf 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -866,7 +866,6 @@ class Aggregate(BASE, NovaBase): __tablename__ = 'aggregates' id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(255)) - availability_zone = Column(String(255), nullable=False) _hosts = relationship(AggregateHost, lazy="joined", secondary="aggregate_hosts", @@ -893,7 +892,7 @@ class Aggregate(BASE, NovaBase): backref='aggregates') def _extra_keys(self): - return ['hosts', 'metadetails'] + return ['hosts', 'metadetails', 'availability_zone'] @property def hosts(self): @@ -903,6 +902,12 @@ class Aggregate(BASE, NovaBase): def metadetails(self): return dict([(m.key, m.value) for m in self._metadata]) + @property + def availability_zone(self): + if 'availability_zone' not in self.metadetails: + return None + return self.metadetails['availability_zone'] + class AgentBuild(BASE, NovaBase): """Represents an agent build.""" diff --git a/nova/scheduler/filters/availability_zone_filter.py b/nova/scheduler/filters/availability_zone_filter.py index 4e55d0b0c..24ea0dd35 100644 --- a/nova/scheduler/filters/availability_zone_filter.py +++ b/nova/scheduler/filters/availability_zone_filter.py @@ -14,11 +14,17 @@ # under the License. +from nova import db from nova.scheduler import filters class AvailabilityZoneFilter(filters.BaseHostFilter): - """Filters Hosts by availability zone.""" + """Filters Hosts by availability zone. + + Works with both service and aggregate metadata. + For aggregate metadata uses the key 'availability_zone' + Note: in theory a compute node can be part of multiple availability_zones + """ def host_passes(self, host_state, filter_properties): spec = filter_properties.get('request_spec', {}) @@ -26,5 +32,12 @@ class AvailabilityZoneFilter(filters.BaseHostFilter): availability_zone = props.get('availability_zone') if availability_zone: - return availability_zone == host_state.service['availability_zone'] + if availability_zone == host_state.service['availability_zone']: + return True + context = filter_properties['context'].elevated() + metadata = db.aggregate_metadata_get_by_host( + context, host_state.host, key='availability_zone') + if 'availability_zone' in metadata: + return availability_zone in metadata['availability_zone'] + return False return True 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 @@ <hosts/> <deleted_at>None</deleted_at> <id>1</id> - <metadata/> + <metadata> + <availability_zone>nova2</availability_zone> + </metadata> </aggregate> 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 @@ </hosts> <deleted_at>None</deleted_at> <id>1</id> - <metadata/> + <metadata> + <availability_zone>nova</availability_zone> + </metadata> </aggregate> 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 @@ <hosts/> <deleted_at>None</deleted_at> <id>1</id> - <metadata/> + <metadata> + <availability_zone>nova</availability_zone> + </metadata> </aggregate> 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 @@ <hosts/> <deleted_at>None</deleted_at> <id>1</id> - <metadata/> + <metadata> + <availability_zone>nova</availability_zone> + </metadata> </aggregate> </aggregates> 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 @@ <id>1</id> <metadata> <key>value</key> + <availability_zone>nova</availability_zone> </metadata> </aggregate> 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 @@ <hosts/> <deleted_at>None</deleted_at> <id>1</id> - <metadata/> + <metadata> + <availability_zone>nova</availability_zone> + </metadata> </aggregate> 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) |