diff --git a/openstack/cloud/_compute.py b/openstack/cloud/_compute.py index 62d5b46f5..89e329816 100644 --- a/openstack/cloud/_compute.py +++ b/openstack/cloud/_compute.py @@ -823,8 +823,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): @@ -847,16 +845,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 d255e60e6..5fb868479 100644 --- a/openstack/tests/unit/cloud/test_create_server.py +++ b/openstack/tests/unit/cloud/test_create_server.py @@ -1017,3 +1017,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()