23858725 Cannot nova boot by flavor name
authorSean Wilcox <sean.wilcox@oracle.com>
Tue, 12 Jul 2016 11:21:14 -0600
changeset 6380 833d05f91424
parent 6379 d9146784951c
child 6381 02b02527288b
23858725 Cannot nova boot by flavor name
components/python/novaclient/patches/03-launchpad-1446850.patch
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/python/novaclient/patches/03-launchpad-1446850.patch	Tue Jul 12 11:21:14 2016 -0600
@@ -0,0 +1,216 @@
+This upstream patch has been addressed in Liberty but has not yet been
+addressed in Kilo.
+
+From 098116d6a574b8dede101b257db2ff22d269c6c7 Mon Sep 17 00:00:00 2001
+From: melanie witt <[email protected]>
+Date: Tue, 21 Apr 2015 21:32:33 +0000
+Subject: Revert "nova flavor-show command is inconsistent"
+
+This reverts commit 4e79285b45ec1490c8e923f724cbaf4d42fe81c4.
+
+The aforementioned commit broke flavor-show for mixed case flavorids.
+The reason is a bit complex. On the nova api side, there is caching of
+db items for flavors, keyed off the flavorid retrieved from the db,
+case sensitive, unlike the db query itself. Attempts to flavor-show
+a flavor with flavorid composed of letters will fail with a 400 if
+the capitalization doesn't match.
+
+For the flavor names, they work in lowercase because the find_resource
+function falls back on a search by "human_id" after failing every other
+attempt to find the flavor, because "human_id" is a oslo-slugified
+string (all lowercase, non-word characters removed, spaces converted
+to hyphens).
+
+Closes-Bug: #1446850
+
+Change-Id: I73247b50f5a6918167c071ccc13cd676aa2c7fec
+---
+ novaclient/tests/unit/test_base.py       |  2 +-
+ novaclient/tests/unit/v2/fakes.py        | 20 ++++++++++----------
+ novaclient/tests/unit/v2/test_flavors.py |  4 ++--
+ novaclient/tests/unit/v2/test_shell.py   | 16 ++++++++--------
+ novaclient/v2/shell.py                   | 11 -----------
+ 5 files changed, 21 insertions(+), 32 deletions(-)
+
+diff --git a/novaclient/tests/unit/test_base.py b/novaclient/tests/unit/test_base.py
+index 1ba1d32..b7bceb7 100644
+--- a/novaclient/tests/unit/test_base.py
++++ b/novaclient/tests/unit/test_base.py
[email protected]@ -36,7 +36,7 @@ class BaseTest(utils.TestCase):
+ 
+     def test_resource_lazy_getattr(self):
+         f = flavors.Flavor(cs.flavors, {'id': 1})
+-        self.assertEqual('256 mb server', f.name)
++        self.assertEqual('256 MB Server', f.name)
+         cs.assert_called('GET', '/flavors/1')
+ 
+         # Missing stuff still fails after a second get
+diff --git a/novaclient/tests/unit/v2/fakes.py b/novaclient/tests/unit/v2/fakes.py
+index 343c421..40cd471 100644
+--- a/novaclient/tests/unit/v2/fakes.py
++++ b/novaclient/tests/unit/v2/fakes.py
[email protected]@ -258,7 +258,7 @@ class FakeHTTPClient(base_client.HTTPClient):
+                 },
+                 "flavor": {
+                     "id": 1,
+-                    "name": "256 mb server",
++                    "name": "256 MB Server",
+                 },
+                 "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0",
+                 "status": "BUILD",
[email protected]@ -299,7 +299,7 @@ class FakeHTTPClient(base_client.HTTPClient):
+                 },
+                 "flavor": {
+                     "id": 1,
+-                    "name": "256 mb server",
++                    "name": "256 MB Server",
+                 },
+                 "hostId": "9e107d9d372bb6826bd81d3542a419d6",
+                 "status": "ACTIVE",
[email protected]@ -340,7 +340,7 @@ class FakeHTTPClient(base_client.HTTPClient):
+                 "image": "",
+                 "flavor": {
+                     "id": 1,
+-                    "name": "256 mb server",
++                    "name": "256 MB Server",
+                 },
+                 "hostId": "9e107d9d372bb6826bd81d3542a419d6",
+                 "status": "ACTIVE",
[email protected]@ -672,19 +672,19 @@ class FakeHTTPClient(base_client.HTTPClient):
+ 
+     def get_flavors_detail(self, **kw):
+         flavors = {'flavors': [
+-            {'id': 1, 'name': '256 mb server', 'ram': 256, 'disk': 10,
++            {'id': 1, 'name': '256 MB Server', 'ram': 256, 'disk': 10,
+              'OS-FLV-EXT-DATA:ephemeral': 10,
+              'os-flavor-access:is_public': True,
+              'links': {}},
+-            {'id': 2, 'name': '512 mb server', 'ram': 512, 'disk': 20,
++            {'id': 2, 'name': '512 MB Server', 'ram': 512, 'disk': 20,
+              'OS-FLV-EXT-DATA:ephemeral': 20,
+              'os-flavor-access:is_public': False,
+              'links': {}},
+-            {'id': 4, 'name': '1024 mb server', 'ram': 1024, 'disk': 10,
++            {'id': 4, 'name': '1024 MB Server', 'ram': 1024, 'disk': 10,
+              'OS-FLV-EXT-DATA:ephemeral': 10,
+              'os-flavor-access:is_public': True,
+              'links': {}},
+-            {'id': 'aa1', 'name': '128 mb server', 'ram': 128, 'disk': 0,
++            {'id': 'aa1', 'name': '128 MB Server', 'ram': 128, 'disk': 0,
+              'OS-FLV-EXT-DATA:ephemeral': 0,
+              'os-flavor-access:is_public': True,
+              'links': {}}
[email protected]@ -736,16 +736,16 @@ class FakeHTTPClient(base_client.HTTPClient):
+             {},
+             {'flavor': {
+                 'id': 3,
+-                'name': '256 mb server',
++                'name': '256 MB Server',
+                 'ram': 256,
+                 'disk': 10,
+             }},
+         )
+ 
+-    def get_flavors_512_mb_server(self, **kw):
++    def get_flavors_512_MB_Server(self, **kw):
+         raise exceptions.NotFound('404')
+ 
+-    def get_flavors_128_mb_server(self, **kw):
++    def get_flavors_128_MB_Server(self, **kw):
+         raise exceptions.NotFound('404')
+ 
+     def get_flavors_aa1(self, **kw):
+diff --git a/novaclient/tests/unit/v2/test_flavors.py b/novaclient/tests/unit/v2/test_flavors.py
+index 5045902..4fe43c1 100644
+--- a/novaclient/tests/unit/v2/test_flavors.py
++++ b/novaclient/tests/unit/v2/test_flavors.py
[email protected]@ -93,10 +93,10 @@ class FlavorsTest(utils.TestCase):
+     def test_find(self):
+         f = self.cs.flavors.find(ram=256)
+         self.cs.assert_called('GET', '/flavors/detail')
+-        self.assertEqual('256 mb server', f.name)
++        self.assertEqual('256 MB Server', f.name)
+ 
+         f = self.cs.flavors.find(disk=0)
+-        self.assertEqual('128 mb server', f.name)
++        self.assertEqual('128 MB Server', f.name)
+ 
+         self.assertRaises(exceptions.NotFound, self.cs.flavors.find,
+                           disk=12345)
+diff --git a/novaclient/tests/unit/v2/test_shell.py b/novaclient/tests/unit/v2/test_shell.py
+index b1b9b80..3e7462c 100644
+--- a/novaclient/tests/unit/v2/test_shell.py
++++ b/novaclient/tests/unit/v2/test_shell.py
[email protected]@ -668,10 +668,10 @@ class ShellTest(utils.TestCase):
+ 
+     def test_boot_named_flavor(self):
+         self.run_command(["boot", "--image", "1",
+-                          "--flavor", "512 mb server",
++                          "--flavor", "512 MB Server",
+                           "--max-count", "3", "server"])
+         self.assert_called('GET', '/images/1', pos=0)
+-        self.assert_called('GET', '/flavors/512 mb server', pos=1)
++        self.assert_called('GET', '/flavors/512 MB Server', pos=1)
+         self.assert_called('GET', '/flavors?is_public=None', pos=2)
+         self.assert_called('GET', '/flavors/2', pos=3)
+         self.assert_called(
[email protected]@ -708,15 +708,15 @@ class ShellTest(utils.TestCase):
+         self.assert_called_anytime('GET', '/flavors/aa1')
+ 
+     def test_flavor_show_by_name(self):
+-        self.run_command(['flavor-show', '128 mb server'])
+-        self.assert_called('GET', '/flavors/128 mb server', pos=0)
++        self.run_command(['flavor-show', '128 MB Server'])
++        self.assert_called('GET', '/flavors/128 MB Server', pos=0)
+         self.assert_called('GET', '/flavors?is_public=None', pos=1)
+         self.assert_called('GET', '/flavors/aa1', pos=2)
+         self.assert_called('GET', '/flavors/aa1/os-extra_specs', pos=3)
+ 
+     def test_flavor_show_by_name_priv(self):
+-        self.run_command(['flavor-show', '512 mb server'])
+-        self.assert_called('GET', '/flavors/512 mb server', pos=0)
++        self.run_command(['flavor-show', '512 MB Server'])
++        self.assert_called('GET', '/flavors/512 MB Server', pos=0)
+         self.assert_called('GET', '/flavors?is_public=None', pos=1)
+         self.assert_called('GET', '/flavors/2', pos=2)
+         self.assert_called('GET', '/flavors/2/os-extra_specs', pos=3)
[email protected]@ -753,7 +753,7 @@ class ShellTest(utils.TestCase):
+                            {'addTenantAccess': {'tenant': 'proj2'}})
+ 
+     def test_flavor_access_add_by_name(self):
+-        self.run_command(['flavor-access-add', '512 mb server', 'proj2'])
++        self.run_command(['flavor-access-add', '512 MB Server', 'proj2'])
+         self.assert_called('POST', '/flavors/2/action',
+                            {'addTenantAccess': {'tenant': 'proj2'}})
+ 
[email protected]@ -763,7 +763,7 @@ class ShellTest(utils.TestCase):
+                            {'removeTenantAccess': {'tenant': 'proj2'}})
+ 
+     def test_flavor_access_remove_by_name(self):
+-        self.run_command(['flavor-access-remove', '512 mb server', 'proj2'])
++        self.run_command(['flavor-access-remove', '512 MB Server', 'proj2'])
+         self.assert_called('POST', '/flavors/2/action',
+                            {'removeTenantAccess': {'tenant': 'proj2'}})
+ 
+diff --git a/novaclient/v2/shell.py b/novaclient/v2/shell.py
+index 33653a1..f8b1a10 100644
+--- a/novaclient/v2/shell.py
++++ b/novaclient/v2/shell.py
[email protected]@ -1898,17 +1898,6 @@ def _find_image(cs, image):
+ def _find_flavor(cs, flavor):
+     """Get a flavor by name, ID, or RAM size."""
+     try:
+-        # isinstance() is being used to check if flavor is an instance of
+-        # integer. It will help us to check if the user has entered flavor
+-        # name or flavorid. If flavor name has been entered it is being
+-        # converted to lowercase using lower(). Incase it is an ID the user
+-        # has passed it will not go through the "flavor = flavor.lower()"
+-        # code.The reason for checking if it is a flavor name or flavorid is
+-        #  that int has no lower() so it will give an error.
+-        if isinstance(flavor, six.integer_types):
+-            pass
+-        else:
+-            flavor = flavor.lower()
+         return utils.find_resource(cs.flavors, flavor, is_public=None)
+     except exceptions.NotFound:
+         return cs.flavors.find(ram=flavor)