]> xenbits.xensource.com Git - osstest/openstack-nova.git/commitdiff
Handle special characters in database connection URL netloc
authorMatt Riedemann <mriedem.os@gmail.com>
Thu, 11 May 2017 22:29:42 +0000 (18:29 -0400)
committerMatt Riedemann <mriedem.os@gmail.com>
Mon, 22 May 2017 15:20:55 +0000 (11:20 -0400)
When calling "nova-manage cell_v2 simple_cell_setup" or
"nova-manage cell_v2 map_cell0" without passing in the
--database_connection option, we read the [database]/connection
URL from nova.conf, try to split the URL and then create a
default connection based on the name of the original connection,
so if you're cell database's name is 'nova' you'd end up with
'nova_cell0' for the cell0 database name in the URL.

The problem is the database connection URL has credentials in the
netloc and if the password has special characters in it, those can
mess up the URL split, like splitting on ? which is normally denoting
the beginning of the path in a URL.

This change handles special characters in the password by using
a nice DB connection URL parsing utility method available in
sqlalchemy to get the database name out of the connection URL string
so we can replace it properly with the _cell0 suffix.

Adds a release note as this bug causes issues when upgrading.

Change-Id: I7a7678e4af8160e6f48b96095154fca6ca48ff09
Closes-Bug: #1673613
(cherry picked from commit 9a9a620ea2d06e51c01b0864d7275b57d7203e5a)

nova/cmd/manage.py
nova/tests/unit/test_nova_manage.py
releasenotes/notes/bug-1673613-7357d40ba9ab1fa6.yaml [new file with mode: 0644]

index 9e4e74151e82ff88d0985652584a2f2c0fde9b39..00856c3637d48d2a25e5dd53b0ba9ed35acad967 100644 (file)
@@ -70,6 +70,7 @@ from oslo_utils import uuidutils
 import prettytable
 
 import six.moves.urllib.parse as urlparse
+from sqlalchemy.engine import url as sqla_url
 
 from nova.api.ec2 import ec2utils
 from nova import availability_zones
@@ -1151,12 +1152,19 @@ class CellV2Commands(object):
             # based on the database connection url.
             # The cell0 database will use the same database scheme and
             # netloc as the main database, with a related path.
-            scheme, netloc, path, query, fragment = \
-                urlparse.urlsplit(CONF.database.connection)
-            root, ext = os.path.splitext(path)
-            path = root + "_cell0" + ext
-            return urlparse.urlunsplit((scheme, netloc, path, query,
-                                        fragment))
+            connection = CONF.database.connection
+            # sqlalchemy has a nice utility for parsing database connection
+            # URLs so we use that here to get the db name so we don't have to
+            # worry about parsing and splitting a URL which could have special
+            # characters in the password, which makes parsing a nightmare.
+            url = sqla_url.make_url(connection)
+            cell0_db_name = url.database + '_cell0'
+            # We need to handle multiple occurrences of the substring, e.g. if
+            # the username and db name are both 'nova' we need to only replace
+            # the last one, which is the database name in the URL, not the
+            # username.
+            connection = connection.rstrip(url.database)
+            return connection + cell0_db_name
 
         dbc = database_connection or cell0_default_connection()
         ctxt = context.RequestContext()
index 8d8e3df882f18bd9e0bca751137321d5489ae610..d9208280009b0ff15694a7c979c29dcfab911afb 100644 (file)
@@ -15,6 +15,7 @@
 
 import sys
 
+import ddt
 import fixtures
 import mock
 from oslo_db import exception as db_exc
@@ -809,6 +810,7 @@ class CellCommandsTestCase(test.NoDBTestCase):
         mock_db_cell_create.assert_called_once_with(ctxt, exp_values)
 
 
+@ddt.ddt
 class CellV2CommandsTestCase(test.NoDBTestCase):
     USES_DB_SELF = True
 
@@ -1138,6 +1140,29 @@ class CellV2CommandsTestCase(test.NoDBTestCase):
         self.assertEqual('fake://netloc/nova_cell0',
                          cell_mapping.database_connection)
 
+    @ddt.data('mysql+pymysql://nova:abcd0123:AB@controller/nova',
+              'mysql+pymysql://nova:abcd0123?AB@controller/nova',
+              'mysql+pymysql://nova:abcd0123@AB@controller/nova',
+              'mysql+pymysql://nova:abcd0123/AB@controller/nova',
+              'mysql+pymysql://test:abcd0123%AB@controller/nova')
+    def test_map_cell0_default_database_special_characters(self,
+                                                           decoded_connection):
+        """Tests that a URL with special characters, like in the credentials,
+        is handled properly.
+        """
+        self.flags(connection=decoded_connection, group='database')
+        ctxt = context.RequestContext()
+        self.commands.map_cell0()
+        cell_mapping = objects.CellMapping.get_by_uuid(
+            ctxt, objects.CellMapping.CELL0_UUID)
+        self.assertEqual('cell0', cell_mapping.name)
+        self.assertEqual('none:///', cell_mapping.transport_url)
+        self.assertEqual(
+            decoded_connection + '_cell0',
+            cell_mapping.database_connection)
+        # Delete the cell mapping for the next iteration.
+        cell_mapping.destroy()
+
     def _test_migrate_simple_command(self, cell0_sync_fail=False):
         ctxt = context.RequestContext()
         CONF.set_default('connection',
diff --git a/releasenotes/notes/bug-1673613-7357d40ba9ab1fa6.yaml b/releasenotes/notes/bug-1673613-7357d40ba9ab1fa6.yaml
new file mode 100644 (file)
index 0000000..cd95df6
--- /dev/null
@@ -0,0 +1,9 @@
+---
+fixes:
+  - |
+    Includes the fix for `bug 1673613`_ which could cause issues when upgrading
+    and running ``nova-manage cell_v2 simple_cell_setup`` or
+    ``nova-manage cell_v2 map_cell0`` where the database connection is read
+    from config and has special characters in the URL.
+
+    .. _bug 1673613: https://launchpad.net/bugs/1673613