diff --git a/nova/cmd/manage.py b/nova/cmd/manage.py index be2352dc0874..e0b4579e7875 100644 --- a/nova/cmd/manage.py +++ b/nova/cmd/manage.py @@ -1059,6 +1059,46 @@ class GetLogCommands(object): class CellCommands(object): """Commands for managing cells.""" + def _create_transport_hosts(self, username, password, + broker_hosts=None, hostname=None, port=None): + """Returns a list of oslo.messaging.TransportHost objects.""" + transport_hosts = [] + # Either broker-hosts or hostname should be set + if broker_hosts: + hosts = broker_hosts.split(',') + for host in hosts: + host = host.strip() + broker_hostname, broker_port = utils.parse_server_string(host) + if not broker_port: + msg = _('Invalid broker_hosts value: %s. It should be' + ' in hostname:port format') % host + raise ValueError(msg) + try: + broker_port = int(broker_port) + except ValueError: + msg = _('Invalid port value: %s. It should be ' + 'an integer') % broker_port + raise ValueError(msg) + transport_hosts.append( + messaging.TransportHost( + hostname=broker_hostname, + port=broker_port, + username=username, + password=password)) + else: + try: + port = int(port) + except ValueError: + msg = _("Invalid port value: %s. Should be an integer") % port + raise ValueError(msg) + transport_hosts.append( + messaging.TransportHost( + hostname=hostname, + port=port, + username=username, + password=password)) + return transport_hosts + @args('--name', metavar='', help='Name for the new cell') @args('--cell_type', metavar='', help='Whether the cell is a parent or child') @@ -1066,6 +1106,11 @@ class CellCommands(object): help='Username for the message broker in this cell') @args('--password', metavar='', help='Password for the message broker in this cell') + @args('--broker_hosts', metavar='', + help='Comma separated list of message brokers in this cell. ' + 'Each Broker is specified as hostname:port with both ' + 'mandatory. This option overrides the --hostname ' + 'and --port options (if provided). ') @args('--hostname', metavar='', help='Address of the message broker in this cell') @args('--port', metavar='', @@ -1074,8 +1119,8 @@ class CellCommands(object): help='The virtual host of the message broker in this cell') @args('--woffset', metavar='') @args('--wscale', metavar='') - def create(self, name, cell_type='child', username=None, password=None, - hostname=None, port=None, virtual_host=None, + def create(self, name, cell_type='child', username=None, broker_hosts=None, + password=None, hostname=None, port=None, virtual_host=None, woffset=None, wscale=None): if cell_type not in ['parent', 'child']: @@ -1083,13 +1128,12 @@ class CellCommands(object): return(2) # Set up the transport URL - transport_host = messaging.TransportHost(hostname=hostname, - port=int(port), - username=username, - password=password) - + transport_hosts = self._create_transport_hosts( + username, password, + broker_hosts, hostname, + port) transport_url = rpc.get_transport_url() - transport_url.hosts.append(transport_host) + transport_url.hosts.extend(transport_hosts) transport_url.virtual_host = virtual_host is_parent = cell_type == 'parent' diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index 14ed25754d86..98f5636cbca5 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -17,6 +17,7 @@ import StringIO import sys import fixtures +import mock from nova.cmd import manage from nova import context @@ -346,3 +347,121 @@ class ServiceCommandsTestCase(test.TestCase): def test_service_disable_invalid_params(self): self.assertEqual(2, self.commands.disable('nohost', 'noservice')) + + +class CellCommandsTestCase(test.TestCase): + def setUp(self): + super(CellCommandsTestCase, self).setUp() + self.commands = manage.CellCommands() + + def test_create_transport_hosts_multiple(self): + """Test the _create_transport_hosts method + when broker_hosts is set. + """ + brokers = "127.0.0.1:5672,127.0.0.2:5671" + thosts = self.commands._create_transport_hosts( + 'guest', 'devstack', + broker_hosts=brokers) + self.assertEqual(2, len(thosts)) + self.assertEqual('127.0.0.1', thosts[0].hostname) + self.assertEqual(5672, thosts[0].port) + self.assertEqual('127.0.0.2', thosts[1].hostname) + self.assertEqual(5671, thosts[1].port) + + def test_create_transport_hosts_single(self): + """Test the _create_transport_hosts method when hostname is passed.""" + thosts = self.commands._create_transport_hosts('guest', 'devstack', + hostname='127.0.0.1', + port=80) + self.assertEqual(1, len(thosts)) + self.assertEqual('127.0.0.1', thosts[0].hostname) + self.assertEqual(80, thosts[0].port) + + def test_create_transport_hosts_single_broker(self): + """Test the _create_transport_hosts method for single broker_hosts.""" + thosts = self.commands._create_transport_hosts( + 'guest', 'devstack', + broker_hosts='127.0.0.1:5672') + self.assertEqual(1, len(thosts)) + self.assertEqual('127.0.0.1', thosts[0].hostname) + self.assertEqual(5672, thosts[0].port) + + def test_create_transport_hosts_both(self): + """Test the _create_transport_hosts method when both broker_hosts + and hostname/port are passed. + """ + thosts = self.commands._create_transport_hosts( + 'guest', 'devstack', + broker_hosts='127.0.0.1:5672', + hostname='127.0.0.2', port=80) + self.assertEqual(1, len(thosts)) + self.assertEqual('127.0.0.1', thosts[0].hostname) + self.assertEqual(5672, thosts[0].port) + + def test_create_transport_hosts_wrong_val(self): + """Test the _create_transport_hosts method when broker_hosts + is wrongly sepcified + """ + self.assertRaises(ValueError, + self.commands._create_transport_hosts, + 'guest', 'devstack', + broker_hosts='127.0.0.1:5672,127.0.0.1') + + def test_create_transport_hosts_wrong_port_val(self): + """Test the _create_transport_hosts method when port in + broker_hosts is wrongly sepcified + """ + self.assertRaises(ValueError, + self.commands._create_transport_hosts, + 'guest', 'devstack', + broker_hosts='127.0.0.1:') + + def test_create_transport_hosts_wrong_port_arg(self): + """Test the _create_transport_hosts method when port + argument is wrongly sepcified + """ + self.assertRaises(ValueError, + self.commands._create_transport_hosts, + 'guest', 'devstack', + hostname='127.0.0.1', port='ab') + + @mock.patch.object(context, 'get_admin_context') + @mock.patch.object(db, 'cell_create') + def test_create_broker_hosts(self, mock_db_cell_create, mock_ctxt): + """Test the create function when broker_hosts is + passed + """ + cell_tp_url = "fake://guest:devstack@127.0.0.1:5432" + cell_tp_url += ",guest:devstack@127.0.0.2:9999/" + ctxt = mock.sentinel + mock_ctxt.return_value = mock.sentinel + self.commands.create("test", + broker_hosts='127.0.0.1:5432,127.0.0.2:9999', + woffset=0, wscale=0, + username="guest", password="devstack") + exp_values = {'name': "test", + 'is_parent': False, + 'transport_url': cell_tp_url, + 'weight_offset': 0.0, + 'weight_scale': 0.0} + mock_db_cell_create.assert_called_once_with(ctxt, exp_values) + + @mock.patch.object(context, 'get_admin_context') + @mock.patch.object(db, 'cell_create') + def test_create_hostname(self, mock_db_cell_create, mock_ctxt): + """Test the create function when hostname and port is + passed + """ + cell_tp_url = "fake://guest:devstack@127.0.0.1:9999/" + ctxt = mock.sentinel + mock_ctxt.return_value = mock.sentinel + self.commands.create("test", + hostname='127.0.0.1', port="9999", + woffset=0, wscale=0, + username="guest", password="devstack") + exp_values = {'name': "test", + 'is_parent': False, + 'transport_url': cell_tp_url, + 'weight_offset': 0.0, + 'weight_scale': 0.0} + mock_db_cell_create.assert_called_once_with(ctxt, exp_values)