summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2014-06-10 10:14:52 +0000
committerGerrit Code Review <review@openstack.org>2014-06-10 10:14:52 +0000
commit90acb6e09896f9c0e8ff96a57b1eed1457d89c6e (patch)
treea70bfcd49f336919cfbd07a761d1efb88a56949a
parentb77063e566cba96283d6032a1063bf49b1a590f8 (diff)
parent27dfedbc9d3653df8ab27acfa010d4c356ab2b2b (diff)
downloadironic-90acb6e09896f9c0e8ff96a57b1eed1457d89c6e.tar.gz
Merge "PXE driver to validate the requested image in Glance"
-rw-r--r--ironic/drivers/modules/pxe.py40
-rw-r--r--ironic/tests/drivers/test_pxe.py47
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: