diff --git a/releasenotes/notes/fix-config-drive-a148b7589f7e1022.yaml b/releasenotes/notes/fix-config-drive-a148b7589f7e1022.yaml new file mode 100644 index 000000000..cd08b87cf --- /dev/null +++ b/releasenotes/notes/fix-config-drive-a148b7589f7e1022.yaml @@ -0,0 +1,6 @@ +--- +issues: + - Fixed an issue where nodepool could cause config_drive + to be passed explicitly as None, which was getting directly + passed through to the JSON. Also fix the same logic for key_name + and scheduler_hints while we're in there. diff --git a/shade/openstackcloud.py b/shade/openstackcloud.py index a657f4662..3efef0832 100644 --- a/shade/openstackcloud.py +++ b/shade/openstackcloud.py @@ -5560,11 +5560,15 @@ class OpenStackCloud( kwargs['user_data'] = self._encode_server_userdata(user_data) for (desired, given) in ( ('OS-DCF:diskConfig', 'disk_config'), + ('os:scheduler_hints', 'scheduler_hints'), + ('config_drive', 'config_drive'), + ('key_name', 'key_name'), ('metadata', 'meta'), ('adminPass', 'admin_pass')): value = kwargs.pop(given, None) if value: kwargs[desired] = value + kwargs.setdefault('max_count', kwargs.get('max_count', 1)) kwargs.setdefault('min_count', kwargs.get('min_count', 1)) diff --git a/shade/tests/functional/test_compute.py b/shade/tests/functional/test_compute.py index c4c1e4be0..45883c782 100644 --- a/shade/tests/functional/test_compute.py +++ b/shade/tests/functional/test_compute.py @@ -84,6 +84,44 @@ class TestCompute(base.BaseFunctionalTestCase): self.assertTrue(vol_attachment[key]) # assert string is not empty self.assertIsNone(self.user_cloud.detach_volume(server, volume)) + def test_create_and_delete_server_with_config_drive(self): + self.addCleanup(self._cleanup_servers_and_volumes, self.server_name) + server = self.user_cloud.create_server( + name=self.server_name, + image=self.image, + flavor=self.flavor, + config_drive=True, + wait=True) + self.assertEqual(self.server_name, server['name']) + self.assertEqual(self.image.id, server['image']['id']) + self.assertEqual(self.flavor.id, server['flavor']['id']) + self.assertEqual(True, server['has_config_drive']) + self.assertIsNotNone(server['adminPass']) + self.assertTrue( + self.user_cloud.delete_server(self.server_name, wait=True)) + self.assertIsNone(self.user_cloud.get_server(self.server_name)) + + def test_create_and_delete_server_with_config_drive_none(self): + # check that we're not sending invalid values for config_drive + # if it's passed in explicitly as None - which nodepool does if it's + # not set in the config + self.addCleanup(self._cleanup_servers_and_volumes, self.server_name) + server = self.user_cloud.create_server( + name=self.server_name, + image=self.image, + flavor=self.flavor, + config_drive=None, + wait=True) + self.assertEqual(self.server_name, server['name']) + self.assertEqual(self.image.id, server['image']['id']) + self.assertEqual(self.flavor.id, server['flavor']['id']) + self.assertEqual(False, server['has_config_drive']) + self.assertIsNotNone(server['adminPass']) + self.assertTrue( + self.user_cloud.delete_server( + self.server_name, wait=True)) + self.assertIsNone(self.user_cloud.get_server(self.server_name)) + def test_list_all_servers(self): self.addCleanup(self._cleanup_servers_and_volumes, self.server_name) server = self.user_cloud.create_server( diff --git a/shade/tests/unit/test_create_server.py b/shade/tests/unit/test_create_server.py index a4023f556..308c20cfc 100644 --- a/shade/tests/unit/test_create_server.py +++ b/shade/tests/unit/test_create_server.py @@ -209,6 +209,83 @@ class TestCreateServer(base.RequestsMockTestCase): self.assert_calls() + def test_create_server_config_drive(self): + """ + Test that config_drive gets passed in properly + """ + fake_server = fakes.make_fake_server('1234', '', 'BUILD') + self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'networks.json']), + 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'config_drive': True, + u'max_count': 1, + u'min_count': 1, + u'name': u'server-name'}})), + dict(method='GET', + uri=self.get_mock_url( + 'compute', 'public', append=['servers', '1234']), + json={'server': fake_server}), + ]) + normalized = self.cloud._expand_server( + self.cloud._normalize_server(fake_server), False, False) + self.assertEqual( + normalized, + self.cloud.create_server( + name='server-name', + image=dict(id='image-id'), + flavor=dict(id='flavor-id'), + config_drive=True)) + + self.assert_calls() + + def test_create_server_config_drive_none(self): + """ + Test that config_drive gets not passed in properly + """ + fake_server = fakes.make_fake_server('1234', '', 'BUILD') + self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'network', 'public', append=['v2.0', 'networks.json']), + 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'}})), + dict(method='GET', + uri=self.get_mock_url( + 'compute', 'public', append=['servers', '1234']), + json={'server': fake_server}), + ]) + normalized = self.cloud._expand_server( + self.cloud._normalize_server(fake_server), False, False) + self.assertEqual( + normalized, + self.cloud.create_server( + name='server-name', + image=dict(id='image-id'), + flavor=dict(id='flavor-id'), + config_drive=None)) + + self.assert_calls() + def test_create_server_with_admin_pass_no_wait(self): """ Test that a server with an admin_pass passed returns the password