diff options
author | Jenkins <jenkins@review.openstack.org> | 2014-06-10 10:14:52 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2014-06-10 10:14:52 +0000 |
commit | 90acb6e09896f9c0e8ff96a57b1eed1457d89c6e (patch) | |
tree | a70bfcd49f336919cfbd07a761d1efb88a56949a | |
parent | b77063e566cba96283d6032a1063bf49b1a590f8 (diff) | |
parent | 27dfedbc9d3653df8ab27acfa010d4c356ab2b2b (diff) | |
download | ironic-90acb6e09896f9c0e8ff96a57b1eed1457d89c6e.tar.gz |
Merge "PXE driver to validate the requested image in Glance"
-rw-r--r-- | ironic/drivers/modules/pxe.py | 40 | ||||
-rw-r--r-- | ironic/tests/drivers/test_pxe.py | 47 |
2 files changed, 82 insertions, 5 deletions
diff --git a/ironic/drivers/modules/pxe.py b/ironic/drivers/modules/pxe.py index 1bc38ca2f..7e0c5b5e1 100644 --- a/ironic/drivers/modules/pxe.py +++ b/ironic/drivers/modules/pxe.py @@ -505,6 +505,40 @@ def _check_image_size(task): raise exception.InstanceDeployFailure(msg) +def _validate_glance_image(ctx, driver_info): + """Validate the image in Glance. + + Check if the image exist in Glance and if it contains the + 'kernel_id' and 'ramdisk_id' properties. + + :raises: InvalidParameterValue. + """ + image_id = driver_info['image_source'] + try: + glance_service = service.Service(version=1, context=ctx) + image_props = glance_service.show(image_id)['properties'] + except (exception.GlanceConnectionFailed, + exception.ImageNotAuthorized, + exception.Invalid): + raise exception.InvalidParameterValue(_( + "Failed to connect to Glance to get the properties " + "of the image %s") % image_id) + except exception.ImageNotFound: + raise exception.InvalidParameterValue(_( + "Image %s not found in Glance") % image_id) + + missing_props = [] + for prop in ('kernel_id', 'ramdisk_id'): + if not image_props.get(prop): + missing_props.append(prop) + + if missing_props: + props = ', '.join(missing_props) + raise exception.InvalidParameterValue(_( + "Image %(image)s is missing the following properties: " + "%(properties)s") % {'image': image_id, 'properties': props}) + + class PXEDeploy(base.DeployInterface): """PXE Deploy Interface: just a stub until the real driver is ported.""" @@ -512,13 +546,13 @@ class PXEDeploy(base.DeployInterface): """Validate the driver-specific Node deployment info. :param task: a TaskManager instance containing the node to act on. - :returns: InvalidParameterValue. + :raises: InvalidParameterValue. """ node = task.node if not driver_utils.get_node_mac_addresses(task): raise exception.InvalidParameterValue(_("Node %s does not have " "any port associated with it.") % node.uuid) - _parse_driver_info(node) + d_info = _parse_driver_info(node) # Try to get the URL of the Ironic API try: @@ -531,6 +565,8 @@ class PXEDeploy(base.DeployInterface): "Couldn't get the URL of the Ironic API service from the " "configuration file or keystone catalog.")) + _validate_glance_image(task.context, d_info) + @task_manager.require_exclusive_lock def deploy(self, task): """Start deployment of the task's node'. diff --git a/ironic/tests/drivers/test_pxe.py b/ironic/tests/drivers/test_pxe.py index b464ec34d..e559cc5a0 100644 --- a/ironic/tests/drivers/test_pxe.py +++ b/ironic/tests/drivers/test_pxe.py @@ -571,7 +571,10 @@ class PXEDriverTestCase(db_base.DbTestCase): open(token_path, 'w').close() return token_path - def test_validate_good(self): + @mock.patch.object(base_image_service.BaseImageService, '_show') + def test_validate_good(self, mock_glance): + mock_glance.return_value = {'properties': {'kernel_id': 'fake-kernel', + 'ramdisk_id': 'fake-initr'}} with task_manager.acquire(self.context, [self.node.uuid], shared=True) as task: task.resources[0].driver.deploy.validate(task, self.node) @@ -598,8 +601,12 @@ class PXEDriverTestCase(db_base.DbTestCase): task.resources[0].driver.deploy.validate, task, new_node) + @mock.patch.object(base_image_service.BaseImageService, '_show') @mock.patch.object(keystone, 'get_service_url') - def test_validate_good_api_url_from_config_file(self, mock_ks): + def test_validate_good_api_url_from_config_file(self, mock_ks, + mock_glance): + mock_glance.return_value = {'properties': {'kernel_id': 'fake-kernel', + 'ramdisk_id': 'fake-initr'}} # not present in the keystone catalog mock_ks.side_effect = exception.CatalogFailure @@ -608,8 +615,11 @@ class PXEDriverTestCase(db_base.DbTestCase): task.resources[0].driver.deploy.validate(task, self.node) self.assertFalse(mock_ks.called) + @mock.patch.object(base_image_service.BaseImageService, '_show') @mock.patch.object(keystone, 'get_service_url') - def test_validate_good_api_url_from_keystone(self, mock_ks): + def test_validate_good_api_url_from_keystone(self, mock_ks, mock_glance): + mock_glance.return_value = {'properties': {'kernel_id': 'fake-kernel', + 'ramdisk_id': 'fake-initr'}} # present in the keystone catalog mock_ks.return_value = 'http://127.0.0.1:1234' # not present in the config file @@ -634,6 +644,37 @@ class PXEDriverTestCase(db_base.DbTestCase): task, self.node) mock_ks.assert_called_once_with() + @mock.patch.object(base_image_service.BaseImageService, '_show') + def test_validate_fail_no_image_kernel_ramdisk_props(self, mock_glance): + mock_glance.return_value = {'properties': {}} + with task_manager.acquire(self.context, self.node.uuid, + shared=True) as task: + self.assertRaises(exception.InvalidParameterValue, + task.driver.deploy.validate, + task, self.node) + + @mock.patch.object(base_image_service.BaseImageService, '_show') + def test_validate_fail_glance_image_doesnt_exists(self, mock_glance): + mock_glance.side_effect = exception.ImageNotFound('not found') + with task_manager.acquire(self.context, self.node.uuid, + shared=True) as task: + self.assertRaises(exception.InvalidParameterValue, + task.driver.deploy.validate, + task, self.node) + + @mock.patch.object(base_image_service.BaseImageService, '_show') + def test_validate_fail_glance_conn_problem(self, mock_glance): + exceptions = (exception.GlanceConnectionFailed('connection fail'), + exception.ImageNotAuthorized('not authorized'), + exception.Invalid('invalid')) + mock_glance.side_effect = exceptions + for exc in exceptions: + with task_manager.acquire(self.context, self.node.uuid, + shared=True) as task: + self.assertRaises(exception.InvalidParameterValue, + task.driver.deploy.validate, + task, self.node) + def test_vendor_passthru_validate_good(self): with task_manager.acquire(self.context, [self.node.uuid], shared=True) as task: |