CORD-272 exposed micro services ports on head node and add simply cord command scripts

Change-Id: I43755761c983707f42b1955819ac6234896e55d9
diff --git a/roles/compute-node/files/remove-xos-components b/roles/compute-node/files/remove-xos-components
index d696dda..1b6fc42 100755
--- a/roles/compute-node/files/remove-xos-components
+++ b/roles/compute-node/files/remove-xos-components
@@ -19,7 +19,7 @@
 UVT=$(which uvt-kvm)
 test -z $UVT || uvt-kvm list | xargs uvt-kvm destroy
 
-sudo apt-get remove --purge -y $(dpkg --get-selections | grep "nagioas\|juju\|nova\|neutron" | awk '{print $1}') &&sudo apt-get autoremove -y && sudo rm -rf /etc/juju /etc/neutron /home/ubuntu/.juju && sudo find / -name "*juju*" -exec rm -r \{\} \;
+sudo apt-get remove --purge -y $(dpkg --get-selections | grep "nagioas\|juju\|nova\|neutron" | awk '{print $1}') &&sudo apt-get autoremove -y && sudo rm -rf /etc/juju /etc/neutron /home/ubuntu/.juju && sudo find / -name "*juju*" -exec rm -r \{\} \; && sudo rm -f /var/lib/uvtool/libvirt/images/*
 
 OVS=$(which ovs-vsctl)
 
diff --git a/roles/head-node/files/commands/cord b/roles/head-node/files/commands/cord
new file mode 100755
index 0000000..0d63dc8
--- /dev/null
+++ b/roles/head-node/files/commands/cord
@@ -0,0 +1,119 @@
+#!/bin/bash
+
+PROG=$(basename $0)
+
+usage() {
+    echo "$PROG [-s|--server <head-node-ip>] [-p|--port <head-node-ssh-port>] [-u|--user <head-node-ssh-user>] <command> <options>"
+
+    # Find all files in path that match the pattern "cord-*"
+    ALL_FILES=
+    for DIR in $(echo $PATH | sed -e 's/:/ /g'); do
+        ALL_FILES="$ALL_FILES $(/bin/ls -1 $DIR/cord-* 2> /dev/null)"
+    done
+
+    # Filter that down to only those files that we can "execute"
+    COMMANDS=
+    for CMD in $ALL_FILES; do
+        test -x "$CMD" && COMMANDS="$COMMANDS $CMD"
+    done
+       
+    # Process comands for usage information
+    # Output all commands and their help information to file
+    # so it can be sorted. The format will be:
+    #
+    # command usage_message
+
+    # Find longest command name for table spacing
+    MAX=0
+    for CMD in $COMMANDS; do
+        LEN=$(echo $(basename $CMD | sed -e 's/^[^-]*-//g') | wc -c)
+        test $LEN -gt $MAX && MAX=$LEN
+    done
+
+    FILE=$(mktemp)
+    # Process all the commands into the usage file
+    for CMD in $COMMANDS; do
+        NAME=$(basename $CMD | sed -e 's/^[^-]*-//g')
+        DESC=$(grep "^#D " $CMD | head -1 | sed -e 's/^#D\w*//g' )
+        printf "    %-${MAX}s    %s\n" $NAME "$DESC" >> $FILE
+    done
+    sort -u $FILE
+
+    # clean up
+    rm -f $FILE
+    COMMANDS=$(echo $COMMANDS | sed -e 's/w+/ /g')
+}
+
+if [ $# -eq 0 ]; then
+    usage
+    exit 1
+fi
+
+SHORT=
+while [ "$SHORT x" == " x" -a $# -gt 0 ]; do
+    case $1 in
+        help|-h|--help)
+            usage
+            exit 0
+            ;;
+        -s|--server)
+            shift
+	    if [ $# -eq 0 ]; then
+		>&2 echo "Server parameter must be specified with '--server' option"
+                usage
+		exit 1
+	    fi
+   	    export CORD_HEAD_NODE="$1"
+            ;;
+	-u|--user)
+	    shift
+	    if [ $# -eq 0 ]; then
+	        >&2 echo "User parameter must be specified with '--user' option"
+	        usage
+	        exit 1
+	    fi
+	    export CORD_HEAD_NODE_USER="$1"
+	    ;;
+	-p|--port)
+	    shift
+	    if [ $# -eq 0 ]; then
+                >&2 echo "Port parameter must be specified with '--port' option"
+		usage
+		exit 1
+            fi
+	    export CORD_HEAD_NODE_PORT="$1"
+	    ;;
+        -*)
+            >&2 echo "Unknown command line option '$1'."
+            usage
+            exit 1
+            ;;
+        *)
+            SHORT=$1
+            ;;
+    esac
+    shift
+done
+
+if [ "$SHORT x" == " x" ]; then
+    >&2 echo "CORD command must be specified"
+    usage
+    exit 1
+fi
+
+COMMAND="cord-$SHORT"
+FULL_COMMAND=$(which $COMMAND)
+
+if [ ! -x "$FULL_COMMAND" ]; then
+    >&2 echo "Unknown command specified '$SHORT'."
+    usage
+    exit 1
+fi
+
+test -z $CORD_HEAD_NODE && export CORD_HEAD_NODE="localhost"
+test -z $CORD_HEAD_NODE_USER && export CORD_HEAD_NODE_USER="ubuntu"
+test -z $CORD_HEAD_NODE_PORT && export CORD_HEAD_NODE_PORT="22"
+
+exec "$FULL_COMMAND" $*
+
+
diff --git a/roles/head-node/files/commands/cord-generate b/roles/head-node/files/commands/cord-generate
new file mode 100755
index 0000000..e70d3f6
--- /dev/null
+++ b/roles/head-node/files/commands/cord-generate
@@ -0,0 +1,56 @@
+#!/bin/bash
+#D provides access to the CORD POD fabric configuration generation
+
+PROG=$(echo $(basename $0) | sed -e 's/^cord-/cord /g')
+
+usage() {
+    echo "usage: $PROG [options]"
+    echo "    -hc|--host-count      number of hosts to expect to find in ONOS"
+    echo "    -sc|--switch-count    number of switches to expect to find in ONOS"
+    echo "    -o|--onos             ONOS to which to connect to get host / switch information"
+    echo "    -u|--user             ONOS user (not currently used)"
+    echo "    -p|--passwd           ONOS password (not currently used)"
+    echo "    -h|--help             this message"
+}
+
+HOST_COUNT=0
+SWITCH_COUNT=0
+ONOS_USER="karaf"
+ONOS_PASSWORD="karaf"
+ONOS_HOST="onos-fabric"
+
+if [ $# -eq 0 ]; then
+    usage
+    exit 0
+fi 
+
+while [ $# -gt 0 ]; do
+    case $1 in
+        -hc|--host-count)
+            shift
+	    HOST_COUNT=$1
+	    ;;
+	-sc|--switch-count)
+	    shift
+	    SWITCH_COUNT=$1
+	    ;;
+	-o|--onos)
+            shift
+	    ;;
+	-p|--passwd)
+	    shift
+	    ;;
+	-u|--user)
+            shift
+	    ;;
+        -h|--help)
+	    usage
+	    exit 0
+	    ;;
+    esac
+    shift
+done
+
+curl --fail -sSL -XPOST http://$CORD_HEAD_NODE:4245/config/ -d "{\"hostcount\":$HOST_COUNT,\"switchcount\":$SWITCH_COUNT,\"onosip\":\"$ONOS_HOST\"}"
+
+echo $?
diff --git a/roles/head-node/files/commands/cord-harvest b/roles/head-node/files/commands/cord-harvest
new file mode 100755
index 0000000..fd43182
--- /dev/null
+++ b/roles/head-node/files/commands/cord-harvest
@@ -0,0 +1,115 @@
+#!/bin/bash
+#D provides access to the CORD POD DHCP havesting of IP addresses
+
+PROG=$(echo $(basename $0) | sed -e 's/^cord-/cord /g')
+
+usage() {
+    echo "usage: $PROG <sub-command> [options"
+    echo "    go     performs a harvest from the DHCP server"
+    echo "    list   list the currently harvested information"
+    echo "    check  check the error logs of the harvester for potential isseus"
+    echo "    help   this message"
+}
+
+COMMAND=$1; shift
+SSH_OPT=
+if [ $CORD_HEAD_NODE != "localhost" -a $CORD_HEAD_NODE != "127.0.0.1" ]; then
+    SSH_OPT="ssh -p $CORD_HEAD_NODE_PORT $CORD_HEAD_NODE_USER@$CORD_HEAD_NODE"
+fi
+case $COMMAND in
+    list)
+        DO_JSON=0
+        while [ $# -gt 0 ]; do
+            case $1 in
+                json|--json|-j)
+                    DO_JSON=1
+                    ;;
+                help|--help|-h)
+                    echo "usage $PROG list [--json|-j]"
+                    echo "    json    display output as JSON object"
+                    exit 0
+                    ;;
+                *)
+                    >&2 "Unknoan option '$1'"
+                    echo "usage: $PROG list [--json|-j]"
+                    echo "    json    display output as JSON object"
+                    exit 1
+                    ;;
+            esac
+            shift
+        done
+
+	if [ "$SSH_OPT x" == " x" -a ! -r /etc/bind/maas/dhcp_harvest.inc ]; then
+	    >&2 echo "Unable to find the DHCP harvest file locally, please specify the server option if you are not on the head node."
+	    exit 1
+	fi
+        if [ $DO_JSON -eq 1 ]; then
+            $SSH_OPT cat /etc/bind/maas/dhcp_harvest.inc | grep "^[^ ][^ ]*\s\s*IN A\s" | awk 'BEGIN{printf("[")} {printf("{\"name\":\"%s\",\"ip\":\"%s\",\"mac\":\"%s\"}", $1,$4,$6)} END{printf("]")}' | sed -e 's/}{/},{/g'
+        else
+            $SSH_OPT cat /etc/bind/maas/dhcp_harvest.inc | grep "^[^ ][^ ]*\s\s*IN A\s"
+        fi
+        ;;
+    check)
+        RUNNING=$($SSH_OPT docker inspect --format="'{{ .State.Running }}'" harvester) 
+	if [ $? -ne 0 ]; then
+	    >&2 echo "Unable to execute docker or locate harvester container, if not running on the head node please specify the server address"
+	    exit 1
+	fi
+	if [ "$RUNNING" == "false" ]; then
+	    >&2 echo "The harvester container is not currently running, results may not be of value"
+        fi
+	OUT=$(mktemp)
+	$SSH_OPT docker logs harvester 2>&1 | grep -v "Warning: Permanently added" > $OUT
+	ERROR_CNT=$(cat $OUT | grep -i error | wc -l)
+	LAST_100=$(cat $OUT | tail -100 | grep -i error | wc -l)
+	ERR_FOUND=$(cat $OUT | tail -100 | grep -i "failed to update DNS server" | wc -l)
+	LAST_ERR=$(cat $OUT | grep -i error | tail -1)
+	rm -f $OUT
+
+	if [ $ERROR_CNT -ne 0 ]; then
+	    if [ $LAST_100 -ne 0 ]; then
+		if [ $ERR_FOUND -ne 0 ]; then
+	            echo "There is a recent error in the log that may indicate the harvester is unable to update the DNS server"
+		    echo "This may be able to be fixed by restarting the DNS server, `sudo service bind9 restart`"
+		    echo "Restarting the DNS server will not immediately clear this message"
+		else
+                    echo "Recent errors have been found in the log, the last error was '$LAST_ERR'"
+		fi
+	    else
+		echo "There are errors in the log, but there are not errors in the last 100 log messages, so the harvester is likely ok"
+	    fi
+	else
+	    echo "There are no errors in the log, so the harvester is likely OK"
+	fi
+        ;;
+    go)
+        RESULT=$(curl --fail -sSL -XPOST http://$CORD_HEAD_NODE:8954/harvest 2>&1)
+        ERR=$?
+        if [ $ERR -ne 0 ]; then
+            >&2 "ERROR processing request, exit code '$ERR', '$RESULT'"
+            exit $ERR
+        fi
+        case $(echo $RESULT | jq .response | sed -e 's/"//g') in
+            OK)
+                echo "Havest complete"
+                ;;
+            QUIET)
+                echo "Too many requests, please try in a few seconds"
+                ;;
+            *)
+                >&2 "ERROR: unknown response '$RESULT'"
+                exit 1
+                ;;
+        esac
+        ;;
+    help|--help|-h)
+        usage
+        exit 0
+        ;;
+    *)
+        >&2 echo "Unknown subcommand '$COMMAND'"
+        usage
+        exit 1
+        ;;
+esac
+  
diff --git a/roles/head-node/files/commands/cord-prov b/roles/head-node/files/commands/cord-prov
new file mode 100755
index 0000000..e45059c
--- /dev/null
+++ b/roles/head-node/files/commands/cord-prov
@@ -0,0 +1,143 @@
+#!/bin/bash
+#D provides access to the CORD POD base metal provisioning service
+
+PROG=$(echo $(basename $0) | sed -e 's/^cord-/cord /g')
+
+usage() {
+    echo "usage: $PROG <sub-command> [options]"
+    echo "    list    display the provisioning status of all nodes"
+    echo "    show    display the provisiniong status of a single node"
+    echo "    delete  delete node(s) provisioning state"
+    echo "    help    this message"
+}
+
+COMMAND=$1; shift
+case $COMMAND in
+    list)
+        DO_JSON=0
+        DO_MAP=0
+        while [ $# -gt 0 ]; do
+            case $1 in
+                json|--json|-j)
+                    DO_JSON=1
+                    ;;
+                map|--map|-m)
+                    DO_MAP=1
+                    ;;
+                help|--help|-h)
+                    echo "usage: $PROG list [--json|-j] [--map|-m]"
+                    echo "    json    display output as JSON object"
+                    echo "    map     map node provisioning status to state name"
+                    exit 0
+                    ;;
+                *)
+                    >&2 "Unknown option '$1'"
+                    echo "usage: $PROG list [--json|-j] [--map|-m]"
+                    echo "    json    display output as JSON object"
+                    echo "    map     map node provisioning status to state name"
+                    exit 1
+                    ;;
+            esac
+            shift
+        done
+	if [ $DO_JSON -eq 1 ]; then
+            if [ $DO_MAP -eq 1 ]; then
+		curl -sSL http://$CORD_HEAD_NODE:4243/provision/ | jq -c -M 'def STATUS: ["Unknown","Processing","Complete","Error"]; [ .[] | . | .status|=STATUS[.] ]'
+            else
+                curl -sSL http://$CORD_HEAD_NODE:4243/provision/
+            fi
+        else
+            for LINE in "ID,NAME,MAC,IP,STATUS,MESSAGE" $(curl -sSL http://$CORD_HEAD_NODE:4243/provision/ | jq 'def STATUS: ["Unknown","Processing","Complete","Error"]; .[] | . | .status|=STATUS[.] | .request.Info.id+","+.request.Info.name+","+.request.Info.mac+","+.request.Info.ip+","+.status+","+.message'); do
+                echo $LINE | sed -e 's/^"//;s/"$//'
+            done | column -s , -t
+        fi
+        ;;
+    show)
+        DO_JSON=0
+        DO_MAP=0
+        ID=
+        while [ $# -gt 0 ]; do
+            case $1 in
+                json|--json|-j)
+                    DO_JSON=1
+                    ;;
+                map|--map|-m)
+                    DO_MAP=1
+                    ;;
+                help)
+                    echo "usage: $PROG show [--json|-j] [--map|-m] <id>"
+                    echo "    json    display output as JSON object"
+                    echo "    map     map node provisioning status to state name"
+                    exit 0
+                    ;;
+                *)
+                    ID="$ID $1"
+                    ;;     
+            esac
+            shift
+        done
+        if [ $DO_JSON -eq 1 ]; then
+            if [ $DO_MAP -eq 1 ]; then
+                echo -n "["
+                for i in $ID; do
+                    curl -sSL http://$CORD_HEAD_NODE:4243/provision/$i | jq -c -M 'def STATUS: ["Unknown","Processing","Complete","Error"]; . | .status|=STATUS[.]'
+                done  | awk -vORS=, '{ print }' | sed 's/,$//'
+                echo -n "]"
+            else
+                echo -n "["
+                for i in $ID; do
+                    curl -sSL http://$CORD_HEAD_NODE:4243/provision/$i | jq -c -M .
+                done | awk -vORS=, '{ print }' | sed 's/,$//'
+                echo -n "]"
+            fi
+        else
+            for i in "__TITLE__" $ID; do
+                if [ $i == "__TITLE__" ]; then
+                    echo "ID,NAME,MAC,IP,STATUS,MESSAGE"
+                else
+                  VALUE=$(curl --fail -sSL http://$CORD_HEAD_NODE:4243/provision/$i)
+                  if [ $? -ne 0 ]; then
+                      echo "$i, , , ,Not Found"
+                  else
+  		      echo "$VALUE" | jq 'def STATUS: ["Unknown","Processing","Complete","Error"]; . | .status|=STATUS[.] | .request.Info.id+","+.request.Info.name+","+.request.Info.mac+","+.request.Info.ip+","+.status+","+.message'| sed -e 's/^"//;s/"$//'
+                  fi
+                fi
+            done | column -s , -t
+        fi
+        ;;
+    delete)
+        ID=
+        while [ $# -gt 0 ]; do
+            case $1 in
+                all|-a|--all)
+                    ID=$(curl -sSL http://$CORD_HEAD_NODE:4243/provision/ | jq '.[] | .request.Info.id' | sed -e 's/^"//;s/"$//')
+                ;;
+                help|-h|--help)
+                    echo "$PROG delete [--all|-a] [<id> ...]"
+                    exit 0
+                ;;
+                *)
+                    ID="$ID $1"
+                    ;;
+            esac
+            shift
+        done
+        for i in $ID; do
+            curl --fail -sSL -XDELETE http://$CORD_HEAD_NODE:4243/provision/$i
+            if [ $? -eq 0 ]; then
+                echo "$i DELETED"
+            else
+                echo "$i FAILED"
+            fi
+        done | column -s , -t
+        ;;
+    help|-h|--help)
+        usage
+        exit 0
+        ;;
+    *)
+        >&2 echo "Unknown subcommand '$COMMAND'"
+        usage
+        exit 1
+        ;;
+esac
diff --git a/roles/head-node/files/commands/cord-switch b/roles/head-node/files/commands/cord-switch
new file mode 100755
index 0000000..2f92137
--- /dev/null
+++ b/roles/head-node/files/commands/cord-switch
@@ -0,0 +1,53 @@
+#!/bin/bash
+#D provides access to the CORD POD swtich identification service
+
+PROG=$(echo $(basename $0) | sed -e 's/^cord-/cord /g')
+
+usage() {
+    echo "usage: $PROG <sub-command> [options]"
+    echo "    list    display the known switches"
+    echo "    help    this message"
+}
+
+COMMAND=$1; shift
+case $COMMAND in
+    list)
+        DO_JSON=0
+        DO_MAP=0
+        while [ $# -gt 0 ]; do
+            case $1 in
+                json|--json|-j)
+                    DO_JSON=1
+                    ;;
+                help|--help|-h)
+                    echo "usage: $PROG list [--json|-j]"
+                    echo "    json    display output as JSON object"
+                    exit 0
+                    ;;
+                *)
+                    >&2 "Unknown option '$1'"
+                    echo "usage: $PROG list [--json|-j]"
+                    echo "    json    display output as JSON object"
+                    exit 1
+                    ;;
+            esac
+            shift
+        done
+	if [ $DO_JSON -eq 1 ]; then
+            curl -sSL http://$CORD_HEAD_NODE:4244/switch/
+        else
+            for LINE in "NAME,MAC,IP" $(curl -sSL http://$CORD_HEAD_NODE:4244/switch/ | jq '.[] | .name+","+.mac+","+.ip'); do
+                echo $LINE | sed -e 's/^"//;s/"$//'
+            done | column -s , -t
+        fi
+        ;;
+    help|-h|--help)
+        usage
+        exit 0
+        ;;
+    *)
+        >&2 echo "Unknown subcommand '$COMMAND'"
+        usage
+        exit 1
+        ;;
+esac
diff --git a/roles/head-node/tasks/main.yml b/roles/head-node/tasks/main.yml
index c3aeb51..1aa439a 100644
--- a/roles/head-node/tasks/main.yml
+++ b/roles/head-node/tasks/main.yml
@@ -106,3 +106,16 @@
     owner=maas
     group=maas
     mode=0755
+
+- name: Copy CORD Utility Scripts
+  become: yes
+  copy:
+    src=files/commands/{{ item }}
+    dest=/usr/local/bin/{{ item }}
+    owner=root
+    group=root
+    mode=0755
+  with_items:
+    - cord
+    - cord-harvest
+    - cord-prov
diff --git a/roles/maas/templates/automation-compose.yml.j2 b/roles/maas/templates/automation-compose.yml.j2
index b2c0fb6..cc530c4 100644
--- a/roles/maas/templates/automation-compose.yml.j2
+++ b/roles/maas/templates/automation-compose.yml.j2
@@ -17,6 +17,8 @@
   allocator:
     image: "docker-registry:5000/cord-ip-allocator:{{ docker.image_version }}"
     container_name: allocator
+    ports:
+      - "4242:4242"
     labels:
       - "lab.solution=CORD"
       - "lab.component=allocator"
@@ -35,6 +37,8 @@
     image: "docker-registry:5000/cord-provisioner:{{ docker.image_version }}"
     container_name: provisioner
     dns: {{ mgmt_ip_address.stdout }}
+    ports:
+      - "4243:4243"
     labels:
       - "lab.solution=CORD"
       - "lab.component=provisioner"
@@ -59,6 +63,8 @@
   switchq:
     image: "docker-registry:5000/cord-maas-switchq:{{ docker.image_version }}"
     container_name: switchq
+    ports:
+      - "4244:4244"
     labels:
       - "lab.solution=CORD"
       - "lab.component=switchq"
@@ -109,13 +115,15 @@
   generator:
     image: "docker-registry:5000/config-generator:{{ docker.image_version }}"
     container_name: generator
+    ports:
+      - "4245:4245"
     labels:
       - "lab.solution=CORD"
       - "lab.component=generator"
     environment:
       - "CONFIGGEN_PORT=8181"
       - "CONFIGGEN_IP=onos-fabric"
-      - "CONFIGGEN_CONFIGSERVERPORT=1337"
+      - "CONFIGGEN_CONFIGSERVERPORT=4245"
       - "CONFIGGEN_CONFIGSERVERIP=0.0.0.0"
     restart: unless-stopped