Add os-net-config mappings support
Adds an ansible plug-in that can process the input in THT parameter 'NetConfigDataLookup' used for the OsNetConfigMappings resource in tripleo-heat-templates. Add tasks in the tripleo_network_config role to call the plug-in and write the result into the default mapping file for os-net-config. Change-Id: I968771376cdb3cd2e8e576a4de226b1eccebb23a Blueprint: bp/nova-less-deploychanges/69/749669/6
parent
fc54057525
commit
6bd7eeb7cc
|
@ -0,0 +1,161 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# 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.
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
import copy
|
||||
import os
|
||||
import subprocess
|
||||
import yaml
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'
|
||||
}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: tripleo_os_net_config_mappings
|
||||
author:
|
||||
- Harald Jensås (hjensas@redhat.com)
|
||||
version_added: '2.8'
|
||||
short_description: Configure os-net-config mappings for nodes or node groups
|
||||
notes: []
|
||||
description:
|
||||
- This module creates os-net-config mapping for nodes or node groups based on
|
||||
the input data provided. MAC addresses or DMI table strings can be used
|
||||
to identify specific nodes or node groups. See manual page for DMIDECODE(8)
|
||||
for a list of DMI table strings that can be used.
|
||||
options:
|
||||
net_config_data_lookup:
|
||||
description:
|
||||
- Per node and/or per node group configuration map
|
||||
type: dict
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Map os-net-config nicX abstraction using MAC address
|
||||
tripleo_os_net_config_mappings:
|
||||
net_config_data_lookup:
|
||||
overcloud-controller-0:
|
||||
nic1: "00:c8:7c:e6:f0:2e"
|
||||
overcloud-compute-13:
|
||||
nic1: "00:18:7d:99:0c:b6"
|
||||
- name: Interface name to os-net-config nicX abstraction using system-uuid
|
||||
tripleo_os_net_config_mappings:
|
||||
net_config_data_lookup:
|
||||
overcloud-controller-0:
|
||||
dmiString: 'system-uuid'
|
||||
id: 'A8C85861-1B16-4803-8689-AFC62984F8F6'
|
||||
nic1: em3
|
||||
nic2: em1
|
||||
nic3: em2
|
||||
nic4: em4
|
||||
- name: Interface name to os-net-config nicX abstraction for node groups using system-product-name
|
||||
tripleo_os_net_config_mappings:
|
||||
net_config_data_lookup:
|
||||
nodegroup-dell-poweredge-r630:
|
||||
dmiString: "system-product-name"
|
||||
id: "PowerEdge R630"
|
||||
nic1: em3
|
||||
nic2: em1
|
||||
nic3: em2
|
||||
nodegroup-cisco-ucsb-b200-m4:
|
||||
dmiString: "system-product-name"
|
||||
id: "UCSB-B200-M4"
|
||||
nic1: enp7s0
|
||||
nic2: enp6s0
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
mapping:
|
||||
description:
|
||||
- Dictionary with os-net-config mapping data that can be written to the
|
||||
os-net-config mapping file.
|
||||
returned: when mapping match present in net_config_data_lookup
|
||||
type: dict
|
||||
'''
|
||||
|
||||
|
||||
def _get_interfaces():
|
||||
eth_addr = [
|
||||
# cast to lower case for MAC address match
|
||||
open('/sys/class/net/{}/address'.format(x)).read().strip().lower()
|
||||
for x in os.listdir('/sys/class/net/')]
|
||||
eth_addr = list(filter(None, eth_addr))
|
||||
|
||||
return eth_addr
|
||||
|
||||
|
||||
def _get_mappings(data):
|
||||
eth_addr = _get_interfaces()
|
||||
|
||||
for node in data:
|
||||
iface_mapping = copy.deepcopy(data[node])
|
||||
if 'dmiString' in iface_mapping:
|
||||
del iface_mapping['dmiString']
|
||||
if 'id' in iface_mapping:
|
||||
del iface_mapping['id']
|
||||
|
||||
# Match on mac addresses first - cast all to lower case
|
||||
lc_iface_mapping = copy.deepcopy(iface_mapping)
|
||||
for key, x in lc_iface_mapping.items():
|
||||
lc_iface_mapping[key] = x.lower()
|
||||
|
||||
if any(x in eth_addr for x in
|
||||
lc_iface_mapping.values()):
|
||||
return {'interface_mapping': lc_iface_mapping}
|
||||
|
||||
# If data contain dmiString and id keys, try to match node(group)
|
||||
if 'dmiString' in data[node] and 'id' in data[node]:
|
||||
ps = subprocess.Popen(
|
||||
['dmidecode', '--string', data[node]['dmiString']],
|
||||
stdout=subprocess.PIPE, universal_newlines=True)
|
||||
out, err = ps.communicate()
|
||||
|
||||
# See LP#1816652
|
||||
if data[node].get('id').lower() == out.rstrip().lower():
|
||||
return {'interface_mapping': lc_iface_mapping}
|
||||
|
||||
|
||||
def run(module):
|
||||
results = dict(
|
||||
changed=False,
|
||||
mapping=None,
|
||||
)
|
||||
|
||||
data = module.params['net_config_data_lookup']
|
||||
if isinstance(data, dict) and data:
|
||||
results['mapping'] = _get_mappings(data)
|
||||
|
||||
results['changed'] = True if results['mapping'] else False
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=yaml.safe_load(DOCUMENTATION)['options'],
|
||||
supports_check_mode=False,
|
||||
)
|
||||
run(module)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -27,3 +27,4 @@ tripleo_network_config_hide_sensitive_logs: true
|
|||
tripleo_network_config_interface_name: nic1
|
||||
tripleo_network_config_manage_service: true
|
||||
tripleo_network_config_network_deployment_actions: []
|
||||
tripleo_network_config_os_net_config_mappings: {}
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
tasks:
|
||||
- import_role:
|
||||
name: test_deps
|
||||
vars:
|
||||
test_deps_setup_tripleo: true
|
||||
- name: Ensure legacy scripts installed
|
||||
package:
|
||||
name: network-scripts
|
||||
|
|
|
@ -53,6 +53,26 @@
|
|||
mode: 0755
|
||||
when: not ansible_check_mode|bool
|
||||
|
||||
- name: Create /etc/os-net-config directory
|
||||
become: true
|
||||
file:
|
||||
path: /etc/os-net-config
|
||||
state: directory
|
||||
recurse: true
|
||||
|
||||
- name: Create os-net-config mappings from lookup data
|
||||
tripleo_os_net_config_mappings:
|
||||
net_config_data_lookup:
|
||||
"{{ tripleo_network_config_os_net_config_mappings }}"
|
||||
when: not ansible_check_mode|bool
|
||||
register: os_net_config_mappings_result
|
||||
|
||||
- name: Write os-net-config mappings file /etc/os-net-config/mapping.yaml
|
||||
copy:
|
||||
content: "{{ os_net_config_mappings_result.mapping | to_nice_yaml }}"
|
||||
dest: /etc/os-net-config/mapping.yaml
|
||||
when: os_net_config_mappings_result.changed|bool
|
||||
|
||||
- name: Run NetworkConfig script
|
||||
shell: /var/lib/tripleo-config/scripts/run_os_net_config.sh
|
||||
async: "{{ tripleo_network_config_async_timeout }}"
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
# 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.
|
||||
|
||||
from tripleo_ansible.ansible_plugins.modules import (
|
||||
tripleo_os_net_config_mappings)
|
||||
from tripleo_ansible.tests import base as tests_base
|
||||
|
||||
import mock
|
||||
|
||||
|
||||
@mock.patch('tripleo_ansible.ansible_plugins.modules.'
|
||||
'tripleo_os_net_config_mappings._get_interfaces', autospec=True)
|
||||
@mock.patch('subprocess.Popen', autospec=True)
|
||||
class TestTripleoOsNetConfigMappings(tests_base.TestCase):
|
||||
|
||||
def test_mac_mappings_match(self, mock_Popen, mock_get_ifaces):
|
||||
module = mock.MagicMock()
|
||||
module.params = {
|
||||
'net_config_data_lookup': {
|
||||
'node0': {'nic1': 'aa:bb:cc:dd:ee:ff',
|
||||
'nic2': 'ff:ee:dd:cc:bb:aa'},
|
||||
'node1': {'nic1': '0a:0b:0c:0d:0e:0f',
|
||||
'nic2': 'f0:e0:d0:c0:b0:a0'}
|
||||
}
|
||||
}
|
||||
mock_exit = mock.MagicMock()
|
||||
module.exit_json = mock_exit
|
||||
mock_get_ifaces.side_effect = ['aa:bb:cc:dd:ee:ff', 'ff:ee:dd:cc:bb:aa']
|
||||
expected = module.params['net_config_data_lookup']['node0']
|
||||
tripleo_os_net_config_mappings.run(module)
|
||||
mock_exit.assert_called_once_with(
|
||||
changed=True, mapping={'interface_mapping': expected})
|
||||
|
||||
def test_mac_mappings_no_match(self, mock_Popen, mock_get_ifaces):
|
||||
module = mock.MagicMock()
|
||||
module.params = {
|
||||
'net_config_data_lookup': {
|
||||
'node0': {'nic1': 'aa:bb:cc:dd:ee:ff',
|
||||
'nic2': 'ff:ee:dd:cc:bb:aa'},
|
||||
'node1': {'nic1': '0a:0b:0c:0d:0e:0f',
|
||||
'nic2': 'f0:e0:d0:c0:b0:a0'}
|
||||
}
|
||||
}
|
||||
mock_exit = mock.MagicMock()
|
||||
module.exit_json = mock_exit
|
||||
mock_get_ifaces.side_effect = ['01:02:03:04:05:06', '10:20:30:40:50:60']
|
||||
tripleo_os_net_config_mappings.run(module)
|
||||
mock_exit.assert_called_once_with(changed=False, mapping=None)
|
||||
|
||||
def test_dmi_type_string_match(self, mock_Popen, mock_get_ifaces):
|
||||
module = mock.MagicMock()
|
||||
module.params = {
|
||||
'net_config_data_lookup': {
|
||||
'node2': {'dmiString': 'foo-dmi-type',
|
||||
'id': 'bar-dmi-id',
|
||||
'nic1': 'em3',
|
||||
'nic2': 'em4'},
|
||||
'node3': {'nic1': '0a:0b:0c:0d:0e:0f',
|
||||
'nic2': 'f0:e0:d0:c0:b0:a0'}
|
||||
}
|
||||
}
|
||||
mock_exit = mock.MagicMock()
|
||||
module.exit_json = mock_exit
|
||||
expected = {'nic1': 'em3',
|
||||
'nic2': 'em4'}
|
||||
mock_return = mock.MagicMock()
|
||||
mock_return.return_value.communicate.return_value = ('bar-dmi-id', '')
|
||||
mock_Popen.side_effect = mock_return
|
||||
tripleo_os_net_config_mappings.run(module)
|
||||
mock_exit.assert_called_once_with(
|
||||
changed=True, mapping={'interface_mapping': expected})
|
Loading…
Reference in New Issue