From 4e7772632a74f34a70890ac631bcaacd7a267df2 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Sun, 14 Aug 2016 10:00:56 -0500 Subject: [PATCH] Allow image and flavor by name for create_server We do a bunch of work everywhere else in shade to be friendly - but for some reason we only accept object or id for image and flavor. Fix it. In the unit tests, pass in a dict with an id value to avoid the need to mock the glance client, since that's not really what we're testing in any of those tests ... but add a test that does not do that to verify that the glance and nova clients are used to look at image/flavor lists. Change-Id: I1760a7464e43e19a475f6d277148a3c7e54ac468 --- ...image-flavor-by-name-54865b00ebbf1004.yaml | 9 +++++ shade/openstackcloud.py | 16 ++++++-- shade/tests/unit/test_create_server.py | 37 +++++++++++++------ 3 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 releasenotes/notes/image-flavor-by-name-54865b00ebbf1004.yaml diff --git a/releasenotes/notes/image-flavor-by-name-54865b00ebbf1004.yaml b/releasenotes/notes/image-flavor-by-name-54865b00ebbf1004.yaml new file mode 100644 index 000000000..654812104 --- /dev/null +++ b/releasenotes/notes/image-flavor-by-name-54865b00ebbf1004.yaml @@ -0,0 +1,9 @@ +--- +features: + - The image and flavor parameters for create_server + now accept name in addition to id and dict. If given + as a name or id, shade will do a get_image or a + get_flavor to find the matching image or flavor. + If you have an id already and are not using any caching + and the extra lookup is annoying, passing the id in + as "dict(id='my-id')" will avoid the lookup. diff --git a/shade/openstackcloud.py b/shade/openstackcloud.py index 7bac622eb..376658b19 100644 --- a/shade/openstackcloud.py +++ b/shade/openstackcloud.py @@ -4194,8 +4194,8 @@ class OpenStackCloud(object): """Create a virtual server instance. :param name: Something to name the server. - :param image: Image dict or id to boot with. - :param flavor: Flavor dict or id to boot onto. + :param image: Image dict, name or id to boot with. + :param flavor: Flavor dict, name or id to boot onto. :param auto_ip: Whether to take actions to find a routable IP for the server. (defaults to True) :param ips: List of IPs to attach to the server (defaults to None) @@ -4300,7 +4300,15 @@ class OpenStackCloud(object): if default_network: kwargs['nics'] = [{'net-id': default_network['id']}] - kwargs['image'] = image + if image: + if isinstance(image, dict): + kwargs['image'] = image + else: + kwargs['image'] = self.get_image(image) + if flavor and isinstance(flavor, dict): + kwargs['flavor'] = flavor + else: + kwargs['flavor'] = self.get_flavor(flavor, get_extra=False) kwargs = self._get_boot_from_volume_kwargs( image=image, boot_from_volume=boot_from_volume, @@ -4310,7 +4318,7 @@ class OpenStackCloud(object): with _utils.shade_exceptions("Error in creating instance"): server = self.manager.submitTask(_tasks.ServerCreate( - name=name, flavor=flavor, **kwargs)) + name=name, **kwargs)) admin_pass = server.get('adminPass') or kwargs.get('admin_pass') if not wait: # This is a direct get task call to skip the list_servers diff --git a/shade/tests/unit/test_create_server.py b/shade/tests/unit/test_create_server.py index 6db1589c7..31cc71ddb 100644 --- a/shade/tests/unit/test_create_server.py +++ b/shade/tests/unit/test_create_server.py @@ -99,7 +99,8 @@ class TestCreateServer(base.TestCase): self.assertRaises( OpenStackCloudException, self.cloud.create_server, - 'server-name', 'image-id', 'flavor-id', wait=True) + 'server-name', dict(id='image-id'), + dict(id='flavor-id'), wait=True) def test_create_server_with_timeout(self): """ @@ -117,7 +118,8 @@ class TestCreateServer(base.TestCase): self.assertRaises( OpenStackCloudTimeout, self.cloud.create_server, - 'server-name', 'image-id', 'flavor-id', + 'server-name', + dict(id='image-id'), dict(id='flavor-id'), wait=True, timeout=0.01) def test_create_server_no_wait(self): @@ -142,8 +144,9 @@ class TestCreateServer(base.TestCase): cloud_name=self.cloud.name, region_name=self.cloud.region_name), self.cloud.create_server( - name='server-name', image='image=id', - flavor='flavor-id')) + name='server-name', + image=dict(id='image=id'), + flavor=dict(id='flavor-id'))) def test_create_server_with_admin_pass_no_wait(self): """ @@ -168,8 +171,8 @@ class TestCreateServer(base.TestCase): cloud_name=self.cloud.name, region_name=self.cloud.region_name), self.cloud.create_server( - name='server-name', image='image=id', - flavor='flavor-id', admin_pass='ooBootheiX0edoh')) + name='server-name', image=dict(id='image=id'), + flavor=dict(id='flavor-id'), admin_pass='ooBootheiX0edoh')) @patch.object(OpenStackCloud, "wait_for_server") @patch.object(OpenStackCloud, "nova_client") @@ -188,8 +191,9 @@ class TestCreateServer(base.TestCase): meta.obj_to_dict(fake_server), None, None) server = self.cloud.create_server( - name='server-name', image='image-id', - flavor='flavor-id', admin_pass='ooBootheiX0edoh', wait=True) + name='server-name', image=dict(id='image-id'), + flavor=dict(id='flavor-id'), + admin_pass='ooBootheiX0edoh', wait=True) # Assert that we did wait self.assertTrue(mock_wait.called) @@ -245,7 +249,8 @@ class TestCreateServer(base.TestCase): mock_nova.servers.create.return_value = fake_server self.cloud.create_server( - 'server-name', 'image-id', 'flavor-id', wait=True), + 'server-name', + dict(id='image-id'), dict(id='flavor-id'), wait=True), mock_wait.assert_called_once_with( fake_server, auto_ip=True, ips=None, @@ -291,7 +296,8 @@ class TestCreateServer(base.TestCase): attempt to get the network for the server. """ self.cloud.create_server( - 'server-name', 'image-id', 'flavor-id', network='network-name') + 'server-name', + dict(id='image-id'), dict(id='flavor-id'), network='network-name') mock_get_network.assert_called_once_with(name_or_id='network-name') @patch('shade.OpenStackCloud.nova_client') @@ -304,6 +310,15 @@ class TestCreateServer(base.TestCase): it's treated the same as if 'nics' were not included. """ self.cloud.create_server( - 'server-name', 'image-id', 'flavor-id', + 'server-name', dict(id='image-id'), dict(id='flavor-id'), network='network-name', nics=[]) mock_get_network.assert_called_once_with(name_or_id='network-name') + + @patch('shade.OpenStackCloud.glance_client') + @patch('shade.OpenStackCloud.nova_client') + def test_create_server_get_flavor_image( + self, mock_nova, mock_glance): + self.cloud.create_server( + 'server-name', 'image-id', 'flavor-id') + mock_nova.flavors.list.assert_called_once() + mock_glance.images.list.assert_called_once()