From 4ad5c174c87c9f93f7bbe55f6f04b5b0c7289913 Mon Sep 17 00:00:00 2001 From: Patrik Lundin <patlu@sunet.se> Date: Fri, 25 Nov 2022 10:32:16 +0100 Subject: [PATCH] cloud: create_server: fix scheduler_hints/group Change I1a6ba311ddedc1b8910051257299d3acd367df46 stopped sending server_json data and instead called self.compute.create_server(**kwargs). Before this change the code would pop() scheduler_hints from kwargs, so it was not present when creating the server. Additionally the "group" code would also only operate on the no longer used server_json. Remove server_json and override/set the scheduler_hints group key directly in kwargs if "group" is supplied. Also add tests for this functionality. Co-Authored-by: stephenfin <stephenfin@redhat.com> Change-Id: I7ac2fd85970bf4c6c6e73af8aad11348a290a90a --- openstack/cloud/_compute.py | 10 +- .../tests/unit/cloud/test_create_server.py | 172 ++++++++++++++++++ 2 files changed, 176 insertions(+), 6 deletions(-) diff --git a/openstack/cloud/_compute.py b/openstack/cloud/_compute.py index 58da2feec..64eb80949 100644 --- a/openstack/cloud/_compute.py +++ b/openstack/cloud/_compute.py @@ -779,8 +779,6 @@ class ComputeCloudMixin: raise TypeError( "create_server() requires either 'image' or 'boot_volume'") - server_json = {'server': kwargs} - # TODO(mordred) Add support for description starting in 2.19 security_groups = kwargs.get('security_groups', []) if security_groups and not isinstance(kwargs['security_groups'], list): @@ -803,16 +801,16 @@ class ComputeCloudMixin: if value: kwargs[desired] = value - hints = kwargs.pop('scheduler_hints', {}) if group: group_obj = self.get_server_group(group) if not group_obj: raise exc.OpenStackCloudException( "Server Group {group} was requested but was not found" " on the cloud".format(group=group)) - hints['group'] = group_obj['id'] - if hints: - server_json['os:scheduler_hints'] = hints + if 'scheduler_hints' not in kwargs: + kwargs['scheduler_hints'] = {} + kwargs['scheduler_hints']['group'] = group_obj['id'] + kwargs.setdefault('max_count', kwargs.get('max_count', 1)) kwargs.setdefault('min_count', kwargs.get('min_count', 1)) diff --git a/openstack/tests/unit/cloud/test_create_server.py b/openstack/tests/unit/cloud/test_create_server.py index 48ccd9968..d955bea34 100644 --- a/openstack/tests/unit/cloud/test_create_server.py +++ b/openstack/tests/unit/cloud/test_create_server.py @@ -1012,3 +1012,175 @@ class TestCreateServer(base.TestCase): wait=False) self.assert_calls() + + def test_create_server_scheduler_hints(self): + """ + Test that setting scheduler_hints will include them in POST request + """ + scheduler_hints = { + 'group': self.getUniqueString('group'), + } + fake_server = fakes.make_fake_server('1234', '', 'BUILD') + fake_server['scheduler_hints'] = scheduler_hints + + self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'networks']), + json={'networks': []}), + self.get_nova_discovery_mock_dict(), + dict(method='POST', + uri=self.get_mock_url( + 'compute', 'public', append=['servers']), + json={'server': fake_server}, + validate=dict( + json={ + 'server': { + u'flavorRef': u'flavor-id', + u'imageRef': u'image-id', + u'max_count': 1, + u'min_count': 1, + u'name': u'server-name', + 'networks': 'auto'}, + u'OS-SCH-HNT:scheduler_hints': scheduler_hints, })), + dict(method='GET', + uri=self.get_mock_url( + 'compute', 'public', append=['servers', '1234']), + json={'server': fake_server}), + ]) + + self.cloud.create_server( + name='server-name', image=dict(id='image-id'), + flavor=dict(id='flavor-id'), + scheduler_hints=scheduler_hints, wait=False) + + self.assert_calls() + + def test_create_server_scheduler_hints_group_merge(self): + """ + Test that setting both scheduler_hints and group results in merged + hints in POST request + """ + group_id = uuid.uuid4().hex + group_name = self.getUniqueString('server-group') + policies = ['affinity'] + fake_group = fakes.make_fake_server_group( + group_id, group_name, policies) + + # The scheduler hints we pass in + scheduler_hints = { + 'different_host': [], + } + + # The scheduler hints we expect to be in POST request + scheduler_hints_merged = { + 'different_host': [], + 'group': group_id, + } + + fake_server = fakes.make_fake_server('1234', '', 'BUILD') + fake_server['scheduler_hints'] = scheduler_hints_merged + + self.register_uris([ + self.get_nova_discovery_mock_dict(), + dict(method='GET', + uri=self.get_mock_url( + 'compute', 'public', append=['os-server-groups']), + json={'server_groups': [fake_group]}), + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'networks']), + json={'networks': []}), + dict(method='POST', + uri=self.get_mock_url( + 'compute', 'public', append=['servers']), + json={'server': fake_server}, + validate=dict( + json={ + 'server': { + u'flavorRef': u'flavor-id', + u'imageRef': u'image-id', + u'max_count': 1, + u'min_count': 1, + u'name': u'server-name', + 'networks': 'auto'}, + u'OS-SCH-HNT:scheduler_hints': scheduler_hints_merged, + })), + dict(method='GET', + uri=self.get_mock_url( + 'compute', 'public', append=['servers', '1234']), + json={'server': fake_server}), + ]) + + self.cloud.create_server( + name='server-name', image=dict(id='image-id'), + flavor=dict(id='flavor-id'), + scheduler_hints=dict(scheduler_hints), group=group_name, + wait=False) + + self.assert_calls() + + def test_create_server_scheduler_hints_group_override(self): + """ + Test that setting group in both scheduler_hints and group param prefers + param + """ + group_id_scheduler_hints = uuid.uuid4().hex + group_id_param = uuid.uuid4().hex + group_name = self.getUniqueString('server-group') + policies = ['affinity'] + fake_group = fakes.make_fake_server_group( + group_id_param, group_name, policies) + + # The scheduler hints we pass in that are expected to be ignored in + # POST call + scheduler_hints = { + 'group': group_id_scheduler_hints, + } + + # The scheduler hints we expect to be in POST request + group_scheduler_hints = { + 'group': group_id_param, + } + + fake_server = fakes.make_fake_server('1234', '', 'BUILD') + fake_server['scheduler_hints'] = group_scheduler_hints + + self.register_uris([ + self.get_nova_discovery_mock_dict(), + dict(method='GET', + uri=self.get_mock_url( + 'compute', 'public', append=['os-server-groups']), + json={'server_groups': [fake_group]}), + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'networks']), + json={'networks': []}), + dict(method='POST', + uri=self.get_mock_url( + 'compute', 'public', append=['servers']), + json={'server': fake_server}, + validate=dict( + json={ + 'server': { + u'flavorRef': u'flavor-id', + u'imageRef': u'image-id', + u'max_count': 1, + u'min_count': 1, + u'name': u'server-name', + 'networks': 'auto'}, + u'OS-SCH-HNT:scheduler_hints': group_scheduler_hints, + })), + dict(method='GET', + uri=self.get_mock_url( + 'compute', 'public', append=['servers', '1234']), + json={'server': fake_server}), + ]) + + self.cloud.create_server( + name='server-name', image=dict(id='image-id'), + flavor=dict(id='flavor-id'), + scheduler_hints=dict(scheduler_hints), group=group_name, + wait=False) + + self.assert_calls()