From d6cd2b1391ee102b14147f14eee269c8255c806c Mon Sep 17 00:00:00 2001 From: apetrich Date: Thu, 31 Oct 2019 13:50:24 +0100 Subject: [PATCH] Create a node import module and playbook Moving the import from mistral into ansible Adding a playbook to do the node import and modules for validating and registering Story: 2007126 Task: 38183 Change-Id: I46ecb0e46bdeba3496b888a645bc95d86ef58498 --- .../modules/baremetal_nodes_validate.py | 107 ++++++++++++ .../baremetal_register_or_update_nodes.py | 161 ++++++++++++++++++ .../playbooks/cli-overcloud-node-import.yaml | 98 +++++++++++ 3 files changed, 366 insertions(+) create mode 100644 tripleo_ansible/ansible_plugins/modules/baremetal_nodes_validate.py create mode 100644 tripleo_ansible/ansible_plugins/modules/baremetal_register_or_update_nodes.py create mode 100644 tripleo_ansible/playbooks/cli-overcloud-node-import.yaml diff --git a/tripleo_ansible/ansible_plugins/modules/baremetal_nodes_validate.py b/tripleo_ansible/ansible_plugins/modules/baremetal_nodes_validate.py new file mode 100644 index 000000000..c08637dc4 --- /dev/null +++ b/tripleo_ansible/ansible_plugins/modules/baremetal_nodes_validate.py @@ -0,0 +1,107 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright (c) 2018 OpenStack Foundation +# 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. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from ansible.module_utils.basic import AnsibleModule + +from tripleo_common import exception +from tripleo_common.utils import nodes + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +--- +module: baremetal_nodes_validate + +short_description: Baremetal nodes + +version_added: "2.8" + +description: + - "Baremetal nodes functions." + +options: + node_list: + description: + - List of the nodes to be validated + required: true + +author: + - Adriano Petrich (@frac) +''' + +EXAMPLES = ''' +# Pass in a message +- name: Test with a message + baremetal_nodes_validate: + nodes_list: + - _comment: 'This is a comment' + pm_type: 'pxe_ipmitool' + pm_addr: '192.168.0.1' + pm_user: 'root' + pm_password: 'p@$$w0rd' + + - pm_type: 'ipmi' + pm_addr: '192.168.1.1' + pm_user: 'root' + pm_password: 'p@$$w0rd' +''' + + +def run_module(): + module_args = dict( + nodes_list=dict(type='list', required=True), + ) + + result = dict( + success=False, + error='' + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True + ) + + if module.check_mode: + module.exit_json(**result) + try: + nodes_json = nodes.convert_nodes_json_mac_to_ports( + module.params['nodes_list'] + ) + nodes.validate_nodes(nodes_json) + result['success'] = True + except exception.InvalidNode as exc: + result['error'] = str(exc) + module.fail_json(msg='Validation Failed', **result) + + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/tripleo_ansible/ansible_plugins/modules/baremetal_register_or_update_nodes.py b/tripleo_ansible/ansible_plugins/modules/baremetal_register_or_update_nodes.py new file mode 100644 index 000000000..58c319002 --- /dev/null +++ b/tripleo_ansible/ansible_plugins/modules/baremetal_register_or_update_nodes.py @@ -0,0 +1,161 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright (c) 2018 OpenStack Foundation +# 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. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import yaml + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.openstack import openstack_full_argument_spec +from ansible.module_utils.openstack import openstack_module_kwargs +from ansible.module_utils.openstack import openstack_cloud_from_module + +# NOTE(cloudnull): This is still using the legacy clients. We've not +# changed to using the OpenStackSDK fully because +# tripleo-common expects the legacy clients. Once +# we've updated tripleo-common to use the SDK we +# should revise this. +from glanceclient import client as glanceclient +from ironicclient import client as ironicclient +from tripleo_common.utils import nodes + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +--- +module: baremetal_nodes_validate + +short_description: Baremetal nodes + +version_added: "2.8" + +description: + - "Baremetal nodes functions." + +options: + nodes_json: + description: + - List of the nodes to be validated + type: list + required: true + remove: + required: false + type: bool + kernel_name: + required: false + ramdisk_name: + required: false + instance_boot_option: + required: false + +author: + - Adriano Petrich (@frac) +''' + + +def _get_baremetal_client(session): + return ironicclient.Client( + 1, + session=session, + os_ironic_api_version='1.36' + ) + + +def _get_image_client(session): + return glanceclient.Client( + 2, + session=session + ) + + +def run_module(): + result = dict( + success=False, + error="", + nodes=[] + ) + + argument_spec = openstack_full_argument_spec( + **yaml.safe_load(DOCUMENTATION)['options'] + ) + + module = AnsibleModule( + argument_spec, + supports_check_mode=True, + **openstack_module_kwargs() + ) + + sdk, _ = openstack_cloud_from_module(module) + conn = sdk.connect() + session = conn.session + + # if the user is working with this module in only check mode we do not + # want to make any changes to the environment, just return the current + # state with no modifications + if module.check_mode: + module.exit_json(**result) + + nodes_json = nodes.convert_nodes_json_mac_to_ports( + module.params['nodes_json'] + ) + + for node in nodes_json: + caps = node.get('capabilities', {}) + caps = nodes.capabilities_to_dict(caps) + if module.params['instance_boot_option'] is not None: + caps.setdefault('boot_option', + module.params['instance_boot_option']) + node['capabilities'] = nodes.dict_to_capabilities(caps) + + baremetal_client = _get_baremetal_client(session) + image_client = _get_image_client(session) + + try: + registered_nodes = nodes.register_all_nodes( + nodes_json, + client=baremetal_client, + remove=module.params['remove'], + glance_client=image_client, + kernel_name=module.params['kernel_name'], + ramdisk_name=module.params['ramdisk_name']) + result['success'] = True + result['nodes'] = [ + dict(uuid=node.uuid, provision_state=node.provision_state) + for node in registered_nodes + ] + except Exception as exc: + # LOG.exception("Error registering nodes with ironic.") + result['error'] = str(exc) + module.fail_json(msg='Validation Failed', **result) + + # in the event of a successful module execution, you will want to + # simple AnsibleModule.exit_json(), passing the key/value results + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/tripleo_ansible/playbooks/cli-overcloud-node-import.yaml b/tripleo_ansible/playbooks/cli-overcloud-node-import.yaml new file mode 100644 index 000000000..8c0275bb8 --- /dev/null +++ b/tripleo_ansible/playbooks/cli-overcloud-node-import.yaml @@ -0,0 +1,98 @@ +--- +# Copyright 2019 Red Hat, Inc. +# +# 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. + +- name: TripleO register or update nodes + connection: "{{ (tripleo_target_host is defined) | ternary('ssh', 'local') }}" + hosts: "{{ tripleo_target_host | default('localhost') }}" + remote_user: "{{ tripleo_target_user | default(lookup('env', 'USER')) }}" + gather_facts: "{{ (tripleo_target_host is defined) | ternary(true, false) }}" + any_errors_fatal: true + vars: + nodes_json: [] + remove: false + kernel_name: null + ramdisk_name: null + instance_boot_option: null + initial_state: 'manageable' + tasks: + - name: "Check if temp_file is set" + fail: + msg: the option temp_file is undefined + when: + - temp_file is undefined + + - name: Validate nodes + baremetal_nodes_validate: + nodes_list: "{{ nodes_json }}" + register: result + + - name: Register or Update nodes + baremetal_register_or_update_nodes: + nodes_json: "{{ nodes_json }}" + remove: "{{ remove }}" + kernel_name: "{{ kernel_name }}" + ramdisk_name: "{{ ramdisk_name }}" + instance_boot_option: "{{ instance_boot_option }}" + register: registered_nodes + + - name: "Registered nodes during enroll" + debug: + msg: "{{ registered_nodes }}" + + - name: "Save registered nodes" + copy: + content: "{{ registered_nodes.nodes | to_json }}" + dest: "{{ temp_file }}" + + - name: Register nodes failed + fail: + msg: "{{ registered_nodes.error }}" + when: result.failed + + - name: "Exit early if initial state for enroll only" + block: + - name: "Registered nodes during enroll" + debug: + msg: "{{ registered_nodes }}" + + - meta: end_play + when: + - initial_state == "enroll" + + - name: "Set new nodes fact" + set_fact: + new_nodes: "{{ registered_nodes.nodes | selectattr('provision_state', 'eq', 'enroll') | list }}" + + - name: Make nodes available + command: >- + openstack baremetal node manage {{ item.uuid }} --wait 1200 + loop: "{{ new_nodes }}" + async: 2400 + poll: 0 + register: node_manageable + + - name: poll for completion + async_status: + jid: "{{ item.ansible_job_id }}" + loop: "{{ node_manageable.results }}" + loop_control: + label: "{{ item.item }}" + register: wait + until: wait.finished + retries: 120 + + - name: "Registered nodes during enroll" + debug: + msg: "{{ new_nodes | length }} node(s) successfully moved to the 'manageable' state."