Merge "Check image size before provisioning for agent driver"
This commit is contained in:
commit
83506795b8
@ -379,6 +379,13 @@
|
||||
# Deprecated group/name - [agent]/manage_tftp
|
||||
#manage_agent_boot=true
|
||||
|
||||
# The memory size in MiB consumed by agent when it is booted on a
|
||||
# bare metal node. This is used for checking if the image can
|
||||
# be downloaded and deployed on the bare metal node after booting
|
||||
# agent ramdisk. This may be set according to the memory
|
||||
# consumed by the agent ramdisk image. (integer value)
|
||||
#memory_consumed_by_agent=0
|
||||
|
||||
|
||||
#
|
||||
# Options defined in ironic.drivers.modules.agent_base_vendor
|
||||
|
@ -17,6 +17,7 @@ import time
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import units
|
||||
|
||||
from ironic.common import dhcp_factory
|
||||
from ironic.common import exception
|
||||
@ -24,7 +25,9 @@ from ironic.common.glance_service import service_utils
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common.i18n import _LE
|
||||
from ironic.common.i18n import _LI
|
||||
from ironic.common.i18n import _LW
|
||||
from ironic.common import image_service
|
||||
from ironic.common import images
|
||||
from ironic.common import keystone
|
||||
from ironic.common import paths
|
||||
from ironic.common import raid
|
||||
@ -68,6 +71,14 @@ agent_opts = [
|
||||
'ramdisk. If set to False, you will need to configure '
|
||||
'your mechanism to allow booting the agent '
|
||||
'ramdisk.')),
|
||||
cfg.IntOpt('memory_consumed_by_agent',
|
||||
default=0,
|
||||
help=_('The memory size in MiB consumed by agent when it is '
|
||||
'booted on a bare metal node. This is used for '
|
||||
'checking if the image can be downloaded and deployed '
|
||||
'on the bare metal node after booting agent ramdisk. '
|
||||
'This may be set according to the memory consumed by '
|
||||
'the agent ramdisk image.')),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -157,6 +168,36 @@ def build_instance_info_for_deploy(task):
|
||||
return instance_info
|
||||
|
||||
|
||||
def check_image_size(task, image_source):
|
||||
"""Check if the requested image is larger than the ram size.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param image_source: href of the image.
|
||||
:raises: InvalidParameterValue if size of the image is greater than
|
||||
the available ram size.
|
||||
"""
|
||||
properties = task.node.properties
|
||||
# skip check if 'memory_mb' is not defined
|
||||
if 'memory_mb' not in properties:
|
||||
LOG.warning(_LW('Skip the image size check as memory_mb is not '
|
||||
'defined in properties on node %s.'), task.node.uuid)
|
||||
return
|
||||
|
||||
memory_size = int(properties.get('memory_mb'))
|
||||
image_size = int(images.download_size(task.context, image_source))
|
||||
reserved_size = CONF.agent.memory_consumed_by_agent
|
||||
if (image_size + (reserved_size * units.Mi)) > (memory_size * units.Mi):
|
||||
msg = (_('Memory size is too small for requested image, if it is '
|
||||
'less than (image size + reserved RAM size), will break '
|
||||
'the IPA deployments. Image size: %(image_size)d MiB, '
|
||||
'Memory size: %(memory_size)d MiB, Reserved size: '
|
||||
'%(reserved_size)d MiB.')
|
||||
% {'image_size': image_size / units.Mi,
|
||||
'memory_size': memory_size,
|
||||
'reserved_size': reserved_size})
|
||||
raise exception.InvalidParameterValue(msg)
|
||||
|
||||
|
||||
class AgentDeploy(base.DeployInterface):
|
||||
"""Interface for deploy-related actions."""
|
||||
|
||||
@ -175,7 +216,10 @@ class AgentDeploy(base.DeployInterface):
|
||||
the node.
|
||||
|
||||
:param task: a TaskManager instance
|
||||
:raises: MissingParameterValue
|
||||
:raises: MissingParameterValue, if any of the required parameters are
|
||||
missing.
|
||||
:raises: InvalidParameterValue, if any of the parameters have invalid
|
||||
value.
|
||||
"""
|
||||
if CONF.agent.manage_agent_boot:
|
||||
task.driver.boot.validate(task)
|
||||
@ -194,6 +238,7 @@ class AgentDeploy(base.DeployInterface):
|
||||
"image_source's image_checksum must be provided in "
|
||||
"instance_info for node %s") % node.uuid)
|
||||
|
||||
check_image_size(task, image_source)
|
||||
is_whole_disk_image = node.driver_internal_info.get(
|
||||
'is_whole_disk_image')
|
||||
# TODO(sirushtim): Remove once IPA has support for partition images.
|
||||
|
@ -19,6 +19,7 @@ from oslo_config import cfg
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common import image_service
|
||||
from ironic.common import images
|
||||
from ironic.common import keystone
|
||||
from ironic.common import raid
|
||||
from ironic.common import states
|
||||
@ -140,6 +141,51 @@ class TestAgentMethods(db_base.DbTestCase):
|
||||
self.assertRaises(exception.ImageRefValidationFailed,
|
||||
agent.build_instance_info_for_deploy, task)
|
||||
|
||||
@mock.patch.object(images, 'download_size', autospec=True)
|
||||
def test_check_image_size(self, size_mock):
|
||||
size_mock.return_value = 10 * 1024 * 1024
|
||||
mgr_utils.mock_the_extension_manager(driver='fake_agent')
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.node.properties['memory_mb'] = 10
|
||||
agent.check_image_size(task, 'fake-image')
|
||||
size_mock.assert_called_once_with(self.context, 'fake-image')
|
||||
|
||||
@mock.patch.object(images, 'download_size', autospec=True)
|
||||
def test_check_image_size_without_memory_mb(self, size_mock):
|
||||
mgr_utils.mock_the_extension_manager(driver='fake_agent')
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.node.properties.pop('memory_mb', None)
|
||||
agent.check_image_size(task, 'fake-image')
|
||||
self.assertFalse(size_mock.called)
|
||||
|
||||
@mock.patch.object(images, 'download_size', autospec=True)
|
||||
def test_check_image_size_fail(self, size_mock):
|
||||
size_mock.return_value = 11 * 1024 * 1024
|
||||
|
||||
mgr_utils.mock_the_extension_manager(driver='fake_agent')
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.node.properties['memory_mb'] = 10
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
agent.check_image_size,
|
||||
task, 'fake-image')
|
||||
size_mock.assert_called_once_with(self.context, 'fake-image')
|
||||
|
||||
@mock.patch.object(images, 'download_size', autospec=True)
|
||||
def test_check_image_size_fail_by_agent_consumed_memory(self, size_mock):
|
||||
self.config(memory_consumed_by_agent=2, group='agent')
|
||||
size_mock.return_value = 9 * 1024 * 1024
|
||||
mgr_utils.mock_the_extension_manager(driver='fake_agent')
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.node.properties['memory_mb'] = 10
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
agent.check_image_size,
|
||||
task, 'fake-image')
|
||||
size_mock.assert_called_once_with(self.context, 'fake-image')
|
||||
|
||||
|
||||
class TestAgentDeploy(db_base.DbTestCase):
|
||||
def setUp(self):
|
||||
@ -160,17 +206,20 @@ class TestAgentDeploy(db_base.DbTestCase):
|
||||
expected = agent.COMMON_PROPERTIES
|
||||
self.assertEqual(expected, self.driver.get_properties())
|
||||
|
||||
@mock.patch.object(images, 'download_size', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'validate', autospec=True)
|
||||
def test_validate(self, pxe_boot_validate_mock):
|
||||
def test_validate(self, pxe_boot_validate_mock, size_mock):
|
||||
with task_manager.acquire(
|
||||
self.context, self.node['uuid'], shared=False) as task:
|
||||
self.driver.validate(task)
|
||||
pxe_boot_validate_mock.assert_called_once_with(
|
||||
task.driver.boot, task)
|
||||
size_mock.assert_called_once_with(self.context, 'fake-image')
|
||||
|
||||
@mock.patch.object(images, 'download_size', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'validate', autospec=True)
|
||||
def test_validate_driver_info_manage_agent_boot_false(
|
||||
self, pxe_boot_validate_mock):
|
||||
self, pxe_boot_validate_mock, size_mock):
|
||||
self.config(manage_agent_boot=False, group='agent')
|
||||
self.node.driver_info = {}
|
||||
self.node.save()
|
||||
@ -178,6 +227,7 @@ class TestAgentDeploy(db_base.DbTestCase):
|
||||
self.context, self.node['uuid'], shared=False) as task:
|
||||
self.driver.validate(task)
|
||||
self.assertFalse(pxe_boot_validate_mock.called)
|
||||
size_mock.assert_called_once_with(self.context, 'fake-image')
|
||||
|
||||
@mock.patch.object(pxe.PXEBoot, 'validate', autospec=True)
|
||||
def test_validate_instance_info_missing_params(
|
||||
@ -209,9 +259,10 @@ class TestAgentDeploy(db_base.DbTestCase):
|
||||
pxe_boot_validate_mock.assert_called_once_with(
|
||||
task.driver.boot, task)
|
||||
|
||||
@mock.patch.object(images, 'download_size', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'validate', autospec=True)
|
||||
def test_validate_agent_fail_partition_image(
|
||||
self, pxe_boot_validate_mock):
|
||||
self, pxe_boot_validate_mock, size_mock):
|
||||
with task_manager.acquire(
|
||||
self.context, self.node['uuid'], shared=False) as task:
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = False
|
||||
@ -219,10 +270,12 @@ class TestAgentDeploy(db_base.DbTestCase):
|
||||
self.driver.validate, task)
|
||||
pxe_boot_validate_mock.assert_called_once_with(
|
||||
task.driver.boot, task)
|
||||
size_mock.assert_called_once_with(self.context, 'fake-image')
|
||||
|
||||
@mock.patch.object(images, 'download_size', autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'validate', autospec=True)
|
||||
def test_validate_invalid_root_device_hints(
|
||||
self, pxe_boot_validate_mock):
|
||||
self, pxe_boot_validate_mock, size_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
task.node.properties['root_device'] = {'size': 'not-int'}
|
||||
@ -230,6 +283,7 @@ class TestAgentDeploy(db_base.DbTestCase):
|
||||
task.driver.deploy.validate, task)
|
||||
pxe_boot_validate_mock.assert_called_once_with(
|
||||
task.driver.boot, task)
|
||||
size_mock.assert_called_once_with(self.context, 'fake-image')
|
||||
|
||||
@mock.patch('ironic.conductor.utils.node_power_action', autospec=True)
|
||||
def test_deploy(self, power_mock):
|
||||
|
Loading…
x
Reference in New Issue
Block a user