Support creating a swap partition

BREAKING: --root-disk-size => --root-size

Story: #2002171
Task: #20033
Change-Id: I5d6b38e1a9e71b608b40e140e34c4509b896a1ff
This commit is contained in:
Dmitry Tantsur 2018-09-10 12:44:26 +02:00
parent c06e2c9baf
commit 2afd20938d
10 changed files with 213 additions and 532 deletions

View File

@ -124,6 +124,7 @@
metalsmith_precreate_port: false
metalsmith_partition_image: test-centos-partition
metalsmith_whole_disk_image: test-centos-wholedisk
metalsmith_swap_size: 1024
metalsmith_traits: [CUSTOM_GOLD]
- job:

View File

@ -81,7 +81,8 @@ def _do_deploy(api, args, formatter):
instance = api.provision_node(node,
image=source,
nics=args.nics,
root_disk_size=args.root_disk_size,
root_size_gb=args.root_size,
swap_size_mb=args.swap_size,
config=config,
hostname=args.hostname,
netboot=args.netboot,
@ -150,9 +151,12 @@ def _parse_args(args, config):
dest='nics', action=NICAction)
deploy.add_argument('--netboot', action='store_true',
help='boot from network instead of local disk')
deploy.add_argument('--root-disk-size', type=int,
help='root disk size (in GiB), defaults to (local_gb '
'- 2)')
deploy.add_argument('--root-size', type=int,
help='root partition size (in GiB), defaults to '
'(local_gb - 1)')
deploy.add_argument('--swap-size', type=int,
help='swap partition size (in MiB), defaults to '
'no swap')
deploy.add_argument('--capability', action='append', metavar='NAME=VALUE',
default=[], help='capabilities the node should have')
deploy.add_argument('--trait', action='append',

View File

@ -17,6 +17,7 @@ import logging
import random
import sys
import time
import warnings
from openstack import connection
import six
@ -194,9 +195,10 @@ class Provisioner(object):
return hostname
def provision_node(self, node, image, nics=None, root_disk_size=None,
config=None, hostname=None, netboot=False,
capabilities=None, traits=None, wait=None):
def provision_node(self, node, image, nics=None, root_size_gb=None,
swap_size_mb=None, config=None, hostname=None,
netboot=False, capabilities=None, traits=None,
wait=None, root_disk_size=None):
"""Provision the node with the given image.
Example::
@ -204,7 +206,7 @@ class Provisioner(object):
provisioner.provision_node("compute-1", "centos",
nics=[{"network": "private"},
{"network": "external"}],
root_disk_size=50,
root_size_gb=50,
wait=3600)
:param node: Node object, UUID or name. Will be reserved first, if
@ -216,8 +218,10 @@ class Provisioner(object):
Each item is a dict with a key describing the type of the NIC:
either a port (``{"port": "<port name or ID>"}``) or a network
to create a port on (``{"network": "<network name or ID>"}``).
:param root_disk_size: The size of the root partition. By default
:param root_size_gb: The size of the root partition. By default
the value of the local_gb property is used.
:param swap_size_mb: The size of the swap partition. It's an error
to specify it for a whole disk image.
:param config: :py:class:`metalsmith.InstanceConfig` object with
the configuration to pass to the instance.
:param hostname: Hostname to assign to the instance. Defaults to the
@ -233,6 +237,7 @@ class Provisioner(object):
:meth:`reserve_node` for that.
:param wait: How many seconds to wait for the deployment to finish,
None to return immediately.
:param root_disk_size: DEPRECATED, use ``root_size_gb``.
:return: :py:class:`metalsmith.Instance` object with the current
status of provisioning. If ``wait`` is not ``None``, provisioning
is already finished.
@ -242,6 +247,10 @@ class Provisioner(object):
config = _config.InstanceConfig()
if isinstance(image, six.string_types):
image = sources.GlanceImage(image)
if root_disk_size is not None:
warnings.warn("root_disk_size is deprecated, use root_size_gb "
"instead", DeprecationWarning)
root_size_gb = root_disk_size
node = self._check_node_for_deploy(node)
created_ports = []
@ -249,7 +258,7 @@ class Provisioner(object):
try:
hostname = self._check_hostname(node, hostname)
root_disk_size = _utils.get_root_disk(root_disk_size, node)
root_size_gb = _utils.get_root_disk(root_size_gb, node)
image._validate(self.connection)
@ -268,7 +277,7 @@ class Provisioner(object):
capabilities['boot_option'] = 'netboot' if netboot else 'local'
updates = {'/instance_info/root_gb': root_disk_size,
updates = {'/instance_info/root_gb': root_size_gb,
'/instance_info/capabilities': capabilities,
'/extra/%s' % _CREATED_PORTS: created_ports,
'/extra/%s' % _ATTACHED_PORTS: attached_ports,
@ -276,6 +285,8 @@ class Provisioner(object):
updates.update(image._node_updates(self.connection))
if traits is not None:
updates['/instance_info/traits'] = traits
if swap_size_mb is not None:
updates['/instance_info/swap_mb'] = swap_size_mb
LOG.debug('Updating node %(node)s with %(updates)s',
{'node': _utils.log_node(node), 'updates': updates})

View File

@ -42,22 +42,22 @@ def get_capabilities(node):
return caps
def get_root_disk(root_disk_size, node):
def get_root_disk(root_size_gb, node):
"""Validate and calculate the root disk size."""
if root_disk_size is not None:
if not isinstance(root_disk_size, int):
raise TypeError("The root_disk_size argument must be "
"a positive integer, got %r" % root_disk_size)
elif root_disk_size <= 0:
raise ValueError("The root_disk_size argument must be "
"a positive integer, got %d" % root_disk_size)
if root_size_gb is not None:
if not isinstance(root_size_gb, int):
raise TypeError("The root_size_gb argument must be "
"a positive integer, got %r" % root_size_gb)
elif root_size_gb <= 0:
raise ValueError("The root_size_gb argument must be "
"a positive integer, got %d" % root_size_gb)
else:
try:
assert int(node.properties['local_gb']) > 0
except KeyError:
raise exceptions.UnknownRootDiskSize(
'No local_gb for node %s and no root disk size requested' %
log_node(node))
'No local_gb for node %s and no root partition size '
'specified' % log_node(node))
except (TypeError, ValueError, AssertionError):
raise exceptions.UnknownRootDiskSize(
'The local_gb for node %(node)s is invalid: '
@ -66,9 +66,9 @@ def get_root_disk(root_disk_size, node):
'value': node.properties['local_gb']})
# allow for partitioning and config drive
root_disk_size = int(node.properties['local_gb']) - 1
root_size_gb = int(node.properties['local_gb']) - 1
return root_disk_size
return root_size_gb
_HOSTNAME_RE = re.compile(r"""^

View File

@ -29,7 +29,6 @@ from metalsmith import sources
@mock.patch.object(_provisioner, 'Provisioner', autospec=True)
@mock.patch.object(_cmd.os_config, 'OpenStackConfig', autospec=True)
class TestDeploy(testtools.TestCase):
def setUp(self):
super(TestDeploy, self).setUp()
@ -37,8 +36,42 @@ class TestDeploy(testtools.TestCase):
'metalsmith._format._print', autospec=True))
self.mock_print = self.print_fixture.mock
self.os_conf_fixture = self.useFixture(fixtures.MockPatchObject(
_cmd.os_config, 'OpenStackConfig', autospec=True))
self.mock_os_conf = self.os_conf_fixture.mock
def _check(self, mock_pr, args, reserve_args, provision_args,
dry_run=False):
reserve_defaults = dict(resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None)
reserve_defaults.update(reserve_args)
provision_defaults = dict(image='myimg',
nics=[{'network': 'mynet'}],
root_size_gb=None,
swap_size_mb=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
provision_defaults.update(provision_args)
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=self.mock_os_conf.return_value.get_one.return_value,
dry_run=dry_run)
mock_pr.return_value.reserve_node.assert_called_once_with(
**reserve_defaults)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
**provision_defaults)
@mock.patch.object(_cmd, 'logging', autospec=True)
def test_args_ok(self, mock_log, mock_os_conf, mock_pr):
def test_args_ok(self, mock_log, mock_pr):
instance = mock_pr.return_value.provision_node.return_value
instance.create_autospec(_instance.Instance)
instance.node.name = None
@ -49,27 +82,8 @@ class TestDeploy(testtools.TestCase):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--resource-class', 'compute']
_cmd.main(args)
self._check(mock_pr, args, {}, {})
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
config = mock_pr.return_value.provision_node.call_args[1]['config']
self.assertEqual([], config.ssh_keys)
mock_log.basicConfig.assert_called_once_with(level=mock_log.WARNING,
@ -86,7 +100,7 @@ class TestDeploy(testtools.TestCase):
])
@mock.patch.object(_cmd, 'logging', autospec=True)
def test_args_json_format(self, mock_log, mock_os_conf, mock_pr):
def test_args_json_format(self, mock_log, mock_pr):
instance = mock_pr.return_value.provision_node.return_value
instance.create_autospec(_instance.Instance)
instance.to_dict.return_value = {'node': 'dict'}
@ -95,29 +109,10 @@ class TestDeploy(testtools.TestCase):
'--image', 'myimg', '--resource-class', 'compute']
fake_io = six.StringIO()
with mock.patch('sys.stdout', fake_io):
_cmd.main(args)
self._check(mock_pr, args, {}, {})
self.assertEqual(json.loads(fake_io.getvalue()),
{'node': 'dict'})
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
mock_log.basicConfig.assert_called_once_with(level=mock_log.WARNING,
format=mock.ANY)
self.assertEqual(
@ -126,7 +121,7 @@ class TestDeploy(testtools.TestCase):
mock_log.CRITICAL).call_list(),
mock_log.getLogger.mock_calls)
def test_no_ips(self, mock_os_conf, mock_pr):
def test_no_ips(self, mock_pr):
instance = mock_pr.return_value.provision_node.return_value
instance.create_autospec(_instance.Instance)
instance.is_deployed = True
@ -137,12 +132,12 @@ class TestDeploy(testtools.TestCase):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--resource-class', 'compute']
_cmd.main(args)
self._check(mock_pr, args, {}, {})
self.mock_print.assert_called_once_with(mock.ANY, node='123',
state='active'),
def test_not_deployed_no_ips(self, mock_os_conf, mock_pr):
def test_not_deployed_no_ips(self, mock_pr):
instance = mock_pr.return_value.provision_node.return_value
instance.create_autospec(_instance.Instance)
instance.is_deployed = False
@ -152,72 +147,34 @@ class TestDeploy(testtools.TestCase):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--resource-class', 'compute']
_cmd.main(args)
self._check(mock_pr, args, {}, {})
self.mock_print.assert_called_once_with(mock.ANY, node='123',
state='deploying'),
@mock.patch.object(_cmd.LOG, 'info', autospec=True)
def test_no_logs_not_deployed(self, mock_log, mock_os_conf, mock_pr):
def test_no_logs_not_deployed(self, mock_log, mock_pr):
instance = mock_pr.return_value.provision_node.return_value
instance.create_autospec(_instance.Instance)
instance.is_deployed = False
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--resource-class', 'compute']
_cmd.main(args)
self._check(mock_pr, args, {}, {})
self.assertFalse(mock_log.called)
self.assertFalse(instance.ip_addresses.called)
def test_args_dry_run(self, mock_os_conf, mock_pr):
def test_args_dry_run(self, mock_pr):
args = ['--dry-run', 'deploy', '--network', 'mynet',
'--image', 'myimg', '--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=True)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
self._check(mock_pr, args, {}, {}, dry_run=True)
@mock.patch.object(_cmd, 'logging', autospec=True)
def test_args_debug(self, mock_log, mock_os_conf, mock_pr):
def test_args_debug(self, mock_log, mock_pr):
args = ['--debug', 'deploy', '--network', 'mynet', '--image', 'myimg',
'--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
self._check(mock_pr, args, {}, {})
mock_log.basicConfig.assert_called_once_with(level=mock_log.DEBUG,
format=mock.ANY)
@ -228,29 +185,10 @@ class TestDeploy(testtools.TestCase):
mock_log.getLogger.mock_calls)
@mock.patch.object(_cmd, 'logging', autospec=True)
def test_args_quiet(self, mock_log, mock_os_conf, mock_pr):
def test_args_quiet(self, mock_log, mock_pr):
args = ['--quiet', 'deploy', '--network', 'mynet', '--image', 'myimg',
'--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
self._check(mock_pr, args, {}, {})
mock_log.basicConfig.assert_called_once_with(level=mock_log.CRITICAL,
format=mock.ANY)
@ -263,29 +201,10 @@ class TestDeploy(testtools.TestCase):
self.assertFalse(self.mock_print.called)
@mock.patch.object(_cmd, 'logging', autospec=True)
def test_args_verbose_1(self, mock_log, mock_os_conf, mock_pr):
def test_args_verbose_1(self, mock_log, mock_pr):
args = ['-v', 'deploy', '--network', 'mynet', '--image', 'myimg',
'--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
self._check(mock_pr, args, {}, {})
mock_log.basicConfig.assert_called_once_with(level=mock_log.WARNING,
format=mock.ANY)
@ -296,29 +215,10 @@ class TestDeploy(testtools.TestCase):
mock_log.getLogger.mock_calls)
@mock.patch.object(_cmd, 'logging', autospec=True)
def test_args_verbose_2(self, mock_log, mock_os_conf, mock_pr):
def test_args_verbose_2(self, mock_log, mock_pr):
args = ['-vv', 'deploy', '--network', 'mynet', '--image', 'myimg',
'--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
self._check(mock_pr, args, {}, {})
mock_log.basicConfig.assert_called_once_with(level=mock_log.INFO,
format=mock.ANY)
@ -329,29 +229,10 @@ class TestDeploy(testtools.TestCase):
mock_log.getLogger.mock_calls)
@mock.patch.object(_cmd, 'logging', autospec=True)
def test_args_verbose_3(self, mock_log, mock_os_conf, mock_pr):
def test_args_verbose_3(self, mock_log, mock_pr):
args = ['-vvv', 'deploy', '--network', 'mynet', '--image', 'myimg',
'--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
self._check(mock_pr, args, {}, {})
mock_log.basicConfig.assert_called_once_with(level=mock_log.DEBUG,
format=mock.ANY)
@ -362,7 +243,7 @@ class TestDeploy(testtools.TestCase):
mock_log.getLogger.mock_calls)
@mock.patch.object(_cmd.LOG, 'critical', autospec=True)
def test_reservation_failure(self, mock_log, mock_os_conf, mock_pr):
def test_reservation_failure(self, mock_log, mock_pr):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--resource-class', 'compute']
failure = RuntimeError('boom')
@ -371,7 +252,7 @@ class TestDeploy(testtools.TestCase):
mock_log.assert_called_once_with('%s', failure, exc_info=False)
@mock.patch.object(_cmd.LOG, 'critical', autospec=True)
def test_deploy_failure(self, mock_log, mock_os_conf, mock_pr):
def test_deploy_failure(self, mock_log, mock_pr):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--resource-class', 'compute']
failure = RuntimeError('boom')
@ -380,356 +261,116 @@ class TestDeploy(testtools.TestCase):
mock_log.assert_called_once_with('%s', failure, exc_info=False)
@mock.patch.object(_cmd.LOG, 'critical', autospec=True)
def test_invalid_hostname(self, mock_log, mock_os_conf, mock_pr):
def test_invalid_hostname(self, mock_log, mock_pr):
args = ['deploy', '--hostname', 'n_1', '--image', 'myimg',
'--resource-class', 'compute']
self.assertRaises(SystemExit, _cmd.main, args)
self.assertTrue(mock_log.called)
def test_args_capabilities(self, mock_os_conf, mock_pr):
def test_args_capabilities(self, mock_pr):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--capability', 'foo=bar', '--capability', 'answer=42',
'--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={'foo': 'bar', 'answer': '42'},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
self._check(mock_pr, args,
{'capabilities': {'foo': 'bar', 'answer': '42'}}, {})
def test_args_traits(self, mock_os_conf, mock_pr):
def test_args_traits(self, mock_pr):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--trait', 'foo:bar', '--trait', 'answer:42',
'--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=['foo:bar', 'answer:42'],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
self._check(mock_pr, args,
{'traits': ['foo:bar', 'answer:42']}, {})
def test_args_configdrive(self, mock_os_conf, mock_pr):
def test_args_configdrive(self, mock_pr):
with tempfile.NamedTemporaryFile() as fp:
fp.write(b'foo\n')
fp.flush()
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--ssh-public-key', fp.name, '--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
self._check(mock_pr, args, {}, {})
config = mock_pr.return_value.provision_node.call_args[1]['config']
self.assertEqual(['foo'], config.ssh_keys)
@mock.patch.object(_config.InstanceConfig, 'add_user', autospec=True)
def test_args_user_name(self, mock_add_user, mock_os_conf, mock_pr):
def test_args_user_name(self, mock_add_user, mock_pr):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--user-name', 'banana', '--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
self._check(mock_pr, args, {}, {})
config = mock_pr.return_value.provision_node.call_args[1]['config']
self.assertEqual([], config.ssh_keys)
mock_add_user.assert_called_once_with(config, 'banana', sudo=False)
@mock.patch.object(_config.InstanceConfig, 'add_user', autospec=True)
def test_args_user_name_with_sudo(self, mock_add_user, mock_os_conf,
mock_pr):
def test_args_user_name_with_sudo(self, mock_add_user, mock_pr):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--user-name', 'banana', '--resource-class', 'compute',
'--passwordless-sudo']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
self._check(mock_pr, args, {}, {})
config = mock_pr.return_value.provision_node.call_args[1]['config']
self.assertEqual([], config.ssh_keys)
mock_add_user.assert_called_once_with(config, 'banana', sudo=True)
def test_args_port(self, mock_os_conf, mock_pr):
def test_args_port(self, mock_pr):
args = ['deploy', '--port', 'myport', '--image', 'myimg',
'--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'port': 'myport'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
self._check(mock_pr, args, {}, {'nics': [{'port': 'myport'}]})
def test_args_no_nics(self, mock_os_conf, mock_pr):
def test_args_no_nics(self, mock_pr):
args = ['deploy', '--image', 'myimg', '--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=None,
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
self._check(mock_pr, args, {}, {'nics': None})
def test_args_networks_and_ports(self, mock_os_conf, mock_pr):
def test_args_networks_and_ports(self, mock_pr):
args = ['deploy', '--network', 'net1', '--port', 'port1',
'--port', 'port2', '--network', 'net2',
'--image', 'myimg', '--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'net1'}, {'port': 'port1'},
{'port': 'port2'}, {'network': 'net2'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
self._check(mock_pr, args, {},
{'nics': [{'network': 'net1'}, {'port': 'port1'},
{'port': 'port2'}, {'network': 'net2'}]})
def test_args_hostname(self, mock_os_conf, mock_pr):
args = ['deploy', '--hostname', 'host', '--image', 'myimg',
def test_args_hostname(self, mock_pr):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--hostname', 'host', '--resource-class', 'compute']
self._check(mock_pr, args, {}, {'hostname': 'host'})
def test_args_with_candidates(self, mock_pr):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--candidate', 'node1', '--candidate', 'node2',
'--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=None,
root_disk_size=None,
config=mock.ANY,
hostname='host',
netboot=False,
wait=1800)
self._check(mock_pr, args, {'candidates': ['node1', 'node2']}, {})
def test_args_with_candidates(self, mock_os_conf, mock_pr):
args = ['deploy', '--hostname', 'host', '--image', 'myimg',
'--candidate', 'node1', '--candidate', 'node2']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class=None,
conductor_group=None,
capabilities={},
traits=[],
candidates=['node1', 'node2']
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=None,
root_disk_size=None,
config=mock.ANY,
hostname='host',
netboot=False,
wait=1800)
def test_args_conductor_group(self, mock_pr):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--conductor-group', 'loc1', '--resource-class', 'compute']
self._check(mock_pr, args, {'conductor_group': 'loc1'}, {})
def test_args_conductor_group(self, mock_os_conf, mock_pr):
args = ['deploy', '--conductor-group', 'loc1', '--image', 'myimg',
'--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group='loc1',
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=None,
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
def test_args_http_image_with_checksum(self, mock_os_conf, mock_pr):
def test_args_http_image_with_checksum(self, mock_pr):
args = ['deploy', '--image', 'https://example.com/image.img',
'--image-checksum', '95e750180c7921ea0d545c7165db66b8',
'--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image=mock.ANY,
nics=None,
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
'--network', 'mynet', '--resource-class', 'compute']
self._check(mock_pr, args, {}, {'image': mock.ANY})
source = mock_pr.return_value.provision_node.call_args[1]['image']
self.assertIsInstance(source, sources.HttpWholeDiskImage)
self.assertEqual('https://example.com/image.img', source.url)
self.assertEqual('95e750180c7921ea0d545c7165db66b8', source.checksum)
def test_args_http_image_with_checksum_url(self, mock_os_conf, mock_pr):
def test_args_http_image_with_checksum_url(self, mock_pr):
args = ['deploy', '--image', 'http://example.com/image.img',
'--image-checksum', 'http://example.com/CHECKSUMS',
'--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image=mock.ANY,
nics=None,
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=1800)
'--network', 'mynet', '--resource-class', 'compute']
self._check(mock_pr, args, {}, {'image': mock.ANY})
source = mock_pr.return_value.provision_node.call_args[1]['image']
self.assertIsInstance(source, sources.HttpWholeDiskImage)
self.assertEqual('http://example.com/image.img', source.url)
self.assertEqual('http://example.com/CHECKSUMS', source.checksum_url)
@mock.patch.object(_cmd.LOG, 'critical', autospec=True)
def test_args_http_image_without_checksum(self, mock_log, mock_os_conf,
mock_pr):
def test_args_http_image_without_checksum(self, mock_log, mock_pr):
args = ['deploy', '--image', 'http://example.com/image.img',
'--resource-class', 'compute']
self.assertRaises(SystemExit, _cmd.main, args)
@ -737,53 +378,25 @@ class TestDeploy(testtools.TestCase):
self.assertFalse(mock_pr.return_value.reserve_node.called)
self.assertFalse(mock_pr.return_value.provision_node.called)
def test_args_custom_wait(self, mock_os_conf, mock_pr):
def test_args_custom_wait(self, mock_pr):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--wait', '3600', '--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=3600)
self._check(mock_pr, args, {}, {'wait': 3600})
def test_args_no_wait(self, mock_os_conf, mock_pr):
def test_args_no_wait(self, mock_pr):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--no-wait', '--resource-class', 'compute']
_cmd.main(args)
mock_pr.assert_called_once_with(
cloud_region=mock_os_conf.return_value.get_one.return_value,
dry_run=False)
mock_pr.return_value.reserve_node.assert_called_once_with(
resource_class='compute',
conductor_group=None,
capabilities={},
traits=[],
candidates=None
)
mock_pr.return_value.provision_node.assert_called_once_with(
mock_pr.return_value.reserve_node.return_value,
image='myimg',
nics=[{'network': 'mynet'}],
root_disk_size=None,
config=mock.ANY,
hostname=None,
netboot=False,
wait=None)
self._check(mock_pr, args, {}, {'wait': None})
def test_with_root_size(self, mock_pr):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--root-size', '100', '--resource-class', 'compute']
self._check(mock_pr, args, {}, {'root_size_gb': 100})
def test_with_swap_size(self, mock_pr):
args = ['deploy', '--network', 'mynet', '--image', 'myimg',
'--swap-size', '4096', '--resource-class', 'compute']
self._check(mock_pr, args, {}, {'swap_size_mb': 4096})
@mock.patch.object(_provisioner, 'Provisioner', autospec=True)

View File

@ -585,7 +585,26 @@ abcd image
self.assertFalse(self.api.release_node.called)
self.assertFalse(self.conn.network.delete_port.called)
def test_with_root_disk_size(self):
def test_with_root_size(self):
self.updates['/instance_info/root_gb'] = 50
self.pr.provision_node(self.node, 'image', [{'network': 'network'}],
root_size_gb=50)
self.conn.network.create_port.assert_called_once_with(
network_id=self.conn.network.find_network.return_value.id)
self.api.attach_port_to_node.assert_called_once_with(
self.node.uuid, self.conn.network.create_port.return_value.id)
self.api.update_node.assert_called_once_with(self.node, self.updates)
self.api.validate_node.assert_called_once_with(self.node,
validate_deploy=True)
self.api.node_action.assert_called_once_with(self.node, 'active',
configdrive=mock.ANY)
self.assertFalse(self.wait_mock.called)
self.assertFalse(self.api.release_node.called)
self.assertFalse(self.conn.network.delete_port.called)
def test_with_deprecated_root_size(self):
self.updates['/instance_info/root_gb'] = 50
self.pr.provision_node(self.node, 'image', [{'network': 'network'}],
@ -604,6 +623,25 @@ abcd image
self.assertFalse(self.api.release_node.called)
self.assertFalse(self.conn.network.delete_port.called)
def test_with_swap_size(self):
self.updates['/instance_info/swap_mb'] = 4096
self.pr.provision_node(self.node, 'image', [{'network': 'network'}],
swap_size_mb=4096)
self.conn.network.create_port.assert_called_once_with(
network_id=self.conn.network.find_network.return_value.id)
self.api.attach_port_to_node.assert_called_once_with(
self.node.uuid, self.conn.network.create_port.return_value.id)
self.api.update_node.assert_called_once_with(self.node, self.updates)
self.api.validate_node.assert_called_once_with(self.node,
validate_deploy=True)
self.api.node_action.assert_called_once_with(self.node, 'active',
configdrive=mock.ANY)
self.assertFalse(self.wait_mock.called)
self.assertFalse(self.api.release_node.called)
self.assertFalse(self.conn.network.delete_port.called)
def test_with_capabilities(self):
inst = self.pr.provision_node(self.node, 'image',
[{'network': 'network'}],

View File

@ -13,6 +13,8 @@
vars:
image: "{{ metalsmith_whole_disk_image }}"
image_checksum: "{{ metalsmith_whole_disk_checksum | default('') }}"
# NOTE(dtantsur): cannot specify swap with whole disk images
metalsmith_swap_size:
- name: Test a partition image
include: exercise.yaml

View File

@ -35,6 +35,8 @@ The following optional variables provide the defaults for Instance_ attributes:
the default for ``root_size``.
``metalsmith_ssh_public_keys``
the default for ``ssh_public_keys``.
``metalsmith_swap_size``
the default for ``swap_size``.
``metalsmith_traits``
the default for ``traits``.
``metalsmith_user_name``
@ -87,7 +89,7 @@ Each instances has the following attributes:
``resource_class`` (defaults to ``metalsmith_resource_class``)
requested node's resource class.
``root_size`` (defaults to ``metalsmith_root_size``)
size of the root partition, if partition images are used.
size of the root partition (in GiB), if partition images are used.
.. note::
Also required for whole-disk images due to how the Bare Metal service
@ -95,6 +97,9 @@ Each instances has the following attributes:
``ssh_public_keys`` (defaults to ``metalsmith_ssh_public_keys``)
list of file names with SSH public keys to put to the node.
``swap_size`` (defaults to ``metalsmith_swap_size``)
size of the swap partition (in MiB), if partition images are used
(it's an error to set it for a whole disk image).
``traits``
list of traits the node should have.
``user_name`` (defaults to ``metalsmith_user_name``)
@ -123,6 +128,7 @@ Example
- hostname: compute-0
resource_class: compute
root_size: 100
swap_size: 4096
capabilities:
boot_mode: uefi
traits:
@ -130,6 +136,7 @@ Example
- hostname: compute-1
resource_class: compute
root_size: 100
swap_size: 4096
capabilities:
boot_mode: uefi
user_name: heat-admin

View File

@ -8,8 +8,9 @@ metalsmith_netboot: false
metalsmith_nics: []
metalsmith_resource_class:
metalsmith_root_size:
metalsmith_traits: []
metalsmith_ssh_public_keys: []
metalsmith_swap_size:
metalsmith_traits: []
metalsmith_user_name: metalsmith
# Wait parameters

View File

@ -15,7 +15,10 @@
{% endfor %}
{% endfor %}
{% if root_size %}
--root-disk-size {{ root_size }}
--root-size {{ root_size }}
{% endif %}
{% if swap_size %}
--swap-size {{ swap_size }}
{% endif %}
{% for ssh_key in ssh_public_keys %}
--ssh-public-key {{ ssh_key }}
@ -54,6 +57,7 @@
root_size: "{{ instance.root_size | default(metalsmith_root_size) }}"
ssh_public_keys: "{{ instance.ssh_public_keys | default(metalsmith_ssh_public_keys) }}"
state: "{{ instance.state | default('present') }}"
swap_size: "{{ instance.swap_size | default(metalsmith_swap_size) }}"
traits: "{{ instance.traits | default(metalsmith_traits) }}"
user_name: "{{ instance.user_name | default(metalsmith_user_name) }}"
with_items: "{{ metalsmith_instances }}"