diff --git a/nova/availability_zones.py b/nova/availability_zones.py index 216c4604114a..d627fa0d79d1 100644 --- a/nova/availability_zones.py +++ b/nova/availability_zones.py @@ -94,12 +94,16 @@ def get_host_availability_zone(context, host, conductor_api=None): return az -def get_availability_zones(context): - """Return available and unavailable zones.""" +def get_availability_zones(context, get_only_available=False): + """Return available and unavailable zones on demands. + + :param get_only_available: flag to determine whether to return + available zones only, default False indicates return both + available zones and not available zones, True indicates return + available zones only + """ enabled_services = db.service_get_all(context, False) - disabled_services = db.service_get_all(context, True) enabled_services = set_availability_zones(context, enabled_services) - disabled_services = set_availability_zones(context, disabled_services) available_zones = [] for zone in [service['availability_zone'] for service @@ -107,13 +111,18 @@ def get_availability_zones(context): if zone not in available_zones: available_zones.append(zone) - not_available_zones = [] - zones = [service['availability_zone'] for service in disabled_services - if service['availability_zone'] 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) + if not get_only_available: + disabled_services = db.service_get_all(context, True) + disabled_services = set_availability_zones(context, disabled_services) + not_available_zones = [] + zones = [service['availability_zone'] for service in disabled_services + if service['availability_zone'] 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) + else: + return available_zones def get_instance_availability_zone(context, instance): diff --git a/nova/compute/api.py b/nova/compute/api.py index 7230c8a9b8e8..bddd3c4a4e50 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -598,8 +598,8 @@ class API(base.Base): max_count, display_name, display_description, key_name, key_data, security_groups, - availability_zone, user_data, - metadata, injected_files, + availability_zone, forced_host, + user_data, metadata, injected_files, access_ip_v4, access_ip_v6, requested_networks, config_drive, block_device_mapping, @@ -612,6 +612,13 @@ class API(base.Base): msg = _('Cannot attach one or more volumes to multiple' ' instances') raise exception.InvalidRequest(msg) + if availability_zone: + available_zones = availability_zones.\ + get_availability_zones(context.elevated(), True) + if forced_host is None and availability_zone not in \ + available_zones: + msg = _('The requested availability zone is not available') + raise exception.InvalidRequest(msg) if instance_type['disabled']: raise exception.InstanceTypeNotFound( instance_type_id=instance_type['id']) @@ -799,9 +806,10 @@ class API(base.Base): instance_type, boot_meta, image_href, image_id, kernel_id, ramdisk_id, min_count, max_count, display_name, display_description, key_name, key_data, security_groups, - availability_zone, user_data, metadata, injected_files, - access_ip_v4, access_ip_v6, requested_networks, config_drive, - block_device_mapping, auto_disk_config, reservation_id) + availability_zone, forced_host, user_data, metadata, + injected_files, access_ip_v4, access_ip_v6, requested_networks, + config_drive, block_device_mapping, auto_disk_config, + reservation_id) instances = self._provision_instances(context, instance_type, min_count, max_count, base_options, boot_meta, security_groups, diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_availability_zone.py b/nova/tests/api/openstack/compute/plugins/v3/test_availability_zone.py index 3626d781bef1..4861674a4bce 100644 --- a/nova/tests/api/openstack/compute/plugins/v3/test_availability_zone.py +++ b/nova/tests/api/openstack/compute/plugins/v3/test_availability_zone.py @@ -447,6 +447,14 @@ class ServersControllerCreateTest(test.TestCase): req.method = 'POST' req.body = jsonutils.dumps(body) req.headers["content-type"] = "application/json" + admin_context = context.get_admin_context() + service1 = db.service_create(admin_context, {'host': 'host1_zones', + 'binary': "nova-compute", + 'topic': 'compute', + 'report_count': 0}) + agg = db.aggregate_create(admin_context, + {'name': 'agg1'}, {'availability_zone': 'nova'}) + db.aggregate_host_add(admin_context, agg['id'], 'host1_zones') res = self.controller.create(req, body).obj server = res['server'] self.assertEqual(FAKE_UUID, server['id']) diff --git a/nova/tests/api/openstack/compute/test_servers.py b/nova/tests/api/openstack/compute/test_servers.py index bde3a521ad19..86891853e18c 100644 --- a/nova/tests/api/openstack/compute/test_servers.py +++ b/nova/tests/api/openstack/compute/test_servers.py @@ -2716,6 +2716,20 @@ class ServersControllerCreateTest(test.TestCase): return old_create(*args, **kwargs) self.stubs.Set(compute_api.API, 'create', create) + + try: + self._test_create_extra(params) + except webob.exc.HTTPBadRequest as e: + expected = 'The requested availability zone is not available' + self.assertEquals(e.explanation, expected) + admin_context = context.get_admin_context() + service1 = db.service_create(admin_context, {'host': 'host1_zones', + 'binary': "nova-compute", + 'topic': 'compute', + 'report_count': 0}) + agg = db.aggregate_create(admin_context, + {'name': 'agg1'}, {'availability_zone': availability_zone}) + db.aggregate_host_add(admin_context, agg['id'], 'host1_zones') self._test_create_extra(params) def test_create_instance_with_availability_zone_disabled(self): diff --git a/nova/tests/test_availability_zones.py b/nova/tests/test_availability_zones.py index 0c58cd08aed0..7b29616ff7b0 100644 --- a/nova/tests/test_availability_zones.py +++ b/nova/tests/test_availability_zones.py @@ -157,8 +157,11 @@ class AvailabilityZoneTestCases(test.TestCase): def test_get_availability_zones(self): """Test get_availability_zones.""" - # get_availability_zones returns two lists, zones with at least one - # enabled services, and zones with no enabled services. + # When the param get_only_available of get_availability_zones is set + # to default False, it returns two lists, zones with at least one + # enabled services, and zones with no enabled services, + # when get_only_available is set to True, only return a list of zones + # with at least one enabled servies. # Use the following test data: # # zone host enabled @@ -192,6 +195,10 @@ class AvailabilityZoneTestCases(test.TestCase): self.assertEquals(zones, ['nova-test', 'nova-test2']) self.assertEquals(not_zones, ['nova-test3', 'nova']) + zones = az.get_availability_zones(self.context, True) + + self.assertEquals(zones, ['nova-test', 'nova-test2']) + def test_get_instance_availability_zone_default_value(self): """Test get right availability zone by given an instance.""" fake_inst_id = 162