[CORD-2270]
Support head node on Ubuntu 16.04 (Xenial)

Change-Id: Ic13ea784b8fa55a481f08d21f5187fd37d13499c
diff --git a/roles/interface-config/defaults/main.yml b/roles/interface-config/defaults/main.yml
index e1039fd..8dcbf0e 100644
--- a/roles/interface-config/defaults/main.yml
+++ b/roles/interface-config/defaults/main.yml
@@ -1,4 +1,4 @@
-
+---
 # Copyright 2017-present Open Networking Foundation
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,14 +12,41 @@
 # 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.
-
-
----
+#
 # interface-config/defaults/main.yml
 
-mgmt_interface: eth1
-
-mgmt_ipv4_first_octets: "192.168.200"
-
+# list of physical nodes in the scenario
 physical_node_list: []
 
+# headnode internet-facing interface to NAT mgmtbridge traffic out of
+headnode_nat_interface: eth0
+
+# network interfaces on  physical nodes
+management_net_interfaces: []
+fabric_net_interfaces: []
+
+# management network configuration
+management_net_cidr: "192.168.200.0/24"
+dns_servers:
+  - "{{ management_net_cidr | ipaddr('1') | ipaddr('address') }}"
+
+# VTN MANAGEMENT_HOST network
+use_vtn_net_management_host: False
+vtn_net_management_host_cidr: "{{ management_net_cidr }}"
+vtn_net_management_host_hwaddr_prefix: "06A6"
+
+# VTN PUBLIC network, used with fabric
+use_vtn_net_fabric: False
+vtn_data_plane_interface: "vethfabric1"
+vtn_net_public_cidr: "10.6.1.0/24"
+vtn_net_public_hwaddr_prefix: "0242"
+
+# VSG and public address pools
+use_addresspool_vsg: False
+addresspool_vsg_cidr: "10.7.1.0/24"
+addresspool_vsg_hwaddr_prefix: "0ACA"
+
+use_addresspool_public: False
+addresspool_public_cidr: "10.8.1.0/24"
+addresspool_public_hwaddr_prefix: "0EFE"
+
diff --git a/roles/interface-config/handlers/main.yml b/roles/interface-config/handlers/main.yml
new file mode 100644
index 0000000..13b765b
--- /dev/null
+++ b/roles/interface-config/handlers/main.yml
@@ -0,0 +1,10 @@
+---
+# interface-config/handlers/main.yml
+
+# the next handler may need to change in 16.04 as iptables-persistent was split
+# and may need 'netfilter-persistent' installed as well
+- name: iptables-save
+  shell: iptables-save | grep -vi docker > /etc/iptables/rules.v4
+  tags:
+    - skip_ansible_lint
+
diff --git a/roles/interface-config/tasks/main.yml b/roles/interface-config/tasks/main.yml
index f768c38..44efe78 100644
--- a/roles/interface-config/tasks/main.yml
+++ b/roles/interface-config/tasks/main.yml
@@ -1,4 +1,4 @@
-
+---
 # Copyright 2017-present Open Networking Foundation
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,22 +13,90 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-
----
 # interface-config/tasks/main.yml
 
-- name: Create network interface for management network
+- name: Install bridging/bonding utilities
+  apt:
+    name: "{{ item }}"
+    update_cache: yes
+    cache_valid_time: 3600
+  with_items:
+    - bridge-utils
+    - ifenslave
+    - iptables-persistent
+
+- name: Create management network interfaces
   template:
-    src: eth.cfg.j2
-    dest: "/etc/network/interfaces.d/{{ mgmt_interface }}.cfg"
+    src: management.cfg.j2
+    dest: "/etc/network/interfaces.d/management.cfg"
     owner: root
     group: root
     mode: 0644
-  register: mgmtint_config
+  register: management_net_config
 
-- name: Bring up management network if reconfigured
-  when: mgmtint_config.changed
-  command: "ifup {{ mgmt_interface }}"
+- name: Bring up management network interfaces, if reconfigured
+  when: management_net_config.changed
+  command: "ifup {{ item }}"
+  with_flattened:
+   - mgmtbridge
+   - mgmtbond
+   - "{{ management_net_interfaces }}"
+   - vethmgmt0
+  tags:
+    - skip_ansible_lint # needs to be run before next steps
+
+# NAT/forward management network traffic out the head node
+- name: Default to accept forwarded traffic
+  when: "'head' in group_names and management_net_config.changed"
+  iptables:
+    chain: FORWARD
+    policy: ACCEPT
+  notify:
+    - iptables-save
+  tags:
+    - skip_ansible_lint # need to save config in following steps
+
+- name: Configure forwarding for management bridge
+  when: "'head' in group_names and management_net_config.changed"
+  iptables:
+    chain: FORWARD
+    in_interface: mgmtbridge
+    jump: ACCEPT
+  notify:
+    - iptables-save
+  tags:
+    - skip_ansible_lint # need to save config in following steps
+
+- name: Configure NAT for management network
+  when: "'head' in group_names and management_net_config.changed"
+  iptables:
+    table: nat
+    chain: POSTROUTING
+    out_interface: "{{ headnode_nat_interface }}"
+    jump: MASQUERADE
+  notify:
+    - iptables-save
+  tags:
+    - skip_ansible_lint # need to save config in following steps
+
+# Create fabric bridge and veth pair
+- name: Create fabric network interfaces on compute nodes
+  template:
+    src: fabric.cfg.j2
+    dest: "/etc/network/interfaces.d/fabric.cfg"
+    owner: root
+    group: root
+    mode: 0644
+  register: compute_fabric_config
+
+- name: Bring up fabric interfaces, if reconfigured
+  when: compute_fabric_config.changed
+  command: "ifup {{ item }}"
+  with_flattened:
+    - fabricbridge
+    - fabricbond
+    - "{{ fabric_net_interfaces }}"
+    - vethfabric0
   tags:
     - skip_ansible_lint # needs to be run before next steps
 
diff --git a/roles/interface-config/templates/eth.cfg.j2 b/roles/interface-config/templates/eth.cfg.j2
deleted file mode 100644
index b9aa67d..0000000
--- a/roles/interface-config/templates/eth.cfg.j2
+++ /dev/null
@@ -1,30 +0,0 @@
-
-{#
-Copyright 2017-present Open Networking Foundation
-
-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.
-#}
-
-
-{% for node in physical_node_list if node.name == ansible_hostname %}
-auto {{ node.interface | default(mgmt_interface) }}
-
-iface {{ node.interface | default(mgmt_interface) }} inet static
-    address {{ mgmt_ipv4_first_octets }}.{{ node.ipv4_last_octet }}
-    network {{ mgmt_ipv4_first_octets }}.0
-    netmask 255.255.255.0
-    broadcast {{ mgmt_ipv4_first_octets }}.255
-{% if node.gateway_enabled is defined and node.gateway_enabled %}
-    gateway {{ mgmt_ipv4_first_octets }}.1
-{% endif %}
-{% endfor %}
diff --git a/roles/interface-config/templates/fabric.cfg.j2 b/roles/interface-config/templates/fabric.cfg.j2
new file mode 100644
index 0000000..0547f6a
--- /dev/null
+++ b/roles/interface-config/templates/fabric.cfg.j2
@@ -0,0 +1,83 @@
+# Created by platform-install: interface-config/templates/fabric.cfg.j2
+{% for node in physical_node_list if node.name == ansible_hostname %}
+
+# fabricbridge between physical bond and virtual interfaces for VTN
+auto fabricbridge
+iface fabricbridge inet manual
+  pre-up ip link add fabricbridge type bridge
+  bridge_ports fabricbond vethfabric0
+
+# fabric bond of physical interfaces for VTN
+auto fabricbond
+iface fabricbond inet manual
+  pre-up ip link add fabricbond type bond
+  pre-up ip link set fabricbond up
+  bond-miimon 100
+  bond-slaves none
+  bond-mode active-backup
+  post-down ip link del fabricbond
+
+{% if fabric_net_interfaces %}
+# physical network members of fabricbond
+{% for fab_int in fabric_net_interfaces %}
+auto {{ fab_int }}
+iface {{ fab_int }} inet manual
+  pre-up ip link set {{ mgmt_int }} master fabricbond
+  bond-master fabricbond
+  bond-mode active-backup
+  bond-primary {{ management_net_interfaces | join(' ') }}
+  post-down ip link set dev {{ mgmt_int }} nomaster
+
+{% endfor %}
+{% endif %}
+
+# vethfabric0/vethfabric1 interfaces connect from VTN br-int to fabricbridge
+# vethfabric0: connected to fabricbridge
+auto vethfabric0
+iface vethfabric0 inet manual
+  pre-up ip link add vethfabric0 type veth peer name vethfabric1
+  pre-up ip link set vethfabric0 up
+  post-up ip link set dev vethfabric0 master fabricbridge
+  pre-down ip link set dev vethfabric0 nomaster
+  post-down ip link del vethfabric0
+
+# vethfabric1: becomes a part of br-int, which takes over the IP address
+{% set vtn_veth_ip = ( vtn_net_public_cidr | ipaddr(node.ipv4_last_octet) | ipaddr('address')) %}
+auto vethfabric1
+iface vethfabric1 inet static
+  address {{ vtn_veth_ip }}
+  network {{ vtn_net_public_cidr | ipaddr('network') }}
+  netmask {{ vtn_net_public_cidr | ipaddr('netmask') }}
+  gateway {{ vtn_net_public_cidr | ipaddr('1') | ipaddr('address') }}
+  broadcast {{ vtn_net_public_cidr | ipaddr('broadcast') }}
+  hwaddress ether {{ ( vtn_net_public_hwaddr_prefix ~ ( vtn_veth_ip | ip4_hex )) | hwaddr('unix') }}
+
+{% if use_addresspool_vsg %}
+# vSG public gateway
+{% set ap_vsg_veth_ip = ( addresspool_vsg_cidr | ipaddr(node.ipv4_last_octet) | ipaddr('address')) %}
+auto vethfabric1:0
+iface vethfabric1:0 inet static
+  address {{ ap_vsg_veth_ip }}
+  network {{ addresspool_vsg_cidr | ipaddr('network') }}
+  netmask {{ addresspool_vsg_cidr | ipaddr('netmask') }}
+  gateway {{ addresspool_vsg_cidr | ipaddr('1') | ipaddr('address') }}
+  broadcast {{ addresspool_vsg_cidr | ipaddr('broadcast') }}
+  hwaddress ether {{ ( addresspool_vsg_hwaddr_prefix ~ ( ap_vsg_eth_ip | ip4_hex )) | hwaddr('unix') }}
+
+{% endif %}
+
+{% if use_addresspool_public %}
+# public network gateway
+{% set ap_pub_veth_ip = ( addresspool_public_cidr | ipaddr(node.ipv4_last_octet) | ipaddr('address')) %}
+auto vethfabric1:1
+iface vethfabric1:1 inet static
+  address {{ ap_pub_veth_ip }}
+  network {{ addresspool_public_cidr | ipaddr('network') }}
+  netmask {{ addresspool_public_cidr | ipaddr('netmask') }}
+  gateway {{ addresspool_public_cidr | ipaddr('1') | ipaddr('address') }}
+  broadcast {{ addresspool_public_cidr | ipaddr('broadcast') }}
+  hwaddress ether {{ ( addresspool_public_hwaddr_prefix ~ ( ap_pub_veth_ip | ip4_hex )) | hwaddr('unix') }}
+
+{% endif %}
+{% endfor %}
+
diff --git a/roles/interface-config/templates/management.cfg.j2 b/roles/interface-config/templates/management.cfg.j2
new file mode 100644
index 0000000..bb3c1e6
--- /dev/null
+++ b/roles/interface-config/templates/management.cfg.j2
@@ -0,0 +1,57 @@
+# Created by platform-install: interface-config/templates/management.cfg.j2
+{% for node in physical_node_list if node.name == ansible_hostname %}
+
+# management bridge between physical and virtual interfaces for VTN
+{% set mgmtbr_ip = ( vtn_net_management_host_cidr | ipaddr(node.ipv4_last_octet) | ipaddr('address')) %}
+auto mgmtbridge
+iface mgmtbridge inet static
+  pre-up ip link add mgmtbridge type bridge
+  bridge_ports mgmtbond vethmgmt0
+  address {{ mgmtbr_ip }}
+  network {{ vtn_net_management_host_cidr | ipaddr('network') }}
+  netmask {{ vtn_net_management_host_cidr | ipaddr('netmask') }}
+  broadcast {{ vtn_net_management_host_cidr | ipaddr('broadcast') }}
+  hwaddress ether {{ ( vtn_net_management_host_hwaddr_prefix ~ ( mgmtbr_ip | ip4_hex )) | hwaddr('unix') }}
+  dns-search {{ site_suffix }}
+  dns-nameservers {{ dns_servers | join(" ") }}
+
+# management bond of physical interfaces
+auto mgmtbond
+iface mgmtbond inet manual
+  pre-up ip link add mgmtbond type bond
+  pre-up ip link set mgmtbond up
+  bond-slaves none
+  bond-mode active-backup
+  bond-miimon 100
+  post-down ip link del mgmtbond
+
+{% if management_net_interfaces %}
+# physical network members of mgmtbond
+{% for mgmt_int in management_net_interfaces %}
+auto {{ mgmt_int }}
+iface {{ mgmt_int }} inet manual
+  pre-up ip link set {{ mgmt_int }} master mgmtbond
+  bond-master mgmtbond
+  bond-mode active-backup
+  bond-primary {{ management_net_interfaces | join(' ') }}
+  post-down ip link set dev {{ mgmt_int }} nomaster
+
+{% endfor %}
+{% endif %}
+
+# veth interfaces for VTN MANAGEMENT_HOST
+# vethmgmt0: connected to mgmtbond
+auto vethmgmt0
+iface vethmgmt0 inet manual
+  pre-up ip link add vethmgmt0 type veth peer name vethmgmt1
+  pre-up ip link set vethmgmt0 up
+  pre-up ip link set vethmgmt1 up
+  post-up ip link set dev vethmgmt0 master mgmtbridge
+  pre-down ip link set dev vethmgmt0 nomaster
+  post-down ip link del vethmgmt0
+
+# vethmgmt1: connected to VTN as hostManagementIface, if enabled
+# Brought up by vethmgmt0
+
+{% endfor %}
+