[ansible] reuse build_instance_info_for_deploy

this function was moved to common place in deploy_utils and now can be
easily reused.

Also, it does basic disk layout parsing and validation so there is no
more need to do it in ansible-deploy driver.

Change-Id: I2054dc1f124a62ef1f45c8a0dad0b755e8de23ea
This commit is contained in:
Pavlo Shchelokovskyy 2017-03-17 14:53:52 +00:00
parent 4e2b9280a9
commit 6eaf42f11f
3 changed files with 41 additions and 164 deletions

View File

@ -23,8 +23,6 @@ from ironic_lib import utils as irlib_utils
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_log import log
from oslo_utils import excutils
from oslo_utils import strutils
from oslo_utils import units
import retrying
import six
@ -33,12 +31,10 @@ import yaml
from ironic.common import dhcp_factory
from ironic.common import exception
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 states
from ironic.common import utils
@ -137,8 +133,6 @@ OPTIONAL_PROPERTIES = {
}
COMMON_PROPERTIES = OPTIONAL_PROPERTIES
DISK_LAYOUT_PARAMS = ('root_gb', 'swap_mb', 'ephemeral_gb')
INVENTORY_FILE = os.path.join(CONF.ansible.playbooks_path, 'inventory')
@ -160,37 +154,6 @@ def _get_configdrive_path(basename):
return os.path.join(CONF.tempdir, basename + '.cndrive')
# NOTE(yuriyz): this is a copy from agent driver
def build_instance_info_for_deploy(task):
"""Build instance_info necessary for deploying to a node."""
node = task.node
instance_info = node.instance_info
image_source = instance_info['image_source']
if service_utils.is_glance_image(image_source):
glance = image_service.GlanceImageService(version=2,
context=task.context)
image_info = glance.show(image_source)
swift_temp_url = glance.swift_temp_url(image_info)
LOG.debug('Got image info: %(info)s for node %(node)s.',
{'info': image_info, 'node': node.uuid})
instance_info['image_url'] = swift_temp_url
instance_info['image_checksum'] = image_info['checksum']
instance_info['image_disk_format'] = image_info['disk_format']
else:
try:
image_service.HttpImageService().validate_href(image_source)
except exception.ImageRefValidationFailed:
with excutils.save_and_reraise_exception():
LOG.error(_LE("Ansible deploy supports only HTTP(S) URLs as "
"instance_info['image_source']. Either %s "
"is not a valid HTTP(S) URL or "
"is not reachable."), image_source)
instance_info['image_url'] = image_source
return instance_info
def _get_node_ip(task):
api = dhcp_factory.DHCPFactory().provider
ip_addrs = api.get_ip_addresses(task)
@ -292,60 +255,32 @@ def _parse_partitioning_info(node):
info = node.instance_info
i_info = {}
i_info['root_gb'] = info.get('root_gb')
error_msg = _("'root_gb' is missing in node's instance_info")
deploy_utils.check_for_missing_params(i_info, error_msg)
i_info['swap_mb'] = info.get('swap_mb', 0)
i_info['ephemeral_gb'] = info.get('ephemeral_gb', 0)
err_msg_invalid = _("Cannot validate parameter for deploy. Invalid "
"parameter %(param)s. Reason: %(reason)s")
for param in DISK_LAYOUT_PARAMS:
try:
i_info[param] = int(i_info[param])
except ValueError:
reason = _("%s is not an integer value") % i_info[param]
raise exception.InvalidParameterValue(err_msg_invalid %
{'param': param,
'reason': reason})
# convert to sizes expected by 'parted' Ansible module
root_mib = 1024 * i_info.pop('root_gb')
swap_mib = i_info.pop('swap_mb')
ephemeral_mib = 1024 * i_info.pop('ephemeral_gb')
partitions = []
root_partition = {'name': 'root',
'size_mib': root_mib,
'size_mib': info['root_mb'],
'boot': 'yes',
'swap': 'no'}
partitions.append(root_partition)
if swap_mib:
swap_mb = info['swap_mb']
if swap_mb:
swap_partition = {'name': 'swap',
'size_mib': swap_mib,
'size_mib': swap_mb,
'boot': 'no',
'swap': 'yes'}
partitions.append(swap_partition)
if ephemeral_mib:
ephemeral_mb = info['ephemeral_mb']
if ephemeral_mb:
ephemeral_partition = {'name': 'ephemeral',
'size_mib': ephemeral_mib,
'size_mib': ephemeral_mb,
'boot': 'no',
'swap': 'no'}
partitions.append(ephemeral_partition)
i_info['ephemeral_format'] = info.get('ephemeral_format')
if not i_info['ephemeral_format']:
i_info['ephemeral_format'] = CONF.pxe.default_ephemeral_format
preserve_ephemeral = info.get('preserve_ephemeral', False)
try:
i_info['preserve_ephemeral'] = (
strutils.bool_from_string(preserve_ephemeral, strict=True))
except ValueError as e:
raise exception.InvalidParameterValue(
err_msg_invalid % {'param': 'preserve_ephemeral', 'reason': e})
i_info['ephemeral_format'] = info['ephemeral_format']
i_info['preserve_ephemeral'] = (
'yes' if i_info['preserve_ephemeral'] else 'no')
'yes' if info['preserve_ephemeral'] else 'no')
i_info['ironic_partitions'] = partitions
return i_info
@ -550,7 +485,8 @@ class AnsibleDeploy(base.DeployInterface):
manager_utils.node_power_action(task, states.POWER_OFF)
task.driver.network.add_provisioning_network(task)
if node.provision_state not in [states.ACTIVE, states.ADOPTING]:
node.instance_info = build_instance_info_for_deploy(task)
node.instance_info = deploy_utils.build_instance_info_for_deploy(
task)
node.save()
boot_opt = deploy_utils.build_agent_options(node)
task.driver.boot.prepare_ramdisk(task, boot_opt)

View File

@ -15,7 +15,6 @@ import os
from ironic.common import dhcp_factory
from ironic.common import exception
from ironic.common import image_service
from ironic.common import states
from ironic.common import utils as com_utils
from ironic.conductor import task_manager
@ -40,7 +39,9 @@ INSTANCE_INFO = {
'image_url': 'http://image',
'image_checksum': 'checksum',
'image_disk_format': 'qcow2',
'root_gb': 5,
'root_mb': 5120,
'swap_mb': 0,
'ephemeral_mb': 0
}
DRIVER_INFO = {
@ -80,69 +81,6 @@ class TestAnsibleMethods(db_base.DbTestCase):
ansible_deploy._parse_ansible_driver_info,
self.node, 'test')
@mock.patch.object(image_service, 'GlanceImageService', autospec=True)
def test_build_instance_info_for_deploy_glance_image(self, glance_mock):
i_info = self.node.instance_info
i_info['image_source'] = '733d1c44-a2ea-414b-aca7-69decf20d810'
self.node.instance_info = i_info
self.node.save()
image_info = {'checksum': 'aa', 'disk_format': 'qcow2'}
glance_mock.return_value.show = mock.Mock(spec_set=[],
return_value=image_info)
with task_manager.acquire(
self.context, self.node.uuid) as task:
ansible_deploy.build_instance_info_for_deploy(task)
glance_mock.assert_called_once_with(version=2,
context=task.context)
glance_mock.return_value.show.assert_called_once_with(
self.node.instance_info['image_source'])
glance_mock.return_value.swift_temp_url.assert_called_once_with(
image_info)
@mock.patch.object(image_service.HttpImageService, 'validate_href',
autospec=True)
def test_build_instance_info_for_deploy_nonglance_image(
self, validate_href_mock):
i_info = self.node.instance_info
driver_internal_info = self.node.driver_internal_info
i_info['image_source'] = 'http://image-ref'
i_info['image_checksum'] = 'aa'
i_info['root_gb'] = 10
driver_internal_info['is_whole_disk_image'] = True
self.node.instance_info = i_info
self.node.driver_internal_info = driver_internal_info
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
info = ansible_deploy.build_instance_info_for_deploy(task)
self.assertEqual(self.node.instance_info['image_source'],
info['image_url'])
validate_href_mock.assert_called_once_with(
mock.ANY, 'http://image-ref')
@mock.patch.object(image_service.HttpImageService, 'validate_href',
autospec=True)
def test_build_instance_info_for_deploy_nonsupported_image(
self, validate_href_mock):
validate_href_mock.side_effect = iter(
[exception.ImageRefValidationFailed(
image_href='file://img.qcow2', reason='fail')])
i_info = self.node.instance_info
i_info['image_source'] = 'file://img.qcow2'
i_info['image_checksum'] = 'aa'
self.node.instance_info = i_info
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(
exception.ImageRefValidationFailed,
ansible_deploy.build_instance_info_for_deploy, task)
def test__get_node_ip(self):
dhcp_provider_mock = mock.Mock()
dhcp_factory.DHCPFactory._dhcp_provider = dhcp_provider_mock
@ -233,24 +171,18 @@ class TestAnsibleMethods(db_base.DbTestCase):
ansible_deploy.INVENTORY_FILE, '-e', json.dumps(extra_vars),
'--tags=wait', '--private-key=/path/to/key', '-vvvv')
@mock.patch.object(deploy_utils, 'check_for_missing_params',
autospec=True)
def test__parse_partitioning_info(self, check_missing_param_mock):
def test__parse_partitioning_info(self):
expected_info = {
'ironic_partitions':
[{'boot': 'yes', 'swap': 'no',
'size_mib': 1024 * INSTANCE_INFO['root_gb'],
'size_mib': INSTANCE_INFO['root_mb'],
'name': 'root'}]}
i_info = ansible_deploy._parse_partitioning_info(self.node)
check_missing_param_mock.assert_called_once_with(
expected_info, mock.ANY)
self.assertEqual(expected_info, i_info)
@mock.patch.object(deploy_utils, 'check_for_missing_params',
autospec=True)
def test__parse_partitioning_info_swap(self, check_missing_param_mock):
def test__parse_partitioning_info_swap(self):
in_info = dict(INSTANCE_INFO)
in_info['swap_mb'] = 128
self.node.instance_info = in_info
@ -259,29 +191,37 @@ class TestAnsibleMethods(db_base.DbTestCase):
expected_info = {
'ironic_partitions':
[{'boot': 'yes', 'swap': 'no',
'size_mib': 1024 * INSTANCE_INFO['root_gb'],
'size_mib': INSTANCE_INFO['root_mb'],
'name': 'root'},
{'boot': 'no', 'swap': 'yes',
'size_mib': 128, 'name': 'swap'}]}
i_info = ansible_deploy._parse_partitioning_info(self.node)
check_missing_param_mock.assert_called_once_with(
expected_info, mock.ANY)
self.assertEqual(expected_info, i_info)
@mock.patch.object(deploy_utils, 'check_for_missing_params',
autospec=True)
def test__parse_partitioning_info_invalid_param(self,
check_missing_param_mock):
def test__parse_partitioning_info_ephemeral(self):
in_info = dict(INSTANCE_INFO)
in_info['root_gb'] = 'five'
in_info['ephemeral_mb'] = 128
in_info['ephemeral_format'] = 'ext4'
in_info['preserve_ephemeral'] = True
self.node.instance_info = in_info
self.node.save()
self.assertRaises(exception.InvalidParameterValue,
ansible_deploy._parse_partitioning_info,
self.node)
expected_info = {
'ironic_partitions':
[{'boot': 'yes', 'swap': 'no',
'size_mib': INSTANCE_INFO['root_mb'],
'name': 'root'},
{'boot': 'no', 'swap': 'no',
'size_mib': 128, 'name': 'ephemeral'}],
'ephemeral_format': 'ext4',
'preserve_ephemeral': 'yes'
}
i_info = ansible_deploy._parse_partitioning_info(self.node)
self.assertEqual(expected_info, i_info)
@mock.patch.object(pxe.PXEBoot, 'clean_up_ramdisk')
@mock.patch.object(ansible_deploy, '_reboot_and_finish_deploy',
@ -467,8 +407,9 @@ class TestAnsibleDeploy(db_base.DbTestCase):
@mock.patch('ironic.drivers.modules.deploy_utils.build_agent_options',
return_value={'op1': 'test1'}, autospec=True)
@mock.patch.object(ansible_deploy, 'build_instance_info_for_deploy',
return_value={'test': 'test'}, autospec=True)
@mock.patch('ironic.drivers.modules.deploy_utils.'
'build_instance_info_for_deploy',
return_value={'test': 'test'}, autospec=True)
@mock.patch.object(pxe.PXEBoot, 'prepare_ramdisk')
def test_prepare(self, pxe_prepare_ramdisk_mock,
build_instance_info_mock, build_options_mock):

View File

@ -1,3 +1,3 @@
---
upgrade:
- Ansible-deploy driver requires ironic of Newton release or newer
- Ansible-deploy driver requires ironic of version >= 7.0.0 (Ocata release).