From 26be2c5af3e60ecf34a1464c5d208ffa00df4bc0 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Fri, 24 Mar 2017 15:30:20 +0000 Subject: [PATCH] Support Neutron VLAN networks For VLAN networks we need to use a tagged Linux bridge on the controller with a veth pair to link it to the corresponding Neutron OVS bridge. This must be done for the physical network(s) carrying the provisioning, tenant, and external network traffic. We also need untagged VLAN subinterfaces on the tagged bridge for the controller to communicate on these networks with. The network interface role in use requires that we use two passes through interface configuration to ensure that VLAN subinterfaces can be added to bridge interfaces. Finally, we must configure Ironic with the name of the Neutron network used for provisioning. --- ansible/filter_plugins/networks.py | 18 ++++ ansible/group_vars/all/ironic | 16 ++++ ansible/ip-allocation.yml | 1 + ansible/kolla-ansible.yml | 33 ++++--- ansible/network.yml | 96 +++++++++++-------- ansible/provision-net.yml | 8 +- .../roles/kolla-openstack/defaults/main.yml | 8 +- .../kolla-openstack/templates/ironic.conf.j2 | 6 +- etc/kayobe/ironic.yml | 16 ++++ 9 files changed, 146 insertions(+), 56 deletions(-) create mode 100644 ansible/group_vars/all/ironic create mode 100644 etc/kayobe/ironic.yml diff --git a/ansible/filter_plugins/networks.py b/ansible/filter_plugins/networks.py index a6c19045f..b0e91f8ae 100644 --- a/ansible/filter_plugins/networks.py +++ b/ansible/filter_plugins/networks.py @@ -157,6 +157,11 @@ def net_is_bridge(context, name, inventory_hostname=None): return net_bridge_ports(context, name) is not None +@jinja2.contextfilter +def net_is_vlan(context, name, inventory_hostname=None): + return net_vlan(context, name) is not None + + @jinja2.contextfilter def net_select_ethers(context, names): return [name for name in names if net_is_ether(context, name)] @@ -167,6 +172,16 @@ def net_select_bridges(context, names): return [name for name in names if net_is_bridge(context, name)] +@jinja2.contextfilter +def net_select_vlans(context, names): + return [name for name in names if net_is_vlan(context, name)] + + +@jinja2.contextfilter +def net_reject_vlans(context, names): + return [name for name in names if not net_is_vlan(context, name)] + + @jinja2.contextfilter def net_configdrive_network_device(context, name, inventory_hostname=None): device = net_interface(context, name, inventory_hostname) @@ -212,7 +227,10 @@ class FilterModule(object): 'net_bridge_obj': net_bridge_obj, 'net_is_ether': net_is_ether, 'net_is_bridge': net_is_bridge, + 'net_is_vlan': net_is_vlan, 'net_select_ethers': net_select_ethers, 'net_select_bridges': net_select_bridges, + 'net_select_vlans': net_select_vlans, + 'net_reject_vlans': net_reject_vlans, 'net_configdrive_network_device': net_configdrive_network_device, } diff --git a/ansible/group_vars/all/ironic b/ansible/group_vars/all/ironic new file mode 100644 index 000000000..ac7f8b788 --- /dev/null +++ b/ansible/group_vars/all/ironic @@ -0,0 +1,16 @@ +--- +############################################################################### +# Ironic configuration. + +# List of enabled Ironic drivers. +kolla_ironic_drivers: + - agent_ssh + - agent_ipmitool + - pxe_ssh + - pxe_ipmitool + +# Name of the Neutron network to use for cleaning. +kolla_ironic_cleaning_network: 'provision-net' + +# Name of the Neutron network to use for provisioning. +kolla_ironic_provisioning_network: 'provision-net' diff --git a/ansible/ip-allocation.yml b/ansible/ip-allocation.yml index 167c59b34..c3f582385 100644 --- a/ansible/ip-allocation.yml +++ b/ansible/ip-allocation.yml @@ -20,6 +20,7 @@ }] }} with_items: "{{ network_interfaces }}" + when: "{{ item|net_cidr != None }}" roles: - role: ip-allocation ip_allocation_filename: "{{ kayobe_config_path }}/network-allocation.yml" diff --git a/ansible/kolla-ansible.yml b/ansible/kolla-ansible.yml index b8457efcd..d77de0ddb 100644 --- a/ansible/kolla-ansible.yml +++ b/ansible/kolla-ansible.yml @@ -52,24 +52,35 @@ kolla_cluster_interface: "{{ storage_mgmt_net_name | net_interface(controller_host) | replace('-', '_') }}" kolla_provision_interface: "{{ provision_wl_net_name | net_interface(controller_host) | replace('-', '_') }}" kolla_inspector_dnsmasq_interface: "{{ provision_wl_net_name | net_interface(controller_host) | replace('-', '_') }}" + # Initialise the following lists. + kolla_neutron_bridge_names: [] + kolla_neutron_external_interfaces: [] + kolla_neutron_bridge_interfaces: [] - - name: Set facts containing the Neutron bridge and interface names for the provisioning network + # When these networks are VLANs, we need to use the underlying tagged + # bridge interface rather than the untagged interface. We therefore + # strip the . suffix of the interface name. We use a union here + # as a single tagged interface may be shared between these networks. + - name: Set a fact containing the bridges to be patched to the Neutron OVS bridges set_fact: - kolla_neutron_bridge_names: - - "{{ provision_wl_net_name | net_interface(controller_host) ~ network_bridge_suffix_ovs }}" - kolla_neutron_external_interfaces: - - "{{ network_patch_prefix ~ provision_wl_net_name | net_interface(controller_host) ~ network_patch_suffix_ovs }}" + kolla_neutron_bridge_interfaces: > + {{ kolla_neutron_bridge_interfaces | + union([item | net_interface(controller_host) | replace('.' ~ item | net_vlan(controller_host) | default('!nomatch!'), '')]) | + list }} + with_items: + - "{{ provision_wl_net_name }}" + - "{{ external_net_name }}" + when: "{{ item in hostvars[controller_host].network_interfaces }}" - - name: Update facts containing the Neutron bridge and interface names for the external network + - name: Set facts containing the Neutron bridge and interface names set_fact: kolla_neutron_bridge_names: > {{ kolla_neutron_bridge_names + - [external_net_name | net_interface(controller_host) ~ network_bridge_suffix_ovs] }} + [item ~ network_bridge_suffix_ovs] }} kolla_neutron_external_interfaces: > - {{ kolla_neutron_external_interfaces + - [network_patch_prefix ~ external_net_name | net_interface(controller_host) ~ network_patch_suffix_ovs] }} - when: - - "{{ provision_wl_net_name != external_net_name }}" + {{ kolla_neutron_bridge_names + + [network_patch_prefix ~ item ~ network_patch_suffix_ovs] }} + with_items: "{{ kolla_neutron_bridge_interfaces }}" - name: Validate controller Kolla Ansible network configuration fail: diff --git a/ansible/network.yml b/ansible/network.yml index 26e124ed2..41a01566d 100644 --- a/ansible/network.yml +++ b/ansible/network.yml @@ -1,6 +1,11 @@ --- - name: Ensure networking is configured hosts: seed:controllers + tags: + - config + vars: + ether_interfaces: "{{ network_interfaces | net_select_ethers | list }}" + bridge_interfaces: "{{ network_interfaces | net_select_bridges | list }}" pre_tasks: - block: - name: Validate network interface configuration @@ -8,7 +13,7 @@ msg: > Network interface validation failed - no interface configured for {{ item }}. This should be configured via '{{ item }}_interface'. - with_items: "{{ network_interfaces | net_select_ethers | list }}" + with_items: "{{ ether_interfaces }}" when: "{{ not item | net_interface }}" - name: Validate bridge interface configuration @@ -16,10 +21,9 @@ msg: > Bridge interface validation failed - no interface configured for {{ item }}. This should be configured via '{{ item }}_interface'. - with_items: "{{ network_interfaces | net_select_bridges | list }}" + with_items: "{{ bridge_interfaces }}" when: "{{ not item | net_interface }}" tags: - - config - config-validation - name: Ensure NetworkManager is disabled @@ -37,53 +41,67 @@ roles: - role: ahuffman.resolv become: True - tags: - - config + + # On the first pass we configure all ethernet interfaces that are not on + # VLANs and all bridges. On the second pass we configure all ethernet + # interfaces that are on VLANs. This allows us to specify VLAN interfaces + # on bridges. - role: MichaelRigart.interfaces interfaces_ether_interfaces: > - {{ network_interfaces | - net_select_ethers | + {{ ether_interfaces | + net_reject_vlans | map('net_interface_obj') | list }} interfaces_bridge_interfaces: > - {{ network_interfaces | - net_select_bridges | + {{ bridge_interfaces | map('net_bridge_obj') | list }} become: True - tags: - - config + - role: MichaelRigart.interfaces + interfaces_ether_interfaces: > + {{ ether_interfaces | + net_select_vlans | + map('net_interface_obj') | + list }} + become: True + +# Configure a virtual ethernet patch links to connect the workload provision +# and external network bridges to the Neutron OVS bridge. - name: Ensure controller workload OVS patch links exist hosts: controllers - roles: - # Configure a virtual ethernet patch link to connect the workload provision - # network bridge to the Neutron OVS bridge. - - role: veth - veth_interfaces: - - device: "{{ network_patch_prefix ~ provision_wl_net_name | net_interface ~ network_patch_suffix_phy }}" - bootproto: "static" - bridge: "{{ provision_wl_net_name | net_interface }}" - peer_device: "{{ network_patch_prefix ~ provision_wl_net_name | net_interface ~ network_patch_suffix_ovs }}" - peer_bootproto: "static" - onboot: yes - when: "{{ provision_wl_net_name in network_interfaces }}" - tags: - - config + tags: + - config + vars: + veth_bridges: [] + veth_interfaces: [] + pre_tasks: + # When these networks are VLANs, we need to use the underlying tagged + # bridge interface rather than the untagged interface. We therefore strip + # the . suffix of the interface name. We use a union here as a single + # tagged interface may be shared between these networks. + - name: Update a fact containing bridges to be patched to the Neutron OVS bridge + set_fact: + veth_bridges: > + {{ veth_bridges | + union([item | net_interface | replace('.' ~ item | net_vlan | default('!nomatch!'), '')]) | + list }} + with_items: + - "{{ provision_wl_net_name }}" + - "{{ external_net_name }}" + when: "{{ item in network_interfaces }}" - # Configure a virtual ethernet patch link to connect the external network - # bridge to the Neutron OVS bridge. + - name: Update a fact containing veth interfaces + set_fact: + veth_interfaces: > + {{ veth_interfaces + + [{'device': network_patch_prefix ~ item ~ network_patch_suffix_phy, + 'bootproto': 'static', + 'bridge': item, + 'peer_device': network_patch_prefix ~ item ~ network_patch_suffix_ovs, + 'peer_bootproto': 'static', + 'onboot': 'yes'}] }} + with_items: "{{ veth_bridges }}" + roles: - role: veth - veth_interfaces: - - device: "{{ network_patch_prefix ~ external_net_name | net_interface ~ network_patch_suffix_phy }}" - bootproto: "static" - bridge: "{{ external_net_name | net_interface }}" - peer_device: "{{ network_patch_prefix ~ external_net_name | net_interface ~ network_patch_suffix_ovs }}" - peer_bootproto: "static" - onboot: yes - when: - - "{{ external_net_name in network_interfaces }}" - - "{{ external_net_name != provision_wl_net_name }}" - tags: - - config diff --git a/ansible/provision-net.yml b/ansible/provision-net.yml index a9f1cd4f5..567f49d92 100644 --- a/ansible/provision-net.yml +++ b/ansible/provision-net.yml @@ -10,13 +10,13 @@ neutron_net_openstack_auth_type: "{{ openstack_auth_type }}" neutron_net_openstack_auth: "{{ openstack_auth }}" # Network configuration. - neutron_net_name: "provision-net" - neutron_net_type: "flat" + neutron_net_name: "{{ kolla_ironic_provisioning_network }}" + neutron_net_type: "{% if provision_wl_net_name | net_vlan %}vlan{% else %}flat{% endif %}" neutron_net_physical_network: "physnet1" - neutron_net_segmentation_id: + neutron_net_segmentation_id: "{{ provision_wl_net_name | net_vlan }}" neutron_net_shared: True # Subnet configuration. - neutron_net_subnet_name: "provision-subnet" + neutron_net_subnet_name: "{{ kolla_ironic_provisioning_network }}" neutron_net_cidr: "{{ provision_wl_net_name | net_cidr }}" neutron_net_gateway_ip: "{{ provision_wl_net_name | net_gateway }}" neutron_net_allocation_pool_start: "{{ provision_wl_net_name | net_allocation_pool_start }}" diff --git a/ansible/roles/kolla-openstack/defaults/main.yml b/ansible/roles/kolla-openstack/defaults/main.yml index 971c42a79..746aef5a5 100644 --- a/ansible/roles/kolla-openstack/defaults/main.yml +++ b/ansible/roles/kolla-openstack/defaults/main.yml @@ -6,12 +6,18 @@ kolla_node_custom_config_path: # Ironic configuration. # List of enabled Ironic drivers. -ironic_drivers: +kolla_ironic_drivers: - agent_ssh - agent_ipmitool - pxe_ssh - pxe_ipmitool +# Name or UUID of the Neutron network to use for cleaning. +kolla_ironic_cleaning_network: + +# Name or UUID of the Neutron network to use for provisioning. +kolla_ironic_provisioning_network: + # Free form extra configuration to append to ironic.conf. kolla_extra_ironic: diff --git a/ansible/roles/kolla-openstack/templates/ironic.conf.j2 b/ansible/roles/kolla-openstack/templates/ironic.conf.j2 index 9d01a7b3e..2b6d84a15 100644 --- a/ansible/roles/kolla-openstack/templates/ironic.conf.j2 +++ b/ansible/roles/kolla-openstack/templates/ironic.conf.j2 @@ -1,7 +1,7 @@ # {{ ansible_managed }} [DEFAULT] -enabled_drivers = {{ ironic_drivers | join(',') }} +enabled_drivers = {{ kolla_ironic_drivers | join(',') }} [conductor] {% raw %} @@ -11,6 +11,10 @@ api_url = {{ internal_protocol }}://{{ hostvars[inventory_hostname]['ansible_' + [agent] deploy_logs_local_path = /var/log/kolla/ironic/deploy +[neutron] +cleaning_network = {{ kolla_ironic_cleaning_network }} +provisioning_network = {{ kolla_ironic_provisioning_network }} + [pxe] {% raw %} tftp_server = {{ hostvars[inventory_hostname]['ansible_' + provision_interface | replace('-', '_')]['ipv4']['address'] }} diff --git a/etc/kayobe/ironic.yml b/etc/kayobe/ironic.yml new file mode 100644 index 000000000..6d9a3c526 --- /dev/null +++ b/etc/kayobe/ironic.yml @@ -0,0 +1,16 @@ +--- +############################################################################### +# Ironic configuration. + +# List of enabled Ironic drivers. +#kolla_ironic_drivers: + +# Name of the Neutron network to use for cleaning. +#kolla_ironic_cleaning_network: + +# Name of the Neutron network to use for provisioning. +#kolla_ironic_provisioning_network: + +############################################################################### +# Dummy variable to allow Ansible to accept this file. +workaround_ansible_issue_8743: yes