Merge branch 'master' into xos_auto_scaling_app_changes
diff --git a/.gitignore b/.gitignore
index cc49c01..451dd79 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@
 xos/core/xoslib/docs
 xos/core/xoslib/coverage
 node_modules
+xos/xos_configuration/*
diff --git a/containers/synchronizer/Dockerfile b/containers/synchronizer/Dockerfile
index f9d79ae..7170e1c 100644
--- a/containers/synchronizer/Dockerfile
+++ b/containers/synchronizer/Dockerfile
@@ -10,6 +10,9 @@
     python-httplib2 \
     supervisor
 
+RUN pip install -U \
+    jinja2
+
 RUN \
     git clone -b release1.8.2 git://github.com/ansible/ansible.git /opt/ansible && \
     git clone -b release1.8.2 git://github.com/ansible/ansible-modules-extras.git /opt/ansible/lib/ansible/modules/extras && \
@@ -41,4 +44,4 @@
 # Supervisor
 RUN cp /tmp/xos/containers/synchronizer/conf/synchronizer.conf /etc/supervisor/conf.d/
 
-CMD /usr/bin/supervisord -c /etc/supervisor/conf.d/synchronizer.conf
+CMD update-ca-certificates && /usr/bin/supervisord -c /etc/supervisor/conf.d/synchronizer.conf
diff --git a/containers/synchronizer/Makefile b/containers/synchronizer/Makefile
index 8620438..3b10312 100644
--- a/containers/synchronizer/Makefile
+++ b/containers/synchronizer/Makefile
@@ -6,7 +6,7 @@
 build: ; docker build --no-cache=${NO_DOCKER_CACHE} --rm -t ${IMAGE_NAME} .
 
 .PHONY: run
-run: ; docker run -d --name ${CONTAINER_NAME} ${IMAGE_NAME}
+run: ; docker run -d --name ${CONTAINER_NAME} -v /usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro ${IMAGE_NAME}
 
 .PHONY: stop
 stop: ; docker stop ${CONTAINER_NAME}
diff --git a/containers/xos/Dockerfile b/containers/xos/Dockerfile
index 38e08a1..639cbd5 100644
--- a/containers/xos/Dockerfile
+++ b/containers/xos/Dockerfile
@@ -1,4 +1,4 @@
-FROM       ubuntu:14.04.3
+FROM       python:2.7.11
 
 # XXX Workaround for docker bug:
 # https://github.com/docker/docker/issues/6345
@@ -36,12 +36,11 @@
     python-glanceclient \
     python-ceilometerclient
 
-RUN pip install -U \
+RUN pip install \
     django==1.7 \
     django-bitfield \
     django-crispy-forms \
     django-encrypted-fields \
-    django_evolution \
     django-extensions \
     django-filter \
     django-geoposition \
@@ -51,20 +50,27 @@
     django-timezones \
     djangorestframework==2.4.4 \
     dnslib \
-    google_api_python_client \
-    httplib2 \
-    httplib2.ca_certs_locater \
     lxml \  
     markdown \
     netaddr \
+    pyOpenSSL \
+    psycopg2 \ 
+    python-ceilometerclient \
     python-dateutil \
-    python_gflags \
     python-keyczar \
     pygraphviz \
     pytz \
     pyyaml \
     requests
 
+RUN easy_install --upgrade httplib2
+
+RUN easy_install \
+    django_evolution \
+    python_gflags \
+    google_api_python_client \
+    httplib2.ca_certs_locater
+
 ADD http://code.jquery.com/jquery-1.9.1.min.js /usr/local/lib/python2.7/dist-packages/suit/static/suit/js/
 
 # Install XOS
@@ -74,6 +80,7 @@
     /opt/xos/scripts/opencloud genkeys
 
 # install Tosca engine
+RUN chmod +x /opt/xos/tosca/run.py
 RUN bash /opt/xos/tosca/install_tosca.sh
 
 EXPOSE 8000
@@ -82,7 +89,7 @@
 ENV HOME /root
 
 # Define working directory.
-WORKDIR /root
+WORKDIR /opt/xos
 
 # Define default command.
-CMD python /opt/xos/manage.py runserver 0.0.0.0:8000 --insecure
+CMD update-ca-certificates && python manage.py runserver 0.0.0.0:8000 --insecure
diff --git a/containers/xos/Makefile b/containers/xos/Makefile
index 367f0ec..833046b 100644
--- a/containers/xos/Makefile
+++ b/containers/xos/Makefile
@@ -6,13 +6,13 @@
 NO_DOCKER_CACHE?=false
 
 .PHONY: build
-build: ; docker build --no-cache=${NO_DOCKER_CACHE} --rm -t ${IMAGE_NAME} . 
+build: ; make -C ../../xos/configurations/common -f Makefile.cloudlab; docker build --no-cache=${NO_DOCKER_CACHE} --rm -t ${IMAGE_NAME} . 
 
 .PHONY: custom
 custom: ; cat Dockerfile.templ | sed -e "s|XOS_GIT_REPO|${XOS_GIT_REPO}|g" -e "s|XOS_GIT_BRANCH|${XOS_GIT_BRANCH}|g" | docker build --no-cache=${NO_DOCKER_CACHE} -
 
 .PHONY: run
-run: ; docker run -d --name ${CONTAINER_NAME} -p 80:8000 ${IMAGE_NAME}
+run: ; docker run -d --name ${CONTAINER_NAME} -p 80:8000 -v /usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro ${IMAGE_NAME}
 
 .PHONY: runtosca
 runtosca: ; docker exec -it ${CONTAINER_NAME} /usr/bin/python /opt/xos/tosca/run.py padmin@vicci.org ${TOSCA_CONFIG_PATH}
diff --git a/containers/xos/initdb b/containers/xos/initdb
index 41e0a9a..3252747 100755
--- a/containers/xos/initdb
+++ b/containers/xos/initdb
@@ -5,13 +5,12 @@
 DB_HOST=$(wget http://ipinfo.io/ip -qO -)
 
 # configure db host
-docker run -it --name=$CONTAINER_NAME $IMAGE_NAME sed -i '0,/host/{s/host=localhost/host='$DB_HOST'/}' /opt/xos/xos_config
+docker run -it --name=$CONTAINER_NAME $IMAGE_NAME sed -i '0,/host/{s/host=localhost/host='$DB_HOST'/}' /opt/xos/xos_configuration/xos_common_config
 docker commit $CONTAINER_NAME $IMAGE_NAME
 docker rm $CONTAINER_NAME
 
 # init db schema
 docker run -it --name=$CONTAINER_NAME $IMAGE_NAME /opt/xos/scripts/opencloud makemigrations
 # run overrides the CMD specifed in the Dockerfile, so we re-set the CMD in the final commit"
-echo docker commit --change="CMD python /opt/xos/manage.py runserver 0.0.0.0:8000 --insecure" $CONTAINER_NAME $IMAGE_NAME
-docker commit --change="CMD python /opt/xos/manage.py runserver 0.0.0.0:8000 --insecure" $CONTAINER_NAME $IMAGE_NAME
+docker commit --change="CMD update-ca-certificates && python /opt/xos/manage.py runserver 0.0.0.0:8000 --insecure" $CONTAINER_NAME $IMAGE_NAME
 docker rm $CONTAINER_NAME
diff --git a/xos/configurations/cord/Makefile b/xos/configurations/cord/Makefile
index ef823cd..80b71e0 100644
--- a/xos/configurations/cord/Makefile
+++ b/xos/configurations/cord/Makefile
@@ -2,7 +2,7 @@
 RUNNING_CONTAINER:=$(shell sudo docker ps|grep "xos"|awk '{print $$NF}')
 LAST_CONTAINER=$(shell sudo docker ps -l -q)
 
-cord: common_cloudlab ceilometer_dashboard virtualbng_json
+cord: common_cloudlab ceilometer_dashboard virtualbng_json vtn_network_cfg_json
 	rm ../../xos_configuration/*
 	echo "# Autogenerated -- do not edit" > Dockerfile
 	cat ../common/Dockerfile.common Dockerfile.cord >> Dockerfile
@@ -24,6 +24,9 @@
 virtualbng_json:
 	bash make-virtualbng-json.sh
 
+vtn_network_cfg_json:
+	bash make-vtn-networkconfig-json.sh
+
 stop:
 	sudo docker stop $(RUNNING_CONTAINER)
 
@@ -32,3 +35,27 @@
 
 enter:
 	sudo docker exec -t -i $(RUNNING_CONTAINER) bash
+
+dataplane: etc_hosts
+	cd dataplane; ./gen-inventory.sh > hosts
+	cd dataplane; ansible-playbook -i hosts dataplane.yaml
+
+dataplane_bm: dataplane
+	cd dataplane; bash -c "./generate-bm.sh > hosts-bm"
+	cd dataplane; sudo bash -c "ansible-playbook -i hosts-bm dataplane-bm.yaml"
+
+etc_hosts:
+	sudo bash -c "sed -i '/^10.11.10/ d' /etc/hosts"
+	cd dataplane; sudo bash -c "./gen-etc-hosts.sh >> /etc/hosts"
+
+setup_client:
+	# add subscriber to vOLT?  Is there a REST API?
+	echo "Don't forget: add-subscriber-access of:0000000000000001 1 432"
+	cd dataplane; ansible -i hosts client -m shell -s -a "route del default gw 10.11.10.5; dhclient br-sub"
+	# reboot the vBNG ONOS
+	cd dataplane; ansible -i hosts onos_vbng -m shell -s -a "docker restart ONOS"
+
+cleanup: stop
+	cd dataplane; ./cleanup.sh
+	bash -c "source ../common/admin-openrc.sh; nova list --all-tenants; neutron net-list"
+	echo "Don't forget to clean up containers"
diff --git a/xos/configurations/cord/cord.yaml b/xos/configurations/cord/cord.yaml
index 5d95d7a..8aa4676 100644
--- a/xos/configurations/cord/cord.yaml
+++ b/xos/configurations/cord/cord.yaml
@@ -437,6 +437,7 @@
         container_format: na
         disk_format: na
         path: andybavier/docker-vcpe
+        tag: develop
 
     # A subscriber
     My House:
@@ -451,7 +452,7 @@
     Mom's PC:
        type: tosca.nodes.CORDUser
        properties:
-           mac: 010203040506
+           mac: 01:02:03:04:05:06
            level: PG_13
        requirements:
            - household:
@@ -461,7 +462,7 @@
     Dad's PC:
        type: tosca.nodes.CORDUser
        properties:
-           mac: 90E2Ba82F975
+           mac: 90:E2:BA:82:F9:75
            level: PG_13
        requirements:
            - household:
@@ -471,7 +472,7 @@
     Jack's Laptop:
        type: tosca.nodes.CORDUser
        properties:
-           mac: 685B359D91D5
+           mac: 68:5B:35:9D:91:D5
            level: PG_13
        requirements:
            - household:
@@ -481,7 +482,7 @@
     Jill's Laptop:
        type: tosca.nodes.CORDUser
        properties:
-           mac: 34363BC9B6A6
+           mac: 34:36:3B:C9:B6:A6
            level: PG_13
        requirements:
            - household:
diff --git a/xos/configurations/cord/dataplane/cleanup.sh b/xos/configurations/cord/dataplane/cleanup.sh
new file mode 100755
index 0000000..721f054
--- /dev/null
+++ b/xos/configurations/cord/dataplane/cleanup.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+function cleanup_network {
+  NETWORK=$1
+  SUBNETS=`neutron net-show $NETWORK | grep -i subnets | awk '{print $4}'`
+  if [[ $SUBNETS != "" ]]; then
+      PORTS=`neutron port-list | grep -i $SUBNETS | awk '{print $2}'`
+      for PORT in $PORTS; do
+          echo "Deleting port $PORT"
+          neutron port-delete $PORT
+      done
+  fi
+  neutron net-delete $NETWORK
+}
+
+source ../../common/admin-openrc.sh
+
+echo "Deleting VMs"
+# Delete all VMs
+VMS=$( nova list --all-tenants|grep mysite|awk '{print $2}' )
+for VM in $VMS
+do
+    nova delete $VM
+done
+
+echo "Waiting 5 seconds..."
+sleep 5
+
+cleanup_network wan_network
+cleanup_network mysite_vcpe-private
+
+echo "Deleting networks"
+# Delete all networks beginning with mysite_
+NETS=$( neutron net-list --all-tenants|grep mysite|awk '{print $2}' )
+for NET in $NETS
+do
+    neutron net-delete $NET
+done
+
+neutron net-delete lan_network || true
+neutron net-delete subscriber_network || true
+neutron net-delete public_network || true
+neutron net-delete hpc_client_network || true
+neutron net-delete ceilometer_network || true
diff --git a/xos/configurations/cord/dataplane/dataplane.yaml b/xos/configurations/cord/dataplane/dataplane.yaml
index a0950be..3ca3bbe 100644
--- a/xos/configurations/cord/dataplane/dataplane.yaml
+++ b/xos/configurations/cord/dataplane/dataplane.yaml
@@ -49,6 +49,7 @@
 
   - name: Remove IP address on public_network
     command: /sbin/ifconfig {{ public_net.stdout }} 0.0.0.0
+    when: public_net.stdout
 
   - name: Change datapath ID of bridge to match config file
     command: /usr/bin/ovs-vsctl set bridge br-vbng other-config:datapath-id={{ ovs_dpid }}
diff --git a/xos/configurations/cord/make-vtn-networkconfig-json.sh b/xos/configurations/cord/make-vtn-networkconfig-json.sh
new file mode 100644
index 0000000..df94597
--- /dev/null
+++ b/xos/configurations/cord/make-vtn-networkconfig-json.sh
@@ -0,0 +1,58 @@
+FN=vtn-network-cfg.json
+
+rm -f $FN
+
+cat >> $FN <<EOF
+{
+    "apps" : {
+        "org.onosproject.cordvtn" : {
+            "cordvtn" : {
+                "nodes" : [
+EOF
+
+NODES=$( sudo bash -c "source /root/setup/admin-openrc.sh ; nova hypervisor-list" |grep cloudlab|awk '{print $4}' )
+
+NODECOUNT=0
+for NODE in $NODES; do
+    ((NODECOUNT++))
+done
+
+I=0
+for NODE in $NODES; do
+    echo $NODE
+    NODEIP=`getent hosts $NODE | awk '{ print $1 }'`
+
+    ((I++))
+    cat >> $FN <<EOF
+                    {
+                      "hostname": "$NODE",
+                      "ovsdbIp": "$NODEIP",
+                      "ovsdbPort": "6640",
+                      "bridgeId": "of:000000000000000$I"
+EOF
+    if [[ "$I" -lt "$NODECOUNT" ]]; then
+        echo "                    }," >> $FN
+    else
+        echo "                    }" >> $FN
+    fi
+done
+
+NEUTRONIP="127.0.0.1"
+KEYSTONEIP="127.0.0.1"
+
+cat >> $FN <<EOF
+                ]
+            }

+        },
+        "org.onosproject.openstackswitching" : {
+            "openstackswitching" : {
+                 "do_not_push_flows" : "true",
+                 "neutron_server" : "http://$NEUTRONIP:9696/v2.0/",
+                 "keystone_server" : "http://$KEYSTONEIP:5000/v2.0/",
+                 "user_name" : "admin",
+                 "password" : "passwd"
+             }
+        }
+    }
+}
+EOF
diff --git a/xos/cord/models.py b/xos/cord/models.py
index daac40c..1a81419 100644
--- a/xos/cord/models.py
+++ b/xos/cord/models.py
@@ -689,7 +689,7 @@
 
     def save_instance(self, instance):
         with transaction.atomic():
-            instance.volumes = "/etc/dnsmasq.d"
+            instance.volumes = "/etc/dnsmasq.d,/etc/ufw"
             super(VCPETenant, self).save_instance(instance)
 
             if instance.isolation in ["container", "container_vm"]:
diff --git a/xos/core/static/xos.css b/xos/core/static/xos.css
index 40cfcc3..685361e 100644
--- a/xos/core/static/xos.css
+++ b/xos/core/static/xos.css
@@ -62,6 +62,7 @@
   -moz-transition: all 0.5s ease;
   -o-transition: all 0.5s ease;
   transition: all 0.5s ease;
+  background: white;
 }
 
 #wrapper.toggled #sidebar-wrapper {
diff --git a/xos/core/xoslib/dashboards/xosCeilometerDashboard.html b/xos/core/xoslib/dashboards/xosCeilometerDashboard.html
index 1db309a..22511f4 100644
--- a/xos/core/xoslib/dashboards/xosCeilometerDashboard.html
+++ b/xos/core/xoslib/dashboards/xosCeilometerDashboard.html
@@ -3,8 +3,9 @@
 <!-- browserSync -->
 
 <!-- inject:css -->
-<link rel="stylesheet" href="/static/css/ceilometerDashboard.css">
+<link rel="stylesheet" href="/../../static/css/xosCeilometerDashboard.css">
 <!-- endinject -->
+<link rel="stylesheet" href="css/ceilometerDashboard.css">
 <div id="xosCeilometerDashboard">
   <div ui-view ng-class="stateName"></div>
 </div>
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/.eslintrc b/xos/core/xoslib/ngXosViews/ceilometerDashboard/.eslintrc
index f495e79..868fd4b 100644
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/.eslintrc
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/.eslintrc
@@ -16,7 +16,7 @@
     ],
     "rules": {
         "quotes": [2, "single"],
-        "camelcase": [1, {"properties": "always"}],
+        "camelcase": [0, {"properties": "always"}],
         "no-underscore-dangle": 1,
         "eqeqeq": [2, "smart"],
         "no-alert": 1,
@@ -38,6 +38,7 @@
         //"angular/ng_di": [0, "function or array"]
     },
     "globals" :{
-        "angular": true
+        "angular": true,
+        "Chart": true
     } 
 }
\ No newline at end of file
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/env/default.js b/xos/core/xoslib/ngXosViews/ceilometerDashboard/env/default.js
index 26dd953..8fc0bd5 100644
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/env/default.js
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/env/default.js
@@ -8,6 +8,6 @@
 
 module.exports = {
   host: 'http://clnode015.clemson.cloudlab.us:9999/',
-  xoscsrftoken: 'kL0QrqJ5Y3TbTR6mLgPBGVCMNv2JksQv',
-  xossessionid: '8ygydigw52qgc715fgznj15k7nfe1139'
+  xoscsrftoken: 'w0qMPZCk8nByQh9Zy86nPs5OM5G5lKdb',
+  xossessionid: 'kvv2aa1il6u4i7j6yu2399r4tdpdeoxz'
 };
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/gulp/build.js b/xos/core/xoslib/ngXosViews/ceilometerDashboard/gulp/build.js
index d0d38f9..9f9bfb8 100644
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/gulp/build.js
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/gulp/build.js
@@ -22,6 +22,10 @@
 var inject = require('gulp-inject');
 var rename = require('gulp-rename');
 var replace = require('gulp-replace');
+var postcss = require('gulp-postcss');
+var autoprefixer = require('autoprefixer');
+var mqpacker = require('css-mqpacker');
+var csswring = require('csswring');
 
 var TEMPLATE_FOOTER = `}]);
 angular.module('xos.ceilometerDashboard').run(function($location){$location.path('/')});
@@ -37,6 +41,28 @@
     );
   });
 
+  // minify css
+  gulp.task('css', function () {
+    var processors = [
+      autoprefixer({browsers: ['last 1 version']}),
+      mqpacker,
+      csswring
+    ];
+    
+    gulp.src([
+      `${options.css}**/*.css`,
+      `!${options.css}dev.css`
+    ])
+    .pipe(postcss(processors))
+    .pipe(gulp.dest(options.tmp + '/css/'));
+  });
+
+  gulp.task('copyCss', ['css'], function(){
+    return gulp.src([`${options.tmp}/css/*.css`])
+    .pipe(concat('xosCeilometerDashboard.css'))
+    .pipe(gulp.dest(options.static + 'css/'))
+  });
+
   // compile and minify scripts
   gulp.task('scripts', function() {
     return gulp.src([
@@ -67,12 +93,15 @@
       .pipe(replace(/<!-- bower:css -->(\n.*)*\n<!-- endbower --><!-- endcss -->/, ''))
       .pipe(replace(/<!-- bower:js -->(\n.*)*\n<!-- endbower --><!-- endjs -->/, ''))
       .pipe(replace(/ng-app=".*"\s/, ''))
+      //rewriting css path
+      // .pipe(replace(/(<link.*">)/, ''))
       // injecting minified files
       .pipe(
         inject(
           gulp.src([
             options.static + 'js/vendor/xosCeilometerDashboardVendor.js',
-            options.static + 'js/xosCeilometerDashboard.js'
+            options.static + 'js/xosCeilometerDashboard.js',
+            options.static + 'css/xosCeilometerDashboard.css'
           ])
         )
       )
@@ -107,11 +136,13 @@
 
   gulp.task('build', function() {
     runSequence(
+      'lint',
       'templates',
       'babel',
       'scripts',
       'wiredep',
       'copyHtml',
+      'copyCss',
       'cleanTmp'
     );
   });
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/gulp/server.js b/xos/core/xoslib/ngXosViews/ceilometerDashboard/gulp/server.js
index d176950..7268b13 100644
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/gulp/server.js
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/gulp/server.js
@@ -107,7 +107,7 @@
                 options.api + '*.js',
                 options.helpers + '**/*.js'
               ])
-              .pipe(debug({title: 'unicorn:'}))
+              // .pipe(debug({title: 'unicorn:'}))
               .pipe(angularFilesort()),
               {
                 ignorePath: [options.src, '/../../ngXosLib']
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/gulpfile.js b/xos/core/xoslib/ngXosViews/ceilometerDashboard/gulpfile.js
index f114774..de911e9 100644
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/gulpfile.js
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/gulpfile.js
@@ -5,6 +5,7 @@
 
 var options = {
   src: 'src/',
+  css: 'src/css/',
   scripts: 'src/js/',
   tmp: 'src/.tmp',
   dist: 'dist/',
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/karma.conf.js b/xos/core/xoslib/ngXosViews/ceilometerDashboard/karma.conf.js
index dbd344a..12d0697 100644
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/karma.conf.js
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/karma.conf.js
@@ -10,9 +10,8 @@
 var bowerComponents = wiredep( {devDependencies: true} )[ 'js' ].map(function( file ){
   return path.relative(process.cwd(), file);
 });
-
-module.exports = function(config) {
 /*eslint-enable*/
+module.exports = function(config) {
   config.set({
 
     // base path that will be used to resolve all patterns (eg. files, exclude)
@@ -45,10 +44,17 @@
     // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
     preprocessors: {
       'src/js/**/*.js': ['babel'],
-      'spec/**/*.test.js': ['babel'],
+      'spec/**/*.js': ['babel'],
       'src/**/*.html': ['ng-html2js']
     },
 
+    babelPreprocessor: {
+      options: {
+        presets: ['es2015'],
+        sourceMap: 'inline'
+      }
+    },
+
     ngHtml2JsPreprocessor: {
       stripPrefix: 'src/', //strip the src path from template url (http://stackoverflow.com/questions/22869668/karma-unexpected-request-when-testing-angular-directive-even-with-ng-html2js)
       moduleName: 'templates' // define the template module name
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/package.json b/xos/core/xoslib/ngXosViews/ceilometerDashboard/package.json
index ba3a8fd..0a9ccae 100644
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/package.json
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/package.json
@@ -7,6 +7,7 @@
     "start": "gulp serve",
     "prebuild": "npm install && bower install",
     "build": "gulp",
+    "pretest": "npm install",
     "test": "karma start",
     "lint": "eslint src/js/"
   },
@@ -19,7 +20,12 @@
   "license": "MIT",
   "dependencies": {},
   "devDependencies": {
+    "autoprefixer": "^6.1.2",
+    "babel": "^6.3.13",
+    "babel-preset-es2015": "^6.3.13",
     "browser-sync": "^2.9.11",
+    "css-mqpacker": "^4.0.0",
+    "csswring": "^4.1.1",
     "del": "^2.0.2",
     "eslint": "^1.8.0",
     "eslint-plugin-angular": "linkmesrl/eslint-plugin-angular",
@@ -33,12 +39,21 @@
     "gulp-inject": "^3.0.0",
     "gulp-minify-html": "^1.0.4",
     "gulp-ng-annotate": "^1.1.0",
+    "gulp-postcss": "^6.0.1",
     "gulp-rename": "^1.2.2",
     "gulp-replace": "^0.5.4",
     "gulp-uglify": "^1.4.2",
     "http-proxy": "^1.12.0",
+    "jasmine-core": "^2.4.1",
     "karma": "^0.13.15",
+    "karma-babel-preprocessor": "^6.0.1",
+    "karma-jasmine": "^0.3.6",
+    "karma-mocha-reporter": "^1.1.3",
+    "karma-ng-html2js-preprocessor": "^0.2.0",
+    "karma-phantomjs-launcher": "^0.2.1",
     "lodash": "^3.10.1",
+    "mocha": "^2.3.4",
+    "phantomjs": "^1.9.19",
     "proxy-middleware": "^0.15.0",
     "run-sequence": "^1.1.4",
     "wiredep": "^3.0.0-beta",
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/spec/.eslintrc b/xos/core/xoslib/ngXosViews/ceilometerDashboard/spec/.eslintrc
new file mode 100644
index 0000000..ad4bc2d
--- /dev/null
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/spec/.eslintrc
@@ -0,0 +1,9 @@
+{
+    "globals" :{
+        "describe": true,
+        "beforeEach": true,
+        "it": true,
+        "inject": true,
+        "expect": true
+    } 
+}
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/spec/backend.mock.js b/xos/core/xoslib/ngXosViews/ceilometerDashboard/spec/backend.mock.js
new file mode 100644
index 0000000..c7642bb
--- /dev/null
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/spec/backend.mock.js
@@ -0,0 +1,98 @@
+'use strict';
+(function () {
+
+  const meters = [
+    // service_1
+    //  - slice_1
+    //    - resource_1
+    // service_2
+    //  - slice_2
+    //    - resource_2
+    //    - resource_3
+    //  - slice_3
+    //    - resource_4
+    {
+      service: 'service_1',
+      slice: 'slice_1',
+      resource_name: 'resource_1',
+      resource_id: 'resource_id_1',
+      name: 'instance_1',
+      unit: 'instance'
+    },
+    {
+      service: 'service_2',
+      slice: 'slice_2',
+      resource_name: 'resource_2',
+      resource_id: 'resource_id_2',
+      name: 'instance_2',
+      unit: 'instance'
+    },
+    {
+      service: 'service_2',
+      slice: 'slice_2',
+      resource_name: 'resource_3',
+      resource_id: 'resource_id_3',
+      name: 'instance_2',
+      unit: 'instance'
+    },
+    {
+      service: 'service_2',
+      slice: 'slice_3',
+      resource_name: 'resource_4',
+      resource_id: 'resource_id_4',
+      name: 'instance_3',
+      unit: 'instance'
+    }
+  ];
+
+  const samples = [
+    {
+      meter: 'cpu',
+      resource_name: 'fakeName',
+      project_id: 'fakeTenant',
+      timestamp: '2015-12-15T00:34:08',
+      volume: 110
+    },
+    {
+      meter: 'cpu',
+      resource_name: 'fakeName',
+      project_id: 'fakeTenant',
+      timestamp: '2015-12-15T00:44:08',
+      volume: 120
+    },
+    {
+      meter: 'cpu',
+      resource_name: 'anotherName',
+      project_id: 'anotherTenant',
+      timestamp: '2015-12-15T00:24:08',
+      volume: 210
+    },
+    {
+      meter: 'cpu',
+      resource_name: 'anotherName',
+      project_id: 'anotherTenant',
+      timestamp: '2015-12-15T00:34:08',
+      volume: 220
+    },
+    {
+      meter: 'cpu',
+      resource_name: 'anotherName',
+      project_id: 'anotherTenant',
+      timestamp: '2015-12-15T00:44:08',
+      volume: 230
+    },
+    {
+      meter: 'cpu',
+      resource_name: 'thirdName',
+      project_id: 'thirdTenant',
+      timestamp: '2015-12-15T00:44:08',
+      volume: 310
+    }
+  ];
+
+  angular.module('xos.ceilometerDashboard')
+  .run(($httpBackend) => {
+    $httpBackend.whenGET(/metersamples/).respond(samples);
+    $httpBackend.whenGET(/meters/).respond(meters);
+  });
+})();
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/spec/ceilometer.test.js b/xos/core/xoslib/ngXosViews/ceilometerDashboard/spec/ceilometer.test.js
new file mode 100644
index 0000000..c5bb850
--- /dev/null
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/spec/ceilometer.test.js
@@ -0,0 +1,137 @@
+'use strict';
+
+describe('In Ceilometer View', () => {
+  
+  var scope, element, vm, httpBackend;
+
+  beforeEach(module('xos.ceilometerDashboard'));
+  beforeEach(module('templates'));
+
+  beforeEach(inject(($httpBackend, $rootScope) => {
+    httpBackend = $httpBackend;
+    scope = $rootScope.$new();
+  }))
+
+  describe('The dashboard', () => {
+    beforeEach(inject(function($httpBackend, $compile){
+      element = angular.element('<ceilometer-dashboard></ceilometer-dashboard>');
+      $compile(element)(scope);
+      scope.$digest();
+      vm = element.isolateScope().vm;
+      httpBackend.flush();
+    }));
+
+    describe('when loading meters', () => {
+      it('should group meters by services', () => {
+        expect(Object.keys(vm.projects).length).toBe(2);
+      });
+
+      it('should group services by slices', () => {
+        expect(Object.keys(vm.projects.service_2).length).toBe(2);
+      });
+
+      it('should group slices by resources', () => {
+        expect(Object.keys(vm.projects.service_2.slice_2).length).toBe(2);
+      });
+    });
+  });
+
+  describe('the sample view', () => {
+    beforeEach(inject(function($httpBackend, $compile, $stateParams){
+
+      $stateParams.name = 'fakeName';
+      $stateParams.tenant = 'fakeTenant';
+
+      element = angular.element('<ceilometer-samples></ceilometer-samples>');
+      $compile(element)(scope);
+      scope.$digest();
+      vm = element.isolateScope().vm;
+      httpBackend.flush();
+    }));
+
+    it('should group samples by resource_id', () => {
+      expect(Object.keys(vm.samplesList.fakeTenant).length).toBe(2)
+      expect(Object.keys(vm.samplesList.anotherTenant).length).toBe(3)
+      expect(Object.keys(vm.samplesList.thirdTenant).length).toBe(1)
+    });
+
+    it('should add the comparable samples to the dropdown list', () => {
+      expect(vm.sampleLabels[0].id).toEqual('anotherTenant')
+      expect(vm.sampleLabels[1].id).toEqual('thirdTenant')
+    });
+
+    it('should add the selected meter to the chart', () => {
+      expect(vm.chart.labels.length).toBe(2);
+      expect(vm.chart.series[0]).toBe('fakeTenant');
+      expect(vm.chart.data[0].length).toBe(2);
+      expect(vm.chart.data[0][0]).toBe(110);
+      expect(vm.chart.data[0][1]).toBe(120);
+      expect(vm.chartMeters[0].project_id).toBe('fakeTenant')
+      expect(vm.chartMeters[0].resource_name).toBe('fakeName')
+    });
+
+    it('should add a sample to the chart', () => {
+      vm.addMeterToChart('anotherTenant');
+      expect(vm.chart.labels.length).toBe(3);
+      expect(vm.chart.data[1].length).toBe(3);
+      expect(vm.chart.data[1][0]).toBe(210);
+      expect(vm.chart.data[1][1]).toBe(220);
+      expect(vm.chart.data[1][2]).toBe(230);
+      expect(vm.chartMeters[1].project_id).toBe('anotherTenant')
+      expect(vm.chartMeters[1].resource_name).toBe('anotherName')
+    });
+
+    it('should remove a sample from the chart', () => {
+      // for simplyvity add a tenant (it's tested)
+      vm.addMeterToChart('anotherTenant');
+      vm.removeFromChart(vm.chartMeters[0]);
+      expect(vm.chart.data[0].length).toBe(3);
+      expect(vm.chart.data[0][0]).toBe(210);
+      expect(vm.chart.data[0][1]).toBe(220);
+      expect(vm.chart.data[0][2]).toBe(230);
+      expect(vm.chartMeters[0].project_id).toBe('anotherTenant')
+      expect(vm.chartMeters[0].resource_name).toBe('anotherName')
+    });
+
+    describe('The format sample labels method', () => {
+      it('should create an array of unique labels', () => {
+        // unique because every resource has multiple samples (time-series)
+        const samples = [
+          {project_id: 1, resource_name: 'fakeName'},
+          {project_id: 1, resource_name: 'fakeName'},
+          {project_id: 2, resource_name: 'anotherName'},
+          {project_id: 2, resource_name: 'anotherName'}
+        ];
+
+        const result = vm.formatSamplesLabels(samples);
+
+        expect(result.length).toBe(2);
+        expect(result[0]).toEqual({id: 1, name: 'fakeName'});
+        expect(result[1]).toEqual({id: 2, name: 'anotherName'});
+      });
+    });
+  });
+});
+
+describe('The orderObjectByKey filter', () => {
+  var $filter;
+
+  beforeEach(function () {
+    module('xos.ceilometerDashboard');
+
+    inject(function (_$filter_) {
+      $filter = _$filter_;
+    });
+  });
+
+  it('should order an object by the key value', function () {
+    // Arrange.
+    const list = {c: 3, b: 2, a: 1};
+
+    // call the filter function
+    const result = $filter('orderObjectByKey')(list);
+
+    // Assert.
+    expect(result).toEqual({a: 1, b: 2, c: 3});
+  });
+});
\ No newline at end of file
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/spec/sample.test.js b/xos/core/xoslib/ngXosViews/ceilometerDashboard/spec/sample.test.js
deleted file mode 100644
index a224095..0000000
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/spec/sample.test.js
+++ /dev/null
@@ -1,34 +0,0 @@
-'use strict';
-
-describe('The User List', () => {
-  
-  var scope, element, isolatedScope, httpBackend;
-
-  beforeEach(module('xos.ceilometerDashboard'));
-  beforeEach(module('templates'));
-
-  beforeEach(inject(function($httpBackend, $compile, $rootScope){
-
-    httpBackend = $httpBackend;
-    // Setting up mock request
-    $httpBackend.expectGET(/meters/).respond([
-      {
-        email: 'teo@onlab.us',
-        firstname: 'Matteo',
-        lastname: 'Scandolo'
-      }
-    ]);
-  
-    scope = $rootScope.$new();
-    element = angular.element('<ceilometer-dashboard></ceilometer-dashboard>');
-    $compile(element)(scope);
-    scope.$digest();
-    isolatedScope = element.isolateScope().vm;
-  }));
-
-  it('should load 1 users', () => {
-    httpBackend.flush();
-    expect(isolatedScope.meters.length).toBe(1);
-  });
-
-});
\ No newline at end of file
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/css/ceilometerDashboard.css b/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/css/ceilometerDashboard.css
index 9b53d98..879d7b4 100644
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/css/ceilometerDashboard.css
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/css/ceilometerDashboard.css
@@ -1,267 +1,263 @@
-/*ceilometer-dashboard{*/
+#xosCeilometerDashboard{
+  position: relative;
+}
 
-  #xosCeilometerDashboard{
-    position: relative;
+/* Panel Layout */
+.panel {
+  margin-top: 10px;
+}
+
+.panel-body:not(:first-child) {
+  border-top: 1px solid #e3e3e3;
+}
+
+.panel-body .row {
+  margin-top: 10px;
+}
+
+/* Chart details */
+.chart {
+  width: 100%;
+  height: 300px;
+}
+
+.btn-chart, .btn-chart:hover {
+  color: #fff;
+}
+
+.side-container {
+  position: relative;
+}
+
+.service-list {
+  margin-top: -10px;
+}
+
+.service-list h3 {
+  margin-top: 0px;
+  margin-bottom: 0px;
+}
+
+.service-list a {
+  text-decoration: none;
+  color: #333;
+}
+
+.meters, .stats {
+  margin-top: 25px;
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+}
+
+/* LOADER */
+.loader {
+  font-size: 10px;
+  margin: 150px auto;
+  text-indent: -9999em;
+  width: 11em;
+  height: 11em;
+  border-radius: 50%;
+  background: #ffffff;
+  background: -moz-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+  background: -webkit-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+  background: -o-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+  background: -ms-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+  background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+  position: relative;
+  -webkit-animation: load3 1.4s infinite linear;
+  animation: load3 1.4s infinite linear;
+  -webkit-transform: translateZ(0);
+  -ms-transform: translateZ(0);
+  transform: translateZ(0);
+}
+.loader:before {
+  width: 50%;
+  height: 50%;
+  background: #105E9E;
+  border-radius: 100% 0 0 0;
+  position: absolute;
+  top: 0;
+  left: 0;
+  content: '';
+}
+.loader:after {
+  background: #fff;
+  width: 75%;
+  height: 75%;
+  border-radius: 50%;
+  content: '';
+  margin: auto;
+  position: absolute;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  right: 0;
+}
+@-webkit-keyframes load3 {
+  0% {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+  100% {
+    -webkit-transform: rotate(360deg);
+    transform: rotate(360deg);
+  }
+}
+@keyframes load3 {
+  0% {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+  100% {
+    -webkit-transform: rotate(360deg);
+    transform: rotate(360deg);
+  }
+}
+
+/* VIEW ANIMATION */
+
+[ui-view] {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  width: 100%;
+}
+
+/* FROM DASHBOARD TO DETAIL */
+
+/* dash out */
+[ui-view].ceilometerDashboard.ng-leave {
+  animation:1s bounceOutLeft ease; 
+}
+/* samples in */
+[ui-view].samples.ng-enter {
+  animation:1s bounceInRight ease;
+}
+
+/* FROM DETAIL TO DASHBOARD */
+
+/* samples out */
+[ui-view].samples.ng-leave {
+  animation:1s bounceOutRight ease; 
+}
+/* dash in */
+[ui-view].ceilometerDashboard.ng-enter {
+  animation:1s bounceInLeft ease;
+}
+
+/* COLUMS ANIMATION */
+/* when showing the thing */
+.animate .animate-slide-left.ng-hide-remove { 
+  animation:0.5s bounceInRight ease; 
+}
+
+/* when hiding the picture */
+.animate .animate-slide-left.ng-hide-add {
+  animation:0.5s bounceOutRight ease;
+}
+
+/* ANIMATIONS */
+
+@keyframes bounceInRight {
+  from, 60%, 75%, 90%, to {
+    animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
   }
 
-  /* Panel Layout */
-  .panel {
-    margin-top: 10px;
+  from {
+    opacity: 0;
+    transform: translate3d(3000px, 0, 0);
   }
 
-  .panel-body:not(:first-child) {
-    border-top: 1px solid #e3e3e3;
+  60% {
+    opacity: 1;
+    transform: translate3d(-25px, 0, 0);
   }
 
-  .panel-body .row {
-    margin-top: 10px;
+  75% {
+    transform: translate3d(10px, 0, 0);
   }
 
-  /* Chart details */
-  .chart {
-    width: 100%;
-    height: 300px;
+  90% {
+    transform: translate3d(-5px, 0, 0);
   }
 
-  .btn-chart, .btn-chart:hover {
-    color: #fff;
+  to {
+    transform: none;
+  }
+}
+
+@keyframes bounceInLeft {
+  from, 60%, 75%, 90%, to {
+    animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
   }
 
-  .side-container {
-    position: relative;
+  0% {
+    opacity: 0;
+    transform: translate3d(-3000px, 0, 0);
   }
 
-  .service-list {
-    margin-top: -10px;
+  60% {
+    opacity: 1;
+    transform: translate3d(25px, 0, 0);
   }
 
-  .service-list h3 {
-    margin-top: 0px;
-    margin-bottom: 0px;
+  75% {
+    transform: translate3d(-10px, 0, 0);
   }
 
-  .service-list a {
-    text-decoration: none;
-    color: #333;
+  90% {
+    transform: translate3d(5px, 0, 0);
   }
 
-  .meters, .stats {
-    margin-top: 25px;
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
+  to {
+    transform: none;
+  }
+}
+
+@keyframes slideInUp {
+  from {
+    transform: translate3d(0, 100%, 0);
+    visibility: visible;
   }
 
-  /* LOADER */
-  .loader {
-    font-size: 10px;
-    margin: 150px auto;
-    text-indent: -9999em;
-    width: 11em;
-    height: 11em;
-    border-radius: 50%;
-    background: #ffffff;
-    background: -moz-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
-    background: -webkit-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
-    background: -o-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
-    background: -ms-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
-    background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
-    position: relative;
-    -webkit-animation: load3 1.4s infinite linear;
-    animation: load3 1.4s infinite linear;
-    -webkit-transform: translateZ(0);
-    -ms-transform: translateZ(0);
-    transform: translateZ(0);
+  to {
+    transform: translate3d(0, 0, 0);
   }
-  .loader:before {
-    width: 50%;
-    height: 50%;
-    background: #105E9E;
-    border-radius: 100% 0 0 0;
-    position: absolute;
-    top: 0;
-    left: 0;
-    content: '';
-  }
-  .loader:after {
-    background: #fff;
-    width: 75%;
-    height: 75%;
-    border-radius: 50%;
-    content: '';
-    margin: auto;
-    position: absolute;
-    top: 0;
-    left: 0;
-    bottom: 0;
-    right: 0;
-  }
-  @-webkit-keyframes load3 {
-    0% {
-      -webkit-transform: rotate(0deg);
-      transform: rotate(0deg);
-    }
-    100% {
-      -webkit-transform: rotate(360deg);
-      transform: rotate(360deg);
-    }
-  }
-  @keyframes load3 {
-    0% {
-      -webkit-transform: rotate(0deg);
-      transform: rotate(0deg);
-    }
-    100% {
-      -webkit-transform: rotate(360deg);
-      transform: rotate(360deg);
-    }
+}
+
+@keyframes bounceOutRight {
+  20% {
+    opacity: 1;
+    transform: translate3d(-20px, 0, 0);
   }
 
-  /* VIEW ANIMATION */
+  to {
+    opacity: 0;
+    transform: translate3d(2000px, 0, 0);
+  }
+}
 
-  [ui-view] {
-    position: absolute;
-    top: 0;
-    bottom: 0;
-    width: 100%;
+@keyframes bounceOutLeft {
+  20% {
+    opacity: 1;
+    transform: translate3d(20px, 0, 0);
   }
 
-  /* FROM DASHBOARD TO DETAIL */
-
-  /* dash out */
-  [ui-view].ceilometerDashboard.ng-leave {
-    animation:1s bounceOutLeft ease; 
+  to {
+    opacity: 0;
+    transform: translate3d(-2000px, 0, 0);
   }
-  /* samples in */
-  [ui-view].samples.ng-enter {
-    animation:1s bounceInRight ease;
+}
+
+@keyframes slideOutDown {
+  from {
+    transform: translate3d(0, 0, 0);
   }
 
-  /* FROM DETAIL TO DASHBOARD */
-
-  /* samples out */
-  [ui-view].samples.ng-leave {
-    animation:1s bounceOutRight ease; 
+  to {
+    visibility: hidden;
+    transform: translate3d(0, 100%, 0);
   }
-  /* dash in */
-  [ui-view].ceilometerDashboard.ng-enter {
-    animation:1s bounceInLeft ease;
-  }
-
-  /* COLUMS ANIMATION */
-  /* when showing the thing */
-  .animate .animate-slide-left.ng-hide-remove { 
-    animation:0.5s bounceInRight ease; 
-  }
-
-  /* when hiding the picture */
-  .animate .animate-slide-left.ng-hide-add {
-    animation:0.5s bounceOutRight ease;
-  }
-  
-  /* ANIMATIONS */
-
-  @keyframes bounceInRight {
-    from, 60%, 75%, 90%, to {
-      animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
-    }
-
-    from {
-      opacity: 0;
-      transform: translate3d(3000px, 0, 0);
-    }
-
-    60% {
-      opacity: 1;
-      transform: translate3d(-25px, 0, 0);
-    }
-
-    75% {
-      transform: translate3d(10px, 0, 0);
-    }
-
-    90% {
-      transform: translate3d(-5px, 0, 0);
-    }
-
-    to {
-      transform: none;
-    }
-  }
-
-  @keyframes bounceInLeft {
-    from, 60%, 75%, 90%, to {
-      animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
-    }
-
-    0% {
-      opacity: 0;
-      transform: translate3d(-3000px, 0, 0);
-    }
-
-    60% {
-      opacity: 1;
-      transform: translate3d(25px, 0, 0);
-    }
-
-    75% {
-      transform: translate3d(-10px, 0, 0);
-    }
-
-    90% {
-      transform: translate3d(5px, 0, 0);
-    }
-
-    to {
-      transform: none;
-    }
-  }
-
-  @keyframes slideInUp {
-    from {
-      transform: translate3d(0, 100%, 0);
-      visibility: visible;
-    }
-
-    to {
-      transform: translate3d(0, 0, 0);
-    }
-  }
-
-  @keyframes bounceOutRight {
-    20% {
-      opacity: 1;
-      transform: translate3d(-20px, 0, 0);
-    }
-
-    to {
-      opacity: 0;
-      transform: translate3d(2000px, 0, 0);
-    }
-  }
-
-  @keyframes bounceOutLeft {
-    20% {
-      opacity: 1;
-      transform: translate3d(20px, 0, 0);
-    }
-
-    to {
-      opacity: 0;
-      transform: translate3d(-2000px, 0, 0);
-    }
-  }
-
-  @keyframes slideOutDown {
-    from {
-      transform: translate3d(0, 0, 0);
-    }
-
-    to {
-      visibility: hidden;
-      transform: translate3d(0, 100%, 0);
-    }
-  }
-
-/*}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/css/dev.css b/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/css/dev.css
index b72bfdc..228d394 100644
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/css/dev.css
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/css/dev.css
@@ -9,6 +9,6 @@
 
 @media (min-width: 768px) {
   #xosCeilometerDashboard {
-    margin: 10px:;
+    margin: 10px
   }
 }
\ No newline at end of file
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/css/grips/vertical.png b/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/css/grips/vertical.png
deleted file mode 100644
index 0ac8fa1..0000000
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/css/grips/vertical.png
+++ /dev/null
Binary files differ
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/js/main.js b/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/js/main.js
index e85a26e..a20d563 100644
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/js/main.js
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/js/main.js
@@ -33,24 +33,12 @@
 })
 .service('Ceilometer', function($http, $q, lodash){
 
-  this.resourceMap = {};
-
   this.getMeters = () => {
     let deferred = $q.defer();
 
     $http.get('/xoslib/meters/', {cache: true})
     // $http.get('../meters_mock.json', {cache: true})
     .then((res) => {
-
-      // saving resources name and ids for later user,
-      // {resource_id: resource_name}
-      // NOTE REMOVE IF NOT ANYMORE NEEDED
-      const resourceObj = lodash.groupBy(res.data, 'resource_id');
-      this.resourceMap = lodash.reduce(Object.keys(resourceObj), (map, item) => {
-        map[item] = resourceObj[item][0].resource_name;
-        return map;
-      }, {});
-
       deferred.resolve(res.data)
     })
     .catch((e) => {
@@ -88,6 +76,11 @@
 
     return deferred.promise;
   };
+
+  // hold dashboard status (opened service, slice, resource)
+  this.selectedService = null;
+  this.selectedSlice = null;
+  this.selectedResource = null;
 })
 .directive('ceilometerDashboard', function(lodash){
   return {
@@ -98,9 +91,34 @@
     templateUrl: 'templates/ceilometer-dashboard.tpl.html',
     controller: function(Ceilometer){
 
+      console.log(Ceilometer.selectedService, Ceilometer.selectedSlice, Ceilometer.selectedResource);
+
+      // this open the accordion
+      this.accordion = {
+        open: {}
+      }
+
+      /**
+      * Open the active panel base on the service stored values
+      */
+      this.openPanels = () => {
+        if(Ceilometer.selectedService){
+          this.accordion.open[Ceilometer.selectedService] = true;
+          if(Ceilometer.selectedSlice){
+            this.selectedSlice = Ceilometer.selectedSlice;
+            this.selectedResources = this.projects[Ceilometer.selectedService][Ceilometer.selectedSlice]
+            if(Ceilometer.selectedResource){
+              this.selectedResource = Ceilometer.selectedResource;
+              this.selectedMeters = this.selectedResources[Ceilometer.selectedResource];
+            }
+          }
+        }
+      }
+
       this.loadMeters = () => {
         this.loader = true;
 
+        // TODO rename projects in meters
         Ceilometer.getMeters()
         .then(meters => {
           //group project by service
@@ -113,6 +131,9 @@
               this.projects[project][slice] = lodash.groupBy(this.projects[project][slice], 'resource_name');
             });
           });
+
+          // open selected panels
+          this.openPanels();
         })
         .catch(err => {
           this.error = err.data.detail;
@@ -125,33 +146,26 @@
       this.loadMeters();
 
       /**
-      * Select the current service
-      */
-     
-      this.selectService = (service) => {
-        //cleaning
-        this.selectedResources = null;
-        this.selectedResource = null;
-        this.selectedMeters = null;
-
-        this.selectedService = service;
-      };
-
-      /**
       * Select Resources for a slice
       *
       * @param Array resources The list of selected resources
       * @returns void
       */
       this.selectedResources = null;
-      this.selectResources = (resources, slice) => {
+      this.selectResources = (resources, slice, service) => {
         //cleaning
         this.selectedResources = null;
         this.selectedResource = null;
         this.selectedMeters = null;
 
+        // hold the resource list for the current slice
         this.selectedResources = resources;
         this.selectedSlice = slice;
+        this.selectedService = service;
+
+        // store the status
+        Ceilometer.selectedSlice = slice;
+        Ceilometer.selectedService = service;
       }
 
       /**
@@ -163,6 +177,8 @@
       this.selectedMeters = null;
       this.selectMeters = (meters, resource) => {
         this.selectedMeters = meters;
+
+        Ceilometer.selectedResource = resource;
         this.selectedResource = resource;
       }
 
@@ -181,6 +197,8 @@
     templateUrl: 'templates/ceilometer-samples.tpl.html',
     controller: function(Ceilometer) {
 
+      // console.log(Ceilometer.selectResource);
+
       this.chartColors = [
         '#286090',
         '#F7464A',
@@ -204,9 +222,11 @@
       if($stateParams.name && $stateParams.tenant){
         this.name = $stateParams.name;
         this.tenant = $stateParams.tenant;
+        // TODO rename tenant in project_id
       }
-
-      // Mock
+      else{
+        throw new Error('Missing Name and Tenant Params!');
+      }
 
       /**
       * Goes trough the array and format date to be used as labels
@@ -243,22 +263,21 @@
       * @param string resource_id
       */
       this.chartMeters = [];
-      this.addMeterToChart = (resource_id) => {
-        this.chart['labels'] = this.getLabels(lodash.sortBy(this.samplesList[resource_id], 'timestamp'));
-        this.chart['series'].push(resource_id);
-        this.chart['data'].push(this.getData(lodash.sortBy(this.samplesList[resource_id], 'timestamp')));
-        console.log(this.samplesList[resource_id]);
-        this.chartMeters.push(this.samplesList[resource_id][0]); //use the 0 as are all samples for the same resource and I need the name
-        lodash.remove(this.sampleLabels, {id: resource_id});
+      this.addMeterToChart = (project_id) => {
+        this.chart['labels'] = this.getLabels(lodash.sortBy(this.samplesList[project_id], 'timestamp'));
+        this.chart['series'].push(project_id);
+        this.chart['data'].push(this.getData(lodash.sortBy(this.samplesList[project_id], 'timestamp')));
+        this.chartMeters.push(this.samplesList[project_id][0]); //use the 0 as are all samples for the same resource and I need the name
+        lodash.remove(this.sampleLabels, {id: project_id});
       }
 
       this.removeFromChart = (meter) => {
-        this.chart.data.splice(this.chart.series.indexOf(meter.resource_id), 1);
-        this.chart.series.splice(this.chart.series.indexOf(meter.resource_id), 1);
-        this.chartMeters.splice(this.chartMeters.indexOf(meter.resource_id), 1);
+        this.chart.data.splice(this.chart.series.indexOf(meter.project_id), 1);
+        this.chart.series.splice(this.chart.series.indexOf(meter.project_id), 1);
+        this.chartMeters.splice(lodash.findIndex(this.chartMeters, {project_id: meter.project_id}), 1);
         this.sampleLabels.push({
-          id: meter.resource_id,
-          name: meter.resource_name
+          id: meter.project_id,
+          name: meter.resource_name || meter.project_id
         })
       };
 
@@ -268,13 +287,14 @@
      
       this.formatSamplesLabels = (samples) => {
 
-        return lodash.uniq(samples.reduce((labels, item) => {
+        return lodash.uniq(samples, 'project_id')
+        .reduce((labels, item) => {
           labels.push({
-            id: item.resource_id,
-            name: item.resource_name
+            id: item.project_id,
+            name: item.resource_name || item.project_id
           });
           return labels;
-        }, []), item => item.id);
+        }, []);
       }
 
 
@@ -289,7 +309,7 @@
         .then(res => {
 
           // setup data for visualization
-          this.samplesList = lodash.groupBy(res, 'resource_id');
+          this.samplesList = lodash.groupBy(res, 'project_id');
           this.sampleLabels = this.formatSamplesLabels(res);
           
           // add current meter to chart
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/templates/ceilometer-dashboard.tpl.html b/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/templates/ceilometer-dashboard.tpl.html
index 2d880b5..f04a974 100644
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/templates/ceilometer-dashboard.tpl.html
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/templates/ceilometer-dashboard.tpl.html
@@ -30,11 +30,12 @@
           <uib-accordion-group
             ng-repeat="(service, slices) in vm.projects | orderObjectByKey"
             template-url="templates/accordion-group.html"
+            is-open="vm.accordion.open[service]"
             heading="{{service}}">
             <h4>Slices:</h4>
             <a ng-repeat="(slice, resources) in slices" 
               ng-class="{active: slice === vm.selectedSlice}"
-              ng-click="vm.selectResources(resources, slice)"
+              ng-click="vm.selectResources(resources, slice, service)"
               href="#" class="list-group-item" >
               {{slice}} <i class="glyphicon glyphicon-chevron-right pull-right"></i>
             </a>
@@ -85,7 +86,7 @@
                   </div>
                   <div class="col-xs-3"></div>
                 </div>
-                <div class="row" ng-click="vm.selectMeters(meters)" ng-repeat="meter in vm.selectedMeters" style="margin-bottom: 10px;">
+                <div class="row" ng-repeat="meter in vm.selectedMeters" style="margin-bottom: 10px;">
                   <div class="col-xs-6">
                     {{meter.name}}
                   </div>
@@ -93,7 +94,7 @@
                     {{meter.unit}}
                   </div>
                   <div class="col-xs-3">
-                    <a ui-sref="samples({name: meter.name, tenant: meter.resource_id})" class="btn btn-primary">
+                    <a ui-sref="samples({name: meter.name, tenant: meter.project_id})" class="btn btn-primary">
                       <i class="glyphicon glyphicon-search"></i>
                     </a>
                   </div>
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/templates/ceilometer-samples.tpl.html b/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/templates/ceilometer-samples.tpl.html
index f323802e..b279123 100644
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/templates/ceilometer-samples.tpl.html
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/templates/ceilometer-samples.tpl.html
@@ -42,7 +42,7 @@
   <div class="row" ng-if="!vm.loader">
     <div class="col-xs-12">
       <a ng-click="vm.removeFromChart(meter)" class="btn btn-chart" ng-style="{'background-color': vm.chartColors[$index]}" ng-repeat="meter in vm.chartMeters">
-        {{meter.resource_name}}
+        {{meter.resource_name || meter.resource_id}}
       </a>
     </div>
   </div>
diff --git a/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/templates/ceilometer-stats.tpl.html b/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/templates/ceilometer-stats.tpl.html
index fabce0a..1f5ff11 100644
--- a/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/templates/ceilometer-stats.tpl.html
+++ b/xos/core/xoslib/ngXosViews/ceilometerDashboard/src/templates/ceilometer-stats.tpl.html
@@ -14,7 +14,7 @@
       <th>Value:</th>
     </tr>
     <tr ng-repeat="item in vm.stats">
-      <td>{{item.type}}</td>
+      <td>{{item.category}}</td>
       <td>{{item.meter}}</td>
       <td>{{item.unit}}</td>
       <td>{{item.value}}</td>
diff --git a/xos/core/xoslib/static/css/xosCeilometerDashboard.css b/xos/core/xoslib/static/css/xosCeilometerDashboard.css
new file mode 100644
index 0000000..8aad3b2
--- /dev/null
+++ b/xos/core/xoslib/static/css/xosCeilometerDashboard.css
@@ -0,0 +1 @@
+#xosCeilometerDashboard{position:relative}.panel{margin-top:10px}.panel-body:not(:first-child){border-top:1px solid #e3e3e3}.panel-body .row{margin-top:10px}.chart{width:100%;height:300px}.btn-chart,.btn-chart:hover{color:#fff}.side-container{position:relative}.service-list{margin-top:-10px}.service-list h3{margin-top:0;margin-bottom:0}.service-list a{text-decoration:none;color:#333}.meters,.stats{margin-top:25px;position:absolute;top:0;left:0;width:100%}.loader{font-size:10px;margin:150px auto;text-indent:-9999em;width:11em;height:11em;border-radius:50%;background:#fff;background:linear-gradient(to right,#fff 10%,rgba(255,255,255,0) 42%);position:relative;animation:load3 1.4s infinite linear;transform:translateZ(0)}.loader:before{width:50%;height:50%;background:#105e9e;border-radius:100% 0 0;position:absolute;top:0;left:0;content:''}.loader:after{background:#fff;width:75%;height:75%;border-radius:50%;content:'';margin:auto;position:absolute;top:0;left:0;bottom:0;right:0}@keyframes load3{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}[ui-view]{position:absolute;top:0;bottom:0;width:100%}[ui-view].ceilometerDashboard.ng-leave{animation:1s bounceOutLeft ease}[ui-view].samples.ng-enter{animation:1s bounceInRight ease}[ui-view].samples.ng-leave{animation:1s bounceOutRight ease}[ui-view].ceilometerDashboard.ng-enter{animation:1s bounceInLeft ease}.animate .animate-slide-left.ng-hide-remove{animation:.5s bounceInRight ease}.animate .animate-slide-left.ng-hide-add{animation:.5s bounceOutRight ease}@keyframes bounceInRight{from,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1.000)}from{opacity:0;transform:translate3d(3000px,0,0)}60%{opacity:1;transform:translate3d(-25px,0,0)}75%{transform:translate3d(10px,0,0)}90%{transform:translate3d(-5px,0,0)}to{transform:none}}@keyframes bounceInLeft{from,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1.000)}0%{opacity:0;transform:translate3d(-3000px,0,0)}60%{opacity:1;transform:translate3d(25px,0,0)}75%{transform:translate3d(-10px,0,0)}90%{transform:translate3d(5px,0,0)}to{transform:none}}@keyframes slideInUp{from{transform:translate3d(0,100%,0);visibility:visible}to{transform:translate3d(0,0,0)}}@keyframes bounceOutRight{20%{opacity:1;transform:translate3d(-20px,0,0)}to{opacity:0;transform:translate3d(2000px,0,0)}}@keyframes bounceOutLeft{20%{opacity:1;transform:translate3d(20px,0,0)}to{opacity:0;transform:translate3d(-2000px,0,0)}}@keyframes slideOutDown{from{transform:translate3d(0,0,0)}to{visibility:hidden;transform:translate3d(0,100%,0)}}
\ No newline at end of file
diff --git a/xos/core/xoslib/static/js/xosCeilometerDashboard.js b/xos/core/xoslib/static/js/xosCeilometerDashboard.js
index 9e314d4..5aa7234 100644
--- a/xos/core/xoslib/static/js/xosCeilometerDashboard.js
+++ b/xos/core/xoslib/static/js/xosCeilometerDashboard.js
@@ -1 +1 @@
-"use strict";angular.module("xos.ceilometerDashboard",["ngResource","ngCookies","ngLodash","ui.router","xos.helpers","ngAnimate","chart.js","ui.bootstrap.accordion"]).config(["$stateProvider","$urlRouterProvider",function(e,t){e.state("ceilometerDashboard",{url:"/",template:"<ceilometer-dashboard></ceilometer-dashboard>"}).state("samples",{url:"/:name/:tenant/samples",template:"<ceilometer-samples></ceilometer-samples>"}),t.otherwise("/")}]).config(["$httpProvider",function(e){e.interceptors.push("NoHyperlinks")}]).run(["$rootScope",function(e){e.stateName="ceilometerDashboard",e.$on("$stateChangeStart",function(t,s){e.stateName=s.name})}]).service("Ceilometer",["$http","$q","lodash",function(e,t,s){var n=this;this.resourceMap={},this.getMeters=function(){var r=t.defer();return e.get("/xoslib/meters/",{cache:!0}).then(function(e){var t=s.groupBy(e.data,"resource_id");n.resourceMap=s.reduce(Object.keys(t),function(e,s){return e[s]=t[s][0].resource_name,e},{}),r.resolve(e.data)})["catch"](function(e){r.reject(e)}),r.promise},this.getSamples=function(s,n){var r=t.defer();return e.get("/xoslib/metersamples/",{params:{meter:s,tenant:n}}).then(function(e){r.resolve(e.data)})["catch"](function(e){r.reject(e)}),r.promise},this.getStats=function(n){var r=t.defer();return e.get("/xoslib/meterstatistics/",{cache:!0}).then(function(e){r.resolve(s.filter(e.data,{slice:n}))})["catch"](function(e){r.reject(e)}),r.promise}}]).directive("ceilometerDashboard",["lodash",function(e){return{restrict:"E",scope:{},bindToController:!0,controllerAs:"vm",templateUrl:"templates/ceilometer-dashboard.tpl.html",controller:["Ceilometer",function(t){var s=this;this.loadMeters=function(){s.loader=!0,t.getMeters().then(function(t){s.projects=e.groupBy(t,"service"),e.forEach(Object.keys(s.projects),function(t){s.projects[t]=e.groupBy(s.projects[t],"slice"),e.forEach(Object.keys(s.projects[t]),function(n){s.projects[t][n]=e.groupBy(s.projects[t][n],"resource_name")})})})["catch"](function(e){s.error=e.data.detail})["finally"](function(){s.loader=!1})},this.loadMeters(),this.selectService=function(e){s.selectedResources=null,s.selectedResource=null,s.selectedMeters=null,s.selectedService=e},this.selectedResources=null,this.selectResources=function(e,t){s.selectedResources=null,s.selectedResource=null,s.selectedMeters=null,s.selectedResources=e,s.selectedSlice=t},this.selectedMeters=null,this.selectMeters=function(e,t){s.selectedMeters=e,s.selectedResource=t}}]}}]).directive("ceilometerSamples",["lodash","$stateParams",function(e,t){return{restrict:"E",scope:{name:"=name",tenant:"=tenant"},bindToController:!0,controllerAs:"vm",templateUrl:"templates/ceilometer-samples.tpl.html",controller:["Ceilometer",function(s){var n=this;this.chartColors=["#286090","#F7464A","#46BFBD","#FDB45C","#97BBCD","#4D5360","#8c4f9f"],this.chart={series:[],labels:[],data:[]},Chart.defaults.global.colours=this.chartColors,this.chartType="line",t.name&&t.tenant&&(this.name=t.name,this.tenant=t.tenant),this.getLabels=function(e){return e.reduce(function(e,t){var s=new Date(t.timestamp);return e.push(s.getHours()+":"+((s.getMinutes()<10?"0":"")+s.getMinutes())+":"+s.getSeconds()),e},[])},this.getData=function(e){return e.reduce(function(e,t){return e.push(t.volume),e},[])},this.chartMeters=[],this.addMeterToChart=function(t){n.chart.labels=n.getLabels(e.sortBy(n.samplesList[t],"timestamp")),n.chart.series.push(t),n.chart.data.push(n.getData(e.sortBy(n.samplesList[t],"timestamp"))),console.log(n.samplesList[t]),n.chartMeters.push(n.samplesList[t][0]),e.remove(n.sampleLabels,{id:t})},this.removeFromChart=function(e){n.chart.data.splice(n.chart.series.indexOf(e.resource_id),1),n.chart.series.splice(n.chart.series.indexOf(e.resource_id),1),n.chartMeters.splice(n.chartMeters.indexOf(e.resource_id),1),n.sampleLabels.push({id:e.resource_id,name:e.resource_name})},this.formatSamplesLabels=function(t){return e.uniq(t.reduce(function(e,t){return e.push({id:t.resource_id,name:t.resource_name}),e},[]),function(e){return e.id})},this.showSamples=function(){n.loader=!0,s.getSamples(n.name).then(function(t){n.samplesList=e.groupBy(t,"resource_id"),n.sampleLabels=n.formatSamplesLabels(t),n.addMeterToChart(n.tenant)})["catch"](function(e){n.error=e.data.detail})["finally"](function(){n.loader=!1})},this.showSamples()}]}}]).directive("ceilometerStats",function(){return{restrict:"E",scope:{name:"=name"},bindToController:!0,controllerAs:"vm",templateUrl:"templates/ceilometer-stats.tpl.html",controller:["$scope","Ceilometer",function(e,t){var s=this;this.getStats=function(){s.loader=!0,t.getStats(s.name).then(function(e){s.stats=e})["catch"](function(e){s.error=e.data})["finally"](function(){s.loader=!1})},this.getStats(),e.$watch(function(){return s.name},function(){s.getStats()})}]}}).filter("orderObjectByKey",["lodash",function(e){return function(t){return t?e.reduce(Object.keys(t).reverse(),function(e,s){return e[s]=t[s],e},{}):void 0}}]),angular.module("xos.ceilometerDashboard").run(["$templateCache",function(e){e.put("templates/accordion-group.html",'<div class="panel {{panelClass || \'panel-default\'}}">\n  <div class="panel-heading" ng-keypress="toggleOpen($event)">\n    <h3>\n      <a href tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading"><span ng-class="{\'text-muted\': isDisabled}">{{heading}}</span></a>\n    </h3>\n  </div>\n  <div class="panel-collapse collapse" uib-collapse="!isOpen">\n	  <div class="panel-body" ng-transclude></div>\n  </div>\n</div>\n'),e.put("templates/accordion.html",'<div class="panel-group" ng-transclude></div>'),e.put("templates/ceilometer-dashboard.tpl.html",'<div class="row">\n  <div class="col-sm-10">\n    <h1>XOS Monitoring Statistics</h1>\n  </div>\n  <div class="col-xs-2 text-right">\n    <a href="" class="btn btn-default" \n      ng-show="vm.selectedResources && !vm.showStats"\n      ng-click="vm.showStats = true">\n      <i class="glyphicon glyphicon-transfer"></i>\n    </a>\n    <a href="" class="btn btn-default" \n      ng-show="vm.selectedResources && vm.showStats"\n      ng-click="vm.showStats = false">\n      <i class="glyphicon glyphicon-transfer"></i>\n    </a>\n  </div>\n</div>\n\n<div class="row" ng-show="vm.loader">\n  <div class="col-xs-12">\n    <div class="loader">Loading</div>\n  </div>\n</div>\n\n<section ng-hide="vm.loader" ng-class="{animate: !vm.loader}">\n  <div class="row">\n    <div class="col-sm-3 service-list">\n        <h3>XOS Service: </h3>\n        <uib-accordion close-others="true" template-url="templates/accordion.html">\n          <uib-accordion-group\n            ng-repeat="(service, slices) in vm.projects | orderObjectByKey"\n            template-url="templates/accordion-group.html"\n            heading="{{service}}">\n            <h4>Slices:</h4>\n            <a ng-repeat="(slice, resources) in slices" \n              ng-class="{active: slice === vm.selectedSlice}"\n              ng-click="vm.selectResources(resources, slice)"\n              href="#" class="list-group-item" >\n              {{slice}} <i class="glyphicon glyphicon-chevron-right pull-right"></i>\n            </a>\n          </uib-accordion-group>\n        </uib-accordion>\n    </div>\n    <section class="side-container col-sm-9">\n      <div class="row">\n        <article ng-hide="!vm.showStats" class="stats animate-slide-left">\n          <div class="col-xs-12">\n            <div class="list-group">\n              <div class="list-group-item">\n                <h3>Stats</h3>\n              </div>\n              <div class="list-group-item">\n                <ceilometer-stats ng-if="vm.selectedSlice" name="vm.selectedSlice"></ceilometer-stats>\n              </div>\n            </div>\n          </div>\n        </article>\n        <article ng-hide="vm.showStats" class="meters animate-slide-left">\n          <div class="col-sm-4 animate-slide-left" ng-hide="!vm.selectedResources">\n            <div class="list-group">\n              <div class="list-group-item">\n                <h3>Resources</h3>\n              </div>\n              <a href="#" \n                ng-click="vm.selectMeters(meters, resource)" \n                class="list-group-item" \n                ng-repeat="(resource, meters) in vm.selectedResources" \n                ng-class="{active: resource === vm.selectedResource}">\n                {{resource}} <i class="glyphicon glyphicon-chevron-right pull-right"></i>\n              </a>\n            </div>\n          </div>\n          <div class="col-sm-8 animate-slide-left" ng-hide="!vm.selectedMeters">\n            <div class="list-group">\n              <div class="list-group-item">\n                <h3>Meters</h3>\n              </div>\n              <div class="list-group-item">\n                <div class="row">\n                  <div class="col-xs-6">\n                    <label>Name:</label>\n                  </div>\n                  <div class="col-xs-3">\n                    <label>Unit:</label>\n                  </div>\n                  <div class="col-xs-3"></div>\n                </div>\n                <div class="row" ng-click="vm.selectMeters(meters)" ng-repeat="meter in vm.selectedMeters" style="margin-bottom: 10px;">\n                  <div class="col-xs-6">\n                    {{meter.name}}\n                  </div>\n                  <div class="col-xs-3">\n                    {{meter.unit}}\n                  </div>\n                  <div class="col-xs-3">\n                    <a ui-sref="samples({name: meter.name, tenant: meter.resource_id})" class="btn btn-primary">\n                      <i class="glyphicon glyphicon-search"></i>\n                    </a>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </article>\n      </div>\n    </section>\n  </div>\n</section>\n<section ng-if="!vm.loader && vm.error">\n  <div class="alert alert-danger">\n    {{vm.error}}\n  </div>\n</section>\n'),e.put("templates/ceilometer-samples.tpl.html",'<!-- <pre>{{ vm | json}}</pre> -->\n\n<div class="row">\n  <div class="col-xs-10">\n    <h1>{{vm.name | uppercase}}</h1>\n  </div>\n  <div class="col-xs-2">\n    <a ui-sref="ceilometerDashboard" class="btn btn-primary pull-right">\n      <i class="glyphicon glyphicon-arrow-left"></i> Back to list\n    </a>\n  </div>\n</div>\n<div class="row" ng-show="vm.loader">\n  <div class="col-xs-12">\n    <div class="loader">Loading</div>\n  </div>\n</div>\n<section ng-if="!vm.loader && !vm.error">\n  <div class="row">\n    <form class="form-inline col-xs-8" ng-submit="vm.addMeterToChart(vm.addMeterValue)">\n      <select ng-model="vm.addMeterValue" class="form-control" ng-options="resource.id as resource.name for resource in vm.sampleLabels"></select>\n      <button class="btn btn-success"> \n        <i class="glyphicon glyphicon-plus"></i> Add\n      </button>\n    </form>\n    <div class="col-xs-4 text-right">\n      <a ng-click="vm.chartType = \'line\'" class="btn" ng-class="{\'btn-default\': vm.chartType != \'bar\', \'btn-primary\': vm.chartType == \'line\'}">Lines</a>\n      <a ng-click="vm.chartType = \'bar\'" class="btn" ng-class="{\'btn-default\': vm.chartType != \'line\', \'btn-primary\': vm.chartType == \'bar\'}">Bars</a>\n    </div>\n  </div>\n  <div class="row" ng-if="!vm.loader">\n    <div class="col-xs-12">\n      <canvas ng-if="vm.chartType === \'line\'" id="line" class="chart chart-line" chart-data="vm.chart.data" chart-options="{datasetFill: false}"\n        chart-labels="vm.chart.labels" chart-legend="false" chart-series="vm.chart.series">\n      </canvas>\n      <canvas ng-if="vm.chartType === \'bar\'" id="bar" class="chart chart-bar" chart-data="vm.chart.data"\n        chart-labels="vm.chart.labels" chart-legend="false" chart-series="vm.chart.series">\n      </canvas>\n      <!-- <pre>{{vm.chartMeters | json}}</pre> -->\n    </div>\n  </div>\n  <div class="row" ng-if="!vm.loader">\n    <div class="col-xs-12">\n      <a ng-click="vm.removeFromChart(meter)" class="btn btn-chart" ng-style="{\'background-color\': vm.chartColors[$index]}" ng-repeat="meter in vm.chartMeters">\n        {{meter.resource_name}}\n      </a>\n    </div>\n  </div>\n</section>\n<section ng-if="!vm.loader && vm.error">\n  <div class="alert alert-danger">\n    {{vm.error}}\n  </div>\n</section>'),e.put("templates/ceilometer-stats.tpl.html",'<div ng-show="vm.loader" class="loader">Loading</div>\n\n<section ng-if="!vm.loader && !vm.error">\n\n  <div class="alert alert-danger" ng-if="vm.stats.length == 0">\n    No result\n  </div>  \n\n  <table class="table" ng-if="vm.stats.length > 0">\n    <tr>\n      <th>Type:</th>\n      <th>Meter:</th>\n      <th>Unit:</th>\n      <th>Value:</th>\n    </tr>\n    <tr ng-repeat="item in vm.stats">\n      <td>{{item.type}}</td>\n      <td>{{item.meter}}</td>\n      <td>{{item.unit}}</td>\n      <td>{{item.value}}</td>\n    </tr>\n  </table>\n</section>\n\n<section ng-if="!vm.loader && vm.error">\n  <div class="alert alert-danger">\n    {{vm.error}}\n  </div>\n</section>\n')}]),angular.module("xos.ceilometerDashboard").run(["$location",function(e){e.path("/")}]),angular.bootstrap(angular.element("#xosCeilometerDashboard"),["xos.ceilometerDashboard"]);
\ No newline at end of file
+"use strict";angular.module("xos.ceilometerDashboard",["ngResource","ngCookies","ngLodash","ui.router","xos.helpers","ngAnimate","chart.js","ui.bootstrap.accordion"]).config(["$stateProvider","$urlRouterProvider",function(e,t){e.state("ceilometerDashboard",{url:"/",template:"<ceilometer-dashboard></ceilometer-dashboard>"}).state("samples",{url:"/:name/:tenant/samples",template:"<ceilometer-samples></ceilometer-samples>"}),t.otherwise("/")}]).config(["$httpProvider",function(e){e.interceptors.push("NoHyperlinks")}]).run(["$rootScope",function(e){e.stateName="ceilometerDashboard",e.$on("$stateChangeStart",function(t,s){e.stateName=s.name})}]).service("Ceilometer",["$http","$q","lodash",function(e,t,s){var n=this;this.resourceMap={},this.getMeters=function(){var r=t.defer();return e.get("/xoslib/meters/",{cache:!0}).then(function(e){var t=s.groupBy(e.data,"resource_id");n.resourceMap=s.reduce(Object.keys(t),function(e,s){return e[s]=t[s][0].resource_name,e},{}),r.resolve(e.data)})["catch"](function(e){r.reject(e)}),r.promise},this.getSamples=function(s,n){var r=t.defer();return e.get("/xoslib/metersamples/",{params:{meter:s,tenant:n}}).then(function(e){r.resolve(e.data)})["catch"](function(e){r.reject(e)}),r.promise},this.getStats=function(n){var r=t.defer();return e.get("/xoslib/meterstatistics/",{cache:!0}).then(function(e){r.resolve(s.filter(e.data,{slice:n}))})["catch"](function(e){r.reject(e)}),r.promise}}]).directive("ceilometerDashboard",["lodash",function(e){return{restrict:"E",scope:{},bindToController:!0,controllerAs:"vm",templateUrl:"templates/ceilometer-dashboard.tpl.html",controller:["Ceilometer",function(t){var s=this;this.loadMeters=function(){s.loader=!0,t.getMeters().then(function(t){s.projects=e.groupBy(t,"service"),e.forEach(Object.keys(s.projects),function(t){s.projects[t]=e.groupBy(s.projects[t],"slice"),e.forEach(Object.keys(s.projects[t]),function(n){s.projects[t][n]=e.groupBy(s.projects[t][n],"resource_name")})})})["catch"](function(e){s.error=e.data.detail})["finally"](function(){s.loader=!1})},this.loadMeters(),this.selectService=function(e){s.selectedResources=null,s.selectedResource=null,s.selectedMeters=null,s.selectedService=e},this.selectedResources=null,this.selectResources=function(e,t){s.selectedResources=null,s.selectedResource=null,s.selectedMeters=null,s.selectedResources=e,s.selectedSlice=t},this.selectedMeters=null,this.selectMeters=function(e,t){s.selectedMeters=e,s.selectedResource=t}}]}}]).directive("ceilometerSamples",["lodash","$stateParams",function(e,t){return{restrict:"E",scope:{name:"=name",tenant:"=tenant"},bindToController:!0,controllerAs:"vm",templateUrl:"templates/ceilometer-samples.tpl.html",controller:["Ceilometer",function(s){var n=this;if(this.chartColors=["#286090","#F7464A","#46BFBD","#FDB45C","#97BBCD","#4D5360","#8c4f9f"],this.chart={series:[],labels:[],data:[]},Chart.defaults.global.colours=this.chartColors,this.chartType="line",!t.name||!t.tenant)throw new Error("Missing Name and Tenant Params!");this.name=t.name,this.tenant=t.tenant,this.getLabels=function(e){return e.reduce(function(e,t){var s=new Date(t.timestamp);return e.push(s.getHours()+":"+((s.getMinutes()<10?"0":"")+s.getMinutes())+":"+s.getSeconds()),e},[])},this.getData=function(e){return e.reduce(function(e,t){return e.push(t.volume),e},[])},this.chartMeters=[],this.addMeterToChart=function(t){n.chart.labels=n.getLabels(e.sortBy(n.samplesList[t],"timestamp")),n.chart.series.push(t),n.chart.data.push(n.getData(e.sortBy(n.samplesList[t],"timestamp"))),n.chartMeters.push(n.samplesList[t][0]),e.remove(n.sampleLabels,{id:t})},this.removeFromChart=function(t){n.chart.data.splice(n.chart.series.indexOf(t.resource_id),1),n.chart.series.splice(n.chart.series.indexOf(t.resource_id),1),n.chartMeters.splice(e.findIndex(n.chartMeters,{resource_id:t.resource_id}),1),n.sampleLabels.push({id:t.resource_id,name:t.resource_name})},this.formatSamplesLabels=function(t){return e.uniq(t,"resource_id").reduce(function(e,t){return e.push({id:t.resource_id,name:t.resource_name}),e},[])},this.showSamples=function(){n.loader=!0,s.getSamples(n.name).then(function(t){n.samplesList=e.groupBy(t,"resource_id"),n.sampleLabels=n.formatSamplesLabels(t),n.addMeterToChart(n.tenant)})["catch"](function(e){n.error=e.data.detail})["finally"](function(){n.loader=!1})},this.showSamples()}]}}]).directive("ceilometerStats",function(){return{restrict:"E",scope:{name:"=name"},bindToController:!0,controllerAs:"vm",templateUrl:"templates/ceilometer-stats.tpl.html",controller:["$scope","Ceilometer",function(e,t){var s=this;this.getStats=function(){s.loader=!0,t.getStats(s.name).then(function(e){s.stats=e})["catch"](function(e){s.error=e.data})["finally"](function(){s.loader=!1})},this.getStats(),e.$watch(function(){return s.name},function(){s.getStats()})}]}}).filter("orderObjectByKey",["lodash",function(e){return function(t){return t?e.reduce(Object.keys(t).reverse(),function(e,s){return e[s]=t[s],e},{}):void 0}}]),angular.module("xos.ceilometerDashboard").run(["$templateCache",function(e){e.put("templates/accordion-group.html",'<div class="panel {{panelClass || \'panel-default\'}}">\n  <div class="panel-heading" ng-keypress="toggleOpen($event)">\n    <h3>\n      <a href tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading"><span ng-class="{\'text-muted\': isDisabled}">{{heading}}</span></a>\n    </h3>\n  </div>\n  <div class="panel-collapse collapse" uib-collapse="!isOpen">\n	  <div class="panel-body" ng-transclude></div>\n  </div>\n</div>\n'),e.put("templates/accordion.html",'<div class="panel-group" ng-transclude></div>'),e.put("templates/ceilometer-dashboard.tpl.html",'<div class="row">\n  <div class="col-sm-10">\n    <h1>XOS Monitoring Statistics</h1>\n  </div>\n  <div class="col-xs-2 text-right">\n    <a href="" class="btn btn-default" \n      ng-show="vm.selectedResources && !vm.showStats"\n      ng-click="vm.showStats = true">\n      <i class="glyphicon glyphicon-transfer"></i>\n    </a>\n    <a href="" class="btn btn-default" \n      ng-show="vm.selectedResources && vm.showStats"\n      ng-click="vm.showStats = false">\n      <i class="glyphicon glyphicon-transfer"></i>\n    </a>\n  </div>\n</div>\n\n<div class="row" ng-show="vm.loader">\n  <div class="col-xs-12">\n    <div class="loader">Loading</div>\n  </div>\n</div>\n\n<section ng-hide="vm.loader" ng-class="{animate: !vm.loader}">\n  <div class="row">\n    <div class="col-sm-3 service-list">\n        <h3>XOS Service: </h3>\n        <uib-accordion close-others="true" template-url="templates/accordion.html">\n          <uib-accordion-group\n            ng-repeat="(service, slices) in vm.projects | orderObjectByKey"\n            template-url="templates/accordion-group.html"\n            heading="{{service}}">\n            <h4>Slices:</h4>\n            <a ng-repeat="(slice, resources) in slices" \n              ng-class="{active: slice === vm.selectedSlice}"\n              ng-click="vm.selectResources(resources, slice)"\n              href="#" class="list-group-item" >\n              {{slice}} <i class="glyphicon glyphicon-chevron-right pull-right"></i>\n            </a>\n          </uib-accordion-group>\n        </uib-accordion>\n    </div>\n    <section class="side-container col-sm-9">\n      <div class="row">\n        <article ng-hide="!vm.showStats" class="stats animate-slide-left">\n          <div class="col-xs-12">\n            <div class="list-group">\n              <div class="list-group-item">\n                <h3>Stats</h3>\n              </div>\n              <div class="list-group-item">\n                <ceilometer-stats ng-if="vm.selectedSlice" name="vm.selectedSlice"></ceilometer-stats>\n              </div>\n            </div>\n          </div>\n        </article>\n        <article ng-hide="vm.showStats" class="meters animate-slide-left">\n          <div class="col-sm-4 animate-slide-left" ng-hide="!vm.selectedResources">\n            <div class="list-group">\n              <div class="list-group-item">\n                <h3>Resources</h3>\n              </div>\n              <a href="#" \n                ng-click="vm.selectMeters(meters, resource)" \n                class="list-group-item" \n                ng-repeat="(resource, meters) in vm.selectedResources" \n                ng-class="{active: resource === vm.selectedResource}">\n                {{resource}} <i class="glyphicon glyphicon-chevron-right pull-right"></i>\n              </a>\n            </div>\n          </div>\n          <div class="col-sm-8 animate-slide-left" ng-hide="!vm.selectedMeters">\n            <div class="list-group">\n              <div class="list-group-item">\n                <h3>Meters</h3>\n              </div>\n              <div class="list-group-item">\n                <div class="row">\n                  <div class="col-xs-6">\n                    <label>Name:</label>\n                  </div>\n                  <div class="col-xs-3">\n                    <label>Unit:</label>\n                  </div>\n                  <div class="col-xs-3"></div>\n                </div>\n                <div class="row" ng-click="vm.selectMeters(meters)" ng-repeat="meter in vm.selectedMeters" style="margin-bottom: 10px;">\n                  <div class="col-xs-6">\n                    {{meter.name}}\n                  </div>\n                  <div class="col-xs-3">\n                    {{meter.unit}}\n                  </div>\n                  <div class="col-xs-3">\n                    <a ui-sref="samples({name: meter.name, tenant: meter.resource_id})" class="btn btn-primary">\n                      <i class="glyphicon glyphicon-search"></i>\n                    </a>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </article>\n      </div>\n    </section>\n  </div>\n</section>\n<section ng-if="!vm.loader && vm.error">\n  <div class="alert alert-danger">\n    {{vm.error}}\n  </div>\n</section>\n'),e.put("templates/ceilometer-samples.tpl.html",'<!-- <pre>{{ vm | json}}</pre> -->\n\n<div class="row">\n  <div class="col-xs-10">\n    <h1>{{vm.name | uppercase}}</h1>\n  </div>\n  <div class="col-xs-2">\n    <a ui-sref="ceilometerDashboard" class="btn btn-primary pull-right">\n      <i class="glyphicon glyphicon-arrow-left"></i> Back to list\n    </a>\n  </div>\n</div>\n<div class="row" ng-show="vm.loader">\n  <div class="col-xs-12">\n    <div class="loader">Loading</div>\n  </div>\n</div>\n<section ng-if="!vm.loader && !vm.error">\n  <div class="row">\n    <form class="form-inline col-xs-8" ng-submit="vm.addMeterToChart(vm.addMeterValue)">\n      <select ng-model="vm.addMeterValue" class="form-control" ng-options="resource.id as resource.name for resource in vm.sampleLabels"></select>\n      <button class="btn btn-success"> \n        <i class="glyphicon glyphicon-plus"></i> Add\n      </button>\n    </form>\n    <div class="col-xs-4 text-right">\n      <a ng-click="vm.chartType = \'line\'" class="btn" ng-class="{\'btn-default\': vm.chartType != \'bar\', \'btn-primary\': vm.chartType == \'line\'}">Lines</a>\n      <a ng-click="vm.chartType = \'bar\'" class="btn" ng-class="{\'btn-default\': vm.chartType != \'line\', \'btn-primary\': vm.chartType == \'bar\'}">Bars</a>\n    </div>\n  </div>\n  <div class="row" ng-if="!vm.loader">\n    <div class="col-xs-12">\n      <canvas ng-if="vm.chartType === \'line\'" id="line" class="chart chart-line" chart-data="vm.chart.data" chart-options="{datasetFill: false}"\n        chart-labels="vm.chart.labels" chart-legend="false" chart-series="vm.chart.series">\n      </canvas>\n      <canvas ng-if="vm.chartType === \'bar\'" id="bar" class="chart chart-bar" chart-data="vm.chart.data"\n        chart-labels="vm.chart.labels" chart-legend="false" chart-series="vm.chart.series">\n      </canvas>\n      <!-- <pre>{{vm.chartMeters | json}}</pre> -->\n    </div>\n  </div>\n  <div class="row" ng-if="!vm.loader">\n    <div class="col-xs-12">\n      <a ng-click="vm.removeFromChart(meter)" class="btn btn-chart" ng-style="{\'background-color\': vm.chartColors[$index]}" ng-repeat="meter in vm.chartMeters">\n        {{meter.resource_name}}\n      </a>\n    </div>\n  </div>\n</section>\n<section ng-if="!vm.loader && vm.error">\n  <div class="alert alert-danger">\n    {{vm.error}}\n  </div>\n</section>'),e.put("templates/ceilometer-stats.tpl.html",'<div ng-show="vm.loader" class="loader">Loading</div>\n\n<section ng-if="!vm.loader && !vm.error">\n\n  <div class="alert alert-danger" ng-if="vm.stats.length == 0">\n    No result\n  </div>  \n\n  <table class="table" ng-if="vm.stats.length > 0">\n    <tr>\n      <th>Type:</th>\n      <th>Meter:</th>\n      <th>Unit:</th>\n      <th>Value:</th>\n    </tr>\n    <tr ng-repeat="item in vm.stats">\n      <td>{{item.type}}</td>\n      <td>{{item.meter}}</td>\n      <td>{{item.unit}}</td>\n      <td>{{item.value}}</td>\n    </tr>\n  </table>\n</section>\n\n<section ng-if="!vm.loader && vm.error">\n  <div class="alert alert-danger">\n    {{vm.error}}\n  </div>\n</section>\n')}]),angular.module("xos.ceilometerDashboard").run(["$location",function(e){e.path("/")}]),angular.bootstrap(angular.element("#xosCeilometerDashboard"),["xos.ceilometerDashboard"]);
\ No newline at end of file
diff --git a/xos/observers/vcpe/files/etc/rc.local b/xos/observers/vcpe/files/etc/rc.local
new file mode 100755
index 0000000..24c8f8d
--- /dev/null
+++ b/xos/observers/vcpe/files/etc/rc.local
@@ -0,0 +1,22 @@
+#!/bin/sh -e
+#
+# rc.local
+#
+# This script is executed at the end of each multiuser runlevel.
+# Make sure that the script will "exit 0" on success or any other
+# value on error.
+#
+# In order to enable or disable this script just change the execution
+# bits.
+#
+# By default this script does nothing.
+
+ufw enable
+ufw allow bootps
+ufw allow from 192.168.0.0/24
+ufw route allow in on eth1 out on eth0
+ufw route allow in on eth1 out on eth2
+
+# service dnsmasq start
+
+exit 0
diff --git a/xos/observers/vcpe/files/etc/ufw/after.init b/xos/observers/vcpe/files/etc/ufw/after.init
new file mode 100644
index 0000000..e89217d
--- /dev/null
+++ b/xos/observers/vcpe/files/etc/ufw/after.init
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# after.init: if executable, called by ufw-init. See 'man ufw-framework' for
+#             details. Note that output from these scripts is not seen via the
+#             the ufw command, but instead via ufw-init.
+#
+# Copyright 2013 Canonical Ltd.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License version 3,
+#    as published by the Free Software Foundation.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+set -e
+
+case "$1" in
+start)
+    # typically required
+    ;;
+stop)
+    # typically required
+    ;;
+status)
+    # optional
+    ;;
+flush-all)
+    # optional
+    ;;
+*)
+    echo "'$1' not supported"
+    echo "Usage: after.init {start|stop|flush-all|status}"
+    ;;
+esac
diff --git a/xos/observers/vcpe/files/etc/ufw/after.rules b/xos/observers/vcpe/files/etc/ufw/after.rules
new file mode 100644
index 0000000..0d6c646
--- /dev/null
+++ b/xos/observers/vcpe/files/etc/ufw/after.rules
@@ -0,0 +1,30 @@
+#
+# rules.input-after
+#
+# Rules that should be run after the ufw command line added rules. Custom
+# rules should be added to one of these chains:
+#   ufw-after-input
+#   ufw-after-output
+#   ufw-after-forward
+#
+
+# Don't delete these required lines, otherwise there will be errors
+*filter
+:ufw-after-input - [0:0]
+:ufw-after-output - [0:0]
+:ufw-after-forward - [0:0]
+# End required lines
+
+# don't log noisy services by default
+-A ufw-after-input -p udp --dport 137 -j ufw-skip-to-policy-input
+-A ufw-after-input -p udp --dport 138 -j ufw-skip-to-policy-input
+-A ufw-after-input -p tcp --dport 139 -j ufw-skip-to-policy-input
+-A ufw-after-input -p tcp --dport 445 -j ufw-skip-to-policy-input
+-A ufw-after-input -p udp --dport 67 -j ufw-skip-to-policy-input
+-A ufw-after-input -p udp --dport 68 -j ufw-skip-to-policy-input
+
+# don't log noisy broadcast
+-A ufw-after-input -m addrtype --dst-type BROADCAST -j ufw-skip-to-policy-input
+
+# don't delete the 'COMMIT' line or these rules won't be processed
+COMMIT
diff --git a/xos/observers/vcpe/files/etc/ufw/after6.rules b/xos/observers/vcpe/files/etc/ufw/after6.rules
new file mode 100644
index 0000000..0d99672
--- /dev/null
+++ b/xos/observers/vcpe/files/etc/ufw/after6.rules
@@ -0,0 +1,27 @@
+#
+# rules.input-after
+#
+# Rules that should be run after the ufw command line added rules. Custom
+# rules should be added to one of these chains:
+#   ufw6-after-input
+#   ufw6-after-output
+#   ufw6-after-forward
+#
+
+# Don't delete these required lines, otherwise there will be errors
+*filter
+:ufw6-after-input - [0:0]
+:ufw6-after-output - [0:0]
+:ufw6-after-forward - [0:0]
+# End required lines
+
+# don't log noisy services by default
+-A ufw6-after-input -p udp --dport 137 -j ufw6-skip-to-policy-input
+-A ufw6-after-input -p udp --dport 138 -j ufw6-skip-to-policy-input
+-A ufw6-after-input -p tcp --dport 139 -j ufw6-skip-to-policy-input
+-A ufw6-after-input -p tcp --dport 445 -j ufw6-skip-to-policy-input
+-A ufw6-after-input -p udp --dport 546 -j ufw6-skip-to-policy-input
+-A ufw6-after-input -p udp --dport 547 -j ufw6-skip-to-policy-input
+
+# don't delete the 'COMMIT' line or these rules won't be processed
+COMMIT
diff --git a/xos/observers/vcpe/files/etc/ufw/before.init b/xos/observers/vcpe/files/etc/ufw/before.init
new file mode 100644
index 0000000..1348cb1
--- /dev/null
+++ b/xos/observers/vcpe/files/etc/ufw/before.init
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# before.init: if executable, called by ufw-init. See 'man ufw-framework' for
+#              details. Note that output from these scripts is not seen via the
+#              the ufw command, but instead via ufw-init.
+#
+# Copyright 2013 Canonical Ltd.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License version 3,
+#    as published by the Free Software Foundation.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+set -e
+
+case "$1" in
+start)
+    # typically required
+    ;;
+stop)
+    # typically required
+    ;;
+status)
+    # optional
+    ;;
+flush-all)
+    # optional
+    ;;
+*)
+    echo "'$1' not supported"
+    echo "Usage: before.init {start|stop|flush-all|status}"
+    ;;
+esac
diff --git a/xos/observers/vcpe/files/etc/ufw/before6.rules b/xos/observers/vcpe/files/etc/ufw/before6.rules
new file mode 100644
index 0000000..0b26ed8
--- /dev/null
+++ b/xos/observers/vcpe/files/etc/ufw/before6.rules
@@ -0,0 +1,73 @@
+#
+# rules.before
+#
+# Rules that should be run before the ufw command line added rules. Custom
+# rules should be added to one of these chains:
+#   ufw6-before-input
+#   ufw6-before-output
+#   ufw6-before-forward
+#
+
+# Don't delete these required lines, otherwise there will be errors
+*filter
+:ufw6-before-input - [0:0]
+:ufw6-before-output - [0:0]
+:ufw6-before-forward - [0:0]
+# End required lines
+
+
+# allow all on loopback
+-A ufw6-before-input -i lo -j ACCEPT
+-A ufw6-before-output -o lo -j ACCEPT
+
+# drop packets with RH0 headers
+-A ufw6-before-input -m rt --rt-type 0 -j DROP
+-A ufw6-before-forward -m rt --rt-type 0 -j DROP
+-A ufw6-before-output -m rt --rt-type 0 -j DROP
+
+# for stateless autoconfiguration (restrict NDP messages to hop limit of 255)
+-A ufw6-before-input -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT
+-A ufw6-before-output -p icmpv6 --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT
+-A ufw6-before-input -p icmpv6 --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT
+-A ufw6-before-output -p icmpv6 --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT
+-A ufw6-before-input -p icmpv6 --icmpv6-type router-solicitation -m hl --hl-eq 255 -j ACCEPT
+-A ufw6-before-input -p icmpv6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT
+
+# quickly process packets for which we already have a connection
+-A ufw6-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+-A ufw6-before-output -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+-A ufw6-before-forward -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+
+# for multicast ping replies from link-local addresses (these don't have an
+# associated connection and would otherwise be marked INVALID)
+-A ufw6-before-input -p icmpv6 --icmpv6-type echo-reply -s fe80::/10 -j ACCEPT
+
+# drop INVALID packets (logs these in loglevel medium and higher)
+-A ufw6-before-input -m conntrack --ctstate INVALID -j ufw6-logging-deny
+-A ufw6-before-input -m conntrack --ctstate INVALID -j DROP
+
+# ok icmp codes for INPUT
+-A ufw6-before-input -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT
+-A ufw6-before-input -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT
+-A ufw6-before-input -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT
+-A ufw6-before-input -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT
+-A ufw6-before-input -p icmpv6 --icmpv6-type echo-request -j ACCEPT
+
+# ok icmp code for FORWARD
+-A ufw6-before-forward -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT
+-A ufw6-before-forward -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT
+-A ufw6-before-forward -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT
+-A ufw6-before-forward -p icmpv6 --icmpv6-type parameter-problem -j ACCEPT
+-A ufw6-before-forward -p icmpv6 --icmpv6-type echo-request -j ACCEPT
+
+# allow dhcp client to work
+-A ufw6-before-input -p udp -s fe80::/10 --sport 547 -d fe80::/10 --dport 546 -j ACCEPT
+
+# allow MULTICAST mDNS for service discovery
+-A ufw6-before-input -p udp -d ff02::fb --dport 5353 -j ACCEPT
+
+# allow MULTICAST UPnP for service discovery
+-A ufw6-before-input -p udp -d ff02::f --dport 1900 -j ACCEPT
+
+# don't delete the 'COMMIT' line or these rules won't be processed
+COMMIT
diff --git a/xos/observers/vcpe/files/etc/ufw/sysctl.conf b/xos/observers/vcpe/files/etc/ufw/sysctl.conf
new file mode 100644
index 0000000..8707032
--- /dev/null
+++ b/xos/observers/vcpe/files/etc/ufw/sysctl.conf
@@ -0,0 +1,57 @@
+#
+# Configuration file for setting network variables. Please note these settings
+# override /etc/sysctl.conf. If you prefer to use /etc/sysctl.conf, please
+# adjust IPT_SYSCTL in /etc/default/ufw.
+#
+
+# Uncomment this to allow this host to route packets between interfaces
+#net/ipv4/ip_forward=1
+#net/ipv6/conf/default/forwarding=1
+#net/ipv6/conf/all/forwarding=1
+
+# Turn on Source Address Verification in all interfaces to prevent some
+# spoofing attacks
+net/ipv4/conf/default/rp_filter=1
+net/ipv4/conf/all/rp_filter=1
+
+# Do not accept IP source route packets (we are not a router)
+net/ipv4/conf/default/accept_source_route=0
+net/ipv4/conf/all/accept_source_route=0
+net/ipv6/conf/default/accept_source_route=0
+net/ipv6/conf/all/accept_source_route=0
+
+# Disable ICMP redirects. ICMP redirects are rarely used but can be used in
+# MITM (man-in-the-middle) attacks. Disabling ICMP may disrupt legitimate
+# traffic to those sites.
+net/ipv4/conf/default/accept_redirects=0
+net/ipv4/conf/all/accept_redirects=0
+net/ipv6/conf/default/accept_redirects=0
+net/ipv6/conf/all/accept_redirects=0
+
+# Ignore bogus ICMP errors
+net/ipv4/icmp_echo_ignore_broadcasts=1
+net/ipv4/icmp_ignore_bogus_error_responses=1
+net/ipv4/icmp_echo_ignore_all=0
+
+# Don't log Martian Packets (impossible packets)
+net/ipv4/conf/default/log_martians=0
+net/ipv4/conf/all/log_martians=0
+
+# Change to '1' to enable TCP/IP SYN cookies This disables TCP Window Scaling
+# (http://lkml.org/lkml/2008/2/5/167)
+net/ipv4/tcp_syncookies=0
+
+#net/ipv4/tcp_fin_timeout=30
+#net/ipv4/tcp_keepalive_intvl=1800
+
+# normally allowing tcp_sack is ok, but if going through OpenBSD 3.8 RELEASE or
+# earlier pf firewall, should set this to 0
+net/ipv4/tcp_sack=1
+
+# Uncomment this to turn off ipv6 autoconfiguration
+#net/ipv6/conf/default/autoconf=0
+#net/ipv6/conf/all/autoconf=0
+
+# Uncomment this to enable ipv6 privacy addressing
+#net/ipv6/conf/default/use_tempaddr=2
+#net/ipv6/conf/all/use_tempaddr=2
diff --git a/xos/observers/vcpe/files/etc/ufw/ufw.conf b/xos/observers/vcpe/files/etc/ufw/ufw.conf
new file mode 100644
index 0000000..28fe534
--- /dev/null
+++ b/xos/observers/vcpe/files/etc/ufw/ufw.conf
@@ -0,0 +1,10 @@
+# /etc/ufw/ufw.conf
+#
+
+# Set to yes to start on boot. If setting this remotely, be sure to add a rule
+# to allow your remote connection before starting ufw. Eg: 'ufw allow 22/tcp'
+ENABLED=yes
+
+# Please use the 'ufw' command to set the loglevel. Eg: 'ufw logging medium'.
+# See 'man ufw' for details.
+LOGLEVEL=low
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant_new.yaml b/xos/observers/vcpe/steps/sync_vcpetenant_new.yaml
index 960b480..eb647b6 100644
--- a/xos/observers/vcpe/steps/sync_vcpetenant_new.yaml
+++ b/xos/observers/vcpe/steps/sync_vcpetenant_new.yaml
@@ -67,8 +67,39 @@
     notify:
     - restart dnsmasq
 
+  - name: create directory for "safe" config
+    file: path=/var/container_volumes/{{ container_name }}/etc/dnsmasq.d/safe state=directory
+
+  - name: dnsmasq "safe" config
+    template: src=/opt/xos/observers/vcpe/templates/dnsmasq_safe_servers.j2 dest=/var/container_volumes/{{ container_name }}/etc/dnsmasq.d/safe/servers.conf owner=root group=root
+    notify:
+    - restart dnsmasq
+
+  - name: copy base ufw files
+    synchronize: src=/opt/xos/observers/vcpe/files/etc/ufw/ dest=/var/container_volumes/{{ container_name }}/etc/ufw/
+    notify:
+    - reload ufw
+
+  - name: redirection rules for safe DNS
+    template: src=/opt/xos/observers/vcpe/templates/before.rules.j2 dest=/var/container_volumes/{{ container_name }}/etc/ufw/before.rules owner=root group=root
+    notify:
+    - reload ufw
+
+  - name: base ufw setup uses /etc/rc.local
+    copy: src=/opt/xos/observers/vcpe/files/etc/rc.local dest=/var/container_volumes/{{ container_name }}/etc/ owner=root group=root
+    notify:
+    - copy in /etc/rc.local
+
   handlers:
   # Dnsmasq is automatically restarted in the container
   - name: restart dnsmasq
     shell: docker exec {{ container_name }} /usr/bin/killall dnsmasq
 
+  - name: reload ufw
+    shell: docker exec {{ container_name }} bash -c "/sbin/iptables -t nat -F PREROUTING; /usr/sbin/ufw reload"
+
+  # Use docker cp instead of single-file volume
+  # The reason is that changes to external file volume don't show up inside the container
+  # Probably Ansible deletes and then recreates the external file, and container has old version
+  - name: copy in /etc/rc.local
+    shell: docker cp /var/container_volumes/{{ container_name }}/etc/rc.local {{ container_name }}:/etc/
diff --git a/xos/observers/vcpe/templates/before.rules.j2 b/xos/observers/vcpe/templates/before.rules.j2
new file mode 100644
index 0000000..e6f7d4a
--- /dev/null
+++ b/xos/observers/vcpe/templates/before.rules.j2
@@ -0,0 +1,97 @@
+#
+# rules.before
+#
+# Rules that should be run before the ufw command line added rules. Custom
+# rules should be added to one of these chains:
+#   ufw-before-input
+#   ufw-before-output
+#   ufw-before-forward
+#
+
+# nat Table rules
+*nat
+:POSTROUTING ACCEPT [0:0]
+
+# Forward traffic from eth1 through eth0.
+-A POSTROUTING -o eth0 -j MASQUERADE
+
+# Set up NAT for CDN services
+-A POSTROUTING -o eth2 -j MASQUERADE
+
+# DNS safe browsing
+{% if safe_browsing %}
+{% for mac in safe_browsing %}
+-A PREROUTING -i eth1 -m mac --mac-source {{ mac }} -p udp --dport 53 -j REDIRECT --to-port 5353
+-A PREROUTING -i eth1 -m mac --mac-source {{ mac }} -p tcp --dport 53 -j REDIRECT --to-port 5353
+{% endfor %}
+{% endif %}
+
+# don't delete the 'COMMIT' line or these nat table rules won't be processed
+COMMIT
+
+# Don't delete these required lines, otherwise there will be errors
+*filter
+:ufw-before-input - [0:0]
+:ufw-before-output - [0:0]
+:ufw-before-forward - [0:0]
+:ufw-not-local - [0:0]
+# End required lines
+
+# allow all on loopback
+-A ufw-before-input -i lo -j ACCEPT
+-A ufw-before-output -o lo -j ACCEPT
+
+# quickly process packets for which we already have a connection
+-A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+-A ufw-before-output -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+-A ufw-before-forward -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+
+# drop INVALID packets (logs these in loglevel medium and higher)
+-A ufw-before-input -m conntrack --ctstate INVALID -j ufw-logging-deny
+-A ufw-before-input -m conntrack --ctstate INVALID -j DROP
+
+# ok icmp codes for INPUT
+-A ufw-before-input -p icmp --icmp-type destination-unreachable -j ACCEPT
+-A ufw-before-input -p icmp --icmp-type source-quench -j ACCEPT
+-A ufw-before-input -p icmp --icmp-type time-exceeded -j ACCEPT
+-A ufw-before-input -p icmp --icmp-type parameter-problem -j ACCEPT
+-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT
+
+# ok icmp code for FORWARD
+-A ufw-before-forward -p icmp --icmp-type destination-unreachable -j ACCEPT
+-A ufw-before-forward -p icmp --icmp-type source-quench -j ACCEPT
+-A ufw-before-forward -p icmp --icmp-type time-exceeded -j ACCEPT
+-A ufw-before-forward -p icmp --icmp-type parameter-problem -j ACCEPT
+-A ufw-before-forward -p icmp --icmp-type echo-request -j ACCEPT
+
+# allow dhcp client to work
+-A ufw-before-input -p udp --sport 67 --dport 68 -j ACCEPT
+
+#
+# ufw-not-local
+#
+-A ufw-before-input -j ufw-not-local
+
+# if LOCAL, RETURN
+-A ufw-not-local -m addrtype --dst-type LOCAL -j RETURN
+
+# if MULTICAST, RETURN
+-A ufw-not-local -m addrtype --dst-type MULTICAST -j RETURN
+
+# if BROADCAST, RETURN
+-A ufw-not-local -m addrtype --dst-type BROADCAST -j RETURN
+
+# all other non-local packets are dropped
+-A ufw-not-local -m limit --limit 3/min --limit-burst 10 -j ufw-logging-deny
+-A ufw-not-local -j DROP
+
+# allow MULTICAST mDNS for service discovery (be sure the MULTICAST line above
+# is uncommented)
+-A ufw-before-input -p udp -d 224.0.0.251 --dport 5353 -j ACCEPT
+
+# allow MULTICAST UPnP for service discovery (be sure the MULTICAST line above
+# is uncommented)
+-A ufw-before-input -p udp -d 239.255.255.250 --dport 1900 -j ACCEPT
+
+# don't delete the 'COMMIT' line or these rules won't be processed
+COMMIT
diff --git a/xos/observers/vcpe/templates/dnsmasq_safe_servers.j2 b/xos/observers/vcpe/templates/dnsmasq_safe_servers.j2
new file mode 100644
index 0000000..2f93777
--- /dev/null
+++ b/xos/observers/vcpe/templates/dnsmasq_safe_servers.j2
@@ -0,0 +1,14 @@
+# This file autogenerated by vCPE observer
+# It contains a list of DNS servers for dnsmasq to use.
+no-resolv
+
+{% if cdn_enable %}
+# CDN
+{% for prefix in cdn_prefixes %}
+server=/{{ prefix }}/{{ dnsdemux_ip }}
+{% endfor %}
+{% endif %}
+
+# use OpenDNS service
+server=208.67.222.123
+server=208.67.220.123
diff --git a/xos/observers/vcpe/templates/dnsmasq_servers.j2 b/xos/observers/vcpe/templates/dnsmasq_servers.j2
index 88f523d..c89c762 100644
--- a/xos/observers/vcpe/templates/dnsmasq_servers.j2
+++ b/xos/observers/vcpe/templates/dnsmasq_servers.j2
@@ -3,21 +3,12 @@
 no-resolv
 
 {% if cdn_enable %}
-# CDN 
+# CDN
 {% for prefix in cdn_prefixes %}
 server=/{{ prefix }}/{{ dnsdemux_ip }}
 {% endfor %}
 {% endif %}
 
-{% if url_filter_enable %}
-# Point to BroadbandShield service, disable cache, and enable eDNS MAC passing
-add-mac
-cache-size=0
-{% for addr in bbs_addrs %}
-server={{ addr }}
-{% endfor %}
-{% else %}
 # use google's DNS service
 server=8.8.8.8
 server=8.8.4.4
-{% endif %}
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index dc23c84..30798d5 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -166,6 +166,19 @@
                 type: string
                 required: false
 
+    tosca.nodes.ONOSVTNApp:
+        derived_from: tosca.nodes.Root
+        description: >
+            An ONOS VTN Application.
+        properties:
+            xos_base_tenant_props
+            dependencies:
+                type: string
+                required: false
+            rest_onos/v1/network/configuration/:
+                type: string
+                required: false
+
     tosca.nodes.VCPEService:
         description: >
             CORD: The vCPE Service.
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 1256c3b..63d7395 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -195,6 +195,26 @@
                 type: string
                 required: false
 
+    tosca.nodes.ONOSVTNApp:
+        derived_from: tosca.nodes.Root
+        description: >
+            An ONOS VTN Application.
+        properties:
+            kind:
+                type: string
+                default: generic
+                description: Kind of tenant
+            service_specific_id:
+                type: string
+                required: false
+                description: Service specific ID opaque to XOS but meaningful to service
+            dependencies:
+                type: string
+                required: false
+            rest_onos/v1/network/configuration/:
+                type: string
+                required: false
+
     tosca.nodes.VCPEService:
         description: >
             CORD: The vCPE Service.
diff --git a/xos/tosca/resources/onosapp.py b/xos/tosca/resources/onosapp.py
index 7ed47d7..42cb0bf 100644
--- a/xos/tosca/resources/onosapp.py
+++ b/xos/tosca/resources/onosapp.py
@@ -12,7 +12,7 @@
 from xosresource import XOSResource
 
 class XOSONOSApp(XOSResource):
-    provides = ["tosca.nodes.ONOSApp", "tosca.nodes.ONOSvBNGApp", "tosca.nodes.ONOSvOLTApp"]
+    provides = ["tosca.nodes.ONOSApp", "tosca.nodes.ONOSvBNGApp", "tosca.nodes.ONOSvOLTApp", "tosca.nodes.ONOSVTNApp"]
     xos_model = ONOSApp
     copyin_props = ["service_specific_id", "dependencies"]
 
diff --git a/xos/tosca/samples/vtn.yaml b/xos/tosca/samples/vtn.yaml
index 9cb7c95..5ce4670 100644
--- a/xos/tosca/samples/vtn.yaml
+++ b/xos/tosca/samples/vtn.yaml
@@ -18,13 +18,16 @@
           pubkey: /opt/xos/observers/onos/onos_key.pub
 
     VTN_ONOS_app:
-      type: tosca.nodes.ONOSvBNGApp
+      type: tosca.nodes.ONOSVTNApp
       requirements:
           - onos_tenant:
               node: service_ONOS_VTN
               relationship: tosca.relationships.TenantOfService
       properties:
-#          dependencies: org.onosproject.proxyarp, org.onosproject.virtualbng, org.onosproject.openflow, org.onosproject.fwd
+          dependencies: org.onosproject.openstackswitching, org.onosproject.cordvtn
+          rest_onos/v1/network/configuration/: { get_artifact: [ SELF, vtn_network_cfg_json, LOCAL_FILE ] }
+      artifacts:
+          vtn_network_cfg_json: /root/setup/vtn-network-cfg.json
 
     # docker image for vcpe containers
     docker-onos:
diff --git a/xos/xos_configuration/xos_frontend_config b/xos/xos_configuration/xos_frontend_config
deleted file mode 100755
index 13fe53b..0000000
--- a/xos/xos_configuration/xos_frontend_config
+++ /dev/null
@@ -1,4 +0,0 @@
-[gui]
-branding_name=CORD
-branding_css=/static/cord.css
-branding_icon=/static/onos-logo.png