From 4db042ef3c570cdcb51b04830db5d39c95228208 Mon Sep 17 00:00:00 2001
From: "Stephen C. Phillips" <scp@it-innovation.soton.ac.uk>
Date: Mon, 18 Jun 2018 15:33:46 +0100
Subject: [PATCH] merge selected commits from workshop-demo branch (#80)

---
 Vagrantfile                                   |  15 +
 scripts/clmc-agent/configure.sh               |  24 +-
 scripts/clmc-agent/install.sh                 |   2 +
 scripts/clmc-agent/telegraf.conf              |  14 +-
 scripts/clmc-agent/telegraf_output.conf       |   4 +-
 scripts/clmc-service/install-clmc-service.sh  | 126 +++
 scripts/clmc-service/install-neo4j.sh         |  61 ++
 scripts/clmc-service/install-tick-stack.sh    |  58 ++
 scripts/clmc-service/install.sh               | 180 +---
 scripts/test/fixture.sh                       |  38 +-
 .../clmctest/services/host/telegraf_host.conf |  77 +-
 .../services/minio/telegraf_minio.conf        |  16 +-
 .../services/nginx/telegraf_nginx.conf        |  12 +-
 .../workshopdemo/dashboards/dc_dash.json      | 147 +++
 .../workshopdemo/dashboards/minio_dash.json   | 720 ++++++++++++++
 .../workshopdemo/dashboards/nginx_dash.json   | 935 ++++++++++++++++++
 .../workshopdemo/dashboards/sf_dash.json      | 500 ++++++++++
 17 files changed, 2647 insertions(+), 282 deletions(-)
 create mode 100644 scripts/clmc-service/install-clmc-service.sh
 create mode 100644 scripts/clmc-service/install-neo4j.sh
 create mode 100644 scripts/clmc-service/install-tick-stack.sh
 create mode 100644 src/test/clmctest/workshopdemo/dashboards/dc_dash.json
 create mode 100644 src/test/clmctest/workshopdemo/dashboards/minio_dash.json
 create mode 100644 src/test/clmctest/workshopdemo/dashboards/nginx_dash.json
 create mode 100644 src/test/clmctest/workshopdemo/dashboards/sf_dash.json

diff --git a/Vagrantfile b/Vagrantfile
index 89e39b3..a45ee26 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -17,6 +17,21 @@ sed -i s/10.0.3/172.40.231/g /etc/default/lxc-net
 sed -i s/#LXC_DHCP_CONFILE/LXC_DHCP_CONFILE/g /etc/default/lxc-net
 service lxc-net restart
 
+# enable NTP
+# use network time to make sure we are synchronised
+echo "Disabling timesyncd..."
+timedatectl set-ntp no
+until timedatectl | grep -m 1 "Network time on: no";
+do
+  echo "Waiting for timesyncd to turn off.."
+  sleep 1
+done
+apt-get install ntp
+echo "timesync set to ntpd"
+
+# set timezone to London
+timedatectl set-timezone Europe/London
+
 SCRIPT
 
 Vagrant.configure("2") do |config|
diff --git a/scripts/clmc-agent/configure.sh b/scripts/clmc-agent/configure.sh
index 35226d6..9f98a51 100755
--- a/scripts/clmc-agent/configure.sh
+++ b/scripts/clmc-agent/configure.sh
@@ -47,23 +47,23 @@ INFLUXDB_URL=$8
 DATABASE_NAME=$9
 
 TELEGRAF_CONF_DIR="/etc/telegraf"
-TELEGRAF_CONF_FILE=$TELEGRAF_CONF_DIR"/telegraf.conf"
-TELEGRAF_INCLUDE_CONF_DIR=$TELEGRAF_CONF_DIR"/telegraf.d"
-TELEGRAF_OUTPUT_CONF_FILE=$TELEGRAF_INCLUDE_CONF_DIR"/telegraf_output.conf"
+TELEGRAF_CONF_FILE=${TELEGRAF_CONF_DIR}"/telegraf.conf"
+TELEGRAF_INCLUDE_CONF_DIR=${TELEGRAF_CONF_DIR}"/telegraf.d"
+TELEGRAF_OUTPUT_CONF_FILE=${TELEGRAF_INCLUDE_CONF_DIR}"/telegraf_output.conf"
 
 #cat ${TELEGRAF_OUTPUT_CONF_FILE}
 
 # Replace template parameters on general configuration
-sed -i 's/$LOCATION/'$LOCATION'/g' $TELEGRAF_CONF_FILE
-sed -i 's/$SFC_ID/'$SFC_ID'/g' $TELEGRAF_CONF_FILE
-sed -i 's/$SFC_ID_INSTANCE/'$SFC_ID_INSTANCE'/g' $TELEGRAF_CONF_FILE
-sed -i 's/$SF_ID/'$SF_ID'/g' $TELEGRAF_CONF_FILE
-sed -i 's/$SF_ID_INSTANCE}}/'$SF_ID_INSTANCE'/g' $TELEGRAF_CONF_FILE
-sed -i 's/$IP_ENDPOINT_ID/'$IP_ENDPOINT_ID'/g' $TELEGRAF_CONF_FILE
-sed -i 's/$SR_ID/'$SR_ID'/g' $TELEGRAF_CONF_FILE
+sed -i 's/${LOCATION}/'${LOCATION}'/g' ${TELEGRAF_CONF_FILE}
+sed -i 's/${SFC_ID}/'${SFC_ID}'/g' ${TELEGRAF_CONF_FILE}
+sed -i 's/${SFC_ID_INSTANCE}/'${SFC_ID_INSTANCE}'/g' ${TELEGRAF_CONF_FILE}
+sed -i 's/${SF_ID}/'${SF_ID}'/g' ${TELEGRAF_CONF_FILE}
+sed -i 's/${SF_ID_INSTANCE}/'${SF_ID_INSTANCE}'/g' ${TELEGRAF_CONF_FILE}
+sed -i 's/${IP_ENDPOINT_ID}/'${IP_ENDPOINT_ID}'/g' ${TELEGRAF_CONF_FILE}
+sed -i 's/${SR_ID}/'${SR_ID}'/g' ${TELEGRAF_CONF_FILE}
 
 echo "Telegraf Output Configuration File: ${TELEGRAF_OUTPUT_CONF_FILE}"
 
 # Replace parameters in output configuration file
-sed -i 's|$INFLUXDB_URL|'$INFLUXDB_URL'|g' $TELEGRAF_OUTPUT_CONF_FILE
-sed -i 's/$DATABASE_NAME/'$DATABASE_NAME'/g' $TELEGRAF_OUTPUT_CONF_FILE
\ No newline at end of file
+sed -i 's|${INFLUXDB_URL}|'${INFLUXDB_URL}'|g' ${TELEGRAF_OUTPUT_CONF_FILE}
+sed -i 's/${DATABASE_NAME}/'${DATABASE_NAME}'/g' ${TELEGRAF_OUTPUT_CONF_FILE}
\ No newline at end of file
diff --git a/scripts/clmc-agent/install.sh b/scripts/clmc-agent/install.sh
index cf5e4eb..7271193 100755
--- a/scripts/clmc-agent/install.sh
+++ b/scripts/clmc-agent/install.sh
@@ -33,6 +33,8 @@ apt-get install wget -y
 echo "Installing Telegraf agent"
 
 TELEGRAF_VERSION=1.8.0~2736fa0-0
+#TELEGRAF_VERSION=1.7.0~5618bb0-0
+
 TELEGRAF_CHECKSUM=dc24932fa1aef9392582880c077dd2493b9f2c66babd7733a0654540bbb5003b
 
 # Install telegraf
diff --git a/scripts/clmc-agent/telegraf.conf b/scripts/clmc-agent/telegraf.conf
index 6c5aed6..07ac73a 100644
--- a/scripts/clmc-agent/telegraf.conf
+++ b/scripts/clmc-agent/telegraf.conf
@@ -33,19 +33,19 @@
 # Global tags can be specified here in key="value" format.
 [global_tags]
   # location of the data centre
-  location="$LOCATION"
+  location="${LOCATION}"
   # media service template id
-  sfc="$SFC_ID"
+  sfc="${SFC_ID}"
   # media service instance
-  sfc_i="$SFC_ID_INSTANCE"
+  sfc_i="${SFC_ID_INSTANCE}"
   # service function type
-  sf="$SF_ID"
+  sf="${SF_ID}"
   # service function instance id
-  sf_i="$SF_ID_INSTANCE"
+  sf_i="${SF_ID_INSTANCE}"
   # ipendpoint id aka surrogate instance
-  ipendpoint="$IP_ENDPOINT_ID"
+  ipendpoint="${IP_ENDPOINT_ID}"
   # the service router providing access to the network
-  sr="$SR_ID"
+  sr="${SR_ID}"
 
 # Configuration for telegraf agent
 [agent]
diff --git a/scripts/clmc-agent/telegraf_output.conf b/scripts/clmc-agent/telegraf_output.conf
index e77f247..7521ac3 100644
--- a/scripts/clmc-agent/telegraf_output.conf
+++ b/scripts/clmc-agent/telegraf_output.conf
@@ -29,9 +29,9 @@
   # Multiple urls can be specified but it is assumed that they are part of the same
   # cluster, this means that only ONE of the urls will be written to each interval.
   # urls = ["udp://127.0.0.1:8089"] # UDP endpoint example
-  urls = ["$INFLUXDB_URL"] # required
+  urls = ["${INFLUXDB_URL}"] # required
   # The target database for metrics (telegraf will create it if not exists)
-  database = "$DATABASE_NAME" # required
+  database = "${DATABASE_NAME}" # required
   # Precision of writes, valid values are "ns", "us" (or "µs"), "ms", "s", "m", "h".
   # note: using second precision greatly helps InfluxDB compression
   precision = "s"
diff --git a/scripts/clmc-service/install-clmc-service.sh b/scripts/clmc-service/install-clmc-service.sh
new file mode 100644
index 0000000..c1d93d9
--- /dev/null
+++ b/scripts/clmc-service/install-clmc-service.sh
@@ -0,0 +1,126 @@
+#!/bin/bash
+
+# Get command line parameters
+if [ "$#" -ne 3 ]; then
+    echo "Error: illegal number of arguments: "$#
+    echo "Usage: install.sh INFLUX_URL DATABASE_NAME REPORT_PERIOD"
+    exit 1 
+fi
+
+INFLUX_URL=$1
+DATABASE_NAME=$2
+REPORT_PERIOD=$3
+
+## CLMC-SERVICE
+## ----------------------------------------------------------------------------------
+echo "----> Configuring virtualenvwrapper"
+export WORKON_HOME=$HOME/.virtualenvs
+source /usr/local/bin/virtualenvwrapper.sh
+
+# check the mkvirtualenv with a return value of 1 if version comes back correctly
+mkvirtualenv --version
+if [ $? -ne 1 ] ; then
+        echo "Failed: installing virtualenvwrapper"
+		exit 1
+fi
+
+# create CLMC virtual environment - and check
+echo "----> Making CLMC Python environment"
+mkvirtualenv CLMC
+if [ $? -ne 0 ] ; then
+        echo "Failed: creating CLMC python environment"
+		exit 1
+fi
+
+# switch the CLMC environment - and check
+echo "----> Switching to use CLMC python environment"
+workon CLMC
+if [ $? -ne 0 ] ; then
+        echo "Failed: switching to CLMC python environment"
+		exit 1
+fi
+
+# install tox - and check
+echo "----> Installing TOX"
+pip3 install tox
+tox --version
+if [ $? -ne 0 ] ; then
+        echo "Failed: installing tox"
+		exit 1
+fi
+
+# navigate to the clmc-webservice - and check
+echo "----> Moving to CLMC webservice"
+cd ${REPO_ROOT}/src/service
+if [ $? -ne 0 ] ; then
+        echo "Failed: could not find clmc-webservice"
+		exit 1
+fi
+
+# running tests using tox
+echo "----> Running tox"
+TOX_OUTPUT="$(tox)"
+# check if tox output contains the 'congratulations :)' bit for tests passed
+if [[ $TOX_OUTPUT != *"congratulations :)"* ]]; then
+  echo $TOX_OUTPUT
+  echo "CLMC service unit tests failed."
+  exit 1
+fi
+echo "----> Tox execution of unit tests passed successfully"
+
+# install the service
+echo "----> Installing CLMC web service"
+pip3 install  .
+if [ $? -ne 0 ] ; then
+        echo "Failed: installing clmc-webservice"
+		exit 1
+fi
+
+# create directory for CLMC service logs
+echo "----> Creating CLMC web service log directory"
+mkdir -p /var/log/flame/clmc
+
+# Install minioclmc as systemctl service
+# -----------------------------------------------------------------------
+mkdir -p /opt/flame/clmc
+start_script_file="/opt/flame/clmc/start.sh"
+echo "#!/bin/bash" > $start_script_file
+echo "export WORKON_HOME=${HOME}/.virtualenvs" >> $start_script_file
+echo "source /usr/local/bin/virtualenvwrapper.sh" >> $start_script_file
+echo "workon CLMC" >> $start_script_file
+echo "pserve ${REPO_ROOT}/src/service/production.ini &" >> $start_script_file
+
+chmod 755 $start_script_file
+
+file="/lib/systemd/system/flameclmc.service"
+echo "[Unit]" > $file
+echo "Description=flameclmc" >> $file
+echo "After=network.target" >> $file
+echo "" >> $file
+echo "[Service]" >> $file
+echo "Type=forking" >> $file
+echo "ExecStart=${start_script_file}" >> $file
+echo "" >> $file
+echo "[Install]" >> $file
+echo "WantedBy=multi-user.target" >> $file
+
+systemctl daemon-reload
+systemctl enable flameclmc.service
+systemctl start flameclmc.service
+
+# wait for the clmc service to start
+while ! nc -z localhost 9080
+do
+  echo "Waiting for clmc service port 9080 to be ready on localhost..."
+  sleep 5
+done
+
+# configure the CLMC service
+JSON="{\"aggregator_report_period\": ${REPORT_PERIOD}, \"aggregator_database_name\": \"${DATABASE_NAME}\", \"aggregator_database_url\": \"${INFLUX_URL}\"}"
+echo "CONFIG JSON=${JSON}"
+curl -H 'Content-Type: application/json' -X PUT -d "${JSON}" http://localhost:9080/aggregator/config
+
+# start the aggregator
+JSON="{\"action\": \"start\"}"
+echo "START ACTION JSON=${JSON}"
+curl -H 'Content-Type: application/json' -X PUT -d "${JSON}" http://localhost:9080/aggregator/control
\ No newline at end of file
diff --git a/scripts/clmc-service/install-neo4j.sh b/scripts/clmc-service/install-neo4j.sh
new file mode 100644
index 0000000..64188c4
--- /dev/null
+++ b/scripts/clmc-service/install-neo4j.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+# status of neo4j can be found using: journalctl -e -u neo4j
+# Directories: 
+#/etc/neo4j/neo4j.conf
+#/var/lib/neo4j/data
+#/var/log/neo4j
+#/var/lib/neo4j/metrics
+#/var/lib/neo4j/import
+#/usr/bin
+#/usr/share/neo4j/lib
+#/var/lib/neo4j/plugins
+#
+#admin tool is neo4j-admin
+#
+# bolt 7687
+# http 7474 including the browser on localhost:7474
+# https 7473
+
+sudo apt update
+sudo apt install wget openjdk-8-jdk -y
+
+# configure apt
+wget -O - https://debian.neo4j.org/neotechnology.gpg.key | sudo apt-key add -
+echo 'deb https://debian.neo4j.org/repo stable/' | sudo tee -a /etc/apt/sources.list.d/neo4j.list
+apt-get update
+
+# add neo4j user with sudo permissions
+useradd --comment 'neo4j' --create-home neo4j --shell /bin/bash
+echo "neo4j ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
+
+# install neo4j as neo4j user
+# when installed as root the systemctl service fails to start as it expects to be run as neo4j user
+# there's some directories owned as root that neo4j wants to write to but cannot
+# the following set of commands install everything as neo4j with some run as sudo.
+# Expected to only have to run the install as neo4j however it does not work unless all of these are
+# run as neo4j. hence the weird su neo4j -c "sudo..."
+su neo4j -c "sudo apt-get install neo4j=1:3.4.0 -y"
+
+su neo4j -c "sed -i s/\#dbms.connectors.default_listen_address=0.0.0.0/dbms.connectors.default_listen_address=0.0.0.0/g /etc/neo4j/neo4j.conf"
+su neo4j -c "sed -i s/\#dbms.connector.bolt.listen_address=:7687/dbms.connector.bolt.listen_address=0.0.0.0:7687/g /etc/neo4j/neo4j.conf"
+
+# set initial password before starting 
+su neo4j -c "neo4j-admin set-initial-password admin"
+
+# install service
+su neo4j -c "sudo systemctl enable neo4j"
+su neo4j -c "sudo systemctl start neo4j"
+
+### waiting for the service to start
+end="$((SECONDS+60))"
+while true; do
+    nc -w 2 localhost 7687 && break
+    [[ "${SECONDS}" -ge "${end}" ]] && exit 1
+    sleep 1
+done
+
+apt-get -y install python3 python3-pip
+update-alternatives --install /usr/bin/python python /usr/bin/python3 10
+apt-get update
+pip3 install influxdb py2neo
diff --git a/scripts/clmc-service/install-tick-stack.sh b/scripts/clmc-service/install-tick-stack.sh
new file mode 100644
index 0000000..f52fce9
--- /dev/null
+++ b/scripts/clmc-service/install-tick-stack.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+echo "----> Installing Tick Stack"
+# Define tickstack software versions
+INFLUX_VERSION=1.5.2
+INFLUX_CHECKSUM=42fede7b497bdf30d4eb5138db218d1add986fca4fce4a8bcd9c7d6dabaf572a
+
+KAPACITOR_VERSION=1.4.1
+KAPACITOR_CHECKSUM=eea9b215f241906570eafe3857e1d4c5
+
+CHRONOGRAF_VERSION=1.4.4.2
+CHRONOGRAF_CHECKSUM=eea6915aa6db8f134fcd3b095e863b773bfb3a16a26e346dd65904a07df97963
+
+# install virtualenvwrapper to manage python environments - and check
+apt-get update
+echo "----> Installing Python3 and Pip3"
+apt-get install -y python3 python3-pip wget curl
+update-alternatives --install /usr/bin/python python /usr/bin/python3 10
+
+echo "----> Installing virtualenv and wrapper"
+apt-get install -y python3-virtualenv virtualenvwrapper
+pip3 install virtualenv
+pip3 install virtualenvwrapper
+
+# install influx
+echo "----> Installing InfluxDB"
+wget https://dl.influxdata.com/influxdb/releases/influxdb_${INFLUX_VERSION}_amd64.deb 2> /dev/null
+sha256sum influxdb_${INFLUX_VERSION}_amd64.deb | grep $INFLUX_CHECKSUM > /dev/null
+if [ $? == 1 ]; then
+	echo "influx download failed checksum"
+	exit 1
+fi
+dpkg -i influxdb_${INFLUX_VERSION}_amd64.deb
+
+# install kapacitor
+echo "----> Installing Kapacitor"
+wget https://dl.influxdata.com/kapacitor/releases/kapacitor_${KAPACITOR_VERSION}_amd64.deb 2> /dev/null
+md5sum kapacitor_${KAPACITOR_VERSION}_amd64.deb | grep $KAPACITOR_CHECKSUM > /dev/null
+if [ $? == 1 ]; then
+	echo "Kapacitor download failed checksum"
+	exit 1
+fi
+dpkg -i kapacitor_${KAPACITOR_VERSION}_amd64.deb
+
+# install Chronograf
+echo "----> Installing Chronograph"
+wget https://dl.influxdata.com/chronograf/releases/chronograf_${CHRONOGRAF_VERSION}_amd64.deb 2> /dev/null
+sha256sum chronograf_${CHRONOGRAF_VERSION}_amd64.deb | grep $CHRONOGRAF_CHECKSUM > /dev/null
+if [ $? == 1 ]; then
+	echo "Chronograf download failed checksum"
+	exit 1
+fi
+dpkg -i chronograf_${CHRONOGRAF_VERSION}_amd64.deb
+
+systemctl start influxdb
+systemctl start kapacitor
+systemctl start chronograf
+
diff --git a/scripts/clmc-service/install.sh b/scripts/clmc-service/install.sh
index 8f1132b..49abe7a 100755
--- a/scripts/clmc-service/install.sh
+++ b/scripts/clmc-service/install.sh
@@ -27,183 +27,15 @@
 # Force fail on command fail (off for now as virtualenvwrapper install fails)
 # set -euo pipefail
 
+# Ensure everything runs in directory of the parent script
+cd `dirname $0`
 
-echo "Configuring CLMC service"
+echo "Provisioning CLMC service"
 
-# Get command line parameters
-if [ "$#" -ne 3 ]; then
-    echo "Error: illegal number of arguments: "$#
-    echo "Usage: install.sh INFLUX_URL DATABASE_NAME REPORT_PERIOD"
-    exit 1 
-fi
+./install-tick-stack.sh $@
+./install-clmc-service.sh $@
+./install-neo4j.sh $@
 
-INFLUX_URL=$1
-DATABASE_NAME=$2
-REPORT_PERIOD=$3
 
-# Define tickstack software versions
-INFLUX_VERSION=1.5.2
-INFLUX_CHECKSUM=42fede7b497bdf30d4eb5138db218d1add986fca4fce4a8bcd9c7d6dabaf572a
 
-KAPACITOR_VERSION=1.4.1
-KAPACITOR_CHECKSUM=eea9b215f241906570eafe3857e1d4c5
-
-CHRONOGRAF_VERSION=1.4.4.2
-CHRONOGRAF_CHECKSUM=eea6915aa6db8f134fcd3b095e863b773bfb3a16a26e346dd65904a07df97963
-
-# install virtualenvwrapper to manage python environments - and check
-apt-get update
-echo "----> Installing Python3 and Pip3"
-apt-get install -y python3 python3-pip wget curl
-update-alternatives --install /usr/bin/python python /usr/bin/python3 10
-
-echo "----> Installing virtualenv and wrapper"
-apt-get install -y python3-virtualenv virtualenvwrapper
-pip3 install virtualenv
-pip3 install virtualenvwrapper
-
-# install influx
-wget https://dl.influxdata.com/influxdb/releases/influxdb_${INFLUX_VERSION}_amd64.deb 2> /dev/null
-sha256sum influxdb_${INFLUX_VERSION}_amd64.deb | grep $INFLUX_CHECKSUM > /dev/null
-if [ $? == 1 ]; then
-	echo "influx download failed checksum"
-	exit 1
-fi
-dpkg -i influxdb_${INFLUX_VERSION}_amd64.deb
-
-# install kapacitor
-wget https://dl.influxdata.com/kapacitor/releases/kapacitor_${KAPACITOR_VERSION}_amd64.deb 2> /dev/null
-md5sum kapacitor_${KAPACITOR_VERSION}_amd64.deb | grep $KAPACITOR_CHECKSUM > /dev/null
-if [ $? == 1 ]; then
-	echo "Kapacitor download failed checksum"
-	exit 1
-fi
-dpkg -i kapacitor_${KAPACITOR_VERSION}_amd64.deb
-
-# install Chronograf
-wget https://dl.influxdata.com/chronograf/releases/chronograf_${CHRONOGRAF_VERSION}_amd64.deb 2> /dev/null
-sha256sum chronograf_${CHRONOGRAF_VERSION}_amd64.deb | grep $CHRONOGRAF_CHECKSUM > /dev/null
-if [ $? == 1 ]; then
-	echo "Chronograf download failed checksum"
-	exit 1
-fi
-dpkg -i chronograf_${CHRONOGRAF_VERSION}_amd64.deb
-
-systemctl start influxdb
-systemctl start kapacitor
-systemctl start chronograf
-
-## CLMC-SERVICE
-## ----------------------------------------------------------------------------------
-echo "----> Configuring virtualenvwrapper"
-export WORKON_HOME=$HOME/.virtualenvs
-source /usr/local/bin/virtualenvwrapper.sh
-
-# check the mkvirtualenv with a return value of 1 if version comes back correctly
-mkvirtualenv --version
-if [ $? -ne 1 ] ; then
-        echo "Failed: installing virtualenvwrapper"
-		exit 1
-fi
-
-# create CLMC virtual environment - and check
-echo "----> Making CLMC Python environment"
-mkvirtualenv CLMC
-if [ $? -ne 0 ] ; then
-        echo "Failed: creating CLMC python environment"
-		exit 1
-fi
-
-# switch the CLMC environment - and check
-echo "----> Switching to use CLMC python environment"
-workon CLMC
-if [ $? -ne 0 ] ; then
-        echo "Failed: switching to CLMC python environment"
-		exit 1
-fi
-
-# install tox - and check
-echo "----> Installing TOX"
-pip3 install tox
-tox --version
-if [ $? -ne 0 ] ; then
-        echo "Failed: installing tox"
-		exit 1
-fi
-
-# navigate to the clmc-webservice - and check
-echo "----> Moving to CLMC webservice"
-cd ${REPO_ROOT}/src/service
-if [ $? -ne 0 ] ; then
-        echo "Failed: could not find clmc-webservice"
-		exit 1
-fi
-
-# running tests using tox
-echo "----> Running tox"
-TOX_OUTPUT="$(tox)"
-# check if tox output contains the 'congratulations :)' bit for tests passed
-if [[ $TOX_OUTPUT != *"congratulations :)"* ]]; then
-  echo $TOX_OUTPUT
-  echo "CLMC service unit tests failed."
-  exit 1
-fi
-echo "----> Tox execution of unit tests passed successfully"
-
-# install the service
-echo "----> Installing CLMC web service"
-pip3 install  .
-if [ $? -ne 0 ] ; then
-        echo "Failed: installing clmc-webservice"
-		exit 1
-fi
-
-# create directory for CLMC service logs
-echo "----> Creating CLMC web service log directory"
-mkdir -p /var/log/flame/clmc
-
-# Install minioclmc as systemctl service
-# -----------------------------------------------------------------------
-mkdir -p /opt/flame/clmc
-start_script_file="/opt/flame/clmc/start.sh"
-echo "#!/bin/bash" > $start_script_file
-echo "export WORKON_HOME=${HOME}/.virtualenvs" >> $start_script_file
-echo "source /usr/local/bin/virtualenvwrapper.sh" >> $start_script_file
-echo "workon CLMC" >> $start_script_file
-echo "pserve ${REPO_ROOT}/src/service/production.ini &" >> $start_script_file
-
-chmod 755 $start_script_file
-
-file="/lib/systemd/system/flameclmc.service"
-echo "[Unit]" > $file
-echo "Description=flameclmc" >> $file
-echo "After=network.target" >> $file
-echo "" >> $file
-echo "[Service]" >> $file
-echo "Type=forking" >> $file
-echo "ExecStart=${start_script_file}" >> $file
-echo "" >> $file
-echo "[Install]" >> $file
-echo "WantedBy=multi-user.target" >> $file
-
-systemctl daemon-reload
-systemctl enable flameclmc.service
-systemctl start flameclmc.service
-
-# wait for the clmc service to start
-while ! nc -z localhost 9080
-do
-  echo "Waiting for clmc service port 9080 to be ready on localhost..."
-  sleep 5
-done
-
-# configure the CLMC service
-JSON="{\"aggregator_report_period\": ${REPORT_PERIOD}, \"aggregator_database_name\": \"${DATABASE_NAME}\", \"aggregator_database_url\": \"${INFLUX_URL}\"}"
-echo "CONFIG JSON=${JSON}"
-curl -H 'Content-Type: application/json' -X PUT -d "${JSON}" http://localhost:9080/aggregator/config
-
-# start the aggregator
-JSON="{\"action\": \"start\"}"
-echo "START ACTION JSON=${JSON}"
-curl -H 'Content-Type: application/json' -X PUT -d "${JSON}" http://localhost:9080/aggregator/control
 
diff --git a/scripts/test/fixture.sh b/scripts/test/fixture.sh
index 26a5058..fc94e8e 100755
--- a/scripts/test/fixture.sh
+++ b/scripts/test/fixture.sh
@@ -1,8 +1,8 @@
 #!/bin/bash
 
-usage() { 
+usage() {
     echo "Usage: $0 create|start|stop|destroy [-f config_file] [-r repo_root] [-c service_name]" 1>&2
-    exit 1 
+    exit 1
 }
 
 create() {
@@ -30,11 +30,11 @@ create() {
         cp -rf ${repo_root}/src ${container_vagrant_dir}
 
         # start the container
-        echo "Starting: ${service_name}"        
+        echo "Starting: ${service_name}"
         lxc-start -n ${service_name}
         echo "Waiting for container to start: ${service_name}"
-        STARTED="0"        
-        while [ "$STARTED" == "0" ]; do STARTED=$(lxc-info -n ${service_name} -i | wc -l); done; 
+        STARTED="0"
+        while [ "$STARTED" == "0" ]; do STARTED=$(lxc-info -n ${service_name} -i | wc -l); done;
 
         # provision software into each container
         echo "Provisioning: ${service_name}"
@@ -72,16 +72,16 @@ create() {
             # copy telegraf configuration templates
             cp -f ${repo_root}/scripts/clmc-agent/telegraf.conf ${container_dir}/etc/telegraf/
             cp -f ${repo_root}/scripts/clmc-agent/telegraf_output.conf ${container_dir}/etc/telegraf/telegraf.d/
-            cp ${repo_root}/src/test/clmctest/services/${sf_id}/telegraf_${sf_id}.conf ${container_dir}/etc/telegraf/telegraf.d/
+            # copy the 'host' config into all service containers
+            cp ${repo_root}/src/test/clmctest/services/host/telegraf*.conf ${container_dir}/etc/telegraf/telegraf.d/
+            # copy the service-specific config
+            cp ${repo_root}/src/test/clmctest/services/${sf_id}/telegraf*.conf ${container_dir}/etc/telegraf/telegraf.d/
 
             # replace telegraf template with container parameters
-            # @todo do we really need both scripts to do this?
-            cmd=/vagrant/scripts/clmc-agent/configure_template.sh
-            lxc-attach -n ${service_name} -- ${cmd}
             cmd="/vagrant/scripts/clmc-agent/configure.sh ${location} ${sfc_id} ${sfc_id_instance} ${sf_id} ${sf_id_instance} ${ipendpoint_id} ${sr_id} ${influxdb_url} ${database_name}"
             lxc-attach -n ${service_name} -- ${cmd}
 
-            # start telegraf
+            # restart telegraf
             lxc-attach -n ${service_name} -- service telegraf restart
         fi
 
@@ -96,12 +96,12 @@ create() {
                 host_port=$(_jq '.host')
                 iptables -t nat -A PREROUTING -p tcp -i enp0s3 --dport ${host_port} -j DNAT --to-destination ${ip}:${guest_port}
             done
-        fi        
+        fi
     fi
 }
 
 start() {
-    service_name=$1 
+    service_name=$1
     if lxc-info -n ${service_name}; then
         echo "Starting container: ${service_name}"
         lxc-start -n ${service_name}
@@ -109,7 +109,7 @@ start() {
 }
 
 stop() {
-    service_name=$1  
+    service_name=$1
     if lxc-info -n ${service_name}; then
         echo "Stopping container: ${service_name}"
         lxc-stop -n ${service_name}
@@ -142,8 +142,8 @@ destroy() {
                 host_port=$(_jq '.host')
                 iptables -t nat -D PREROUTING -p tcp -i enp0s3 --dport ${host_port} -j DNAT --to-destination ${ip}:${guest_port}
             done
-        fi        
-    fi    
+        fi
+    fi
 }
 
 # inc option index by 1 as first argument is the command and not parsed by getopts
@@ -159,7 +159,7 @@ while getopts "hf:r:c:" opt; do
   case $opt in
     h) usage; exit ;;
     f) config_file=${OPTARG} ;;
-    r) repo_root=${OPTARG} ;;        
+    r) repo_root=${OPTARG} ;;
     c) container=${OPTARG} ;;
     \?) usage ;;
   esac
@@ -179,7 +179,7 @@ if [ ! -d ${repo_root} ]; then
     exit 1
 fi
 
-# iterate of list of services in configuration file 
+# iterate of list of services in configuration file
 command=$1
 service_names=$(jq -r '.[].name' ${config_file})
 for service_name in $service_names; do
@@ -193,10 +193,10 @@ for service_name in $service_names; do
                 start ${service_name} ${config_file} ${repo_root}
                 ;;
             stop)
-                stop ${service_name} ${config_file} ${repo_root} 
+                stop ${service_name} ${config_file} ${repo_root}
                 ;;
             destroy)
-                destroy ${service_name} ${config_file} ${repo_root} 
+                destroy ${service_name} ${config_file} ${repo_root}
                 ;;
             *)
                 usage
diff --git a/src/test/clmctest/services/host/telegraf_host.conf b/src/test/clmctest/services/host/telegraf_host.conf
index b1fc8dd..a1441e7 100644
--- a/src/test/clmctest/services/host/telegraf_host.conf
+++ b/src/test/clmctest/services/host/telegraf_host.conf
@@ -1,36 +1,3 @@
-## © University of Southampton IT Innovation Centre, 2018
-##
-## Copyright in this software belongs to University of Southampton
-## IT Innovation Centre of Gamma House, Enterprise Road, 
-## Chilworth Science Park, Southampton, SO16 7NS, UK.
-##
-## This software may not be used, sold, licensed, transferred, copied
-## or reproduced in whole or in part in any manner or form or in or
-## on any media by any person other than in accordance with the terms
-## of the Licence Agreement supplied with the software, or otherwise
-## without the prior written consent of the copyright owners.
-##
-## This software is distributed WITHOUT ANY WARRANTY, without even the
-## implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-## PURPOSE, except where stated in the Licence Agreement supplied with
-## the software.
-##
-##      Created By :            Michael Boniface
-##      Created Date :          20-03-2018
-##      Created for Project :   FLAME
-
-###############################################################################
-#                                  INPUTS                                     #
-###############################################################################
-# # Read metrics about network interface usage
- [[inputs.net]]
-#   ## By default, telegraf gathers stats from any up interface (excluding loopback)
-#   ## Setting interfaces will tell it to gather these explicit interfaces,
-#   ## regardless of status.
-#   ##
-#   # interfaces = ["eth0"]
-
-# Read metrics about cpu usage
 [[inputs.cpu]]
   ## Whether to report per-cpu stats or not
   percpu = true
@@ -39,26 +6,18 @@
   ## If true, collect raw CPU time metrics.
   collect_cpu_time = false
   ## If true, compute and report the sum of all non-idle CPU states.
- #report_active = false
-
-
-# Read metrics about disk usage by mount point
+  report_active = false
 [[inputs.disk]]
-  ## By default, telegraf gather stats for all mountpoints.
-  ## Setting mountpoints will restrict the stats to the specified mountpoints.
+  ## By default stats will be gathered for all mount points.
+  ## Set mount_points will restrict the stats to only the specified mount points.
   # mount_points = ["/"]
-
-  ## Ignore some mountpoints by filesystem type. For example (dev)tmpfs (usually
-  ## present on /run, /var/run, /dev/shm or /dev).
+  ## Ignore mount points by filesystem type.
   ignore_fs = ["tmpfs", "devtmpfs", "devfs"]
-
-
-# Read metrics about disk IO by device
 [[inputs.diskio]]
   ## By default, telegraf will gather stats for all devices including
   ## disk partitions.
   ## Setting devices will restrict the stats to the specified devices.
-  # devices = ["sda", "sdb"]
+  devices = ["sda1"]
   ## Uncomment the following line if you need disk serial numbers.
   # skip_serial_number = false
   #
@@ -78,23 +37,21 @@
   ## The typical use case is for LVM volumes, to get the VG/LV name instead of
   ## the near-meaningless DM-0 name.
   # name_templates = ["$ID_FS_LABEL","$DM_VG_NAME/$DM_LV_NAME"]
-
-# Read metrics about memory usage
+[[inputs.kernel]]
+  # no configuration
 [[inputs.mem]]
   # no configuration
+[[inputs.processes]]
+  # no configuration
+[[inputs.swap]]
+  # no configuration
+[[inputs.system]]
+  # no configuration
+[[inputs.net]]
+[[inputs.netstat]]
+  # no configuration
 
-# # Influx HTTP write listener
+# Influx HTTP write listener
 [[inputs.http_listener]]
   ## Address and port to host HTTP listener on
   service_address = ":8186"
-
-  ## timeouts
-  read_timeout = "10s"
-  write_timeout = "10s"
-
-  ## HTTPS
-  #tls_cert= "/etc/telegraf/cert.pem"
-  #tls_key = "/etc/telegraf/key.pem"
-
-  ## MTLS
-  #tls_allowed_cacerts = ["/etc/telegraf/clientca.pem"]
\ No newline at end of file
diff --git a/src/test/clmctest/services/minio/telegraf_minio.conf b/src/test/clmctest/services/minio/telegraf_minio.conf
index f184957..afba368 100644
--- a/src/test/clmctest/services/minio/telegraf_minio.conf
+++ b/src/test/clmctest/services/minio/telegraf_minio.conf
@@ -20,4 +20,18 @@
 ##      Created for Project :   FLAME
 
 [[inputs.prometheus]]
-    urls = ["http://localhost:9000/minio/prometheus/metrics"]
\ No newline at end of file
+    urls = ["http://localhost:9000/minio/prometheus/metrics"]
+
+[[inputs.procstat]]
+  #pid_file = "/var/run/nginx.pid"
+  exe = "minio"
+
+# [[inputs.systemctl]]
+#   ## Service array
+#   services = [
+#     "minio.service"
+#   ]
+
+#   ## Sample rate for sampling state of service.
+#   ## Must be greter that the collection_interval/2
+#   #sample_rate = 2
\ No newline at end of file
diff --git a/src/test/clmctest/services/nginx/telegraf_nginx.conf b/src/test/clmctest/services/nginx/telegraf_nginx.conf
index cce0734..11bbcc5 100644
--- a/src/test/clmctest/services/nginx/telegraf_nginx.conf
+++ b/src/test/clmctest/services/nginx/telegraf_nginx.conf
@@ -23,14 +23,12 @@
 [[inputs.nginx]]
   ## An array of Nginx stub_status URI to gather stats.
   urls = ["http://localhost:80/nginx_status"]
-
   ## HTTP response timeout (default: 5s)
-#  response_timeout = "5s"
+  #  response_timeout = "5s"
 
-# # Influx HTTP write listener
-[[inputs.http_listener]]
-  ## Address and port to host HTTP listener on
-  service_address = ":8186"
+[[inputs.procstat]]
+  #pid_file = "/var/run/nginx.pid"
+  exe = "nginx"
 
 [[inputs.systemctl]]
 	services = [
@@ -42,4 +40,4 @@
 	
 	## Logging level for this plugin according to logrus log levels
 	## "debug", "info", "warning", "error", "fatal", "panic"
-	log_level = "debug"
\ No newline at end of file
+	log_level = "debug"
diff --git a/src/test/clmctest/workshopdemo/dashboards/dc_dash.json b/src/test/clmctest/workshopdemo/dashboards/dc_dash.json
new file mode 100644
index 0000000..7d98cf5
--- /dev/null
+++ b/src/test/clmctest/workshopdemo/dashboards/dc_dash.json
@@ -0,0 +1,147 @@
+{
+  "id": 4,
+  "cells": [
+    {
+      "i": "5df7003f-6367-40e1-9eb4-c3ac9d92b05c",
+      "x": 0,
+      "y": 0,
+      "w": 6,
+      "h": 4,
+      "name": "Mean %CPU",
+      "queries": [
+        {
+          "query": "SELECT mean(\"cpu_usage\") AS \"mean_cpu_usage\" FROM \"MSDemo\".\"autogen\".\"procstat\" WHERE time > :dashboardTime: AND \"location\"= :location: GROUP BY time(:interval:) FILL(null)",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "SELECT mean(\"cpu_usage\") AS \"mean_cpu_usage\" FROM \"MSDemo\".\"autogen\".\"procstat\" WHERE time > :dashboardTime: AND \"location\"= :location: GROUP BY time(:interval:) FILL(null)",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/1"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "Mean %CPU",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line",
+      "colors": [
+        {
+          "id": "2f5981b6-6f85-4efc-989c-9da90fe54189",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "696e12a0-4b7d-4a8f-9d95-a5be5ef3e8b8",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "f3788c7b-bdbe-4d14-8439-c6f3b4db75e9",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/4/cells/5df7003f-6367-40e1-9eb4-c3ac9d92b05c"
+      }
+    }
+  ],
+  "templates": [
+    {
+      "tempVar": ":location:",
+      "values": [
+        {
+          "value": "DC3",
+          "type": "tagValue",
+          "selected": true
+        }
+      ],
+      "id": "51e972f9-c4c3-4bcf-a264-9f95ffa86017",
+      "type": "tagValues",
+      "label": "",
+      "query": {
+        "influxql": "SHOW TAG VALUES ON :database: FROM :measurement: WITH KEY=:tagKey:",
+        "db": "MSDemo",
+        "measurement": "cpu",
+        "tagKey": "location",
+        "fieldKey": ""
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/4/templates/51e972f9-c4c3-4bcf-a264-9f95ffa86017"
+      }
+    }
+  ],
+  "name": "Data Centre",
+  "organization": "default",
+  "links": {
+    "self": "/chronograf/v1/dashboards/4",
+    "cells": "/chronograf/v1/dashboards/4/cells",
+    "templates": "/chronograf/v1/dashboards/4/templates"
+  }
+}
diff --git a/src/test/clmctest/workshopdemo/dashboards/minio_dash.json b/src/test/clmctest/workshopdemo/dashboards/minio_dash.json
new file mode 100644
index 0000000..9063650
--- /dev/null
+++ b/src/test/clmctest/workshopdemo/dashboards/minio_dash.json
@@ -0,0 +1,720 @@
+{
+  "id": 3,
+  "cells": [
+    {
+      "i": "e62df88a-1e9c-4055-a157-2e01f9f340f2",
+      "x": 6,
+      "y": 12,
+      "w": 6,
+      "h": 4,
+      "name": "minio2: Network RX",
+      "queries": [
+        {
+          "query": "SELECT derivative(max(\"bytes_recv\")) / 62914560 AS \"RX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"=:minio2: GROUP BY time(1m)",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "SELECT derivative(max(\"bytes_recv\")) / 62914560 AS \"RX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"=:minio2: GROUP BY time(1m)",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/2"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "MB / s",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line-stepplot",
+      "colors": [
+        {
+          "id": "19960cdc-ba07-4374-815e-83ac91d2d09c",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "0dba565e-1e38-4632-817c-30bc795ae63b",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "cc618b53-490d-43f5-aac5-103672a87c2f",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/cells/e62df88a-1e9c-4055-a157-2e01f9f340f2"
+      }
+    },
+    {
+      "i": "0e1b3956-71c4-4130-80f3-b69fcc9831d8",
+      "x": 0,
+      "y": 12,
+      "w": 6,
+      "h": 4,
+      "name": "minio1: Network RX",
+      "queries": [
+        {
+          "query": "SELECT derivative(max(\"bytes_recv\")) / 62914560 AS \"RX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"=:minio1: GROUP BY time(1m)",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "SELECT derivative(max(\"bytes_recv\")) / 62914560 AS \"RX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"=:minio1: GROUP BY time(1m)",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/2"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "MB / s",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line-stepplot",
+      "colors": [
+        {
+          "id": "19960cdc-ba07-4374-815e-83ac91d2d09c",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "0dba565e-1e38-4632-817c-30bc795ae63b",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "cc618b53-490d-43f5-aac5-103672a87c2f",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/cells/0e1b3956-71c4-4130-80f3-b69fcc9831d8"
+      }
+    },
+    {
+      "i": "ab98bded-9b3d-43f1-bf4e-ba21f692cae9",
+      "x": 6,
+      "y": 8,
+      "w": 6,
+      "h": 4,
+      "name": "minio2: Network TX",
+      "queries": [
+        {
+          "query": "SELECT derivative(max(\"bytes_sent\")) / 62914560 AS \"TX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"=:minio2: GROUP BY time(1m)",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "SELECT derivative(max(\"bytes_sent\")) / 62914560 AS \"TX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"=:minio2: GROUP BY time(1m)",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/2"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "MB / s",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line-stepplot",
+      "colors": [
+        {
+          "id": "19960cdc-ba07-4374-815e-83ac91d2d09c",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "0dba565e-1e38-4632-817c-30bc795ae63b",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "cc618b53-490d-43f5-aac5-103672a87c2f",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/cells/ab98bded-9b3d-43f1-bf4e-ba21f692cae9"
+      }
+    },
+    {
+      "i": "f36a8d10-2750-46a7-810f-34399c4e9639",
+      "x": 0,
+      "y": 8,
+      "w": 6,
+      "h": 4,
+      "name": "minio1: Network TX",
+      "queries": [
+        {
+          "query": "SELECT derivative(max(\"bytes_sent\")) / 62914560 AS \"TX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"=:minio1: GROUP BY time(1m)",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "SELECT derivative(max(\"bytes_sent\")) / 62914560 AS \"TX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"=:minio1: GROUP BY time(1m)",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/2"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "MB / s",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line-stepplot",
+      "colors": [
+        {
+          "id": "19960cdc-ba07-4374-815e-83ac91d2d09c",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "0dba565e-1e38-4632-817c-30bc795ae63b",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "cc618b53-490d-43f5-aac5-103672a87c2f",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/cells/f36a8d10-2750-46a7-810f-34399c4e9639"
+      }
+    },
+    {
+      "i": "bb5e93f2-113c-46a1-ad10-da7fefe67c77",
+      "x": 6,
+      "y": 0,
+      "w": 6,
+      "h": 8,
+      "name": "minio2: Response time",
+      "queries": [
+        {
+          "query": "SELECT 100*last(\"0.001\") / last(\"count\") AS \"0.001\", 100*last(\"0.003\") / last(\"count\") AS \"0.003\", 100*last(\"0.005\") / last(\"count\") AS \"0.005\", 100*last(\"0.1\") / last(\"count\") AS \"0.1\", 100*last(\"0.5\") / last(\"count\") AS \"0.5\", 100*last(\"1\") / last(\"count\") AS \"1\" FROM \"MSDemo\".\"autogen\".\"minio_http_requests_duration_seconds\" WHERE time > :dashboardTime: and \"ipendpoint\"=:minio2: GROUP BY time(:interval:) FILL(null)",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "SELECT 100*last(\"0.001\") / last(\"count\") AS \"0.001\", 100*last(\"0.003\") / last(\"count\") AS \"0.003\", 100*last(\"0.005\") / last(\"count\") AS \"0.005\", 100*last(\"0.1\") / last(\"count\") AS \"0.1\", 100*last(\"0.5\") / last(\"count\") AS \"0.5\", 100*last(\"1\") / last(\"count\") AS \"1\" FROM \"MSDemo\".\"autogen\".\"minio_http_requests_duration_seconds\" WHERE time > :dashboardTime: and \"ipendpoint\"=:minio2: GROUP BY time(:interval:) FILL(null)",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/2"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "% response",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line",
+      "colors": [
+        {
+          "id": "3d1219b0-2bae-4784-a7e1-9fab2e72ed55",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "63c18823-a5e6-4aca-9a1e-29f820096c00",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "b567535f-1289-48df-b96f-5e46d8d61fb1",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/cells/bb5e93f2-113c-46a1-ad10-da7fefe67c77"
+      }
+    },
+    {
+      "i": "fef268eb-b3d5-4415-94bc-bc23d2795d05",
+      "x": 0,
+      "y": 0,
+      "w": 6,
+      "h": 8,
+      "name": "minio1: Response time",
+      "queries": [
+        {
+          "query": "SELECT 100*last(\"0.001\") / last(\"count\") AS \"0.001\", 100*last(\"0.003\") / last(\"count\") AS \"0.003\", 100*last(\"0.005\") / last(\"count\") AS \"0.005\", 100*last(\"0.1\") / last(\"count\") AS \"0.1\", 100*last(\"0.5\") / last(\"count\") AS \"0.5\", 100*last(\"1\") / last(\"count\") AS \"1\" FROM \"MSDemo\".\"autogen\".\"minio_http_requests_duration_seconds\" WHERE time > :dashboardTime: and \"ipendpoint\"=:minio1: GROUP BY time(:interval:) FILL(null)",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "SELECT 100*last(\"0.001\") / last(\"count\") AS \"0.001\", 100*last(\"0.003\") / last(\"count\") AS \"0.003\", 100*last(\"0.005\") / last(\"count\") AS \"0.005\", 100*last(\"0.1\") / last(\"count\") AS \"0.1\", 100*last(\"0.5\") / last(\"count\") AS \"0.5\", 100*last(\"1\") / last(\"count\") AS \"1\" FROM \"MSDemo\".\"autogen\".\"minio_http_requests_duration_seconds\" WHERE time > :dashboardTime: and \"ipendpoint\"=:minio1: GROUP BY time(:interval:) FILL(null)",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/2"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "% response",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line",
+      "colors": [
+        {
+          "id": "3d1219b0-2bae-4784-a7e1-9fab2e72ed55",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "63c18823-a5e6-4aca-9a1e-29f820096c00",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "b567535f-1289-48df-b96f-5e46d8d61fb1",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/cells/fef268eb-b3d5-4415-94bc-bc23d2795d05"
+      }
+    }
+  ],
+  "templates": [
+    {
+      "tempVar": ":minio1:",
+      "values": [
+        {
+          "value": "minio_1_ep1",
+          "type": "tagValue",
+          "selected": true
+        }
+      ],
+      "id": "ad1cd560-3ae5-4cac-8c67-46970649b641",
+      "type": "tagValues",
+      "label": "",
+      "query": {
+        "influxql": "SHOW TAG VALUES ON :database: FROM :measurement: WITH KEY=:tagKey:",
+        "db": "MSDemo",
+        "measurement": "cpu",
+        "tagKey": "ipendpoint",
+        "fieldKey": ""
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/templates/ad1cd560-3ae5-4cac-8c67-46970649b641"
+      }
+    },
+    {
+      "tempVar": ":minio2:",
+      "values": [
+        {
+          "value": "minio_1_ep2",
+          "type": "tagValue",
+          "selected": true
+        }
+      ],
+      "id": "11f88a29-0534-42af-bee7-a1f8ae653ba9",
+      "type": "tagValues",
+      "label": "",
+      "query": {
+        "influxql": "SHOW TAG VALUES ON :database: FROM :measurement: WITH KEY=:tagKey:",
+        "db": "MSDemo",
+        "measurement": "cpu",
+        "tagKey": "ipendpoint",
+        "fieldKey": ""
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/templates/11f88a29-0534-42af-bee7-a1f8ae653ba9"
+      }
+    }
+  ],
+  "name": "Minio endpoints",
+  "organization": "default",
+  "links": {
+    "self": "/chronograf/v1/dashboards/3",
+    "cells": "/chronograf/v1/dashboards/3/cells",
+    "templates": "/chronograf/v1/dashboards/3/templates"
+  }
+}
diff --git a/src/test/clmctest/workshopdemo/dashboards/nginx_dash.json b/src/test/clmctest/workshopdemo/dashboards/nginx_dash.json
new file mode 100644
index 0000000..d605726
--- /dev/null
+++ b/src/test/clmctest/workshopdemo/dashboards/nginx_dash.json
@@ -0,0 +1,935 @@
+{
+  "id": 3,
+  "cells": [
+    {
+      "i": "1ba178dd-a091-47f4-9c46-31dff88403b1",
+      "x": 5,
+      "y": 6,
+      "w": 5,
+      "h": 2,
+      "name": "nginx_ep_2: Requests / s",
+      "queries": [
+        {
+          "query": "SELECT non_negative_derivative(\"requests\") AS \"requests/sec\" FROM \"MSDemo\".\"autogen\".\"nginx\" WHERE time > :dashboardTime: AND \"ipendpoint\"='nginx_1_ep2'",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "SELECT non_negative_derivative(\"requests\") AS \"requests/sec\" FROM \"MSDemo\".\"autogen\".\"nginx\" WHERE time > :dashboardTime: AND \"ipendpoint\"='nginx_1_ep2'",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/1"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "requests / s",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line",
+      "colors": [
+        {
+          "id": "2f5981b6-6f85-4efc-989c-9da90fe54189",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "696e12a0-4b7d-4a8f-9d95-a5be5ef3e8b8",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "f3788c7b-bdbe-4d14-8439-c6f3b4db75e9",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/cells/1ba178dd-a091-47f4-9c46-31dff88403b1"
+      }
+    },
+    {
+      "i": "6cac8c3f-ad9d-4394-b043-637d44d39a90",
+      "x": 5,
+      "y": 4,
+      "w": 5,
+      "h": 2,
+      "name": "nginx_ep_2: Network TX",
+      "queries": [
+        {
+          "query": "SELECT derivative(max(\"bytes_sent\")) / 62914560 AS \"TX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"='nginx_1_ep2' GROUP BY time(1m)",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "SELECT derivative(max(\"bytes_sent\")) / 62914560 AS \"TX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"='nginx_1_ep2' GROUP BY time(1m)",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/1"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "Mb / s",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line-stepplot",
+      "colors": [
+        {
+          "id": "2f5981b6-6f85-4efc-989c-9da90fe54189",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "696e12a0-4b7d-4a8f-9d95-a5be5ef3e8b8",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "f3788c7b-bdbe-4d14-8439-c6f3b4db75e9",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/cells/6cac8c3f-ad9d-4394-b043-637d44d39a90"
+      }
+    },
+    {
+      "i": "433d8774-f57a-4d80-8008-5b24c62de8f0",
+      "x": 5,
+      "y": 2,
+      "w": 5,
+      "h": 2,
+      "name": "nginx_ep_2: Network RX",
+      "queries": [
+        {
+          "query": "SELECT derivative(max(\"bytes_sent\")) / 62914560 AS \"RX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"='nginx_1_ep2' GROUP By time(1m) fill(previous)",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "SELECT derivative(max(\"bytes_sent\")) / 62914560 AS \"RX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"='nginx_1_ep2' GROUP By time(1m) fill(previous)",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/1"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "Mb / s",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line-stepplot",
+      "colors": [
+        {
+          "id": "2f5981b6-6f85-4efc-989c-9da90fe54189",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "696e12a0-4b7d-4a8f-9d95-a5be5ef3e8b8",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "f3788c7b-bdbe-4d14-8439-c6f3b4db75e9",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/cells/433d8774-f57a-4d80-8008-5b24c62de8f0"
+      }
+    },
+    {
+      "i": "e630e38e-2ba1-45e6-b0cd-cc294259d6de",
+      "x": 0,
+      "y": 6,
+      "w": 5,
+      "h": 2,
+      "name": "nginx_ep_1: Requests / s",
+      "queries": [
+        {
+          "query": "SELECT non_negative_derivative(\"requests\") AS \"requests/sec\" FROM \"MSDemo\".\"autogen\".\"nginx\" WHERE time > :dashboardTime: AND \"ipendpoint\"='nginx_1_ep1'",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "SELECT non_negative_derivative(\"requests\") AS \"requests/sec\" FROM \"MSDemo\".\"autogen\".\"nginx\" WHERE time > :dashboardTime: AND \"ipendpoint\"='nginx_1_ep1'",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/1"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "requests / s",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line",
+      "colors": [
+        {
+          "id": "2f5981b6-6f85-4efc-989c-9da90fe54189",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "696e12a0-4b7d-4a8f-9d95-a5be5ef3e8b8",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "f3788c7b-bdbe-4d14-8439-c6f3b4db75e9",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/cells/e630e38e-2ba1-45e6-b0cd-cc294259d6de"
+      }
+    },
+    {
+      "i": "af1fcc24-00a1-459b-b1c8-f41834032891",
+      "x": 0,
+      "y": 4,
+      "w": 5,
+      "h": 2,
+      "name": "nginx_ep_1: Network TX",
+      "queries": [
+        {
+          "query": "SELECT derivative(max(\"bytes_sent\")) / 62914560 AS \"TX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"='nginx_1_ep1' GROUP BY time(1m)",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "SELECT derivative(max(\"bytes_sent\")) / 62914560 AS \"TX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"='nginx_1_ep1' GROUP BY time(1m)",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/1"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "Mb / s",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line-stepplot",
+      "colors": [
+        {
+          "id": "2f5981b6-6f85-4efc-989c-9da90fe54189",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "696e12a0-4b7d-4a8f-9d95-a5be5ef3e8b8",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "f3788c7b-bdbe-4d14-8439-c6f3b4db75e9",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/cells/af1fcc24-00a1-459b-b1c8-f41834032891"
+      }
+    },
+    {
+      "i": "23b48164-50d6-4c54-b284-05c8125bfa66",
+      "x": 0,
+      "y": 2,
+      "w": 5,
+      "h": 2,
+      "name": "nginx_ep_1: Network RX",
+      "queries": [
+        {
+          "query": "SELECT derivative(max(\"bytes_sent\")) / 62914560 AS \"RX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"='nginx_1_ep1' GROUP By time(1m) fill(previous)",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "SELECT derivative(max(\"bytes_sent\")) / 62914560 AS \"RX_Mb_per_second\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"ipendpoint\"='nginx_1_ep1' GROUP By time(1m) fill(previous)",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/1"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "Mb / s",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line-stepplot",
+      "colors": [
+        {
+          "id": "2f5981b6-6f85-4efc-989c-9da90fe54189",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "696e12a0-4b7d-4a8f-9d95-a5be5ef3e8b8",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "f3788c7b-bdbe-4d14-8439-c6f3b4db75e9",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/cells/23b48164-50d6-4c54-b284-05c8125bfa66"
+      }
+    },
+    {
+      "i": "9a5fce61-3013-4b17-aece-9b4252c7d4ab",
+      "x": 5,
+      "y": 0,
+      "w": 5,
+      "h": 2,
+      "name": "nginx_ep_2: CPU usage",
+      "queries": [
+        {
+          "query": "SELECT mean(\"cpu_usage\") AS \"mean_cpu_usage\" FROM \"MSDemo\".\"autogen\".\"procstat\" WHERE time > :dashboardTime: AND \"exe\"='nginx' AND \"ipendpoint\"='nginx_1_ep2' GROUP BY time(:interval:) FILL(null)",
+          "queryConfig": {
+            "database": "MSDemo",
+            "measurement": "procstat",
+            "retentionPolicy": "autogen",
+            "fields": [
+              {
+                "value": "mean",
+                "type": "func",
+                "alias": "mean_cpu_usage",
+                "args": [
+                  {
+                    "value": "cpu_usage",
+                    "type": "field",
+                    "alias": ""
+                  }
+                ]
+              }
+            ],
+            "tags": {
+              "exe": [
+                "nginx"
+              ],
+              "ipendpoint": [
+                "nginx_1_ep2"
+              ]
+            },
+            "groupBy": {
+              "time": "auto",
+              "tags": []
+            },
+            "areTagsAccepted": true,
+            "fill": "null",
+            "rawText": null,
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/1"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "%CPU",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line",
+      "colors": [
+        {
+          "id": "2f5981b6-6f85-4efc-989c-9da90fe54189",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "696e12a0-4b7d-4a8f-9d95-a5be5ef3e8b8",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "f3788c7b-bdbe-4d14-8439-c6f3b4db75e9",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/cells/9a5fce61-3013-4b17-aece-9b4252c7d4ab"
+      }
+    },
+    {
+      "i": "2cdaa83a-95d1-4a5f-9611-8bebe8b3035a",
+      "x": 0,
+      "y": 0,
+      "w": 5,
+      "h": 2,
+      "name": "nginx_ep_1: CPU usage",
+      "queries": [
+        {
+          "query": "SELECT mean(\"cpu_usage\") AS \"mean_cpu_usage\" FROM \"MSDemo\".\"autogen\".\"procstat\" WHERE time > :dashboardTime: AND \"exe\"='nginx' AND \"ipendpoint\"='nginx_1_ep1' GROUP BY time(:interval:) FILL(null)",
+          "queryConfig": {
+            "database": "MSDemo",
+            "measurement": "procstat",
+            "retentionPolicy": "autogen",
+            "fields": [
+              {
+                "value": "mean",
+                "type": "func",
+                "alias": "mean_cpu_usage",
+                "args": [
+                  {
+                    "value": "cpu_usage",
+                    "type": "field",
+                    "alias": ""
+                  }
+                ]
+              }
+            ],
+            "tags": {
+              "exe": [
+                "nginx"
+              ],
+              "ipendpoint": [
+                "nginx_1_ep1"
+              ]
+            },
+            "groupBy": {
+              "time": "auto",
+              "tags": []
+            },
+            "areTagsAccepted": true,
+            "fill": "null",
+            "rawText": null,
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/1"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "%CPU",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line",
+      "colors": [
+        {
+          "id": "2f5981b6-6f85-4efc-989c-9da90fe54189",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "696e12a0-4b7d-4a8f-9d95-a5be5ef3e8b8",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "f3788c7b-bdbe-4d14-8439-c6f3b4db75e9",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/3/cells/2cdaa83a-95d1-4a5f-9611-8bebe8b3035a"
+      }
+    }
+  ],
+  "templates": [],
+  "name": "Nginx endpoints",
+  "organization": "default",
+  "links": {
+    "self": "/chronograf/v1/dashboards/3",
+    "cells": "/chronograf/v1/dashboards/3/cells",
+    "templates": "/chronograf/v1/dashboards/3/templates"
+  }
+}
diff --git a/src/test/clmctest/workshopdemo/dashboards/sf_dash.json b/src/test/clmctest/workshopdemo/dashboards/sf_dash.json
new file mode 100644
index 0000000..8430953
--- /dev/null
+++ b/src/test/clmctest/workshopdemo/dashboards/sf_dash.json
@@ -0,0 +1,500 @@
+{
+  "id": 2,
+  "cells": [
+    {
+      "i": "d4dad017-395e-4192-9a89-cfde2b5d133a",
+      "x": 5,
+      "y": 4,
+      "w": 5,
+      "h": 4,
+      "name": "Average MB/s sent/recv for service-function 2",
+      "queries": [
+        {
+          "query": "select derivative(total_RX_MB, 1m) / 60 as RX_MB_per_s, derivative(total_TX_MB, 1m) / 60 as TX_MB_per_s from (select sum(RX_MB) as total_RX_MB, sum(TX_MB) as total_TX_MB from (SELECT last(\"bytes_recv\") / 1048576 AS \"RX_MB\", last(\"bytes_sent\") / 1048576 AS \"TX_MB\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"sf\"=:sf2: GROUP BY time(1m), ipendpoint FILL(null)) group by time(1m)) ",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "select derivative(total_RX_MB, 1m) / 60 as RX_MB_per_s, derivative(total_TX_MB, 1m) / 60 as TX_MB_per_s from (select sum(RX_MB) as total_RX_MB, sum(TX_MB) as total_TX_MB from (SELECT last(\"bytes_recv\") / 1048576 AS \"RX_MB\", last(\"bytes_sent\") / 1048576 AS \"TX_MB\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"sf\"=:sf2: GROUP BY time(1m), ipendpoint FILL(null)) group by time(1m)) ",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/2"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "MB / sec",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line-stepplot",
+      "colors": [
+        {
+          "id": "2f5981b6-6f85-4efc-989c-9da90fe54189",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "696e12a0-4b7d-4a8f-9d95-a5be5ef3e8b8",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "f3788c7b-bdbe-4d14-8439-c6f3b4db75e9",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/2/cells/d4dad017-395e-4192-9a89-cfde2b5d133a"
+      }
+    },
+    {
+      "i": "8522ec33-51a1-4cd4-a02b-02525800c25e",
+      "x": 5,
+      "y": 0,
+      "w": 5,
+      "h": 4,
+      "name": "Total MB sent/recv for service-function 2",
+      "queries": [
+        {
+          "query": "select sum(RX_MB) as total_RX_MB, sum(TX_MB) as total_TX_MB from (SELECT last(\"bytes_recv\") / 1048576 AS \"RX_MB\", last(\"bytes_sent\") / 1048576 AS \"TX_MB\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"sf\"=:sf2: GROUP BY time(1m), ipendpoint FILL(null)) group by time(1m)",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "select sum(RX_MB) as total_RX_MB, sum(TX_MB) as total_TX_MB from (SELECT last(\"bytes_recv\") / 1048576 AS \"RX_MB\", last(\"bytes_sent\") / 1048576 AS \"TX_MB\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"sf\"=:sf2: GROUP BY time(1m), ipendpoint FILL(null)) group by time(1m)",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/2"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "MB",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line-stepplot",
+      "colors": [
+        {
+          "id": "2f5981b6-6f85-4efc-989c-9da90fe54189",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "696e12a0-4b7d-4a8f-9d95-a5be5ef3e8b8",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "f3788c7b-bdbe-4d14-8439-c6f3b4db75e9",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/2/cells/8522ec33-51a1-4cd4-a02b-02525800c25e"
+      }
+    },
+    {
+      "i": "dd7693ca-0622-41f0-9f2c-d9a468434097",
+      "x": 0,
+      "y": 4,
+      "w": 5,
+      "h": 4,
+      "name": "Average MB/s sent/recv for service-function 1",
+      "queries": [
+        {
+          "query": "select derivative(total_RX_MB, 1m) / 60 as RX_MB_per_s, derivative(total_TX_MB, 1m) / 60 as TX_MB_per_s from (select sum(RX_MB) as total_RX_MB, sum(TX_MB) as total_TX_MB from (SELECT last(\"bytes_recv\") / 1048576 AS \"RX_MB\", last(\"bytes_sent\") / 1048576 AS \"TX_MB\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"sf\"=:sf1: GROUP BY time(1m), ipendpoint FILL(null)) group by time(1m)) ",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "select derivative(total_RX_MB, 1m) / 60 as RX_MB_per_s, derivative(total_TX_MB, 1m) / 60 as TX_MB_per_s from (select sum(RX_MB) as total_RX_MB, sum(TX_MB) as total_TX_MB from (SELECT last(\"bytes_recv\") / 1048576 AS \"RX_MB\", last(\"bytes_sent\") / 1048576 AS \"TX_MB\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"sf\"=:sf1: GROUP BY time(1m), ipendpoint FILL(null)) group by time(1m)) ",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/2"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "MB / sec",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line-stepplot",
+      "colors": [
+        {
+          "id": "2f5981b6-6f85-4efc-989c-9da90fe54189",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "696e12a0-4b7d-4a8f-9d95-a5be5ef3e8b8",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "f3788c7b-bdbe-4d14-8439-c6f3b4db75e9",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/2/cells/dd7693ca-0622-41f0-9f2c-d9a468434097"
+      }
+    },
+    {
+      "i": "419b4e3c-1ca3-49ac-bc91-410bfbeb879a",
+      "x": 0,
+      "y": 0,
+      "w": 5,
+      "h": 4,
+      "name": "Total MB sent/recv for service-function 1",
+      "queries": [
+        {
+          "query": "select sum(RX_MB) as total_RX_MB, sum(TX_MB) as total_TX_MB from (SELECT last(\"bytes_recv\") / 1048576 AS \"RX_MB\", last(\"bytes_sent\") / 1048576 AS \"TX_MB\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"sf\"=:sf1: GROUP BY time(1m), ipendpoint FILL(null)) group by time(1m)",
+          "queryConfig": {
+            "database": "",
+            "measurement": "",
+            "retentionPolicy": "",
+            "fields": [],
+            "tags": {},
+            "groupBy": {
+              "time": "",
+              "tags": []
+            },
+            "areTagsAccepted": false,
+            "rawText": "select sum(RX_MB) as total_RX_MB, sum(TX_MB) as total_TX_MB from (SELECT last(\"bytes_recv\") / 1048576 AS \"RX_MB\", last(\"bytes_sent\") / 1048576 AS \"TX_MB\" FROM \"MSDemo\".\"autogen\".\"net\" WHERE time > :dashboardTime: AND \"sf\"=:sf1: GROUP BY time(1m), ipendpoint FILL(null)) group by time(1m)",
+            "range": null,
+            "shifts": null
+          },
+          "source": "/chronograf/v1/sources/2"
+        }
+      ],
+      "axes": {
+        "x": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "MB",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        },
+        "y2": {
+          "bounds": [
+            "",
+            ""
+          ],
+          "label": "",
+          "prefix": "",
+          "suffix": "",
+          "base": "10",
+          "scale": "linear"
+        }
+      },
+      "type": "line-stepplot",
+      "colors": [
+        {
+          "id": "2f5981b6-6f85-4efc-989c-9da90fe54189",
+          "type": "scale",
+          "hex": "#31C0F6",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "696e12a0-4b7d-4a8f-9d95-a5be5ef3e8b8",
+          "type": "scale",
+          "hex": "#A500A5",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        },
+        {
+          "id": "f3788c7b-bdbe-4d14-8439-c6f3b4db75e9",
+          "type": "scale",
+          "hex": "#FF7E27",
+          "name": "Nineteen Eighty Four",
+          "value": "0"
+        }
+      ],
+      "legend": {},
+      "tableOptions": {
+        "timeFormat": "MM/DD/YYYY HH:mm:ss",
+        "verticalTimeAxis": true,
+        "sortBy": {
+          "internalName": "time",
+          "displayName": "",
+          "visible": true
+        },
+        "wrapping": "truncate",
+        "fieldNames": [
+          {
+            "internalName": "time",
+            "displayName": "",
+            "visible": true
+          }
+        ],
+        "fixFirstColumn": true
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/2/cells/419b4e3c-1ca3-49ac-bc91-410bfbeb879a"
+      }
+    }
+  ],
+  "templates": [
+    {
+      "tempVar": ":sf1:",
+      "values": [
+        {
+          "value": "nginx",
+          "type": "tagValue",
+          "selected": true
+        }
+      ],
+      "id": "2160b8b2-a885-4518-90dc-f2363eb3fc83",
+      "type": "tagValues",
+      "label": "",
+      "query": {
+        "influxql": "SHOW TAG VALUES ON :database: FROM :measurement: WITH KEY=:tagKey:",
+        "db": "MSDemo",
+        "measurement": "cpu",
+        "tagKey": "sf",
+        "fieldKey": ""
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/2/templates/2160b8b2-a885-4518-90dc-f2363eb3fc83"
+      }
+    },
+    {
+      "tempVar": ":sf2:",
+      "values": [
+        {
+          "value": "minio",
+          "type": "tagValue",
+          "selected": true
+        }
+      ],
+      "id": "beb094ee-5bed-4956-a551-83fe8e905c19",
+      "type": "tagValues",
+      "label": "",
+      "query": {
+        "influxql": "SHOW TAG VALUES ON :database: FROM :measurement: WITH KEY=:tagKey:",
+        "db": "MSDemo",
+        "measurement": "cpu",
+        "tagKey": "sf",
+        "fieldKey": ""
+      },
+      "links": {
+        "self": "/chronograf/v1/dashboards/2/templates/beb094ee-5bed-4956-a551-83fe8e905c19"
+      }
+    }
+  ],
+  "name": "Service Functions",
+  "organization": "default",
+  "links": {
+    "self": "/chronograf/v1/dashboards/2",
+    "cells": "/chronograf/v1/dashboards/2/cells",
+    "templates": "/chronograf/v1/dashboards/2/templates"
+  }
+}
-- 
GitLab