diff --git a/nova/cmd/manage.py b/nova/cmd/manage.py index 8114806ccf7e..3562e87f19f9 100644 --- a/nova/cmd/manage.py +++ b/nova/cmd/manage.py @@ -68,6 +68,7 @@ import oslo_messaging as messaging from oslo_utils import importutils from oslo_utils import uuidutils import six +import six.moves.urllib.parse as urlparse from nova.api.ec2 import ec2utils from nova import availability_zones @@ -1114,6 +1115,46 @@ class CellCommands(object): class CellV2Commands(object): """Commands for managing cells v2.""" + @args('--database_connection', + metavar='', + help='The database connection url for cell0. ' + 'This is optional. If not provided, a standard database ' + 'connection will be used based on the API database connection ' + 'from the Nova configuration.' + ) + def map_cell0(self, database_connection=None): + """Create a cell mapping for cell0. + + cell0 is used for instances that have not been scheduled to any cell. + This generally applies to instances that have encountered an error + before they have been scheduled. + + This command creates a cell mapping for this special cell which + requires a database to store the instance data. + """ + def cell0_default_connection(): + # If no database connection is provided one is generated + # based on the API database connection url. + # The cell0 database will use the same database scheme and + # netloc as the API database, with a related path. + scheme, netloc, path, query, fragment = \ + urlparse.urlsplit(CONF.api_database.connection) + root, ext = os.path.splitext(path) + path = root + "_cell0" + ext + return urlparse.urlunsplit((scheme, netloc, path, query, + fragment)) + + dbc = database_connection or cell0_default_connection() + ctxt = context.RequestContext() + # A transport url of 'none://' is provided for cell0. RPC should not + # be used to access cell0 objects. Cells transport switching will + # ignore any 'none' transport type. + cell_mapping = objects.CellMapping( + ctxt, uuid=objects.CellMapping.CELL0_UUID, name="cell0", + transport_url="none:///", + database_connection=dbc) + cell_mapping.create() + @args('--cell_uuid', metavar='', required=True, help='Unmigrated instances will be mapped to the cell with the ' 'uuid provided.') diff --git a/nova/objects/cell_mapping.py b/nova/objects/cell_mapping.py index cc42290ce0d5..739d6e00f04d 100644 --- a/nova/objects/cell_mapping.py +++ b/nova/objects/cell_mapping.py @@ -22,6 +22,8 @@ class CellMapping(base.NovaTimestampObject, base.NovaObject): # Version 1.0: Initial version VERSION = '1.0' + CELL0_UUID = '00000000-0000-0000-0000-000000000000' + fields = { 'id': fields.IntegerField(read_only=True), 'uuid': fields.UUIDField(), diff --git a/nova/tests/unit/test_nova_manage.py b/nova/tests/unit/test_nova_manage.py index dc61211680de..fe687372459b 100644 --- a/nova/tests/unit/test_nova_manage.py +++ b/nova/tests/unit/test_nova_manage.py @@ -21,6 +21,7 @@ import mock from oslo_utils import uuidutils from nova.cmd import manage +from nova import conf from nova import context from nova import db from nova.db import migration @@ -33,6 +34,8 @@ from nova.tests.unit import fake_instance from nova.tests.unit.objects import test_network from nova.tests.unit import test_flavors +CONF = conf.CONF + class FixedIpCommandsTestCase(test.TestCase): def setUp(self): @@ -882,3 +885,26 @@ class CellV2CommandsTestCase(test.TestCase): inst_mapping = objects.InstanceMapping.get_by_instance_uuid(ctxt, uuid) self.assertEqual(ctxt.project_id, inst_mapping.project_id) + + def test_map_cell0(self): + ctxt = context.RequestContext() + database_connection = 'fake:/foobar//' + self.commands.map_cell0(database_connection) + 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(database_connection, cell_mapping.database_connection) + + def test_map_cell0_default_database(self): + CONF.set_default('connection', + 'fake://netloc/nova_api', + group='api_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('fake://netloc/nova_api_cell0', + cell_mapping.database_connection) diff --git a/releasenotes/notes/create-cell0-mapping-60a9229c223a7516.yaml b/releasenotes/notes/create-cell0-mapping-60a9229c223a7516.yaml new file mode 100644 index 000000000000..9ccd0e74ebbd --- /dev/null +++ b/releasenotes/notes/create-cell0-mapping-60a9229c223a7516.yaml @@ -0,0 +1,7 @@ +--- +upgrade: + - | + A new nova-manage command 'nova-manage cell_v2 map_cell0' is + now available. Creates a cell mapping for cell0, which is used for + storing instances that cannot be scheduled to any cell. This command + only needs to be called when upgrading to CellsV2.