Ansible interface for os-net-config
This patch implements an Ansible interface to os-net-config. * os_net_config module; which is in charge of running os-net-config tool. It supports a few options now (from os-net-config CLI): - cleanup, to cleanup unconfigured interfaces - config_file, to specify the os-net-config configuration file - debug, to enable debug level logging - detailed_exit_codes, to exit os-net-config with code 2 if files were modified. Note: the module will still return rc 0 if no error otherwise Ansible pukes. - safe_defaults, which will try to apply safe defaults if the provided configuration failed to execute. It'll basically run dhcp on all discovered interfaces. The module returns "rc" as the return code. The module has molecule testing, and we re-use the same script to generate safe defaults for os-net-config. * update tripleo_network_config role; in charge of TripleO Network configuration. - Adding tripleo_network_config_legacy_script parameter, true by default so the legacy shell script is run. - Adding tripleo_network_config_safe_defaults since some network configs don't need default safe defaults. - Updating main, to run the newly created os_net_config module if tripleo_network_config_legacy_script is False. - New playbook, os_net_config.yml, running os-net-config with the provided config. Change-Id: I1d0f1ab91c9cf290f70a554bfca9b508afc2c34a
This commit is contained in:
parent
325d080583
commit
a4d205f378
|
@ -0,0 +1,14 @@
|
|||
==============================
|
||||
Module - tripleo_os_net_config
|
||||
==============================
|
||||
|
||||
|
||||
This module provides for the following ansible plugin:
|
||||
|
||||
* tripleo_os_net_config
|
||||
|
||||
|
||||
.. ansibleautoplugin::
|
||||
:module: tripleo_ansible/ansible_plugins/modules/tripleo_os_net_config.py
|
||||
:documentation: true
|
||||
:examples: true
|
|
@ -25,3 +25,4 @@ jsonschema # MIT
|
|||
|
||||
# Upstream requirements from constraints.txt
|
||||
tripleo-common
|
||||
os-net-config # Apache-2.0
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
# 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.
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
import yaml
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.0',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: tripleo_os_net_config
|
||||
author:
|
||||
- OpenStack TripleO Contributors
|
||||
version_added: '1.0'
|
||||
short_description: Execute os-net-config tool.
|
||||
notes: []
|
||||
requirements:
|
||||
- os-net-config
|
||||
description:
|
||||
- Configure host network interfaces using a JSON config file format.
|
||||
options:
|
||||
cleanup:
|
||||
description:
|
||||
- Cleanup unconfigured interfaces.
|
||||
type: bool
|
||||
default: false
|
||||
config_file:
|
||||
description:
|
||||
- Path to the configuration file.
|
||||
type: str
|
||||
default: /etc/os-net-config/config.yaml
|
||||
debug:
|
||||
description:
|
||||
- Print debug output.
|
||||
type: bool
|
||||
default: false
|
||||
detailed_exit_codes:
|
||||
description:
|
||||
- If enabled an exit code of '2' means that files were modified.
|
||||
type: bool
|
||||
default: false
|
||||
safe_defaults:
|
||||
description:
|
||||
- If enabled, safe defaults (DHCP for all interfaces) will be applied in
|
||||
case of failuring while applying the provided net config.
|
||||
type: bool
|
||||
default: false
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Create network configs with defaults
|
||||
tripleo_os_net_config:
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
rc:
|
||||
description:
|
||||
- Integer for the return code
|
||||
returned: always
|
||||
type: int
|
||||
stdout:
|
||||
description:
|
||||
- The command standard output
|
||||
returned: always
|
||||
type: str
|
||||
stderr:
|
||||
description:
|
||||
- The command standard error
|
||||
returned: always
|
||||
type: str
|
||||
"""
|
||||
|
||||
DEFAULT_CFG = '/etc/os-net-config/dhcp_all_interfaces.yaml'
|
||||
|
||||
|
||||
def _run_os_net_config(config_file, cleanup=False, debug=False,
|
||||
detailed_exit_codes=False, noop=False):
|
||||
# Build os-net-config command
|
||||
argv = ['os-net-config --config-file {}'.format(config_file)]
|
||||
if cleanup:
|
||||
argv.append('--cleanup')
|
||||
if debug:
|
||||
argv.append('--debug')
|
||||
if detailed_exit_codes:
|
||||
argv.append('--detailed-exit-codes')
|
||||
if noop:
|
||||
argv.append('--noop')
|
||||
cmd = " ".join(argv)
|
||||
|
||||
# Apply the provided network configuration
|
||||
run = subprocess.run(cmd, shell=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
return cmd, run
|
||||
|
||||
|
||||
def _apply_safe_defaults(debug=False):
|
||||
_generate_default_cfg()
|
||||
cmd, run = _run_os_net_config(config_file=DEFAULT_CFG, cleanup=True,
|
||||
debug=debug, detailed_exit_codes=True)
|
||||
return cmd, run
|
||||
|
||||
|
||||
def _generate_default_cfg():
|
||||
with open(DEFAULT_CFG, "w") as config_file:
|
||||
config_file.write('# autogenerated safe defaults file which'
|
||||
'will run dhcp on discovered interfaces\n\n')
|
||||
network_interfaces = []
|
||||
for i in os.listdir('/sys/class/net/'):
|
||||
excluded_ints = ['lo', 'vnet']
|
||||
if i in excluded_ints:
|
||||
continue
|
||||
mac_addr_type = int(open('/sys/class/net/{}/'
|
||||
'addr_assign_type'.format(i)).read().strip())
|
||||
if mac_addr_type != 0:
|
||||
print('Device {} has generated MAC, skipping.'.format(i))
|
||||
continue
|
||||
try:
|
||||
open('/sys/class/net/{}/'
|
||||
'device/physfn'.format(i))
|
||||
print("Device ({}) is a SR-IOV VF, skipping.".format(i))
|
||||
continue
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
retries = 10
|
||||
has_link = _has_link(i)
|
||||
while has_link and retries > 0:
|
||||
cmd = 'ip link set dev {} up &>/dev/null'.format(i)
|
||||
subprocess.run(cmd, shell=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
has_link = _has_link(i)
|
||||
if has_link:
|
||||
break
|
||||
time.sleep(1)
|
||||
retries -= 1
|
||||
if has_link:
|
||||
network_interface = {
|
||||
'type': 'interface',
|
||||
'name': i,
|
||||
'use_dhcp': True
|
||||
}
|
||||
network_interfaces.append(network_interface)
|
||||
|
||||
network_config = {'network_config': network_interfaces}
|
||||
with open(DEFAULT_CFG, "ab") as config_file:
|
||||
config_file.write(json.dumps(network_config, indent=2).encode('utf-8'))
|
||||
|
||||
|
||||
def _has_link(interface):
|
||||
try:
|
||||
has_link = int(open('/sys/class/net/{}/'
|
||||
'carrier'.format(interface)).read().strip())
|
||||
except FileNotFoundError:
|
||||
has_link = 0
|
||||
if has_link == 1:
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=yaml.safe_load(DOCUMENTATION)['options'],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
results = dict(
|
||||
changed=False
|
||||
)
|
||||
# parse args
|
||||
args = module.params
|
||||
|
||||
# Set parameters
|
||||
cleanup = args['cleanup']
|
||||
config_file = args['config_file']
|
||||
debug = args['debug']
|
||||
detailed_exit_codes = args['detailed_exit_codes']
|
||||
safe_defaults = args['safe_defaults']
|
||||
return_codes = [0]
|
||||
if detailed_exit_codes:
|
||||
return_codes.append(2)
|
||||
|
||||
# Apply the provided network configuration
|
||||
cmd, run = _run_os_net_config(config_file, cleanup, debug,
|
||||
detailed_exit_codes,
|
||||
module.check_mode)
|
||||
results['stderr'] = run.stderr
|
||||
results['stdout'] = run.stdout
|
||||
if run.returncode not in return_codes and not module.check_mode:
|
||||
results['failed'] = True
|
||||
results['rc'] = run.returncode
|
||||
results['msg'] = ("Running %s failed with return code %s." % (
|
||||
cmd, run.returncode))
|
||||
if safe_defaults:
|
||||
module.warn("Error applying the provided network configuration, "
|
||||
"safe defaults will be applied in best effort.")
|
||||
# Best effort to restore safe networking defaults to allow
|
||||
# an operator to ssh the node and debug if needed.
|
||||
_apply_safe_defaults(debug)
|
||||
else:
|
||||
results['rc'] = 0
|
||||
results['msg'] = ("Successfully run %s." % cmd)
|
||||
if run.returncode == 2 and detailed_exit_codes:
|
||||
# NOTE: dprince this udev rule can apparently leak DHCP processes?
|
||||
# https://bugs.launchpad.net/tripleo/+bug/1538259
|
||||
# until we discover the root cause we can simply disable the
|
||||
# rule because networking has already been configured at this point
|
||||
udev_file = '/etc/udev/rules.d/99-dhcp-all-interfaces.rules'
|
||||
if os.path.isfile(udev_file):
|
||||
os.remove(udev_file)
|
||||
results['changed'] = True
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,68 @@
|
|||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
become: true
|
||||
tasks:
|
||||
- name: create os-net-config config file
|
||||
tempfile:
|
||||
state: file
|
||||
prefix: os-net-config
|
||||
register: onc_cfg
|
||||
# This is a simple test to reconfigure the loopback interface
|
||||
# because it's not easy to know what nics are available on the host
|
||||
# and not breaking the current network configs.
|
||||
- name: write os-net-config config file
|
||||
copy:
|
||||
content:
|
||||
network_config:
|
||||
-
|
||||
type: interface
|
||||
name: lo
|
||||
use_dhcp: false
|
||||
use_dhcpv6: false
|
||||
addresses:
|
||||
-
|
||||
ip_netmask: 127.0.0.1/8
|
||||
dest: "{{ onc_cfg.path }}"
|
||||
- name: Create safe os-net-config defaults for all interfaces
|
||||
become: true
|
||||
shell: |
|
||||
set -eux
|
||||
cat > {{ onc_cfg.path }} <<EOF_CAT
|
||||
network_config:
|
||||
EOF_CAT
|
||||
for iface in $(ls /sys/class/net | grep -v -e ^lo$ -e ^vnet$); do
|
||||
mac_addr_type="$(cat /sys/class/net/${iface}/addr_assign_type)"
|
||||
vf_parent="/sys/class/net/${iface}/device/physfn"
|
||||
if [ "$mac_addr_type" != "0" ]; then
|
||||
echo "Device has generated MAC, skipping."
|
||||
elif [[ -d $vf_parent ]]; then
|
||||
echo "Device (${iface}) is a SR-IOV VF, skipping."
|
||||
else
|
||||
HAS_LINK="$(cat /sys/class/net/${iface}/carrier || echo 0)"
|
||||
TRIES=10
|
||||
while [ "$HAS_LINK" == "0" -a $TRIES -gt 0 ]; do
|
||||
# Need to set the link up on each iteration
|
||||
ip link set dev $iface up &>/dev/null
|
||||
HAS_LINK="$(cat /sys/class/net/${iface}/carrier || echo 0)"
|
||||
if [ "$HAS_LINK" == "1" ]; then
|
||||
break
|
||||
else
|
||||
sleep 1
|
||||
fi
|
||||
TRIES=$(( TRIES - 1 ))
|
||||
done
|
||||
if [ "$HAS_LINK" == "1" ] ; then
|
||||
cat >> {{ onc_cfg.path }} <<EOF_CAT
|
||||
-
|
||||
type: interface
|
||||
name: $iface
|
||||
use_dhcp: true
|
||||
EOF_CAT
|
||||
fi
|
||||
fi
|
||||
done
|
||||
- name: apply network config
|
||||
tripleo_os_net_config:
|
||||
config_file: "{{ onc_cfg.path }}"
|
||||
debug: true
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
driver:
|
||||
name: delegated
|
||||
options:
|
||||
managed: false
|
||||
login_cmd_template: >-
|
||||
ssh
|
||||
-o UserKnownHostsFile=/dev/null
|
||||
-o StrictHostKeyChecking=no
|
||||
-o Compression=no
|
||||
-o TCPKeepAlive=yes
|
||||
-o VerifyHostKeyDNS=no
|
||||
-o ForwardX11=no
|
||||
-o ForwardAgent=no
|
||||
{instance}
|
||||
ansible_connection_options:
|
||||
ansible_connection: ssh
|
||||
|
||||
log: true
|
||||
|
||||
platforms:
|
||||
- name: instance
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
config_options:
|
||||
defaults:
|
||||
fact_caching: jsonfile
|
||||
fact_caching_connection: /tmp/molecule/facts
|
||||
inventory:
|
||||
hosts:
|
||||
all:
|
||||
hosts:
|
||||
instance:
|
||||
ansible_host: localhost
|
||||
log: true
|
||||
env:
|
||||
ANSIBLE_STDOUT_CALLBACK: yaml
|
||||
ANSIBLE_ROLES_PATH: "${ANSIBLE_ROLES_PATH:-/usr/share/ansible/roles}:${HOME}/zuul-jobs/roles"
|
||||
ANSIBLE_LIBRARY: "${ANSIBLE_LIBRARY:-/usr/share/ansible/plugins/modules}"
|
||||
ANSIBLE_FILTER_PLUGINS: "${ANSIBLE_FILTER_PLUGINS:-/usr/share/ansible/plugins/filter}"
|
||||
|
||||
scenario:
|
||||
name: tripleo_os_net_config
|
||||
test_sequence:
|
||||
- prepare
|
||||
- converge
|
||||
|
||||
verifier:
|
||||
name: testinfra
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
# 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.
|
||||
|
||||
- name: Prepare
|
||||
hosts: all
|
||||
roles:
|
||||
- role: test_deps
|
||||
test_deps_extra_packages:
|
||||
- os-net-config
|
||||
test_deps_setup_tripleo: true
|
|
@ -25,6 +25,8 @@ tripleo_network_config_bridge_name: br-ex
|
|||
tripleo_network_config_debug: "{{ (ansible_verbosity | int) >= 2 | bool }}"
|
||||
tripleo_network_config_hide_sensitive_logs: true
|
||||
tripleo_network_config_interface_name: nic1
|
||||
tripleo_network_config_legacy_script: true
|
||||
tripleo_network_config_manage_service: true
|
||||
tripleo_network_config_network_deployment_actions: []
|
||||
tripleo_network_config_os_net_config_mappings: {}
|
||||
tripleo_network_config_safe_defaults: true
|
||||
|
|
|
@ -38,21 +38,6 @@
|
|||
- name: NetworkConfig
|
||||
become: true
|
||||
block:
|
||||
- name: Create /var/lib/tripleo-config/scripts directory
|
||||
file:
|
||||
path: /var/lib/tripleo-config/scripts
|
||||
state: directory
|
||||
setype: container_file_t
|
||||
selevel: s0
|
||||
recurse: true
|
||||
|
||||
- name: Render NetworkConfig script
|
||||
template:
|
||||
dest: /var/lib/tripleo-config/scripts/run_os_net_config.sh
|
||||
src: "{{ tripleo_network_config_script_path }}"
|
||||
mode: 0755
|
||||
when: not ansible_check_mode|bool
|
||||
|
||||
- name: Create /etc/os-net-config directory
|
||||
become: true
|
||||
file:
|
||||
|
@ -73,17 +58,48 @@
|
|||
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 }}"
|
||||
poll: "{{ tripleo_network_config_async_poll }}"
|
||||
environment:
|
||||
bridge_name: "{{ tripleo_network_config_bridge_name }}"
|
||||
interface_name: "{{ tripleo_network_config_interface_name }}"
|
||||
register: NetworkConfig_result
|
||||
- name: Manage NetworkConfig with legacy run_os_net_config.sh
|
||||
block:
|
||||
- name: Create /var/lib/tripleo-config/scripts directory
|
||||
file:
|
||||
path: /var/lib/tripleo-config/scripts
|
||||
state: directory
|
||||
setype: container_file_t
|
||||
selevel: s0
|
||||
recurse: true
|
||||
|
||||
- name: Render NetworkConfig script
|
||||
template:
|
||||
dest: /var/lib/tripleo-config/scripts/run_os_net_config.sh
|
||||
src: "{{ tripleo_network_config_script_path }}"
|
||||
mode: 0755
|
||||
when: not ansible_check_mode|bool
|
||||
|
||||
- name: Run NetworkConfig script
|
||||
shell: /var/lib/tripleo-config/scripts/run_os_net_config.sh
|
||||
async: "{{ tripleo_network_config_async_timeout }}"
|
||||
poll: "{{ tripleo_network_config_async_poll }}"
|
||||
environment:
|
||||
bridge_name: "{{ tripleo_network_config_bridge_name }}"
|
||||
interface_name: "{{ tripleo_network_config_interface_name }}"
|
||||
register: NetworkConfig_result
|
||||
when:
|
||||
- not ansible_check_mode|bool
|
||||
failed_when: NetworkConfig_result.rc is not defined
|
||||
when:
|
||||
- not ansible_check_mode|bool
|
||||
failed_when: NetworkConfig_result.rc is not defined
|
||||
- tripleo_network_config_legacy_script|bool
|
||||
|
||||
- name: Manage NetworkConfig with tripleo_os_net_config module
|
||||
block:
|
||||
- name: Remove /var/lib/tripleo-config/scripts directory
|
||||
file:
|
||||
path: /var/lib/tripleo-config/scripts
|
||||
state: absent
|
||||
|
||||
- name: Run NetworkConfig with tripleo_os_net_config
|
||||
include_tasks: os_net_config.yml
|
||||
when:
|
||||
- not tripleo_network_config_legacy_script|bool
|
||||
|
||||
- name: Write rc of NetworkConfig script
|
||||
copy:
|
||||
|
@ -92,11 +108,15 @@
|
|||
when:
|
||||
- NetworkConfig_result.rc is defined
|
||||
|
||||
# This task can be removed once we stop supporting the legacy script.
|
||||
# tripleo_os_net_config module already prints out stdout/stderr and fails
|
||||
# when there is a failure, we don't need an extra task for it.
|
||||
- name: NetworkConfig stdout
|
||||
debug:
|
||||
var: NetworkConfig_result.stderr_lines
|
||||
failed_when: NetworkConfig_result.rc != 0
|
||||
when:
|
||||
- tripleo_network_config_legacy_script|bool
|
||||
- NetworkConfig_result.rc is defined
|
||||
|
||||
# os-net-config currently relies on the legacy network
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
# 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.
|
||||
#
|
||||
# Apply network configuration with os-net-config.
|
||||
#
|
||||
|
||||
- name: Apply os-net-config configuration
|
||||
become: true
|
||||
block:
|
||||
- name: Render network_config
|
||||
no_log: "{{ tripleo_network_config_hide_sensitive_logs | bool }}"
|
||||
template:
|
||||
src: "{{ tripleo_network_config_script_path }}"
|
||||
dest: /etc/os-net-config/config.json
|
||||
mode: 0600
|
||||
backup: true
|
||||
- name: Run tripleo_os_net_config_module with network_config
|
||||
tripleo_os_net_config:
|
||||
config_file: /etc/os-net-config/config.json
|
||||
debug: "{{ tripleo_network_config_debug|bool }}"
|
||||
detailed_exit_codes: true
|
||||
safe_defaults: "{{ tripleo_network_config_safe_defaults | bool }}"
|
||||
async: "{{ tripleo_network_config_async_timeout }}"
|
||||
poll: "{{ tripleo_network_config_async_poll }}"
|
||||
register: NetworkConfig_result
|
|
@ -360,6 +360,7 @@
|
|||
- job:
|
||||
files:
|
||||
- ^tripleo_ansible/roles/tripleo_network_config/.*
|
||||
- ^tripleo_ansible/ansible_plugins/modules/tripleo_os_net_config.py
|
||||
name: tripleo-ansible-centos-8-molecule-tripleo_network_config
|
||||
parent: tripleo-ansible-centos-8-base
|
||||
vars:
|
||||
|
|
Loading…
Reference in New Issue