diff --git a/nova/api/openstack/compute/extended_server_attributes.py b/nova/api/openstack/compute/extended_server_attributes.py deleted file mode 100644 index 6f2c7f92d7c5..000000000000 --- a/nova/api/openstack/compute/extended_server_attributes.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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. - -"""The Extended Server Attributes API extension.""" - -from nova.api.openstack import api_version_request -from nova.api.openstack import wsgi -from nova import compute -from nova.policies import extended_server_attributes as esa_policies -from nova.policies import servers as servers_policies - - -class ExtendedServerAttributesController(wsgi.Controller): - def __init__(self, *args, **kwargs): - super(ExtendedServerAttributesController, self).__init__(*args, - **kwargs) - self.compute_api = compute.API() - - def _extend_server(self, context, server, instance, req): - key = "OS-EXT-SRV-ATTR:hypervisor_hostname" - server[key] = instance.node - - properties = ['host', 'name'] - if api_version_request.is_supported(req, min_version='2.3'): - # NOTE(mriedem): These will use the OS-EXT-SRV-ATTR prefix below - # and that's OK for microversion 2.3 which is being compatible - # with v2.0 for the ec2 API split out from Nova. After this, - # however, new microversions should not be using the - # OS-EXT-SRV-ATTR prefix. - properties += ['reservation_id', 'launch_index', - 'hostname', 'kernel_id', 'ramdisk_id', - 'root_device_name', 'user_data'] - for attr in properties: - if attr == 'name': - key = "OS-EXT-SRV-ATTR:instance_%s" % attr - else: - # NOTE(mriedem): Nothing after microversion 2.3 should use the - # OS-EXT-SRV-ATTR prefix for the attribute key name. - key = "OS-EXT-SRV-ATTR:%s" % attr - server[key] = instance[attr] - - def _server_host_status(self, context, server, instance, req): - host_status = self.compute_api.get_instance_host_status(instance) - server['host_status'] = host_status - - @wsgi.extends - def show(self, req, resp_obj, id): - context = req.environ['nova.context'] - authorize_extend = False - authorize_host_status = False - if context.can(esa_policies.BASE_POLICY_NAME, fatal=False): - authorize_extend = True - if (api_version_request.is_supported(req, min_version='2.16') and - context.can(servers_policies.SERVERS % 'show:host_status', - fatal=False)): - authorize_host_status = True - if authorize_extend or authorize_host_status: - server = resp_obj.obj['server'] - db_instance = req.get_db_instance(server['id']) - # server['id'] is guaranteed to be in the cache due to - # the core API adding it in its 'show' method. - if authorize_extend: - self._extend_server(context, server, db_instance, req) - if authorize_host_status: - self._server_host_status(context, server, db_instance, req) - - @wsgi.extends - def detail(self, req, resp_obj): - context = req.environ['nova.context'] - authorize_extend = False - authorize_host_status = False - if context.can(esa_policies.BASE_POLICY_NAME, fatal=False): - authorize_extend = True - if (api_version_request.is_supported(req, min_version='2.16') and - context.can(servers_policies.SERVERS % 'show:host_status', - fatal=False)): - authorize_host_status = True - if authorize_extend or authorize_host_status: - servers = list(resp_obj.obj['servers']) - # NOTE(dinesh-bhor): Skipping fetching of instances from cache as - # servers list can be empty if invalid status is provided to the - # core API 'detail' method. - if servers: - instances = req.get_db_instances() - if authorize_host_status: - host_statuses = ( - self.compute_api.get_instances_host_statuses( - instances.values())) - for server in servers: - if authorize_extend: - instance = instances[server['id']] - self._extend_server(context, server, instance, req) - if authorize_host_status: - server['host_status'] = host_statuses[server['id']] diff --git a/nova/api/openstack/compute/routes.py b/nova/api/openstack/compute/routes.py index eeefe6b6efa2..ee02d4c67c81 100644 --- a/nova/api/openstack/compute/routes.py +++ b/nova/api/openstack/compute/routes.py @@ -34,7 +34,6 @@ from nova.api.openstack.compute import consoles from nova.api.openstack.compute import create_backup from nova.api.openstack.compute import deferred_delete from nova.api.openstack.compute import evacuate -from nova.api.openstack.compute import extended_server_attributes from nova.api.openstack.compute import extended_status from nova.api.openstack.compute import extended_volumes from nova.api.openstack.compute import extension_info @@ -267,7 +266,6 @@ security_group_rules_controller = functools.partial(_create_controller, server_controller = functools.partial(_create_controller, servers.ServersController, [ - extended_server_attributes.ExtendedServerAttributesController, extended_status.ExtendedStatusController, extended_volumes.ExtendedVolumesController, hide_server_addresses.Controller, diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index f34eafa077fe..889b0f50d2c1 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -719,7 +719,9 @@ class ServersController(wsgi.Controller): return self._view_builder.show(req, instance, extend_address=False, show_AZ=False, - show_config_drive=False) + show_config_drive=False, + show_extended_attr=False, + show_host_status=False) except exception.InstanceNotFound: msg = _("Instance could not be found") raise exc.HTTPNotFound(explanation=msg) @@ -985,7 +987,9 @@ class ServersController(wsgi.Controller): view = self._view_builder.show(req, instance, extend_address=False, show_AZ=False, - show_config_drive=False) + show_config_drive=False, + show_extended_attr=False, + show_host_status=False) # Add on the admin_password attribute since the view doesn't do it # unless instance passwords are disabled diff --git a/nova/api/openstack/compute/views/servers.py b/nova/api/openstack/compute/views/servers.py index 9578f83ad73e..024bda78d908 100644 --- a/nova/api/openstack/compute/views/servers.py +++ b/nova/api/openstack/compute/views/servers.py @@ -22,10 +22,13 @@ from nova.api.openstack.compute.views import addresses as views_addresses from nova.api.openstack.compute.views import flavors as views_flavors from nova.api.openstack.compute.views import images as views_images from nova import availability_zones as avail_zone +from nova import compute from nova import context as nova_context from nova import exception from nova import objects +from nova.policies import extended_server_attributes as esa_policies from nova.policies import flavor_extra_specs as fes_policies +from nova.policies import servers as servers_policies from nova import utils @@ -61,6 +64,7 @@ class ViewBuilder(common.ViewBuilder): self._address_builder = views_addresses.ViewBuilder() self._image_builder = views_images.ViewBuilder() self._flavor_builder = views_flavors.ViewBuilder() + self.compute_api = compute.API() def create(self, request, instance): """View that should be returned when an instance is created.""" @@ -78,7 +82,8 @@ class ViewBuilder(common.ViewBuilder): }, } - def basic(self, request, instance, show_extra_specs=False): + def basic(self, request, instance, show_extra_specs=False, + show_extended_attr=None, show_host_status=None): """Generic, non-detailed view of an instance.""" return { "server": { @@ -111,7 +116,8 @@ class ViewBuilder(common.ViewBuilder): return sorted(list(set(self._show_expected_attrs + expected_attrs))) def show(self, request, instance, extend_address=True, - show_extra_specs=None, show_AZ=True, show_config_drive=True): + show_extra_specs=None, show_AZ=True, show_config_drive=True, + show_extended_attr=None, show_host_status=None): """Detailed view of a single instance.""" ip_v4 = instance.get('access_ip_v4') ip_v6 = instance.get('access_ip_v6') @@ -161,8 +167,8 @@ class ViewBuilder(common.ViewBuilder): if server["server"]["status"] in self._progress_statuses: server["server"]["progress"] = instance.get("progress", 0) + context = request.environ['nova.context'] if show_AZ: - context = request.environ['nova.context'] az = avail_zone.get_instance_availability_zone(context, instance) # NOTE(mriedem): The OS-EXT-AZ prefix should not be used for new # attributes after v2.1. They are only in v2.1 for backward compat @@ -172,6 +178,40 @@ class ViewBuilder(common.ViewBuilder): if show_config_drive: server["server"]["config_drive"] = instance["config_drive"] + if show_extended_attr is None: + show_extended_attr = context.can( + esa_policies.BASE_POLICY_NAME, fatal=False) + if show_extended_attr: + server["server"][ + "OS-EXT-SRV-ATTR:hypervisor_hostname"] = instance.node + + properties = ['host', 'name'] + if api_version_request.is_supported(request, min_version='2.3'): + # NOTE(mriedem): These will use the OS-EXT-SRV-ATTR prefix + # below and that's OK for microversion 2.3 which is being + # compatible with v2.0 for the ec2 API split out from Nova. + # After this, however, new microversions should not be using + # the OS-EXT-SRV-ATTR prefix. + properties += ['reservation_id', 'launch_index', + 'hostname', 'kernel_id', 'ramdisk_id', + 'root_device_name', 'user_data'] + for attr in properties: + if attr == 'name': + key = "OS-EXT-SRV-ATTR:instance_%s" % attr + else: + # NOTE(mriedem): Nothing after microversion 2.3 should use + # the OS-EXT-SRV-ATTR prefix for the attribute key name. + key = "OS-EXT-SRV-ATTR:%s" % attr + server["server"][key] = instance[attr] + if (api_version_request.is_supported(request, min_version='2.16')): + if show_host_status is None: + show_host_status = context.can( + servers_policies.SERVERS % 'show:host_status', fatal=False) + if show_host_status: + host_status = self.compute_api.get_instance_host_status( + instance) + server["server"]['host_status'] = host_status + if api_version_request.is_supported(request, min_version="2.9"): server["server"]["locked"] = (True if instance["locked_by"] else False) @@ -209,11 +249,20 @@ class ViewBuilder(common.ViewBuilder): fatal=False) else: show_extra_specs = False - + context = request.environ['nova.context'] + show_extended_attr = context.can( + esa_policies.BASE_POLICY_NAME, fatal=False) + show_host_status = False + if (api_version_request.is_supported(request, min_version='2.16')): + show_host_status = context.can( + servers_policies.SERVERS % 'show:host_status', fatal=False) return self._list_view(self.show, request, instances, coll_name, - show_extra_specs) + show_extra_specs, + show_extended_attr=show_extended_attr, + show_host_status=show_host_status) - def _list_view(self, func, request, servers, coll_name, show_extra_specs): + def _list_view(self, func, request, servers, coll_name, show_extra_specs, + show_extended_attr=None, show_host_status=None): """Provide a view for a list of servers. :param func: Function used to format the server data @@ -221,10 +270,16 @@ class ViewBuilder(common.ViewBuilder): :param servers: List of servers in dictionary format :param coll_name: Name of collection, used to generate the next link for a pagination query + :param show_extended_attr: If the server extended attributes should be + included in the response dict. + :param show_host_status: If the host status should be included in + the response dict. :returns: Server data in dictionary format """ server_list = [func(request, server, - show_extra_specs=show_extra_specs)["server"] + show_extra_specs=show_extra_specs, + show_extended_attr=show_extended_attr, + show_host_status=show_host_status)["server"] for server in servers] servers_links = self._get_collection_links(request, servers, diff --git a/nova/tests/unit/api/openstack/compute/test_extended_server_attributes.py b/nova/tests/unit/api/openstack/compute/test_extended_server_attributes.py deleted file mode 100644 index 8f55c001e839..000000000000 --- a/nova/tests/unit/api/openstack/compute/test_extended_server_attributes.py +++ /dev/null @@ -1,246 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# 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. - -import mock -from oslo_config import cfg -from oslo_serialization import jsonutils - -from nova.api.openstack import wsgi as os_wsgi -from nova import compute -from nova import exception -from nova import objects -from nova import test -from nova.tests.unit.api.openstack import fakes - - -NAME_FMT = cfg.CONF.instance_name_template -UUID1 = '00000000-0000-0000-0000-000000000001' -UUID2 = '00000000-0000-0000-0000-000000000002' -UUID3 = '00000000-0000-0000-0000-000000000003' -UUID4 = '00000000-0000-0000-0000-000000000004' -UUID5 = '00000000-0000-0000-0000-000000000005' - - -def fake_services(host): - service_list = [objects.Service(id=0, host=host, forced_down=True, - binary='nova-compute')] - return objects.ServiceList(objects=service_list) - - -def fake_compute_get(*args, **kwargs): - return fakes.stub_instance_obj( - None, 1, uuid=UUID3, host="host-fake", - node="node-fake", - reservation_id="r-1", launch_index=0, - kernel_id=UUID4, ramdisk_id=UUID5, - display_name="hostname-1", - root_device_name="/dev/vda", - user_data="userdata", - services=fake_services("host-fake")) - - -def fake_compute_get_all(*args, **kwargs): - inst_list = [ - fakes.stub_instance_obj( - None, 1, uuid=UUID1, host="host-1", node="node-1", - reservation_id="r-1", launch_index=0, - kernel_id=UUID4, ramdisk_id=UUID5, - display_name="hostname-1", - root_device_name="/dev/vda", - user_data="userdata", - services=fake_services("host-1")), - fakes.stub_instance_obj( - None, 2, uuid=UUID2, host="host-2", node="node-2", - reservation_id="r-2", launch_index=1, - kernel_id=UUID4, ramdisk_id=UUID5, - display_name="hostname-2", - root_device_name="/dev/vda", - user_data="userdata", - services=fake_services("host-2")), - ] - return objects.InstanceList(objects=inst_list) - - -class ExtendedServerAttributesTestV21(test.TestCase): - content_type = 'application/json' - prefix = 'OS-EXT-SRV-ATTR:' - fake_url = '/v2/fake' - wsgi_api_version = os_wsgi.DEFAULT_API_VERSION - - def setUp(self): - super(ExtendedServerAttributesTestV21, self).setUp() - fakes.stub_out_nw_api(self) - fakes.stub_out_secgroup_api(self) - self.stub_out('nova.compute.api.API.get', fake_compute_get) - self.stub_out('nova.compute.api.API.get_all', fake_compute_get_all) - self.stub_out('nova.db.api.instance_get_by_uuid', fake_compute_get) - - def _make_request(self, url): - req = fakes.HTTPRequest.blank(url) - req.headers['Accept'] = self.content_type - req.headers = {os_wsgi.API_VERSION_REQUEST_HEADER: - 'compute %s' % self.wsgi_api_version} - res = req.get_response( - fakes.wsgi_app_v21()) - return res - - def _get_server(self, body): - return jsonutils.loads(body).get('server') - - def _get_servers(self, body): - return jsonutils.loads(body).get('servers') - - def assertServerAttributes(self, server, host, node, instance_name): - self.assertEqual(server.get('%shost' % self.prefix), host) - self.assertEqual(server.get('%sinstance_name' % self.prefix), - instance_name) - self.assertEqual(server.get('%shypervisor_hostname' % self.prefix), - node) - - def test_show(self): - url = self.fake_url + '/servers/%s' % UUID3 - res = self._make_request(url) - - self.assertEqual(res.status_int, 200) - self.assertServerAttributes(self._get_server(res.body), - host='host-fake', - node='node-fake', - instance_name=NAME_FMT % 1) - - def test_detail(self): - url = self.fake_url + '/servers/detail' - res = self._make_request(url) - - self.assertEqual(res.status_int, 200) - for i, server in enumerate(self._get_servers(res.body)): - self.assertServerAttributes(server, - host='host-%s' % (i + 1), - node='node-%s' % (i + 1), - instance_name=NAME_FMT % (i + 1)) - - @mock.patch.object(compute.api.API, 'get_all') - def test_detail_empty_instance_list_invalid_status(self, - mock_get_all_method): - mock_get_all_method.return_value = objects.InstanceList(objects=[]) - - url = "%s%s" % (self.fake_url, '/servers/detail?status=invalid_status') - res = self._make_request(url) - # check status code 200 with empty instance list - self.assertEqual(200, res.status_int) - self.assertEqual(0, len(self._get_servers(res.body))) - - def test_no_instance_passthrough_404(self): - - def fake_compute_get(*args, **kwargs): - raise exception.InstanceNotFound(instance_id='fake') - - self.stub_out('nova.compute.api.API.get', fake_compute_get) - url = self.fake_url + '/servers/70f6db34-de8d-4fbd-aafb-4065bdfa6115' - res = self._make_request(url) - - self.assertEqual(res.status_int, 404) - - -class ExtendedServerAttributesTestV23(ExtendedServerAttributesTestV21): - wsgi_api_version = '2.3' - - def assertServerAttributes(self, server, host, node, instance_name, - reservation_id, launch_index, kernel_id, - ramdisk_id, hostname, root_device_name, - user_data): - super(ExtendedServerAttributesTestV23, self).assertServerAttributes( - server, host, node, instance_name) - self.assertEqual(server.get('%sreservation_id' % self.prefix), - reservation_id) - self.assertEqual(server.get('%slaunch_index' % self.prefix), - launch_index) - self.assertEqual(server.get('%skernel_id' % self.prefix), - kernel_id) - self.assertEqual(server.get('%sramdisk_id' % self.prefix), - ramdisk_id) - self.assertEqual(server.get('%shostname' % self.prefix), - hostname) - self.assertEqual(server.get('%sroot_device_name' % self.prefix), - root_device_name) - self.assertEqual(server.get('%suser_data' % self.prefix), - user_data) - - def test_show(self): - url = self.fake_url + '/servers/%s' % UUID3 - res = self._make_request(url) - - self.assertEqual(res.status_int, 200) - self.assertServerAttributes(self._get_server(res.body), - host='host-fake', - node='node-fake', - instance_name=NAME_FMT % 1, - reservation_id="r-1", - launch_index=0, - kernel_id=UUID4, - ramdisk_id=UUID5, - hostname="hostname-1", - root_device_name="/dev/vda", - user_data="userdata") - - def test_detail(self): - url = self.fake_url + '/servers/detail' - res = self._make_request(url) - - self.assertEqual(res.status_int, 200) - for i, server in enumerate(self._get_servers(res.body)): - self.assertServerAttributes(server, - host='host-%s' % (i + 1), - node='node-%s' % (i + 1), - instance_name=NAME_FMT % (i + 1), - reservation_id="r-%s" % (i + 1), - launch_index=i, - kernel_id=UUID4, - ramdisk_id=UUID5, - hostname="hostname-%s" % (i + 1), - root_device_name="/dev/vda", - user_data="userdata") - - -class ExtendedServerAttributesTestV216(ExtendedServerAttributesTestV21): - wsgi_api_version = '2.16' - - def assertServerAttributes(self, server, host, node, instance_name, - host_status): - super(ExtendedServerAttributesTestV216, self).assertServerAttributes( - server, host, node, instance_name) - self.assertEqual(server.get('host_status'), host_status) - - def test_show(self): - url = self.fake_url + '/servers/%s' % UUID3 - res = self._make_request(url) - - self.assertEqual(res.status_int, 200) - self.assertServerAttributes(self._get_server(res.body), - host='host-fake', - node='node-fake', - instance_name=NAME_FMT % 1, - host_status="DOWN") - - def test_detail(self): - url = self.fake_url + '/servers/detail' - res = self._make_request(url) - - self.assertEqual(res.status_int, 200) - for i, server in enumerate(self._get_servers(res.body)): - self.assertServerAttributes(server, - host='host-%s' % (i + 1), - node='node-%s' % (i + 1), - instance_name=NAME_FMT % (i + 1), - host_status="DOWN") diff --git a/nova/tests/unit/api/openstack/compute/test_serversV21.py b/nova/tests/unit/api/openstack/compute/test_serversV21.py index d5c81c428dce..b5e5e899ff06 100644 --- a/nova/tests/unit/api/openstack/compute/test_serversV21.py +++ b/nova/tests/unit/api/openstack/compute/test_serversV21.py @@ -73,6 +73,9 @@ CONF = nova.conf.CONF FAKE_UUID = fakes.FAKE_UUID +UUID1 = '00000000-0000-0000-0000-000000000001' +UUID2 = '00000000-0000-0000-0000-000000000002' + INSTANCE_IDS = {FAKE_UUID: 1} FIELDS = instance_obj.INSTANCE_DEFAULT_FIELDS @@ -159,7 +162,7 @@ class ControllerTest(test.TestCase): self.flags(use_ipv6=False) fakes.stub_out_key_pair_funcs(self) fake.stub_out_image_service(self) - return_server = fakes.fake_compute_get(availability_zone='nova') + return_server = fakes.fake_compute_get(id=2, availability_zone='nova') return_servers = fakes.fake_compute_get_all() # Server sort keys extension is enabled in v21 so sort data is passed # to the instance API and the sorted DB API is invoked @@ -379,6 +382,9 @@ class ServersControllerTest(ControllerTest): "accessIPv6": '', "OS-EXT-AZ:availability_zone": "nova", "config_drive": None, + "OS-EXT-SRV-ATTR:host": None, + "OS-EXT-SRV-ATTR:hypervisor_hostname": None, + "OS-EXT-SRV-ATTR:instance_name": "instance-00000002", } } @@ -395,8 +401,6 @@ class ServersControllerTest(ControllerTest): image_bookmark, flavor_bookmark, progress=0) - expected_server['server']['name'] = 'server1' - expected_server['server']['metadata']['seq'] = '1' self.assertThat(res_dict, matchers.DictMatches(expected_server)) def test_get_server_empty_az(self): @@ -411,15 +415,12 @@ class ServersControllerTest(ControllerTest): image_bookmark = "http://localhost/fake/images/10" flavor_bookmark = "http://localhost/fake/flavors/2" - self.mock_get.side_effect = fakes.fake_compute_get( - id=2, vm_state=vm_states.ACTIVE, progress=100, - availability_zone='nova') - req = self.req('/fake/servers/%s' % FAKE_UUID) res_dict = self.controller.show(req, FAKE_UUID) expected_server = self._get_server_data_dict(FAKE_UUID, image_bookmark, - flavor_bookmark) + flavor_bookmark, + progress=0) self.assertThat(res_dict, matchers.DictMatches(expected_server)) self.mock_get.assert_called_once_with( req.environ['nova.context'], FAKE_UUID, @@ -427,21 +428,15 @@ class ServersControllerTest(ControllerTest): 'numa_topology']) def test_get_server_with_id_image_ref_by_id(self): - image_ref = "10" image_bookmark = "http://localhost/fake/images/10" - flavor_id = "1" flavor_bookmark = "http://localhost/fake/flavors/2" - self.mock_get.side_effect = fakes.fake_compute_get( - id=2, vm_state=vm_states.ACTIVE, image_ref=image_ref, - flavor_id=flavor_id, progress=100, - availability_zone='nova') - req = self.req('/fake/servers/%s' % FAKE_UUID) res_dict = self.controller.show(req, FAKE_UUID) expected_server = self._get_server_data_dict(FAKE_UUID, image_bookmark, - flavor_bookmark) + flavor_bookmark, + progress=0) self.assertThat(res_dict, matchers.DictMatches(expected_server)) self.mock_get.assert_called_once_with( @@ -1435,9 +1430,104 @@ class ServersControllerTest(ControllerTest): self.assertIn('servers', self.controller.detail(req)) +class ServersControllerTestV23(ServersControllerTest): + wsgi_api_version = '2.3' + + def setUp(self): + super(ServersControllerTestV23, self).setUp() + self.mock_get.side_effect = fakes.fake_compute_get( + id=2, uuid=FAKE_UUID, + node="node-fake", + reservation_id="r-1", launch_index=0, + kernel_id=UUID1, ramdisk_id=UUID2, + display_name="server2", + root_device_name="/dev/vda", + user_data="userdata", + metadata={"seq": "2"}, + availability_zone='nova') + + def _get_server_data_dict(self, uuid, image_bookmark, flavor_bookmark, + status="ACTIVE", progress=100): + server_dict = super(ServersControllerTestV23, + self)._get_server_data_dict(uuid, + image_bookmark, + flavor_bookmark, + status, + progress) + server_dict['server']["OS-EXT-SRV-ATTR:hostname"] = "server2" + server_dict['server'][ + "OS-EXT-SRV-ATTR:hypervisor_hostname"] = "node-fake" + server_dict['server']["OS-EXT-SRV-ATTR:kernel_id"] = UUID1 + server_dict['server']["OS-EXT-SRV-ATTR:launch_index"] = 0 + server_dict['server']["OS-EXT-SRV-ATTR:ramdisk_id"] = UUID2 + server_dict['server']["OS-EXT-SRV-ATTR:reservation_id"] = "r-1" + server_dict['server']["OS-EXT-SRV-ATTR:root_device_name"] = "/dev/vda" + server_dict['server']["OS-EXT-SRV-ATTR:user_data"] = "userdata" + return server_dict + + def test_show(self): + image_bookmark = "http://localhost/fake/images/10" + flavor_bookmark = "http://localhost/fake/flavors/2" + + req = self.req('/fake/servers/%s' % FAKE_UUID) + res_dict = self.controller.show(req, FAKE_UUID) + + expected_server = self._get_server_data_dict(FAKE_UUID, + image_bookmark, + flavor_bookmark, + progress=0) + self.assertThat(res_dict, matchers.DictMatches(expected_server)) + + def test_detail(self): + def fake_get_all(context, search_opts=None, + limit=None, marker=None, + expected_attrs=None, sort_keys=None, sort_dirs=None): + obj_list = [] + for i in range(2): + server = fakes.stub_instance_obj(context, + id=2, uuid=FAKE_UUID, + node="node-fake", + reservation_id="r-1", launch_index=0, + kernel_id=UUID1, ramdisk_id=UUID2, + display_name="server2", + root_device_name="/dev/vda", + user_data="userdata", + metadata={"seq": "2"}, + availability_zone='nova') + obj_list.append(server) + return objects.InstanceList(objects=obj_list) + + self.mock_get_all.side_effect = None + self.mock_get_all.return_value = fake_get_all(context) + + req = self.req('/fake/servers/detail') + servers_list = self.controller.detail(req) + image_bookmark = "http://localhost/fake/images/10" + flavor_bookmark = "http://localhost/fake/flavors/2" + expected_server = self._get_server_data_dict(FAKE_UUID, + image_bookmark, + flavor_bookmark, + progress=0) + + self.assertIn(expected_server['server'], servers_list['servers']) + + class ServersControllerTestV29(ServersControllerTest): wsgi_api_version = '2.9' + def setUp(self): + super(ServersControllerTestV29, self).setUp() + self.mock_get.side_effect = fakes.fake_compute_get( + id=2, uuid=FAKE_UUID, + node="node-fake", + reservation_id="r-1", launch_index=0, + kernel_id=UUID1, ramdisk_id=UUID2, + display_name="server2", + root_device_name="/dev/vda", + user_data="userdata", + metadata={"seq": "2"}, + availability_zone='nova') + def _get_server_data_dict(self, uuid, image_bookmark, flavor_bookmark, status="ACTIVE", progress=100): server_dict = super(ServersControllerTestV29, @@ -1447,6 +1537,15 @@ class ServersControllerTestV29(ServersControllerTest): status, progress) server_dict['server']['locked'] = False + server_dict['server']["OS-EXT-SRV-ATTR:hostname"] = "server2" + server_dict['server'][ + "OS-EXT-SRV-ATTR:hypervisor_hostname"] = "node-fake" + server_dict['server']["OS-EXT-SRV-ATTR:kernel_id"] = UUID1 + server_dict['server']["OS-EXT-SRV-ATTR:launch_index"] = 0 + server_dict['server']["OS-EXT-SRV-ATTR:ramdisk_id"] = UUID2 + server_dict['server']["OS-EXT-SRV-ATTR:reservation_id"] = "r-1" + server_dict['server']["OS-EXT-SRV-ATTR:root_device_name"] = "/dev/vda" + server_dict['server']["OS-EXT-SRV-ATTR:user_data"] = "userdata" return server_dict def _test_get_server_with_lock(self, locked_by): @@ -1454,6 +1553,13 @@ class ServersControllerTestV29(ServersControllerTest): flavor_bookmark = "http://localhost/fake/flavors/2" self.mock_get.side_effect = fakes.fake_compute_get( id=2, locked_by=locked_by, uuid=FAKE_UUID, + node="node-fake", + reservation_id="r-1", launch_index=0, + kernel_id=UUID1, ramdisk_id=UUID2, + display_name="server2", + root_device_name="/dev/vda", + user_data="userdata", + metadata={"seq": "2"}, availability_zone='nova') req = self.req('/fake/servers/%s' % FAKE_UUID) @@ -1484,7 +1590,16 @@ class ServersControllerTestV29(ServersControllerTest): s2_locked): self.mock_get_all.side_effect = None self.mock_get_all.return_value = fake_instance_get_all_with_locked( - context, [s1_locked, s2_locked]) + context, [s1_locked, s2_locked], + node="node-fake", + reservation_id="r-1", launch_index=0, + kernel_id=UUID1, ramdisk_id=UUID2, + display_name="server2", + root_device_name="/dev/vda", + user_data="userdata", + metadata={"seq": "2"}, + availability_zone='nova') + req = self.req('/fake/servers/detail') servers_list = self.controller.detail(req) # Check that each returned server has the same 'locked' value @@ -1525,9 +1640,111 @@ class ServersControllerTestV29(ServersControllerTest): self.assertNotIn(key, search_opts) +class ServersControllerTestV216(ServersControllerTest): + wsgi_api_version = '2.16' + + def setUp(self): + super(ServersControllerTestV216, self).setUp() + self.mock_get.side_effect = fakes.fake_compute_get( + id=2, uuid=FAKE_UUID, + node="node-fake", + reservation_id="r-1", launch_index=0, + kernel_id=UUID1, ramdisk_id=UUID2, + display_name="server2", + root_device_name="/dev/vda", + user_data="userdata", + metadata={"seq": "2"}, + availability_zone='nova') + self.useFixture(fixtures.MockPatchObject( + compute_api.API, 'get_instance_host_status', + return_value='UP')).mock + + def _get_server_data_dict(self, uuid, image_bookmark, flavor_bookmark, + status="ACTIVE", progress=100): + server_dict = super(ServersControllerTestV216, + self)._get_server_data_dict(uuid, + image_bookmark, + flavor_bookmark, + status, + progress) + server_dict['server']['locked'] = False + server_dict['server']["host_status"] = "UP" + server_dict['server']["OS-EXT-SRV-ATTR:hostname"] = "server2" + server_dict['server'][ + "OS-EXT-SRV-ATTR:hypervisor_hostname"] = "node-fake" + server_dict['server']["OS-EXT-SRV-ATTR:kernel_id"] = UUID1 + server_dict['server']["OS-EXT-SRV-ATTR:launch_index"] = 0 + server_dict['server']["OS-EXT-SRV-ATTR:ramdisk_id"] = UUID2 + server_dict['server']["OS-EXT-SRV-ATTR:reservation_id"] = "r-1" + server_dict['server']["OS-EXT-SRV-ATTR:root_device_name"] = "/dev/vda" + server_dict['server']["OS-EXT-SRV-ATTR:user_data"] = "userdata" + + return server_dict + + def test_show(self): + image_bookmark = "http://localhost/fake/images/10" + flavor_bookmark = "http://localhost/fake/flavors/2" + req = self.req('/fake/servers/%s' % FAKE_UUID) + res_dict = self.controller.show(req, FAKE_UUID) + expected_server = self._get_server_data_dict(FAKE_UUID, + image_bookmark, + flavor_bookmark, + progress=0) + self.assertThat(res_dict, matchers.DictMatches(expected_server)) + + def test_detail(self): + def fake_get_all(context, search_opts=None, + limit=None, marker=None, + expected_attrs=None, sort_keys=None, sort_dirs=None): + obj_list = [] + for i in range(2): + server = fakes.stub_instance_obj(context, + id=2, uuid=FAKE_UUID, + node="node-fake", + reservation_id="r-1", launch_index=0, + kernel_id=UUID1, ramdisk_id=UUID2, + display_name="server2", + root_device_name="/dev/vda", + user_data="userdata", + metadata={"seq": "2"}, + availability_zone='nova') + obj_list.append(server) + return objects.InstanceList(objects=obj_list) + + self.mock_get_all.side_effect = None + self.mock_get_all.return_value = fake_get_all(context) + + req = self.req('/fake/servers/detail') + servers_list = self.controller.detail(req) + image_bookmark = "http://localhost/fake/images/10" + flavor_bookmark = "http://localhost/fake/flavors/2" + expected_server = self._get_server_data_dict(FAKE_UUID, + image_bookmark, + flavor_bookmark, + progress=0) + + self.assertIn(expected_server['server'], servers_list['servers']) + + class ServersControllerTestV219(ServersControllerTest): wsgi_api_version = '2.19' + def setUp(self): + super(ServersControllerTestV219, self).setUp() + self.mock_get.side_effect = fakes.fake_compute_get( + id=2, uuid=FAKE_UUID, + node="node-fake", + reservation_id="r-1", launch_index=0, + kernel_id=UUID1, ramdisk_id=UUID2, + display_name="server2", + root_device_name="/dev/vda", + user_data="userdata", + metadata={"seq": "2"}, + availability_zone='nova') + self.useFixture(fixtures.MockPatchObject( + compute_api.API, 'get_instance_host_status', + return_value='UP')).mock + def _get_server_data_dict(self, uuid, image_bookmark, flavor_bookmark, status="ACTIVE", progress=100, description=None): server_dict = super(ServersControllerTestV219, @@ -1538,6 +1755,17 @@ class ServersControllerTestV219(ServersControllerTest): progress) server_dict['server']['locked'] = False server_dict['server']['description'] = description + server_dict['server']["host_status"] = "UP" + server_dict['server']["OS-EXT-SRV-ATTR:hostname"] = "server2" + server_dict['server'][ + "OS-EXT-SRV-ATTR:hypervisor_hostname"] = "node-fake" + server_dict['server']["OS-EXT-SRV-ATTR:kernel_id"] = UUID1 + server_dict['server']["OS-EXT-SRV-ATTR:launch_index"] = 0 + server_dict['server']["OS-EXT-SRV-ATTR:ramdisk_id"] = UUID2 + server_dict['server']["OS-EXT-SRV-ATTR:reservation_id"] = "r-1" + server_dict['server']["OS-EXT-SRV-ATTR:root_device_name"] = "/dev/vda" + server_dict['server']["OS-EXT-SRV-ATTR:user_data"] = "userdata" + return server_dict def _test_get_server_with_description(self, description): @@ -1545,6 +1773,13 @@ class ServersControllerTestV219(ServersControllerTest): flavor_bookmark = "http://localhost/fake/flavors/2" self.mock_get.side_effect = fakes.fake_compute_get( id=2, display_description=description, uuid=FAKE_UUID, + node="node-fake", + reservation_id="r-1", launch_index=0, + kernel_id=UUID1, ramdisk_id=UUID2, + display_name="server2", + root_device_name="/dev/vda", + user_data="userdata", + metadata={"seq": "2"}, availability_zone='nova') req = self.req('/fake/servers/%s' % FAKE_UUID) @@ -2088,8 +2323,19 @@ class ServersControllerRebuildInstanceTest(ControllerTest): body = self.controller._action_rebuild(self.req, FAKE_UUID, body=body).obj - self.assertNotIn('OS-EXT-AZ:availability_zone', body['server']) - self.assertNotIn('config_drive', body['server']) + get_only_fields = ['OS-EXT-AZ:availability_zone', 'config_drive', + 'OS-EXT-SRV-ATTR:host', + 'OS-EXT-SRV-ATTR:hypervisor_hostname', + 'OS-EXT-SRV-ATTR:instance_name', + 'OS-EXT-SRV-ATTR:hostname' + 'OS-EXT-SRV-ATTR:kernel_id', + 'OS-EXT-SRV-ATTR:launch_index', + 'OS-EXT-SRV-ATTR:ramdisk_id', + 'OS-EXT-SRV-ATTR:reservation_id', + 'OS-EXT-SRV-ATTR:root_device_name', + 'OS-EXT-SRV-ATTR:user_data', 'host_status'] + for field in get_only_fields: + self.assertNotIn(field, body['server']) @mock.patch.object(compute_api.API, 'start') def test_start(self, mock_start): @@ -2684,8 +2930,19 @@ class ServersControllerUpdateTest(ControllerTest): body = {'server': {'name': 'server_test'}} req = self._get_request(body) res_dict = self.controller.update(req, FAKE_UUID, body=body) - self.assertNotIn('OS-EXT-AZ:availability_zone', res_dict['server']) - self.assertNotIn('config_drive', res_dict['server']) + get_only_fields = ['OS-EXT-AZ:availability_zone', 'config_drive', + 'OS-EXT-SRV-ATTR:host', + 'OS-EXT-SRV-ATTR:hypervisor_hostname', + 'OS-EXT-SRV-ATTR:instance_name', + 'OS-EXT-SRV-ATTR:hostname' + 'OS-EXT-SRV-ATTR:kernel_id', + 'OS-EXT-SRV-ATTR:launch_index', + 'OS-EXT-SRV-ATTR:ramdisk_id', + 'OS-EXT-SRV-ATTR:reservation_id', + 'OS-EXT-SRV-ATTR:root_device_name', + 'OS-EXT-SRV-ATTR:user_data', 'host_status'] + for field in get_only_fields: + self.assertNotIn(field, res_dict['server']) def test_update_server_name_too_long(self): body = {'server': {'name': 'x' * 256}} @@ -5996,6 +6253,9 @@ class ServersViewBuilderTest(test.TestCase): "accessIPv6": '', "OS-EXT-AZ:availability_zone": "nova", "config_drive": None, + "OS-EXT-SRV-ATTR:host": None, + "OS-EXT-SRV-ATTR:hypervisor_hostname": None, + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", } } @@ -6077,6 +6337,9 @@ class ServersViewBuilderTest(test.TestCase): "accessIPv6": '', "OS-EXT-AZ:availability_zone": "nova", "config_drive": None, + "OS-EXT-SRV-ATTR:host": None, + "OS-EXT-SRV-ATTR:hypervisor_hostname": None, + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", } } @@ -6257,6 +6520,9 @@ class ServersViewBuilderTest(test.TestCase): "accessIPv6": '', "OS-EXT-AZ:availability_zone": "nova", "config_drive": None, + "OS-EXT-SRV-ATTR:host": None, + "OS-EXT-SRV-ATTR:hypervisor_hostname": None, + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", } } @@ -6335,6 +6601,9 @@ class ServersViewBuilderTest(test.TestCase): "accessIPv6": '', "OS-EXT-AZ:availability_zone": "nova", "config_drive": None, + "OS-EXT-SRV-ATTR:host": None, + "OS-EXT-SRV-ATTR:hypervisor_hostname": None, + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", } }