diff --git a/doc/source/cli/nova-manage.rst b/doc/source/cli/nova-manage.rst index 3601d55cac49..c364ba2f9189 100644 --- a/doc/source/cli/nova-manage.rst +++ b/doc/source/cli/nova-manage.rst @@ -231,9 +231,10 @@ Nova Cells v2 defined by ``[database]/connection`` in the configuration file. If a transport_url is not specified, it will attempt to use the one defined by ``[DEFAULT]/transport_url`` in the configuration file. If the cell is not - found by uuid, this command will return an exit code of 1. If the - properties cannot be set, this will return 2. Otherwise, the exit code will - be 0. + found by uuid, this command will return an exit code of 1. If the provided + transport_url or/and database_connection is/are same as another cell, + this command will return an exit code of 3. If the properties cannot be set, + this will return 2. Otherwise, the exit code will be 0. .. note:: diff --git a/nova/cmd/manage.py b/nova/cmd/manage.py index 7653ab09aec5..38a3f33c6b06 100644 --- a/nova/cmd/manage.py +++ b/nova/cmd/manage.py @@ -931,6 +931,17 @@ class CellV2Commands(object): 'is not set in the configuration file.') return transport_url + def _non_unique_transport_url_database_connection_checker(self, ctxt, + transport_url, database_connection): + for cell in objects.CellMappingList.get_all(ctxt): + if (cell.database_connection == database_connection or + cell.transport_url == transport_url): + print(_('The specified transport_url and/or ' + 'database_connection combination already exists ' + 'for another cell with uuid %s.') % cell.uuid) + return True + return False + @args('--transport-url', metavar='', dest='transport_url', help='The transport url for the cell message queue') def simple_cell_setup(self, transport_url=None): @@ -1308,11 +1319,8 @@ class CellV2Commands(object): 'if [database]/connection is not set ' 'in the configuration file.')) return 1 - if any(cell.database_connection == database_connection - and cell.transport_url == transport_url - for cell in objects.CellMappingList.get_all(ctxt)): - print(_('Cell with the specified transport_url ' - 'and database_connection combination already exists')) + if (self._non_unique_transport_url_database_connection_checker(ctxt, + transport_url, database_connection)): return 2 cell_mapping_uuid = uuidutils.generate_uuid() cell_mapping = objects.CellMapping( @@ -1433,7 +1441,9 @@ class CellV2Commands(object): """Updates the properties of a cell by the given uuid. If the cell is not found by uuid, this command will return an exit - code of 1. If the properties cannot be set, this will return 2. + code of 1. If the provided transport_url or/and database_connection + is/are same as another cell, this command will return an exit code + of 3. If the properties cannot be set, this will return 2. Otherwise, the exit code will be 0. NOTE: Updating the transport_url or database_connection fields on @@ -1451,10 +1461,17 @@ class CellV2Commands(object): cell_mapping.name = name transport_url = transport_url or CONF.transport_url + db_connection = db_connection or CONF.database.connection + + if (self._non_unique_transport_url_database_connection_checker(ctxt, + transport_url, db_connection)): + # We use the return code 3 before 2 to avoid changing the + # semantic meanings of return codes. + return 3 + if transport_url: cell_mapping.transport_url = transport_url - db_connection = db_connection or CONF.database.connection if db_connection: cell_mapping.database_connection = db_connection diff --git a/nova/tests/unit/test_nova_manage.py b/nova/tests/unit/test_nova_manage.py index 54734f237fce..9ef27f965408 100644 --- a/nova/tests/unit/test_nova_manage.py +++ b/nova/tests/unit/test_nova_manage.py @@ -1554,6 +1554,26 @@ class CellV2CommandsTestCase(test.NoDBTestCase): self.assertEqual(from_cli, self.commands._validate_transport_url(from_cli)) + def test_non_unique_transport_url_database_connection_checker(self): + ctxt = context.RequestContext() + objects.CellMapping(context=ctxt, uuid=uuidsentinel.cell1, + name='cell1', + transport_url='fake://mq1', + database_connection='fake:///db1').create() + objects.CellMapping(context=ctxt, uuid=uuidsentinel.cell2, + name='cell2', + transport_url='fake://mq2', + database_connection='fake:///db2').create() + resultf = self.commands.\ + _non_unique_transport_url_database_connection_checker( + ctxt, 'fake://mq3', 'fake:///db3') + resultt = self.commands.\ + _non_unique_transport_url_database_connection_checker( + ctxt, 'fake://mq1', 'fake:///db1') + self.assertFalse(resultf) + self.assertTrue(resultt) + self.assertIn('exists', self.output.getvalue()) + def test_create_cell_use_params(self): ctxt = context.get_context() kwargs = dict( @@ -1778,6 +1798,35 @@ class CellV2CommandsTestCase(test.NoDBTestCase): uuidsentinel.cell1, 'foo', 'fake://new', 'fake:///new')) self.assertIn('not found', self.output.getvalue()) + def test_update_cell_failed_if_non_unique_transport_db_urls(self): + ctxt = context.get_admin_context() + objects.CellMapping(context=ctxt, uuid=uuidsentinel.cell1, + name='cell1', + transport_url='fake://mq1', + database_connection='fake:///db1').create() + objects.CellMapping(context=ctxt, uuid=uuidsentinel.cell2, + name='cell2', + transport_url='fake://mq2', + database_connection='fake:///db2').create() + cell2_update1 = self.commands.update_cell( + uuidsentinel.cell2, 'foo', 'fake://mq1', 'fake:///db1') + self.assertEqual(3, cell2_update1) + self.assertIn('exists', self.output.getvalue()) + + cell2_update2 = self.commands.update_cell( + uuidsentinel.cell2, 'foo', 'fake://mq1', 'fake:///db3') + self.assertEqual(3, cell2_update2) + self.assertIn('exists', self.output.getvalue()) + + cell2_update3 = self.commands.update_cell( + uuidsentinel.cell2, 'foo', 'fake://mq3', 'fake:///db1') + self.assertEqual(3, cell2_update3) + self.assertIn('exists', self.output.getvalue()) + + cell2_update4 = self.commands.update_cell( + uuidsentinel.cell2, 'foo', 'fake://mq3', 'fake:///db3') + self.assertEqual(0, cell2_update4) + def test_update_cell_failed(self): ctxt = context.get_admin_context() objects.CellMapping(context=ctxt, uuid=uuidsentinel.cell1,