From 1ab2fc6477c402e29a95fbc93fe4a67950c083df Mon Sep 17 00:00:00 2001 From: Joe Gordon Date: Thu, 20 Dec 2012 03:13:01 +0000 Subject: Remove availability_zones from service table This is the final step in enabling availability_zones using aggregate metadata. Previously all services had an availability_zone, but the availability_zone is only used for nova-compute. Services such as nova-scheduler, nova-network, nova-conductor have always spanned all availability_zones. After this change only compute nodes (nova-compute), will have an availability_zone. In order to preserve current APIs, when running: * nova host-list (os-hosts) * euca-describe-availability-zones verbose * nova-manage service list Internal services will appear in there own internal availability_zone (CONF.internal_service_availability_zone) Internal zone is hidden in euca-describe-availability_zones (non-verbose) CONF.node_availability_zone has been renamed to CONF.default_availability_zone and is only used by the nova-api and nova-scheduler. CONF.node_availability_zone still works but is deprecated DocImpact Completes blueprint aggregate-based-availability-zones Change-Id: Ib772df5f9ac2865f20df479f8ddce575a9ce3aff --- bin/nova-manage | 3 + doc/api_samples/os-hosts/hosts-list-resp.json | 10 +-- nova/api/ec2/cloud.py | 43 ++++++----- nova/api/ec2/ec2utils.py | 3 +- nova/api/openstack/compute/contrib/hosts.py | 2 + nova/availability_zones.py | 62 ++++++++++++++++ nova/compute/api.py | 5 -- nova/config.py | 3 - nova/db/api.py | 6 +- .../migrate_repo/versions/147_no_service_zones.py | 83 ++++++++++++++++++++++ nova/db/sqlalchemy/models.py | 1 - nova/network/quantumv2/api.py | 5 +- nova/scheduler/filters/availability_zone_filter.py | 14 ++-- nova/service.py | 5 +- nova/servicegroup/db_driver.py | 3 - nova/tests/api/ec2/test_cloud.py | 36 ++++++---- .../api/openstack/compute/contrib/test_hosts.py | 14 ++-- nova/tests/compute/test_compute.py | 3 +- .../api_samples/os-hosts/hosts-list-resp.json.tpl | 8 +-- nova/tests/network/test_quantumv2.py | 1 + nova/tests/test_libvirt.py | 3 +- nova/tests/test_migrations.py | 58 +++++++++++++++ nova/tests/test_service.py | 4 +- nova/tests/test_xenapi.py | 9 ++- 24 files changed, 291 insertions(+), 93 deletions(-) create mode 100644 nova/availability_zones.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/147_no_service_zones.py diff --git a/bin/nova-manage b/bin/nova-manage index 45002cfd8..ba484a04e 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -70,6 +70,7 @@ if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'nova', '__init__.py')): gettext.install('nova', unicode=1) from nova.api.ec2 import ec2utils +from nova import availability_zones from nova.compute import instance_types from nova.compute import rpcapi as compute_rpcapi from nova import config @@ -626,6 +627,7 @@ class ServiceCommands(object): ctxt = context.get_admin_context() now = timeutils.utcnow() services = db.service_get_all(ctxt) + services = availability_zone.set_availability_zones(ctxt, services) if host: services = [s for s in services if s['host'] == host] if service: @@ -741,6 +743,7 @@ class HostCommands(object): ctxt = context.get_admin_context() now = timeutils.utcnow() services = db.service_get_all(ctxt) + services = availability_zones.set_availability_zones(ctxt, services) if zone: services = [s for s in services if s['availability_zone'] == zone] hosts = [] diff --git a/doc/api_samples/os-hosts/hosts-list-resp.json b/doc/api_samples/os-hosts/hosts-list-resp.json index d4146c082..5a963c602 100644 --- a/doc/api_samples/os-hosts/hosts-list-resp.json +++ b/doc/api_samples/os-hosts/hosts-list-resp.json @@ -8,22 +8,22 @@ { "host_name": "a98b433151084aee8b1a986e28823b36", "service": "cert", - "zone": "nova" + "zone": "internal" }, { "host_name": "c56158d13a884a87abf9171efb7de9d8", "service": "network", - "zone": "nova" + "zone": "internal" }, { "host_name": "81d5cdcda0014918b3ebd3503a2e5c9a", "service": "scheduler", - "zone": "nova" + "zone": "internal" }, { "host_name": "6e48bfe1a3304b7b86154326328750ae", "service": "conductor", - "zone": "nova" + "zone": "internal" } ] -} \ No newline at end of file +} diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 208574903..a7162a101 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -28,6 +28,7 @@ import time from nova.api.ec2 import ec2utils from nova.api.ec2 import inst_state from nova.api import validator +from nova import availability_zones from nova import block_device from nova import compute from nova.compute import api as compute_api @@ -72,6 +73,8 @@ CONF.register_opts(ec2_opts) CONF.import_opt('my_ip', 'nova.config') CONF.import_opt('vpn_image_id', 'nova.config') CONF.import_opt('vpn_key_suffix', 'nova.config') +CONF.import_opt('internal_service_availability_zone', + 'nova.availability_zones') LOG = logging.getLogger(__name__) @@ -250,6 +253,10 @@ class CloudController(object): """Return available and unavailable zones.""" enabled_services = db.service_get_all(context, False) disabled_services = db.service_get_all(context, True) + enabled_services = availability_zones.set_availability_zones(context, + enabled_services) + disabled_services = availability_zones.set_availability_zones(context, + disabled_services) available_zones = [] for zone in [service['availability_zone'] for service @@ -257,17 +264,11 @@ 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: + zones = [service['available_zones'] for service in disabled_services + if service['available_zones'] not in available_zones] + for zone in zones: + if zone not in not_available_zones: not_available_zones.append(zone) return (available_zones, not_available_zones) @@ -277,6 +278,9 @@ class CloudController(object): result = [] for zone in available_zones: + # Hide internal_service_availability_zone + if zone == CONF.internal_service_availability_zone: + continue result.append({'zoneName': zone, 'zoneState': "available"}) for zone in not_available_zones: @@ -290,6 +294,8 @@ class CloudController(object): # Available services enabled_services = db.service_get_all(context, False) + enabled_services = availability_zones.set_availability_zones(context, + enabled_services) zone_hosts = {} host_services = {} for service in enabled_services: @@ -298,17 +304,10 @@ class CloudController(object): zone_hosts[service['availability_zone']].append( service['host']) - 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) + host_services.setdefault(service['availability_zone'] + + service['host'], []) + host_services[service['availability_zone'] + service['host']].\ + append(service) result = [] for zone in available_zones: @@ -318,7 +317,7 @@ class CloudController(object): result.append({'zoneName': '|- %s' % host, 'zoneState': ''}) - for service in host_services[host]: + for service in host_services[zone + host]: alive = self.servicegroup_api.service_is_up(service) art = (alive and ":-)") or "XXX" active = 'enabled' diff --git a/nova/api/ec2/ec2utils.py b/nova/api/ec2/ec2utils.py index 1c2ceea6f..f86642ff6 100644 --- a/nova/api/ec2/ec2utils.py +++ b/nova/api/ec2/ec2utils.py @@ -18,6 +18,7 @@ import re +from nova import availability_zones from nova import context from nova import db from nova import exception @@ -116,7 +117,7 @@ def get_ip_info_for_instance(context, instance): def get_availability_zone_by_host(services, host): if len(services) > 0: - return services[0]['availability_zone'] + return availability_zones.get_host_availability_zone(context, host) return 'unknown zone' diff --git a/nova/api/openstack/compute/contrib/hosts.py b/nova/api/openstack/compute/contrib/hosts.py index 7da596a78..2401147f4 100644 --- a/nova/api/openstack/compute/contrib/hosts.py +++ b/nova/api/openstack/compute/contrib/hosts.py @@ -22,6 +22,7 @@ from xml.parsers import expat from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil +from nova import availability_zones from nova.compute import api as compute_api from nova import db from nova import exception @@ -99,6 +100,7 @@ def _list_hosts(req): """ context = req.environ['nova.context'] services = db.service_get_all(context, False) + services = availability_zones.set_availability_zones(context, services) zone = '' if 'zone' in req.GET: zone = req.GET['zone'] diff --git a/nova/availability_zones.py b/nova/availability_zones.py new file mode 100644 index 000000000..c08e029cf --- /dev/null +++ b/nova/availability_zones.py @@ -0,0 +1,62 @@ +# Copyright (c) 2012 OpenStack, LLC. +# All Rights Reserved. +# +# 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. + +""" utilities for multiple APIs""" + +from nova import db +from nova.openstack.common import cfg +from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging + +availability_zone_opts = [ + cfg.StrOpt('internal_service_availability_zone', + default='internal', + help='availability_zone to show internal services under'), + cfg.StrOpt('default_availability_zone', + # deprecated in Grizzly release + deprecated_name='node_availability_zone', + default='nova', + help='default compute node availability_zone'), + ] + +CONF = cfg.CONF +CONF.register_opts(availability_zone_opts) + +LOG = logging.getLogger(__name__) + + +def set_availability_zones(context, services): + # Makes sure services isn't a sqlalchemy object + services = [dict(service.iteritems()) for service in services] + metadata = db.aggregate_host_get_by_metadata_key(context, + key='availability_zone') + for service in services: + az = CONF.internal_service_availability_zone + if service['topic'] == "compute": + if metadata.get(service['host']): + az = str(metadata[service['host']])[5:-2] + else: + az = CONF.default_availability_zone + service['availability_zone'] = az + return services + + +def get_host_availability_zone(context, host): + metadata = db.aggregate_metadata_get_by_host( + context.get_admin_context(), host, key='availability_zone') + if 'availability_zone' in metadata: + return list(metadata['availability_zone'])[0] + else: + return CONF.default_availability_zone diff --git a/nova/compute/api.py b/nova/compute/api.py index 4ac04e790..c7a1d6126 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -2241,11 +2241,6 @@ class AggregateAPI(base.Base): # validates the host; ComputeHostNotFound is raised if invalid service = self.db.service_get_all_compute_by_host(context, host)[0] aggregate = self.db.aggregate_get(context, aggregate_id) - if service['availability_zone'] != aggregate['availability_zone']: - raise exception.InvalidAggregateAction( - action='add host', - aggregate_id=aggregate_id, - reason='availability zone mismatch') self.db.aggregate_host_add(context, aggregate_id, host) #NOTE(jogo): Send message to host to support resource pools self.compute_rpcapi.add_aggregate_host(context, diff --git a/nova/config.py b/nova/config.py index c39adfd45..e5d65db23 100644 --- a/nova/config.py +++ b/nova/config.py @@ -118,9 +118,6 @@ global_opts = [ 'However, the node name must be valid within ' 'an AMQP key, and if using ZeroMQ, a valid ' 'hostname, FQDN, or IP address'), - cfg.StrOpt('node_availability_zone', - default='nova', - help='availability zone of this node'), cfg.ListOpt('memcached_servers', default=None, help='Memcached servers or None for in process cache.'), diff --git a/nova/db/api.py b/nova/db/api.py index 8d93701c8..7f202862e 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1546,8 +1546,10 @@ def aggregate_metadata_get_by_host(context, host, key=None): 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""" + Returns a dictionary where each key is a hostname and each value is a set + of the key values + return value: {machine: set( az1, az2 )} + """ return IMPL.aggregate_host_get_by_metadata_key(context, key) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/147_no_service_zones.py b/nova/db/sqlalchemy/migrate_repo/versions/147_no_service_zones.py new file mode 100644 index 000000000..a20799fbe --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/147_no_service_zones.py @@ -0,0 +1,83 @@ +# 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, select + +from nova.openstack.common import log as logging + +LOG = logging.getLogger(__name__) + + +""" Remove availability_zone column from services model and replace with + aggregate based zone.""" + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + services = Table('services', meta, autoload=True) + aggregates = Table('aggregates', meta, autoload=True) + aggregate_metadata = Table('aggregate_metadata', meta, autoload=True) + # migrate data + record_list = list(services.select().execute()) + for rec in record_list: + # Only need to migrate nova-compute availability_zones + if rec['binary'] != 'nova-compute': + continue + # if zone doesn't exist create + result = aggregate_metadata.select().where(aggregate_metadata.c.key == + 'availability_zone' and + aggregate_metadata.c.key == rec['availability_zone']).execute() + result = [r for r in result] + if len(result) > 0: + agg_id = result[0].aggregate_id + else: + agg = aggregates.insert() + result = agg.execute({'name': rec['availability_zone']}) + agg_id = result.inserted_primary_key[0] + 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': agg_id, + }) + # add host to zone + agg_hosts = Table('aggregate_hosts', meta, autoload=True) + row = agg_hosts.insert() + row.execute({'host': rec['host'], 'aggregate_id': agg_id}) + + services.drop_column('availability_zone') + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + services = Table('services', meta, autoload=True) + aggregate_metadata = Table('aggregate_metadata', meta, autoload=True) + agg_hosts = Table('aggregate_hosts', meta, autoload=True) + availability_zone = Column('availability_zone', String(255), + default='nova') + services.create_column(availability_zone) + # migrate data + services.update().values(availability_zone=select( + [aggregate_metadata.c.value]). + where(agg_hosts.c.aggregate_id == aggregate_metadata.c.aggregate_id). + where(aggregate_metadata.c.key == 'availability_zone'). + where(agg_hosts.c.host == services.c.host). + where(services.c.binary == 'nova-compute')).execute() diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 8a161efdf..2d3e23c26 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -106,7 +106,6 @@ class Service(BASE, NovaBase): topic = Column(String(255)) report_count = Column(Integer, nullable=False, default=0) disabled = Column(Boolean, default=False) - availability_zone = Column(String(255), default='nova') class ComputeNode(BASE, NovaBase): diff --git a/nova/network/quantumv2/api.py b/nova/network/quantumv2/api.py index 88431b91d..0a4b24538 100644 --- a/nova/network/quantumv2/api.py +++ b/nova/network/quantumv2/api.py @@ -52,7 +52,6 @@ quantum_opts = [ CONF = cfg.CONF CONF.register_opts(quantum_opts) -CONF.import_opt('node_availability_zone', 'nova.config') CONF.import_opt('default_floating_pool', 'nova.network.manager') LOG = logging.getLogger(__name__) @@ -126,7 +125,7 @@ class API(base.Base): created_port_ids = [] for network in nets: network_id = network['id'] - zone = 'compute:%s' % CONF.node_availability_zone + zone = 'compute:%s' % instance['availability_zone'] port_req_body = {'port': {'device_id': instance['uuid'], 'device_owner': zone}} try: @@ -287,7 +286,7 @@ class API(base.Base): def _get_port_id_by_fixed_address(self, client, instance, address): - zone = 'compute:%s' % CONF.node_availability_zone + zone = 'compute:%s' % instance['availability_zone'] search_opts = {'device_id': instance['uuid'], 'device_owner': zone} data = client.list_ports(**search_opts) diff --git a/nova/scheduler/filters/availability_zone_filter.py b/nova/scheduler/filters/availability_zone_filter.py index 24ea0dd35..585acbaf8 100644 --- a/nova/scheduler/filters/availability_zone_filter.py +++ b/nova/scheduler/filters/availability_zone_filter.py @@ -14,15 +14,21 @@ # under the License. +from nova import availability_zones from nova import db +from nova.openstack.common import cfg from nova.scheduler import filters +CONF = cfg.CONF +CONF.import_opt('default_availability_zone', 'nova.availability_zones') + + class AvailabilityZoneFilter(filters.BaseHostFilter): """Filters Hosts by availability zone. - Works with both service and aggregate metadata. - For aggregate metadata uses the key 'availability_zone' + Works with aggregate metadata availability zones, using the key + 'availability_zone' Note: in theory a compute node can be part of multiple availability_zones """ @@ -32,12 +38,12 @@ class AvailabilityZoneFilter(filters.BaseHostFilter): availability_zone = props.get('availability_zone') if 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'] + else: + return availability_zone == CONF.default_availability_zone return False return True diff --git a/nova/service.py b/nova/service.py index fc0ac4a1b..43619fd56 100644 --- a/nova/service.py +++ b/nova/service.py @@ -92,7 +92,6 @@ service_opts = [ CONF = cfg.CONF CONF.register_opts(service_opts) CONF.import_opt('host', 'nova.config') -CONF.import_opt('node_availability_zone', 'nova.config') class SignalExit(SystemExit): @@ -447,13 +446,11 @@ class Service(object): self.timers.append(periodic) def _create_service_ref(self, context): - zone = CONF.node_availability_zone service_ref = db.service_create(context, {'host': self.host, 'binary': self.binary, 'topic': self.topic, - 'report_count': 0, - 'availability_zone': zone}) + 'report_count': 0}) self.service_id = service_ref['id'] def __getattr__(self, key): diff --git a/nova/servicegroup/db_driver.py b/nova/servicegroup/db_driver.py index a52ed258c..f859f9f8b 100644 --- a/nova/servicegroup/db_driver.py +++ b/nova/servicegroup/db_driver.py @@ -72,7 +72,6 @@ class DbDriver(api.ServiceGroupDriver): def _report_state(self, service): """Update the state of this service in the datastore.""" ctxt = context.get_admin_context() - zone = CONF.node_availability_zone state_catalog = {} try: try: @@ -84,8 +83,6 @@ class DbDriver(api.ServiceGroupDriver): service_ref = db.service_get(ctxt, service.service_id) state_catalog['report_count'] = service_ref['report_count'] + 1 - if zone != service_ref['availability_zone']: - state_catalog['availability_zone'] = zone db.service_update(ctxt, service.service_id, state_catalog) diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py index 429746dac..e0470689a 100644 --- a/nova/tests/api/ec2/test_cloud.py +++ b/nova/tests/api/ec2/test_cloud.py @@ -703,23 +703,24 @@ class CloudTestCase(test.TestCase): service1 = db.service_create(self.context, {'host': 'host1_zones', 'binary': "nova-compute", 'topic': 'compute', - 'report_count': 0, - 'availability_zone': "zone1"}) + 'report_count': 0}) service2 = db.service_create(self.context, {'host': 'host2_zones', 'binary': "nova-compute", 'topic': 'compute', - 'report_count': 0, - 'availability_zone': "zone2"}) + 'report_count': 0}) # Aggregate based zones agg = db.aggregate_create(self.context, - {'name': 'agg1'}, {'availability_zone': 'aggzones'}) + {'name': 'agg1'}, {'availability_zone': 'zone1'}) + db.aggregate_host_add(self.context, agg.id, 'host1_zones') + agg = db.aggregate_create(self.context, + {'name': 'agg2'}, {'availability_zone': 'zone2'}) db.aggregate_host_add(self.context, agg.id, 'host2_zones') result = self.cloud.describe_availability_zones(self.context) - self.assertEqual(len(result['availabilityZoneInfo']), 4) + self.assertEqual(len(result['availabilityZoneInfo']), 3) 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) + self.assertEqual(len(result['availabilityZoneInfo']), 16) db.service_destroy(self.context, service1['id']) db.service_destroy(self.context, service2['id']) @@ -728,13 +729,14 @@ class CloudTestCase(test.TestCase): service1 = db.service_create(self.context, {'host': 'host1_zones', 'binary': "nova-compute", 'topic': 'compute', - 'report_count': 0, - 'availability_zone': "zone1"}) + 'report_count': 0}) service2 = db.service_create(self.context, {'host': 'host2_zones', 'binary': "nova-compute", 'topic': 'compute', - 'report_count': 0, - 'availability_zone': "zone2"}) + 'report_count': 0}) + agg = db.aggregate_create(self.context, + {'name': 'agg1'}, {'availability_zone': 'second_zone'}) + db.aggregate_host_add(self.context, agg.id, 'host2_zones') admin_ctxt = context.get_admin_context(read_deleted="no") result = self.cloud.describe_availability_zones(admin_ctxt, @@ -765,11 +767,17 @@ class CloudTestCase(test.TestCase): 'hostname': 'server-4321', 'vm_state': 'active'}) comp1 = db.service_create(self.context, {'host': 'host1', - 'availability_zone': 'zone1', 'topic': "compute"}) + agg = db.aggregate_create(self.context, + {'name': 'agg1'}, {'availability_zone': 'zone1'}) + db.aggregate_host_add(self.context, agg.id, 'host1') + comp2 = db.service_create(self.context, {'host': 'host2', - 'availability_zone': 'zone2', 'topic': "compute"}) + agg2 = db.aggregate_create(self.context, + {'name': 'agg2'}, {'availability_zone': 'zone2'}) + db.aggregate_host_add(self.context, agg2.id, 'host2') + result = self.cloud.describe_instances(self.context) result = result['reservationSet'][0] self.assertEqual(len(result['instancesSet']), 2) @@ -852,11 +860,9 @@ class CloudTestCase(test.TestCase): inst3 = db.instance_create(self.context, inst3_kwargs) comp1 = db.service_create(self.context, {'host': 'host1', - 'availability_zone': 'zone1', 'topic': "compute"}) comp2 = db.service_create(self.context, {'host': 'host2', - 'availability_zone': 'zone2', 'topic': "compute"}) result = self.cloud.describe_instances(self.context) diff --git a/nova/tests/api/openstack/compute/contrib/test_hosts.py b/nova/tests/api/openstack/compute/contrib/test_hosts.py index 8469c7eba..71eae6f81 100644 --- a/nova/tests/api/openstack/compute/contrib/test_hosts.py +++ b/nova/tests/api/openstack/compute/contrib/test_hosts.py @@ -27,18 +27,14 @@ from nova import test LOG = logging.getLogger(__name__) HOST_LIST = {"hosts": [ {"host_name": "host_c1", "service": "compute", "zone": "nova"}, - {"host_name": "host_c2", "service": "compute", "zone": "nonova"}, - {"host_name": "host_v1", "service": "volume", "zone": "nova"}, - {"host_name": "host_v2", "service": "volume", "zone": "nonova"}] + {"host_name": "host_c2", "service": "compute", "zone": "nova"}] } HOST_LIST_NOVA_ZONE = [ {"host_name": "host_c1", "service": "compute", "zone": "nova"}, - {"host_name": "host_v1", "service": "volume", "zone": "nova"}] + {"host_name": "host_c2", "service": "compute", "zone": "nova"}] SERVICES_LIST = [ - {"host": "host_c1", "topic": "compute", "availability_zone": "nova"}, - {"host": "host_c2", "topic": "compute", "availability_zone": "nonova"}, - {"host": "host_v1", "topic": "volume", "availability_zone": "nova"}, - {"host": "host_v2", "topic": "volume", "availability_zone": "nonova"}] + {"host": "host_c1", "topic": "compute"}, + {"host": "host_c2", "topic": "compute"}] def stub_service_get_all(self, req): @@ -250,7 +246,7 @@ class HostTestCase(test.TestCase): """Create compute-manager(ComputeNode and Service record).""" ctxt = context.get_admin_context() dic = {'host': 'dummy', 'binary': 'nova-compute', 'topic': 'compute', - 'report_count': 0, 'availability_zone': 'dummyzone'} + 'report_count': 0} s_ref = db.service_create(ctxt, dic) dic = {'service_id': s_ref['id'], diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index 823eeaf4e..f6889ff2c 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -5383,8 +5383,7 @@ def _create_service_entries(context, values={'avail_zone1': ['fake_host1', {'host': host, 'binary': 'nova-compute', 'topic': 'compute', - 'report_count': 0, - 'availability_zone': avail_zone}) + 'report_count': 0}) return values diff --git a/nova/tests/integrated/api_samples/os-hosts/hosts-list-resp.json.tpl b/nova/tests/integrated/api_samples/os-hosts/hosts-list-resp.json.tpl index 555901b2e..eeb191597 100644 --- a/nova/tests/integrated/api_samples/os-hosts/hosts-list-resp.json.tpl +++ b/nova/tests/integrated/api_samples/os-hosts/hosts-list-resp.json.tpl @@ -8,22 +8,22 @@ { "host_name": "%(host_name)s", "service": "cert", - "zone": "nova" + "zone": "internal" }, { "host_name": "%(host_name)s", "service": "network", - "zone": "nova" + "zone": "internal" }, { "host_name": "%(host_name)s", "service": "scheduler", - "zone": "nova" + "zone": "internal" }, { "host_name": "%(host_name)s", "service": "conductor", - "zone": "nova" + "zone": "internal" } ] } diff --git a/nova/tests/network/test_quantumv2.py b/nova/tests/network/test_quantumv2.py index 1eab23a03..622365c76 100644 --- a/nova/tests/network/test_quantumv2.py +++ b/nova/tests/network/test_quantumv2.py @@ -137,6 +137,7 @@ class TestQuantumv2(test.TestCase): self.instance = {'project_id': '9d049e4b60b64716978ab415e6fbd5c0', 'uuid': str(uuid.uuid4()), 'display_name': 'test_instance', + 'availability_zone': 'nova', 'security_groups': []} self.nets1 = [{'id': 'my_netid1', 'name': 'my_netname1', diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 6bc18251f..7443d5289 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -628,8 +628,7 @@ class LibvirtConnTestCase(test.TestCase): service_ref = {'host': kwargs.get('host', 'dummy'), 'binary': 'nova-compute', 'topic': 'compute', - 'report_count': 0, - 'availability_zone': 'zone'} + 'report_count': 0} return db.service_create(context.get_admin_context(), service_ref) diff --git a/nova/tests/test_migrations.py b/nova/tests/test_migrations.py index bcd858d96..9badfb61a 100644 --- a/nova/tests/test_migrations.py +++ b/nova/tests/test_migrations.py @@ -331,3 +331,61 @@ class TestMigrations(test.TestCase): migration_api.downgrade(engine, TestMigrations.REPOSITORY, 145) _145_check() + + def test_migration_147(self): + az = 'test_zone' + host1 = 'compute-host1' + host2 = 'compute-host2' + + def _146_check(): + service = services.select(services.c.id == 1).execute().first() + self.assertEqual(az, service.availability_zone) + self.assertEqual(host1, service.host) + service = services.select(services.c.id == 2).execute().first() + self.assertNotEqual(az, service.availability_zone) + service = services.select(services.c.id == 3).execute().first() + self.assertEqual(az, service.availability_zone) + self.assertEqual(host2, service.host) + + for key, engine in self.engines.items(): + migration_api.version_control(engine, TestMigrations.REPOSITORY, + migration.INIT_VERSION) + migration_api.upgrade(engine, TestMigrations.REPOSITORY, 146) + metadata = sqlalchemy.schema.MetaData() + metadata.bind = engine + + #populate service table + services = sqlalchemy.Table('services', metadata, + autoload=True) + services.insert().values(id=1, host=host1, + binary='nova-compute', topic='compute', report_count=0, + availability_zone=az).execute() + services.insert().values(id=2, host='sched-host', + binary='nova-scheduler', topic='scheduler', report_count=0, + availability_zone='ignore_me').execute() + services.insert().values(id=3, host=host2, + binary='nova-compute', topic='compute', report_count=0, + availability_zone=az).execute() + + _146_check() + + migration_api.upgrade(engine, TestMigrations.REPOSITORY, 147) + + # check aggregate metadata + aggregate_metadata = sqlalchemy.Table('aggregate_metadata', + metadata, autoload=True) + aggregate_hosts = sqlalchemy.Table('aggregate_hosts', + metadata, autoload=True) + metadata = aggregate_metadata.select(aggregate_metadata.c. + aggregate_id == 1).execute().first() + self.assertEqual(az, metadata['value']) + self.assertEqual(aggregate_hosts.select( + aggregate_hosts.c.aggregate_id == 1).execute(). + first().host, host1) + blank = [h for h in aggregate_hosts.select( + aggregate_hosts.c.aggregate_id == 2).execute()] + self.assertEqual(blank, []) + + migration_api.downgrade(engine, TestMigrations.REPOSITORY, 146) + + _146_check() diff --git a/nova/tests/test_service.py b/nova/tests/test_service.py index 211a91e45..4c834e9c9 100644 --- a/nova/tests/test_service.py +++ b/nova/tests/test_service.py @@ -127,13 +127,11 @@ class ServiceTestCase(test.TestCase): service_create = {'host': self.host, 'binary': self.binary, 'topic': self.topic, - 'report_count': 0, - 'availability_zone': 'nova'} + 'report_count': 0} service_ref = {'host': self.host, 'binary': self.binary, 'topic': self.topic, 'report_count': 0, - 'availability_zone': 'nova', 'id': 1} service.db.service_get_by_args(mox.IgnoreArg(), diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 64659a21f..897bccd39 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -61,7 +61,7 @@ CONF.import_opt('compute_manager', 'nova.config') CONF.import_opt('compute_driver', 'nova.virt.driver') CONF.import_opt('host', 'nova.config') CONF.import_opt('network_manager', 'nova.config') -CONF.import_opt('node_availability_zone', 'nova.config') +CONF.import_opt('default_availability_zone', 'nova.availability_zones') IMAGE_MACHINE = '1' IMAGE_KERNEL = '2' @@ -206,7 +206,7 @@ class XenAPIVolumeTestCase(stubs.XenAPITestBase): vol['user_id'] = 'fake' vol['project_id'] = 'fake' vol['host'] = 'localhost' - vol['availability_zone'] = CONF.node_availability_zone + vol['availability_zone'] = CONF.default_availability_zone vol['status'] = "creating" vol['attach_status'] = "detached" return db.volume_create(self.context, vol) @@ -2196,8 +2196,7 @@ def _create_service_entries(context, values={'avail_zone1': ['fake_host1', {'host': host, 'binary': 'nova-compute', 'topic': 'compute', - 'report_count': 0, - 'availability_zone': avail_zone}) + 'report_count': 0}) return values @@ -2213,7 +2212,7 @@ class XenAPIAggregateTestCase(stubs.XenAPITestBase): 'Dom0IptablesFirewallDriver', host='host', compute_driver='xenapi.XenAPIDriver', - node_availability_zone='avail_zone1') + default_availability_zone='avail_zone1') self.flags(use_local=True, group='conductor') host_ref = xenapi_fake.get_all('host')[0] stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) -- cgit