Merge branch 'master' of github.com:jermowery/xos into AddVPNService
diff --git a/README.md b/README.md
index 75b07dd..7bed9c5 100644
--- a/README.md
+++ b/README.md
@@ -6,11 +6,14 @@
 Additional design notes, presentations, and other collateral are 
 also available at http://xosproject.org and http://cord.onosproject.org.
 
-One quick way to get started is to build and run the containers in
-`containers/` (see the README in that directory for more information).
-
-Another quick way to get started is to look at the collection of
+The best way to get started is to look at the collection of
 canned configurations in `xos/configurations/`. The `cord` 
 configuration in that directory corresponds to our current 
 CORD development environment, and the `README.md` you'll find there
 will help you get started.
+
+Source tree layout:
+ * applications -- stand-alone applications that run on top of XOS.
+ * containers -- common Dockerfiles used by various XOS configurations
+ * views -- mechanisms to extend XOS with customized views
+ * xos -- XOS internals
diff --git a/containers/README.md b/containers/README.md
index 0fcdb13..b4a8ea8 100644
--- a/containers/README.md
+++ b/containers/README.md
@@ -60,12 +60,16 @@
 `http://localhost:8000` and log in using the default `padmin@vicci.org` account
 with password `letmein`.
 
-#### Configuring XOS for OpenStack
+## Configuring XOS for OpenStack
+
+There are many possible configurations of XOS. The best way to get started
+is to find the configuration that best matches your needs and modify it as
+necessary. The available "canned" configurations can be found i `../xos/configurations/`.
 
 If you have your own OpenStack cluster, and you would like to configure XOS to
-control it, copy the `admin-openrc.sh` credentials file for your cluster to
-this directory.  Make sure that OpenStack commands work from the local machine
-using the credentials, e.g., `source ./admin-openrc.sh; nova list`.  Then run:
+control it, then take the following steps. Copy the `admin-openrc.sh` credentials 
+file for your cluster to this directory.  Make sure that OpenStack commands work 
+from the local machine using the credentials, e.g., `source ./admin-openrc.sh; nova list`.  Then run:
 
 ```
 $ make
diff --git a/views/ngXosLib/karma.conf.js b/views/ngXosLib/karma.conf.js
index 06939fb..b75f95b 100644
--- a/views/ngXosLib/karma.conf.js
+++ b/views/ngXosLib/karma.conf.js
@@ -65,7 +65,7 @@
 
     // level of logging
     // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
-    logLevel: config.LOG_INFO,
+    logLevel: config.LOG_DEBUG,
 
 
     // enable / disable watching file and executing tests whenever any file changes
diff --git a/views/ngXosLib/package.json b/views/ngXosLib/package.json
index 1f8713c..9e13e28 100644
--- a/views/ngXosLib/package.json
+++ b/views/ngXosLib/package.json
@@ -28,6 +28,14 @@
     "gulp-concat": "^2.6.0",
     "gulp-ng-annotate": "^1.1.0",
     "gulp-uglify": "^1.4.2",
+    "jasmine-core": "^2.4.1",
+    "karma": "^0.13.19",
+    "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",
+    "phantomjs": "^2.1.3",
     "wiredep": "^3.0.0-beta",
     "wrench": "^1.5.8"
   }
diff --git a/views/ngXosLib/xosHelpers/spec/csrftoken.test.js b/views/ngXosLib/xosHelpers/spec/csrftoken.test.js
index 6a74040..e49c52b 100644
--- a/views/ngXosLib/xosHelpers/spec/csrftoken.test.js
+++ b/views/ngXosLib/xosHelpers/spec/csrftoken.test.js
@@ -1,20 +1,22 @@
 'use strict';
 
-describe('The xos.helper module', () => {
+describe('The xos.helper module', function(){
   
   var app, httpProvider;
 
   beforeEach(module('xos.helpers'));
+
   beforeEach(function(){
-    module(function($httpProvider){
-      httpProvider = $httpProvider;
+    module(function(_$httpProvider_){
+      console.log('beforeEach');
+      httpProvider = _$httpProvider_;
     });
   });
 
-
-
-  it('should set SetCSRFToken interceptor', inject(($http) => {
-    expect(httpProvider.interceptors).toContain('SetCSRFToken');
+  it('should set SetCSRFToken interceptor', inject(function($http){
+    console.log('httpProvider',httpProvider);
+    expect(true).toBeTrue();
+    // expect(httpProvider.interceptors).toContain('SetCSRFToken');
   }));
 
 });
\ No newline at end of file
diff --git a/views/ngXosLib/xosHelpers/src/xosHelpers.module.js b/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
index 1f73be1..25600aa 100644
--- a/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
+++ b/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
@@ -1,12 +1,19 @@
 (function() {
     'use strict';
 
+    angular.module('bugSnag', []).factory('$exceptionHandler', function () {
+      return function (exception, cause) {
+        Bugsnag.notifyException(exception, {diagnostics:{cause: cause}});
+      };
+    });
+
     angular
         .module('xos.helpers',[
           'ngCookies',
           'xos.xos',
           'xos.hpcapi',
-          'xos.xoslib'
+          'xos.xoslib',
+          'bugSnag'
         ])
         .config(config);
 
diff --git a/views/ngXosViews/ceilometerDashboard/env/default.js b/views/ngXosViews/ceilometerDashboard/env/default.js
index 67006ec..f76b607 100644
--- a/views/ngXosViews/ceilometerDashboard/env/default.js
+++ b/views/ngXosViews/ceilometerDashboard/env/default.js
@@ -7,7 +7,7 @@
 // (works only for local environment as both application are served on the same domain)
 
 module.exports = {
-  host: 'http://clnode067.clemson.cloudlab.us:9999/',
-  xoscsrftoken: 'prHoBeRKIHqQE53sKYo3EfzHAgaVIQ1z',
-  xossessionid: 'mp5xe6345ef4fgs6n0t5rfd0su33c12x'
+  host: 'http://clnode078.clemson.cloudlab.us:9999/',
+  xoscsrftoken: 'Lbrkulk7c9fQOloSjhQEqLdDDFRNHsuL',
+  xossessionid: '7j0w1m7t4qcyu472voe32jz6ck9dnq14'
 };
diff --git a/views/ngXosViews/ceilometerDashboard/spec/ceilometer.test.js b/views/ngXosViews/ceilometerDashboard/spec/ceilometer.test.js
index bc8b2e2..cbd8ca0 100644
--- a/views/ngXosViews/ceilometerDashboard/spec/ceilometer.test.js
+++ b/views/ngXosViews/ceilometerDashboard/spec/ceilometer.test.js
@@ -47,11 +47,11 @@
       it('should load corresponding meters', () => {
         vm.loadSliceMeter(vm.services[0].slice[0]);
 
+        httpBackend.flush();
+
         expect(vm.selectedSlice).toEqual('slice-a-1');
         expect(vm.selectedTenant).toEqual('id-a-1');
 
-        httpBackend.flush();
-
         expect(Object.keys(vm.selectedResources).length).toBe(2);
         expect(vm.selectedResources['resource-1'].length).toBe(2);
         expect(vm.selectedResources['resource-2'].length).toBe(1);
diff --git a/views/ngXosViews/ceilometerDashboard/src/js/main.js b/views/ngXosViews/ceilometerDashboard/src/js/main.js
index aeb8243..0bf12db 100644
--- a/views/ngXosViews/ceilometerDashboard/src/js/main.js
+++ b/views/ngXosViews/ceilometerDashboard/src/js/main.js
@@ -31,7 +31,7 @@
     $rootScope.stateName = toState.name;
   })
 })
-.service('Ceilometer', function($http, $q, lodash){
+.service('Ceilometer', function($http, $q){
 
   this.getMappings = () => {
     let deferred = $q.defer();
@@ -161,15 +161,17 @@
 
         // visualization info
         this.loader = true;
-        this.selectedSlice = slice.slice;
-        this.selectedTenant = slice.project_id;
-
-        // store the status
-        Ceilometer.selectedSlice = slice;
-        Ceilometer.selectedService = service_name;
+        this.error = null;
+        this.ceilometerError = null;
 
         Ceilometer.getMeters({tenant: slice.project_id})
         .then((sliceMeters) => {
+          this.selectedSlice = slice.slice;
+          this.selectedTenant = slice.project_id;
+
+          // store the status
+          Ceilometer.selectedSlice = slice;
+          Ceilometer.selectedService = service_name;
           this.selectedResources = lodash.groupBy(sliceMeters, 'resource_name');
 
           // hacky
@@ -178,7 +180,13 @@
           }
         })
         .catch(err => {
-          this.error = (err.data && err.data.detail) ? err.data.detail : 'An Error occurred. Please try again later.';
+
+          // this means that ceilometer is not yet ready
+          if(err.status === 503){
+            return this.ceilometerError = err.data.detail.specific_error;
+          }
+
+          this.error = (err.data && err.data.detail.specific_error) ? err.data.detail.specific_error : 'An Error occurred. Please try again later.';
         })
         .finally(() => {
           this.loader = false;
diff --git a/views/ngXosViews/ceilometerDashboard/src/templates/ceilometer-dashboard.tpl.html b/views/ngXosViews/ceilometerDashboard/src/templates/ceilometer-dashboard.tpl.html
index 23afc1a..fe7720c 100644
--- a/views/ngXosViews/ceilometerDashboard/src/templates/ceilometer-dashboard.tpl.html
+++ b/views/ngXosViews/ceilometerDashboard/src/templates/ceilometer-dashboard.tpl.html
@@ -59,6 +59,9 @@
         </article>
         <!-- METERS -->
         <article ng-hide="vm.showStats" class="meters animate-slide-left">
+          <div class="alert alert-danger" ng-show="vm.ceilometerError">
+            {{vm.ceilometerError}}
+          </div>
           <div class="col-sm-4 animate-slide-left" ng-hide="!vm.selectedSlice">
             <div class="list-group">
               <div class="list-group-item">
diff --git a/xos/configurations/common/fixtures.yaml b/xos/configurations/common/fixtures.yaml
index c5e9dd1..6419211 100644
--- a/xos/configurations/common/fixtures.yaml
+++ b/xos/configurations/common/fixtures.yaml
@@ -21,3 +21,6 @@
 
     bridge:
       type: tosca.nodes.NetworkParameterType
+
+    neutron_port_name:
+      type: tosca.nodes.NetworkParameterType
diff --git a/xos/configurations/common/make-nodes-yaml.sh b/xos/configurations/common/make-nodes-yaml.sh
index 74b8d0b..65e16bb 100644
--- a/xos/configurations/common/make-nodes-yaml.sh
+++ b/xos/configurations/common/make-nodes-yaml.sh
@@ -18,7 +18,7 @@
         type: tosca.nodes.Site
 EOF
 
-NODES=$( bash -c "source $SETUPDIR/admin-openrc.sh ; nova hypervisor-list" |grep -v ID|grep -v +|awk '{print $4}' )
+NODES=$( bash -c "source $SETUPDIR/admin-openrc.sh ; nova host-list" |grep compute|awk '{print $2}' )
 I=0
 for NODE in $NODES; do
     echo $NODE
diff --git a/xos/configurations/cord-pod/Makefile b/xos/configurations/cord-pod/Makefile
index 35f3ff3..606f106 100644
--- a/xos/configurations/cord-pod/Makefile
+++ b/xos/configurations/cord-pod/Makefile
@@ -1,11 +1,18 @@
 .PHONY: xos
-xos: nodes.yaml images.yaml vtn_network_cfg_json
+xos: nodes.yaml images.yaml vtn_network_cfg_json virtualbng_json
 	sudo docker-compose up -d
 	../common/wait_for_xos_port.sh 80
 	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/setup.yaml
 	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/nodes.yaml
 	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/images.yaml
-	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/tosca/samples/vtn.yaml
+
+vtn:
+	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/vtn-external.yaml
+
+cord:
+	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/mgmt-net.yaml
+	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/fixtures.yaml
+	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/cord-vtn-vsg.yaml
 
 nodes.yaml:
 	export SETUPDIR=.; bash ../common/make-nodes-yaml.sh
@@ -13,6 +20,9 @@
 images.yaml:
 	export SETUPDIR=.; bash ../common/make-images-yaml.sh
 
+virtualbng_json:
+	export SETUPDIR=.; bash ./make-virtualbng-json.sh
+
 vtn_network_cfg_json:
 	export SETUPDIR=.; bash ./make-vtn-networkconfig-json.sh
 
diff --git a/xos/configurations/cord-pod/NOTES.txt b/xos/configurations/cord-pod/NOTES.txt
index 1dd6b5a..d832f2b 100644
--- a/xos/configurations/cord-pod/NOTES.txt
+++ b/xos/configurations/cord-pod/NOTES.txt
@@ -3,4 +3,35 @@
 Requirements:
 * admin-openrc.sh: Admin credentials for your OpenStack cloud
 * id_rsa[.pub]: Keypair for use by the various services
-* node_key: Private key that allows root login to the compute nodes 
+* node_key: Private key that allows root login to the compute nodes
+
+Steps for bringing up the POD:
+
+OpenStack
+* Configure management net
+  - mgmtbr on head nodes
+  - dnsmasq on head1 using cord config file
+* Install OpenStack using the openstack-cluster-install repo
+
+VTN
+* onos-cord VM is created by openstack-cluster-install
+* Bring up ONOS
+  # cd cord; docker-compose up -d
+* On each compute node it's necessary perform a few manual steps (FIX ME)
+  - Disable neutron-plugin-openvswitch-agent. As root:
+    # service neutron-plugin-openvswitch-agent stop
+    # echo manual > /etc/init/neutron-plugin-openvswitch-agent.override
+  - Clean up OVS: delete br-int and any other bridges
+  - Listen for connections from VTN:
+    # ovs-appctl -t ovsdb-server ovsdb-server/add-remote ptcp:6641
+
+XOS
+* xos VM is created by openstack-cluster-install
+  - requirements listed above should already be satisfied by install
+* cd xos/xos/configurations/cord-pod
+* Bring up XOS cord-pod configuration
+  # make
+  # make vtn
+  # make cord
+* Login to XOS at http://xos
+  - padmin@vicci.org / letmein
diff --git a/xos/tosca/samples/cord.yaml b/xos/configurations/cord-pod/cord-vtn-vsg.yaml
similarity index 65%
copy from xos/tosca/samples/cord.yaml
copy to xos/configurations/cord-pod/cord-vtn-vsg.yaml
index a9baf25..d9fda9b 100644
--- a/xos/tosca/samples/cord.yaml
+++ b/xos/configurations/cord-pod/cord-vtn-vsg.yaml
@@ -1,6 +1,6 @@
 tosca_definitions_version: tosca_simple_yaml_1_0
 
-description: Setup CORD-related services -- vOLT, vCPE, vBNG.
+description: Just enough Tosca to get the vSG slice running on the CORD POD
 
 imports:
    - custom_types/xos.yaml
@@ -11,64 +11,88 @@
     service_volt:
       type: tosca.nodes.Service
       requirements:
-          - vcpe_tenant:
-              node: service_vcpe
+          - vsg_tenant:
+              node: service_vsg
               relationship: tosca.relationships.TenantOfService
       properties:
           view_url: /admin/cord/voltservice/$id$/
           kind: vOLT
 
-    Private:
-      type: tosca.nodes.NetworkTemplate
+    public_addresses:
+      type: tosca.nodes.AddressPool
+      properties:
+          addresses: 207.141.192.128/27
 
-    # networks required by vCPE
-    lan_network:

-      type: tosca.nodes.network.Network

-      properties:

-          ip_version: 4

-      requirements:

-          - network_template:

-              node: Private

-              relationship: tosca.relationships.UsesNetworkTemplate

-          - owner:

-              node: mysite_vcpe

-              relationship: tosca.relationships.MemberOfSlice

-          - connection:

-              node: mysite_vcpe

-              relationship: tosca.relationships.ConnectsToSlice
-
-    service_vcpe:
-      type: tosca.nodes.VCPEService
+    service_vsg:
+      type: tosca.nodes.VSGService
       requirements:
           - vbng_tenant:
               node: service_vbng
               relationship: tosca.relationships.TenantOfService
       properties:
-          view_url: /admin/cord/vcpeservice/$id$/
+          view_url: /admin/cord/vsgservice/$id$/
           backend_network_label: hpc_client
           public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+          private_key_fn: /opt/xos/synchronizers/vcpe/vcpe_private_key
+          wan_container_gateway_ip: 207.141.192.158
+          wan_container_gateway_mac: a4:23:05:34:56:78
+          wan_container_netbits: 27
       artifacts:
-          pubkey: /opt/xos/observers/vcpe/vcpe_public_key
+          pubkey: /opt/xos/synchronizers/vcpe/vcpe_public_key
 
     service_vbng:
       type: tosca.nodes.VBNGService
       properties:
           view_url: /admin/cord/vbngservice/$id$/
-          vbng_url: http://10.0.3.136:8181/onos/virtualbng/
+# if unspecified, vbng observer will look for an ONOSApp Tenant and
+# generate a URL from its IP address
+#          vbng_url: http://10.11.10.24:8181/onos/virtualbng/
+
+    Private:
+      type: tosca.nodes.NetworkTemplate
+
+    management:
+      type: tosca.nodes.network.Network.XOS
+      properties:
+          no-create: true
+          no-delete: true
+          no-update: true
 
     mysite:
       type: tosca.nodes.Site
 
-    mysite_vcpe:
-      description: vCPE Controller Slice
-      type: tosca.nodes.Slice
+    # Networks required by the CORD setup
+    mysite_vsg-access:
+      type: tosca.nodes.network.Network
+      properties:
+          ip_version: 4
       requirements:
-          - vcpe_service:
-              node: service_vcpe
+          - network_template:
+              node: Private
+              relationship: tosca.relationships.UsesNetworkTemplate
+          - owner:
+              node: mysite_vsg
+              relationship: tosca.relationships.MemberOfSlice
+          - connection:
+              node: mysite_vsg
+              relationship: tosca.relationships.ConnectsToSlice
+
+    # CORD Slices
+    mysite_vsg:
+      description: vSG Controller Slice
+      type: tosca.nodes.Slice
+      properties:
+          network: noauto
+      requirements:
+          - vsg_service:
+              node: service_vsg
               relationship: tosca.relationships.MemberOfService
           - site:
               node: mysite
               relationship: tosca.relationships.MemberOfSite
+          - management:
+              node: management
+              relationship: tosca.relationships.ConnectsToNetwork
 
     # Let's add a user who can be administrator of the household
     johndoe@myhouse.com:
@@ -82,14 +106,14 @@
               node: mysite
               relationship: tosca.relationships.MemberOfSite
 
-    # Now let's add a subscriber
+    # A subscriber
     My House:
        type: tosca.nodes.CORDSubscriber
        properties:
-           service_specific_id: 1234
-           firewall_enable: true
-           cdn_enable: true
-           url_filter_enable: true
+           service_specific_id: 123
+           firewall_enable: false
+           cdn_enable: false
+           url_filter_enable: false
            url_filter_level: R
        requirements:
           - house_admin:
@@ -99,7 +123,7 @@
     Mom's PC:
        type: tosca.nodes.CORDUser
        properties:
-           mac: 010203040506
+           mac: 01:02:03:04:05:06
            level: PG_13
        requirements:
            - household:
@@ -109,7 +133,7 @@
     Dad's PC:
        type: tosca.nodes.CORDUser
        properties:
-           mac: 90E2Ba82F975
+           mac: 90:E2:BA:82:F9:75
            level: PG_13
        requirements:
            - household:
@@ -119,7 +143,7 @@
     Jack's Laptop:
        type: tosca.nodes.CORDUser
        properties:
-           mac: 685B359D91D5
+           mac: 68:5B:35:9D:91:D5
            level: PG_13
        requirements:
            - household:
@@ -129,7 +153,7 @@
     Jill's Laptop:
        type: tosca.nodes.CORDUser
        properties:
-           mac: 34363BC9B6A6
+           mac: 34:36:3B:C9:B6:A6
            level: PG_13
        requirements:
            - household:
@@ -139,9 +163,9 @@
     My Volt:
         type: tosca.nodes.VOLTTenant
         properties:
-            service_specific_id: 1234
+            service_specific_id: 123
             s_tag: 222
-            c_tag: 432
+            c_tag: 111
         requirements:
             - provider_service:
                 node: service_volt
@@ -149,8 +173,3 @@
             - subscriber:
                 node: My House
                 relationship: tosca.relationships.BelongsToSubscriber
-
-
-
-
-
diff --git a/xos/configurations/cord-pod/docker-compose.yml b/xos/configurations/cord-pod/docker-compose.yml
index 0116a1b..6f442af 100644
--- a/xos/configurations/cord-pod/docker-compose.yml
+++ b/xos/configurations/cord-pod/docker-compose.yml
@@ -12,8 +12,11 @@
     links:
         - xos_db
     volumes:
+        - ../common/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
+        - ../cord//xos_cord_config:/opt/xos/xos_configuration/xos_cord_config:ro
         - .:/root/setup:ro
         - ../vtn/files/xos_vtn_config:/opt/xos/xos_configuration/xos_vtn_config:ro
+        - ./images:/opt/xos/images:ro
 
 xos_synchronizer_onos:
     image: xosproject/xos-synchronizer-openstack
@@ -27,12 +30,48 @@
         - .:/root/setup:ro
         - ./id_rsa:/opt/xos/synchronizers/onos/onos_key:ro  # private key
 
-# FUTURE
-#xos_swarm_synchronizer:
-#    image: xosproject/xos-swarm-synchronizer
+xos_synchronizer_vcpe:
+    image: xosproject/xos-synchronizer-openstack
+    command: bash -c "sleep 120; cp /root/setup/node_key /opt/xos/synchronizers/vcpe/; chmod 0600 /opt/xos/synchronizers/vcpe/node_key; python /opt/xos/synchronizers/vcpe/vcpe-synchronizer.py -C /root/setup/files/vcpe_synchronizer_config"
+    labels:
+        org.xosproject.kind: synchronizer
+        org.xosproject.target: vcpe
+    links:
+        - xos_db
+    volumes:
+        - .:/root/setup:ro
+        - ./id_rsa:/opt/xos/synchronizers/vcpe/vcpe_private_key:ro  # private key
+
+xos_synchronizer_vtn:
+    image: xosproject/xos-synchronizer-openstack
+    command: bash -c "sleep 120; python /opt/xos/synchronizers/vtn/vtn-synchronizer.py -C /opt/xos/synchronizers/vtn/vtn_synchronizer_config"
+    labels:
+        org.xosproject.kind: synchronizer
+        org.xosproject.target: vtn
+    links:
+        - xos_db
+    volumes:
+        - .:/root/setup:ro
+
+#xos_synchronizer_vbng:
+#    image: xosproject/xos-synchronizer-openstack
+#    command: bash -c "sleep 120; python /opt/xos/synchronizers/vbng/vbng-synchronizer.py -C /opt/xos/synchronizers/vbng/vbng_synchronizer_config"
 #    labels:
 #        org.xosproject.kind: synchronizer
-#        org.xosproject.target: swarm
+#        org.xosproject.target: vbng
+#    links:
+#        - xos_db
+
+#xos_synchronizer_monitoring_channel:
+#    image: xosproject/xos-synchronizer-openstack
+#    command: bash -c "sleep 120; python /opt/xos/synchronizers/monitoring_channel/monitoring_channel_synchronizer.py -C /opt/xos/synchronizers/monitoring_channel/monitoring_channel_synchronizer_config"
+#    labels:
+#        org.xosproject.kind: synchronizer
+#        org.xosproject.target: monitoring_channel
+#    links:
+#        - xos_db
+#    volumes:
+#        - ./id_rsa:/opt/xos/synchronizers/monitoring_channel/monitoring_channel_private_key:ro  # private key
 
 xos:
     command: python /opt/xos/manage.py runserver 0.0.0.0:80 --insecure --makemigrations
@@ -47,3 +86,4 @@
         - ../cord/xos_cord_config:/opt/xos/xos_configuration/xos_cord_config:ro
         - ../vtn/files/xos_vtn_config:/opt/xos/xos_configuration/xos_vtn_config:ro
         - ./id_rsa.pub:/opt/xos/synchronizers/onos/onos_key.pub:ro
+        - ./id_rsa.pub:/opt/xos/synchronizers/vcpe/vcpe_public_key:ro
diff --git a/xos/configurations/cord-pod/files/vcpe_synchronizer_config b/xos/configurations/cord-pod/files/vcpe_synchronizer_config
new file mode 100644
index 0000000..46ee0c3
--- /dev/null
+++ b/xos/configurations/cord-pod/files/vcpe_synchronizer_config
@@ -0,0 +1,47 @@
+
+[plc]
+name=plc
+deployment=VICCI
+
+[db]
+name=xos
+user=postgres
+password=password
+host=localhost
+port=5432
+
+[api]
+host=128.112.171.237
+port=8000
+ssl_key=None
+ssl_cert=None
+ca_ssl_cert=None
+ratelimit_enabled=0
+omf_enabled=0
+mail_support_address=support@localhost
+nova_enabled=True
+
+[observer]
+name=vcpe
+dependency_graph=/opt/xos/synchronizers/vcpe/model-deps
+steps_dir=/opt/xos/synchronizers/vcpe/steps
+sys_dir=/opt/xos/synchronizers/vcpe/sys
+deleters_dir=/opt/xos/synchronizers/vcpe/deleters
+log_file=console
+#/var/log/hpc.log
+driver=None
+pretend=False
+backoff_disabled=True
+save_ansible_output=True
+# set proxy_ssh to false on cloudlab
+full_setup=True
+proxy_ssh=True
+proxy_ssh_key=/root/setup/node_key
+proxy_ssh_user=root
+
+[networking]
+use_vtn=True
+
+[feefie]
+client_id='vicci_dev_central'
+user_id='pl'
diff --git a/xos/configurations/cord-pod/images/README.md b/xos/configurations/cord-pod/images/README.md
new file mode 100644
index 0000000..aca55a9
--- /dev/null
+++ b/xos/configurations/cord-pod/images/README.md
@@ -0,0 +1,5 @@
+# VM images for XOS
+
+Any Cloud image files placed in this directory (with suffix .img) will be automatically
+imported by XOS and added to Glance (OpenStack's image repository).  For instance, the image
+`trusty-server-multi-nic.img` will be imported with name `trusty-server-multi-nic`.
diff --git a/xos/configurations/cord-pod/make-virtualbng-json.sh b/xos/configurations/cord-pod/make-virtualbng-json.sh
new file mode 100644
index 0000000..993643c
--- /dev/null
+++ b/xos/configurations/cord-pod/make-virtualbng-json.sh
@@ -0,0 +1,38 @@
+FN=$SETUPDIR/virtualbng.json
+
+rm -f $FN
+
+cat >> $FN <<EOF
+{
+    "localPublicIpPrefixes" : [
+        "10.254.0.128/25"
+    ],
+    "nextHopIpAddress" : "10.254.0.1",
+    "publicFacingMac" : "00:00:00:00:00:66",
+    "xosIpAddress" : "10.11.10.1",
+    "xosRestPort" : "9999",
+    "hosts" : {
+EOF
+
+NODES=$( sudo bash -c "source $SETUPDIR/admin-openrc.sh ; nova hypervisor-list" |grep -v ID|grep -v +|awk '{print $4}' )
+
+NODECOUNT=0
+for NODE in $NODES; do
+    ((NODECOUNT++))
+done
+
+I=0
+for NODE in $NODES; do
+    echo $NODE
+    ((I++))
+    if [[ "$I" -lt "$NODECOUNT" ]]; then
+        echo "      \"$NODE\" : \"of:0000000000000001/1\"," >> $FN
+    else
+        echo "      \"$NODE\" : \"of:0000000000000001/1\"" >> $FN
+    fi
+done
+
+cat >> $FN <<EOF
+    }
+}
+EOF
diff --git a/xos/configurations/cord-pod/make-vtn-networkconfig-json.sh b/xos/configurations/cord-pod/make-vtn-networkconfig-json.sh
index 8302ab0..5239267 100755
--- a/xos/configurations/cord-pod/make-vtn-networkconfig-json.sh
+++ b/xos/configurations/cord-pod/make-vtn-networkconfig-json.sh
@@ -9,15 +9,27 @@
     "apps" : {
         "org.onosproject.cordvtn" : {
             "cordvtn" : {
-                "gatewayMac" : "00:00:00:00:00:01",
+                "privateGatewayMac" : "00:00:00:00:00:01",
+                "localManagementIp": "172.27.0.1/24",
+                "ovsdbPort": "6641",
+                "sshPort": "22",
+                "sshUser": "root",
+                "sshKeyFile": "/root/node_key",
+                "publicGateways": [
+                    {
+                        "gatewayIp": "207.141.192.158",
+                        "gatewayMac": "a4:23:05:34:56:78"
+                    }
+                ],
                 "nodes" : [
 EOF
 
 NODES=$( sudo bash -c "source $SETUPDIR/admin-openrc.sh ; nova hypervisor-list" |grep -v ID|grep -v +|awk '{print $4}' )
 
+# XXX disabled - we don't need or want the nm node at this time
 # also configure ONOS to manage the nm node
-NM="neutron-gateway"
-NODES="$NODES $NM"
+#NM="neutron-gateway"
+#NODES="$NODES $NM"
 
 NODECOUNT=0
 for NODE in $NODES; do
@@ -29,18 +41,18 @@
     echo $NODE
     NODEIP=`getent hosts $NODE | awk '{ print $1 }'`
 
-    PHYPORT=eth0
-    LOCALIP=$NODEIP
+    PHYPORT=mlx0
+    # How to set LOCALIP?
+    LOCALIPNET="192.168.199"
 
     ((I++))
     cat >> $FN <<EOF
                     {
                       "hostname": "$NODE",
-                      "ovsdbIp": "$NODEIP",
-                      "ovsdbPort": "6641",
+                      "hostManagementIp": "$NODEIP/24",
                       "bridgeId": "of:000000000000000$I",
-                      "phyPortName": "$PHYPORT",
-                      "localIp": "$LOCALIP"
+                      "dataPlaneIntf": "$PHYPORT",
+                      "dataPlaneIp": "$LOCALIPNET.$I/24"
 EOF
     if [[ "$I" -lt "$NODECOUNT" ]]; then
         echo "                    }," >> $FN
@@ -61,7 +73,7 @@
             "openstackswitching" : {
                  "do_not_push_flows" : "true",
                  "neutron_server" : "$NEUTRON_URL/v2.0/",
-                 "keystone_server" : "$OS_AUTH_URL",
+                 "keystone_server" : "$OS_AUTH_URL/",
                  "user_name" : "$OS_USERNAME",
                  "password" : "$OS_PASSWORD"
              }
diff --git a/xos/configurations/cord/vtn.yaml b/xos/configurations/cord-pod/mgmt-net.yaml
similarity index 91%
rename from xos/configurations/cord/vtn.yaml
rename to xos/configurations/cord-pod/mgmt-net.yaml
index 68c0fdb..2bd0173 100644
--- a/xos/configurations/cord/vtn.yaml
+++ b/xos/configurations/cord-pod/mgmt-net.yaml
@@ -1,12 +1,12 @@
 tosca_definitions_version: tosca_simple_yaml_1_0
 
-description: Some VTN related stuff
-
+description: Set up management network for CORD POD
 imports:
    - custom_types/xos.yaml
 
 topology_template:
   node_templates:
+
     management_template:
       type: tosca.nodes.NetworkTemplate
       properties:
@@ -17,6 +17,7 @@
       type: tosca.nodes.network.Network
       properties:
           ip_version: 4
+          cidr: 172.27.0.0/24
       requirements:
           - network_template:
               node: management_template
@@ -37,4 +38,3 @@
           - site:
               node: mysite
               relationship: tosca.relationships.MemberOfSite
-
diff --git a/xos/configurations/cord-pod/vtn-external.yaml b/xos/configurations/cord-pod/vtn-external.yaml
new file mode 100644
index 0000000..9c1a550
--- /dev/null
+++ b/xos/configurations/cord-pod/vtn-external.yaml
@@ -0,0 +1,30 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Set up ONOS VTN app
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+
+    service_ONOS_VTN:
+      type: tosca.nodes.ONOSService
+      requirements:
+      properties:
+          kind: onos
+          view_url: /admin/onos/onosservice/$id$/
+          no_container: true
+          rest_hostname: onos-cord
+
+    VTN_ONOS_app:
+      type: tosca.nodes.ONOSVTNApp
+      requirements:
+          - onos_tenant:
+              node: service_ONOS_VTN
+              relationship: tosca.relationships.TenantOfService
+      properties:
+          dependencies: org.onosproject.drivers, org.onosproject.drivers.ovsdb, org.onosproject.lldpprovider, org.onosproject.openflow-base, org.onosproject.ovsdb-base, org.onosproject.dhcp, 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
diff --git a/xos/configurations/cord-pod/vtn-setup.yml b/xos/configurations/cord-pod/vtn-setup.yml
deleted file mode 100644
index e8bb962..0000000
--- a/xos/configurations/cord-pod/vtn-setup.yml
+++ /dev/null
@@ -1,60 +0,0 @@
----
-- hosts: neutron-api
-  sudo: yes
-  vars:
-    vtn_host: node2.juju2.xos-pg0.clemson.cloudlab.us
-  tasks:
-
-  # Most of this should happen in the neutron-api charm
-  # Make a local copy and deploy from there for starters
-  # * Use latest copy of neutron-api charm
-  # * Add an "onos-vtn" core plugin
-  # * Do the rest of tehse steps when the "onos-vtn" plugin is selected
-  # * Can we add a "vtn-host" argument to the charm?
-  - apt: name={{ item }} state=installed
-    with_items:
-    - python-pip
-
-  - pip: name={{ item }} state=latest
-    with_items:
-    - setuptools
-    - pip
-    - testrepository
-  - git: repo=https://github.com/openstack/networking-onos.git
-      dest=/srv/networking-onos
-  - shell: cd /srv/networking-onos; python setup.py install
-
-  # Edit /usr/local/etc/neutron/plugins/ml2/conf_onos.ini
-  - ini_file: dest=/usr/local/etc/neutron/plugins/ml2/conf_onos.ini
-      section=onos option=url_path value=http://{{ vtn_host }}:8181/onos/openstackswitching
-  - ini_file: dest=/usr/local/etc/neutron/plugins/ml2/conf_onos.ini
-      section=onos option=username value=karaf
-  - ini_file: dest=/usr/local/etc/neutron/plugins/ml2/conf_onos.ini
-      section=onos option=password value=karaf
-
-  # Edit /etc/neutron/neutron.conf
-#  - ini_file: dest=/etc/neutron/neutron.conf
-#      section=DEFAULT option=core_plugin value=neutron.plugins.ml2.plugin.Ml2Plugin
-
-  # Edit /etc/neutron/plugins/ml2/ml2_conf.ini
-  # DOING IT THIS WAY WILL CONFLICT WITH JUJU!
-  - ini_file: dest=/etc/neutron/plugins/ml2/ml2_conf.ini
-      section=ml2 option=tenant_network_types value=vxlan
-  - ini_file: dest=/etc/neutron/plugins/ml2/ml2_conf.ini
-      section=ml2 option=type_drivers value=vxlan
-  - ini_file: dest=/etc/neutron/plugins/ml2/ml2_conf.ini
-      section=ml2 option=mechanism_drivers value=onos_ml2
-
-  # Already present
-  #- ini_file: dest=/etc/neutron/plugins/ml2/ml2_conf.ini
-  #    section=ml2_type_vxlan option=vni_ranges value=1001:2000
-
-  - service: name=neutron-server state=stopped enabled=no
-  # Run neutron-server with extra config file
-  # DOING IT THIS WAY WILL CONFLICT WITH JUJU!
-  - copy: src=files/neutron-supervisor.conf dest=/etc/supervisor/conf.d/
-  - shell: supervisorctl reload
-
-#  - shell: ../../scripts/destroy-all-networks.sh
-  - shell: cd ../cord/dataplane; bash ./generate-bm.sh > hosts-bm
-  - shell: cd ../cord/dataplane; ansible-playbook -i hosts-bm dataplane-vtn.yaml
diff --git a/xos/configurations/cord/README-VTN.md b/xos/configurations/cord/README-VTN.md
index 2d9b7aa..3d61940 100644
--- a/xos/configurations/cord/README-VTN.md
+++ b/xos/configurations/cord/README-VTN.md
@@ -30,7 +30,7 @@
     # not still an issue lurking...
     cat > /usr/local/etc/neutron/plugins/ml2/conf_onos.ini <<EOF
     [onos]
-    url_path = http://$ONOS_VTN_HOSTNAME:8181/onos/openstackswitching
+    url_path = http://$ONOS_VTN_HOSTNAME:8181/onos/cordvtn
     username = karaf
     password = karaf
     EOF
@@ -113,3 +113,29 @@
 11. You should see the pings arrive and responses sent out. Note that the ping responses will not reach Slice-1, since VTN traffic is unidirectional.
 12. Delete the Tenancy relation you created in Step #7. The ping traffic should no longer appear in the tcpdump.
 
+Getting external connectivity working on cloudlab
+
+Inside of vSG:
+
+    ip link add link eth0 eth0.500 type vlan id 500
+    ifconfig eth0.500 up
+    route del default gw 172.27.0.1
+    /sbin/ifconfig eth0.500 10.123.0.3
+    route del -net 10.0.0.0 netmask 255.0.0.0 dev eth0.500 # only need to do this if this route exists
+    route add -net 10.123.0.0 netmask 255.255.255.0 dev eth0.500
+    route add default gw 10.123.0.1
+    arp -s 10.123.0.1 00:8c:fa:5b:09:d8
+    
+On head node:
+
+    ifconfig eth2 10.123.0.1
+    iptables --table nat --append POSTROUTING --out-interface br-ex -j MASQUERADE
+    arp -s 10.123.0.3 fa:16:3e:ea:11:0a
+    
+Substitute for your installation:
+
+    10.123.0.3 = wan_ip of vSG
+    10.123.0.1 = wan gateway
+    fa:16:3e:ea:11:0a = wan_mac of vSG
+    00:8c:fa:5b:09:d8 = wan_mac of gateway
+    
diff --git a/xos/configurations/cord/cord.yaml b/xos/configurations/cord/cord.yaml
index f0b45de..07d4b68 100644
--- a/xos/configurations/cord/cord.yaml
+++ b/xos/configurations/cord/cord.yaml
@@ -11,8 +11,8 @@
     service_volt:
       type: tosca.nodes.Service
       requirements:
-          - vcpe_tenant:
-              node: service_vcpe
+          - vsg_tenant:
+              node: service_vsg
               relationship: tosca.relationships.TenantOfService
           - lan_network:
               node: lan_network
@@ -24,14 +24,20 @@
           view_url: /admin/cord/voltservice/$id$/
           kind: vOLT
 
-    service_vcpe:
-      type: tosca.nodes.VCPEService
+    # set a pool of addresses that we can hand out for the VSG Wan.
+    public_addresses:
+      type: tosca.nodes.AddressPool
+      properties:
+          addresses: 10.123.0.0/24 10.124.0.0/24
+
+    service_vsg:
+      type: tosca.nodes.VSGService
       requirements:
           - vbng_tenant:
               node: service_vbng
               relationship: tosca.relationships.TenantOfService
       properties:
-          view_url: /admin/cord/vcpeservice/$id$/
+          view_url: /admin/cord/vsgservice/$id$/
           backend_network_label: hpc_client
           public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
           private_key_fn: /opt/xos/synchronizers/vcpe/vcpe_private_key
@@ -145,6 +151,7 @@
                    "rabbit.user": "<rabbit_user>",
                    "rabbit.password": "<rabbit_password>",
                    "rabbit.host": "<rabbit_host>",
+                   "publish.kafka": "false",
                    "publish.rabbit": "true",
                    "volt.events.rabbit.topic": "notifications.info",
                    "volt.events.rabbit.exchange": "voltlistener",
@@ -190,13 +197,13 @@
               node: Private
               relationship: tosca.relationships.UsesNetworkTemplate
           - owner:
-              node: mysite_vcpe
+              node: mysite_vsg
               relationship: tosca.relationships.MemberOfSlice
           - connection:
-              node: mysite_vcpe
+              node: mysite_vsg
               relationship: tosca.relationships.ConnectsToSlice
           - connection:
-              node: mysite_volt
+              node: mysite_vsg
               relationship: tosca.relationships.ConnectsToSlice
 
     wan_network:
@@ -208,13 +215,13 @@
               node: Private
               relationship: tosca.relationships.UsesNetworkTemplate
           - owner:
-              node: mysite_vcpe
+              node: mysite_vsg
               relationship: tosca.relationships.MemberOfSlice
           - connection:
-              node: mysite_vcpe
+              node: mysite_vsg
               relationship: tosca.relationships.ConnectsToSlice
           - connection:
-              node: mysite_vbng
+              node: mysite_vsg
               relationship: tosca.relationships.ConnectsToSlice
 
     Private-Direct:
@@ -265,18 +272,18 @@
 
 
     # CORD Slices
-    mysite_vcpe:
-      description: vCPE Controller Slice
+    mysite_vsg:
+      description: vSG Controller Slice
       type: tosca.nodes.Slice
       requirements:
-          - vcpe_service:
-              node: service_vcpe
+          - vsg_service:
+              node: service_vsg
               relationship: tosca.relationships.MemberOfService
           - site:
               node: mysite
               relationship: tosca.relationships.MemberOfSite
-          - vcpe_docker_image:
-              node: docker-vcpe
+          - vsg_docker_image:
+              node: docker-vsg
               relationship: tosca.relationships.UsesImage
 #      properties:
 #          default_isolation: container
@@ -442,8 +449,8 @@
                 node: mysite_clients
                 relationship: tosca.relationships.MemberOfSlice
 
-    # docker image for vcpe containers
-    docker-vcpe:
+    # docker image for vsg containers
+    docker-vsg:
       # TODO: need to attach this to mydeployment
       type: tosca.nodes.Image
       properties:
diff --git a/xos/configurations/cord/dataplane/cleanup.sh b/xos/configurations/cord/dataplane/cleanup.sh
index 9860de7..120454d 100755
--- a/xos/configurations/cord/dataplane/cleanup.sh
+++ b/xos/configurations/cord/dataplane/cleanup.sh
@@ -26,8 +26,11 @@
 echo "Waiting 5 seconds..."
 sleep 5
 
+cleanup_network lan_network
 cleanup_network wan_network
 cleanup_network mysite_vcpe-private
+cleanup_network mysite_vsg-access
+cleanup_network management
 
 echo "Deleting networks"
 # Delete all networks beginning with mysite_
@@ -42,3 +45,5 @@
 neutron net-delete public_network || true
 neutron net-delete hpc_client_network || true
 neutron net-delete ceilometer_network || true
+neutron net-delete management || true
+neutron net-delete mysite_vsg-access || true
diff --git a/xos/configurations/cord/make-vtn-networkconfig-json.sh b/xos/configurations/cord/make-vtn-networkconfig-json.sh
old mode 100755
new mode 100644
index c60a939..2cccd65
--- a/xos/configurations/cord/make-vtn-networkconfig-json.sh
+++ b/xos/configurations/cord/make-vtn-networkconfig-json.sh
@@ -9,15 +9,27 @@
     "apps" : {
         "org.onosproject.cordvtn" : {
             "cordvtn" : {
-                "gatewayMac" : "00:00:00:00:00:01",
+                "privateGatewayMac" : "00:00:00:00:00:01",
+                "localManagementIp": "172.27.0.1/24",
+                "ovsdbPort": "6641",
+                "sshPort": "22",
+                "sshUser": "root",
+                "sshKeyFile": "/root/node_key",
+                "publicGateways": [
+                    {
+                        "gatewayIp": "10.123.0.1",
+                        "gatewayMac": "00:8c:fa:5b:09:d8"
+                    }
+                ],
                 "nodes" : [
 EOF
 
 NODES=$( sudo bash -c "source $SETUPDIR/admin-openrc.sh ; nova hypervisor-list" |grep -v ID|grep -v +|awk '{print $4}' )
 
+# XXX disabled - we don't need or want the nm node at this time
 # also configure ONOS to manage the nm node
-NM=`grep "^nm" /root/setup/fqdn.map | awk '{ print $2 }'`
-NODES="$NODES $NM"
+# NM=`grep "^nm" /root/setup/fqdn.map | awk '{ print $2 }'`
+# NODES="$NODES $NM"
 
 NODECOUNT=0
 for NODE in $NODES; do
@@ -39,11 +51,10 @@
     cat >> $FN <<EOF
                     {
                       "hostname": "$NODE",
-                      "ovsdbIp": "$NODEIP",
-                      "ovsdbPort": "6641",
+                      "hostManagementIp": "$NODEIP/24",
                       "bridgeId": "of:000000000000000$I",
-                      "phyPortName": "$PHYPORT",
-                      "localIp": "$LOCALIP"
+                      "dataPlaneIntf": "$PHYPORT",
+                      "dataPlaneIp": "$LOCALIP/24"
 EOF
     if [[ "$I" -lt "$NODECOUNT" ]]; then
         echo "                    }," >> $FN
diff --git a/xos/configurations/frontend/Makefile b/xos/configurations/frontend/Makefile
index 130153a..5c0ce0e 100644
--- a/xos/configurations/frontend/Makefile
+++ b/xos/configurations/frontend/Makefile
@@ -23,3 +23,9 @@
 
 enter-xos:
 	sudo docker exec -ti frontend_xos_1 bash
+
+mock-cord:
+	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/fixtures.yaml
+	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/frontend/mocks/cord.yaml
+	sudo docker exec frontend_xos_1 cp /opt/xos/configurations/cord/xos_cord_config /opt/xos/xos_configuration/
+	sudo docker exec frontend_xos_1 touch /opt/xos/xos/settings.py
diff --git a/xos/configurations/frontend/README.md b/xos/configurations/frontend/README.md
index a0ee72e..c8f4097 100644
--- a/xos/configurations/frontend/README.md
+++ b/xos/configurations/frontend/README.md
@@ -37,17 +37,11 @@
 
 You can find a Swagger documentation for endpoint at: `http://0.0.0.0:9000/docs/`
 
-## Test
+## Populate the Data Model with custom data
 
-To run the FE tests, navigate to: `xos/core/xoslib`, and run 'npm test'.
+Sometimes while developing the GUI is usefull to have control over the DataModel. Sample `tosca` recipes for different configuration are defined in the `mocks` folder, and corresponding `make` commands are provided.
 
-This will install the required `npm` dependencies and run the test.
-
-Tests are runned in a headless browser (_PhantomJs_) by _Karma_ and the assertions are made with _Jasmine_. This is a pretty common standard for FE testing so you should feel at home.
-
-You can find the tests in the `spec/` folder, each source file has a corresponding `.test` file in it.
-
-After test have run you can find a Coverage report in `xos/core/xoslib/coverage` folder
+- Bring up the **CORD** data model: `make mock-cord`
 
 ## JS Styleguide
 
diff --git a/xos/configurations/frontend/docker-compose.yml b/xos/configurations/frontend/docker-compose.yml
index 6b71d2e..c7c9c19 100644
--- a/xos/configurations/frontend/docker-compose.yml
+++ b/xos/configurations/frontend/docker-compose.yml
@@ -23,3 +23,4 @@
       - ../../core/xoslib:/opt/xos/core/xoslib
       - ../../core/static:/opt/xos/core/static
       - ../../templates/admin:/opt/xos/templates/admin
+      - ../../configurations:/opt/xos/configurations
diff --git a/xos/configurations/frontend/mocks/cord.yaml b/xos/configurations/frontend/mocks/cord.yaml
new file mode 100644
index 0000000..8c84d8f
--- /dev/null
+++ b/xos/configurations/frontend/mocks/cord.yaml
@@ -0,0 +1,534 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup CORD-related services -- vOLT, vCPE, vBNG.
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    # CORD Services
+    service_volt:
+      type: tosca.nodes.Service
+      requirements:
+          - vcpe_tenant:
+              node: service_vsg
+              relationship: tosca.relationships.TenantOfService
+          - lan_network:
+              node: lan_network
+              relationship: tosca.relationships.UsesNetwork
+          - wan_network:
+              node: wan_network
+              relationship: tosca.relationships.UsesNetwork
+      properties:
+          view_url: /admin/cord/voltservice/$id$/
+          kind: vOLT
+
+    service_vsg:
+      type: tosca.nodes.VSGService
+      requirements:
+          - vbng_tenant:
+              node: service_vbng
+              relationship: tosca.relationships.TenantOfService
+      properties:
+          view_url: /admin/cord/vsgservice/$id$/
+          backend_network_label: hpc_client
+          #public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+          #private_key_fn: /opt/xos/synchronizers/vcpe/vcpe_private_key
+      #artifacts:
+          #pubkey: /opt/xos/synchronizers/vcpe/vcpe_public_key
+
+    service_vbng:
+      type: tosca.nodes.VBNGService
+      properties:
+          view_url: /admin/cord/vbngservice/$id$/
+# if unspecified, vbng observer will look for an ONOSApp Tenant and
+# generate a URL from its IP address
+#          vbng_url: http://10.11.10.24:8181/onos/virtualbng/
+
+    service_ONOS_vBNG:
+      type: tosca.nodes.ONOSService
+      requirements:
+      properties:
+          kind: onos
+          view_url: /admin/onos/onosservice/$id$/
+          #public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+      #artifacts:
+          #pubkey: /opt/xos/synchronizers/onos/onos_key.pub
+
+#
+# To actually bring up the vBNG app
+# - Set up the dataplane using the ansible script
+# - Log into the vBNG ONOS and run 'devices' to get switch dpID
+# - Change the dpID values in vBNG ONOS app in XOS GUI
+# - (Synchronizer should copy the files to ONOS container immediately)
+# - Log into service_ONOS_vBNG VM and restart ONOS Docker container
+#   (Should roll this step into a Synchronizer)
+#f
+    vBNG_ONOS_app:
+      type: tosca.nodes.ONOSvBNGApp
+      requirements:
+          - onos_tenant:
+              node: service_ONOS_vBNG
+              relationship: tosca.relationships.TenantOfService
+          - vbng_service:
+              node: service_vbng
+              relationship: tosca.relationships.UsedByService
+      properties:
+          dependencies: org.onosproject.proxyarp, org.onosproject.virtualbng, org.onosproject.openflow, org.onosproject.fwd
+          config_network-cfg.json: >
+            {
+              "ports" : {
+                "of:0000000000000001/1" : {
+                  "interfaces" : [
+                    {
+                      "ips"  : [ "10.0.1.253/24" ],
+                      "mac"  : "00:00:00:00:00:99"
+                    }
+                  ]
+                },
+                "of:0000000000000001/2" : {
+                  "interfaces" : [
+                    {
+                      "ips"  : [ "10.254.0.2/24" ],
+                      "mac"  : "00:00:00:00:00:98"
+                    }
+                  ]
+                }
+              }
+            }
+          #config_virtualbng.json: { get_artifact: [ SELF, virtualbng_json, LOCAL_FILE] }
+      #artifacts:
+          #virtualbng_json: /root/setup/virtualbng.json
+
+    service_ONOS_vOLT:
+      type: tosca.nodes.ONOSService
+      requirements:
+      properties:
+          kind: onos
+          view_url: /admin/onos/onosservice/$id$/
+          #public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+          rest_onos/v1/network/configuration/: >
+            {
+              "devices" : {
+                "of:0000000000000001" : {
+                  "accessDevice" : {
+                    "uplink" : "2",
+                    "vlan"   : "222",
+                    "defaultVlan" : "1"
+                  },
+                  "basic" : {
+                    "driver" : "pmc-olt"
+                  }
+                }
+              }
+            }
+      #artifacts:
+          #pubkey: /opt/xos/synchronizers/onos/onos_key.pub
+
+
+    vOLT_ONOS_app:
+      type: tosca.nodes.ONOSvOLTApp
+      requirements:
+          - onos_tenant:
+              node: service_ONOS_vOLT
+              relationship: tosca.relationships.TenantOfService
+          - volt_service:
+              node: service_volt
+              relationship: tosca.relationships.UsedByService
+      properties:
+          install_dependencies: onos-ext-notifier-1.0-SNAPSHOT.oar, onos-ext-volt-event-publisher-1.0-SNAPSHOT.oar
+          dependencies: org.onosproject.openflow-base, org.onosproject.olt, org.ciena.onos.ext_notifier, org.ciena.onos.volt_event_publisher
+          component_config: >
+             {
+                "org.ciena.onos.ext_notifier.KafkaNotificationBridge":{
+                   "rabbit.user": "<rabbit_user>",
+                   "rabbit.password": "<rabbit_password>",
+                   "rabbit.host": "<rabbit_host>",
+                   "publish.rabbit": "true",
+                   "volt.events.rabbit.topic": "notifications.info",
+                   "volt.events.rabbit.exchange": "voltlistener",
+                   "volt.events.opaque.info": "{project_id: <keystone_tenant_id>, user_id: <keystone_user_id>}",
+                   "publish.volt.events": "true"
+                }
+             }
+#          config_network-cfg.json: >
+#            {
+#              "devices" : {
+#                "of:0000000000000001" : {
+#                  "accessDevice" : {
+#                    "uplink" : "2",
+#                    "vlan"   : "222",
+#                    "defaultVlan" : "1"
+#                  },
+#                  "basic" : {
+#                    "driver" : "default"
+#                  }
+#                }
+#              }
+#            }
+
+    # Network templates
+    Private:
+      type: tosca.nodes.NetworkTemplate
+
+    Public network hack:
+      type: tosca.nodes.NetworkTemplate
+      properties:
+          visibility: private
+          translation: NAT
+          shared_network_name: tun0-net
+
+
+    # Networks required by the CORD setup
+    lan_network:
+      type: tosca.nodes.network.Network
+      properties:
+          ip_version: 4
+      requirements:
+          - network_template:
+              node: Private
+              relationship: tosca.relationships.UsesNetworkTemplate
+          - owner:
+              node: mysite_vcpe
+              relationship: tosca.relationships.MemberOfSlice
+          - connection:
+              node: mysite_vcpe
+              relationship: tosca.relationships.ConnectsToSlice
+          - connection:
+              node: mysite_volt
+              relationship: tosca.relationships.ConnectsToSlice
+
+    wan_network:
+      type: tosca.nodes.network.Network
+      properties:
+          ip_version: 4
+      requirements:
+          - network_template:
+              node: Private
+              relationship: tosca.relationships.UsesNetworkTemplate
+          - owner:
+              node: mysite_vcpe
+              relationship: tosca.relationships.MemberOfSlice
+          - connection:
+              node: mysite_vcpe
+              relationship: tosca.relationships.ConnectsToSlice
+          - connection:
+              node: mysite_vbng
+              relationship: tosca.relationships.ConnectsToSlice
+
+    Private-Direct:
+      type: tosca.nodes.NetworkTemplate
+      properties:
+          access: direct
+
+    Private-Indirect:
+      type: tosca.nodes.NetworkTemplate
+      properties:
+          access: indirect
+
+    subscriber_network:
+      type: tosca.nodes.network.Network
+      properties:
+          ip_version: 4
+      requirements:
+          - network_template:
+              node: Private
+              relationship: tosca.relationships.UsesNetworkTemplate
+          - owner:
+              node: mysite_volt
+              relationship: tosca.relationships.MemberOfSlice
+          - connection:
+              node: mysite_volt
+              relationship: tosca.relationships.ConnectsToSlice
+          - connection:
+              node: mysite_clients
+              relationship: tosca.relationships.ConnectsToSlice
+
+    public_network:
+      type: tosca.nodes.network.Network
+      properties:
+      requirements:
+          - network_template:
+              node: Public network hack
+              relationship: tosca.relationships.UsesNetworkTemplate
+          - owner:
+              node: mysite_vbng
+              relationship: tosca.relationships.MemberOfSlice
+          - connection:
+              node: mysite_vbng
+              relationship: tosca.relationships.ConnectsToSlice
+
+
+    mysite:
+      type: tosca.nodes.Site
+
+
+    # CORD Slices
+    mysite_vcpe:
+      description: vCPE Controller Slice
+      type: tosca.nodes.Slice
+      requirements:
+          - vcpe_service:
+              node: service_vsg
+              relationship: tosca.relationships.MemberOfService
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+          - vcpe_docker_image:
+              node: docker-vcpe
+              relationship: tosca.relationships.UsesImage
+#      properties:
+#          default_isolation: container
+
+    mysite_onos_vbng:
+      description: ONOS Controller Slice for vBNG
+      type: tosca.nodes.Slice
+      requirements:
+          - ONOS:
+              node: service_ONOS_vBNG
+              relationship: tosca.relationships.MemberOfService
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
+    mysite_onos_volt:
+      description: ONOS Controller Slice for vOLT
+      type: tosca.nodes.Slice
+      requirements:
+          - ONOS:
+              node: service_ONOS_vOLT
+              relationship: tosca.relationships.MemberOfService
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
+    mysite_vbng:
+      description: slice running OVS controlled by vBNG
+      type: tosca.nodes.Slice
+      requirements:
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
+    mysite_volt:
+      description: OVS controlled by vOLT
+      type: tosca.nodes.Slice
+      requirements:
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
+    mysite_clients:
+      description: slice for clients at the subscriber
+      type: tosca.nodes.Slice
+      requirements:
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
+
+    # Virtual machines
+    onos_app_1:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: Ubuntu
+            version: 14.10
+      requirements:
+          - slice:
+                node: mysite_onos_vbng
+                relationship: tosca.relationships.MemberOfSlice
+
+    onos_app_2:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: Ubuntu
+            version: 14.10
+      requirements:
+          - slice:
+                node: mysite_onos_volt
+                relationship: tosca.relationships.MemberOfSlice
+
+    # VM for running the OVS controlled by vBNG
+    ovs_vbng:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: ubuntu
+            version: 14.04
+      requirements:
+          - slice:
+                node: mysite_vbng
+                relationship: tosca.relationships.MemberOfSlice
+
+    # VM for running the OVS controlled by vOLT
+    ovs_volt:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: ubuntu
+            version: 14.04
+      requirements:
+          - slice:
+                node: mysite_volt
+                relationship: tosca.relationships.MemberOfSlice
+
+    # A subscriber client VM
+    client1:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: ubuntu
+            version: 14.04
+      requirements:
+          - slice:
+                node: mysite_clients
+                relationship: tosca.relationships.MemberOfSlice
+
+    # docker image for vcpe containers
+    docker-vcpe:
+      # TODO: need to attach this to mydeployment
+      type: tosca.nodes.Image
+      properties:
+        kind: container
+        container_format: na
+        disk_format: na
+        path: andybavier/docker-vcpe
+        tag: develop
+
+    # Let's add a user who can be administrator of the household
+    johndoe@myhouse.com:
+      type: tosca.nodes.User
+      properties:
+          password: letmein
+          firstname: john
+          lastname: doe
+      requirements:
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
+    # A subscriber
+    My House:
+       type: tosca.nodes.CORDSubscriber
+       properties:
+           service_specific_id: 123
+           firewall_enable: false
+           cdn_enable: false
+           url_filter_enable: false
+           url_filter_level: R
+       requirements:
+          - house_admin:
+              node: johndoe@myhouse.com
+              relationship: tosca.relationships.AdminPrivilege
+
+    Mom's PC:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 01:02:03:04:05:06
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    Dad's PC:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 90:E2:BA:82:F9:75
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    Jack's Laptop:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 68:5B:35:9D:91:D5
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    Jill's Laptop:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 34:36:3B:C9:B6:A6
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    My Volt:
+        type: tosca.nodes.VOLTTenant
+        properties:
+            service_specific_id: 123
+            s_tag: 222
+            c_tag: 432
+        requirements:
+            - provider_service:
+                node: service_volt
+                relationship: tosca.relationships.MemberOfService
+            - subscriber:
+                node: My House
+                relationship: tosca.relationships.BelongsToSubscriber
diff --git a/xos/configurations/opencloud/docker-compose.yml b/xos/configurations/opencloud/docker-compose.yml
index 3813dee..b44c828 100644
--- a/xos/configurations/opencloud/docker-compose.yml
+++ b/xos/configurations/opencloud/docker-compose.yml
@@ -16,6 +16,7 @@
         - ../common/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
         - /usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro
         - ./files/xos_opencloud_config:/opt/xos/xos_configuration/xos_opencloud_config:ro
+        - ./images:/opt/xos/images:ro
 
 xos_synchronizer_hpc:
     image: xosproject/xos-synchronizer-openstack
diff --git a/xos/configurations/test/README b/xos/configurations/test/README.md
similarity index 86%
rename from xos/configurations/test/README
rename to xos/configurations/test/README.md
index 31f7786..37af594 100644
--- a/xos/configurations/test/README
+++ b/xos/configurations/test/README.md
@@ -1,2 +1,4 @@
+#CORD Test Configuration
+
 This configuration launches the XOS container on cloudlab and runs a test suite. The test results will be printed to
 the console, and then the docker container will exit. 
diff --git a/xos/configurations/vtn/Makefile b/xos/configurations/vtn/Makefile
index 335f83d..1315b39 100644
--- a/xos/configurations/vtn/Makefile
+++ b/xos/configurations/vtn/Makefile
@@ -8,9 +8,10 @@
 xos: vtn_network_cfg_json
 	sudo MYIP=$(MYIP) docker-compose up -d
 	bash ../common/wait_for_xos.sh
+	sudo MYIP=$(MYIP) docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/fixtures.yaml
 	sudo MYIP=$(MYIP) docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/base.yaml
 	sudo MYIP=$(MYIP) docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/nodes.yaml
-	sudo MYIP=$(MYIP) docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/tosca/samples/vtn.yaml
+	sudo MYIP=$(MYIP) docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/vtn/vtn.yaml
 
 containers:
 	cd ../../../containers/xos; make devel
diff --git a/xos/tosca/samples/cord.yaml b/xos/configurations/vtn/cord-vtn-vsg.yaml
similarity index 66%
rename from xos/tosca/samples/cord.yaml
rename to xos/configurations/vtn/cord-vtn-vsg.yaml
index a9baf25..f08a1b9 100644
--- a/xos/tosca/samples/cord.yaml
+++ b/xos/configurations/vtn/cord-vtn-vsg.yaml
@@ -1,6 +1,6 @@
 tosca_definitions_version: tosca_simple_yaml_1_0
 
-description: Setup CORD-related services -- vOLT, vCPE, vBNG.
+description: Just enough Tosca to get the vSG slice running on VTN-Cloudlab
 
 imports:
    - custom_types/xos.yaml
@@ -11,64 +11,88 @@
     service_volt:
       type: tosca.nodes.Service
       requirements:
-          - vcpe_tenant:
-              node: service_vcpe
+          - vsg_tenant:
+              node: service_vsg
               relationship: tosca.relationships.TenantOfService
       properties:
           view_url: /admin/cord/voltservice/$id$/
           kind: vOLT
 
-    Private:
-      type: tosca.nodes.NetworkTemplate
+    public_addresses:
+      type: tosca.nodes.AddressPool
+      properties:
+          addresses: 10.123.0.0/24 10.124.0.0/24
 
-    # networks required by vCPE
-    lan_network:

-      type: tosca.nodes.network.Network

-      properties:

-          ip_version: 4

-      requirements:

-          - network_template:

-              node: Private

-              relationship: tosca.relationships.UsesNetworkTemplate

-          - owner:

-              node: mysite_vcpe

-              relationship: tosca.relationships.MemberOfSlice

-          - connection:

-              node: mysite_vcpe

-              relationship: tosca.relationships.ConnectsToSlice
-
-    service_vcpe:
-      type: tosca.nodes.VCPEService
+    service_vsg:
+      type: tosca.nodes.VSGService
       requirements:
           - vbng_tenant:
               node: service_vbng
               relationship: tosca.relationships.TenantOfService
       properties:
-          view_url: /admin/cord/vcpeservice/$id$/
+          view_url: /admin/cord/vsgservice/$id$/
           backend_network_label: hpc_client
           public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+          private_key_fn: /opt/xos/synchronizers/vcpe/vcpe_private_key
+          wan_container_gateway_ip: 10.123.0.1
+          wan_container_gateway_mac: 00:8c:fa:5b:09:d8
+          wan_container_netbits: 24
       artifacts:
-          pubkey: /opt/xos/observers/vcpe/vcpe_public_key
+          pubkey: /opt/xos/synchronizers/vcpe/vcpe_public_key
 
     service_vbng:
       type: tosca.nodes.VBNGService
       properties:
           view_url: /admin/cord/vbngservice/$id$/
-          vbng_url: http://10.0.3.136:8181/onos/virtualbng/
+# if unspecified, vbng observer will look for an ONOSApp Tenant and
+# generate a URL from its IP address
+#          vbng_url: http://10.11.10.24:8181/onos/virtualbng/
+
+    Private:
+      type: tosca.nodes.NetworkTemplate
+
+    management:
+      type: tosca.nodes.network.Network.XOS
+      properties:
+          no-create: true
+          no-delete: true
+          no-update: true
 
     mysite:
       type: tosca.nodes.Site
 
-    mysite_vcpe:
-      description: vCPE Controller Slice
-      type: tosca.nodes.Slice
+    # Networks required by the CORD setup
+    mysite_vsg-access:
+      type: tosca.nodes.network.Network
+      properties:
+          ip_version: 4
       requirements:
-          - vcpe_service:
-              node: service_vcpe
+          - network_template:
+              node: Private
+              relationship: tosca.relationships.UsesNetworkTemplate
+          - owner:
+              node: mysite_vsg
+              relationship: tosca.relationships.MemberOfSlice
+          - connection:
+              node: mysite_vsg
+              relationship: tosca.relationships.ConnectsToSlice
+
+    # CORD Slices
+    mysite_vsg:
+      description: vSG Controller Slice
+      type: tosca.nodes.Slice
+      properties:
+          network: noauto
+      requirements:
+          - vsg_service:
+              node: service_vsg
               relationship: tosca.relationships.MemberOfService
           - site:
               node: mysite
               relationship: tosca.relationships.MemberOfSite
+          - management:
+              node: management
+              relationship: tosca.relationships.ConnectsToNetwork
 
     # Let's add a user who can be administrator of the household
     johndoe@myhouse.com:
@@ -82,14 +106,14 @@
               node: mysite
               relationship: tosca.relationships.MemberOfSite
 
-    # Now let's add a subscriber
+    # A subscriber
     My House:
        type: tosca.nodes.CORDSubscriber
        properties:
-           service_specific_id: 1234
-           firewall_enable: true
-           cdn_enable: true
-           url_filter_enable: true
+           service_specific_id: 123
+           firewall_enable: false
+           cdn_enable: false
+           url_filter_enable: false
            url_filter_level: R
        requirements:
           - house_admin:
@@ -99,7 +123,7 @@
     Mom's PC:
        type: tosca.nodes.CORDUser
        properties:
-           mac: 010203040506
+           mac: 01:02:03:04:05:06
            level: PG_13
        requirements:
            - household:
@@ -109,7 +133,7 @@
     Dad's PC:
        type: tosca.nodes.CORDUser
        properties:
-           mac: 90E2Ba82F975
+           mac: 90:E2:BA:82:F9:75
            level: PG_13
        requirements:
            - household:
@@ -119,7 +143,7 @@
     Jack's Laptop:
        type: tosca.nodes.CORDUser
        properties:
-           mac: 685B359D91D5
+           mac: 68:5B:35:9D:91:D5
            level: PG_13
        requirements:
            - household:
@@ -129,7 +153,7 @@
     Jill's Laptop:
        type: tosca.nodes.CORDUser
        properties:
-           mac: 34363BC9B6A6
+           mac: 34:36:3B:C9:B6:A6
            level: PG_13
        requirements:
            - household:
@@ -139,7 +163,7 @@
     My Volt:
         type: tosca.nodes.VOLTTenant
         properties:
-            service_specific_id: 1234
+            service_specific_id: 123
             s_tag: 222
             c_tag: 432
         requirements:
@@ -149,8 +173,3 @@
             - subscriber:
                 node: My House
                 relationship: tosca.relationships.BelongsToSubscriber
-
-
-
-
-
diff --git a/xos/configurations/vtn/docker-compose.yml b/xos/configurations/vtn/docker-compose.yml
index e7bb6b1..7fb68f1 100644
--- a/xos/configurations/vtn/docker-compose.yml
+++ b/xos/configurations/vtn/docker-compose.yml
@@ -21,7 +21,7 @@
 
 xos_synchronizer_onos:
     image: xosproject/xos-synchronizer-openstack
-    command: bash -c "python /opt/xos/synchronizers/onos/onos-observer.py -C /opt/xos/synchronizers/onos/onos_observer_config"
+    command: bash -c "python /opt/xos/synchronizers/onos/onos-synchronizer.py -C /opt/xos/synchronizers/onos/onos_synchronizer_config"
     labels:
         org.xosproject.kind: synchronizer
         org.xosproject.target: onos
diff --git a/xos/tosca/samples/vtn.yaml b/xos/configurations/vtn/vtn.yaml
similarity index 72%
rename from xos/tosca/samples/vtn.yaml
rename to xos/configurations/vtn/vtn.yaml
index 50e8c86..9b36852 100644
--- a/xos/tosca/samples/vtn.yaml
+++ b/xos/configurations/vtn/vtn.yaml
@@ -1,12 +1,44 @@
 tosca_definitions_version: tosca_simple_yaml_1_0
 
-description: Setup CORD-related services -- vOLT, vCPE, vBNG.
+description: Some VTN related stuff
 
 imports:
    - custom_types/xos.yaml
 
 topology_template:
   node_templates:
+    management_template:
+      type: tosca.nodes.NetworkTemplate
+      properties:
+          visibility: private
+          translation: none
+
+    management:
+      type: tosca.nodes.network.Network
+      properties:
+          ip_version: 4
+          cidr: 172.27.0.0/24
+      requirements:
+          - network_template:
+              node: management_template
+              relationship: tosca.relationships.UsesNetworkTemplate
+          - owner:
+              node: mysite_management
+              relationship: tosca.relationships.MemberOfSlice
+
+    mysite:
+      type: tosca.nodes.Site
+
+    mysite_management:
+      description: This slice exists solely to own the management network
+      type: tosca.nodes.Slice
+      properties:
+          network: noauto
+      requirements:
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
     service_ONOS_VTN:
       type: tosca.nodes.ONOSService
       requirements:
@@ -14,8 +46,10 @@
           kind: onos
           view_url: /admin/onos/onosservice/$id$/
           public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+          node_key: { get_artifact: [ SELF, nodekey, LOCAL_FILE] }
       artifacts:
           pubkey: /opt/xos/synchronizers/onos/onos_key.pub
+          nodekey: /root/setup/node_key
 
     VTN_ONOS_app:
       type: tosca.nodes.ONOSVTNApp
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 7e2ae73..f5578ec 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -42,13 +42,7 @@
     # FIXME: Need to clean this up by separating Javascript from Python
     if (obj.pk):
         script = """
-        <script type="text/javascript">
-            $(document).ready(function () {
-                $("#show_details_%d").click(function () {
-                    $("#status%d").dialog({modal: true, height: 200, width: 200 });
-                });
-            });
-        </script>
+        <script type="text/javascript">$(document).ready(function () {$("#show_details_%d").click(function () {$("#status%d").dialog({modal: true, height: 200, width: 200 });});});</script>
         """%(obj.pk,obj.pk)
 
         div = """
@@ -1907,7 +1901,7 @@
     suit_form_tabs = (('general','Network Template Details'), ('netparams', 'Parameters') )
 
 class PortAdmin(XOSBaseAdmin):
-    list_display = ("backend_status_icon", "name", "id", "ip")
+    list_display = ("backend_status_icon", "id", "ip")
     list_display_links = ('backend_status_icon', 'id')
     readonly_fields = ("subnet", )
     inlines = [NetworkParameterInline]
diff --git a/xos/core/models/__init__.py b/xos/core/models/__init__.py
index a022cae..628a3bb 100644
--- a/xos/core/models/__init__.py
+++ b/xos/core/models/__init__.py
@@ -26,7 +26,7 @@
 from .instance import Instance
 from .reservation import ReservedResource
 from .reservation import Reservation
-from .network import Network, NetworkParameterType, NetworkParameter, Port, NetworkTemplate, Router, NetworkSlice, ControllerNetwork
+from .network import Network, NetworkParameterType, NetworkParameter, Port, NetworkTemplate, Router, NetworkSlice, ControllerNetwork, AddressPool
 from .billing import Account, Invoice, Charge, UsableObject, Payment
 from .program import Program
 
diff --git a/xos/core/models/instance.py b/xos/core/models/instance.py
index 62a86c4..7f13eb8 100644
--- a/xos/core/models/instance.py
+++ b/xos/core/models/instance.py
@@ -101,6 +101,9 @@
     volumes = models.TextField(null=True, blank=True, help_text="Comma-separated list of directories to expose to parent context")
     parent = models.ForeignKey("Instance", null=True, blank=True, help_text="Parent Instance for containers nested inside of VMs")
 
+    def get_controller (self):
+        return self.node.site_deployment.controller
+
     def __unicode__(self):
         if self.name and Slice.objects.filter(id=self.slice_id) and (self.name != self.slice.name):
             # NOTE: The weird check on self.slice_id was due to a problem when
@@ -183,6 +186,9 @@
 
     # return an address that the synchronizer can use to SSH to the instance
     def get_ssh_ip(self):
+        management=self.get_network_ip("management")
+        if management:
+            return management
         return self.get_network_ip("nat")
 
     @staticmethod
diff --git a/xos/core/models/network.py b/xos/core/models/network.py
index a019091..80ee9ba 100644
--- a/xos/core/models/network.py
+++ b/xos/core/models/network.py
@@ -1,7 +1,7 @@
 import os
 import socket
 import sys
-from django.db import models
+from django.db import models, transaction
 from core.models import PlCoreBase, Site, Slice, Instance, Controller
 from core.models import ControllerLinkManager,ControllerLinkDeletionManager
 from django.contrib.contenttypes.models import ContentType
@@ -259,6 +259,7 @@
     ip = models.GenericIPAddressField(help_text="Instance ip address", blank=True, null=True)
     port_id = models.CharField(null=True, blank=True, max_length=256, help_text="Neutron port id")
     mac = models.CharField(null=True, blank=True, max_length=256, help_text="MAC address associated with this port")
+    xos_created = models.BooleanField(default=False) # True if XOS created this port in Neutron, False if port created by Neutron and observed by XOS
 
     class Meta:
         unique_together = ('network', 'instance')
@@ -337,4 +338,60 @@
     def __unicode__(self):
         return self.parameter.name
 
+class AddressPool(PlCoreBase):
+    name = models.CharField(max_length=32)
+    addresses = models.TextField(blank=True, null=True)
+    inuse = models.TextField(blank=True, null=True)
+
+    def __unicode__(self): return u'%s' % (self.name)
+
+    def get_address(self):
+        with transaction.atomic():
+            ap = AddressPool.objects.get(pk=self.pk)
+            if ap.addresses:
+                avail_ips = ap.addresses.split()
+            else:
+                avail_ips = []
+
+            if ap.inuse:
+                inuse_ips = ap.inuse.split()
+            else:
+                inuse_ips = []
+
+            while avail_ips:
+                addr = avail_ips.pop(0)
+
+                if addr in inuse_ips:
+                    # This may have happened if someone re-ran the tosca
+                    # recipe and 'refilled' the AddressPool while some addresses
+                    # were still in use.
+                    continue
+
+                inuse_ips.insert(0,addr)
+
+                ap.inuse = " ".join(inuse_ips)
+                ap.addresses = " ".join(avail_ips)
+                ap.save()
+                return addr
+
+            addr = None
+        return addr
+
+    def put_address(self, addr):
+        with transaction.atomic():
+            ap = AddressPool.objects.get(pk=self.pk)
+            addresses = ap.addresses or ""
+            parts = addresses.split()
+            if addr not in parts:
+                parts.insert(0,addr)
+                ap.addresses = " ".join(parts)
+
+            inuse = ap.inuse or ""
+            parts = inuse.split()
+            if addr in parts:
+                parts.remove(addr)
+                ap.inuse = " ".join(parts)
+
+            ap.save()
+
 
diff --git a/xos/core/models/plcorebase.py b/xos/core/models/plcorebase.py
index 0822bf5..99acc15 100644
--- a/xos/core/models/plcorebase.py
+++ b/xos/core/models/plcorebase.py
@@ -224,6 +224,9 @@
         self._initial = self._dict # for PlModelMixIn
         self.silent = False
 
+    def get_controller(self):
+        return self.controller
+
     def can_update(self, user):
         return user.can_update_root()
 
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index b5ba737..6ece1b3 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -554,6 +554,22 @@
         self.set_attribute("instance_id", value)
 
     @property
+    def external_hostname(self):
+        return self.get_attribute("external_hostname", "")
+
+    @external_hostname.setter
+    def external_hostname(self, value):
+        self.set_attribute("external_hostname", value)
+
+    @property
+    def external_container(self):
+        return self.get_attribute("external_container", "")
+
+    @external_container.setter
+    def external_container(self, value):
+        self.set_attribute("external_container", value)
+
+    @property
     def creator(self):
         from core.models import User
         if getattr(self, "cached_creator", None):
@@ -645,12 +661,15 @@
                 instance = self.pick_least_loaded_instance_in_slice(slices)
 
             if not instance:
-                flavors = Flavor.objects.filter(name="m1.small")
-                if not flavors:
-                    raise XOSConfigurationError("No m1.small flavor")
-
                 slice = self.provider_service.slices.all()[0]
 
+                flavor = slice.default_flavor
+                if not flavor:
+                    flavors = Flavor.objects.filter(name="m1.small")
+                    if not flavors:
+                        raise XOSConfigurationError("No m1.small flavor")
+                    flavor = flavors[0]
+
                 if slice.default_isolation == "container_vm":
                     (node, parent) = ContainerVmScheduler(slice).pick()
                 else:
@@ -661,7 +680,7 @@
                                 image = self.image,
                                 creator = self.creator,
                                 deployment = node.site_deployment.deployment,
-                                flavor = flavors[0],
+                                flavor = flavor,
                                 isolation = slice.default_isolation,
                                 parent = parent)
                 self.save_instance(instance)
diff --git a/xos/core/models/site.py b/xos/core/models/site.py
index 1bdef36..b98c40a 100644
--- a/xos/core/models/site.py
+++ b/xos/core/models/site.py
@@ -310,6 +310,11 @@
     site = models.ForeignKey(Site,related_name='controllersite')
     controller = models.ForeignKey(Controller, null=True, blank=True, related_name='controllersite')
     tenant_id = StrippedCharField(null=True, blank=True, max_length=200, db_index=True, help_text="Keystone tenant id")
+
+    def delete(self, *args, **kwds):
+        pdb.set_trace()
+        super(ControllerSite, self).delete(*args, **kwds)
+
     
     class Meta:
         unique_together = ('site', 'controller') 
diff --git a/xos/core/models/tag.py b/xos/core/models/tag.py
index d774800..76a4e2e 100644
--- a/xos/core/models/tag.py
+++ b/xos/core/models/tag.py
@@ -27,6 +27,10 @@
     def can_update(self, user):
         return user.can_update_root()
 
+    @classmethod
+    def select_by_content_object(cls, obj):
+        return cls.objects.filter(content_type=ContentType.objects.get_for_model(obj), object_id=obj.id)
+
     @staticmethod
     def select_by_user(user):
         return Tag.objects.all()
diff --git a/xos/core/xoslib/methods/ceilometerview.py b/xos/core/xoslib/methods/ceilometerview.py
index c2ecb15..5f99b61 100644
--- a/xos/core/xoslib/methods/ceilometerview.py
+++ b/xos/core/xoslib/methods/ceilometerview.py
@@ -222,6 +222,7 @@
         self._kwapi_meters_info = self._get_kwapi_meters_info()
         self._ipmi_meters_info = self._get_ipmi_meters_info()
         self._vcpe_meters_info = self._get_vcpe_meters_info()
+        self._volt_meters_info = self._get_volt_meters_info()
         self._sdn_meters_info = self._get_sdn_meters_info()
 
         # Storing the meters info of all services together.
@@ -233,6 +234,7 @@
                                self._kwapi_meters_info,
                                self._ipmi_meters_info,
                                self._vcpe_meters_info,
+                               self._volt_meters_info,
                                self._sdn_meters_info)
         self._all_meters_info = {}
         for service_meters in all_services_meters:
@@ -335,6 +337,16 @@
         return self._list(only_meters=self._vcpe_meters_info.keys(),
                           except_meters=except_meters)
 
+    def list_volt(self, except_meters=None):
+        """Returns a list of meters tied to volt service
+
+        :Parameters:
+          - `except_meters`: The list of meter names we don't want to show
+        """
+
+        return self._list(only_meters=self._volt_meters_info.keys(),
+                          except_meters=except_meters)
+
     def list_sdn(self, except_meters=None):
         """Returns a list of meters tied to sdn service
 
@@ -949,6 +961,39 @@
             }),
         ])
 
+    def _get_volt_meters_info(self):
+        """Returns additional info for each meter
+
+        That will be used for augmenting the Ceilometer meter
+        """
+
+        # TODO(lsmola) Unless the Ceilometer will provide the information
+        # below, I need to define it as a static here. I will be joining this
+        # to info that I am able to obtain from Ceilometer meters, hopefully
+        # some day it will be supported all.
+        return datastructures.SortedDict([
+            ('volt.device', {
+                'type': _("VOLT"),
+                'label': '',
+                'description': _("Existence of olt device"),
+            }),
+            ('volt.device.disconnect', {
+                'type': _("VOLT"),
+                'label': '',
+                'description': _("Olt device disconnected"),
+            }),
+            ('volt.device.subscriber', {
+                'type': _("VOLT"),
+                'label': '',
+                'description': _("Existence of olt subscriber"),
+            }),
+            ('volt.device.subscriber.unregister', {
+                'type': _("VOLT"),
+                'label': '',
+                'description': _("Olt subscriber unregistered"),
+            }),
+        ])
+
     def _get_sdn_meters_info(self):
         """Returns additional info for each meter
 
@@ -1121,6 +1166,7 @@
             _('Nova'): meters.list_nova(),
             _('Neutron'): meters.list_neutron(),
             _('VCPE'): meters.list_vcpe(),
+            _('VOLT'): meters.list_volt(),
             _('SDN'): meters.list_sdn(),
         }
         meters = []
@@ -1191,6 +1237,7 @@
             _('Nova'): meters.list_nova(),
             _('Neutron'): meters.list_neutron(),
             _('VCPE'): meters.list_vcpe(),
+            _('VOLT'): meters.list_volt(),
             _('SDN'): meters.list_sdn(),
         }
         report_rows = []
@@ -1343,6 +1390,7 @@
                 _('Nova'): meters.list_nova(except_meters=exclude_nova_meters_info),
                 _('Neutron'): meters.list_neutron(except_meters=exclude_neutron_meters_info),
                 _('VCPE'): meters.list_vcpe(),
+                _('VOLT'): meters.list_volt(),
                 _('SDN'): meters.list_sdn(),
             }
             for service,meters in services.items():
diff --git a/xos/core/xoslib/static/js/vendor/ngXosHelpers.js b/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
index e8f063f..82a141e 100644
--- a/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
+++ b/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
@@ -1 +1 @@
-!function(){"use strict";function e(e,n,o){e.interceptors.push("SetCSRFToken"),n.startSymbol("{$"),n.endSymbol("$}"),o.defaults.stripTrailingSlashes=!1}angular.module("xos.helpers",["ngCookies","xos.xos","xos.hpcapi","xos.xoslib"]).config(e),e.$inject=["$httpProvider","$interpolateProvider","$resourceProvider"]}(),function(){"use strict";function e(){return{request:function(e){return-1===e.url.indexOf(".html")&&(e.url+="?no_hyperlinks=1"),e}}}angular.module("xos.helpers").factory("NoHyperlinks",e)}(),function(){"use strict";function e(e){return{request:function(n){return"GET"!==n.method&&(n.headers["X-CSRFToken"]=e.get("xoscsrftoken")),n}}}angular.module("xos.helpers").factory("SetCSRFToken",e),e.$inject=["$cookies"]}(),function(){"use strict";function e(e){return r||(r=new e({domain:""})),r}function n(e){return t||(t=new e({domain:""})),t}function o(e){return i||(i=new e({domain:""})),i}angular.module("xos.helpers").service("XosApi",e).service("XoslibApi",n).service("HpcApi",o);var r,t,i;e.$inject=["xos"],n.$inject=["xoslib"],o.$inject=["hpcapi"]}();
\ No newline at end of file
+!function(){"use strict";function e(e,n,o){e.interceptors.push("SetCSRFToken"),n.startSymbol("{$"),n.endSymbol("$}"),o.defaults.stripTrailingSlashes=!1}e.$inject=["$httpProvider","$interpolateProvider","$resourceProvider"],angular.module("bugSnag",[]).factory("$exceptionHandler",function(){return function(e,n){Bugsnag.notifyException(e,{diagnostics:{cause:n}})}}),angular.module("xos.helpers",["ngCookies","xos.xos","xos.hpcapi","xos.xoslib","bugSnag"]).config(e)}(),function(){"use strict";function e(){return{request:function(e){return-1===e.url.indexOf(".html")&&(e.url+="?no_hyperlinks=1"),e}}}angular.module("xos.helpers").factory("NoHyperlinks",e)}(),function(){"use strict";function e(e){return{request:function(n){return"GET"!==n.method&&(n.headers["X-CSRFToken"]=e.get("xoscsrftoken")),n}}}e.$inject=["$cookies"],angular.module("xos.helpers").factory("SetCSRFToken",e)}(),function(){"use strict";function e(e){return t||(t=new e({domain:""})),t}function n(e){return r||(r=new e({domain:""})),r}function o(e){return i||(i=new e({domain:""})),i}e.$inject=["xos"],n.$inject=["xoslib"],o.$inject=["hpcapi"],angular.module("xos.helpers").service("XosApi",e).service("XoslibApi",n).service("HpcApi",o);var t,r,i}();
\ 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 10435f4..1df27f4 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,n){e.stateName=n.name})}]).service("Ceilometer",["$http","$q","lodash",function(e,t,n){this.getMappings=function(){var n=t.defer();return e.get("/xoslib/xos-slice-service-mapping/").then(function(e){n.resolve(e.data)})["catch"](function(e){n.reject(e)}),n.promise},this.getMeters=function(n){var s=t.defer();return e.get("/xoslib/meters/",{cache:!0,params:n}).then(function(e){s.resolve(e.data)})["catch"](function(e){s.reject(e)}),s.promise},this.getSamples=function(n,s){var a=t.defer();return e.get("/xoslib/metersamples/",{params:{meter:n,tenant:s}}).then(function(e){a.resolve(e.data)})["catch"](function(e){a.reject(e)}),a.promise},this.getStats=function(n){var s=t.defer();return e.get("/xoslib/meterstatistics/",{cache:!0,params:n}).then(function(e){s.resolve(e.data)})["catch"](function(e){s.reject(e)}),s.promise},this.selectedService=null,this.selectedSlice=null,this.selectedResource=null}]).directive("ceilometerDashboard",["lodash",function(e){return{restrict:"E",scope:{},bindToController:!0,controllerAs:"vm",templateUrl:"templates/ceilometer-dashboard.tpl.html",controller:["Ceilometer",function(t){var n=this;this.showStats=!1,this.accordion={open:{}},this.openPanels=function(){t.selectedService&&(n.accordion.open[t.selectedService]=!0,t.selectedSlice&&(n.loadSliceMeter(t.selectedSlice,t.selectedService),n.selectedSlice=t.selectedSlice,t.selectedResource&&(n.selectedResource=t.selectedResource)))},this.loadMappings=function(){n.loader=!0,t.getMappings().then(function(e){n.services=e,n.openPanels()})["catch"](function(e){n.error=e.data&&e.data.detail?e.data.detail:"An Error occurred. Please try again later."})["finally"](function(){n.loader=!1})},this.loadMappings(),this.loadSliceMeter=function(s,a){t.selectedSlice=null,t.selectedService=null,t.selectedResources=null,n.loader=!0,n.selectedSlice=s.slice,n.selectedTenant=s.project_id,t.selectedSlice=s,t.selectedService=a,t.getMeters({tenant:s.project_id}).then(function(s){n.selectedResources=e.groupBy(s,"resource_name"),t.selectedResource&&(n.selectedMeters=n.selectedResources[t.selectedResource])})["catch"](function(e){n.error=e.data&&e.data.detail?e.data.detail:"An Error occurred. Please try again later."})["finally"](function(){n.loader=!1})},this.selectedMeters=null,this.selectMeters=function(e,s){n.selectedMeters=e,t.selectedResource=s,n.selectedResource=s}}]}}]).directive("ceilometerSamples",["lodash","$stateParams",function(e,t){return{restrict:"E",scope:{},bindToController:!0,controllerAs:"vm",templateUrl:"templates/ceilometer-samples.tpl.html",controller:["Ceilometer",function(n){var s=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 n=new Date(t.timestamp);return e.push(n.getHours()+":"+((n.getMinutes()<10?"0":"")+n.getMinutes())+":"+n.getSeconds()),e},[])},this.getData=function(e){return e.reduce(function(e,t){return e.push(t.volume),e},[])},this.chartMeters=[],this.addMeterToChart=function(t){s.chart.labels=s.getLabels(e.sortBy(s.samplesList[t],"timestamp")),s.chart.series.push(t),s.chart.data.push(s.getData(e.sortBy(s.samplesList[t],"timestamp"))),s.chartMeters.push(s.samplesList[t][0]),e.remove(s.sampleLabels,{id:t})},this.removeFromChart=function(t){s.chart.data.splice(s.chart.series.indexOf(t.project_id),1),s.chart.series.splice(s.chart.series.indexOf(t.project_id),1),s.chartMeters.splice(e.findIndex(s.chartMeters,{project_id:t.project_id}),1),s.sampleLabels.push({id:t.project_id,name:t.resource_name||t.project_id})},this.formatSamplesLabels=function(t){return e.uniq(t,"project_id").reduce(function(e,t){return e.push({id:t.project_id,name:t.resource_name||t.project_id}),e},[])},this.showSamples=function(){s.loader=!0,n.getSamples(s.name).then(function(t){s.samplesList=e.groupBy(t,"project_id"),s.sampleLabels=s.formatSamplesLabels(t),s.addMeterToChart(s.tenant)})["catch"](function(e){s.error=e.data.detail})["finally"](function(){s.loader=!1})},this.showSamples()}]}}]).directive("ceilometerStats",function(){return{restrict:"E",scope:{name:"=name",tenant:"=tenant"},bindToController:!0,controllerAs:"vm",templateUrl:"templates/ceilometer-stats.tpl.html",controller:["$scope","Ceilometer",function(e,t){var n=this;this.getStats=function(e){n.loader=!0,t.getStats({tenant:e}).then(function(e){n.stats=e})["catch"](function(e){n.error=e.data})["finally"](function(){n.loader=!1})},e.$watch(function(){return n.name},function(e){e&&n.getStats(n.tenant)})}]}}),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.selectedSlice && !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.selectedSlice && 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 in vm.services | orderBy:\'-service\'"\n            template-url="templates/accordion-group.html"\n            is-open="vm.accordion.open[service.service]"\n            heading="{{service.service}}">\n            <h4>Slices:</h4>\n            <a ng-repeat="slice in service.slices" \n              ng-class="{active: slice.slice === vm.selectedSlice}"\n              ng-click="vm.loadSliceMeter(slice, service.service)"\n              href="#" class="list-group-item" >\n              {{slice.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        <!-- STATS -->\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" tenant="vm.selectedTenant"></ceilometer-stats>\n              </div>\n            </div>\n          </div>\n        </article>\n        <!-- METERS -->\n        <article ng-hide="vm.showStats" class="meters animate-slide-left">\n          <div class="col-sm-4 animate-slide-left" ng-hide="!vm.selectedSlice">\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-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.project_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 || meter.resource_id}}\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>\n        <a ng-click="order = \'category\'">Type:</a>\n      </th>\n      <th>\n        <a ng-click="order = \'resource_name\'">Resource:</a>\n      </th>\n      <th>\n        <a ng-click="order = \'meter\'">Meter:</a>\n      </th>\n      <th>\n        Unit:\n      </th>\n      <th>\n        Value:\n      </th>\n    </tr>\n    <!-- <tr>\n      <td>\n        <input type="text" ng-model="query.category">\n      </td>\n      <td>\n        <input type="text" ng-model="query.resource_name">\n      </td>\n      <td>\n        <input type="text" ng-model="query.meter">\n      </td>\n      <td>\n        <input type="text" ng-model="query.unit">\n      </td>\n      <td>\n        <input type="text" ng-model="query.value">\n      </td>\n    </tr> -->\n    <tr ng-repeat="item in vm.stats | orderBy:order">\n      <td>{{item.category}}</td>\n      <td>{{item.resource_name}}</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,n){e.stateName=n.name})}]).service("Ceilometer",["$http","$q",function(e,t){this.getMappings=function(){var n=t.defer();return e.get("/xoslib/xos-slice-service-mapping/").then(function(e){n.resolve(e.data)})["catch"](function(e){n.reject(e)}),n.promise},this.getMeters=function(n){var s=t.defer();return e.get("/xoslib/meters/",{cache:!0,params:n}).then(function(e){s.resolve(e.data)})["catch"](function(e){s.reject(e)}),s.promise},this.getSamples=function(n,s){var r=t.defer();return e.get("/xoslib/metersamples/",{params:{meter:n,tenant:s}}).then(function(e){r.resolve(e.data)})["catch"](function(e){r.reject(e)}),r.promise},this.getStats=function(n){var s=t.defer();return e.get("/xoslib/meterstatistics/",{cache:!0,params:n}).then(function(e){s.resolve(e.data)})["catch"](function(e){s.reject(e)}),s.promise},this.selectedService=null,this.selectedSlice=null,this.selectedResource=null}]).directive("ceilometerDashboard",["lodash",function(e){return{restrict:"E",scope:{},bindToController:!0,controllerAs:"vm",templateUrl:"templates/ceilometer-dashboard.tpl.html",controller:["Ceilometer",function(t){var n=this;this.showStats=!1,this.accordion={open:{}},this.openPanels=function(){t.selectedService&&(n.accordion.open[t.selectedService]=!0,t.selectedSlice&&(n.loadSliceMeter(t.selectedSlice,t.selectedService),n.selectedSlice=t.selectedSlice,t.selectedResource&&(n.selectedResource=t.selectedResource)))},this.loadMappings=function(){n.loader=!0,t.getMappings().then(function(e){n.services=e,n.openPanels()})["catch"](function(e){n.error=e.data&&e.data.detail?e.data.detail:"An Error occurred. Please try again later."})["finally"](function(){n.loader=!1})},this.loadMappings(),this.loadSliceMeter=function(s,r){t.selectedSlice=null,t.selectedService=null,t.selectedResources=null,n.loader=!0,n.error=null,n.ceilometerError=null,t.getMeters({tenant:s.project_id}).then(function(a){n.selectedSlice=s.slice,n.selectedTenant=s.project_id,t.selectedSlice=s,t.selectedService=r,n.selectedResources=e.groupBy(a,"resource_name"),t.selectedResource&&(n.selectedMeters=n.selectedResources[t.selectedResource])})["catch"](function(e){return 503===e.status?n.ceilometerError=e.data.detail.specific_error:void(n.error=e.data&&e.data.detail.specific_error?e.data.detail.specific_error:"An Error occurred. Please try again later.")})["finally"](function(){n.loader=!1})},this.selectedMeters=null,this.selectMeters=function(e,s){n.selectedMeters=e,t.selectedResource=s,n.selectedResource=s}}]}}]).directive("ceilometerSamples",["lodash","$stateParams",function(e,t){return{restrict:"E",scope:{},bindToController:!0,controllerAs:"vm",templateUrl:"templates/ceilometer-samples.tpl.html",controller:["Ceilometer",function(n){var s=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 n=new Date(t.timestamp);return e.push(n.getHours()+":"+((n.getMinutes()<10?"0":"")+n.getMinutes())+":"+n.getSeconds()),e},[])},this.getData=function(e){return e.reduce(function(e,t){return e.push(t.volume),e},[])},this.chartMeters=[],this.addMeterToChart=function(t){s.chart.labels=s.getLabels(e.sortBy(s.samplesList[t],"timestamp")),s.chart.series.push(t),s.chart.data.push(s.getData(e.sortBy(s.samplesList[t],"timestamp"))),s.chartMeters.push(s.samplesList[t][0]),e.remove(s.sampleLabels,{id:t})},this.removeFromChart=function(t){s.chart.data.splice(s.chart.series.indexOf(t.project_id),1),s.chart.series.splice(s.chart.series.indexOf(t.project_id),1),s.chartMeters.splice(e.findIndex(s.chartMeters,{project_id:t.project_id}),1),s.sampleLabels.push({id:t.project_id,name:t.resource_name||t.project_id})},this.formatSamplesLabels=function(t){return e.uniq(t,"project_id").reduce(function(e,t){return e.push({id:t.project_id,name:t.resource_name||t.project_id}),e},[])},this.showSamples=function(){s.loader=!0,n.getSamples(s.name).then(function(t){s.samplesList=e.groupBy(t,"project_id"),s.sampleLabels=s.formatSamplesLabels(t),s.addMeterToChart(s.tenant)})["catch"](function(e){s.error=e.data.detail})["finally"](function(){s.loader=!1})},this.showSamples()}]}}]).directive("ceilometerStats",function(){return{restrict:"E",scope:{name:"=name",tenant:"=tenant"},bindToController:!0,controllerAs:"vm",templateUrl:"templates/ceilometer-stats.tpl.html",controller:["$scope","Ceilometer",function(e,t){var n=this;this.getStats=function(e){n.loader=!0,t.getStats({tenant:e}).then(function(e){n.stats=e})["catch"](function(e){n.error=e.data})["finally"](function(){n.loader=!1})},e.$watch(function(){return n.name},function(e){e&&n.getStats(n.tenant)})}]}}),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.selectedSlice && !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.selectedSlice && 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 in vm.services | orderBy:\'-service\'"\n            template-url="templates/accordion-group.html"\n            is-open="vm.accordion.open[service.service]"\n            heading="{{service.service}}">\n            <h4>Slices:</h4>\n            <a ng-repeat="slice in service.slices" \n              ng-class="{active: slice.slice === vm.selectedSlice}"\n              ng-click="vm.loadSliceMeter(slice, service.service)"\n              href="#" class="list-group-item" >\n              {{slice.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        <!-- STATS -->\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" tenant="vm.selectedTenant"></ceilometer-stats>\n              </div>\n            </div>\n          </div>\n        </article>\n        <!-- METERS -->\n        <article ng-hide="vm.showStats" class="meters animate-slide-left">\n          <div class="alert alert-danger" ng-show="vm.ceilometerError">\n            {{vm.ceilometerError}}\n          </div>\n          <div class="col-sm-4 animate-slide-left" ng-hide="!vm.selectedSlice">\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-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.project_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 || meter.resource_id}}\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>\n        <a ng-click="order = \'category\'">Type:</a>\n      </th>\n      <th>\n        <a ng-click="order = \'resource_name\'">Resource:</a>\n      </th>\n      <th>\n        <a ng-click="order = \'meter\'">Meter:</a>\n      </th>\n      <th>\n        Unit:\n      </th>\n      <th>\n        Value:\n      </th>\n    </tr>\n    <!-- <tr>\n      <td>\n        <input type="text" ng-model="query.category">\n      </td>\n      <td>\n        <input type="text" ng-model="query.resource_name">\n      </td>\n      <td>\n        <input type="text" ng-model="query.meter">\n      </td>\n      <td>\n        <input type="text" ng-model="query.unit">\n      </td>\n      <td>\n        <input type="text" ng-model="query.value">\n      </td>\n    </tr> -->\n    <tr ng-repeat="item in vm.stats | orderBy:order">\n      <td>{{item.category}}</td>\n      <td>{{item.resource_name}}</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/model_autodeletion.py b/xos/model_autodeletion.py
index 2bfc48c..6eaf63c 100644
--- a/xos/model_autodeletion.py
+++ b/xos/model_autodeletion.py
@@ -1 +1 @@
-ephemeral_models = ['ReservedResource','Instance','Image','Network','Port','Tag','SitePrivilege','SliceMembership','SliceTag','Reservation','Slice']
+ephemeral_models = ['ReservedResource','Instance','Image','Network','Tag','SitePrivilege','SliceMembership','SliceTag','Reservation','Slice']
diff --git a/xos/services/ceilometer/models.py b/xos/services/ceilometer/models.py
index 42734de..2684097 100644
--- a/xos/services/ceilometer/models.py
+++ b/xos/services/ceilometer/models.py
@@ -35,6 +35,10 @@
         proxy = True
 
     KIND = CEILOMETER_KIND
+    LOOK_FOR_IMAGES=[ "trusty-server-multi-nic-docker", # CloudLab
+                      "trusty-server-multi-nic",
+                    ]
+
 
     sync_attributes = ("private_ip", "private_mac",
                        "ceilometer_ip", "ceilometer_mac",
diff --git a/xos/services/cord/admin.py b/xos/services/cord/admin.py
index 5f1a285..40e0f29 100644
--- a/xos/services/cord/admin.py
+++ b/xos/services/cord/admin.py
@@ -98,21 +98,27 @@
 # vCPE
 #-----------------------------------------------------------------------------
 
-class VCPEServiceForm(forms.ModelForm):
+class VSGServiceForm(forms.ModelForm):
     bbs_api_hostname = forms.CharField(required=False)
     bbs_api_port = forms.IntegerField(required=False)
     bbs_server = forms.CharField(required=False)
     backend_network_label = forms.CharField(required=False)
     bbs_slice = forms.ModelChoiceField(queryset=Slice.objects.all(), required=False)
+    wan_container_gateway_ip = forms.CharField(required=False)
+    wan_container_gateway_mac = forms.CharField(required=False)
+    wan_container_netbits = forms.CharField(required=False)
 
     def __init__(self,*args,**kwargs):
-        super (VCPEServiceForm,self ).__init__(*args,**kwargs)
+        super (VSGServiceForm,self ).__init__(*args,**kwargs)
         if self.instance:
             self.fields['bbs_api_hostname'].initial = self.instance.bbs_api_hostname
             self.fields['bbs_api_port'].initial = self.instance.bbs_api_port
             self.fields['bbs_server'].initial = self.instance.bbs_server
             self.fields['backend_network_label'].initial = self.instance.backend_network_label
             self.fields['bbs_slice'].initial = self.instance.bbs_slice
+            self.fields['wan_container_gateway_ip'].initial = self.instance.wan_container_gateway_ip
+            self.fields['wan_container_gateway_mac'].initial = self.instance.wan_container_gateway_mac
+            self.fields['wan_container_netbits'].initial = self.instance.wan_container_netbits
 
     def save(self, commit=True):
         self.instance.bbs_api_hostname = self.cleaned_data.get("bbs_api_hostname")
@@ -120,24 +126,27 @@
         self.instance.bbs_server = self.cleaned_data.get("bbs_server")
         self.instance.backend_network_label = self.cleaned_data.get("backend_network_label")
         self.instance.bbs_slice = self.cleaned_data.get("bbs_slice")
-        return super(VCPEServiceForm, self).save(commit=commit)
+        self.instance.wan_container_gateway_ip = self.cleaned_data.get("wan_container_gateway_ip")
+        self.instance.wan_container_gateway_mac = self.cleaned_data.get("wan_container_gateway_mac")
+        self.instance.wan_container_netbits = self.cleaned_data.get("wan_container_netbits")
+        return super(VSGServiceForm, self).save(commit=commit)
 
     class Meta:
-        model = VCPEService
+        model = VSGService
 
-class VCPEServiceAdmin(ReadOnlyAwareAdmin):
-    model = VCPEService
+class VSGServiceAdmin(ReadOnlyAwareAdmin):
+    model = VSGService
     verbose_name = "vCPE Service"
     verbose_name_plural = "vCPE Service"
     list_display = ("backend_status_icon", "name", "enabled")
     list_display_links = ('backend_status_icon', 'name', )
     fieldsets = [(None,             {'fields': ['backend_status_text', 'name','enabled','versionNumber', 'description', "view_url", "icon_url", "service_specific_attribute",],
                                      'classes':['suit-tab suit-tab-general']}),
-                 ("backend config", {'fields': [ "backend_network_label", "bbs_api_hostname", "bbs_api_port", "bbs_server", "bbs_slice"],
+                 ("backend config", {'fields': [ "backend_network_label", "bbs_api_hostname", "bbs_api_port", "bbs_server", "bbs_slice", "wan_container_gateway_ip", "wan_container_gateway_mac", "wan_container_netbits"],
                                      'classes':['suit-tab suit-tab-backend']}) ]
     readonly_fields = ('backend_status_text', "service_specific_attribute")
     inlines = [SliceInline,ServiceAttrAsTabInline,ServicePrivilegeInline]
-    form = VCPEServiceForm
+    form = VSGServiceForm
 
     extracontext_registered_admins = True
 
@@ -156,53 +165,57 @@
                            ) #('hpctools.html', 'top', 'tools') )
 
     def queryset(self, request):
-        return VCPEService.get_service_objects_by_user(request.user)
+        return VSGService.get_service_objects_by_user(request.user)
 
-class VCPETenantForm(forms.ModelForm):
+class VSGTenantForm(forms.ModelForm):
     bbs_account = forms.CharField(required=False)
     creator = forms.ModelChoiceField(queryset=User.objects.all())
     instance = forms.ModelChoiceField(queryset=Instance.objects.all(),required=False)
     last_ansible_hash = forms.CharField(required=False)
+    wan_container_ip = forms.CharField(required=False)
+    wan_container_mac = forms.CharField(required=False)
 
     def __init__(self,*args,**kwargs):
-        super (VCPETenantForm,self ).__init__(*args,**kwargs)
+        super (VSGTenantForm,self ).__init__(*args,**kwargs)
         self.fields['kind'].widget.attrs['readonly'] = True
-        self.fields['provider_service'].queryset = VCPEService.get_service_objects().all()
+        self.fields['provider_service'].queryset = VSGService.get_service_objects().all()
         if self.instance:
             # fields for the attributes
             self.fields['bbs_account'].initial = self.instance.bbs_account
             self.fields['creator'].initial = self.instance.creator
             self.fields['instance'].initial = self.instance.instance
             self.fields['last_ansible_hash'].initial = self.instance.last_ansible_hash
+            self.fields['wan_container_ip'].initial = self.instance.wan_container_ip
+            self.fields['wan_container_mac'].initial = self.instance.wan_container_mac
         if (not self.instance) or (not self.instance.pk):
             # default fields for an 'add' form
             self.fields['kind'].initial = VCPE_KIND
             self.fields['creator'].initial = get_request().user
-            if VCPEService.get_service_objects().exists():
-               self.fields["provider_service"].initial = VCPEService.get_service_objects().all()[0]
+            if VSGService.get_service_objects().exists():
+               self.fields["provider_service"].initial = VSGService.get_service_objects().all()[0]
 
     def save(self, commit=True):
         self.instance.creator = self.cleaned_data.get("creator")
         self.instance.instance = self.cleaned_data.get("instance")
         self.instance.last_ansible_hash = self.cleaned_data.get("last_ansible_hash")
-        return super(VCPETenantForm, self).save(commit=commit)
+        return super(VSGTenantForm, self).save(commit=commit)
 
     class Meta:
-        model = VCPETenant
+        model = VSGTenant
 
-class VCPETenantAdmin(ReadOnlyAwareAdmin):
+class VSGTenantAdmin(ReadOnlyAwareAdmin):
     list_display = ('backend_status_icon', 'id', 'subscriber_tenant' )
     list_display_links = ('backend_status_icon', 'id')
     fieldsets = [ (None, {'fields': ['backend_status_text', 'kind', 'provider_service', 'subscriber_tenant', 'service_specific_id', # 'service_specific_attribute',
-                                     'bbs_account', 'creator', 'instance', 'last_ansible_hash'],
+                                     'wan_container_ip', 'wan_container_mac', 'bbs_account', 'creator', 'instance', 'last_ansible_hash'],
                           'classes':['suit-tab suit-tab-general']})]
-    readonly_fields = ('backend_status_text', 'service_specific_attribute', 'bbs_account')
-    form = VCPETenantForm
+    readonly_fields = ('backend_status_text', 'service_specific_attribute', 'bbs_account', 'wan_container_ip', 'wan_container_mac')
+    form = VSGTenantForm
 
     suit_form_tabs = (('general','Details'),)
 
     def queryset(self, request):
-        return VCPETenant.get_tenant_objects_by_user(request.user)
+        return VSGTenant.get_tenant_objects_by_user(request.user)
 
 #-----------------------------------------------------------------------------
 # vBNG
@@ -362,8 +375,8 @@
 
 admin.site.register(VOLTService, VOLTServiceAdmin)
 admin.site.register(VOLTTenant, VOLTTenantAdmin)
-admin.site.register(VCPEService, VCPEServiceAdmin)
-admin.site.register(VCPETenant, VCPETenantAdmin)
+admin.site.register(VSGService, VSGServiceAdmin)
+admin.site.register(VSGTenant, VSGTenantAdmin)
 admin.site.register(VBNGService, VBNGServiceAdmin)
 admin.site.register(VBNGTenant, VBNGTenantAdmin)
 admin.site.register(CordSubscriberRoot, CordSubscriberRootAdmin)
diff --git a/xos/services/cord/models.py b/xos/services/cord/models.py
index bf20e86..959bf19 100644
--- a/xos/services/cord/models.py
+++ b/xos/services/cord/models.py
@@ -1,5 +1,5 @@
 from django.db import models
-from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber, NetworkParameter, NetworkParameterType, Port
+from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber, NetworkParameter, NetworkParameterType, Port, AddressPool
 from core.models.plcorebase import StrippedCharField
 import os
 from django.db import models, transaction
@@ -10,44 +10,7 @@
 from core.models.service import LeastLoadedNodeScheduler
 import traceback
 from xos.exceptions import *
-
-"""
-import os
-import sys
-sys.path.append("/opt/xos")
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
-import django
-from core.models import *
-from services.hpc.models import *
-from services.cord.models import *
-django.setup()
-
-t = VOLTTenant()
-t.caller = User.objects.all()[0]
-t.save()
-
-for v in VOLTTenant.get_tenant_objects().all():
-    v.caller = User.objects.all()[0]
-    v.delete()
-
-for v in VCPETenant.get_tenant_objects().all():
-    v.caller = User.objects.all()[0]
-    v.delete()
-
-for v in VOLTTenant.get_tenant_objects().all():
-    v.caller = User.objects.all()[0]
-    v.delete()
-
-for v in VOLTTenant.get_tenant_objects().all():
-    if not v.creator:
-        v.creator= User.objects.all()[0]
-        v.save()
-
-for v in VCPETenant.get_tenant_objects().all():
-    if not v.creator:
-        v.creator= User.objects.all()[0]
-        v.save()
-"""
+from xos.config import Config
 
 class ConfigurationError(Exception):
     pass
@@ -57,6 +20,8 @@
 VBNG_KIND = "vBNG"
 CORD_SUBSCRIBER_KIND = "CordSubscriberRoot"
 
+CORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
+
 # -------------------------------------------
 # CordSubscriberRoot
 # -------------------------------------------
@@ -292,7 +257,7 @@
 
     @property
     def vcpe(self):
-        vcpe = self.get_newest_subscribed_tenant(VCPETenant)
+        vcpe = self.get_newest_subscribed_tenant(VSGTenant)
         if not vcpe:
             return None
 
@@ -346,11 +311,11 @@
             return
 
         if self.vcpe is None:
-            vcpeServices = VCPEService.get_service_objects().all()
-            if not vcpeServices:
-                raise XOSConfigurationError("No VCPE Services available")
+            vsgServices = VSGService.get_service_objects().all()
+            if not vsgServices:
+                raise XOSConfigurationError("No VSG Services available")
 
-            vcpe = VCPETenant(provider_service = vcpeServices[0],
+            vcpe = VSGTenant(provider_service = vsgServices[0],
                               subscriber_tenant = self)
             vcpe.caller = self.creator
             vcpe.save()
@@ -382,7 +347,7 @@
     def cleanup_orphans(self):
         # ensure vOLT only has one vCPE
         cur_vcpe = self.vcpe
-        for vcpe in list(self.get_subscribed_tenants(VCPETenant)):
+        for vcpe in list(self.get_subscribed_tenants(VSGTenant)):
             if (not cur_vcpe) or (vcpe.id != cur_vcpe.id):
                 # print "XXX clean up orphaned vcpe", vcpe
                 vcpe.delete()
@@ -428,24 +393,27 @@
 # VCPE
 # -------------------------------------------
 
-class VCPEService(Service):
+class VSGService(Service):
     KIND = VCPE_KIND
 
     simple_attributes = ( ("bbs_api_hostname", None),
                           ("bbs_api_port", None),
                           ("bbs_server", None),
-                          ("backend_network_label", "hpc_client"), )
+                          ("backend_network_label", "hpc_client"),
+                          ("wan_container_gateway_ip", ""),
+                          ("wan_container_gateway_mac", ""),
+                          ("wan_container_netbits", "24") )
 
     def __init__(self, *args, **kwargs):
-        super(VCPEService, self).__init__(*args, **kwargs)
+        super(VSGService, self).__init__(*args, **kwargs)
 
     class Meta:
         app_label = "cord"
-        verbose_name = "vCPE Service"
+        verbose_name = "vSG Service"
         proxy = True
 
     def allocate_bbs_account(self):
-        vcpes = VCPETenant.get_tenant_objects().all()
+        vcpes = VSGTenant.get_tenant_objects().all()
         bbs_accounts = [vcpe.bbs_account for vcpe in vcpes]
 
         # There's a bit of a race here; some other user could be trying to
@@ -474,7 +442,7 @@
             value = value.id
         self.set_attribute("bbs_slice_id", value)
 
-VCPEService.setup_simple_attributes()
+VSGService.setup_simple_attributes()
 
 #class STagBlock(PlCoreBase):
 #    instance = models.ForeignKey(Instance, related_name="s_tags")
@@ -483,7 +451,7 @@
 #
 #    def __unicode__(self): return u'%s' % (self.s_tag)
 
-class VCPETenant(TenantWithContainer):
+class VSGTenant(TenantWithContainer):
     class Meta:
         proxy = True
 
@@ -491,7 +459,8 @@
 
     sync_attributes = ("nat_ip", "nat_mac",
                        "lan_ip", "lan_mac",
-                       "wan_ip", "wan_mac", "wan_container_mac",
+                       "wan_ip", "wan_mac",
+                       "wan_container_ip", "wan_container_mac",
                        "private_ip", "private_mac",
                        "hpc_client_ip", "hpc_client_mac")
 
@@ -499,10 +468,11 @@
                           "container_id": None,
                           "users": [],
                           "bbs_account": None,
-                          "last_ansible_hash": None}
+                          "last_ansible_hash": None,
+                          "wan_container_ip": None}
 
     def __init__(self, *args, **kwargs):
-        super(VCPETenant, self).__init__(*args, **kwargs)
+        super(VSGTenant, self).__init__(*args, **kwargs)
         self.cached_vbng=None
 
     @property
@@ -582,6 +552,10 @@
                 addresses["hpc_client"] = (ns.ip, ns.mac)
         return addresses
 
+    # ------------------------------------------------------------------------
+    # The following IP addresses all come from the VM
+    # Note: They might not be useful for the VTN-vSG
+
     @property
     def nat_ip(self):
         return self.addresses.get("nat", (None,None) )[0]
@@ -606,11 +580,37 @@
     def wan_mac(self):
         return self.addresses.get("wan", (None, None) )[1]
 
+    # end of VM IP address stubs
+    # ------------------------------------------------------------------------
+
+    @property
+    def wan_container_ip(self):
+        if CORD_USE_VTN:
+            # When using VTN, wan_container_ip is stored and maintained inside
+            # of the vSG object.
+            return self.get_attribute("wan_container_ip", self.default_attributes["wan_container_ip"])
+        else:
+            # When not using VTN, wan_container_ip is the same as wan_ip.
+            # XXX Is this broken for multiple-containers-per-VM?
+            return self.wan_ip
+
+    @wan_container_ip.setter
+    def wan_container_ip(self, value):
+        if CORD_USE_VTN:
+            self.set_attribute("wan_container_ip", value)
+        else:
+            raise Exception("wan_container_ip.setter called on non-VTN CORD")
+
+    def ip_to_mac(self, ip):
+        (a, b, c, d) = ip.split('.')
+        return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
+
     # Generate the MAC for the container interface connected to WAN
     @property
     def wan_container_mac(self):
-        (a, b, c, d) = self.wan_ip.split('.')
-        return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
+        if not self.wan_container_ip:
+            return None
+        return self.ip_to_mac(self.wan_container_ip)
 
     @property
     def private_ip(self):
@@ -712,6 +712,7 @@
                         flavor = flavors[0],
                         isolation = slice.default_isolation,
                         parent = parent)
+
         self.save_instance(instance)
 
         return instance
@@ -726,19 +727,23 @@
         # provides us
         slice = self.get_slice()
         if slice.default_isolation in ["container_vm", "container"]:
-            super(VCPETenant,self).manage_container()
+            super(VSGTenant,self).manage_container()
             return
 
         if not self.volt:
             raise XOSConfigurationError("This vCPE container has no volt")
 
+        if self.instance:
+            # We're good.
+            return
+
         instance = self.find_or_make_instance_for_s_tag(self.volt.s_tag)
         self.instance = instance
         super(TenantWithContainer, self).save()
 
     def cleanup_container(self):
         if self.get_slice().default_isolation in ["container_vm", "container"]:
-            super(VCPETenant,self).cleanup_container()
+            super(VSGTenant,self).cleanup_container()
 
         # To-do: cleanup unused instances
         pass
@@ -749,14 +754,41 @@
 
         if self.volt and self.volt.subscriber and self.volt.subscriber.url_filter_enable:
             if not self.bbs_account:
-                # make sure we use the proxied VCPEService object, not the generic Service object
-                vcpe_service = VCPEService.objects.get(id=self.provider_service.id)
+                # make sure we use the proxied VSGService object, not the generic Service object
+                vcpe_service = VSGService.objects.get(id=self.provider_service.id)
                 self.bbs_account = vcpe_service.allocate_bbs_account()
-                super(VCPETenant, self).save()
+                super(VSGTenant, self).save()
         else:
             if self.bbs_account:
                 self.bbs_account = None
-                super(VCPETenant, self).save()
+                super(VSGTenant, self).save()
+
+    def get_wan_address_from_pool(self):
+        ap = AddressPool.objects.filter(name="public_addresses")
+        if not ap:
+            raise Exception("AddressPool 'public_addresses' does not exist. Please configure it.")
+        ap = ap[0]
+
+        addr = ap.get_address()
+        if not addr:
+            raise Exception("AddressPool 'public_addresses' has run out of addresses.")
+        return addr
+
+    def put_wan_address_to_pool(self, addr):
+        AddressPool.objects.filter(name="public_addresses")[0].put_address(addr)
+
+    def manage_wan_container_ip(self):
+        if CORD_USE_VTN:
+            if not self.wan_container_ip:
+                addr = self.get_wan_address_from_pool()
+
+                self.wan_container_ip = addr
+                super(TenantWithContainer, self).save()
+
+    def cleanup_wan_container_ip(self):
+        if CORD_USE_VTN and self.wan_container_ip:
+            self.put_wan_address_to_pool(self.wan_container_ip)
+            self.wan_container_ip = None
 
     def find_or_make_port(self, instance, network, **kwargs):
         port = Port.objects.filter(instance=instance, network=network)
@@ -767,16 +799,27 @@
             port.save()
         return port
 
+    def get_lan_network(self, instance):
+        slice = self.provider_service.slices.all()[0]
+        if CORD_USE_VTN:
+            # there should only be one network private network, and its template should not be the management template
+            lan_networks = [x for x in slice.networks.all() if x.template.visibility=="private" and (not "management" in x.template.name)]
+            if len(lan_networks)>1:
+                raise XOSProgrammingError("The vSG slice should only have one non-management private network")
+        else:
+            lan_networks = [x for x in slice.networks.all() if "lan" in x.name]
+        if not lan_networks:
+            raise XOSProgrammingError("No lan_network")
+        return lan_networks[0]
+
     def save_instance(self, instance):
         with transaction.atomic():
             instance.volumes = "/etc/dnsmasq.d,/etc/ufw"
-            super(VCPETenant, self).save_instance(instance)
+            super(VSGTenant, self).save_instance(instance)
 
             if instance.isolation in ["container", "container_vm"]:
-                lan_networks = [x for x in instance.slice.networks.all() if "lan" in x.name]
-                if not lan_networks:
-                    raise XOSProgrammingError("No lan_network")
-                port = self.find_or_make_port(instance, lan_networks[0], ip="192.168.0.1", port_id="unmanaged")
+                lan_network = self.get_lan_network(instance)
+                port = self.find_or_make_port(instance, lan_network, ip="192.168.0.1", port_id="unmanaged")
                 port.set_parameter("c_tag", self.volt.c_tag)
                 port.set_parameter("s_tag", self.volt.s_tag)
                 port.set_parameter("device", "eth1")
@@ -789,6 +832,14 @@
                 port.set_parameter("next_hop", value="10.0.1.253")   # FIX ME
                 port.set_parameter("device", "eth0")
 
+            if instance.isolation in ["vm"]:
+                lan_network = self.get_lan_network(instance)
+                port = self.find_or_make_port(instance, lan_network)
+                port.set_parameter("c_tag", self.volt.c_tag)
+                port.set_parameter("s_tag", self.volt.s_tag)
+                port.set_parameter("neutron_port_name", "stag-%s" % self.volt.s_tag)
+                port.save()
+
             # tag the instance with the s-tag, so we can easily find the
             # instance later
             if self.volt and self.volt.s_tag:
@@ -797,34 +848,41 @@
                     tag = Tag(service=self.provider_service, content_object=instance, name="s_tag", value=self.volt.s_tag)
                     tag.save()
 
+            # VTN-CORD needs a WAN address for the VM, so that the VM can
+            # be configured.
+            if CORD_USE_VTN:
+                tags = Tag.select_by_content_object(instance).filter(name="vm_wan_addr")
+                if not tags:
+                    address = self.get_wan_address_from_pool()
+                    tag = Tag(service=self.provider_service, content_object=instance, name="vm_wan_addr", value="%s,%s,%s" % ("public_addresses", address, self.ip_to_mac(address)))
+                    tag.save()
+
     def save(self, *args, **kwargs):
         if not self.creator:
             if not getattr(self, "caller", None):
                 # caller must be set when creating a vCPE since it creates a slice
-                raise XOSProgrammingError("VCPETenant's self.caller was not set")
+                raise XOSProgrammingError("VSGTenant's self.caller was not set")
             self.creator = self.caller
             if not self.creator:
-                raise XOSProgrammingError("VCPETenant's self.creator was not set")
+                raise XOSProgrammingError("VSGTenant's self.creator was not set")
 
-        super(VCPETenant, self).save(*args, **kwargs)
+        super(VSGTenant, self).save(*args, **kwargs)
         model_policy_vcpe(self.pk)
-        #self.manage_instance()
-        #self.manage_vbng()
-        #self.manage_bbs_account()
-        #self.cleanup_orphans()
 
     def delete(self, *args, **kwargs):
         self.cleanup_vbng()
         self.cleanup_container()
-        super(VCPETenant, self).delete(*args, **kwargs)
+        self.cleanup_wan_container_ip()
+        super(VSGTenant, self).delete(*args, **kwargs)
 
 def model_policy_vcpe(pk):
     # TODO: this should be made in to a real model_policy
     with transaction.atomic():
-        vcpe = VCPETenant.objects.select_for_update().filter(pk=pk)
+        vcpe = VSGTenant.objects.select_for_update().filter(pk=pk)
         if not vcpe:
             return
         vcpe = vcpe[0]
+        vcpe.manage_wan_container_ip()
         vcpe.manage_container()
         vcpe.manage_vbng()
         vcpe.manage_bbs_account()
diff --git a/xos/services/cord/templates/vcpeadmin.html b/xos/services/cord/templates/vcpeadmin.html
index a21dabe..c93f032 100644
--- a/xos/services/cord/templates/vcpeadmin.html
+++ b/xos/services/cord/templates/vcpeadmin.html
@@ -1,6 +1,6 @@
 <div class = "row text-center">
     <div class="col-xs-6">
-        <a class="btn btn-primary" href="/admin/cord/vcpetenant/">vCPE Tenants</a>
+        <a class="btn btn-primary" href="/admin/cord/vsgtenant/">vSG Tenants</a>
     </div>
     <div class="col-xs-6">
         <a class="btn btn-primary" href="/admin/dashboard/cord/">Subscriber View</a>
diff --git a/xos/services/onos/admin.py b/xos/services/onos/admin.py
index 3f9f96c..fb0f1d7 100644
--- a/xos/services/onos/admin.py
+++ b/xos/services/onos/admin.py
@@ -19,16 +19,28 @@
 from django.contrib.admin.utils import quote
 
 class ONOSServiceForm(forms.ModelForm):
-    use_external_host = forms.CharField(required=False)
+    rest_hostname = forms.CharField(required=False)
+    rest_port = forms.CharField(required=False)
+    no_container = forms.BooleanField(required=False)
+#    external_hostname = forms.CharField(required=False)
+#    external_container = forms.CharField(required=False)
 
     def __init__(self,*args,**kwargs):
         super (ONOSServiceForm,self ).__init__(*args,**kwargs)
         if self.instance:
             # fields for the attributes
-            self.fields['use_external_host'].initial = self.instance.use_external_host
+            self.fields['rest_hostname'].initial = self.instance.rest_hostname
+            self.fields['rest_port'].initial = self.instance.rest_port
+            self.fields['no_container'].initial = self.instance.no_container
+#            self.fields['external_hostname'].initial = self.instance.external_hostname
+#            self.fields['external_container'].initial = self.instance.external_hostname
 
     def save(self, commit=True):
-        self.instance.use_external_host = self.cleaned_data.get("use_external_host")
+        self.instance.rest_hostname = self.cleaned_data.get("rest_hostname")
+        self.instance.rest_port = self.cleaned_data.get("rest_port")
+        self.instance.no_container = self.cleaned_data.get("no_container")
+#        self.instance.external_hostname = self.cleaned_data.get("external_hostname")
+#        self.instance.external_container = self.cleaned_data.get("external_container")
         return super(ONOSServiceForm, self).save(commit=commit)
 
     class Meta:
@@ -40,7 +52,7 @@
     verbose_name_plural = "ONOS Services"
     list_display = ("backend_status_icon", "name", "enabled")
     list_display_links = ('backend_status_icon', 'name', )
-    fieldsets = [(None, {'fields': ['backend_status_text', 'name','enabled','versionNumber', 'description',"view_url","icon_url", "use_external_host" ], 'classes':['suit-tab suit-tab-general']})]
+    fieldsets = [(None, {'fields': ['backend_status_text', 'name','enabled','versionNumber', 'description',"view_url","icon_url", "rest_hostname", "rest_port", "no_container" ], 'classes':['suit-tab suit-tab-general']})]
     readonly_fields = ('backend_status_text', )
     inlines = [SliceInline,ServiceAttrAsTabInline,ServicePrivilegeInline]
     form = ONOSServiceForm
diff --git a/xos/services/onos/models.py b/xos/services/onos/models.py
index 1e869d1..20fa73f 100644
--- a/xos/services/onos/models.py
+++ b/xos/services/onos/models.py
@@ -21,15 +21,43 @@
         verbose_name = "ONOS Service"
         proxy = True
 
-    default_attributes = {"use_external_host": ""}
+    default_attributes = {"rest_hostname": "",
+                          "rest_port": "8181",
+                          "no_container": False,
+                          "node_key": ""}
 
     @property
-    def use_external_host(self):
-        return self.get_attribute("use_external_host", self.default_attributes["use_external_host"])
+    def rest_hostname(self):
+        return self.get_attribute("rest_hostname", self.default_attributes["rest_hostname"])
 
-    @use_external_host.setter
-    def use_external_host(self, value):
-        self.set_attribute("use_external_host", value)
+    @rest_hostname.setter
+    def rest_hostname(self, value):
+        self.set_attribute("rest_hostname", value)
+
+    @property
+    def rest_port(self):
+        return self.get_attribute("rest_port", self.default_attributes["rest_port"])
+
+    @rest_port.setter
+    def rest_port(self, value):
+        self.set_attribute("rest_port", value)
+
+    @property
+    def no_container(self):
+        return self.get_attribute("no_container", self.default_attributes["no_container"])
+
+    @no_container.setter
+    def no_container(self, value):
+        self.set_attribute("no_container", value)
+
+    @property
+    def node_key(self):
+        return self.get_attribute("node_key", self.default_attributes["node_key"])
+
+    @node_key.setter
+    def node_key(self, value):
+        self.set_attribute("node_key", value)
+
 
 class ONOSApp(Tenant):   # aka 'ONOSTenant'
     class Meta:
@@ -93,19 +121,6 @@
     def install_dependencies(self, value):
         self.set_attribute("install_dependencies", value)
 
-    #@property
-    #def instance(self):
-    #    instance_id = self.get_attribute("instance_id", self.default_attributes["instance_id"])
-    #    if instance_id:
-    #        instances = Instance.objects.filter(id=instance_id)
-    #        if instances:
-    #            return instances[0]
-    #    return None
-
-    #@instance.setter
-    #def instance(self, value):
-    #    self.set_attribute("instance_id", value.id)
-
     def save(self, *args, **kwargs):
         if not self.creator:
             if not getattr(self, "caller", None):
diff --git a/xos/synchronizers/base/SyncInstanceUsingAnsible.py b/xos/synchronizers/base/SyncInstanceUsingAnsible.py
index 335932f..04b98df 100644
--- a/xos/synchronizers/base/SyncInstanceUsingAnsible.py
+++ b/xos/synchronizers/base/SyncInstanceUsingAnsible.py
@@ -15,10 +15,10 @@
 
 class SyncInstanceUsingAnsible(SyncStep):
     # All of the following should be defined for classes derived from this
-    # base class. Examples below use VCPETenant.
+    # base class. Examples below use VSGTenant.
 
-    # provides=[VCPETenant]
-    # observes=VCPETenant
+    # provides=[VSGTenant]
+    # observes=VSGTenant
     # requested_interval=0
     # template_name = "sync_vcpetenant.yaml"
     # service_key_name = "/opt/xos/observers/vcpe/vcpe_private_key"
@@ -26,6 +26,12 @@
     def __init__(self, **args):
         SyncStep.__init__(self, **args)
 
+    def skip_ansible_fields(self, o):
+        # Return True if the instance processing and get_ansible_fields stuff
+        # should be skipped. This hook is primarily for the OnosApp
+        # sync step, so it can do its external REST API sync thing.
+        return False
+
     def defer_sync(self, o, reason):
         logger.info("defer object %s due to %s" % (str(o), reason))
         raise Exception("defer object %s due to %s" % (str(o), reason))
@@ -44,6 +50,14 @@
 
         return o.instance
 
+    def get_external_sync(self, o):
+        hostname = getattr(o, "external_hostname", None)
+        container = getattr(o, "external_container", None)
+        if hostname and container:
+            return (hostname, container)
+        else:
+            return None
+
     def run_playbook(self, o, fields, template_name=None):
         if not template_name:
             template_name = self.template_name
@@ -80,6 +94,7 @@
                        "hostname": instance.node.name,
                        "instance_id": instance.instance_id,
                        "username": "ubuntu",
+                       "ssh_ip": instance.get_ssh_ip(),
                      }
             key_name = self.service_key_name
         elif (instance.isolation == "container"):
@@ -91,6 +106,7 @@
                        "instance_name": "rootcontext",
                        "username": "root",
                        "container_name": "%s-%s" % (instance.slice.name, str(instance.id))
+                       # ssh_ip is not used for container-on-metal
                      }
             key_name = self.get_node_key(node)
         else:
@@ -107,7 +123,7 @@
                        "instance_name": instance.parent.name,
                        "instance_id": instance.parent.instance_id,
                        "username": "ubuntu",
-                       "nat_ip": instance.parent.get_ssh_ip(),
+                       "ssh_ip": instance.parent.get_ssh_ip(),
                        "container_name": "%s-%s" % (instance.slice.name, str(instance.id))
                          }
             key_name = instance.parent.slice.service.private_key_fn
@@ -131,9 +147,9 @@
 
         fields.update({"keystone_tenant_id": cslice.tenant_id,
                        "keystone_user_id": cuser.kuser_id,
-                       "rabbit_user": instance.controller.rabbit_user,
-                       "rabbit_password": instance.controller.rabbit_password,
-                       "rabbit_host": instance.controller.rabbit_host})
+                       "rabbit_user": getattr(instance.controller,"rabbit_user", None),
+                       "rabbit_password": getattr(instance.controller, "rabbit_password", None),
+                       "rabbit_host": getattr(instance.controller, "rabbit_host", None)})
 
         return fields
 
@@ -142,30 +158,43 @@
 
         self.prepare_record(o)
 
-        instance = self.get_instance(o)
-
-        if isinstance(instance, basestring):
-            # sync to some external host
-
-            # XXX - this probably needs more work...
-
-            fields = { "hostname": instance,
-                       "instance_id": "ubuntu",     # this is the username to log into
-                       "private_key": service.key,
-                     }
+        if self.skip_ansible_fields(o):
+            fields = {}
         else:
-            # sync to an XOS instance
-            if not instance:
-                self.defer_sync(o, "waiting on instance")
-                return
+            if self.get_external_sync(o):
+                # sync to some external host
 
-            if not instance.instance_name:
-                self.defer_sync(o, "waiting on instance.instance_name")
-                return
+                # UNTESTED
 
-            fields = self.get_ansible_fields(instance)
+                (hostname, container_name) = self.get_external_sync(o)
+                fields = { "hostname": hostname,
+                           "baremetal_ssh": True,
+                           "instance_name": "rootcontext",
+                           "username": "root",
+                           "container_name": container_name
+                         }
+                key_name = self.get_node_key(node)
+                if not os.path.exists(key_name):
+                    raise Exception("Node key %s does not exist" % key_name)
 
-            fields["ansible_tag"] =  o.__class__.__name__ + "_" + str(o.id)
+                key = file(key_name).read()
+
+                fields["private_key"] = key
+                # TO DO: Ceilometer stuff
+            else:
+                instance = self.get_instance(o)
+                # sync to an XOS instance
+                if not instance:
+                    self.defer_sync(o, "waiting on instance")
+                    return
+
+                if not instance.instance_name:
+                    self.defer_sync(o, "waiting on instance.instance_name")
+                    return
+
+                fields = self.get_ansible_fields(instance)
+
+        fields["ansible_tag"] =  o.__class__.__name__ + "_" + str(o.id)
 
         # If 'o' defines a 'sync_attributes' list, then we'll copy those
         # attributes into the Ansible recipe's field list automatically.
diff --git a/xos/synchronizers/base/SyncSliverUsingAnsible.py b/xos/synchronizers/base/SyncSliverUsingAnsible.py
deleted file mode 100644
index c64e5ea..0000000
--- a/xos/synchronizers/base/SyncSliverUsingAnsible.py
+++ /dev/null
@@ -1,95 +0,0 @@
-import hashlib
-import os
-import socket
-import sys
-import base64
-import time
-from django.db.models import F, Q
-from xos.config import Config
-from synchronizers.base.syncstep import SyncStep
-from synchronizers.base.ansible import run_template_ssh
-from core.models import Service, Slice
-from xos.logger import Logger, logging
-
-logger = Logger(level=logging.INFO)
-
-class SyncInstanceUsingAnsible(SyncStep):
-    # All of the following should be defined for classes derived from this
-    # base class. Examples below use VCPETenant.
-
-    # provides=[VCPETenant]
-    # observes=VCPETenant
-    # requested_interval=0
-    # template_name = "sync_vcpetenant.yaml"
-    # service_key_name = "/opt/xos/observers/vcpe/vcpe_private_key"
-
-    def __init__(self, **args):
-        SyncStep.__init__(self, **args)
-
-    def defer_sync(self, o, reason):
-        logger.info("defer object %s due to %s" % (str(o), reason))
-        raise Exception("defer object %s due to %s" % (str(o), reason))
-
-    def get_extra_attributes(self, o):
-        # This is a place to include extra attributes that aren't part of the
-        # object itself.
-
-        return {}
-
-    def get_instance(self, o):
-        # We need to know what instance is associated with the object. Let's
-        # assume 'o' has a field called 'instance'. If the field is called
-        # something else, or if custom logic is needed, then override this
-        # method.
-
-        return o.instance
-
-    def run_playbook(self, o, fields):
-        tStart = time.time()
-        run_template_ssh(self.template_name, fields)
-        logger.info("playbook execution time %d" % int(time.time()-tStart))
-
-    def pre_sync_hook(self, o, fields):
-        pass
-
-    def post_sync_hook(self, o, fields):
-        pass
-
-    def sync_fields(self, o, fields):
-        self.run_playbook(o, fields)
-
-    def sync_record(self, o):
-        logger.info("sync'ing object %s" % str(o))
-
-        instance = self.get_instance(o)
-        if not instance:
-            self.defer_sync(o, "waiting on instance")
-            return
-
-        if not os.path.exists(self.service_key_name):
-            raise Exception("Service key %s does not exist" % self.service_key_name)
-
-        service_key = file(self.service_key_name).read()
-
-        fields = { "instance_name": instance.name,
-                   "hostname": instance.node.name,
-                   "instance_id": instance.instance_id,
-                   "private_key": service_key,
-                   "ansible_tag": "vcpe_tenant_" + str(o.id)
-                 }
-
-        # If 'o' defines a 'sync_attributes' list, then we'll copy those
-        # attributes into the Ansible recipe's field list automatically.
-        if hasattr(o, "sync_attributes"):
-            for attribute_name in o.sync_attributes:
-                fields[attribute_name] = getattr(o, attribute_name)
-
-        fields.update(self.get_extra_attributes(o))
-
-        self.sync_fields(o, fields)
-
-        o.save()
-
-    def delete_record(self, m):
-        pass
-
diff --git a/xos/synchronizers/base/ansible.py b/xos/synchronizers/base/ansible.py
index d2dca3b..d92835a 100644
--- a/xos/synchronizers/base/ansible.py
+++ b/xos/synchronizers/base/ansible.py
@@ -34,11 +34,20 @@
         if (l.startswith(magic_str)):
             w = len(magic_str)
             str = l[w:]
+
+            # handle ok: [127.0.0.1] => (item=org.onosproject.driver) => {...
+            if str.startswith("(") and (" => {" in str):
+                str = str.split("=> ",1)[1]
+
             d = json.loads(str)
             results.append(d)
         elif (l.startswith(magic_str2)):
             w = len(magic_str2)
             str = l[w:]
+
+            if str.startswith("(") and (" => {" in str):
+                str = str.split("=> ",1)[1]
+
             d = json.loads(str)
             results.append(d)
 
@@ -149,12 +158,12 @@
     private_key = opts["private_key"]
     baremetal_ssh = opts.get("baremetal_ssh",False)
     if baremetal_ssh:
-        # no instance_id or nat_ip for baremetal
+        # no instance_id or ssh_ip for baremetal
         # we never proxy to baremetal
         proxy_ssh = False
     else:
         instance_id = opts["instance_id"]
-        nat_ip = opts["nat_ip"]
+        ssh_ip = opts["ssh_ip"]
         try:
             proxy_ssh = Config().observer_proxy_ssh
         except:
@@ -172,7 +181,15 @@
     f = open(config_pathname, "w")
     f.write("[ssh_connection]\n")
     if proxy_ssh:
-        proxy_command = "ProxyCommand ssh -q -i %s -o StrictHostKeyChecking=no %s@%s" % (private_key_pathname, instance_id, hostname)
+        proxy_ssh_key = getattr(Config(), "observer_proxy_ssh_key", None)
+        proxy_ssh_user = getattr(Config(), "observer_proxy_ssh_user", "root")
+        if proxy_ssh_key:
+            # If proxy_ssh_key is known, then we can proxy into the compute
+            # node without needing to have the OpenCloud sshd machinery in
+            # place.
+            proxy_command = "ProxyCommand ssh -q -i %s -o StrictHostKeyChecking=no %s@%s nc %s 22" % (proxy_ssh_key, proxy_ssh_user, hostname, ssh_ip)
+        else:
+            proxy_command = "ProxyCommand ssh -q -i %s -o StrictHostKeyChecking=no %s@%s" % (private_key_pathname, instance_id, hostname)
         f.write('ssh_args = -o "%s"\n' % proxy_command)
     f.write('scp_if_ssh = True\n')
     f.write('pipelining = True\n')
@@ -186,7 +203,7 @@
         f.write("%s ansible_ssh_private_key_file=%s\n" % (hostname, private_key_pathname))
     else:
         # acb: Login user is hardcoded, this is not great
-        f.write("%s ansible_ssh_private_key_file=%s ansible_ssh_user=ubuntu\n" % (nat_ip, private_key_pathname))
+        f.write("%s ansible_ssh_private_key_file=%s ansible_ssh_user=ubuntu\n" % (ssh_ip, private_key_pathname))
     f.close()
 
     # SSH will complain if private key is world or group readable
diff --git a/xos/synchronizers/base/event_loop.py b/xos/synchronizers/base/event_loop.py
index 6cfc9f6..c1b9cda 100644
--- a/xos/synchronizers/base/event_loop.py
+++ b/xos/synchronizers/base/event_loop.py
@@ -421,7 +421,7 @@
 				except Exception,e:
                         		self.consolePrint(bcolors.FAIL + "Model step %r failed" % (sync_step.__name__) + bcolors.ENDC)
 					logger.error('Model step %r failed. This seems like a misconfiguration or bug: %r. This error will not be relayed to the user!' % (sync_step.__name__, e))
-					logger.log_exc(e)
+					logger.log_exc("Exception in sync step")
 					self.failed_steps.append(S)
 					my_status = STEP_STATUS_KO
 			else:
diff --git a/xos/synchronizers/base/syncstep.py b/xos/synchronizers/base/syncstep.py
index bdab8f3..54c4b89 100644
--- a/xos/synchronizers/base/syncstep.py
+++ b/xos/synchronizers/base/syncstep.py
@@ -142,10 +142,10 @@
     def sync_record(self, o):
         try:
             controller = o.get_controller()
-            controller_register = json.loads(o.node.site_deployment.controller.backend_register)
+            controller_register = json.loads(controller.backend_register)
 
             if (controller_register.get('disabled',False)):
-                raise InnocuousException('Controller %s is disabled'%sliver.node.site_deployment.controller.name)
+                raise InnocuousException('Controller %s is disabled'%controller.name)
         except AttributeError:
             pass
 
@@ -249,7 +249,7 @@
                     except:
                         error = '%s'%str_e
 
-                    if isinstance(e, InnocuousException) and not force_error:
+                    if isinstance(e, InnocuousException):
                         o.backend_status = '1 - %s'%error
                     else:
                         o.backend_status = '2 - %s'%error
diff --git a/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.yaml b/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.yaml
index 89f1aaf..06403a6 100644
--- a/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.yaml
+++ b/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.yaml
@@ -42,19 +42,19 @@
   - name: install Docker
     apt: name=lxc-docker state=present update_cache=yes
 
-#  - name: install python-setuptools
-#    apt: name=python-setuptools state=present
+  - name: install python-setuptools
+    apt: name=python-setuptools state=present
 
-#  - name: install pip
-#    easy_install: name=pip
+  - name: install pip
+    easy_install: name=pip
 
-#  - name: install docker-py
-#    pip: name=docker-py version=0.5.3
+  - name: install docker-py
+    pip: name=docker-py version=0.5.3
 
-#  - name: install Pipework
-#    get_url: url=https://raw.githubusercontent.com/jpetazzo/pipework/master/pipework
-#       dest=/usr/local/bin/pipework
-#       mode=0755
+  - name: install Pipework
+    get_url: url=https://raw.githubusercontent.com/jpetazzo/pipework/master/pipework
+       dest=/usr/local/bin/pipework
+       mode=0755
 
   - name: Disable resolvconf service
     shell: service resolvconf stop
@@ -85,6 +85,32 @@
      - remove container
      - start monitoring-channel
 
+#  - name: Start monitoring-channel container
+#    docker:
+#      docker_api_version: "1.18"
+#      name: monitoring-channel-{{ unique_id }}
+#      # was: reloaded
+#      state: running
+#      image: srikanthvavila/monitoring-channel
+#      expose:
+#      - 8000
+#      ports:
+#      - "{{ ceilometer_port }}:8000"
+#      volumes:
+#      - /usr/local/share/monitoring-channel-{{ unique_id }}_ceilometer_proxy_config:/usr/local/share/ceilometer_proxy_config
+#
+#  - name: Get Docker IP
+#    #TODO: copy dockerip.sh to monitoring service synchronizer
+#    script: /opt/xos/synchronizers/onos/scripts/dockerip.sh monitoring-channel-{{ unique_id }}
+#    register: dockerip
+#
+#  - name: Wait for Monitoring channel to come up
+#    wait_for:
+#      host={{ '{{' }} dockerip.stdout {{ '}}' }}
+#      port={{ '{{' }} item {{ '}}' }}
+#      state=present
+#    with_items:
+#    - {{ ceilometer_port }}
 # These are samples, not necessary for correct function of demo
 
   - name: Make sure Monitoring channel service is running
diff --git a/xos/synchronizers/monitoring_channel/steps/sync_sflowservice.py b/xos/synchronizers/monitoring_channel/steps/sync_sflowservice.py
index 5e5cd83..154c5ab 100644
--- a/xos/synchronizers/monitoring_channel/steps/sync_sflowservice.py
+++ b/xos/synchronizers/monitoring_channel/steps/sync_sflowservice.py
@@ -55,7 +55,6 @@
         fields["instance_hostname"] = self.get_instance(o).instance_name.replace("_","-")
         fields["sflow_port"] = o.sflow_port
         fields["sflow_api_port"] = o.sflow_api_port
-        fields["nat_ip"] = self.get_instance(o).get_ssh_ip()
         fields["sflow_container"] = "sflowpubsub"
         return fields
 
diff --git a/xos/synchronizers/monitoring_channel/steps/sync_sflowtenant.py b/xos/synchronizers/monitoring_channel/steps/sync_sflowtenant.py
index 6de0374..a15fa54 100644
--- a/xos/synchronizers/monitoring_channel/steps/sync_sflowtenant.py
+++ b/xos/synchronizers/monitoring_channel/steps/sync_sflowtenant.py
@@ -64,7 +64,6 @@
         instance = self.get_instance(o)
 
         fields={}
-        fields["nat_ip"] = instance.get_ssh_ip()
         fields["sflow_api_base_url"] = self.get_sflow_service(o).sflow_api_url
         fields["sflow_api_port"] = self.get_sflow_service(o).sflow_api_port
         fields["listening_endpoint"] = o.listening_endpoint
diff --git a/xos/synchronizers/monitoring_channel/templates/start-monitoring-channel.sh.j2 b/xos/synchronizers/monitoring_channel/templates/start-monitoring-channel.sh.j2
index f56c247..ea5b639 100755
--- a/xos/synchronizers/monitoring_channel/templates/start-monitoring-channel.sh.j2
+++ b/xos/synchronizers/monitoring_channel/templates/start-monitoring-channel.sh.j2
@@ -22,7 +22,12 @@
 then
     #sudo docker build -t monitoring-channel -f Dockerfile.monitoring_channel .
     sudo docker pull srikanthvavila/monitoring-channel
+if [ -z "$HEADNODEFLATLANIP" ] || [ "$HEADNODEFLATLANIP" == "None" ]
+then
+    docker run -d --name=$MONITORING_CHANNEL --privileged=true -p $HOST_FORWARDING_PORT_FOR_CEILOMETER:8000 srikanthvavila/monitoring-channel
+else
     docker run -d --name=$MONITORING_CHANNEL --add-host="ctl:$HEADNODEFLATLANIP" --privileged=true -p $HOST_FORWARDING_PORT_FOR_CEILOMETER:8000 srikanthvavila/monitoring-channel
+fi
 else
     docker start $MONITORING_CHANNEL
 fi
diff --git a/xos/synchronizers/onos/onos-ext-notifier-1.0-SNAPSHOT.oar b/xos/synchronizers/onos/onos-ext-notifier-1.0-SNAPSHOT.oar
index 893c01a..23c6fcd 100644
--- a/xos/synchronizers/onos/onos-ext-notifier-1.0-SNAPSHOT.oar
+++ b/xos/synchronizers/onos/onos-ext-notifier-1.0-SNAPSHOT.oar
Binary files differ
diff --git a/xos/synchronizers/onos/onos-ext-volt-event-publisher-1.0-SNAPSHOT.oar b/xos/synchronizers/onos/onos-ext-volt-event-publisher-1.0-SNAPSHOT.oar
index 7a32268..244f589 100644
--- a/xos/synchronizers/onos/onos-ext-volt-event-publisher-1.0-SNAPSHOT.oar
+++ b/xos/synchronizers/onos/onos-ext-volt-event-publisher-1.0-SNAPSHOT.oar
Binary files differ
diff --git a/xos/synchronizers/onos/steps/sync_onosapp.py b/xos/synchronizers/onos/steps/sync_onosapp.py
index 8942e59..2dfdfbd 100644
--- a/xos/synchronizers/onos/steps/sync_onosapp.py
+++ b/xos/synchronizers/onos/steps/sync_onosapp.py
@@ -7,8 +7,10 @@
 import time
 import re
 import json
+from collections import OrderedDict
 from django.db.models import F, Q
 from xos.config import Config
+from synchronizers.base.ansible import run_template
 from synchronizers.base.syncstep import SyncStep
 from synchronizers.base.ansible import run_template_ssh
 from synchronizers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
@@ -46,8 +48,8 @@
 
         serv = self.get_onos_service(o)
 
-        if serv.use_external_host:
-            return serv.use_external_host
+        if serv.no_container:
+            raise Exception("get_instance() was called on a service that was marked no_container")
 
         if serv.slices.exists():
             slice = serv.slices.all()[0]
@@ -66,6 +68,12 @@
 
         return onoses[0]
 
+    def is_no_container(self, o):
+        return self.get_onos_service(o).no_container
+
+    def skip_ansible_fields(self, o):
+        return self.is_no_container(o)
+
     def get_files_dir(self, o):
         if not hasattr(Config(), "observer_steps_dir"):
             # make steps_dir mandatory; there's no valid reason for it to not
@@ -125,8 +133,15 @@
 
         ordered_attrs = attrs.keys()
 
+        onos = self.get_onos_service(o)
+        if onos.node_key:
+            file(os.path.join(o.files_dir, "node_key"),"w").write(onos.node_key)
+            o.node_key_fn="node_key"
+        else:
+            o.node_key_fn=None
+
         o.early_rest_configs=[]
-        if ("cordvtn" in o.dependencies):
+        if ("cordvtn" in o.dependencies) and (not self.is_no_container(o)):
             # For VTN, since it's running in a docker host container, we need
             # to make sure it configures the cluster using the right ip addresses.
             # NOTE: rest_onos/v1/cluster/configuration/ will reboot the cluster and
@@ -155,7 +170,7 @@
                 file(os.path.join(o.files_dir, fn),"w").write(" " +value)
                 o.rest_configs.append( {"endpoint": endpoint, "fn": fn} )
             if name.startswith("component_config"):
-                components = json.loads(value)
+                components = json.loads(value,object_pairs_hook=OrderedDict)
                 for component in components.keys():
                     config = components[component]
                     for key in config.keys():
@@ -172,22 +187,37 @@
     def prepare_record(self, o):
         self.write_configs(o)
 
-    def get_extra_attributes(self, o):
-        instance = self.get_instance(o)
+    def get_extra_attributes_common(self, o):
+        fields = {}
 
-        fields={}
+        # These are attributes that are not dependent on Instance. For example,
+        # REST API stuff.
+
+        onos = self.get_onos_service(o)
+
         fields["files_dir"] = o.files_dir
         fields["appname"] = o.name
-        fields["nat_ip"] = instance.get_ssh_ip()
-        fields["config_fns"] = o.config_fns
         fields["rest_configs"] = o.rest_configs
-        fields["early_rest_configs"] = o.early_rest_configs
-        fields["component_configs"] = o.component_configs
+        fields["rest_hostname"] = onos.rest_hostname
+        fields["rest_port"] = onos.rest_port
+
         if o.dependencies:
             fields["dependencies"] = [x.strip() for x in o.dependencies.split(",")]
         else:
             fields["dependencies"] = []
 
+        return fields
+
+    def get_extra_attributes_full(self, o):
+        instance = self.get_instance(o)
+
+        fields = self.get_extra_attributes_common(o)
+
+        fields["config_fns"] = o.config_fns
+        fields["early_rest_configs"] = o.early_rest_configs
+        fields["component_configs"] = o.component_configs
+        fields["node_key_fn"] = o.node_key_fn
+
         if o.install_dependencies:
             fields["install_dependencies"] = [x.strip() for x in o.install_dependencies.split(",")]
         else:
@@ -199,12 +229,23 @@
             fields["ONOS_container"] = "ONOS"
         return fields
 
+    def get_extra_attributes(self, o):
+        if self.is_no_container(o):
+            return self.get_extra_attributes_common(o)
+        else:
+            return self.get_extra_attributes_full(o)
+
     def sync_fields(self, o, fields):
         # the super causes the playbook to be run
         super(SyncONOSApp, self).sync_fields(o, fields)
 
     def run_playbook(self, o, fields):
-        super(SyncONOSApp, self).run_playbook(o, fields)
+        if self.is_no_container(o):
+            # There is no machine to SSH to, so use the synchronizer's
+            # run_template method directly.
+            run_template("sync_onosapp_nocontainer.yaml", fields)
+        else:
+            super(SyncONOSApp, self).run_playbook(o, fields)
 
     def delete_record(self, m):
         pass
diff --git a/xos/synchronizers/onos/steps/sync_onosapp.yaml b/xos/synchronizers/onos/steps/sync_onosapp.yaml
index f0af0d6..8235286 100644
--- a/xos/synchronizers/onos/steps/sync_onosapp.yaml
+++ b/xos/synchronizers/onos/steps/sync_onosapp.yaml
@@ -50,6 +50,16 @@
       path=/home/ubuntu/{{ appname }}/
       state=directory
 
+{% if node_key_fn %}
+  - name: Copy over key
+    copy:
+      src={{ files_dir }}/{{ node_key_fn }}
+      dest=/home/ubuntu/node_key
+
+  - name: Copy node key into container
+    shell: docker cp /home/ubuntu/node_key {{ ONOS_container }}:/root/node_key
+{% endif %}
+
 {% if config_fns %}
   - name: Copy over configuration files
     copy:
diff --git a/xos/synchronizers/onos/steps/sync_onosapp_nocontainer.yaml b/xos/synchronizers/onos/steps/sync_onosapp_nocontainer.yaml
new file mode 100644
index 0000000..5aad569
--- /dev/null
+++ b/xos/synchronizers/onos/steps/sync_onosapp_nocontainer.yaml
@@ -0,0 +1,57 @@
+---
+- hosts: 127.0.0.1
+  connection: local
+  vars:
+    appname: {{ appname }}
+    dependencies: {{ dependencies }}
+{% if component_configs %}
+    component_configs:
+{% for component_config in component_configs %}
+       - component: {{ component_config.component }}
+         config_params: {{  component_config.config_params }}
+{% endfor %}
+{% endif %}
+{% if rest_configs %}
+    rest_configs:
+{% for rest_config in rest_configs %}
+       - endpoint: {{ rest_config.endpoint }}
+         body: "{{ '{{' }} lookup('file', '{{ files_dir }}/{{ rest_config.fn }}') {{ '}}' }}"
+{% endfor %}
+{% endif %}
+{% if early_rest_configs %}
+    early_rest_configs:
+{% for early_rest_config in early_rest_configs %}
+       - endpoint: {{ early_rest_config.endpoint }}
+         body: "{{ '{{' }} lookup('file', '{{ files_dir }}/{{ early_rest_config.fn }}') {{ '}}' }}"
+{% endfor %}
+{% endif %}
+    rest_hostname: {{ rest_hostname }}
+    rest_port: {{ rest_port }}
+
+  tasks:
+{% if dependencies %}
+  - name: Add dependencies to ONOS
+    uri:
+      url: http://{{ '{{' }} rest_hostname {{ '}}' }}:{{ '{{' }} rest_port {{ '}}' }}/onos/v1/applications/{{ '{{' }} item {{ '}}' }}/active
+      method: POST
+      user: karaf
+      password: karaf
+    with_items:
+        {% for dependency in dependencies %}
+        - {{ dependency }}
+        {% endfor %}
+{% endif %}
+
+{% if rest_configs %}
+# Do this after services have been activated, or it will cause an exception.
+# vOLT will re-read its net config; vbng may not.
+  - name: Add ONOS configuration values
+    uri:
+      url: http://{{ '{{' }} rest_hostname {{ '}}' }}:{{ '{{' }} rest_port {{ '}}' }}/{{ '{{' }} item.endpoint {{ '}}' }} #http://localhost:8181/onos/v1/network/configuration/
+      body: "{{ '{{' }} item.body {{ '}}' }}"
+      body_format: raw
+      method: POST
+      user: karaf
+      password: karaf
+    with_items: "rest_configs"
+{% endif %}
diff --git a/xos/synchronizers/onos/steps/sync_onosservice.py b/xos/synchronizers/onos/steps/sync_onosservice.py
index e70be0c..944a05c 100644
--- a/xos/synchronizers/onos/steps/sync_onosservice.py
+++ b/xos/synchronizers/onos/steps/sync_onosservice.py
@@ -43,9 +43,6 @@
 
         serv = o
 
-        if serv.use_external_host:
-            return serv.use_external_host
-
         if serv.slices.exists():
             slice = serv.slices.all()[0]
             if slice.instances.exists():
@@ -57,10 +54,16 @@
         fields={}
         fields["instance_hostname"] = self.get_instance(o).instance_name.replace("_","-")
         fields["appname"] = o.name
-        fields["nat_ip"] = self.get_instance(o).get_ssh_ip()
         fields["ONOS_container"] = "ONOS"
         return fields
 
+    def sync_record(self, o):
+        if o.no_container:
+            logger.info("no work to do for onos service, because o.no_container is set")
+            o.save()
+        else:
+            super(SyncONOSService, self).sync_record(o)
+
     def sync_fields(self, o, fields):
         # the super causes the playbook to be run
         super(SyncONOSService, self).sync_fields(o, fields)
diff --git a/xos/synchronizers/openstack/event_loop.py b/xos/synchronizers/openstack/event_loop.py
index 6cfc9f6..db78f07 100644
--- a/xos/synchronizers/openstack/event_loop.py
+++ b/xos/synchronizers/openstack/event_loop.py
@@ -23,8 +23,8 @@
 #from timeout import timeout
 from xos.config import Config, XOS_DIR
 from synchronizers.base.steps import *
-from syncstep import SyncStep
-from toposort import toposort
+from synchronizers.base.syncstep import SyncStep
+from synchronizers.base.toposort import toposort
 from synchronizers.base.error_mapper import *
 from synchronizers.openstack.openstacksyncstep import OpenStackSyncStep
 from synchronizers.base.steps.sync_object import SyncObject
@@ -421,7 +421,7 @@
 				except Exception,e:
                         		self.consolePrint(bcolors.FAIL + "Model step %r failed" % (sync_step.__name__) + bcolors.ENDC)
 					logger.error('Model step %r failed. This seems like a misconfiguration or bug: %r. This error will not be relayed to the user!' % (sync_step.__name__, e))
-					logger.log_exc(e)
+					logger.log_exc("Exception in sync step")
 					self.failed_steps.append(S)
 					my_status = STEP_STATUS_KO
 			else:
diff --git a/xos/synchronizers/openstack/model_policies/model_policy_Controller.py b/xos/synchronizers/openstack/model_policies/model_policy_Controller.py
index 2db7a63..c62b612 100644
--- a/xos/synchronizers/openstack/model_policies/model_policy_Controller.py
+++ b/xos/synchronizers/openstack/model_policies/model_policy_Controller.py
@@ -46,6 +46,8 @@
         if network not in ctrls_by_network or \
             controller not in ctrls_by_network[network]:
             controller_network = ControllerNetwork(controller=controller, network=network)
+            if network.subnet and network.subnet.strip():
+                controller_network.subnet = network.subnet.strip()
             controller_network.save()
     # relations for all images
     ctrls_by_image = defaultdict(list)
diff --git a/xos/synchronizers/openstack/openstacksyncstep.py b/xos/synchronizers/openstack/openstacksyncstep.py
index cc568f8..46056cf 100644
--- a/xos/synchronizers/openstack/openstacksyncstep.py
+++ b/xos/synchronizers/openstack/openstacksyncstep.py
@@ -1,6 +1,6 @@
 import os
 import base64
-from syncstep import SyncStep
+from synchronizers.base.syncstep import SyncStep
 
 class OpenStackSyncStep(SyncStep):
     """ XOS Sync step for copying data to OpenStack 
diff --git a/xos/synchronizers/openstack/steps/sync_container.yaml b/xos/synchronizers/openstack/steps/sync_container.yaml
index 82588dc..4ae4eb2 100644
--- a/xos/synchronizers/openstack/steps/sync_container.yaml
+++ b/xos/synchronizers/openstack/steps/sync_container.yaml
@@ -52,10 +52,18 @@
       state=latest
       update_cache=yes
     with_items:
-    - docker-engine
+# XXX docker 1.10 is not working on cloudlab
+#    - docker-engine
     - python-pip
     - python-httplib2
 
+  - name: Install Docker 1.9.1
+    apt:
+      name={{ '{{' }} item {{ '}}' }}
+      update_cache=yes
+    with_items:
+    - docker-engine=1.9.1-0~trusty
+
   # Something is installing a requests library that is incompative with pip, and
   # will cause this recipe to fail next time it tries to run pip. Only the one
   # in /usr/local/lib is bad. There's still a good one in /usr/lib
diff --git a/xos/synchronizers/openstack/steps/sync_instances.py b/xos/synchronizers/openstack/steps/sync_instances.py
index 22aa45c..884bcf5 100644
--- a/xos/synchronizers/openstack/steps/sync_instances.py
+++ b/xos/synchronizers/openstack/steps/sync_instances.py
@@ -34,8 +34,7 @@
             userdata += '  - %s\n' % key
         return userdata
 
-    def sort_controller_networks(self, nets):
-        nets = list(nets)
+    def sort_nics(self, nics):
         result = []
 
         # Enforce VTN's network order requirement. The access network must be
@@ -43,23 +42,27 @@
         # into the second slot.
 
         # move the private and/or access network to the first spot
-        for net in nets[:]:
-            tem = net.network.template
-            if (tem.visibility == "private") and (tem.translation=="none") and ("management" not in tem.name):
-                result.append(net)
-                nets.remove(net)
+        for nic in nics[:]:
+            network=nic.get("network", None)
+            if network:
+                tem = network.template
+                if (tem.visibility == "private") and (tem.translation=="none") and ("management" not in tem.name):
+                    result.append(nic)
+                    nics.remove(nic)
 
         # move the management network to the second spot
-        for net in nets[:]:
-            tem = net.network.template
-            if (tem.visibility == "private") and (tem.translation=="none") and ("management" in tem.name):
-                if len(result)!=1:
-                    raise Exception("Management network needs to be inserted in slot 1, but there are %d private nets" % len(result))
-                result.append(net)
-                nets.remove(net)
+        for net in nics[:]:
+            network=nic.get("network", None)
+            if network:
+                tem = network.template
+                if (tem.visibility == "private") and (tem.translation=="none") and ("management" in tem.name):
+                    if len(result)!=1:
+                        raise Exception("Management network needs to be inserted in slot 1, but there are %d private nics" % len(result))
+                    result.append(nic)
+                    nics.remove(nic)
 
         # add everything else. For VTN there probably shouldn't be any more.
-        result.extend(nets)
+        result.extend(nics)
 
         return result
 
@@ -84,19 +87,30 @@
         if instance.slice.service and instance.slice.service.public_key:
             pubkeys.add(instance.slice.service.public_key)
 
-        nics = []
-        networks = [ns.network for ns in NetworkSlice.objects.filter(slice=instance.slice)]
+        nics=[]
+
+        # handle ports the were created by the user
+        port_ids=[]
+        for port in Port.objects.filter(instance=instance):
+            if not port.port_id:
+                raise DeferredException("Instance %s waiting on port %s" % (instance, port))
+            nics.append({"kind": "port", "value": port.port_id, "network": port.network})
+
+        # we want to exclude from 'nics' any network that already has a Port
+        existing_port_networks = [port.network for network in Port.objects.filter(instance=instance)]
+
+        networks = [ns.network for ns in NetworkSlice.objects.filter(slice=instance.slice) if ns.network not in existing_port_networks]
         controller_networks = ControllerNetwork.objects.filter(network__in=networks,
                                                                 controller=instance.node.site_deployment.controller)
 
-        controller_networks = self.sort_controller_networks(controller_networks)
+        #controller_networks = self.sort_controller_networks(controller_networks)
         for controller_network in controller_networks:
             # Lenient exception - causes slow backoff
             if controller_network.network.template.visibility == 'private' and \
                controller_network.network.template.translation == 'none':
                    if not controller_network.net_id:
                         raise DeferredException("Instance %s Private Network %s has no id; Try again later" % (instance, controller_network.network.name))
-                   nics.append(controller_network.net_id)
+                   nics.append({"kind": "net", "value": controller_network.net_id, "network": controller_network.network})
 
         # now include network template
         network_templates = [network.template.shared_network_name for network in networks \
@@ -107,12 +121,14 @@
         nets = driver.shell.quantum.list_networks()['networks']
         for net in nets:
             if net['name'] in network_templates:
-                nics.append(net['id'])
+                nics.append({"kind": "net", "value": net['id'], "network": None})
 
         if (not nics):
             for net in nets:
                 if net['name']=='public':
-                    nics.append(net['id'])
+                    nics.append({"kind": "net", "value": net['id'], "network": None})
+
+        nics = self.sort_nics(nics)
 
         image_name = None
         controller_images = instance.image.controllerimages.filter(controller=instance.node.site_deployment.controller)
diff --git a/xos/synchronizers/openstack/steps/sync_instances.yaml b/xos/synchronizers/openstack/steps/sync_instances.yaml
index a61e5cf..3e7182a 100644
--- a/xos/synchronizers/openstack/steps/sync_instances.yaml
+++ b/xos/synchronizers/openstack/steps/sync_instances.yaml
@@ -19,11 +19,8 @@
       user_data: "{{ user_data }}"
       config_drive: yes
       nics:
-      {% for net in nics %}
-          - net-id: {{ net }}
-      {% endfor %}
-      {% for port in ports %}
-          - port-id: {{ port }}
+      {% for nic in nics %}
+          - {{ nic.kind }}-id: {{ nic.value }}
       {% endfor %}
 
       {% if meta %}
diff --git a/xos/synchronizers/openstack/steps/sync_ports.py b/xos/synchronizers/openstack/steps/sync_ports.py
index 21376e5..4f6ce14 100644
--- a/xos/synchronizers/openstack/steps/sync_ports.py
+++ b/xos/synchronizers/openstack/steps/sync_ports.py
@@ -16,8 +16,35 @@
     #     has, and then work backward from each port's network-id to determine
     #     which Network is associated from the port.
 
-    def call(self, **args):
-        logger.info("sync'ing network instances")
+    def call(self, failed=[], deletion=False):
+        if deletion:
+            self.delete_ports()
+        else:
+            self.sync_ports()
+
+    def get_driver(self, port):
+        # We need to use a client driver that specifies the tenant
+        # of the destination instance. Nova-compute will not connect
+        # ports to instances if the port's tenant does not match
+        # the instance's tenant.
+
+        # A bunch of stuff to compensate for OpenStackDriver.client_driveR()
+        # not being in working condition.
+        from openstack.client import OpenStackClient
+        from openstack.driver import OpenStackDriver
+        controller = port.instance.node.site_deployment.controller
+        slice = port.instance.slice
+        caller = port.network.owner.creator
+        auth = {'username': caller.email,
+                'password': caller.remote_password,
+                'tenant': slice.name}
+        client = OpenStackClient(controller=controller, **auth) # cacert=self.config.nova_ca_ssl_cert,
+        driver = OpenStackDriver(client=client)
+
+        return driver
+
+    def sync_ports(self):
+        logger.info("sync'ing Ports [delete=False]")
 
         ports = Port.objects.all()
         ports_by_id = {}
@@ -164,33 +191,40 @@
                     logger.info("deferring port %s because controllerNetwork does not have a port-id yet" % port)
                     continue
                 try:
-                    # We need to use a client driver that specifies the tenant
-                    # of the destination instance. Nova-compute will not connect
-                    # ports to instances if the port's tenant does not match
-                    # the instance's tenant.
+                    driver = self.get_driver(port)
 
-                    # A bunch of stuff to compensate for OpenStackDriver.client_driveR()
-                    # not being in working condition.
-                    from openstack.client import OpenStackClient
-                    from openstack.driver import OpenStackDriver
-                    caller = port.network.owner.creator
-                    auth = {'username': caller.email,
-                            'password': caller.remote_password,
-                            'tenant': slice.name}
-                    client = OpenStackClient(controller=controller, **auth) # cacert=self.config.nova_ca_ssl_cert,
-                    driver = OpenStackDriver(client=client)
+                    args = {"network_id": cn.net_id}
+                    neutron_port_name = port.get_parameters().get("neutron_port_name", None)
+                    if neutron_port_name:
+                        args["name"] = neutron_port_name
 
-                    neutron_port = driver.shell.quantum.create_port({"port": {"network_id": cn.net_id}})["port"]
+                    neutron_port = driver.shell.quantum.create_port({"port": args})["port"]
                     port.port_id = neutron_port["id"]
                     if neutron_port["fixed_ips"]:
                         port.ip = neutron_port["fixed_ips"][0]["ip_address"]
                     port.mac = neutron_port["mac_address"]
+                    port.xos_created = True
+                    logger.info("created neutron port %s for %s" % (port.port_id, port))
                 except:
                     logger.log_exc("failed to create neutron port for %s" % port)
                     continue
                 port.save()
 
-    def delete_record(self, network_instance):
-        # Nothing to do, this is an OpenCloud object
-        pass
+    def delete_ports(self):
+        logger.info("sync'ing Ports [delete=True]")
+        for port in Port.deleted_objects.all():
+            self.delete_record(port)
+
+    def delete_record(self, port):
+        if port.xos_created and port.port_id:
+            logger.info("calling openstack to destroy port %s" % port.port_id)
+            try:
+                driver = self.get_driver(port)
+                driver.shell.quantum.delete_port(port.port_id)
+            except:
+                logger.log_exc("failed to delete port %s from neutron" % port.port_id)
+                return
+
+        logger.info("Purging port %s" % port)
+        port.delete(purge=True)
 
diff --git a/xos/synchronizers/openstack/syncstep.py b/xos/synchronizers/openstack/syncstep.py
index bdab8f3..d1639b4 100644
--- a/xos/synchronizers/openstack/syncstep.py
+++ b/xos/synchronizers/openstack/syncstep.py
@@ -142,10 +142,10 @@
     def sync_record(self, o):
         try:
             controller = o.get_controller()
-            controller_register = json.loads(o.node.site_deployment.controller.backend_register)
+            controller_register = json.loads(controller.backend_register)
 
             if (controller_register.get('disabled',False)):
-                raise InnocuousException('Controller %s is disabled'%sliver.node.site_deployment.controller.name)
+                raise InnocuousException('Controller %s is disabled'%controller.name)
         except AttributeError:
             pass
 
diff --git a/xos/synchronizers/vbng/steps/sync_vbngtenant.py b/xos/synchronizers/vbng/steps/sync_vbngtenant.py
index 94875f4..4fa351e 100644
--- a/xos/synchronizers/vbng/steps/sync_vbngtenant.py
+++ b/xos/synchronizers/vbng/steps/sync_vbngtenant.py
@@ -8,7 +8,7 @@
 from synchronizers.base.syncstep import SyncStep
 from synchronizers.base.ansible import run_template_ssh
 from core.models import Service
-from services.cord.models import VCPEService, VCPETenant, VBNGTenant, VBNGService
+from services.cord.models import VSGService, VSGTenant, VBNGTenant, VBNGService
 from services.hpc.models import HpcService, CDNPrefix
 from xos.logger import Logger, logging
 
@@ -21,8 +21,8 @@
 logger = Logger(level=logging.INFO)
 
 class SyncVBNGTenant(SyncStep):
-    provides=[VCPETenant]
-    observes=VCPETenant
+    provides=[VSGTenant]
+    observes=VSGTenant
     requested_interval=0
 
     def __init__(self, **args):
@@ -84,7 +84,7 @@
         raise Exception("vBNG service does not have vbng_url set, and is not linked to an ONOSApp")
 
     def get_private_interface(self, o):
-        vcpes = VCPETenant.get_tenant_objects().all()
+        vcpes = VSGTenant.get_tenant_objects().all()
         vcpes = [x for x in vcpes if (x.vbng is not None) and (x.vbng.id == o.id)]
         if not vcpes:
             raise Exception("No vCPE tenant is associated with vBNG %s" % str(o.id))
diff --git a/xos/synchronizers/vcpe/run-vtn.sh b/xos/synchronizers/vcpe/run-vtn.sh
new file mode 100755
index 0000000..c4c3b00
--- /dev/null
+++ b/xos/synchronizers/vcpe/run-vtn.sh
@@ -0,0 +1,8 @@
+#if [[ ! -e ./vcpe-observer.py ]]; then
+#    ln -s ../../xos-observer.py vcpe-observer.py
+#fi
+
+export XOS_DIR=/opt/xos
+cp /root/setup/node_key $XOS_DIR/synchronizers/vcpe/node_key
+chmod 0600 $XOS_DIR/synchronizers/vcpe/node_key
+python vcpe-synchronizer.py  -C $XOS_DIR/synchronizers/vcpe/vtn_vcpe_synchronizer_config
diff --git a/xos/synchronizers/vcpe/steps/sync_vcpetenant.py b/xos/synchronizers/vcpe/steps/sync_vcpetenant.py
index f0e9301..cd8a292 100644
--- a/xos/synchronizers/vcpe/steps/sync_vcpetenant.py
+++ b/xos/synchronizers/vcpe/steps/sync_vcpetenant.py
@@ -9,8 +9,8 @@
 from synchronizers.base.syncstep import SyncStep
 from synchronizers.base.ansible import run_template_ssh
 from synchronizers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
-from core.models import Service, Slice
-from services.cord.models import VCPEService, VCPETenant, VOLTTenant
+from core.models import Service, Slice, Tag
+from services.cord.models import VSGService, VSGTenant, VOLTTenant
 from services.hpc.models import HpcService, CDNPrefix
 from xos.logger import Logger, logging
 
@@ -25,21 +25,23 @@
 PARENTAL_MECHANISM="dnsmasq"
 ENABLE_QUICK_UPDATE=False
 
-class SyncVCPETenant(SyncInstanceUsingAnsible):
-    provides=[VCPETenant]
-    observes=VCPETenant
+CORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
+
+class SyncVSGTenant(SyncInstanceUsingAnsible):
+    provides=[VSGTenant]
+    observes=VSGTenant
     requested_interval=0
     template_name = "sync_vcpetenant.yaml"
     service_key_name = "/opt/xos/synchronizers/vcpe/vcpe_private_key"
 
     def __init__(self, *args, **kwargs):
-        super(SyncVCPETenant, self).__init__(*args, **kwargs)
+        super(SyncVSGTenant, self).__init__(*args, **kwargs)
 
     def fetch_pending(self, deleted):
         if (not deleted):
-            objs = VCPETenant.get_tenant_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
+            objs = VSGTenant.get_tenant_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
         else:
-            objs = VCPETenant.get_deleted_tenant_objects()
+            objs = VSGTenant.get_deleted_tenant_objects()
 
         return objs
 
@@ -47,7 +49,7 @@
         if not o.provider_service:
             return None
 
-        vcpes = VCPEService.get_service_objects().filter(id=o.provider_service.id)
+        vcpes = VSGService.get_service_objects().filter(id=o.provider_service.id)
         if not vcpes:
             return None
 
@@ -137,6 +139,19 @@
                     if mac:
                         safe_macs.append(mac)
 
+        wan_vm_ip=""
+        wan_vm_mac=""
+        tags = Tag.select_by_content_object(o.instance).filter(name="vm_wan_addr")
+        if tags:
+            parts=tags[0].value.split(",")
+            if len(parts)!=3:
+                raise Exception("vm_wan_addr tag is malformed: %s" % value)
+            wan_vm_ip = parts[1]
+            wan_vm_mac = parts[2]
+        else:
+            if CORD_USE_VTN:
+                raise Exception("no vm_wan_addr tag for instance %s" % o.instance)
+
         fields = {"vlan_ids": vlan_ids,   # XXX remove this
                 "s_tags": s_tags,
                 "c_tags": c_tags,
@@ -145,6 +160,11 @@
                 "bbs_addrs": bbs_addrs,
                 "full_setup": full_setup,
                 "isolation": o.instance.isolation,
+                "wan_container_gateway_mac": vcpe_service.wan_container_gateway_mac,
+                "wan_container_gateway_ip": vcpe_service.wan_container_gateway_ip,
+                "wan_container_netbits": vcpe_service.wan_container_netbits,
+                "wan_vm_mac": wan_vm_mac,
+                "wan_vm_ip": wan_vm_ip,
                 "safe_browsing_macs": safe_macs}
 
         # add in the sync_attributes that come from the SubscriberRoot object
@@ -158,7 +178,7 @@
     def sync_fields(self, o, fields):
         # the super causes the playbook to be run
 
-        super(SyncVCPETenant, self).sync_fields(o, fields)
+        super(SyncVSGTenant, self).sync_fields(o, fields)
 
         # now do all of our broadbandshield stuff...
 
@@ -225,9 +245,12 @@
             logger.info("quick_update triggered; skipping ansible recipe")
         else:
             if o.instance.isolation in ["container", "container_vm"]:
-                super(SyncVCPETenant, self).run_playbook(o, fields, "sync_vcpetenant_new.yaml")
+                super(SyncVSGTenant, self).run_playbook(o, fields, "sync_vcpetenant_new.yaml")
             else:
-                super(SyncVCPETenant, self).run_playbook(o, fields)
+                if CORD_USE_VTN:
+                    super(SyncVSGTenant, self).run_playbook(o, fields, template_name="sync_vcpetenant_vtn.yaml")
+                else:
+                    super(SyncVSGTenant, self).run_playbook(o, fields)
 
         o.last_ansible_hash = ansible_hash
 
diff --git a/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml b/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml
new file mode 100644
index 0000000..96dc16c
--- /dev/null
+++ b/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml
@@ -0,0 +1,213 @@
+---
+- hosts: {{ instance_name }}
+  gather_facts: False
+  connection: ssh
+  user: ubuntu
+  sudo: yes
+  vars:
+      cdn_enable: {{ cdn_enable }}
+      dnsdemux_ip: {{ dnsdemux_ip }}
+      firewall_enable: {{ firewall_enable }}
+      url_filter_enable: {{ url_filter_enable }}
+      vlan_ids:
+        {% for vlan_id in vlan_ids %}
+        - {{ vlan_id }}
+        {% endfor %}
+      c_tags:
+        {% for c_tag in c_tags %}
+        - {{ c_tag }}
+        {% endfor %}
+      s_tags:
+        {% for s_tag in s_tags %}
+        - {{ s_tag }}
+        {% endfor %}
+      firewall_rules:
+        {% for firewall_rule in firewall_rules.split("\n") %}
+        - {{ firewall_rule }}
+        {% endfor %}
+      cdn_prefixes:
+        {% for prefix in cdn_prefixes %}
+        - {{ prefix }}
+        {% endfor %}
+      bbs_addrs:
+        {% for bbs_addr in bbs_addrs %}
+        - {{ bbs_addr }}
+        {% endfor %}
+      nat_ip: {{ nat_ip }}
+      nat_mac: {{ nat_mac }}
+      lan_ip: {{ lan_ip }}
+      lan_mac: {{ lan_mac }}
+      wan_ip: {{ wan_ip }}
+      wan_mac: {{ wan_mac }}
+      wan_container_ip: {{ wan_container_ip }}
+      wan_container_netbits: {{ wan_container_netbits }}
+      wan_container_mac: {{ wan_container_mac }}
+      wan_container_gateway_ip: {{ wan_container_gateway_ip }}
+      wan_vm_ip: {{ wan_vm_ip }}
+      wan_vm_mac: {{ wan_vm_mac }}
+      wan_next_hop: 10.0.1.253   # FIX ME
+      private_ip: {{ private_ip }}
+      private_mac: {{ private_mac }}
+      hpc_client_ip: {{ hpc_client_ip }}
+      hpc_client_mac: {{ hpc_client_mac }}
+      keystone_tenant_id: {{ keystone_tenant_id }}
+      keystone_user_id: {{ keystone_user_id }}
+      rabbit_user: {{ rabbit_user }}
+      rabbit_password: {{ rabbit_password }}
+      rabbit_host: {{ rabbit_host }}
+      safe_browsing:
+        {% for mac in safe_browsing_macs %}
+        - {{ mac }}
+        {% endfor %}
+
+  tasks:
+  - name: Check to see if network is setup
+    stat: path=/root/network_is_setup
+    register: network_is_setup
+
+  - name: Add eth0.500
+    shell: "{{ '{{' }} item {{ '}}' }}"
+    with_items:
+      - ip link del link eth0 eth0.500 || true
+      - brctl delbr br-wan || true
+      - ip link add link eth0 eth0.500 type vlan id 500
+      - ifconfig eth0.500 up
+      - ifconfig eth0.500 0.0.0.0
+      - ifconfig eth0.500 hw ether {{ wan_vm_mac }}
+      - ip addr add {{ wan_vm_ip }}/{{ wan_container_netbits }} dev eth0.500
+      - ip link set eth0.500 up
+      - ip route del default || true
+      - ip route add default via {{ wan_container_gateway_ip }}
+    when: network_is_setup.stat.exists == False
+
+  - name: install bridge-utils
+    apt: name=bridge-utils state=present
+
+  - name: now redo everything using a bridge
+    shell: "{{ '{{' }} item {{ '}}' }}"
+    with_items:
+       - ip link del link eth0 eth0.500
+       - ip link add link eth0 eth0.500 type vlan id 500
+       - ip link set eth0.500 up
+       - brctl delbr br-wan || true
+       - brctl addbr br-wan
+       - brctl addif br-wan eth0.500
+       - ifconfig br-wan hw ether {{ wan_vm_mac }}
+       - ip addr add {{ wan_vm_ip }}/{{ wan_container_netbits }} dev br-wan
+       - ip link set br-wan up
+       - ip route del default || true
+       - ip route add default via {{ wan_container_gateway_ip }}
+       - ip link set dev br-wan promisc on
+    when: network_is_setup.stat.exists == False
+
+  - name: Remember that the network is setup, so we never do the above again
+    shell: touch /root/network_is_setup
+
+{% if full_setup %}
+  - name: Docker repository
+    copy: src=/opt/xos/synchronizers/vcpe/files/docker.list
+      dest=/etc/apt/sources.list.d/docker.list
+
+  - name: Import the repository key
+    apt_key: keyserver=keyserver.ubuntu.com id=36A1D7869245C8950F966E92D8576A8BA88D21E9
+
+  - name: install Docker
+    apt: name=lxc-docker state=present update_cache=yes
+
+  - name: install python-setuptools
+    apt: name=python-setuptools state=present
+
+  - name: install pip
+    easy_install: name=pip
+
+  - name: install docker-py
+    pip: name=docker-py version=0.5.3
+
+  - name: install Pipework
+    get_url: url=https://raw.githubusercontent.com/jpetazzo/pipework/master/pipework
+       dest=/usr/local/bin/pipework
+       mode=0755
+
+  - name: make sure /etc/dnsmasq.d exists
+    file: path=/etc/dnsmasq.d state=directory owner=root group=root
+
+  - name: Disable resolvconf service
+    shell: service resolvconf stop
+    shell: echo manual > /etc/init/resolvconf.override
+    shell: rm -f /etc/resolv.conf
+
+  - name: Install resolv.conf
+    copy: src=/opt/xos/synchronizers/vcpe/files/vm-resolv.conf
+      dest=/etc/resolv.conf
+
+  - name: Verify if vcpe_stats_notifier ([] is to avoid capturing the shell process) cron job is already running
+    shell: pgrep -f [v]cpe_stats_notifier | wc -l
+    register: cron_job_pids_count
+
+#  - name: DEBUG
+#    debug: var=cron_job_pids_count.stdout
+
+#  - name: make sure ~/bin exists
+#    file: path=~/bin state=directory owner=root group=root
+#    when: cron_job_pids_count.stdout == "0"
+
+#  - name: Copy cron job to destination
+#    copy: src=/opt/xos/synchronizers/vcpe/vcpe_stats_notifier.py
+#      dest=/usr/local/sbin/vcpe_stats_notifier.py
+#    when: cron_job_pids_count.stdout == "0"
+
+#  - name: install python-kombu
+#    apt: name=python-kombu state=present
+#    when: cron_job_pids_count.stdout == "0"
+
+#  - name: Initiate vcpe_stats_notifier cron job
+#    command: sudo python /usr/local/sbin/vcpe_stats_notifier.py --keystone_tenant_id={{ keystone_tenant_id }} --keystone_user_id={{ keystone_user_id }} --rabbit_user={{ rabbit_user }} --rabbit_password={{ rabbit_password }} --rabbit_host={{ rabbit_host }} --vcpeservice_rabbit_exchange='vcpeservice'
+#    async: 9999999999999999
+#    poll: 0
+#    when: cron_job_pids_count.stdout == "0"
+{% endif %}
+
+  - name: vCPE upstart
+    template: src=/opt/xos/synchronizers/vcpe/templates/vcpe.conf.j2 dest=/etc/init/vcpe-{{ s_tags[0] }}-{{ c_tags[0] }}.conf
+
+  - name: vCPE startup script
+    template: src=/opt/xos/synchronizers/vcpe/templates/start-vcpe-vtn.sh.j2 dest=/usr/local/sbin/start-vcpe-{{ s_tags[0] }}-{{ c_tags[0] }}.sh mode=0755
+    notify:
+#    - restart vcpe
+     - stop vcpe
+     - remove container
+     - start vcpe
+
+  - name: create /etc/vcpe-{{ s_tags[0] }}-{{ c_tags[0] }}/dnsmasq.d
+    file: path=/etc/vcpe-{{ s_tags[0] }}-{{ c_tags[0] }}/dnsmasq.d state=directory owner=root group=root
+
+  - name: vCPE basic dnsmasq config
+    copy: src=/opt/xos/synchronizers/vcpe/files/vcpe.dnsmasq dest=/etc/vcpe-{{ s_tags[0] }}-{{ c_tags[0] }}/dnsmasq.d/vcpe.conf owner=root group=root
+    notify:
+    - restart dnsmasq
+
+  - name: dnsmasq config
+    template: src=/opt/xos/synchronizers/vcpe/templates/dnsmasq_servers.j2 dest=/etc/vcpe-{{ s_tags[0] }}-{{ c_tags[0] }}/dnsmasq.d/servers.conf owner=root group=root
+    notify:
+    - restart dnsmasq
+
+  - name: Make sure vCPE service is running
+    service: name=vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} state=started
+
+  handlers:
+  # Dnsmasq is automatically restarted in the container
+  - name: restart dnsmasq
+    shell: docker exec vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} killall dnsmasq
+
+  - name: restart vcpe
+    shell: service vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} stop; sleep 1; service vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} start
+
+  - name: stop vcpe
+    service: name=vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} state=stopped
+
+  - name: remove container
+    docker: name=vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} state=absent image=docker-vcpe
+
+  - name: start vcpe
+    service: name=vcpe-{{ s_tags[0] }}-{{ c_tags[0] }} state=started
+
diff --git a/xos/synchronizers/vcpe/templates/start-vcpe-vtn.sh.j2 b/xos/synchronizers/vcpe/templates/start-vcpe-vtn.sh.j2
new file mode 100644
index 0000000..bf46515
--- /dev/null
+++ b/xos/synchronizers/vcpe/templates/start-vcpe-vtn.sh.j2
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+function mac_to_iface {
+    MAC=$1
+    ifconfig|grep $MAC| awk '{print $1}'|grep -v '\.'
+}
+
+iptables -L > /dev/null
+ip6tables -L > /dev/null
+
+STAG={{ s_tags[0] }}
+CTAG={{ c_tags[0] }}
+VCPE=vcpe-$STAG-$CTAG
+
+docker inspect $VCPE > /dev/null 2>&1
+if [ "$?" == 1 ]
+then
+    docker pull andybavier/docker-vcpe
+    docker run -d --name=$VCPE --privileged=true --net=none -v /etc/$VCPE/dnsmasq.d:/etc/dnsmasq.d andybavier/docker-vcpe
+else
+    docker start $VCPE
+fi
+
+# Set up networking via pipework
+WAN_IFACE=br-wan
+docker exec $VCPE ifconfig eth0 >> /dev/null || pipework $WAN_IFACE -i eth0 $VCPE {{ wan_container_ip }}/{{ wan_container_netbits }}@{{ wan_container_gateway_ip }} {{ wan_container_mac }}
+
+LAN_IFACE=eth0
+ifconfig $LAN_IFACE >> /dev/null
+if [ "$?" == 0 ]
+then
+    ifconfig $LAN_IFACE.$STAG >> /dev/null || ip link add link $LAN_IFACE name $LAN_IFACE.$STAG type vlan id $STAG
+    ifconfig $LAN_IFACE.$STAG up
+    docker exec $VCPE ifconfig eth1 >> /dev/null || pipework $LAN_IFACE.$STAG -i eth1 $VCPE 192.168.0.1/24 @$CTAG
+fi
+
+#HPC_IFACE=$( mac_to_iface {{ hpc_client_mac }} )
+#docker exec $VCPE ifconfig eth2 >> /dev/null || pipework $HPC_IFACE -i eth2 $VCPE {{ hpc_client_ip }}/24
+
+# Make sure VM's eth0 (hpc_client) has no IP address
+#ifconfig $HPC_IFACE 0.0.0.0
+
+# Now can start up dnsmasq
+docker exec $VCPE service dnsmasq start
+
+# Attach to container
+docker start -a $VCPE
diff --git a/xos/synchronizers/vcpe/vtn_vcpe_synchronizer_config b/xos/synchronizers/vcpe/vtn_vcpe_synchronizer_config
new file mode 100644
index 0000000..e92786b
--- /dev/null
+++ b/xos/synchronizers/vcpe/vtn_vcpe_synchronizer_config
@@ -0,0 +1,47 @@
+
+[plc]
+name=plc
+deployment=VICCI
+
+[db]
+name=xos
+user=postgres
+password=password
+host=localhost
+port=5432
+
+[api]
+host=128.112.171.237
+port=8000
+ssl_key=None
+ssl_cert=None
+ca_ssl_cert=None
+ratelimit_enabled=0
+omf_enabled=0
+mail_support_address=support@localhost
+nova_enabled=True
+
+[observer]
+name=vcpe
+dependency_graph=/opt/xos/synchronizers/vcpe/model-deps
+steps_dir=/opt/xos/synchronizers/vcpe/steps
+sys_dir=/opt/xos/synchronizers/vcpe/sys
+deleters_dir=/opt/xos/synchronizers/vcpe/deleters
+log_file=console
+#/var/log/hpc.log
+driver=None
+pretend=False
+backoff_disabled=True
+save_ansible_output=True
+# set proxy_ssh to false on cloudlab
+full_setup=True
+proxy_ssh=True
+proxy_ssh_key=/opt/xos/synchronizers/vcpe/node_key
+proxy_ssh_user=root
+
+[networking]
+use_vtn=True
+
+[feefie]
+client_id='vicci_dev_central'
+user_id='pl'
diff --git a/xos/synchronizers/vtn/steps/sync_port_addresses.py b/xos/synchronizers/vtn/steps/sync_port_addresses.py
new file mode 100644
index 0000000..6b48911
--- /dev/null
+++ b/xos/synchronizers/vtn/steps/sync_port_addresses.py
@@ -0,0 +1,137 @@
+import os
+import requests
+import socket
+import sys
+import base64
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import SyncStep
+from core.models import Service, Port, Controller, Tag
+from core.models.service import COARSE_KIND
+from services.cord.models import VSGTenant
+from services.cord.models import Tenant
+from xos.logger import Logger, logging
+from requests.auth import HTTPBasicAuth
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+logger = Logger(level=logging.INFO)
+
+# XXX should save and load this
+glo_saved_vtn_maps = []
+
+class SyncPortAddresses(SyncStep):
+    requested_interval = 0 # 3600
+    provides=[Port]
+    observes=Port
+
+    def __init__(self, **args):
+        SyncStep.__init__(self, **args)
+
+    def call(self, **args):
+        global glo_saved_vtn_maps
+
+        logger.info("sync'ing vsg tenant to port addresses")
+
+        # build up a dictionary of port-->[wan_addrs] mappings
+        port_addrs = {}
+        for vsg in VSGTenant.get_tenant_objects().all():
+            if not vsg.instance:
+                logger.info("skipping vsg %s because it has no instance" % vsg)
+
+            wan_ip = vsg.wan_container_ip
+            if not wan_ip:
+                logger.info("skipping vsg %s because it has no wan_container_ip" % vsg)
+
+            wan_mac = vsg.wan_container_mac
+            if not wan_mac:
+                logger.info("skipping vsg %s because it has no wan_container_mac" % vsg)
+
+            lan_network = vsg.get_lan_network(vsg.instance)
+            if not lan_network:
+                logger.info("skipping vsg %s because it has no lan_network" % vsg)
+
+            lan_port = Port.objects.filter(instance = vsg.instance, network=lan_network)
+            if not lan_port:
+                logger.info("skipping vsg %s because it has no lan_port" % vsg)
+            lan_port = lan_port[0]
+
+            if not lan_port.port_id:
+                logger.info("skipping vsg %s because its lan_port has no port_id" % vsg)
+
+            if not (lan_port.pk in port_addrs):
+                port_addrs[lan_port.pk] = []
+            entry = {"mac_address": wan_mac, "ip_address": wan_ip}
+            addr_pairs = port_addrs[lan_port.pk]
+            if not entry in addr_pairs:
+                 addr_pairs.append(entry)
+
+            # now do the VM_WAN_IP from the instance
+            if vsg.instance:
+                tags=Tag.select_by_content_object(vsg.instance).filter(name="vm_wan_addr")
+                if tags:
+                    parts=tags[0].value.split(",")
+                    if len(parts)!=3:
+                        raise Exception("vm_wan_addr tag is malformed: %s" % value)
+                    entry = {"mac_address": parts[2], "ip_address": parts[1]}
+                    if not entry in addr_pairs:
+                        addr_pairs.append(entry)
+
+        # Get all ports in all controllers
+        ports_by_id = {}
+        for controller in Controller.objects.all():
+            if not controller.admin_tenant:
+                logger.info("controller %s has no admin_tenant" % controller)
+                continue
+            try:
+                driver = self.driver.admin_driver(controller = controller)
+                ports = driver.shell.quantum.list_ports()["ports"]
+            except:
+                logger.log_exc("failed to get ports from controller %s" % controller)
+                continue
+
+            for port in ports:
+                ports_by_id[port["id"]] = port
+
+        for port_pk in port_addrs.keys():
+            port = Port.objects.get(pk=port_pk)
+            addr_pairs = port_addrs[port_pk]
+            neutron_port = ports_by_id.get(port.port_id,None)
+            if not neutron_port:
+                logger.info("failed to get neutron port for port %s" % port)
+                continue
+
+            ips = [x["ip_address"] for x in addr_pairs]
+
+            changed = False
+
+            # delete addresses in neutron that don't exist in XOS
+            aaps = neutron_port.get("allowed_address_pairs", [])
+            for aap in aaps[:]:
+                if not aap["ip_address"] in ips:
+                    logger.info("removing address %s from port %s" % (aap["ip_address"], port))
+                    aaps.remove(aap)
+                    changed = True
+
+            aaps_ips = [x["ip_address"] for x in aaps]
+
+            # add addresses in XOS that don't exist in neutron
+            for addr in addr_pairs:
+                if not addr["ip_address"] in aaps_ips:
+                    logger.info("adding address %s to port %s" % (addr, port))
+                    aaps.append( addr )
+                    aaps_ips.append(addr["ip_address"])
+                    changed = True
+
+            if changed:
+                logger.info("updating port %s" % port)
+                driver.shell.quantum.update_port(port.port_id, {"port": {"allowed_address_pairs": aaps}})
+
+
+
+
+
+
+
diff --git a/xos/synchronizers/vtn/vtn_synchronizer_config b/xos/synchronizers/vtn/vtn_synchronizer_config
index 302a096..d931839 100644
--- a/xos/synchronizers/vtn/vtn_synchronizer_config
+++ b/xos/synchronizers/vtn/vtn_synchronizer_config
@@ -29,10 +29,16 @@
 deleters_dir=/opt/xos/synchronizers/vtn/deleters
 log_file=console
 #/var/log/hpc.log
-driver=None
+driver=openstack
 pretend=False
 backoff_disabled=True
 
+[nova]
+ca_ssl_cert=/etc/ssl/certs/ca-certificates.crt
+
 [feefie]
 client_id='vicci_dev_central'
 user_id='pl'
+
+[networking]
+use_vtn=True
diff --git a/xos/templates/admin/base.html b/xos/templates/admin/base.html
index 5a99e0e..8d8dcd1 100644
--- a/xos/templates/admin/base.html
+++ b/xos/templates/admin/base.html
@@ -23,6 +23,11 @@
   <script type="text/javascript" src="{% static 'uploadTextarea.js' %}"></script>
   <script type="text/javascript" src="{% static 'observer_status.js' %}"></script>
 
+  <script
+    src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-2.min.js"
+    data-apikey="748d877b8b4e211dcd3249c1aa46d263">
+  </script>
+
   <!-- ngXosLib -->
   <script src="{% static 'js/vendor/ngXosVendor.js' %}"></script>
   <script src="{% static 'js/vendor/ngXosHelpers.js' %}"></script>
diff --git a/xos/tests/README.md b/xos/tests/README.md
new file mode 100644
index 0000000..f3ddad5
--- /dev/null
+++ b/xos/tests/README.md
@@ -0,0 +1,5 @@
+# CORD Tests
+
+The files in this directory are obsolete. The plan is for this
+directory to hold tests in the furture. There are also tests in
+the form of TOSCA specifications in `../configurations/tests`.
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index 20a537a..80c0d91 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -121,6 +121,18 @@
             rest_onos/v1/network/configuration/:
                 type: string
                 required: false
+            rest_hostname:
+                type: string
+                required: false
+            rest_port:
+                type: string
+                required: false
+            no_container:
+                type: boolean
+                default: false
+            node_key:
+                type: string
+                required: false
 
 
     tosca.nodes.ONOSApp:
@@ -193,9 +205,9 @@
                 type: string
                 required: false
 
-    tosca.nodes.VCPEService:
+    tosca.nodes.VSGService:
         description: >
-            CORD: The vCPE Service.
+            CORD: The vSG Service.
         derived_from: tosca.nodes.Root
         capabilities:
             xos_base_service_caps
@@ -206,6 +218,15 @@
                 type: string
                 required: false
                 description: Label that matches network used to connect HPC and BBS services.
+            wan_container_gateway_ip:
+                type: string
+                required: false
+            wan_container_gateway_mac:
+                type: string
+                required: false
+            wan_container_netbits:
+                type: string
+                required: false
 
     tosca.nodes.VBNGService:
         derived_from: tosca.nodes.Root
@@ -409,6 +430,7 @@
             This is a variant of the TOSCA Network object that includes additional

             XOS-specific properties.

           properties:

+            xos_base_props

             ip_version:

               type: integer

               required: no

@@ -510,6 +532,17 @@
                 required: false
                 description: Comma-separated list of flavors that this deployment supports.
 
+    tosca.nodes.AddressPool:
+        derived_from: tosca.nodes.Root
+        description: >
+            A pool of addresses
+        properties:
+            xos_base_props
+            addresses:
+                type: string
+                required: false
+                description: space-separated list of addresses
+
     tosca.nodes.Image:
         derived_from: tosca.nodes.Root
         description: >
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 1b5db39..2f404dc 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -151,6 +151,18 @@
             rest_onos/v1/network/configuration/:
                 type: string
                 required: false
+            rest_hostname:
+                type: string
+                required: false
+            rest_port:
+                type: string
+                required: false
+            no_container:
+                type: boolean
+                default: false
+            node_key:
+                type: string
+                required: false
 
 
     tosca.nodes.ONOSApp:
@@ -251,9 +263,9 @@
                 type: string
                 required: false
 
-    tosca.nodes.VCPEService:
+    tosca.nodes.VSGService:
         description: >
-            CORD: The vCPE Service.
+            CORD: The vSG Service.
         derived_from: tosca.nodes.Root
         capabilities:
             scalable:
@@ -308,6 +320,15 @@
                 type: string
                 required: false
                 description: Label that matches network used to connect HPC and BBS services.
+            wan_container_gateway_ip:
+                type: string
+                required: false
+            wan_container_gateway_mac:
+                type: string
+                required: false
+            wan_container_netbits:
+                type: string
+                required: false
 
     tosca.nodes.VBNGService:
         derived_from: tosca.nodes.Root
@@ -622,6 +643,18 @@
             This is a variant of the TOSCA Network object that includes additional

             XOS-specific properties.

           properties:

+            no-delete:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to delete this object
+            no-create:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object

             ip_version:

               type: integer

               required: no

@@ -734,6 +767,28 @@
                 required: false
                 description: Comma-separated list of flavors that this deployment supports.
 
+    tosca.nodes.AddressPool:
+        derived_from: tosca.nodes.Root
+        description: >
+            A pool of addresses
+        properties:
+            no-delete:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to delete this object
+            no-create:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object
+            addresses:
+                type: string
+                required: false
+                description: space-separated list of addresses
+
     tosca.nodes.Image:
         derived_from: tosca.nodes.Root
         description: >
diff --git a/xos/tosca/resources/addresspool.py b/xos/tosca/resources/addresspool.py
new file mode 100644
index 0000000..e8577a2
--- /dev/null
+++ b/xos/tosca/resources/addresspool.py
@@ -0,0 +1,53 @@
+import os
+import pdb
+import socket
+import sys
+import struct
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from core.models import AddressPool
+
+from xosresource import XOSResource
+
+class XOSAddressPool(XOSResource):
+    provides = "tosca.nodes.AddressPool"
+    xos_model = AddressPool
+    copyin_props = ["addresses"]
+
+    def expand_cidr(self, cidr):
+        (network, bits) = cidr.split("/")
+        network=network.strip()
+        bits=int(bits.strip())
+
+        dest = []
+
+        netmask = (~(pow(2,32-bits)-1) & 0xFFFFFFFF)
+
+        count = pow(2, 32-bits)
+        for i in range(2, count-1):
+            ip = struct.unpack("!L", socket.inet_aton(network))[0]
+            ip = ip & netmask | i
+            dest.append( socket.inet_ntoa(struct.pack("!L", ip)) )
+
+        return dest
+
+    def get_xos_args(self):
+        args = super(XOSAddressPool, self).get_xos_args()
+
+        if "addresses" in args:
+            dest = []
+            for addr in args["addresses"].split():
+                addr=addr.strip()
+                if "/" in addr:
+                    dest.extend(self.expand_cidr(addr))
+                else:
+                    dest.append(addr)
+            args["addresses"] = " ".join(dest)
+
+        return args
+
+
+
+
diff --git a/xos/tosca/resources/onosapp.py b/xos/tosca/resources/onosapp.py
index 5947400..321600d 100644
--- a/xos/tosca/resources/onosapp.py
+++ b/xos/tosca/resources/onosapp.py
@@ -57,7 +57,7 @@
             v = d.value
             if k.startswith("config_"):
                 self.set_tenant_attr(obj, k, v)
-            elif k.startswith("rest_"):
+            elif k.startswith("rest_") and (k!="rest_hostname") and (k!="rest_port"):
                 self.set_tenant_attr(obj, k, v)
             elif k.startswith("component_config"):
                 self.set_tenant_attr(obj, k, v)
diff --git a/xos/tosca/resources/onosservice.py b/xos/tosca/resources/onosservice.py
index b742ebb..3540dd0 100644
--- a/xos/tosca/resources/onosservice.py
+++ b/xos/tosca/resources/onosservice.py
@@ -13,7 +13,7 @@
 class XOSONOSService(XOSService):
     provides = "tosca.nodes.ONOSService"
     xos_model = ONOSService
-    copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "versionNumber"]
+    copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "versionNumber", "rest_hostname", "rest_port", "no_container", "node_key"]
 
     def set_service_attr(self, obj, prop_name, value):
         value = self.try_intrinsic_function(value)
@@ -36,6 +36,6 @@
             v = d.value
             if k.startswith("config_"):
                 self.set_service_attr(obj, k, v)
-            elif k.startswith("rest_"):
+            elif k.startswith("rest_")  and (k!="rest_hostname") and (k!="rest_port"):
                 self.set_service_attr(obj, k, v)
 
diff --git a/xos/tosca/resources/slice.py b/xos/tosca/resources/slice.py
index 7dcbd59..48e5eb0 100644
--- a/xos/tosca/resources/slice.py
+++ b/xos/tosca/resources/slice.py
@@ -12,7 +12,7 @@
 class XOSSlice(XOSResource):
     provides = "tosca.nodes.Slice"
     xos_model = Slice
-    copyin_props = ["enabled", "description", "slice_url", "max_instances", "default_isolation", "network", "exposed_ports"]
+    copyin_props = ["enabled", "description", "slice_url", "max_instances", "default_isolation", "default_flavor", "network", "exposed_ports"]
 
     def get_xos_args(self):
         args = super(XOSSlice, self).get_xos_args()
diff --git a/xos/tosca/resources/vcpeservice.py b/xos/tosca/resources/vcpeservice.py
index abcdea9..1794010 100644
--- a/xos/tosca/resources/vcpeservice.py
+++ b/xos/tosca/resources/vcpeservice.py
@@ -5,12 +5,12 @@
 sys.path.append("/opt/tosca")
 from translator.toscalib.tosca_template import ToscaTemplate
 
-from services.cord.models import VCPEService
+from services.cord.models import VSGService
 
 from service import XOSService
 
-class XOSVcpeService(XOSService):
-    provides = "tosca.nodes.VCPEService"
-    xos_model = VCPEService
-    copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "private_key_fn", "versionNumber", "backend_network_label"]
+class XOSVsgService(XOSService):
+    provides = "tosca.nodes.VSGService"
+    xos_model = VSGService
+    copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "private_key_fn", "versionNumber", "backend_network_label", "wan_container_gateway_ip", "wan_container_gateway_mac", "wan_container_netbits"]
 
diff --git a/xos/tosca/samples/vtn-external.yaml b/xos/tosca/samples/vtn-external.yaml
new file mode 100644
index 0000000..ee41ac8
--- /dev/null
+++ b/xos/tosca/samples/vtn-external.yaml
@@ -0,0 +1,31 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup CORD-related services -- vOLT, vCPE, vBNG.
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    service_ONOS_VTN:
+      type: tosca.nodes.ONOSService
+      requirements:
+      properties:
+          kind: onos
+          view_url: /admin/onos/onosservice/$id$/
+          no_container: true
+          rest_hostname: ctl.smbaker-xos-neu.xos-pg0.clemson.cloudlab.us
+
+    VTN_ONOS_app:
+      type: tosca.nodes.ONOSVTNApp
+      requirements:
+          - onos_tenant:
+              node: service_ONOS_VTN
+              relationship: tosca.relationships.TenantOfService
+      properties:
+          dependencies: org.onosproject.drivers, org.onosproject.drivers.ovsdb, org.onosproject.lldpprovider, org.onosproject.openflow-base, org.onosproject.ovsdb-base, org.onosproject.dhcp, 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
+
+
diff --git a/xos/xos/settings.py b/xos/xos/settings.py
index 6b503c2..1cec43f 100644
--- a/xos/xos/settings.py
+++ b/xos/xos/settings.py
@@ -225,8 +225,8 @@
     }
 }
 
-RESTAPI_HOSTNAME = getattr(config, "server_restapihostname", getattr(config, "server_hostname", socket.gethostname()))
-RESTAPI_PORT = int(getattr(config, "server_port", "8000"))
+RESTAPI_HOSTNAME = getattr(config, "server_restapi_hostname", getattr(config, "server_hostname", socket.gethostname()))
+RESTAPI_PORT = int(getattr(config, "server_restapi_port", getattr(config, "server_port", "8000")))
 
 BIGQUERY_TABLE = getattr(config, "bigquery_table", "demoevents")