From ced8a4a5284d9c33a8ed4568a9f0b0df4fe8421c Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Fri, 6 Mar 2020 17:44:41 +0000 Subject: [PATCH] Support linux bridge in addition to OVS While OVS was used initially due to availability of a networking-generic-switch driver for it, Linux bridge can be useful for environments where OVS is not available. This is configured via bridge_type variable. It defaults to 'openvswitch', but may be set to 'linuxbridge'. Change-Id: I750a73cebc743edcbcd8c23c67e4920d0058bead --- ansible/filter_plugins/tenks.py | 18 ++--- ansible/group_vars/hypervisors | 13 ++-- ansible/hypervisor_setup.yml | 26 +++---- ansible/node_networking.yml | 5 +- ansible/physical_network.yml | 71 ++++++++++++++++--- ansible/roles/ironic-enrolment/tasks/port.yml | 2 +- ansible/roles/veth-pair/README.md | 14 ++-- ansible/roles/veth-pair/tasks/absent.yml | 2 +- ansible/roles/veth-pair/tasks/present.yml | 24 +++++-- doc/source/configuration.rst | 5 ++ playbooks/tenks-deploy-teardown/run.yml | 1 + .../templates/tenks-overrides.yml.j2 | 2 + zuul.d/base.yaml | 12 ++++ zuul.d/jobs.yaml | 30 ++++++-- zuul.d/project.yaml | 18 +++-- 15 files changed, 183 insertions(+), 60 deletions(-) diff --git a/ansible/filter_plugins/tenks.py b/ansible/filter_plugins/tenks.py index c7fb921..4d0c620 100644 --- a/ansible/filter_plugins/tenks.py +++ b/ansible/filter_plugins/tenks.py @@ -35,11 +35,11 @@ class FilterModule(object): return { # Network name filters. 'bridge_name': bridge_name, - 'ovs_link_name': ovs_link_name, + 'peer_link_name': peer_link_name, 'physnet_index_to_name': physnet_index_to_name, 'physnet_name_to_index': physnet_name_to_index, 'source_link_name': source_link_name, - 'source_to_ovs_link_name': source_to_ovs_link_name, + 'source_to_peer_link_name': source_to_peer_link_name, 'source_link_to_physnet_name': source_link_to_physnet_name, # Libvirt filters. @@ -104,7 +104,7 @@ def set_libvirt_start_params(node): @contextfilter def bridge_name(context, physnet, inventory_hostname=None): - """Get the Tenks OVS bridge name from a physical network name. + """Get the Tenks bridge name from a physical network name. """ return (_get_hostvar(context, 'bridge_prefix', inventory_hostname=inventory_hostname) + @@ -123,22 +123,22 @@ def source_link_name(context, node, physnet, inventory_hostname=None): @contextfilter -def ovs_link_name(context, node, physnet, inventory_hostname=None): - """Get the OVS veth link name for a node/physnet combination. +def peer_link_name(context, node, physnet, inventory_hostname=None): + """Get the peer veth link name for a node/physnet combination. """ return (_link_name(context, node, physnet, inventory_hostname=inventory_hostname) + - _get_hostvar(context, 'veth_node_ovs_suffix', + _get_hostvar(context, 'veth_node_peer_suffix', inventory_hostname=inventory_hostname)) @contextfilter -def source_to_ovs_link_name(context, source, inventory_hostname=None): - """Get the corresponding OVS link name for a source link name. +def source_to_peer_link_name(context, source, inventory_hostname=None): + """Get the corresponding peer link name for a source link name. """ base = source[:-len(_get_hostvar(context, 'veth_node_source_suffix', inventory_hostname=inventory_hostname))] - return base + _get_hostvar(context, 'veth_node_ovs_suffix', + return base + _get_hostvar(context, 'veth_node_peer_suffix', inventory_hostname=inventory_hostname) diff --git a/ansible/group_vars/hypervisors b/ansible/group_vars/hypervisors index 8d19cde..61ec7a1 100644 --- a/ansible/group_vars/hypervisors +++ b/ansible/group_vars/hypervisors @@ -6,6 +6,11 @@ physnet_mappings: {} system_requirements: - "python{% if ansible_python.version.major == 3 %}3{% endif %}-virtualenv" +# Tenks bridge type. Options are "openvswitch", "linuxbridge". Default is +# "openvswitch". Note that this relates to bridges created by Tenks, not the +# source devices in physnet_mappings. +bridge_type: "openvswitch" + # Naming scheme for bridges created by tenks for physical networks is # {{ bridge_prefix + i }}, where `i` is the index of the physical network in # physnet_mappings (sorted alphabetically by key). @@ -14,13 +19,13 @@ bridge_prefix: brtenks # Prefix for all veth links. veth_prefix: 'p-' -# Suffix for veth links attached to a Tenks OVS bridge. -veth_bridge_ovs_suffix: '-ovs' +# Suffix for veth links attached to a Tenks bridge. +veth_bridge_peer_suffix: '-{% if bridge_type == "openvswitch" %}ovs{% else %}br{% endif %}' # Suffix for veth links attached to a source Linux bridge. veth_bridge_source_suffix: '-phy' -# Suffix for veth links attached to a Tenks OVS bridge. -veth_node_ovs_suffix: '-ovs' +# Suffix for veth links attached to a Tenks bridge. +veth_node_peer_suffix: '-{% if bridge_type == "openvswitch" %}ovs{% else %}br{% endif %}' # Suffix for veth links attached to a node. Nodes aren't physical so '-phy' # doesn't seem right. veth_node_source_suffix: '-tap' diff --git a/ansible/hypervisor_setup.yml b/ansible/hypervisor_setup.yml index 82d6e7f..0f3f558 100644 --- a/ansible/hypervisor_setup.yml +++ b/ansible/hypervisor_setup.yml @@ -28,19 +28,21 @@ # Don't remove log directory during teardown to preserve historical logs. when: cmd != 'teardown' -- name: Check if ovs-vsctl command is present - command: ovs-vsctl --version - register: ovs_vsctl_check - failed_when: false - changed_when: false +- block: + - name: Check if ovs-vsctl command is present + command: ovs-vsctl --version + register: ovs_vsctl_check + failed_when: false + changed_when: false -- name: Fail when Open vSwitch is not installed - fail: - msg: >- - Tenks requires openvswitch to be installed and running. Please install - openvswitch. If it is installed, please report this as a bug. - # Assume a non-zero return code means that openvswitch is not installed. - when: ovs_vsctl_check.rc != 0 + - name: Fail when Open vSwitch is not installed + fail: + msg: >- + Tenks requires openvswitch to be installed and running. Please install + openvswitch. If it is installed, please report this as a bug. + # Assume a non-zero return code means that openvswitch is not installed. + when: ovs_vsctl_check.rc != 0 + when: bridge_type == "openvswitch" - name: Configure physical networks include_tasks: physical_network.yml diff --git a/ansible/node_networking.yml b/ansible/node_networking.yml index c3b237d..ecc2045 100644 --- a/ansible/node_networking.yml +++ b/ansible/node_networking.yml @@ -20,8 +20,9 @@ include_role: name: veth-pair vars: - veth_pair_ovs_bridge: "{{ physnet.1 | bridge_name }}" - veth_pair_ovs_link_name: "{{ physnet.0 | ovs_link_name(physnet.1) }}" + veth_pair_peer_bridge_type: "{{ bridge_type }}" + veth_pair_peer_bridge: "{{ physnet.1 | bridge_name }}" + veth_pair_peer_link_name: "{{ physnet.0 | peer_link_name(physnet.1) }}" veth_pair_source_link_name: "{{ physnet.0 | source_link_name(physnet.1) }}" veth_pair_state: "{{ physnet.0.get('state', 'present') }}" # Loop over each physical network for each node allocated to this host. diff --git a/ansible/physical_network.yml b/ansible/physical_network.yml index dbc97cf..c3ad261 100644 --- a/ansible/physical_network.yml +++ b/ansible/physical_network.yml @@ -32,7 +32,7 @@ - name: Register source interface as a Linux bridge set_fact: - source_type: linux_bridge + source_type: linuxbridge when: if_details.stdout_lines[-1].split()[0] == 'bridge' - block: @@ -44,7 +44,7 @@ - name: Register source interface as an Open vSwitch bridge set_fact: - source_type: ovs_bridge + source_type: openvswitch when: source_interface in ovs_bridges.stdout_lines when: if_details.stdout_lines[-1].split()[0] == 'openvswitch' @@ -56,15 +56,44 @@ bridge: "{{ tenks_bridge }}" state: "{{ state }}" become: true + when: bridge_type == "openvswitch" + +- block: + - name: Check if Tenks bridge exists + stat: + path: /sys/class/net/{{ tenks_bridge }} + register: stat_result + + - name: Ensure Tenks bridge exists + vars: + link_operation: "{{ 'add' if state == 'present' else 'del' }}" + command: >- + {{ tenks_ip_path }} link {{ link_operation }} {{ tenks_bridge }} type bridge + changed_when: true + become: true + when: >- + state == 'present' and not stat_result.stat.exists or + state == 'absent' and stat_result.stat.exists + + - name: Ensure Tenks bridge is up + command: >- + {{ tenks_ip_path }} link set {{ tenks_bridge }} up + changed_when: true + become: true + when: + - state == 'present' + - not stat_result.stat.exists + when: bridge_type == "linuxbridge" - name: Configure existing Linux bridge - when: source_type == 'linux_bridge' + when: source_type == 'linuxbridge' include_role: name: veth-pair vars: - veth_pair_ovs_bridge: "{{ tenks_bridge }}" - veth_pair_ovs_link_name: >- - {{ veth_prefix + tenks_bridge + veth_bridge_ovs_suffix }} + veth_pair_peer_bridge_type: "{{ bridge_type }}" + veth_pair_peer_bridge: "{{ tenks_bridge }}" + veth_pair_peer_link_name: >- + {{ veth_prefix + tenks_bridge + veth_bridge_peer_suffix }} veth_pair_source_bridge: "{{ source_interface }}" veth_pair_source_link_name: >- {{ veth_prefix + tenks_bridge + veth_bridge_source_suffix }} @@ -72,16 +101,16 @@ veth_pair_state: "{{ state }}" - name: Configure existing Open vSwitch bridge - when: source_type == 'ovs_bridge' + when: source_type == 'openvswitch' block: - name: Configure patch port on Tenks bridge openvswitch_port: bridge: "{{ tenks_bridge }}" - port: "{{ veth_prefix + tenks_bridge + veth_bridge_ovs_suffix }}" + port: "{{ veth_prefix + tenks_bridge + veth_bridge_peer_suffix }}" # Despite the module documentation, `set` will happily take multiple # properties. set: >- - Interface {{ veth_prefix + tenks_bridge + veth_bridge_ovs_suffix }} + Interface {{ veth_prefix + tenks_bridge + veth_bridge_peer_suffix }} type=patch options:peer={{ veth_prefix + tenks_bridge + veth_bridge_source_suffix }} @@ -98,7 +127,7 @@ Interface {{ veth_prefix + tenks_bridge + veth_bridge_source_suffix }} type=patch options:peer={{ veth_prefix + tenks_bridge + - veth_bridge_ovs_suffix }} + veth_bridge_peer_suffix }} state: "{{ state }}" become: true @@ -108,6 +137,28 @@ port: "{{ source_interface }}" state: "{{ state }}" when: + - bridge_type == 'openvswitch' + - source_type == 'direct' + # If 'absent', we've already deleted the bridge earlier, so no need to + # unplug the interface. + - state != 'absent' + become: true + +- block: + - name: Speculatively check interface's master + command: >- + realpath /sys/class/net/{{ source_interface }}/master + register: master_result + failed_when: false + changed_when: false + + - name: Plug source interface into Tenks bridge + command: >- + {{ tenks_ip_path }} link set dev {{ source_interface }} master {{ tenks_bridge }} + changed_when: true + when: master_result.rc != 0 + when: + - bridge_type == 'linuxbridge' - source_type == 'direct' # If 'absent', we've already deleted the bridge earlier, so no need to # unplug the interface. diff --git a/ansible/roles/ironic-enrolment/tasks/port.yml b/ansible/roles/ironic-enrolment/tasks/port.yml index 1a59818..8377a48 100644 --- a/ansible/roles/ironic-enrolment/tasks/port.yml +++ b/ansible/roles/ironic-enrolment/tasks/port.yml @@ -36,7 +36,7 @@ switch_info: "{{ bridge }}" port_id: >- {{ source_interface - | source_to_ovs_link_name(inventory_hostname=ironic_hypervisor) }} + | source_to_peer_link_name(inventory_hostname=ironic_hypervisor) }} command: >- '{{ ironic_virtualenv_path }}/bin/openstack' baremetal port set {{ uuid.stdout }} diff --git a/ansible/roles/veth-pair/README.md b/ansible/roles/veth-pair/README.md index 8bb03d6..5c2d1d5 100644 --- a/ansible/roles/veth-pair/README.md +++ b/ansible/roles/veth-pair/README.md @@ -4,7 +4,7 @@ Veth Pair This role manages a veth pair. Actions: * If `veth_pair_state` is `present`, it will create the veth pair and - plug one end into the specified OVS bridge. If `veth_pair_plug_into_source` + plug one end into the specified peer bridge. If `veth_pair_plug_into_source` is enabled, it will also plug the other end into/from a source Linux bridge. @@ -15,14 +15,18 @@ This role manages a veth pair. Actions: Requirements ------------ -The host should have the `ip` and `ovs-vsctl` commands accessible. +The host should have the `ip` command available. If +`veth_pair_peer_bridge_type` is `openvswitch`, or `veth_pair_source_link_name` +is an OVS bridge, the `ovs-vsctl` command should also be accessible. Role Variables -------------- -- `veth_pair_ovs_link_name`: The name to give the veth link that plugs into the - OVS bridge. -- `veth_pair_ovs_bridge`: The name of the OVS bridge to plug into. +- `veth_pair_peer_bridge_type`: The type of the peer bridge. One of + `openvswitch`, or `linuxbridge`. +- `veth_pair_peer_link_name`: The name to give the veth link that plugs into + the peer bridge. +- `veth_pair_peer_bridge`: The name of the peer bridge to plug into. - `veth_pair_source_link_name`: The name to give the veth link that plugs into the source device. - `veth_pair_source_bridge`: The name of the source Linux bridge to plug into. Must be diff --git a/ansible/roles/veth-pair/tasks/absent.yml b/ansible/roles/veth-pair/tasks/absent.yml index f61cb0a..3d679fa 100644 --- a/ansible/roles/veth-pair/tasks/absent.yml +++ b/ansible/roles/veth-pair/tasks/absent.yml @@ -15,7 +15,7 @@ - name: Delete veth pair command: >- - ip link del dev {{ veth_pair_ovs_link_name }} + ip link del dev {{ veth_pair_peer_link_name }} type veth peer name {{ veth_pair_source_link_name }} register: res diff --git a/ansible/roles/veth-pair/tasks/present.yml b/ansible/roles/veth-pair/tasks/present.yml index 65a51e4..57b9a2d 100644 --- a/ansible/roles/veth-pair/tasks/present.yml +++ b/ansible/roles/veth-pair/tasks/present.yml @@ -1,7 +1,7 @@ --- - name: Create veth pair command: >- - ip link add dev {{ veth_pair_ovs_link_name }} + ip link add dev {{ veth_pair_peer_link_name }} type veth peer name {{ veth_pair_source_link_name }} register: res @@ -13,7 +13,7 @@ - name: Bring each end of veth up command: ip link set {{ item }} up loop: - - "{{ veth_pair_ovs_link_name }}" + - "{{ veth_pair_peer_link_name }}" - "{{ veth_pair_source_link_name }}" become: true # if the interface is already up, this ultimately ends up being a noop, see: @@ -22,10 +22,26 @@ - name: Plug veth into OVS bridge openvswitch_port: - bridge: "{{ veth_pair_ovs_bridge }}" - port: "{{ veth_pair_ovs_link_name }}" + bridge: "{{ veth_pair_peer_bridge }}" + port: "{{ veth_pair_peer_link_name }}" + when: veth_pair_peer_bridge_type == "openvswitch" become: true +- block: + - include_tasks: is-attached.yml + vars: + bridge: "{{ veth_pair_peer_bridge }}" + interface: "{{ veth_pair_peer_link_name }}" + + - name: Plug veth into source bridge + command: >- + ip link set {{ veth_pair_peer_link_name }} master + {{ veth_pair_peer_bridge }} + when: + - not veth_pair_is_attached + become: true + when: veth_pair_peer_bridge_type == "linuxbridge" + - block: - include_tasks: is-attached.yml vars: diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst index a9675b0..4bfe1e7 100644 --- a/doc/source/configuration.rst +++ b/doc/source/configuration.rst @@ -49,6 +49,11 @@ are required for different hosts, you will need to individually specify them in an inventory host_vars file: for a host with hostname *myhost*, set ``physnet_mappings`` within the file ``ansible/inventory/host_vars/myhost``. +Another variable that may be useful is ``bridge_type``. This may be either +``openvswitch`` (default) or ``linuxbridge``, and defines the type of bridges +created by Tenks. This may be different from the type of interfaces or bridges +in ``physnet_mappings``. + Standalone Ironic ----------------- diff --git a/playbooks/tenks-deploy-teardown/run.yml b/playbooks/tenks-deploy-teardown/run.yml index e8d6b27..e48f16b 100644 --- a/playbooks/tenks-deploy-teardown/run.yml +++ b/playbooks/tenks-deploy-teardown/run.yml @@ -16,6 +16,7 @@ chdir: "{{ tenks_src_dir }}" environment: ANSIBLE_ROLES_PATH: "{{ tenks_src_dir }}/ansible/roles" + when: bridge_type == 'openvswitch' - name: Deploy tenks cluster shell: diff --git a/playbooks/tenks-deploy-teardown/templates/tenks-overrides.yml.j2 b/playbooks/tenks-deploy-teardown/templates/tenks-overrides.yml.j2 index 7e06d3c..d62f5f9 100644 --- a/playbooks/tenks-deploy-teardown/templates/tenks-overrides.yml.j2 +++ b/playbooks/tenks-deploy-teardown/templates/tenks-overrides.yml.j2 @@ -26,4 +26,6 @@ nova_flavors: [] physnet_mappings: physnet1: breth1 +bridge_type: {{ bridge_type }} + python_upper_constraints_url: file://{{ upper_constraints_path }} diff --git a/zuul.d/base.yaml b/zuul.d/base.yaml index 2d5f2d3..e4f8031 100644 --- a/zuul.d/base.yaml +++ b/zuul.d/base.yaml @@ -21,3 +21,15 @@ - ^releasenotes/.* - ^setup.cfg$ - ^tox.ini$ + +- job: + name: tenks-deploy-teardown-ovs-base + parent: tenks-deploy-teardown-base + vars: + bridge_type: openvswitch + +- job: + name: tenks-deploy-teardown-linuxbridge-base + parent: tenks-deploy-teardown-base + vars: + bridge_type: linuxbridge diff --git a/zuul.d/jobs.yaml b/zuul.d/jobs.yaml index 61a0dfa..9d34a18 100644 --- a/zuul.d/jobs.yaml +++ b/zuul.d/jobs.yaml @@ -8,20 +8,38 @@ tox_envlist: alint - job: - name: tenks-deploy-teardown-centos7 - parent: tenks-deploy-teardown-base + name: tenks-deploy-teardown-ovs-centos7 + parent: tenks-deploy-teardown-ovs-base nodeset: centos-7 required-projects: - name: openstack/requirements override-checkout: stable/train - job: - name: tenks-deploy-teardown-centos8 - parent: tenks-deploy-teardown-base + name: tenks-deploy-teardown-ovs-centos8 + parent: tenks-deploy-teardown-ovs-base nodeset: centos-8 - job: - name: tenks-deploy-teardown-ubuntu - parent: tenks-deploy-teardown-base + name: tenks-deploy-teardown-ovs-ubuntu + parent: tenks-deploy-teardown-ovs-base + nodeset: ubuntu-bionic + +- job: + name: tenks-deploy-teardown-linuxbridge-centos7 + parent: tenks-deploy-teardown-linuxbridge-base + nodeset: centos-7 + required-projects: + - name: openstack/requirements + override-checkout: stable/train + +- job: + name: tenks-deploy-teardown-linuxbridge-centos8 + parent: tenks-deploy-teardown-linuxbridge-base + nodeset: centos-8 + +- job: + name: tenks-deploy-teardown-linuxbridge-ubuntu + parent: tenks-deploy-teardown-linuxbridge-base nodeset: ubuntu-bionic diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml index a25957f..662db80 100644 --- a/zuul.d/project.yaml +++ b/zuul.d/project.yaml @@ -9,9 +9,12 @@ check: jobs: - tenks-tox-ansible-lint - - tenks-deploy-teardown-centos7 - - tenks-deploy-teardown-centos8 - - tenks-deploy-teardown-ubuntu + - tenks-deploy-teardown-ovs-centos7 + - tenks-deploy-teardown-ovs-centos8 + - tenks-deploy-teardown-ovs-ubuntu + - tenks-deploy-teardown-linuxbridge-centos7 + - tenks-deploy-teardown-linuxbridge-centos8 + - tenks-deploy-teardown-linuxbridge-ubuntu # Until we have ironic jobs using tenks, gate on the kayobe overcloud # deploy job, which uses tenks to test bare metal compute provisioning. - kayobe-overcloud-centos8 @@ -19,9 +22,12 @@ queue: tenks jobs: - tenks-tox-ansible-lint - - tenks-deploy-teardown-centos7 - - tenks-deploy-teardown-centos8 - - tenks-deploy-teardown-ubuntu + - tenks-deploy-teardown-ovs-centos7 + - tenks-deploy-teardown-ovs-centos8 + - tenks-deploy-teardown-ovs-ubuntu + - tenks-deploy-teardown-linuxbridge-centos7 + - tenks-deploy-teardown-linuxbridge-centos8 + - tenks-deploy-teardown-linuxbridge-ubuntu # Until we have ironic jobs using tenks, gate on the kayobe overcloud # deploy job, which uses tenks to test bare metal compute provisioning. - kayobe-overcloud-centos8