Merge "Add a cell and host mapping utility to nova-manage"
This commit is contained in:
commit
bc5035343d
|
@ -66,6 +66,7 @@ from oslo_db import exception as db_exc
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
import oslo_messaging as messaging
|
import oslo_messaging as messaging
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
|
from oslo_utils import uuidutils
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from nova.api.ec2 import ec2utils
|
from nova.api.ec2 import ec2utils
|
||||||
|
@ -95,6 +96,7 @@ CONF.import_opt('vlan_start', 'nova.network.manager')
|
||||||
CONF.import_opt('vpn_start', 'nova.network.manager')
|
CONF.import_opt('vpn_start', 'nova.network.manager')
|
||||||
CONF.import_opt('default_floating_pool', 'nova.network.floating_ips')
|
CONF.import_opt('default_floating_pool', 'nova.network.floating_ips')
|
||||||
CONF.import_opt('public_interface', 'nova.network.linux_net')
|
CONF.import_opt('public_interface', 'nova.network.linux_net')
|
||||||
|
CONF.import_opt('connection', 'oslo_db.options', group='database')
|
||||||
|
|
||||||
QUOTAS = quota.QUOTAS
|
QUOTAS = quota.QUOTAS
|
||||||
|
|
||||||
|
@ -1360,6 +1362,74 @@ class CellV2Commands(object):
|
||||||
instance = instances[-1]
|
instance = instances[-1]
|
||||||
print('Next marker: - %s' % instance.uuid)
|
print('Next marker: - %s' % instance.uuid)
|
||||||
|
|
||||||
|
# TODO(melwitt): Remove this when the oslo.messaging function
|
||||||
|
# for assembling a transport url from ConfigOpts is available
|
||||||
|
@args('--transport-url', metavar='<transport url>', required=True,
|
||||||
|
dest='transport_url',
|
||||||
|
help='The transport url for the cell message queue')
|
||||||
|
@args('--name', metavar='<name>', help='The name of the cell')
|
||||||
|
@args('--verbose', action='store_true',
|
||||||
|
help='Return and output the uuid of the created cell')
|
||||||
|
def map_cell_and_hosts(self, transport_url, name=None, verbose=False):
|
||||||
|
"""EXPERIMENTAL. Create a cell mapping and host mappings for a cell.
|
||||||
|
|
||||||
|
Users not dividing their cloud into multiple cells will be a single
|
||||||
|
cell v2 deployment and should specify:
|
||||||
|
|
||||||
|
nova-manage cell_v2 map_cell_and_hosts --config-file <nova.conf>
|
||||||
|
|
||||||
|
Users running multiple cells can add a cell v2 by specifying:
|
||||||
|
|
||||||
|
nova-manage cell_v2 map_cell_and_hosts --config-file <cell nova.conf>
|
||||||
|
"""
|
||||||
|
ctxt = context.RequestContext()
|
||||||
|
cell_mapping_uuid = cell_mapping = None
|
||||||
|
# First, try to detect if a CellMapping has already been created
|
||||||
|
compute_nodes = objects.ComputeNodeList.get_all(ctxt)
|
||||||
|
if not compute_nodes:
|
||||||
|
print(_('No hosts found to map to cell, exiting.'))
|
||||||
|
return(0)
|
||||||
|
missing_nodes = []
|
||||||
|
for compute_node in compute_nodes:
|
||||||
|
try:
|
||||||
|
host_mapping = objects.HostMapping.get_by_host(
|
||||||
|
ctxt, compute_node.host)
|
||||||
|
except exception.HostMappingNotFound:
|
||||||
|
missing_nodes.append(compute_node)
|
||||||
|
else:
|
||||||
|
if verbose:
|
||||||
|
print(_(
|
||||||
|
'Host %(host)s is already mapped to cell %(uuid)s'
|
||||||
|
) % {'host': host_mapping.host,
|
||||||
|
'uuid': host_mapping.cell_mapping.uuid})
|
||||||
|
# Re-using the existing UUID in case there is already a mapping
|
||||||
|
# NOTE(sbauza): There could be possibly multiple CellMappings
|
||||||
|
# if the operator provides another configuration file and moves
|
||||||
|
# the hosts to another cell v2, but that's not really something
|
||||||
|
# we should support.
|
||||||
|
cell_mapping_uuid = host_mapping.cell_mapping.uuid
|
||||||
|
if not missing_nodes:
|
||||||
|
print(_('All hosts are already mapped to cell(s), exiting.'))
|
||||||
|
return(0)
|
||||||
|
# Create the cell mapping in the API database
|
||||||
|
if cell_mapping_uuid is not None:
|
||||||
|
cell_mapping = objects.CellMapping.get_by_uuid(
|
||||||
|
ctxt, cell_mapping_uuid)
|
||||||
|
if cell_mapping is None:
|
||||||
|
cell_mapping_uuid = uuidutils.generate_uuid()
|
||||||
|
cell_mapping = objects.CellMapping(
|
||||||
|
ctxt, uuid=cell_mapping_uuid, name=name,
|
||||||
|
transport_url=transport_url,
|
||||||
|
database_connection=CONF.database.connection)
|
||||||
|
cell_mapping.create()
|
||||||
|
# Pull the hosts from the cell database and create the host mappings
|
||||||
|
for compute_node in missing_nodes:
|
||||||
|
host_mapping = objects.HostMapping(
|
||||||
|
ctxt, host=compute_node.host, cell_mapping=cell_mapping)
|
||||||
|
host_mapping.create()
|
||||||
|
if verbose:
|
||||||
|
print(cell_mapping_uuid)
|
||||||
|
|
||||||
|
|
||||||
CATEGORIES = {
|
CATEGORIES = {
|
||||||
'account': AccountCommands,
|
'account': AccountCommands,
|
||||||
|
|
|
@ -18,6 +18,7 @@ import sys
|
||||||
|
|
||||||
import fixtures
|
import fixtures
|
||||||
import mock
|
import mock
|
||||||
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
from nova.cmd import manage
|
from nova.cmd import manage
|
||||||
from nova import context
|
from nova import context
|
||||||
|
@ -680,3 +681,140 @@ class CellCommandsTestCase(test.NoDBTestCase):
|
||||||
'weight_offset': 0.0,
|
'weight_offset': 0.0,
|
||||||
'weight_scale': 0.0}
|
'weight_scale': 0.0}
|
||||||
mock_db_cell_create.assert_called_once_with(ctxt, exp_values)
|
mock_db_cell_create.assert_called_once_with(ctxt, exp_values)
|
||||||
|
|
||||||
|
|
||||||
|
class CellV2CommandsTestCase(test.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(CellV2CommandsTestCase, self).setUp()
|
||||||
|
self.useFixture(fixtures.MonkeyPatch('sys.stdout', StringIO()))
|
||||||
|
self.commands = manage.CellV2Commands()
|
||||||
|
|
||||||
|
def test_map_cell_and_hosts(self):
|
||||||
|
# Create some fake compute nodes and check if they get host mappings
|
||||||
|
ctxt = context.RequestContext()
|
||||||
|
values = {
|
||||||
|
'vcpus': 4,
|
||||||
|
'memory_mb': 4096,
|
||||||
|
'local_gb': 1024,
|
||||||
|
'vcpus_used': 2,
|
||||||
|
'memory_mb_used': 2048,
|
||||||
|
'local_gb_used': 512,
|
||||||
|
'hypervisor_type': 'Hyper-Dan-VM-ware',
|
||||||
|
'hypervisor_version': 1001,
|
||||||
|
'cpu_info': 'Schmintel i786',
|
||||||
|
}
|
||||||
|
for i in range(3):
|
||||||
|
host = 'host%s' % i
|
||||||
|
compute_node = objects.ComputeNode(ctxt, host=host, **values)
|
||||||
|
compute_node.create()
|
||||||
|
cell_transport_url = "fake://guest:devstack@127.0.0.1:9999/"
|
||||||
|
self.commands.map_cell_and_hosts(cell_transport_url, name='ssd',
|
||||||
|
verbose=True)
|
||||||
|
cell_mapping_uuid = sys.stdout.getvalue().strip()
|
||||||
|
# Verify the cell mapping
|
||||||
|
cell_mapping = objects.CellMapping.get_by_uuid(ctxt, cell_mapping_uuid)
|
||||||
|
self.assertEqual('ssd', cell_mapping.name)
|
||||||
|
self.assertEqual(cell_transport_url, cell_mapping.transport_url)
|
||||||
|
# Verify the host mappings
|
||||||
|
for i in range(3):
|
||||||
|
host = 'host%s' % i
|
||||||
|
host_mapping = objects.HostMapping.get_by_host(ctxt, host)
|
||||||
|
self.assertEqual(cell_mapping.uuid, host_mapping.cell_mapping.uuid)
|
||||||
|
|
||||||
|
def test_map_cell_and_hosts_duplicate(self):
|
||||||
|
# Create a cell mapping and hosts and check that nothing new is created
|
||||||
|
ctxt = context.RequestContext()
|
||||||
|
cell_mapping_uuid = uuidutils.generate_uuid()
|
||||||
|
cell_mapping = objects.CellMapping(
|
||||||
|
ctxt, uuid=cell_mapping_uuid, name='fake',
|
||||||
|
transport_url='fake://', database_connection='fake://')
|
||||||
|
cell_mapping.create()
|
||||||
|
# Create compute nodes that will map to the cell
|
||||||
|
values = {
|
||||||
|
'vcpus': 4,
|
||||||
|
'memory_mb': 4096,
|
||||||
|
'local_gb': 1024,
|
||||||
|
'vcpus_used': 2,
|
||||||
|
'memory_mb_used': 2048,
|
||||||
|
'local_gb_used': 512,
|
||||||
|
'hypervisor_type': 'Hyper-Dan-VM-ware',
|
||||||
|
'hypervisor_version': 1001,
|
||||||
|
'cpu_info': 'Schmintel i786',
|
||||||
|
}
|
||||||
|
for i in range(3):
|
||||||
|
host = 'host%s' % i
|
||||||
|
compute_node = objects.ComputeNode(ctxt, host=host, **values)
|
||||||
|
compute_node.create()
|
||||||
|
host_mapping = objects.HostMapping(
|
||||||
|
ctxt, host=host, cell_mapping=cell_mapping)
|
||||||
|
host_mapping.create()
|
||||||
|
cell_transport_url = "fake://guest:devstack@127.0.0.1:9999/"
|
||||||
|
retval = self.commands.map_cell_and_hosts(cell_transport_url,
|
||||||
|
name='ssd',
|
||||||
|
verbose=True)
|
||||||
|
self.assertEqual(0, retval)
|
||||||
|
output = sys.stdout.getvalue().strip()
|
||||||
|
expected = ''
|
||||||
|
for i in range(3):
|
||||||
|
expected += ('Host host%s is already mapped to cell %s\n' %
|
||||||
|
(i, cell_mapping_uuid))
|
||||||
|
expected += 'All hosts are already mapped to cell(s), exiting.'
|
||||||
|
self.assertEqual(expected, output)
|
||||||
|
|
||||||
|
def test_map_cell_and_hosts_partial_update(self):
|
||||||
|
# Create a cell mapping and partial hosts and check that
|
||||||
|
# missing HostMappings are created
|
||||||
|
ctxt = context.RequestContext()
|
||||||
|
cell_mapping_uuid = uuidutils.generate_uuid()
|
||||||
|
cell_mapping = objects.CellMapping(
|
||||||
|
ctxt, uuid=cell_mapping_uuid, name='fake',
|
||||||
|
transport_url='fake://', database_connection='fake://')
|
||||||
|
cell_mapping.create()
|
||||||
|
# Create compute nodes that will map to the cell
|
||||||
|
values = {
|
||||||
|
'vcpus': 4,
|
||||||
|
'memory_mb': 4096,
|
||||||
|
'local_gb': 1024,
|
||||||
|
'vcpus_used': 2,
|
||||||
|
'memory_mb_used': 2048,
|
||||||
|
'local_gb_used': 512,
|
||||||
|
'hypervisor_type': 'Hyper-Dan-VM-ware',
|
||||||
|
'hypervisor_version': 1001,
|
||||||
|
'cpu_info': 'Schmintel i786',
|
||||||
|
}
|
||||||
|
for i in range(3):
|
||||||
|
host = 'host%s' % i
|
||||||
|
compute_node = objects.ComputeNode(ctxt, host=host, **values)
|
||||||
|
compute_node.create()
|
||||||
|
# Only create 2 existing HostMappings out of 3
|
||||||
|
for i in range(2):
|
||||||
|
host = 'host%s' % i
|
||||||
|
host_mapping = objects.HostMapping(
|
||||||
|
ctxt, host=host, cell_mapping=cell_mapping)
|
||||||
|
host_mapping.create()
|
||||||
|
cell_transport_url = "fake://guest:devstack@127.0.0.1:9999/"
|
||||||
|
self.commands.map_cell_and_hosts(cell_transport_url,
|
||||||
|
name='ssd',
|
||||||
|
verbose=True)
|
||||||
|
# Verify the HostMapping for the last host was created
|
||||||
|
host_mapping = objects.HostMapping.get_by_host(ctxt, 'host2')
|
||||||
|
self.assertEqual(cell_mapping.uuid, host_mapping.cell_mapping.uuid)
|
||||||
|
# Verify the output
|
||||||
|
output = sys.stdout.getvalue().strip()
|
||||||
|
expected = ''
|
||||||
|
for i in range(2):
|
||||||
|
expected += ('Host host%s is already mapped to cell %s\n' %
|
||||||
|
(i, cell_mapping_uuid))
|
||||||
|
# The expected CellMapping UUID for the last host should be the same
|
||||||
|
expected += cell_mapping.uuid
|
||||||
|
self.assertEqual(expected, output)
|
||||||
|
|
||||||
|
def test_map_cell_and_hosts_no_hosts_found(self):
|
||||||
|
cell_transport_url = "fake://guest:devstack@127.0.0.1:9999/"
|
||||||
|
retval = self.commands.map_cell_and_hosts(cell_transport_url,
|
||||||
|
name='ssd',
|
||||||
|
verbose=True)
|
||||||
|
self.assertEqual(0, retval)
|
||||||
|
output = sys.stdout.getvalue().strip()
|
||||||
|
expected = 'No hosts found to map to cell, exiting.'
|
||||||
|
self.assertEqual(expected, output)
|
||||||
|
|
Loading…
Reference in New Issue