diff --git a/doc/source/modules/modules-tripleo_baremetal_populate_environment.rst b/doc/source/modules/modules-tripleo_baremetal_populate_environment.rst new file mode 100644 index 000000000..9503d74df --- /dev/null +++ b/doc/source/modules/modules-tripleo_baremetal_populate_environment.rst @@ -0,0 +1,14 @@ +=============================================== +Module - tripleo_baremetal_populate_environment +=============================================== + + +This module provides for the following ansible plugin: + + * tripleo_baremetal_populate_environment + + +.. ansibleautoplugin:: + :module: tripleo_ansible/ansible_plugins/modules/tripleo_baremetal_populate_environment.py + :documentation: true + :examples: true diff --git a/tripleo_ansible/ansible_plugins/module_utils/baremetal_deploy.py b/tripleo_ansible/ansible_plugins/module_utils/baremetal_deploy.py index d2a05e6a3..28f530dcc 100644 --- a/tripleo_ansible/ansible_plugins/module_utils/baremetal_deploy.py +++ b/tripleo_ansible/ansible_plugins/module_utils/baremetal_deploy.py @@ -103,6 +103,20 @@ _ROLES_INPUT_SCHEMA = { """JSON schema of the roles list.""" +NETWORK_KEYS = ( + 'mtu', + 'tags', +) + + +SUBNET_KEYS = ( + 'cidr', + 'gateway_ip', + 'host_routes', + 'dns_nameservers', +) + + class BaremetalDeployException(Exception): pass @@ -282,6 +296,41 @@ def check_existing(instances, provisioner, baremetal): return found, not_found +def populate_environment(instance_uuids, provisioner, environment, + ctlplane_network): + + resource_registry = environment.setdefault( + 'resource_registry', {}) + resource_registry.setdefault( + 'OS::TripleO::DeployedServer::ControlPlanePort', + '/usr/share/openstack-tripleo-heat-templates' + '/deployed-server/deployed-neutron-port.yaml') + port_map = (environment.setdefault('parameter_defaults', {}) + .setdefault('DeployedServerPortMap', {})) + for uuid in instance_uuids: + instance = provisioner.show_instance(uuid) + nets = nics_to_port_map(instance.nics(), provisioner.connection) + ctlplane_net = nets.get(ctlplane_network) + if not ctlplane_net: + continue + fixed_ips = ctlplane_net.get('fixed_ips', []) + network_all = ctlplane_net.get('network', {}) + network = {k: v for k, v in network_all.items() + if k in NETWORK_KEYS} + subnets = [] + for subnet in ctlplane_net.get('subnets', []): + subnets.append({k: v for k, v in subnet.items() + if k in SUBNET_KEYS}) + ctlplane = { + 'fixed_ips': fixed_ips, + 'network': network, + 'subnets': subnets + } + + port_map['%s-%s' % (instance.hostname, ctlplane_network)] = ctlplane + return environment + + def build_hostname_format(hostname_format, role_name): if not hostname_format: hostname_format = '%stackname%-{}-%index%'.format( @@ -351,3 +400,18 @@ def get_source(instance): kernel=image.get('kernel'), ramdisk=image.get('ramdisk'), checksum=image.get('checksum')) + + +def nics_to_port_map(nics, connection): + """Build a port map from a metalsmith instance.""" + port_map = {} + for nic in nics: + for ip in nic.fixed_ips: + net_name = getattr(nic.network, 'name', None) or nic.network.id + subnet = connection.network.get_subnet(ip['subnet_id']) + net_info = port_map.setdefault( + net_name, {'network': nic.network.to_dict(), + 'fixed_ips': [], 'subnets': []}) + net_info['fixed_ips'].append({'ip_address': ip['ip_address']}) + net_info['subnets'].append(subnet.to_dict()) + return port_map diff --git a/tripleo_ansible/ansible_plugins/modules/tripleo_baremetal_populate_environment.py b/tripleo_ansible/ansible_plugins/modules/tripleo_baremetal_populate_environment.py new file mode 100644 index 000000000..b122d49fc --- /dev/null +++ b/tripleo_ansible/ansible_plugins/modules/tripleo_baremetal_populate_environment.py @@ -0,0 +1,133 @@ +#!/usr/bin/python +# Copyright 2020 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. + +from __future__ import absolute_import +__metaclass__ = type + +from ansible.module_utils import baremetal_deploy as bd +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.openstack import openstack_cloud_from_module +from ansible.module_utils.openstack import openstack_full_argument_spec +from ansible.module_utils.openstack import openstack_module_kwargs + +import metalsmith + +import yaml + + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + + +DOCUMENTATION = ''' +--- +module: tripleo_baremetal_populate_environment +short_description: Add parameters to a heat environment with instance data +version_added: "2.9" +author: "Steve Baker (@stevebaker)" +description: + - Takes a list of existing instances and a heat environment file + and appends to that environment with instance-specific parameters such + as the port map. +options: + instances: + description: + - List of instance uuids to use for building the environment. + required: true + type: list + elements: dict + suboptions: + id: + description + - Node UUID to look up node details + type: str + environment: + description: + - Existing heat environment data to add to + type: dict + default: {} + ctlplane_network: + description: + - Name of control plane network + default: ctlplane +''' + +RETURN = ''' +environment: + description: Heat environment data to be used with the overcloud deploy. + This is only a partial environment, further changes are + required once instance changes have been made. + returned: changed + type: dict + sample: { + "parameter_defaults": { + "ComputeDeployedServerCount": 3, + "ComputeDeployedServerHostnameFormat": "%stackname%-novacompute-%index%", + "ControllerDeployedServerCount": 3, + "ControllerDeployedServerHostnameFormat": "%stackname%-controller-%index%", + "DeployedServerPortMap": {} + "HostnameMap": { + "overcloud-controller-0": "overcloud-controller-0", + "overcloud-controller-1": "overcloud-controller-1", + "overcloud-controller-2": "overcloud-controller-2", + "overcloud-novacompute-0": "overcloud-novacompute-0", + "overcloud-novacompute-1": "overcloud-novacompute-1", + "overcloud-novacompute-2": "overcloud-novacompute-2" + } + }, + "resource_registry": { + "OS::TripleO::DeployedServer::ControlPlanePort": "/usr/share/openstack-tripleo-heat-templates/deployed-server/deployed-neutron-port.yaml" + } + } +''' # noqa + + +def main(): + argument_spec = openstack_full_argument_spec( + **yaml.safe_load(DOCUMENTATION)['options'] + ) + module_kwargs = openstack_module_kwargs() + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=False, + **module_kwargs + ) + + sdk, cloud = openstack_cloud_from_module(module) + provisioner = metalsmith.Provisioner(cloud_region=cloud.config) + + instance_uuids = [i['id'] for i in module.params['instances']] + + try: + env = bd.populate_environment( + instance_uuids=instance_uuids, + provisioner=provisioner, + environment=module.params['environment'], + ctlplane_network=module.params['ctlplane_network'] + ) + module.exit_json( + changed=False, + environment=env + ) + except Exception as e: + module.fail_json(msg=str(e)) + + +if __name__ == '__main__': + main()