Files
metalsmith/metalsmith/test/test_metalsmith_instances.py
Steve Baker a7bd8fe1ae metalsmith_deployment role switch to metalsmith_instances
Instead of wrapping the metalsmith CLI, the metalsmith_deployment role
now uses the metalsmith_instances module. There are differences between
the instances formats of the role and the module which are partially
resolved with a simple transformation module called
metalsmith_deployment_defaults.

A 'candidates' attribute is added to metalsmith_instances, but keeping
the single 'name' attribute to remain compatible with the TripleO
usage.

Unresolved differences between the 2 are described below:

metalsmith_instances doesn't have a per-instance state attribute,
instead it has one state attribute for all instances. I propose that
support for per-instance state is dropped. This was never documented
in the README.rst anyway.

extra_args only applies to a CLI. Apart from --dry-run these arguments
are either for output formatting or Ironic API authentication. I propose
that this option is dropped. A metalsmith_debug arg is added to make
the ouput more verbose.

Change-Id: Ia30620821182c58050813e807cdde50a27d03c15
2020-10-08 09:48:20 +13:00

254 lines
8.0 KiB
Python

# Copyright 2019 Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import unittest
from unittest import mock
from metalsmith_ansible.ansible_plugins.modules \
import metalsmith_instances as mi
from metalsmith import exceptions as exc
class TestMetalsmithInstances(unittest.TestCase):
@mock.patch('metalsmith.sources.detect', autospec=True)
def test_get_source(self, mock_detect):
mi._get_source({
'image': {'href': 'overcloud-full'}
})
mi._get_source({
'image': {
'href': 'file://overcloud-full.qcow2',
'checksum': 'asdf',
'kernel': 'file://overcloud-full.vmlinuz',
'ramdisk': 'file://overcloud-full.initrd'
}
})
mock_detect.assert_has_calls([
mock.call(
image='overcloud-full',
checksum=None,
kernel=None,
ramdisk=None
),
mock.call(
image='file://overcloud-full.qcow2',
checksum='asdf',
kernel='file://overcloud-full.vmlinuz',
ramdisk='file://overcloud-full.initrd'
)
])
def test_reserve(self):
provisioner = mock.Mock()
instances = [{
'name': 'node',
'candidates': ['other_node'],
'resource_class': 'boxen',
'capabilities': {'foo': 'bar'},
'traits': ['this', 'that'],
'conductor_group': 'group'
}, {}]
reserved = [
mock.Mock(id=1),
mock.Mock(id=2),
]
# test reserve success
provisioner.reserve_node.side_effect = reserved
result = mi.reserve(provisioner, instances, True)
provisioner.reserve_node.assert_has_calls([
mock.call(
candidates=['other_node', 'node'],
capabilities={'foo': 'bar'},
conductor_group='group',
resource_class='boxen',
traits=['this', 'that']
),
mock.call(
candidates=None,
capabilities=None,
conductor_group=None,
resource_class='baremetal',
traits=None
)
])
self.assertTrue(result[0])
self.assertEqual(reserved, result[1])
# test reserve failure with cleanup
instances = [{}, {}, {}]
reserved = [
mock.Mock(id=1),
mock.Mock(id=2),
exc.ReservationFailed('ouch')
]
provisioner.reserve_node.side_effect = reserved
self.assertRaises(exc.ReservationFailed, mi.reserve,
provisioner, instances, True)
provisioner.unprovision_node.assert_has_calls([
mock.call(1),
mock.call(2)
])
@mock.patch('metalsmith.sources.detect', autospec=True)
@mock.patch('metalsmith.instance_config.CloudInitConfig', autospec=True)
def test_provision(self, mock_config, mock_detect):
config = mock_config.return_value
image = mock_detect.return_value
provisioner = mock.Mock()
instances = [{
'name': 'node-1',
'hostname': 'overcloud-controller-1',
'image': {'href': 'overcloud-full'}
}, {
'name': 'node-2',
'hostname': 'overcloud-controller-2',
'image': {'href': 'overcloud-full'},
'nics': {'network': 'ctlplane'},
'root_size_gb': 200,
'swap_size_mb': 16,
'netboot': True,
'ssh_public_keys': 'abcd',
'user_name': 'centos',
'passwordless_sudo': False
}, {
'name': 'node-3',
'hostname': 'overcloud-controller-3',
'image': {'href': 'overcloud-full'}
}, {
'name': 'node-4',
'hostname': 'overcloud-compute-0',
'image': {'href': 'overcloud-full'}
}]
provisioned = [
mock.Mock(uuid=1),
mock.Mock(uuid=2),
mock.Mock(uuid=3),
mock.Mock(uuid=4),
]
# test provision success
provisioner.provision_node.side_effect = provisioned
# provision 4 nodes with concurrency of 2
result = mi.provision(provisioner, instances, 3600, 2, True, True)
provisioner.provision_node.assert_has_calls([
mock.call(
'node-1',
config=config,
hostname='overcloud-controller-1',
image=image,
netboot=False,
nics=None,
root_size_gb=None,
swap_size_mb=None
),
mock.call(
'node-2',
config=config,
hostname='overcloud-controller-2',
image=image,
netboot=True,
nics={'network': 'ctlplane'},
root_size_gb=200,
swap_size_mb=16
),
mock.call(
'node-3',
config=config,
hostname='overcloud-controller-3',
image=image,
netboot=False,
nics=None,
root_size_gb=None,
swap_size_mb=None
),
mock.call(
'node-4',
config=config,
hostname='overcloud-compute-0',
image=image,
netboot=False,
nics=None,
root_size_gb=None,
swap_size_mb=None
),
])
mock_config.assert_has_calls([
mock.call(ssh_keys=None),
mock.call(ssh_keys='abcd'),
])
config.add_user.assert_called_once_with(
'centos', admin=True, sudo=False)
mock_detect.assert_has_calls([
mock.call(
image='overcloud-full',
checksum=None,
kernel=None,
ramdisk=None
),
mock.call(
image='overcloud-full',
checksum=None,
kernel=None,
ramdisk=None
),
mock.call(
image='overcloud-full',
checksum=None,
kernel=None,
ramdisk=None
),
mock.call(
image='overcloud-full',
checksum=None,
kernel=None,
ramdisk=None
),
])
self.assertTrue(result[0])
self.assertEqual(provisioned, result[1])
# test provision failure with cleanup
instances = [{
'name': 'node-1',
'hostname': 'overcloud-controller-1',
'image': {'href': 'overcloud-full'}
}, {
'name': 'node-2',
'hostname': 'overcloud-controller-2',
'image': {'href': 'overcloud-full'},
}, {
'name': 'node-3',
'hostname': 'overcloud-controller-3',
'image': {'href': 'overcloud-full'},
}]
provisioned = [
mock.Mock(uuid=1),
mock.Mock(uuid=2),
exc.Error('ouch')
]
provisioner.provision_node.side_effect = provisioned
self.assertRaises(exc.Error, mi.provision,
provisioner, instances, 3600, 20, True, True)
provisioner.unprovision_node.assert_has_calls([
mock.call(1),
mock.call(2)
])