Merge "Start moving the import CLI command to the Ansible implementation"
This commit is contained in:
commit
ed75570496
@ -70,7 +70,7 @@ openstack.tripleoclient.v2 =
|
||||
overcloud_image_upload = tripleoclient.v1.overcloud_image:UploadOvercloudImage
|
||||
overcloud_node_configure = tripleoclient.v1.overcloud_node:ConfigureNode
|
||||
overcloud_node_delete = tripleoclient.v1.overcloud_node:DeleteNode
|
||||
overcloud_node_import = tripleoclient.v1.overcloud_node:ImportNode
|
||||
overcloud_node_import = tripleoclient.v2.overcloud_node:ImportNode
|
||||
overcloud_node_introspect = tripleoclient.v2.overcloud_node:IntrospectNode
|
||||
overcloud_node_provide = tripleoclient.v1.overcloud_node:ProvideNode
|
||||
overcloud_node_discover = tripleoclient.v1.overcloud_node:DiscoverNode
|
||||
|
@ -13,7 +13,12 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import collections
|
||||
import fixtures
|
||||
import json
|
||||
import mock
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from osc_lib.tests import utils as test_utils
|
||||
|
||||
@ -22,6 +27,151 @@ from tripleoclient.tests.v2.overcloud_node import fakes
|
||||
from tripleoclient.v2 import overcloud_node
|
||||
|
||||
|
||||
class TestImportNode(fakes.TestOvercloudNode):
|
||||
|
||||
def setUp(self):
|
||||
super(TestImportNode, self).setUp()
|
||||
|
||||
self.nodes_list = [{
|
||||
"pm_user": "stack",
|
||||
"pm_addr": "192.168.122.1",
|
||||
"pm_password": "KEY1",
|
||||
"pm_type": "pxe_ssh",
|
||||
"mac": [
|
||||
"00:0b:d0:69:7e:59"
|
||||
],
|
||||
}, {
|
||||
"pm_user": "stack",
|
||||
"pm_addr": "192.168.122.2",
|
||||
"pm_password": "KEY2",
|
||||
"pm_type": "pxe_ssh",
|
||||
"mac": [
|
||||
"00:0b:d0:69:7e:58"
|
||||
]
|
||||
}]
|
||||
|
||||
# NOTE(cloudnull): Workflow and client calls are still mocked because
|
||||
# mistal is still presnet here.
|
||||
self.workflow = self.app.client_manager.workflow_engine
|
||||
execution = mock.Mock()
|
||||
execution.id = "IDID"
|
||||
self.workflow.executions.create.return_value = execution
|
||||
client = self.app.client_manager.tripleoclient
|
||||
self.websocket = client.messaging_websocket()
|
||||
self.websocket.wait_for_messages.return_value = [{
|
||||
"status": "SUCCESS",
|
||||
"message": "Success",
|
||||
"registered_nodes": [{
|
||||
"uuid": "MOCK_NODE_UUID"
|
||||
}],
|
||||
"execution_id": execution.id
|
||||
}]
|
||||
|
||||
self.json_file = tempfile.NamedTemporaryFile(
|
||||
mode='w', delete=False, suffix='.json')
|
||||
json.dump(self.nodes_list, self.json_file)
|
||||
self.json_file.close()
|
||||
self.addCleanup(os.unlink, self.json_file.name)
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = overcloud_node.ImportNode(self.app, None)
|
||||
|
||||
image = collections.namedtuple('image', ['id', 'name'])
|
||||
self.app.client_manager.image = mock.Mock()
|
||||
self.app.client_manager.image.images.list.return_value = [
|
||||
image(id=3, name='overcloud-full'),
|
||||
]
|
||||
|
||||
self.http_boot = '/var/lib/ironic/httpboot'
|
||||
|
||||
self.useFixture(fixtures.MockPatch(
|
||||
'os.path.exists', autospec=True,
|
||||
side_effect=lambda path: path in [os.path.join(self.http_boot, i)
|
||||
for i in ('agent.kernel',
|
||||
'agent.ramdisk')]))
|
||||
|
||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
||||
autospec=True)
|
||||
def test_import_only(self, mock_playbook):
|
||||
parsed_args = self.check_parser(self.cmd,
|
||||
[self.json_file.name],
|
||||
[('introspect', False),
|
||||
('provide', False)])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
||||
autospec=True)
|
||||
def test_import_and_introspect(self, mock_playbook):
|
||||
parsed_args = self.check_parser(self.cmd,
|
||||
[self.json_file.name,
|
||||
'--introspect'],
|
||||
[('introspect', True),
|
||||
('provide', False)])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_playbook.assert_called_once_with(
|
||||
workdir=mock.ANY,
|
||||
playbook=mock.ANY,
|
||||
inventory=mock.ANY,
|
||||
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
||||
extra_vars={
|
||||
'node_uuids': ['MOCK_NODE_UUID'],
|
||||
'run_validations': False,
|
||||
'concurrency': 20
|
||||
}
|
||||
)
|
||||
|
||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
||||
autospec=True)
|
||||
def test_import_and_provide(self, mock_playbook):
|
||||
parsed_args = self.check_parser(self.cmd,
|
||||
[self.json_file.name,
|
||||
'--provide'],
|
||||
[('introspect', False),
|
||||
('provide', True)])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
||||
autospec=True)
|
||||
def test_import_and_introspect_and_provide(self, mock_playbook):
|
||||
parsed_args = self.check_parser(self.cmd,
|
||||
[self.json_file.name,
|
||||
'--introspect',
|
||||
'--provide'],
|
||||
[('introspect', True),
|
||||
('provide', True)])
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock_playbook.assert_called_once_with(
|
||||
workdir=mock.ANY,
|
||||
playbook=mock.ANY,
|
||||
inventory=mock.ANY,
|
||||
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
||||
extra_vars={
|
||||
'node_uuids': ['MOCK_NODE_UUID'],
|
||||
'run_validations': False,
|
||||
'concurrency': 20
|
||||
}
|
||||
)
|
||||
|
||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
||||
autospec=True)
|
||||
def test_import_with_netboot(self, mock_playbook):
|
||||
parsed_args = self.check_parser(self.cmd,
|
||||
[self.json_file.name,
|
||||
'--no-deploy-image'],
|
||||
[('no_deploy_image', True)])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
||||
autospec=True)
|
||||
def test_import_with_no_deployed_image(self, mock_playbook):
|
||||
parsed_args = self.check_parser(self.cmd,
|
||||
[self.json_file.name,
|
||||
'--instance-boot-option',
|
||||
'netboot'],
|
||||
[('instance_boot_option', 'netboot')])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
|
||||
class TestIntrospectNode(fakes.TestOvercloudNode):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -13,7 +13,9 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
|
||||
from osc_lib.i18n import _
|
||||
|
||||
@ -28,12 +30,103 @@ from tripleoclient.v1.overcloud_node import CleanNode # noqa
|
||||
from tripleoclient.v1.overcloud_node import ConfigureNode # noqa
|
||||
from tripleoclient.v1.overcloud_node import DeleteNode # noqa
|
||||
from tripleoclient.v1.overcloud_node import DiscoverNode # noqa
|
||||
from tripleoclient.v1.overcloud_node import ImportNode # noqa
|
||||
from tripleoclient.v1.overcloud_node import ProvideNode # noqa
|
||||
from tripleoclient.v1.overcloud_node import ProvisionNode # noqa
|
||||
from tripleoclient.v1.overcloud_node import UnprovisionNode # noqa
|
||||
|
||||
|
||||
class ImportNode(command.Command):
|
||||
"""Import baremetal nodes from a JSON, YAML or CSV file.
|
||||
|
||||
The node status will be set to 'manageable' by default.
|
||||
"""
|
||||
|
||||
log = logging.getLogger(__name__ + ".ImportNode")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ImportNode, self).get_parser(prog_name)
|
||||
parser.add_argument('--introspect',
|
||||
action='store_true',
|
||||
help=_('Introspect the imported nodes'))
|
||||
parser.add_argument('--run-validations', action='store_true',
|
||||
default=False,
|
||||
help=_('Run the pre-deployment validations. These'
|
||||
' external validations are from the'
|
||||
' TripleO Validations project.'))
|
||||
parser.add_argument('--validate-only', action='store_true',
|
||||
default=False,
|
||||
help=_('Validate the env_file and then exit '
|
||||
'without actually importing the nodes.'))
|
||||
parser.add_argument('--provide',
|
||||
action='store_true',
|
||||
help=_('Provide (make available) the nodes'))
|
||||
parser.add_argument('--no-deploy-image', action='store_true',
|
||||
help=_('Skip setting the deploy kernel and '
|
||||
'ramdisk.'))
|
||||
parser.add_argument('--instance-boot-option',
|
||||
choices=['local', 'netboot'], default=None,
|
||||
help=_('Whether to set instances for booting from'
|
||||
' local hard drive (local) or network '
|
||||
' (netboot).'))
|
||||
parser.add_argument("--http-boot",
|
||||
default=os.environ.get(
|
||||
'HTTP_BOOT',
|
||||
constants.IRONIC_HTTP_BOOT_BIND_MOUNT),
|
||||
help=_("Root directory for the "
|
||||
" ironic-python-agent image"))
|
||||
parser.add_argument('--concurrency', type=int,
|
||||
default=20,
|
||||
help=_('Maximum number of nodes to introspect at '
|
||||
'once.'))
|
||||
parser.add_argument('env_file', type=argparse.FileType('r'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)" % parsed_args)
|
||||
|
||||
nodes_config = oooutils.parse_env_file(parsed_args.env_file)
|
||||
parsed_args.env_file.close()
|
||||
|
||||
if parsed_args.validate_only:
|
||||
return baremetal.validate_nodes(self.app.client_manager,
|
||||
nodes_json=nodes_config)
|
||||
|
||||
# Look for *specific* deploy images and update the node data if
|
||||
# one is found.
|
||||
if not parsed_args.no_deploy_image:
|
||||
oooutils.update_nodes_deploy_data(nodes_config,
|
||||
http_boot=parsed_args.http_boot)
|
||||
nodes = baremetal.register_or_update(
|
||||
self.app.client_manager,
|
||||
nodes_json=nodes_config,
|
||||
instance_boot_option=parsed_args.instance_boot_option
|
||||
)
|
||||
|
||||
nodes_uuids = [node['uuid'] for node in nodes]
|
||||
|
||||
if parsed_args.introspect:
|
||||
extra_vars = {
|
||||
"node_uuids": nodes_uuids,
|
||||
"run_validations": parsed_args.run_validations,
|
||||
"concurrency": parsed_args.concurrency,
|
||||
}
|
||||
|
||||
with oooutils.TempDirs() as tmp:
|
||||
oooutils.run_ansible_playbook(
|
||||
playbook='cli-baremetal-introspect.yaml',
|
||||
inventory='localhost,',
|
||||
workdir=tmp,
|
||||
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
||||
extra_vars=extra_vars
|
||||
)
|
||||
|
||||
if parsed_args.provide:
|
||||
baremetal.provide(
|
||||
self.app.client_manager,
|
||||
node_uuids=nodes_uuids
|
||||
)
|
||||
|
||||
|
||||
class IntrospectNode(command.Command):
|
||||
"""Introspect specified nodes or all nodes in 'manageable' state."""
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user