From 9a4ce8973079a098a19ad43005a9cf385b3947e5 Mon Sep 17 00:00:00 2001 From: Lucas Alvares Gomes Date: Tue, 11 Mar 2014 10:52:34 +0000 Subject: [PATCH] PXE validate() to fail if no Ironic API URL As part of the deploy process the conductor should pass the URL of the Ironic API to the deploy ramdisk so that it can talk back to the Ironic API, but if the URL is not present in the Ironic configuration file or in the keystone catalog the deploy process will fail in the middle. This patch adds a verification to the validate() method to make sure the URL is present before the deploy starts. Related-Bug: #1290482 Change-Id: I95472660deff0e23a19d0b744f711bbe3acc2191 --- ironic/drivers/modules/pxe.py | 11 +++++++++ ironic/tests/drivers/test_pxe.py | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/ironic/drivers/modules/pxe.py b/ironic/drivers/modules/pxe.py index a3a10abff3..b4d2bb0042 100644 --- a/ironic/drivers/modules/pxe.py +++ b/ironic/drivers/modules/pxe.py @@ -544,6 +544,17 @@ class PXEDeploy(base.DeployInterface): "any port associated with it.") % node.uuid) _parse_driver_info(node) + # Try to get the URL of the Ironic API + try: + # TODO(lucasagomes): Validate the format of the URL + CONF.conductor.api_url or keystone.get_service_url() + except (exception.CatalogFailure, + exception.CatalogNotFound, + exception.CatalogUnauthorized): + raise exception.InvalidParameterValue(_( + "Couldn't get the URL of the Ironic API service from the " + "configuration file or keystone catalog.")) + @task_manager.require_exclusive_lock def deploy(self, task, node): """Perform start deployment a node. diff --git a/ironic/tests/drivers/test_pxe.py b/ironic/tests/drivers/test_pxe.py index 0c6da2f7b2..177c944e28 100644 --- a/ironic/tests/drivers/test_pxe.py +++ b/ironic/tests/drivers/test_pxe.py @@ -30,6 +30,7 @@ from ironic.common import exception from ironic.common.glance_service import base_image_service from ironic.common.glance_service import service_utils from ironic.common import images +from ironic.common import keystone from ironic.common import neutron from ironic.common import states from ironic.common import utils @@ -614,6 +615,7 @@ class PXEDriverTestCase(db_base.DbTestCase): self.node = self.dbapi.create_node(n) self.port = self.dbapi.create_port(db_utils.get_test_port( node_id=self.node.id)) + self.config(group='conductor', api_url='http://127.0.0.1:1234/') def _create_token_file(self): token_path = pxe._get_token_file_path(self.node['uuid']) @@ -645,6 +647,42 @@ class PXEDriverTestCase(db_base.DbTestCase): task.resources[0].driver.deploy.validate, task, new_node) + @mock.patch.object(keystone, 'get_service_url') + def test_validate_good_api_url_from_config_file(self, mock_ks): + # not present in the keystone catalog + mock_ks.side_effect = exception.CatalogFailure + + with task_manager.acquire(self.context, [self.node.uuid], + shared=True) as task: + task.resources[0].driver.deploy.validate(task, self.node) + self.assertFalse(mock_ks.called) + + @mock.patch.object(keystone, 'get_service_url') + def test_validate_good_api_url_from_keystone(self, mock_ks): + # present in the keystone catalog + mock_ks.return_value = 'http://127.0.0.1:1234' + # not present in the config file + self.config(group='conductor', api_url=None) + + with task_manager.acquire(self.context, [self.node.uuid], + shared=True) as task: + task.resources[0].driver.deploy.validate(task, self.node) + mock_ks.assert_called_once_with() + + @mock.patch.object(keystone, 'get_service_url') + def test_validate_fail_no_api_url(self, mock_ks): + # not present in the keystone catalog + mock_ks.side_effect = exception.CatalogFailure + # not present in the config file + self.config(group='conductor', api_url=None) + + with task_manager.acquire(self.context, [self.node.uuid], + shared=True) as task: + self.assertRaises(exception.InvalidParameterValue, + task.resources[0].driver.deploy.validate, + task, self.node) + mock_ks.assert_called_once_with() + def test__get_nodes_mac_addresses(self): ports = [] ports.append(self.port)