initial commit
Change-Id: I5063800f2ddaf90a350325a9186479c25f90f8e1
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3c96d86
--- /dev/null
+++ b/README.md
@@ -0,0 +1,85 @@
+# OpenCORD Bare Metal Provisioning
+
+OpenCORD leverages Canonical's Metal as a Service (MAAS) solution. The MAAS solution provides a PXE boot environment. The basic
+bare metal provisioning flow is:
+ 1. Install and provisioning MAAS and other utilities on one compute node that will have the **head node** role
+ 1. Boot the other components in a CORD POD (switches and other compute nodes)
+ 1. Once other components are operational perform some additional provisioning to prepare them to be part of a CORD POD and
+ to be compliant with best practices of a CORD POD
+
+After the base bare metal provisioning is complete further provisioning, such as XOS or leaf-spine fabric can be deployed.
+
+# Ansible Roles Provided
+
+## docker
+
+Ensures Docker tools are available on the target system. Specifically `docker-engine` and `docker-compose`.
+
+## java8-oracle
+
+Ensures that the Oracle version of Java8 is available on the target system.
+
+## fabric-switch
+
+Ensures the OpenFlow agent (ofdpa) is available on the target switch as well as utility scripts that have been
+helpful in the lab.
+
+### Configuration
+
+One of the scripts made available via this role is called `connect`. This script initiates the connection from
+the switch to an OpenFlow controller. To connect to the controller the **DPID** of the switch is required as is
+the **IP Address** of the SDN controller.
+
+The **IP Address** of the controller is likely universal for all switches and can be either set via the command
+line, using the `--extra-vars` command line option or via a global `vars` file.
+
+The **DPID** is a per switch setting and represents the OpenFlow ID for the switch and will be in the for or
+`0x0000000000000012` and will be unique for each switch. This value can be set either in a host specific variable
+file or if the playbook is being run against a single switch using the `--extra-vars` command line option.
+
+## compute-node
+
+Ensures the 40G network interface card (NIC) drivers are installed and that the interfaces on the compute node are
+named according to best practices. Specificall the 2 40G ports are `eth0` and `eth1`; the 2 10G ports are `eth2` and
+`eth3`. Additionally, this roles sets a default password for the ubuntu user so that console logins are possible
+for debug purposes. This last change, default password, should be eliminated for proxuction use.
+
+### Configuration
+
+Each compute node is statically assigned an IP address for the leaf-spine fabric. This can be configured
+in a host specific variable file found in the `host_vars` directory or could be specified on the command line
+using the `--extra-vars` option if the play book is be run against a single target system.
+
+### Dependencies
+
+This role depends on the `docker` role.
+
+## onos-fabric
+
+Ensures
+
+## mmas
+
+Ensures that Canonical's Metal as a Service (MAAS) is available on the target system and configured according to
+best practices for a CORD POD. This role is meant to be applied to a head node in the CORD POD.
+
+### Assumptions
+
+ - A 2 port _Intel_ 40G card is installed on the head node
+ - A 2 port 10G card is installed on the head node
+ - Head Node has _Internet_ connectivity via the 10G interface named `eth3`
+
+ _Additionally configuration variables, including network IP addressing information can be found in
+ the file `vars/main.yml`._
+
+### Comments
+
+ - `iptables` rules will be established to `NAT` traffic out interface `eth3`
+ - This role installs and starts two docker images to help manage the MAAs install including:
+ - `cord/maas-dhcp-harvester:0.1-prerelease` - adds DHCP addresses to the DNS server that MAAS misses
+ - `cord/maas-automation:0.1-prerelease` - automates compute nodes through the states of MAAS to the
+ deployed state so that they can use used as part of the CORD POD.
+
+### Dependencies
+
+This role depends on the `docker` role.
diff --git a/fabric.yml b/fabric.yml
new file mode 100644
index 0000000..11f14f7
--- /dev/null
+++ b/fabric.yml
@@ -0,0 +1,3 @@
+- hosts: switches
+ roles:
+ - fabric-switch
diff --git a/host_vars/cord-r6-s1 b/host_vars/cord-r6-s1
new file mode 100644
index 0000000..c3a1a5f
--- /dev/null
+++ b/host_vars/cord-r6-s1
@@ -0,0 +1 @@
+fabric_ip: 10.6.1.1/24
diff --git a/host_vars/cord-r6-s2 b/host_vars/cord-r6-s2
new file mode 100644
index 0000000..1591f03
--- /dev/null
+++ b/host_vars/cord-r6-s2
@@ -0,0 +1 @@
+fabric_ip: 10.6.1.2/24
diff --git a/host_vars/cord-r6-s3 b/host_vars/cord-r6-s3
new file mode 100644
index 0000000..f767de6
--- /dev/null
+++ b/host_vars/cord-r6-s3
@@ -0,0 +1 @@
+fabric_ip: 10.6.2.3/24
diff --git a/host_vars/cord-r6-s4 b/host_vars/cord-r6-s4
new file mode 100644
index 0000000..57aae55
--- /dev/null
+++ b/host_vars/cord-r6-s4
@@ -0,0 +1 @@
+fabric_ip: 10.6.2.4/24
diff --git a/hosts b/hosts
new file mode 100644
index 0000000..4f6fe62
--- /dev/null
+++ b/hosts
@@ -0,0 +1,8 @@
+[switches]
+spine-1
+spine-2
+leaf-1
+leaf-2
+
+[switches:vars]
+ansible_ssh_user=root
diff --git a/roles/compute-node/files/i40e-1.4.25.tar.gz b/roles/compute-node/files/i40e-1.4.25.tar.gz
new file mode 100644
index 0000000..6a30a04
--- /dev/null
+++ b/roles/compute-node/files/i40e-1.4.25.tar.gz
Binary files differ
diff --git a/roles/compute-node/files/rename_ifaces.sh b/roles/compute-node/files/rename_ifaces.sh
new file mode 100755
index 0000000..be8cb72
--- /dev/null
+++ b/roles/compute-node/files/rename_ifaces.sh
@@ -0,0 +1,180 @@
+#!/bin/bash
+
+function ip2int {
+ local a b c d
+ { IFS=. read a b c d; } <<< $1
+ echo $(((((((a << 8) | b) << 8) | c) << 8) | d))
+}
+
+function int2ip {
+ local ui32=$1; shift
+ local ip n
+ for n in 1 2 3 4; do
+ ip=$((ui32 & 0xff))${ip:+.}$ip
+ ui32=$((ui32 >> 8))
+ done
+ echo $ip
+}
+
+function netmask {
+ local mask=$((0xffffffff << (32 - $1))); shift
+ int2ip $mask
+}
+
+function broadcast {
+ local addr=$(ip2int $1); shift
+ local mask=$((0xffffffff << (32 -$1))); shift
+ int2ip $((addr | ~mask))
+}
+
+function network {
+ local addr=$(ip2int $1); shift
+ local mask=$((0xffffffff << (32 -$1))); shift
+ int2ip $((addr & mask))
+}
+
+function first {
+ local addr=$(ip2int $1)
+ addr=`expr $addr + 1`
+ int2ip $addr
+}
+
+function guess_type {
+ local CNT=$(echo "$1" | sed -e 's/[:.]/ /g' | wc -w)
+ if [ $CNT -ne 1 ]; then
+ # drop all sub and vlan interfaces
+ echo "DNC"
+ return
+ fi
+ local DRIVER=$(ethtool -i $1 2>/dev/null | grep driver | awk '{print $2}')
+ local RESULT="DNC"
+ case $DRIVER in
+ i40e)
+ RESULT="I40G"
+ ;;
+ igb)
+ RESULT="ETH"
+ ;;
+ *) ;;
+ esac
+ echo $RESULT
+}
+
+function get_mac {
+ echo $(ifconfig $1 | grep HWaddr | awk '{print $5}')
+}
+
+function generate_persistent_names {
+ local OUT=$NAMES_FILE
+#"70-persistent-net.rules"
+ rm -rf $OUT
+
+ IDX=0
+ for i in $(cat $1 | sort); do
+ echo "SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"?*\", ATTR{address}==\"$i\", ATTR{dev_id}==\"0x0\", ATTR{type}==\"1\", KERNEL==\"eth*\", NAME=\"eth$IDX\"" >> $OUT
+ IDX=$(expr $IDX + 1)
+ done
+
+ for i in $(cat $2 | sort); do
+ echo "SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"?*\", ATTR{address}==\"$i\", NAME=\"eth$IDX\"" >> $OUT
+ IDX=$(expr $IDX + 1)
+ done
+}
+
+function generate_interfaces {
+ OUT=$IFACES_FILE
+ rm -rf $OUT
+ echo "# This file describes the network interfaces available on your system" >> $OUT
+ echo "# and how to activate them. For more information, see interfaces(5)." >> $OUT
+ echo "" >> $OUT
+ echo "# The loopback network interface" >> $OUT
+ echo "auto lo" >> $OUT
+ echo "iface lo inet loopback" >> $OUT
+ echo "" >> $OUT
+
+ IDX=0
+ FIRST=1
+ for i in $(cat $1); do
+ if [ $FIRST -eq 1 ]; then
+ echo "auto eth$IDX" >> $OUT
+ echo "iface eth$IDX inet static" >> $OUT
+ echo " address $IP" >> $OUT
+ echo " network $NETWORK" >> $OUT
+ echo " netmask $NETMASK" >> $OUT
+ FIRST=0
+ else
+ echo "iface eth$IDX inet manual" >> $OUT
+ fi
+ echo "" >> $OUT
+ IDX=$(expr $IDX + 1)
+ done
+
+ FIRST=1
+ for i in $(cat $2); do
+ if [ $FIRST -eq 1 ]; then
+ echo "auto eth$IDX" >> $OUT
+ echo "iface eth$IDX inet dhcp" >> $OUT
+ FIRST=0
+ else
+ echo "iface eth$IDX inet manual" >> $OUT
+ fi
+ echo "" >> $OUT
+ IDX=$(expr $IDX + 1)
+ done
+}
+
+ADDR=$1
+IP=$(echo $ADDR | cut -d/ -f1)
+MASKBITS=$(echo $ADDR | cut -d/ -f2)
+NETWORK=$(network $IP $MASKBITS)
+NETMASK=$(netmask $MASKBITS)
+
+LIST_ETH=$(mktemp -u)
+LIST_40G=$(mktemp -u)
+IFACES_FILE=$(mktemp -u)
+NAMES_FILE=$(mktemp -u)
+
+IFACES=$(ifconfig -a | grep "^[a-z]" | awk '{print $1}')
+
+for i in $IFACES; do
+ TYPE=$(guess_type $i)
+ case $TYPE in
+ ETH)
+ echo "$(get_mac $i)" >> $LIST_ETH
+ ;;
+ I40G)
+ echo "$(get_mac $i)" >> $LIST_40G
+ ;;
+ *) ;;
+ esac
+done
+
+RESULT="false"
+
+generate_interfaces $LIST_40G $LIST_ETH
+diff /etc/network/interfaces $IFACES_FILE 2>&1 > /dev/null
+if [ $? -ne 0 ]; then
+ RESULT="true"
+ cp /etc/network/interfaces /etc/network/interfaces.1
+ cp $IFACES_FILE /etc/network/interfaces
+fi
+
+generate_persistent_names $LIST_40G $LIST_ETH
+if [ -r /etc/udev/rules.d/70-persistent-net.rules ]; then
+ diff /etc/udev/rules.d/70-persistent-net.rules $NAMES_FILE 2>&1 > /dev/null
+ if [ $? -ne 0 ]; then
+ RESULT="true"
+ cp /etc/udev/rules.d/70-persistent-net.rules /etc/udev/rules.d/70-persistent-net.rules.1
+ cp $NAMES_FILE /etc/udev/rules.d/70-persistent-net.rules
+ fi
+else
+ RESULT="true"
+ cp $NAMES_FILE /etc/udev/rules.d/70-persistent-net.rules
+fi
+
+rm -rf $IFACES_FILE
+rm -rf $NAMES_FILE
+rm -rf $LIST_ETH
+rm -rf $LIST_40G
+
+echo -n $RESULT
diff --git a/roles/compute-node/files/rename_ifaces.sh.back b/roles/compute-node/files/rename_ifaces.sh.back
new file mode 100755
index 0000000..76056ff
--- /dev/null
+++ b/roles/compute-node/files/rename_ifaces.sh.back
@@ -0,0 +1,110 @@
+#!/bin/bash
+
+BASE="10.4"
+LEAF="1"
+SERVER="1"
+
+function guess_type {
+ local CNT=$(echo "$1" | sed -e 's/[:.]/ /g' | wc -w)
+ if [ $CNT -ne 1 ]; then
+ # drop all sub and vlan interfaces
+ echo "DNC"
+ return
+ fi
+ local DRIVER=$(ethtool -i $1 2>/dev/null | grep driver | awk '{print $2}')
+ local RESULT="DNC"
+ case $DRIVER in
+ i40e)
+ RESULT="I40G"
+ ;;
+ igb)
+ RESULT="ETH"
+ ;;
+ *) ;;
+ esac
+ echo $RESULT
+}
+
+function get_mac {
+ echo $(ifconfig $1 | grep HWaddr | awk '{print $5}')
+}
+
+function generate_persistent_names {
+ local OUT="70-persistent-net.rules"
+ rm -rf $OUT
+
+ IDX=0
+ for i in $(cat $1 | sort); do
+ echo "SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"?*\", ATTR{address}==\"$i\", ATTR{dev_id}==\"0x0\", ATTR{type}==\"1\", KERNEL==\"eth*\", NAME=\"eth$IDX\"" >> $OUT
+ IDX=$(expr $IDX + 1)
+ done
+
+ for i in $(cat $2 | sort); do
+ echo "SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"?*\", ATTR{address}==\"$i\", NAME=\"eth$IDX\"" >> $OUT
+ IDX=$(expr $IDX + 1)
+ done
+}
+
+function generate_interfaces {
+ OUT="interfaces"
+ rm -rf $OUT
+ echo "# This file describes the network interfaces available on your system" >> $OUT
+ echo "# and how to activate them. For more information, see interfaces(5)." >> $OUT
+ echo "" >> $OUT
+ echo "# The loopback network interface" >> $OUT
+ echo "auto lo" >> $OUT
+ echo "iface lo inet loopback" >> $OUT
+ echo "" >> $OUT
+
+ IDX=0
+ FIRST=1
+ for i in $(cat $1); do
+ if [ $FIRST -eq 1 ]; then
+ echo "auto eth$IDX" >> $OUT
+ echo "iface eth$IDX inet static" >> $OUT
+ echo " address $BASE.$LEAF.$SERVER" >> $OUT
+ echo " network $BASE.$LEAF.0" >> $OUT
+ echo " netmask 255.255.255.0" >> $OUT
+ FIRST=0
+ else
+ echo "iface eth$IDX inet manual" >> $OUT
+ fi
+ echo "" >> $OUT
+ IDX=$(expr $IDX + 1)
+ done
+
+ FIRST=1
+ for i in $(cat $2); do
+ if [ $FIRST -eq 1 ]; then
+ echo "auto eth$IDX" >> $OUT
+ echo "iface eth$IDX inet dhcp" >> $OUT
+ FIRST=0
+ else
+ echo "iface eth$IDX inet manual" >> $OUT
+ fi
+ echo "" >> $OUT
+ IDX=$(expr $IDX + 1)
+ done
+}
+
+LIST_ETH=$(mktemp -u)
+LIST_40G=$(mktemp -u)
+IFACES=$(ifconfig -a | grep "^[a-z]" | awk '{print $1}')
+
+for i in $IFACES; do
+ TYPE=$(guess_type $i)
+ case $TYPE in
+ ETH)
+ echo "$(get_mac $i)" >> $LIST_ETH
+ ;;
+ I40G)
+ echo "$(get_mac $i)" >> $LIST_40G
+ ;;
+ *) ;;
+ esac
+done
+
+generate_persistent_names $LIST_40G $LIST_ETH
+generate_interfaces $LIST_40G $LIST_ETH
+rm -rf $LIST_ETH
+rm -rf $LIST_40G
diff --git a/roles/compute-node/meta/main.yml b/roles/compute-node/meta/main.yml
new file mode 100644
index 0000000..45b0e96
--- /dev/null
+++ b/roles/compute-node/meta/main.yml
@@ -0,0 +1,15 @@
+---
+galaxy_info:
+ author: Ciena Blueplanet
+ description: CORD POD Compute Node Base
+ company: Ciena Blueplanet
+ license: Apache 2.0
+ min_ansible_version: 2.0
+ platforms:
+ - name: Ubuntu
+ versions:
+ - trusty
+ galaxy_tags:
+ - cord
+dependencies:
+ - { role : docker }
diff --git a/roles/compute-node/tasks/i40e_driver.yml b/roles/compute-node/tasks/i40e_driver.yml
new file mode 100644
index 0000000..5f6b199
--- /dev/null
+++ b/roles/compute-node/tasks/i40e_driver.yml
@@ -0,0 +1,40 @@
+---
+- name: Copy i40e Interface Driver
+ unarchive:
+ src=files/i40e-1.4.25.tar.gz
+ dest={{ ansible_env.HOME }}
+ owner=ubuntu
+ group=ubuntu
+
+- name: Build i40e Driver
+ command: make
+ args:
+ chdir: i40e-1.4.25/src
+ creates: "{{ ansible_env.HOME }}/i40e-1.4.25/src/i40e/i40e.ko"
+
+- name: Unload i40e Driver
+ become: yes
+ modprobe: name=i40e state=absent
+
+- name: Install i40e Driver
+ become: yes
+ command: make install
+ args:
+ chdir: i40e-1.4.25/src
+
+- name: Load i40e Driver
+ become: yes
+ modprobe: name=i40e state=present
+
+- name: Persist i40e Driver Loadi
+ become: yes
+ lineinfile:
+ dest=/etc/modules
+ line="i40e"
+ state=present
+ insertafter=EOF
+
+- name: Remove Build Files
+ file:
+ path={{ ansible_env.HOME }}/i40e-1.4.25
+ state=absent
diff --git a/roles/compute-node/tasks/main.yml b/roles/compute-node/tasks/main.yml
new file mode 100644
index 0000000..d64adfe
--- /dev/null
+++ b/roles/compute-node/tasks/main.yml
@@ -0,0 +1,32 @@
+---
+- name: Applications
+ become: yes
+ apt: name={{ item }} state=present
+ with_items:
+ - build-essential
+
+- name: Set Default Password
+ become: yes
+ user:
+ name=ubuntu
+ password="$6$TjhJuOgh8xp.v$z/4GwFbn5koVmkD6Ex9wY7bgP7L3uP2ujZkZSs1HNdzQdz9YclbnZH9GvqMC/M1iwC0MceL05.13HoFz/bai0/"
+
+- name: Verify i40e Driver
+ command: modinfo --field=version i40e
+ register: i40e_version
+ changed_when: False
+
+- name: Update i40e Driver
+ include: tasks/i40e_driver.yml
+ when: i40e_version.stdout != '1.4.25'
+
+- name: Consistent Interface Naming
+ become: yes
+ script: files/rename_ifaces.sh {{ fabric_ip }}
+ register: ifaces_changed
+ changed_when: ifaces_changed.stdout != "false"
+
+- name: Reboot Required
+ become: yes
+ command: /sbin/reboot
+ when: ifaces_changed.stdout != "false"
diff --git a/roles/docker/meta/main.yml b/roles/docker/meta/main.yml
new file mode 100644
index 0000000..bf39d8c
--- /dev/null
+++ b/roles/docker/meta/main.yml
@@ -0,0 +1,15 @@
+---
+galaxy_info:
+ author: Ciena Blueplanet
+ description: Docker Engine and Docker Compose
+ company: Ciena Blueplanet
+ license: Apache 2.0
+ min_ansible_version: 2.0
+ platforms:
+ - name: Ubuntu
+ versions:
+ - trusty
+ galaxy_tags:
+ - development
+ - system
+dependencies: []
diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml
new file mode 100644
index 0000000..6e7c5e6
--- /dev/null
+++ b/roles/docker/tasks/main.yml
@@ -0,0 +1,38 @@
+- name: Apt Information
+ become: yes
+ apt: name={{ item }} state=latest force=yes
+ with_items:
+ - apt-transport-https
+ - ca-certificates
+
+- name: Docker Apt Key
+ become: yes
+ apt_key:
+ keyserver: hkp://p80.pool.sks-keyservers.net:80
+ id: 58118E89F3A912897C070ADBF76221572C52609D
+
+- name: Docker repository
+ become: yes
+ apt_repository:
+ repo: deb https://apt.dockerproject.org/repo ubuntu-trusty main
+ update_cache: yes
+ state: present
+
+- name: Docker Engine
+ become: yes
+ apt:
+ name: docker-engine
+ state: latest
+ force: yes
+
+- name: Docker Compose
+ become: yes
+ get_url:
+ url: https://github.com/docker/compose/releases/download/1.4.1/docker-compose-Linux-x86_64
+ dest: /usr/local/bin/docker-compose
+
+- name: Docker Compose Permissions
+ become: yes
+ file:
+ path: /usr/local/bin/docker-compose
+ mode: 0755
diff --git a/roles/fabric-switch/files/connect b/roles/fabric-switch/files/connect
new file mode 100755
index 0000000..a114178
--- /dev/null
+++ b/roles/fabric-switch/files/connect
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+BG=0
+
+while [ $# -gt 0 ]; do
+ case $1 in
+ -bg|-background)
+ BG=1
+ esac
+ shift
+done
+
+if [ $BG -eq 1 ]; then
+ nohup brcm-indigo-ofdpa-ofagent --dpid={{ switch_id }} --controller={{ controller_ip }} 2>&1 > connect.log &
+else
+ brcm-indigo-ofdpa-ofagent --dpid={{ switch_id }} --controller={{ controller_ip }}
+fi
diff --git a/roles/fabric-switch/files/killit b/roles/fabric-switch/files/killit
new file mode 100755
index 0000000..2ed34a1
--- /dev/null
+++ b/roles/fabric-switch/files/killit
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+killall -9 brcm-indigo-ofdpa-ofagent
diff --git a/roles/fabric-switch/files/purge b/roles/fabric-switch/files/purge
new file mode 100755
index 0000000..296a8cf
--- /dev/null
+++ b/roles/fabric-switch/files/purge
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+/usr/bin/ofdpa-i.12.1.1/examples/client_cfg_purge
diff --git a/roles/fabric-switch/files/reset b/roles/fabric-switch/files/reset
new file mode 100755
index 0000000..4f58bd5
--- /dev/null
+++ b/roles/fabric-switch/files/reset
@@ -0,0 +1,6 @@
+#!/bin/bash
+./killit
+./purge
+service ofdpa restart
+./purge
+
diff --git a/roles/fabric-switch/meta/main.yml b/roles/fabric-switch/meta/main.yml
new file mode 100644
index 0000000..0bcac2c
--- /dev/null
+++ b/roles/fabric-switch/meta/main.yml
@@ -0,0 +1,14 @@
+---
+galaxy_info:
+ author: Ciena Blueplanet
+ description: Openflow Agent and Basic Utils for Fabric Switch
+ company: Ciena Blueplanet
+ license: Apache 2.0
+ min_ansible_version: 2.0
+ platforms:
+ - name: Ubuntu
+ versions:
+ - trusty
+ galaxy_tags:
+ - openflow
+dependencies: []
diff --git a/roles/fabric-switch/tasks/main.yml b/roles/fabric-switch/tasks/main.yml
new file mode 100644
index 0000000..326219a
--- /dev/null
+++ b/roles/fabric-switch/tasks/main.yml
@@ -0,0 +1,45 @@
+---
+- name: Verify controller_ip Set
+ fail: msg="Please set variable 'controller_ip'. This can be set via a variable file or via the command line using the '--extra-vars' option."
+ when: controller_ip is not defined
+
+- name: Verify switch_id Set
+ fail: msg="Please set variable 'switch_id'. This can be set via a host specific variable file or via the command line using the '--extra-vars' option."
+ when: switch_id is not defined
+
+- name: Openflow Agent Version
+ shell: ofdpa --version
+ register: ofdpa_version
+ changed_when: false
+
+- name: Version I.12.1.1+1.1 Openflow Agent
+ include: ofdpa.yml
+ when: ofdpa_version.stdout.find('version I.12.1.1+1.1') == -1
+
+- name: Utilities Scripts
+ template:
+ src: files/{{ item }}
+ dest: /root
+ owner: root
+ group: root
+ mode: 0755
+ with_items:
+ - purge
+ - killit
+ - connect
+ - reset
+ register: utils
+
+- name: Mark Persistent
+ command: persist {{ item }}
+ with_items:
+ - purge
+ - killit
+ - connect
+ - reset
+ when: utils.changed
+
+- name: Persist
+ command: savepersist
+ when: utils.changed
+ failed_when: false
diff --git a/roles/fabric-switch/tasks/ofdpa.yml b/roles/fabric-switch/tasks/ofdpa.yml
new file mode 100644
index 0000000..2c643e8
--- /dev/null
+++ b/roles/fabric-switch/tasks/ofdpa.yml
@@ -0,0 +1,22 @@
+---
+- name: Openflow Agent Debian Archive
+ get_url:
+ url: http://github.com/ciena/ZeroTouchProvisioning/raw/master/ofdpa-i.12.1.1_12.1.1%2Baccton1.7-1_amd64.deb
+ validate_certs: false
+ dest: /mnt/flash2/ofdpa-i.12.1.1_12.1.1%2Baccton1.7-1_amd64.deb
+
+- name: OpenFlow Agent Stopped
+ service: name=ofdpa state=stopped
+
+- name: Openflow Agent
+ apt: deb="/mnt/flash2/ofdpa-i.12.1.1_12.1.1%2Baccton1.7-1_amd64.deb" force=true
+
+- name: OpenFlow Agent Started
+ service: name=ofdpa state=started
+
+- name: Mark Persist Openflow Agent
+ command: persist /etc/accton/ofdpa.conf
+
+- name: Persist Openflow Agent
+ command: savepersist
+ failed_when: false
diff --git a/roles/java8-oracle/meta/main.yml b/roles/java8-oracle/meta/main.yml
new file mode 100644
index 0000000..a51875b
--- /dev/null
+++ b/roles/java8-oracle/meta/main.yml
@@ -0,0 +1,15 @@
+---
+galaxy_info:
+ author: Ciena Blueplanet
+ description: Java 8 from Oracle
+ company: Ciena Blueplanet
+ license: Apache 2.0
+ min_ansible_version: 2.0
+ platforms:
+ - name: Ubuntu
+ versions:
+ - trusty
+ galaxy_tags:
+ - development
+ - system
+dependencies: []
diff --git a/roles/java8-oracle/tasks/main.yml b/roles/java8-oracle/tasks/main.yml
new file mode 100644
index 0000000..9397fff
--- /dev/null
+++ b/roles/java8-oracle/tasks/main.yml
@@ -0,0 +1,20 @@
+---
+- name: Install add-apt-repostory
+ become: yes
+ apt: name=software-properties-common state=latest
+
+- name: Add Oracle Java Repository
+ become: yes
+ apt_repository: repo='ppa:webupd8team/java'
+
+- name: Accept Java 8 License
+ become: yes
+ debconf: name='oracle-java8-installer' question='shared/accepted-oracle-license-v1-1' value='true' vtype='select'
+
+- name: Install Oracle Java 8
+ become: yes
+ apt: name={{item}} state=latest
+ with_items:
+ - oracle-java8-installer
+ - ca-certificates
+ - oracle-java8-set-default
diff --git a/roles/maas/files/cord_id_rsa b/roles/maas/files/cord_id_rsa
new file mode 100644
index 0000000..e4a3947
--- /dev/null
+++ b/roles/maas/files/cord_id_rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA4ixakIQFlpSXd3NJ98btbO7Dt0o9ioDl0ZLZa2v1g0dWifn0
+W5gkqo9VSkY2fPwUtgxnyoyVFiEc1MyLNxAkd6UHl+YzFw4dDCrEpu5XDCPCkvky
+dd2P3OiuxmheDqjpklPMWddsWSIA061qSdBQ+pYm9Qq5tfekvOVcAPPkoB22ftPr
+gnOSovvTwHfnZH/gfiI+IxUcWwLkneiUenw5oO+3Jie1Gbn3yFqf3pk+VG6M+8YH
+gZKY0/TBaFBHB+acKN3/yIhnnQzfwRyLX5txnYyaDI0HjVLJQiFK0XKgTGhm4U7C
+mpWkEH6WLF/cOs9+chQDDSwOxRD/VIw6W4sXIwIDAQABAoIBABhQFkg0uPkP7hxc
+G1Z0Xu931zgr1eO+qXXW6GJgz5qWH5pjcT4rY72l/NAoLhFPc9aCDOI8LIaddqD1
+f/2iUZk+90r/5vwSe1LkghFDy721VmRAP4lmEOH5bVhMvderlrgxI+WAf9gxDI+0
+s5lNuHbHj1aGGaKTBXV83mAH18rSUxxPZ+M5xT9RE5uKJwZcRLrqnOI3dleGy7xc
+Pxrtm0v/507DVjRdQzpjcYkndGQXfOqjNLEtwMADwmYCOEF3sqaSSDeqYtg/IOIR
+hiM65ekl7R+bqMaowv82V5NCfdSXKyRLvk4Nr9E9Jji9gPA2bYkx6ASkkWduGzA3
+tYqs5kkCgYEA9y396CV/NQfxGGwRD/lLtpNG/YgzihO+IojLd5RsdTCUxvxB5BCx
+i/KGWisg5PLORBh1UGpyrSIKaoMJXbrSSpQwZri+Xnpjt/qHIT8KWUN4tuiHKluV
+DkWQFkD1aOIZujWEX9J25C/SGICtyCIWp/ylNPX6Kwf/cm8W7A7GPU0CgYEA6j53
+dHW2ia4k0ze0HI0PWaiZoi2jFhzI33le1MMBjRMIS3TW2LajWB3TjFs+AdLb831P
+gQrA76oMQ5KDZ3o2b4NrixwfRAQaBBW2cCsRLkxZEIsoVa3QthT5UihqR/t6G6FB
+u9pl+fK8IsHoc5pKFtOkCD9c/Axyu0m30BWqLi8CgYEAjbEPm8Pi58NlsVpBbaa6
+gC5sw2kQIlau550DBclPYt42atqv6sym+lJMMeQHNzb4hpB+r1pV4mlhDy2OcOxn
+H9lS5Y+BkScXgp9aVvSMOh8zU6Z31RAqocO+lQMnqrfxh4ymFUfQX34KMYGSHOdt
+lV5+VZ2rin9LL43+1dKiUQECgYEAlOCQ4ZbzJjxlMU1lDwRkbjKnOplQ3vv6e3ZT
+XFx4fuZKzlJ7Po+N77I9Qya2mUgf/Xh2cGiaSXjFhKj5FWpqcKORVX/RK1SECHaY
+VmA48jkaHlajkxj+3ssjzyDas9dUO31ZHwDm8V5iTqD5kYfNcQagaZGEEroCraBj
+0EAEwocCgYEAmQ2RumGHjYdSgkxw1MWCAXg1RPBaifZifErhe1MiVkZuHdCxYn3p
+Yv7KPaOQtYfbZEN1Ww3ScWqIRZzOmRdEakFGFh+d1V+qK1r/Bj6SBSOND1UZFm+j
++DeGwmHuPzdNbtoW4dqyFM5OFib3N9P6r87Kfl1X3q31R8gVhK4wtOo=
+-----END RSA PRIVATE KEY-----
diff --git a/roles/maas/files/cord_id_rsa.pub b/roles/maas/files/cord_id_rsa.pub
new file mode 100644
index 0000000..36daa90
--- /dev/null
+++ b/roles/maas/files/cord_id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDiLFqQhAWWlJd3c0n3xu1s7sO3Sj2KgOXRktlra/WDR1aJ+fRbmCSqj1VKRjZ8/BS2DGfKjJUWIRzUzIs3ECR3pQeX5jMXDh0MKsSm7lcMI8KS+TJ13Y/c6K7GaF4OqOmSU8xZ12xZIgDTrWpJ0FD6lib1Crm196S85VwA8+SgHbZ+0+uCc5Ki+9PAd+dkf+B+Ij4jFRxbAuSd6JR6fDmg77cmJ7UZuffIWp/emT5Uboz7xgeBkpjT9MFoUEcH5pwo3f/IiGedDN/BHItfm3GdjJoMjQeNUslCIUrRcqBMaGbhTsKalaQQfpYsX9w6z35yFAMNLA7FEP9UjDpbixcj cord@cord.lab
diff --git a/roles/maas/files/dhcp_harvest.inc b/roles/maas/files/dhcp_harvest.inc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/roles/maas/files/dhcp_harvest.inc
diff --git a/roles/maas/files/dhcpd.blacklist b/roles/maas/files/dhcpd.blacklist
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/roles/maas/files/dhcpd.blacklist
diff --git a/roles/maas/files/dhcpd.conf.template b/roles/maas/files/dhcpd.conf.template
new file mode 100644
index 0000000..ca19b1b
--- /dev/null
+++ b/roles/maas/files/dhcpd.conf.template
@@ -0,0 +1,48 @@
+# WARNING: Do not edit /var/lib/maas/dhcpd.conf yourself. MAAS will overwrite any
+# changes made there.
+#
+# Instead, edit /etc/maas/templates/dhcp/dhcpd.conf.template and your changes
+# will be present whenever MAAS rewrites the DHCP configuration. Update and save
+# the cluster's configuration in MAAS to trigger an update to this file.
+
+include "/etc/dhcp/dhcpd.blacklist";
+
+option arch code 93 = unsigned integer 16; # RFC4578
+option path-prefix code 210 = text; #RFC5071
+{{for dhcp_subnet in dhcp_subnets}}
+subnet {{dhcp_subnet['subnet']}} netmask {{dhcp_subnet['subnet_mask']}} {
+ {{bootloader}}
+ interface "{{dhcp_subnet['interface']}}";
+ ignore-client-uids true;
+ option subnet-mask {{dhcp_subnet['subnet_mask']}};
+ option broadcast-address {{dhcp_subnet['broadcast_ip']}};
+ #{{if dhcp_subnet.get('dns_servers')}}
+ #option domain-name-servers {{dhcp_subnet['dns_servers']}};
+ #{{endif}}
+ option domain-name "{{dhcp_subnet['domain_name']}}";
+ {{if dhcp_subnet['router_ip'] }}
+ option routers {{dhcp_subnet['router_ip']}};
+ option domain-name-servers {{dhcp_subnet['router_ip']}};
+ next-server {{dhcp_subnet['router_ip']}};
+ option dhcp-server-identifier {{dhcp_subnet['router_ip']}};
+ {{endif}}
+ {{if dhcp_subnet.get('ntp_server')}}
+ option ntp-servers {{dhcp_subnet['ntp_server']}};
+ {{endif}}
+ range dynamic-bootp {{dhcp_subnet['ip_range_low']}} {{dhcp_subnet['ip_range_high']}};
+ class "PXE" {
+ match if substring (option vendor-class-identifier, 0, 3) = "PXE";
+ default-lease-time 30;
+ max-lease-time 30;
+ }
+}
+{{endfor}}
+
+include "/etc/dhcp/dhcpd.reservations";
+
+omapi-port 7911;
+key omapi_key {
+ algorithm HMAC-MD5;
+ secret "{{omapi_key}}";
+};
+omapi-key omapi_key;
diff --git a/roles/maas/files/dhcpd.reservations b/roles/maas/files/dhcpd.reservations
new file mode 100644
index 0000000..a7860c3
--- /dev/null
+++ b/roles/maas/files/dhcpd.reservations
@@ -0,0 +1,79 @@
+############################################################################
+## RESERVATIONS
+############################################################################
+
+# RACK1 - Rack with two fabric switches
+host fabric01 {
+ hardware ethernet cc:37:ab:17:7b:c0;
+ fixed-address 10.0.128.100;
+}
+host fabric02 {
+ hardware ethernet 70:72:cf:f5:60:9e;
+ fixed-address 10.0.128.101;
+}
+host cord-r1-s2 {
+ hardware ethernet 2c:60:0c:e3:c4:2d;
+ fixed-address 10.0.128.113;
+}
+host cord-r1-s2-ipmi {
+ hardware ethernet 2c:60:0c:e3:c4:2f;
+ fixed-address 10.0.128.107;
+}
+host cord-r1-s3 {
+ hardware ethernet 2c:60:0c:cb:00:ef;
+ fixed-address 10.0.128.115;
+}
+host cord-r1-s3-ipmi {
+ hardware ethernet 2c:60:0c:cb:00:f1;
+ fixed-address 10.0.128.108;
+}
+host cord-r1-s4 {
+ hardware ethernet 2c:60:0c:cb:00:3b;
+ fixed-address 10.0.128.116;
+}
+host cord-r1-s4-ipmi {
+ hardware ethernet 2c:60:0c:cb:00:3d;
+ fixed-address 10.0.128.110;
+}
+
+# RACK 2 - Rack with full fabric (2 leaf / 2 spine)
+host spine01 {
+ hardware ethernet cc:37:ab:6e:e3:40;
+ fixed-address 192.168.42.238;
+}
+host spine02 {
+ hardware ethernet cc:37:ab:6b:0d:a6;
+ fixed-address 192.168.42.223;
+}
+host leaf01 {
+ hardware ethernet cc:37:ab:6e:e3:c2;
+ fixed-address 192.168.42.221;
+}
+host leaf02 {
+ hardware ethernet cc:37:ab:6e:e4:c6;
+ fixed-address 192.168.42.222;
+}
+host cord-r2-s2 {
+ hardware ethernet 00:25:90:fa:5f:78;
+ fixed-address 10.0.128.113;
+}
+host cord-r2-s2-ipmi {
+ hardware ethernet 00:25:90:ff:a7:97;
+ fixed-address 10.0.128.118;
+}
+host cord-r2-s3 {
+ hardware ethernet 00:25:90:fa:5f:52;
+ fixed-address 10.0.128.115;
+}
+host cord-r2-s3-ipmi {
+ hardware ethernet 00:25:90:ff:a7:3e;
+ fixed-address 10.0.128.121;
+}
+host cord-r2-s4 {
+ hardware ethernet 00:25:90:fa:5f:4e;
+ fixed-address 10.0.128.124;
+}
+host cord-r2-s4-ipmi {
+ hardware ethernet 00:25:90:ff:a7:3c;
+ fixed-address 10.0.128.116;
+}
diff --git a/roles/maas/files/generate_network_config.sh b/roles/maas/files/generate_network_config.sh
new file mode 100755
index 0000000..eb8f7a2
--- /dev/null
+++ b/roles/maas/files/generate_network_config.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+IFACE_MGMT=$1
+NET_MGMT=$2
+NET_BRIDGE=$3
+MGMTBR=$4
+
+ip2int()
+{
+ local a b c d
+ { IFS=. read a b c d; } <<< $1
+ echo $(((((((a << 8) | b) << 8) | c) << 8) | d))
+}
+
+int2ip()
+{
+ local ui32=$1; shift
+ local ip n
+ for n in 1 2 3 4; do
+ ip=$((ui32 & 0xff))${ip:+.}$ip
+ ui32=$((ui32 >> 8))
+ done
+ echo $ip
+}
+
+netmask()
+{
+ local mask=$((0xffffffff << (32 - $1))); shift
+ int2ip $mask
+}
+
+
+broadcast()
+{
+ local addr=$(ip2int $1); shift
+ local mask=$((0xffffffff << (32 -$1))); shift
+ int2ip $((addr | ~mask))
+}
+
+network()
+{
+ local addr=$(ip2int $1); shift
+ local mask=$((0xffffffff << (32 -$1))); shift
+ int2ip $((addr & mask))
+}
+
+first()
+{
+ local addr=$(ip2int $1)
+ addr=`expr $addr + 1`
+ int2ip $addr
+}
+
+MBITS=`echo "$NET_MGMT" | cut -d/ -f2`
+MNETW=`echo "$NET_MGMT" | cut -d/ -f1`
+MMASK=`netmask $MBITS`
+MHOST=`first $MNETW`
+
+BBITS=`echo "$NET_BRIDGE" | cut -d/ -f2`
+BNETW=`echo "$NET_BRIDGE" | cut -d/ -f1`
+BMASK=`netmask $BBITS`
+BHOST=`first $BNETW`
+
+OUT=$(mktemp -u)
+cat /etc/network/interfaces | awk '/## CORD - DO NOT EDIT BELOW THIS LINE/{exit};1' | awk "/^auto / { if (\$2 == \"${IFACE_MGMT}\") { IN=1 } else {IN=0} } /^iface / { if (\$2 == \"${IFACE_MGMT}\") { IN=1 } else {IN=0}} /^#/ || /^\s*\$/ { IN=0 } IN==0 {print} IN==1 { print \"#\" \$0 }" > $OUT
+
+cat <<EOT >> $OUT
+## CORD - DO NOT EDIT BELOW THIS LINE
+
+auto ${IFACE_MGMT}
+iface ${IFACE_MGMT} inet static
+ address ${MHOST}
+ network ${MNETW}
+ netmask ${MMASK}
+ gateway ${MHOST}
+
+auto ${MGMTBR}
+iface ${MGMTBR} inet static
+ address ${BHOST}
+ network ${BNETW}
+ netmask ${BMASK}
+ gateway ${BHOST}
+EOT
+
+diff /etc/network/interfaces $OUT 2>&1 > /dev/null
+if [ $? -ne 0 ]; then
+ cp /etc/network/interfaces /etc/network/interfaces.last
+ cp $OUT /etc/network/interfaces
+ echo -n "true"
+else
+ echo -n "false"
+fi
+
+rm $OUT
diff --git a/roles/maas/files/mappings.json b/roles/maas/files/mappings.json
new file mode 100644
index 0000000..9dc033d
--- /dev/null
+++ b/roles/maas/files/mappings.json
@@ -0,0 +1,20 @@
+{
+ "2c:60:0c:e3:c4:2d":{
+ "hostname":"cord-r1-s2"
+ },
+ "2c:60:0c:cb:00:ef":{
+ "hosname":"cord-r1-s3"
+ },
+ "2c:60:0c:cb:00:3b":{
+ "hostname":"cord-r1-s4"
+ },
+ "00:25:90:fa:5f:78":{
+ "hostname":"cord-r2-s2"
+ },
+ "00:25:90:fa:5f:52":{
+ "hostname":"cord-r2-s3"
+ },
+ "00:25:90:fa:5f:4e":{
+ "hostname":"cord-r2-s4"
+ }
+}
diff --git a/roles/maas/files/named.conf.options.inside.maas b/roles/maas/files/named.conf.options.inside.maas
new file mode 100644
index 0000000..ad87061
--- /dev/null
+++ b/roles/maas/files/named.conf.options.inside.maas
@@ -0,0 +1,9 @@
+forwarders {
+ 8.8.8.8;
+};
+
+dnssec-validation auto;
+
+allow-query { any; };
+allow-recursion { trusted; };
+allow-query-cache { trusted; };
diff --git a/roles/maas/files/update_dns_template.sh b/roles/maas/files/update_dns_template.sh
new file mode 100755
index 0000000..448d3fc
--- /dev/null
+++ b/roles/maas/files/update_dns_template.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+LSUB=$1
+DOMAIN=$2
+
+ip2int() {
+ local a b c d
+ { IFS=. read a b c d; } <<< $1
+ echo $(((((((a << 8) | b) << 8) | c) << 8) | d))
+}
+
+int2ip() {
+ local ui32=$1; shift
+ local ip n
+ for n in 1 2 3 4; do
+ ip=$((ui32 & 0xff))${ip:+.}$ip
+ ui32=$((ui32 >> 8))
+ done
+ echo $ip
+}
+
+netmask() {
+ local mask=$((0xffffffff << (32 - $1))); shift
+ int2ip $mask
+}
+
+
+broadcast() {
+ local addr=$(ip2int $1); shift
+ local mask=$((0xffffffff << (32 -$1))); shift
+ int2ip $((addr | ~mask))
+}
+
+network() {
+ local addr=$(ip2int $1); shift
+ local mask=$((0xffffffff << (32 -$1))); shift
+ int2ip $((addr & mask))
+}
+
+first() {
+ local addr=$(ip2int $1)
+ addr=`expr $addr + 1`
+ int2ip $addr
+}
+
+LBITS=`echo "$LSUB" | cut -d/ -f2`
+LNETW=` echo "$LSUB" | cut -d/ -f1`
+LMASK=`netmask $LBITS`
+LHOST=`first $LNETW`
+
+DEST=/etc/maas/templates/dns/zone.template
+OUT=$(mktemp -u)
+cat /tmp/zone.template | awk '/; CORD - DO NOT EDIT BELOW THIS LINE/{exit};1' | awk "/^auto / { if (\$2 == \"${IFACE_MGMT}\") { IN=1 } else {IN=0} } /^iface / { if (\$2 == \"${IFACE_MGMT}\") { IN=1 } else {IN=0}} /^#/ || /^\s*\$/ { IN=0 } IN==0 {print} IN==1 { print \"#\" \$0 }" > $OUT
+
+cat <<EOT >> $OUT
+; CORD - DO NOT EDIT BELOW THIS LINE
+{{if domain == '$DOMAIN'}}
+\$INCLUDE "/etc/bind/maas/dhcp_harvest.inc"
+$HOSTNAME IN A $LHOST
+{{endif}}
+EOT
+
+diff $DEST $OUT 2>&1 > /dev/null
+if [ $? -ne 0 ]; then
+ cp $DEST $DEST.last
+ cp $OUT $DEST
+ echo -n "true"
+else
+ echo -n "false"
+fi
+
+rm $OUT
diff --git a/roles/maas/files/zone.template b/roles/maas/files/zone.template
new file mode 100644
index 0000000..f3a22fc
--- /dev/null
+++ b/roles/maas/files/zone.template
@@ -0,0 +1,25 @@
+; Zone file modified: {{modified}}.
+; Note that the modification time of this file doesn't reflect
+; the actual modification time. MAAS controls the modification time
+; of this file to be able to force the zone to be reloaded by BIND.
+$TTL 300
+@ IN SOA {{domain}}. nobody.example.com. (
+ {{serial}} ; serial
+ 600 ; Refresh
+ 1800 ; Retry
+ 604800 ; Expire
+ 300 ; TTL
+ )
+
+ IN NS {{domain}}.
+{{for type, directive in generate_directives.items()}}
+{{for iterator_values, rdns, hostname in directive}}
+$GENERATE {{iterator_values}} {{rdns}} IN {{type}} {{hostname}}
+{{endfor}}
+{{endfor}}
+
+{{for type, mapping in mappings.items()}}
+{{for item_from, item_to in mapping}}
+{{item_from}} IN {{type}} {{item_to}}
+{{endfor}}
+{{endfor}}
diff --git a/roles/maas/meta/main.yml b/roles/maas/meta/main.yml
new file mode 100644
index 0000000..bc5468b
--- /dev/null
+++ b/roles/maas/meta/main.yml
@@ -0,0 +1,14 @@
+---
+galaxy_info:
+ author: David Bainbridge
+ description: Ubuntu MAAS from Canonical
+ min_ansible_version: 2
+ platforms:
+ - name: Ubuntu
+ versions:
+ - trusty
+ categories:
+ - development
+ - system
+
+dependencies: [docker]
diff --git a/roles/maas/tasks/main.yml b/roles/maas/tasks/main.yml
new file mode 100644
index 0000000..601154b
--- /dev/null
+++ b/roles/maas/tasks/main.yml
@@ -0,0 +1,193 @@
+---
+- name: Install Prerequisites
+ become: yes
+ apt: name={{ item }} state=latest
+ with_items:
+ - git
+ - bridge-utils
+ - curl
+ - python-pycurl
+ - python-pip
+ - ethtool
+
+- name: Install Python Prerequisites
+ become: yes
+ pip: name={{ item }} state=latest
+ with_items:
+ - docker-py
+
+- name: Stop MAAS Automation Container
+ become: yes
+ docker:
+ name: automation
+ image: cord/maas-automation:0.1-prerelease
+ state: absent
+
+- name: Stop DHCP Harvester Container
+ become: yes
+ docker:
+ name: harvester
+ image: cord/maas-dhcp-harvester:0.1-prerelease
+ state: absent
+
+- name: MAAS Repository
+ become: yes
+ apt_repository:
+ repo: ppa:maas/stable
+ update_cache: yes
+ state: present
+
+- name: MAAS
+ become: yes
+ apt:
+ name: maas
+ state: latest
+
+- name: MAAS Configuration Directory
+ become: yes
+ file:
+ path: /etc/maas
+ owner: maas
+ group: maas
+ mode: 0755
+ state: directory
+
+- name: Host Name Mapping File
+ become: yes
+ copy:
+ src: files/mappings.json
+ dest: /etc/maas/mappings.json
+ owner: maas
+ group: maas
+ mode: 0644
+
+- name: Verify MAAS admin User
+ become: yes
+ shell: maas-region-admin apikey --username=admin 2>/dev/null | wc -l
+ register: maas_admin_user_exists
+ changed_when: false
+
+- name: MAAS admin User
+ become: yes
+ command: maas-region-admin createadmin --username=admin --password=admin --email={{ maas.admin_email }}
+ when: maas_admin_user_exists.stdout == '0'
+
+- name: Verify MAAS User
+ become: yes
+ shell: maas-region-admin apikey --username={{ maas.user }} 2>/dev/null | wc -l
+ register: maas_user_exists
+ changed_when: false
+
+- name: MAAS User
+ become: yes
+ command: maas-region-admin createadmin --username={{ maas.user }} --password={{ maas.user_password }} --email={{ maas.user_email }}
+ when: maas_user_exists.stdout == '0'
+
+- name: MAAS User API Key
+ become: yes
+ command: maas-region-admin apikey --username={{ maas.user }}
+ register: apikey
+ changed_when: false
+
+- name: Verify Default Virsh Network
+ shell: virsh net-list | grep default | wc -l
+ register: virsh_default_network_exists
+ changed_when: false
+
+- name: Default Virsh Network Absent
+ become: yes
+ command: virsh net-destroy default
+ when: virsh_default_network_exists.stdout != '0'
+
+- name: Network Configuration
+ become: yes
+ script: files/generate_network_config.sh {{ interfaces.management }} {{ networks.management }} {{ networks.bridge }} {{ networks.bridge_name }}
+ register: network_config_changed
+ changed_when: network_config_changed.stdout == 'true'
+
+- name: Network Masquerading (NAT)
+ become: yes
+ template:
+ src: templates/nat.j2
+ dest: /etc/network/if-pre-up.d/nat
+ owner: root
+ group: root
+ mode: 0755
+
+- name: Activate Masquerading (NAT)
+ become: yes
+ command: /etc/network/if-pre-up.d/nat report-changed
+ register: masq_changed
+ changed_when: masq_changed.stdout == 'true'
+
+- name: VM Bridge
+ become: yes
+ template:
+ src: templates/create_bridge.j2
+ dest: /etc/network/if-pre-up.d/create_bridge_{{ networks.bridge_name }}
+ owner: root
+ group: root
+ mode: 0755
+
+- name: Activate VM Bridge
+ become: yes
+ command: /etc/network/if-pre-up.d/create_bridge_{{ networks.bridge_name }} report-changed
+ register: bridge_changed
+ changed_when: bridge_changed.stdout == 'true'
+
+- name: Management Interface
+ become: yes
+ shell: ifdown {{ interfaces.management }} && ifup {{ interfaces.management }}
+ when: network_config_changed.stdout == 'true'
+
+- name: Management Interface IP Address
+ shell: ifconfig {{ interfaces.management }} 2>&1 | grep "inet addr:" | sed -e 's/.*:\([.0-9]*\)[ ]*Bcast.*/\1/g'
+ register: mgmt_ip_address
+ changed_when: false
+
+- name: Switch Boot Resources
+ copy:
+ src=files/{{ item }}
+ dest=/var/www/html/{{ item }}
+ owner=root
+ group=root
+ mode=0644
+ with_items:
+ - onie-installer-x86_64-accton_as5712_54x-r0
+ - onie-installer-x86_64-accton_as6712_32x-r0
+
+- name: Wait for MAAS to Intialize (start)
+ pause:
+ seconds=30
+ changed_when: false
+
+- name: Configure MAAS
+ become: yes
+ command: docker run -ti ciena/cord-maas-bootstrap:0.1-prerelease --apikey='{{apikey.stdout}}' --sshkey='{{maas.user_sshkey}}' --url='http://{{mgmt_ip_address.stdout}}/MAAS/api/1.0' --network='{{networks.management}}' --interface='{{interfaces.management}}' --zone='administrative' --cluster='Cluster master' --domain='{{maas.domain}}' --bridge='{{networks.bridge_name}}' --bridge-subnet='{{networks.bridge}}'
+ register: maas_config_result
+ changed_when: maas_config_result.stdout.find("CHANGED") != -1
+ failed_when: "'ERROR' in maas_config_result.stdout"
+
+- name: Custom MAAS Configuration Template
+ become: yes
+ copy:
+ src: files/{{ item.src }}
+ dest: "{{ item.dest }}"
+ owner: maas
+ group: maas
+ mode: 0644
+ with_items:
+ - { src: 'dhcpd.blacklist', dest: '/etc/dhcp' }
+ - { src: 'dhcpd.reservations', dest: '/etc/dhcp' }
+ - { src: 'dhcp_harvest.inc', dest: '/etc/bind/maas' }
+ - { src: 'named.conf.options.inside.maas', dest: '/etc/bind/maas' }
+ - { src: 'dhcpd.conf.template', dest: '/etc/maas/templates/dhcp' }
+ - { src: 'dhcp_harvest.inc', dest: '/etc/maas/templates/dns' }
+ - { src: 'zone.template', dest: '/tmp' }
+
+- name: Custom DNS Zone Template
+ become: yes
+ script: files/update_dns_template.sh {{ networks.management }} {{ maas.domain }}
+ register: dns_template_changed
+ changed_when: dns_template_changed.stdout == 'true'
+
diff --git a/roles/maas/templates/automation-compose.yml.j2 b/roles/maas/templates/automation-compose.yml.j2
new file mode 100644
index 0000000..1d8d751
--- /dev/null
+++ b/roles/maas/templates/automation-compose.yml.j2
@@ -0,0 +1,13 @@
+automation:
+ image: ciena/cord-maas-automation:0.1-prerelease
+ container_name: automation
+ labels:
+ - "lab.solution=CORD"
+ - "lab.component=automation"
+ restart: always
+ environment:
+ # need to explicitly set the resolver, else go will skip the /etc/hosts file
+ - "GODEBUG=netdns=go"
+ volumes:
+ - ".:/mappings"
+ command: [ "-apiVersion", "1.0", "-apikey", "{{ apikey.stdout }}", "-maas", "http://{{ ip_address.stdout }}/MAAS", "-period", "30s", "-mappings", "@/mappings/mappings.json", "-always-rename" ]
diff --git a/roles/maas/templates/create_bridge.j2 b/roles/maas/templates/create_bridge.j2
new file mode 100755
index 0000000..5f12261
--- /dev/null
+++ b/roles/maas/templates/create_bridge.j2
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+REPORT_CHANGED=0
+if [ $# -gt 0 ]; then
+ REPORT_CHANGED=1
+fi
+CHANGED='false'
+
+FOUND=$(brctl show | grep "^{{ networks.bridge_name }}" | wc -l)
+if [ $FOUND -eq 0 ]; then
+ CHANGED='true'
+ brctl addbr {{ networks.bridge_name }}
+fi
+
+if [ $REPORT_CHANGED -ne 0 ]; then
+ echo -n $CHANGED
+fi
diff --git a/roles/maas/templates/harvest-compose.yml.j2 b/roles/maas/templates/harvest-compose.yml.j2
new file mode 100644
index 0000000..b19b0b1
--- /dev/null
+++ b/roles/maas/templates/harvest-compose.yml.j2
@@ -0,0 +1,15 @@
+harvester:
+ image: ciena/cord-maas-dhcp-harvester:0.1-prerelease
+ container_name: harvester
+ restart: always
+ labels:
+ - "lab.solution=cord"
+ - "lab.component=harvester"
+ volumes:
+ - "/var/lib/maas/dhcp:/dhcp"
+ - "/etc/bind/maas:/bind"
+ - "/etc/bind/maas:/key"
+ - "/etc/dhcp:/etc/dhcp"
+ ports:
+ - "8954:8954"
+ command: [ "--server", "{{ ip_address.stdout }}", "--port", "954", "--key", "/key/rndc.conf.maas", "--zone", "cord.lab", "--update", "--verify", "--timeout", "1s", "--repeat", "5m", "--quiet", "2s", "--workers", "10", "--filter", "^(?!cord)" ]
diff --git a/roles/maas/templates/nat.j2 b/roles/maas/templates/nat.j2
new file mode 100755
index 0000000..2a540be
--- /dev/null
+++ b/roles/maas/templates/nat.j2
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Add rules to configure NAT
+
+REPORT_CHANGED=0
+if [ $# -gt 0 ]; then
+ REPORT_CHANGED=1
+fi
+CHANGED='false'
+
+iptables --table nat --check POSTROUTING --out-interface {{ interfaces.external }} -j MASQUERADE &>> /dev/null
+if [ $? -ne 0 ]; then
+ iptables --table nat --append POSTROUTING --out-interface {{ interfaces.external }} -j MASQUERADE
+ CHANGED='true'
+fi
+
+iptables --check FORWARD --in-interface {{ interfaces.management }} -j ACCEPT &>> /dev/null
+if [ $? -ne 0 ]; then
+ iptables --append FORWARD --in-interface {{ interfaces.management }} -j ACCEPT
+ CHANGED='true'
+fi
+
+if [ $REPORT_CHANGED -ne 0 ]; then
+ echo -n $CHANGED
+fi
diff --git a/roles/maas/vars/main.yml b/roles/maas/vars/main.yml
new file mode 100644
index 0000000..f392709
--- /dev/null
+++ b/roles/maas/vars/main.yml
@@ -0,0 +1,38 @@
+maas:
+ admin_email: admin@cord.lab
+ user: cord
+ user_password: cord
+ user_email: cord@cord.lab
+ user_sshkey: "{{ lookup('file', 'files/cord_id_rsa.pub') }}"
+
+ # CHANGE:
+ # 'domain' specifies the domain name configured in to MAAS
+ domain: cord.lab
+
+interfaces:
+ # CHANGE:
+ # 'external' specifies the interface on which the head node is
+ # connected to the internet
+ # 'management' specifies the interface on which the head node will
+ # service DHCP and PXE boot requests
+ external: eth3
+ management: eth2
+
+networks:
+ # CHANGE:
+ # 'management' specifies the network that MAAS will allocate
+ # via DHCP over the 'management' interface specified
+ # above
+ # 'bridge' specifies the network that MAAS will allocate
+ # via DHCP to the VM created in support of XOS
+ # on the head node.
+ # 'fabric' specifies the network that will be assigned to
+ # the leaf - spine fabric
+ management: 10.6.0.0/24
+ bridge: 172.18.0.0/24
+ fabric: 10.6.1.0/24
+
+ # CHANGE:
+ # 'bridge' name of the bride to create that is used when connecting
+ # the VMs created in support of XOS
+ bridge_name: mgmtbr
diff --git a/roles/onos-fabric/files/bin/minify b/roles/onos-fabric/files/bin/minify
new file mode 100755
index 0000000..b91023c
--- /dev/null
+++ b/roles/onos-fabric/files/bin/minify
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+PROG=$(basename $0)
+
+usage() {
+ echo "$PROG: [options]"
+ echo ""
+ echo " -h | --help display this message"
+}
+
+FILE=
+while [ $# -gt 0 ]; do
+ case $1 in
+ -h|--help)
+ usage
+ exit
+ ;;
+ *)
+ FILE=$1
+ ;;
+ esac
+ shift
+done
+
+if [ "$FILE x" == " x" ]; then
+ sed -e 's|//.*$||g' -e '/^\s*$/d' # <&0
+else
+ cat $FILE | sed -e 's|//.*$||g' -e '/^\s*$/d'
+fi
diff --git a/roles/onos-fabric/files/bin/onos-cfg-delete b/roles/onos-fabric/files/bin/onos-cfg-delete
new file mode 100755
index 0000000..4404b5c
--- /dev/null
+++ b/roles/onos-fabric/files/bin/onos-cfg-delete
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+curl -slL -X DELETE --header "Accept: application/json" "http://karaf:karaf@localhost:8181/onos/v1/network/configuration" $*
+
diff --git a/roles/onos-fabric/files/bin/onos-cfg-get b/roles/onos-fabric/files/bin/onos-cfg-get
new file mode 100755
index 0000000..8d0cabf
--- /dev/null
+++ b/roles/onos-fabric/files/bin/onos-cfg-get
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+curl -sLl -X GET --header "Accept: application/json" "http://karaf:karaf@localhost:8181/onos/v1/network/configuration" | python -m json.tool
+
diff --git a/roles/onos-fabric/files/bin/onos-cfg-post b/roles/onos-fabric/files/bin/onos-cfg-post
new file mode 100755
index 0000000..328b8b0
--- /dev/null
+++ b/roles/onos-fabric/files/bin/onos-cfg-post
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+cat $1 | sed -e 's|//.*$||g' -e '/^\s*$/d' | curl -slL -X POST --header "Content-Type: application/json" --header "Accept: application/json" -d "@-" "http://karaf:karaf@localhost:8181/onos/v1/network/configuration"
diff --git a/roles/onos-fabric/files/bin/ping-test.sh b/roles/onos-fabric/files/bin/ping-test.sh
new file mode 100755
index 0000000..d7b894e
--- /dev/null
+++ b/roles/onos-fabric/files/bin/ping-test.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+HOSTS="10.3.1.1 10.3.1.2 10.3.2.1 10.3.2.2 10.3.1.254 10.3.2.254 192.168.10.1 8.8.8.8"
+
+ME=$(ifconfig | grep "10\.3\.[0-9]\.[0-9]" | sed -e 's/.*addr:\(10\.3\.[0-9]\.[0-9]\).*/\1/g' 2> /dev/null)
+echo "FROM: $ME"
+for TO in $HOSTS; do
+ T=$(ping -q -c 1 -W 1 -I eth0 $TO | grep rtt | awk '{print $4}' | sed -e 's|/| |g') #sed -e 's|r| |')
+ echo "$TO: $T" | awk '{printf(" %-15s %-7s\n", $1, $2, $3, $4)}'
+done
diff --git a/roles/onos-fabric/files/bin/restart-vms.sh b/roles/onos-fabric/files/bin/restart-vms.sh
new file mode 100755
index 0000000..ef14e5f
--- /dev/null
+++ b/roles/onos-fabric/files/bin/restart-vms.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+function verify {
+ local L=$1
+ for i in $L; do
+ grep $i /etc/bind/maas/dhcp_harvest.inc > /dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ echo "0"
+ return
+ fi
+ done
+ echo "1"
+}
+
+for i in $(uvt-kvm list); do
+ virsh start $i
+done
+
+LIST=$(uvt-kvm list)
+CNT=$(uvt-kvm list | wc -l)
+# plus 4 for the switches
+
+RETRY=5
+VERIFIED=0
+while [ $VERIFIED -ne 1 -a $RETRY -gt 0 ]; do
+ echo "INFO: Waiting for VMs to start"
+ sleep 5
+ curl -slL -XPOST http://127.0.0.1:8954/harvest >> /dev/null
+ VERIFIED=$(verify $LIST)
+ RETRY=$(expr $RETRY - 1)
+ echo "INFO: Verifing all VMs started"
+done
+
+if [ $VERIFIED -ne 1 ]; then
+ echo "ERROR: Likely VMs did not all boot correctly"
+ exit 1
+else
+ echo "INFO: Looks like all VM started correctly"
+fi
diff --git a/roles/onos-fabric/meta/main.yml b/roles/onos-fabric/meta/main.yml
new file mode 100644
index 0000000..bf39d8c
--- /dev/null
+++ b/roles/onos-fabric/meta/main.yml
@@ -0,0 +1,15 @@
+---
+galaxy_info:
+ author: Ciena Blueplanet
+ description: Docker Engine and Docker Compose
+ company: Ciena Blueplanet
+ license: Apache 2.0
+ min_ansible_version: 2.0
+ platforms:
+ - name: Ubuntu
+ versions:
+ - trusty
+ galaxy_tags:
+ - development
+ - system
+dependencies: []
diff --git a/roles/onos-fabric/tasks/main.yml b/roles/onos-fabric/tasks/main.yml
new file mode 100644
index 0000000..b2bccc5
--- /dev/null
+++ b/roles/onos-fabric/tasks/main.yml
@@ -0,0 +1,45 @@
+---
+- name: User Local bin directory
+ file:
+ path={{ ansible_env.HOME }}/bin
+ state=directory
+ owner=ubuntu
+ group=ubuntu
+ mode=0755
+
+- name: Copy Utility Commands
+ copy:
+ src=files/bin/{{ item }}
+ dest={{ ansible_env.HOME }}/bin
+ owner=ubuntu
+ group=ubuntu
+ mode=0755
+ with_items:
+ - minify
+ - onos-cfg-get
+ - onos-cfg-post
+ - onos-cfg-delete
+ - ping-test.sh
+
+- name: Include Utility Commands in User Path
+ lineinfile:
+ dest={{ ansible_env.HOME }}/.bashrc
+ line="PATH=$HOME/bin:$PATH"
+ state=present
+ insertafter=EOF
+
+- name: Custom ONOS
+ unarchive:
+ src=files/onos-1.6.0.ubuntu.tar.gz
+ dest={{ ansible_env.HOME }}
+ owner=ubuntu
+ group=ubuntu
+
+- name: ONOS Fabric Configuration
+ template:
+ src=templates/fabric-network-config.json.j2
+ dest={{ ansible_env.HOME }}/fabric-network.config.json
+ owner=ubuntu
+ group=ubuntu
+ mode=0644
+
diff --git a/roles/onos-fabric/templates/fabric-network-config.json.j2 b/roles/onos-fabric/templates/fabric-network-config.json.j2
new file mode 100644
index 0000000..f39cf60
--- /dev/null
+++ b/roles/onos-fabric/templates/fabric-network-config.json.j2
@@ -0,0 +1,218 @@
+// This is a commented JSON data file. The comments must stripped from the
+// file before passing it to a JSON parser. This can be done various way
+// including the JavaScript JSON.minify() function or a simple sed script
+// such as "catting" this file through sed -e '|s//.*$||g'.
+
+
+// This file represents the network configuration for the cord demo pod number
+// two (2).
+{
+ "ports" : {
+
+ // Leaf-1/port-1 connected to cord-r2-s1/eth0
+ "of:0000000000000021/1" : {
+ "interfaces" : [
+ {
+ "ips" : [ "10.3.1.254/24" ] // Represents a fake gateway
+ // for this subnet. ONOS will
+ // ARP this and respond.
+ }
+ ]
+ },
+
+ // Leaf-1/port-2 connected to cord-r2-s2/eth0
+ "of:0000000000000021/2" : {
+ "interfaces" : [
+ {
+ "ips" : [ "10.3.1.254/24" ] // Represents a fake gateway
+ // for this subnet. ONOS will
+ // ARP this and respond.
+ },
+ {
+ "vlan" : "1000" // cross-connect s-tag 1000 to Tibit OLT
+ },
+ // Need to specify the public IP for the vSG
+ {
+ "ips" : [ "10.3.1.130/32" ] // vSG public IP address /32
+ }
+ ]
+ },
+
+ // Leaf-1/port-129 connected to Tibit OLT
+ // The physical port is port 25, but we are using a break out cable and thus the switch
+ // create virtual ports
+ "of:0000000000000021/129" : {
+ "interfaces" : [
+ {
+ "name" : "tibit-olt", // unused
+ "vlan" : "1000" // cross-connect s-tag 42 to vSG
+ }
+ ]
+ },
+
+ // Leaf-2/port-3 connected to cord-r2-s3/eth0
+ "of:0000000000000022/3" : {
+ "interfaces" : [
+ {
+ "ips" : [ "10.3.2.254/24" ] // Represents a fake gateway
+ // for this subnet. ONOS will
+ // ARP this and respond.
+ }
+ ]
+ },
+
+ // Leaf-2/port-4 connected to cord-r2-s4/eth0
+ "of:0000000000000022/4" : {
+ "interfaces" : [
+ {
+ "ips" : [ "10.3.2.254/24" ] // Represents a fake gateway
+ // for this subnet. ONOS will
+ // ARP this and respond.
+ }
+ ]
+ }
+ },
+
+ "devices" : {
+ "of:0000000000000021" : {
+ "segmentrouting" : {
+ "name" : "leaf-1",
+ "nodeSid" : 101,
+ "routerIp" : "10.3.1.254",
+ "routerMac" : "cc:37:ab:7c:b9:d6",
+ "isEdgeRouter" : true,
+ "adjacencySids" : []
+ }
+ },
+ "of:0000000000000022" : {
+ "segmentrouting" : {
+ "name" : "leaf-2",
+ "nodeSid" : 102,
+ "routerIp" : "10.3.2.254",
+ "routerMac" : "cc:37:ab:7c:ba:da",
+ "isEdgeRouter" : true,
+ "adjacencySids" : []
+ }
+ },
+ "of:0000000000000011" : {
+ "segmentrouting" : {
+ "name" : "spine-1",
+ "nodeSid" : 103,
+ "routerIp" : "10.2.30.1",
+ "routerMac" : "cc:37:ab:7c:be:68",
+ "isEdgeRouter" : false,
+ "adjacencySids" : []
+ }
+ },
+ "of:0000000000000012" : {
+ "segmentrouting" : {
+ "name" : "spine-2",
+ "nodeSid" : 104,
+ "routerIp" : "10.2.30.2",
+ "routerMac" : "cc:37:ab:7c:bf:ee",
+ "isEdgeRouter" : false,
+ "adjacencySids" : []
+ }
+ }
+ },
+ "links": {
+ // spine 1/1 connected to leaf 1/31
+ "of:0000000000000011/1-of:0000000000000021/31": {
+ "basic": {}
+ },
+
+ // spine 1/2 connected to leaf 2/31
+ "of:0000000000000011/2-of:0000000000000022/31": {
+ "basic": {}
+ },
+
+ // spine 2/1 connected to leaf 1/32
+ "of:0000000000000012/1-of:0000000000000021/32": {
+ "basic": {}
+ },
+
+ // spine 2/2 connected to leaf 2/32
+ "of:0000000000000012/2-of:0000000000000022/32": {
+ "basic": {}
+ },
+
+ // leaf 1/31 connected to spine 1/1
+ "of:0000000000000021/31-of:0000000000000011/1": {
+ "basic": {}
+ },
+
+ // leaf 1/32 connected to spine 2/1
+ "of:0000000000000021/32-of:0000000000000012/1": {
+ "basic": {}
+ },
+
+ // leaf 2/31 connected to spine 1/2
+ "of:0000000000000022/31-of:0000000000000011/2": {
+ "basic": {}
+ },
+
+ // leaf 2/23 connected to spine 2/2
+ "of:0000000000000022/32-of:0000000000000012/2": {
+ "basic": {}
+ }
+ },
+ "hosts" : {
+ // cord-r2-s1 iface eth0
+ "3c:fd:fe:9e:93:10/-1" : {
+ "basic": {
+ "ips": ["10.3.1.1"], // host IP on fabric
+ "location": "of:0000000000000021/1" // link back to fabric leaf-1/port-1
+ }
+ },
+
+ // cord-r2-s2 iface eth0
+ "3c:fd:fe:9e:8a:88/-1" : {
+ "basic": {
+ "ips": ["10.3.1.2"], // host IP on fabric
+ "location": "of:0000000000000021/2" // link back to fabric leaf-1/port-2
+ }
+ },
+
+ // fa:16:3e:94:7e:c5
+ // fa:16:3e:94:7e:c5
+ // OLD: 02:42:0a:03:01:82
+
+ "fa:16:3e:94:7e:c5/-1" : { // vSG1
+ "basic": {
+ "ips": ["10.3.1.130"], // vSG1 public IP address
+ "location": "of:0000000000000001/5"
+ }
+ },
+
+ // OLD: 02:42:0a:03:01:83
+ "fa:16:3e:91:82:6a/-1" : { // vSG1 VM
+ "basic" : {
+ "ips": ["10.3.1.131"],
+ "location": "of:0000000000000001/5"
+ }
+ },
+
+ // cord-r2-s3 iface eth0
+ "3c:fd:fe:9e:94:98/-1" : {
+ "basic": {
+ "ips": ["10.3.2.1"], // host IP on fabric
+ "location": "of:0000000000000022/3" // link back to fabric leaf-2/port-3
+ }
+ },
+
+ // cord-r2-s4 iface eth0
+ "3c:fd:fe:9e:97:98/-1" : {
+ "basic": {
+ "ips": ["10.3.2.2"], // host IP on fabric
+ "location": "of:0000000000000022/4" // link back to fabric leaf-2/port-4
+ }
+ }
+ },
+ "apps" : {
+ "org.onosproject.core" : {
+ "core" : {
+ "linkDiscoveryMode" : "STRICT" // enable strict link validation
+ }
+ }
+ }
+}
diff --git a/roles/onos-fabric/vars/main.yml b/roles/onos-fabric/vars/main.yml
new file mode 100644
index 0000000..457d453
--- /dev/null
+++ b/roles/onos-fabric/vars/main.yml
@@ -0,0 +1,20 @@
+---
+fabric:
+ network: 10.6.1
+ spine1:
+ of_id: of:0000000000000011
+ spine2:
+ of_id: of:0000000000000012
+ leaf1:
+ of_id: of:0000000000000021
+ leaf2:
+ of_id: of:0000000000000022
+ hosts:
+ cord-r6-s1:
+ mac: 3c:fd:fe:9e:94:30
+ ip: 10.6.1.1
+ location:
+ leaf: 1
+ port: 1
+
+