Merge branch 'master' of https://github.com/open-cloud/xos into feature/contentProvider
diff --git a/Dockerfile.cord b/Dockerfile.cord
index 5a2074d..ee0879d 100644
--- a/Dockerfile.cord
+++ b/Dockerfile.cord
@@ -3,3 +3,5 @@
 
 ADD xos/observers/vcpe/supervisor/vcpe-observer.conf /etc/supervisor/conf.d/
 RUN sed -i 's/proxy_ssh=True/proxy_ssh=False/' /opt/xos/observers/vcpe/vcpe_observer_config
+ADD xos/observers/monitoring_channel/supervisor/monitoring_channel_observer.conf /etc/supervisor/conf.d/
+RUN sed -i 's/proxy_ssh=True/proxy_ssh=False/' /opt/xos/observers/monitoring_channel/monitoring_channel_observer_config
diff --git a/cloudlab-init.sh b/cloudlab-init.sh
index f275517..9a2c94a 100755
--- a/cloudlab-init.sh
+++ b/cloudlab-init.sh
@@ -22,6 +22,8 @@
 then
     cp ~/.ssh/id_rsa.pub xos/observers/vcpe/vcpe_public_key
     cp ~/.ssh/id_rsa     xos/observers/vcpe/vcpe_private_key
+    cp ~/.ssh/id_rsa.pub xos/observers/monitoring_channel/monitoring_channel_public_key
+    cp ~/.ssh/id_rsa     xos/observers/monitoring_channel/monitoring_channel_private_key
 fi
 
 sudo docker build -t xos .
@@ -34,6 +36,8 @@
 
 # OpenStack is using port 8000...
 MYIP=$( hostname -i )
+MYFLATLANIF=$( sudo bash -c "netstat -i" |grep flat|awk '{print $1}' )
+MYFLATLANIP=$( ifconfig $MYFLATLANIF | grep "inet addr" | awk -F: '{print $2}' | awk '{print $1}' )
 sudo docker run -d --add-host="ctl:$MYIP" -p 9999:8000 $IMAGE
 
 echo "Waiting for XOS to come up"
@@ -52,7 +56,17 @@
 sudo chmod a+r /tmp/admin-openrc.sh
 #sudo sed -i 's/:5000/:35357/' /tmp/admin-openrc.sh
 source /tmp/admin-openrc.sh
-http --auth $AUTH POST $XOS/xos/controllers/ name=CloudLab deployment=$XOS/xos/deployments/1/ backend_type=OpenStack version=Juno auth_url=$OS_AUTH_URL admin_user=$OS_USERNAME admin_tenant=$OS_TENANT_NAME admin_password=$OS_PASSWORD domain=Default
+
+if [ "$CORD" -ne 1 ]
+then
+     http --auth $AUTH POST $XOS/xos/controllers/ name=CloudLab deployment=$XOS/xos/deployments/1/ backend_type=OpenStack version=Kilo auth_url=$OS_AUTH_URL admin_user=$OS_USERNAME admin_tenant=$OS_TENANT_NAME admin_password=$OS_PASSWORD domain=Default
+else
+     sudo cp /root/setup/settings /tmp
+     sudo chmod a+r /tmp/settings
+     source /tmp/settings
+     source /tmp/admin-openrc.sh
+     http --auth $AUTH POST $XOS/xos/controllers/ name=CloudLab deployment=$XOS/xos/deployments/1/ backend_type=OpenStack version=Kilo auth_url=$OS_AUTH_URL admin_user=$OS_USERNAME admin_tenant=$OS_TENANT_NAME admin_password=$OS_PASSWORD domain=Default rabbit_host=$MYFLATLANIP rabbit_user=$RABBIT_USER rabbit_password=$RABBIT_PASS
+fi
 
 # Add controller to site
 http --auth $AUTH PATCH $XOS/xos/sitedeployments/1/ controller=$XOS/xos/controllers/1/
@@ -78,5 +92,5 @@
 if [ "$CORD" -ne 0 ]
 then
     DOCKER=$( sudo docker ps|grep $IMAGE|awk '{print $NF}' )
-    sudo docker exec $DOCKER bash -c "cd /opt/xos/tosca; python run.py padmin@vicci.org samples/cord-cloudlab.yaml"
+    sudo docker exec $DOCKER bash -c "cd /opt/xos/tosca; python run.py padmin@vicci.org samples/cord-cloudlab.yaml; python run.py padmin@vicci.org samples/ceilometer.yaml"
 fi
diff --git a/xos/apigen/modelgen b/xos/apigen/modelgen
index ec08c01..7f740d1 100644
--- a/xos/apigen/modelgen
+++ b/xos/apigen/modelgen
@@ -15,7 +15,6 @@
 sys.path.append('.')
 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
 from django.db.models.fields.related import ForeignKey, ManyToManyField
-from core.models import *
 
 def singular(foo, keys):
 	for k in keys:
@@ -27,13 +26,24 @@
 
 g = globals()
 
-def enum_classes():
-	model_classes = []
-	for c in g.values():
-		if type(c)==type(PlCoreBase) and c.__name__ not in blacklist:
-			model_classes.append(c)
-	return model_classes
+def enum_classes(apps):
+    model_classes = []
+    for app in apps:
+            app = app + ".models"
+            models_module = __import__(app)
+            for part in app.split(".")[1:]:
+                if hasattr(models_module, "PlCoreBase"):
+                    break
+                models_module = getattr(models_module,part)
 
+            PlCoreBase = getattr(models_module,"PlCoreBase")
+
+            for classname in dir(models_module):
+                    c = getattr(models_module, classname, None)
+                    if type(c)==type(PlCoreBase) and c.__name__ not in blacklist:
+                            model_classes.append(c)
+
+    return model_classes
 
 class GenObj(object):
 	def __str__(self):
@@ -146,15 +156,20 @@
 
 			
 def main():
-	try:
-		output = sys.argv[1]
-	except:
-		print 'Usage: modelgen <output template>'
-		exit(1)
+        if len(sys.argv)==3:
+            apps = [x.strip() for x in sys.argv[1].split(",")]
+            output = sys.argv[2]
+        elif len(sys.argv)==2:
+            apps = ["core"]
+            output = sys.argv[1]
+        else:
+	    print 'Usage: modelgen <output_template>'
+            print 'Alternative Usage: modelgen <app> <output_template>'
+	    exit(1)
 
 	generator = Generator()
 
-	models = enum_classes()
+	models = enum_classes(apps)
 
 	for m in models:
 		generator.add_object(m)
diff --git a/xos/ceilometer/models.py b/xos/ceilometer/models.py
index 50f921b..fbfecd3 100644
--- a/xos/ceilometer/models.py
+++ b/xos/ceilometer/models.py
@@ -27,6 +27,10 @@
 
     KIND = CEILOMETER_KIND
 
+    sync_attributes = ("private_ip", "private_mac",
+                       "ceilometer_ip", "ceilometer_mac",
+                       "nat_ip", "nat_mac",)
+
     default_attributes = {}
     def __init__(self, *args, **kwargs):
         ceilometer_services = CeilometerService.get_service_objects().all()
@@ -66,14 +70,30 @@
         return addresses
 
     @property
+    def nat_ip(self):
+        return self.addresses.get("nat", (None, None))[0]
+
+    @property
+    def nat_mac(self):
+        return self.addresses.get("nat", (None, None))[1]
+
+    @property
     def private_ip(self):
         return self.addresses.get("nat", (None, None))[0]
 
     @property
+    def private_mac(self):
+        return self.addresses.get("nat", (None, None))[1]
+
+    @property
     def ceilometer_ip(self):
         return self.addresses.get("ceilometer", (None, None))[0]
 
     @property
+    def ceilometer_mac(self):
+        return self.addresses.get("ceilometer", (None, None))[1]
+
+    @property
     def site_tenant_list(self):
         tenant_ids = Set()
         for sp in SitePrivilege.objects.filter(user=self.creator):
@@ -109,7 +129,7 @@
     def ceilometer_url(self):
         if not self.ceilometer_ip:
             return None
-        return "http://" + self.ceilometer_ip + "/uri/to/ceilometer/api/"
+        return "http://" + self.private_ip + ":8888/"
 
 def model_policy_monitoring_channel(pk):
     # TODO: this should be made in to a real model_policy
diff --git a/xos/configurations/cord/Dockerfile.cord b/xos/configurations/cord/Dockerfile.cord
index dad9895..a21c7d0 100644
--- a/xos/configurations/cord/Dockerfile.cord
+++ b/xos/configurations/cord/Dockerfile.cord
@@ -5,7 +5,11 @@
 ADD xos/configurations/common/id_rsa.pub /root/setup/padmin_public_key
 ADD xos/configurations/common/id_rsa.pub /opt/xos/observers/vcpe/vcpe_public_key
 ADD xos/configurations/common/id_rsa /opt/xos/observers/vcpe/vcpe_private_key
+ADD xos/configurations/common/id_rsa.pub /opt/xos/observers/onos/onos_key.pub
+ADD xos/configurations/common/id_rsa /opt/xos/observers/onos/onos_key
 ADD xos/observers/vcpe/supervisor/vcpe-observer.conf /etc/supervisor/conf.d/
+ADD xos/observers/vbng/supervisor/vbng-observer.conf /etc/supervisor/conf.d/
+ADD xos/observers/onos/supervisor/onos-observer.conf /etc/supervisor/conf.d/
 RUN sed -i 's/proxy_ssh=True/proxy_ssh=False/' /opt/xos/observers/vcpe/vcpe_observer_config
 
 CMD /usr/bin/make -C /opt/xos/configurations/cord -f Makefile.inside; /bin/bash
diff --git a/xos/configurations/cord/Makefile.inside b/xos/configurations/cord/Makefile.inside
index a4bb5f1..a4494dc 100644
--- a/xos/configurations/cord/Makefile.inside
+++ b/xos/configurations/cord/Makefile.inside
@@ -6,5 +6,8 @@
 	python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/cloudlab-nodes.yaml
 	python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/cord/cord.yaml
 
+setup_subscriber:
+	python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/cord/subscriber.yaml
+
 run_develserver:
 	cd /opt/xos; python manage.py runserver 0.0.0.0:8000 --insecure
diff --git a/xos/configurations/cord/README b/xos/configurations/cord/README
deleted file mode 100644
index 7c80c0f..0000000
--- a/xos/configurations/cord/README
+++ /dev/null
@@ -1,4 +0,0 @@
-This configuration configures XOS with the CORD services.  It is intended to be
-run on CloudLab, on the "ctl" node set up by the OpenStack profile.  It launches
-an XOS container on Cloudlab that runs the XOS develserver.  The container is
-left running in the background.
diff --git a/xos/configurations/cord/README.md b/xos/configurations/cord/README.md
new file mode 100644
index 0000000..5683601
--- /dev/null
+++ b/xos/configurations/cord/README.md
@@ -0,0 +1,97 @@
+# CORD development environment
+
+This configuration can be used to set up a CORD development environment.
+It does the following:
+
+* Sets up a basic dataplane for testing end-to-end packet flow between a subscriber client and the Internet
+* Brings up ONOS apps for controlling the dataplane: virtualbng, olt
+* Configures XOS with the CORD services: vCPE, vBNG, vOLT
+
+**NOTE:** This configuration is under **active development** and is not yet finished!  Some features are not
+fully working yet.
+
+## End-to-end dataplane
+
+The configuration uses XOS to set up an end-to-end dataplane for development of the XOS services and ONOS apps 
+used in CORD.  It abstracts away most of the complexity of the CORD hardware using virtual networks
+and Open vSwitch (OvS) switches.  At a high level the dataplane looks like this:
+
+```
+             olt                virtualbng
+             ----                 ----
+             ONOS                 ONOS
+              |                    |
+client ----> OvS ----> vCPE ----> OvS ----> Internet
+         1         2          3         4
+```
+
+On the datapath are two OvS switches, controlled by the `olt` and `virtualbng` ONOS applications.  Once all the pieces are in
+place, the client at left should be able to obtain an IP address via DHCP from the vCPE and send packets out to the Internet.
+
+All of the components in the above diagram (i.e., client, OvS switches, ONOS, and vCPE) currently run in distinct VMs
+created by XOS.  The numbers in the diagram correspond to networks set up by XOS:
+
+1. subscriber_network
+2. lan_network
+3. wan_network
+4. public_network
+
+## How to run it
+
+The configuration is intended to be run on [CloudLab](http://cloudlab.us), on the *ctl* node set up by the OpenStack profile.
+It launches an XOS container on Cloudlab that runs the XOS develserver.  The container is left running in the background.
+
+Running `make` in this directory creates the XOS Docker container and runs the TOSCA engine with `cord.yaml` to
+configure XOS with the CORD services.  In addition, a number of VMs are created:
+
+1. *Slice mysite_onos*: runs the ONOS Docker container with `virtualbng` app loaded
+1. *Slice mysite_onos*: runs the ONOS Docker container with `olt` app loaded
+1. *Slice mysite_vbng*: for running OvS with the `virtualbng` app as controller
+1. *Slice mysite_volt*: for running OvS with the `olt` app as controller
+1. *Slice mysite_clients*: a subscriber client for end-to-end testing
+
+After the first VM is created (for running the `virtualbng` app) it is necessary to configure XOS's *service_vbng* with its URL.
+Log into XOS, click on *Services* tab at left, then *service_vbng* icon.  Change **Vbng url:** to point to the IP address on
+`flat-lan-1-net` of the VM (it will start with 10.11).
+
+Once all the VMs are up and the ONOS apps are configured, XOS should be able to get an address mapping from the `virtualbng`
+ONOS app when creating a vCPE.  To test this, enter the XOS Docker container and run:
+
+```
+$ cd /opt/xos/configurations/cord/
+$ make -f Makefile.inside setup_subscriber
+```
+
+This will run the TOSCA engine with `subscriber.yaml`.  After a bit, a new VM should be created in slice *mysite_vcpe* running
+the vCPE Docker container.  To verify that it has received an IP address mapping, look at the **Routeable subnet:** field in 
+the appropriate *Vbng tenant* object in XOS.  It should contain an IP address in the 10.254.0.0/24 subnet.
+
+## How to log into ONOS
+
+The ONOS Docker container runs in the VMs belonging to the *mysite_onos* slice.  All ports exposed by the ONOS container are forwarded to the outside, and can be accessed from the *ctl* node using the `flat-lan-1-net` address of the hosting VM.  For example, if the IP addresss of the VM is 10.11.10.30, then it is possible to SSH to ONOS as follows (password is *karaf*):
+
+```
+$ ssh -p 8101 karaf@10.11.10.30
+Password authentication
+Password:
+Welcome to Open Network Operating System (ONOS)!
+     ____  _  ______  ____
+    / __ \/ |/ / __ \/ __/
+   / /_/ /    / /_/ /\ \
+   \____/_/|_/\____/___/
+
+
+Hit '<tab>' for a list of available commands
+and '[cmd] --help' for help on a specific command.
+Hit '<ctrl-d>' or type 'system:shutdown' or 'logout' to shutdown ONOS.
+
+onos>
+```
+
+For instance, to check the IP address mappings managed by the `virtualbng` app:
+
+```
+onos> vbngs
+   Private IP - Public IP
+   10.0.1.3 - 10.254.0.129
+```
diff --git a/xos/configurations/cord/cord.yaml b/xos/configurations/cord/cord.yaml
index 56ab11d..5b48398 100644
--- a/xos/configurations/cord/cord.yaml
+++ b/xos/configurations/cord/cord.yaml
@@ -7,6 +7,7 @@
 
 topology_template:
   node_templates:
+
     # CORD Services
     service_volt:
       type: tosca.nodes.Service
@@ -18,55 +19,6 @@
           view_url: /admin/cord/voltservice/$id$/
           kind: vOLT
 
-    Private:
-      type: tosca.nodes.NetworkTemplate
-
-    # 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
-
-    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
-
-    hpc_client_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
       requirements:
@@ -84,11 +36,148 @@
       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/
+
+    service_ONOS:
+      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/observers/onos/onos_key.pub
+
+    vBNG_ONOS_app:
+      type: tosca.nodes.ONOSvBNGApp
+      requirements:
+          - onos_tenant:
+              node: service_ONOS
+              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_addresses.json: >
+            {
+                "addresses" : [
+                            {
+                                "dpid" : "00:00:00:00:00:00:00:a1",
+                                "port" : "1",
+                                "ips" : [10.0.1.253/24"],
+                                "mac" : "00:00:00:00:00:99"
+
+                            },
+                            {
+                                "dpid" : "00:00:00:00:00:00:00:a5",
+                                "port" : "2",
+                                "ips" : ["10.254.0.1/24"],
+                                "mac" : "00:00:00:00:00:98"
+                            }
+                ]
+            }
+          config_virtualbng.json: >
+            {
+                "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"
+            }
+
+
+    # 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
+
+    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
@@ -100,66 +189,153 @@
               node: mysite
               relationship: tosca.relationships.MemberOfSite
 
-    # Now let's add a subscriber
+    mysite_onos:
+      description: ONOS Controller Slice
+      type: tosca.nodes.Slice
+      requirements:
+          - ONOS:
+              node: service_ONOS
+              relationship: tosca.relationships.MemberOfService
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
 
-    My House:
-       type: tosca.nodes.CORDSubscriber
-       properties:
-           service_specific_id: 1234
-           firewall_enable: false
-           cdn_enable: false
-           url_filter_enable: false
-           url_filter_level: R
+    mysite_vbng:
+      description: slice running OVS controlled by vBNG
+      type: tosca.nodes.Slice
+      requirements:
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
 
-    Mom's PC:
-       type: tosca.nodes.CORDUser
-       properties:
-           mac: 010203040506
-           level: PG_13
-       requirements:
-           - household:
-               node: My House
-               relationship: tosca.relationships.SubscriberDevice
+    mysite_volt:
+      description: OVS controlled by vOLT
+      type: tosca.nodes.Slice
+      requirements:
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
 
-    Dad's PC:
-       type: tosca.nodes.CORDUser
-       properties:
-           mac: 90E2Ba82F975
-           level: PG_13
-       requirements:
-           - household:
-               node: My House
-               relationship: tosca.relationships.SubscriberDevice
+    mysite_clients:
+      description: slice for clients at the subscriber 
+      type: tosca.nodes.Slice
+      requirements:
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
 
-    Jack's Laptop:
-       type: tosca.nodes.CORDUser
-       properties:
-           mac: 685B359D91D5
-           level: PG_13
-       requirements:
-           - household:
-               node: My House
-               relationship: tosca.relationships.SubscriberDevice
 
-    Jill's Laptop:
-       type: tosca.nodes.CORDUser
-       properties:
-           mac: 34363BC9B6A6
-           level: PG_13
-       requirements:
-           - household:
-               node: My House
-               relationship: tosca.relationships.SubscriberDevice
+    # 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
+                relationship: tosca.relationships.MemberOfSlice
 
-    My Volt:
-        type: tosca.nodes.VOLTTenant
-        properties:
-            service_specific_id: 1234
-            vlan_id: 4321
-        requirements:
-            - provider_service:
-                node: service_volt
-                relationship: tosca.relationships.MemberOfService
-            - subscriber:
-                node: My House
-                relationship: tosca.relationships.BelongsToSubscriber
+    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
+                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
+
diff --git a/xos/configurations/cord/subscriber.yaml b/xos/configurations/cord/subscriber.yaml
new file mode 100644
index 0000000..f77f4d5
--- /dev/null
+++ b/xos/configurations/cord/subscriber.yaml
@@ -0,0 +1,82 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Set up a subscriber for CORD
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+
+    # These services defined in cord.yaml
+    service_volt:
+      type: tosca.nodes.Service
+
+    service_vcpe:
+      type: tosca.nodes.VCPEService
+
+    service_vbng:
+      type: tosca.nodes.VBNGService
+
+    # 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
+
+    Mom's PC:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 010203040506
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    Dad's PC:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 90E2Ba82F975
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    Jack's Laptop:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 685B359D91D5
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    Jill's Laptop:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 34363BC9B6A6
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    My Volt:
+        type: tosca.nodes.VOLTTenant
+        properties:
+            service_specific_id: 123
+            vlan_id: 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/Dockerfile b/xos/configurations/opencloud/Dockerfile
index ad85718..5b09507 100644
--- a/xos/configurations/opencloud/Dockerfile
+++ b/xos/configurations/opencloud/Dockerfile
@@ -31,8 +31,8 @@
     libyaml-dev \
     pkg-config \
     supervisor \
-    python-crypto
-    python-httplib2 \
+    python-crypto \
+    python-httplib2>=0.9.1 \
     python-jinja2 \
     python-paramiko \
     python-pip \
@@ -47,7 +47,7 @@
     python-ceilometerclient
 
 RUN pip install \
-    django==1.7
+    django==1.7 \
     djangorestframework==2.4.4 \
     markdown  \ 
     pyyaml \ 
@@ -69,13 +69,14 @@
     pygraphviz \
     dnslib
 
+RUN easy_install --upgrade httplib2
+
 RUN easy_install \
-    django_evolution
+    django_evolution \
     python_gflags \
     google_api_python_client \
     httplib2.ca_certs_locater
 
-RUN easy_install --upgrade httplib2
 
 # Install custom Ansible
 RUN git clone -b release1.8.2 git://github.com/ansible/ansible.git /opt/ansible
@@ -103,7 +104,7 @@
 ADD observer.conf /etc/supervisor/conf.d/
 
 # Get XOS
-ADD xos /opt/xos
+RUN git clone git://github.com/open-cloud/xos.git /tmp/xos && mv /tmp/xos/xos /opt/
 
 # Initscript is broken in Ubuntu
 #ADD observer-initscript /etc/init.d/xosobserver
@@ -134,7 +135,6 @@
 RUN apt-get install -y m4
 RUN pip install python-dateutil
 RUN bash /opt/xos/tosca/install_tosca.sh
-RUN /usr/bin/python /opt/xos/tosca/run.py padmin@vicci.org ${TOSCA_CONFIG_PATH}
 
 EXPOSE 8000
 
diff --git a/xos/configurations/opencloud/Makefile b/xos/configurations/opencloud/Makefile
index aba30dc..863f2b7 100644
--- a/xos/configurations/opencloud/Makefile
+++ b/xos/configurations/opencloud/Makefile
@@ -1,8 +1,13 @@
+RUNNING_CONTAINER:=$(shell sudo docker ps|grep "opencloud_server"|awk '{print $$NF}')
+
 .PHONY: build
 build: ; docker build --rm -t opencloud .
 
 .PHONY: run
-run: ; docker run --rm -d -e TOSCA_CONFIG_PATH=$TOSCA_CONFIG_PATH --name opencloud_server opencloud
+run: ; docker run --rm  --name opencloud_server opencloud
+
+.PHONY: runtosca
+runtosca: ; docker exec -it $RUNNING_CONTAINER /usr/bin/python /opt/xos/tosca/run.py padmin@vicci.org $TOSCA_CONFIG_PATH
 
 .PHONY: stop
 stop: ; docker stop opencloud_server
diff --git a/xos/configurations/opencloud/ansible-hosts b/xos/configurations/opencloud/ansible-hosts
new file mode 100644
index 0000000..0dd74f1
--- /dev/null
+++ b/xos/configurations/opencloud/ansible-hosts
@@ -0,0 +1,2 @@
+[localhost]
+127.0.0.1
diff --git a/xos/configurations/opencloud/observer.conf b/xos/configurations/opencloud/observer.conf
new file mode 100644
index 0000000..92545eb
--- /dev/null
+++ b/xos/configurations/opencloud/observer.conf
@@ -0,0 +1,2 @@
+[program:observer]
+command=python /opt/xos/xos-observer.py
diff --git a/xos/cord/models.py b/xos/cord/models.py
index d55ff2d..26d7d24 100644
--- a/xos/cord/models.py
+++ b/xos/cord/models.py
@@ -689,7 +689,7 @@
 class VBNGService(Service):
     KIND = VBNG_KIND
 
-    simple_attributes = ( ("vbng_url", "http://10.0.3.136:8181/onos/virtualbng/"), )
+    simple_attributes = ( ("vbng_url", ""), )  # "http://10.0.3.136:8181/onos/virtualbng/"
 
     class Meta:
         app_label = "cord"
diff --git a/xos/core/admin.py b/xos/core/admin.py
index b3dc67d..a5b89be 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -757,7 +757,7 @@
 
 class ControllerAdmin(XOSBaseAdmin):
     model = Controller
-    fieldList = ['deployment', 'name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password', 'domain']
+    fieldList = ['deployment', 'name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password', 'domain', 'rabbit_host', 'rabbit_user', 'rabbit_password']
     fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
     inlines = [ControllerSiteInline] # ,ControllerImagesInline]
     list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
@@ -787,6 +787,23 @@
 
         return tabs
 
+class TenantAttributeAdmin(XOSBaseAdmin):
+    model = TenantAttribute
+    list_display = ('backend_status_icon', 'tenant', 'name', 'value')
+    list_display_links = ('backend_status_icon', 'name')
+    fieldList = ('backend_status_text', 'tenant', 'name', 'value', )
+    fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
+    readonly_fields = ('backend_status_text', )
+
+    suit_form_tabs =(('general', 'Tenant Root Details'),
+    )
+
+class TenantAttrAsTabInline(XOSTabularInline):
+    model = TenantAttribute
+    fields = ['name','value']
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-tenantattrs'
+
 class TenantRootRoleAdmin(XOSBaseAdmin):
     model = TenantRootRole
     fields = ('role',)
@@ -2006,4 +2023,5 @@
     admin.site.register(Flavor, FlavorAdmin)
     admin.site.register(TenantRoot, TenantRootAdmin)
     admin.site.register(TenantRootRole, TenantRootRoleAdmin)
+    admin.site.register(TenantAttribute, TenantAttributeAdmin)
 
diff --git a/xos/core/models/__init__.py b/xos/core/models/__init__.py
index ad271a4..c380e9c 100644
--- a/xos/core/models/__init__.py
+++ b/xos/core/models/__init__.py
@@ -2,7 +2,7 @@
 from .project import Project
 from .singletonmodel import SingletonModel
 from .service import Service, Tenant, TenantWithContainer, CoarseTenant, ServicePrivilege, TenantRoot, TenantRootPrivilege, TenantRootRole, Subscriber, Provider
-from .service import ServiceAttribute
+from .service import ServiceAttribute, TenantAttribute
 from .tag import Tag
 from .role import Role
 from .site import Site, Deployment, DeploymentRole, DeploymentPrivilege, Controller, ControllerRole, ControllerSite, SiteDeployment
diff --git a/xos/core/models/instance.py b/xos/core/models/instance.py
index 6240c34..75826f6 100644
--- a/xos/core/models/instance.py
+++ b/xos/core/models/instance.py
@@ -158,6 +158,17 @@
                 return ns.ip
         return None
 
+    # return an address on nat-net
+    def get_network_ip(self, pattern):
+        for ns in self.ports.all():
+            if pattern in ns.network.name.lower():
+                return ns.ip
+        return None
+
+    # return an address that the synchronizer can use to SSH to the instance
+    def get_ssh_ip(self):
+        return self.get_network_ip("nat")
+
     @staticmethod
     def select_by_user(user):
         if user.is_admin:
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 9530c72..950ce02 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -67,6 +67,10 @@
         return cls.objects.filter(kind = cls.KIND)
 
     @classmethod
+    def get_deleted_service_objects(cls):
+        return cls.deleted_objects.filter(kind = cls.KIND)
+
+    @classmethod
     def get_service_objects_by_user(cls, user):
         return cls.select_by_user(user).filter(kind = cls.KIND)
 
@@ -488,6 +492,11 @@
 
     KIND = "Provider"
 
+class TenantAttribute(PlCoreBase):
+    name = models.CharField(help_text="Attribute Name", max_length=128)
+    value = models.TextField(help_text="Attribute Value")
+    tenant = models.ForeignKey(Tenant, related_name='tenantattributes', help_text="The Tenant this attribute is associated with")
+
 class TenantRootRole(PlCoreBase):
     ROLE_CHOICES = (('admin','Admin'), ('access','Access'))
 
diff --git a/xos/core/models/site.py b/xos/core/models/site.py
index 26ff191..1bdef36 100644
--- a/xos/core/models/site.py
+++ b/xos/core/models/site.py
@@ -263,6 +263,9 @@
     admin_password = StrippedCharField(max_length=200, null=True, blank=True, help_text="Password of theadmin user at this controller")
     admin_tenant = StrippedCharField(max_length=200, null=True, blank=True, help_text="Name of the tenant the admin user belongs to")
     domain = StrippedCharField(max_length=200, null=True, blank=True, help_text="Name of the domain this controller belongs to")
+    rabbit_host = StrippedCharField(max_length=200, null=True, blank=True, help_text="IP address of rabbitmq server at this controller")
+    rabbit_user = StrippedCharField(max_length=200, null=True, blank=True, help_text="Username of rabbitmq server at this controller")
+    rabbit_password = StrippedCharField(max_length=200, null=True, blank=True, help_text="Password of rabbitmq server at this controller")
     deployment = models.ForeignKey(Deployment,related_name='controllerdeployments')
 
     def __init__(self, *args, **kwargs):
diff --git a/xos/core/xoslib/methods/monitoringchannel.py b/xos/core/xoslib/methods/monitoringchannel.py
new file mode 100644
index 0000000..baab346
--- /dev/null
+++ b/xos/core/xoslib/methods/monitoringchannel.py
@@ -0,0 +1,89 @@
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from rest_framework.reverse import reverse
+from rest_framework import serializers
+from rest_framework import generics
+from rest_framework import status
+from core.models import *
+from django.forms import widgets
+from ceilometer.models import MonitoringChannel, CeilometerService
+from plus import PlusSerializerMixin
+from xos.apibase import XOSListCreateAPIView, XOSRetrieveUpdateDestroyAPIView, XOSPermissionDenied
+
+if hasattr(serializers, "ReadOnlyField"):
+    # rest_framework 3.x
+    ReadOnlyField = serializers.ReadOnlyField
+else:
+    # rest_framework 2.x
+    ReadOnlyField = serializers.Field
+
+def get_default_ceilometer_service():
+    ceilometer_services = CeilometerService.get_service_objects().all()
+    if ceilometer_services:
+        return ceilometer_services[0].id
+    return None
+
+class MonitoringChannelSerializer(serializers.ModelSerializer, PlusSerializerMixin):
+        id = ReadOnlyField()
+        service_specific_attribute = ReadOnlyField()
+        ceilometer_url = ReadOnlyField()
+        tenant_list_str = ReadOnlyField()
+        creator = ReadOnlyField()
+        instance = ReadOnlyField()
+        provider_service = serializers.PrimaryKeyRelatedField(queryset=CeilometerService.get_service_objects().all(), default=get_default_ceilometer_service)
+
+        humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+
+        computeNodeName = serializers.SerializerMethodField("getComputeNodeName")
+
+        class Meta:
+            model = MonitoringChannel
+            fields = ('humanReadableName', 'id', 'provider_service', 'service_specific_attribute', 'ceilometer_url', 'tenant_list_str', 'creator', 'instance', 'computeNodeName' )
+
+        def getHumanReadableName(self, obj):
+            return obj.__unicode__()
+
+        def getComputeNodeName(self, obj):
+            instance = obj.instance
+            if not instance:
+                return None
+            return instance.node.name
+
+class MonitoringChannelList(XOSListCreateAPIView):
+    serializer_class = MonitoringChannelSerializer
+
+    method_kind = "list"
+    method_name = "monitoringchannel"
+
+    def get_queryset(self):
+        queryset = MonitoringChannel.get_tenant_objects().select_related().all()
+
+        current_user = self.request.user.username
+        if current_user is not None:
+            ids = [x.id for x in queryset if x.creator.username==current_user]
+            queryset = queryset.filter(id__in=ids)
+
+        return queryset
+
+    def post(self, request, format=None):
+        current_user = request.user.username
+        existing_obj = None
+        for obj in MonitoringChannel.get_tenant_objects().all():
+            if (obj.creator.username == current_user):
+               existing_obj = obj
+               break
+
+        if existing_obj:
+            serializer = MonitoringChannelSerializer(existing_obj)
+            headers = self.get_success_headers(serializer.data)
+            return Response( serializer.data, status=status.HTTP_200_OK )
+
+        return super(MonitoringChannelList, self).post(request, format)
+
+class MonitoringChannelDetail(XOSRetrieveUpdateDestroyAPIView):
+    serializer_class = MonitoringChannelSerializer
+    queryset = MonitoringChannel.get_tenant_objects().select_related().all()
+
+    method_kind = "detail"
+    method_name = "monitoringchannel"
+
diff --git a/xos/dmdot b/xos/dmdot
index 8570c4d..124c7cf 100755
--- a/xos/dmdot
+++ b/xos/dmdot
@@ -12,7 +12,7 @@
 from django.db.models.fields.related import ForeignKey
 
 # defaults
-apps = ["core", "hpc", "cord", "requestrouter"]
+apps = ["core", "hpc", "cord", "requestrouter", "services.onos"]
 output = "-json"
 
 # syntax: dmdot [-json | -dot] [app_name]
@@ -22,7 +22,7 @@
     if arg.startswith("-"):
         output = arg
     else:
-        app = arg
+        apps = [arg]
 
 model_classes = []
 class_names = []
diff --git a/xos/helloworld/view.py b/xos/helloworld/view.py
new file mode 100644
index 0000000..b3eec29
--- /dev/null
+++ b/xos/helloworld/view.py
@@ -0,0 +1,58 @@
+from django.http import HttpResponse
+from django.views.generic import TemplateView, View
+from django import template
+from monitor import driver
+from core.models import *
+from helloworld.models import *
+import json
+import os
+import time
+import tempfile
+
+class HelloWorldView(TemplateView):
+    head_template = r"""{% extends "admin/dashboard/dashboard_base.html" %}
+       {% load admin_static %}
+       {% block content %}
+    """
+
+    tail_template = r"{% endblock %}"
+
+    def get(self, request, name="root", *args, **kwargs):
+        head_template = self.head_template
+        tail_template = self.tail_template
+
+        try:
+            hello_name = request.GET['hello_name']
+            world_name = request.GET['world_name']
+            instance_id_str = request.GET['instance_id']
+            instance_id = int(instance_id_str)
+
+            i = Instance.objects.get(pk=instance_id)
+            i.pk=None
+            i.userData=None
+            i.instance_id=None
+            i.instance_name=None
+            i.enacted=None
+            i.save()
+            h = Hello(name=hello_name,sliver_backref=i)
+            w = World(hello=h,name=world_name)
+            h.save()
+            w.save()
+
+            t = template.Template(head_template + 'Done. New instance id: %r'%i.pk + self.tail_template)
+        except KeyError:
+            html = """<form>
+                Hello string: <input type="text" name="hello_name" placeholder="Planet"><br>
+                World string: <input type="text" name="world_name" placeholder="Earth"><br>
+                Id of instance to copy: <input type="text" name="instance_id" placeholder="3"><br>
+                <input type="submit" value="Submit">
+                  </form>"""
+
+            t = template.Template(head_template + html + self.tail_template)
+
+        response_kwargs = {}
+        response_kwargs.setdefault('content_type', self.content_type)
+        return self.response_class(
+            request = request,
+            template = t,
+            **response_kwargs)
diff --git a/xos/observers/base/SyncInstanceUsingAnsible.py b/xos/observers/base/SyncInstanceUsingAnsible.py
index 9455780..901bc97 100644
--- a/xos/observers/base/SyncInstanceUsingAnsible.py
+++ b/xos/observers/base/SyncInstanceUsingAnsible.py
@@ -8,7 +8,7 @@
 from xos.config import Config
 from observer.syncstep import SyncStep
 from observer.ansible import run_template_ssh
-from core.models import Service, Slice
+from core.models import Service, Slice, ControllerSlice, ControllerUser
 from util.logger import Logger, logging
 
 logger = Logger(level=logging.INFO)
@@ -58,25 +58,59 @@
     def sync_fields(self, o, fields):
         self.run_playbook(o, fields)
 
+    def prepare_record(self, o):
+        pass
+
     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)
-                 }
+        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,
+                     }
+        else:
+            # 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
+
+            cslice = ControllerSlice.objects.get(slice=instance.slice)
+            if not cslice:
+                raise Exception("Controller slice object for %s does not exist" % instance.slice.name)
+
+            cuser = ControllerUser.objects.get(user=instance.creator)
+            if not cuser:
+                raise Exception("Controller user object for %s does not exist" % instance.creator)
+
+            fields = { "instance_name": instance.name,
+                       "hostname": instance.node.name,
+                       "instance_id": instance.instance_id,
+                       "private_key": service_key,
+                       "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,
+                       "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/observers/hello_world/run.sh b/xos/observers/hello_world/run.sh
deleted file mode 100755
index 1107a7a..0000000
--- a/xos/observers/hello_world/run.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#if [[ ! -e ./hpc-backend.py ]]; then
-#    ln -s ../xos-observer.py hpc-backend.py
-#fi
-
-export XOS_DIR=/opt/xos
-python helloworld-observer.py  -C $XOS_DIR/observers/hello_world/helloworld_config
diff --git a/xos/observers/hello_world/helloworld-observer.py b/xos/observers/helloworld/helloworld-observer.py
similarity index 100%
rename from xos/observers/hello_world/helloworld-observer.py
rename to xos/observers/helloworld/helloworld-observer.py
diff --git a/xos/observers/hello_world/helloworld_config b/xos/observers/helloworld/helloworld_config
similarity index 94%
rename from xos/observers/hello_world/helloworld_config
rename to xos/observers/helloworld/helloworld_config
index 97248ae..671af51 100644
--- a/xos/observers/hello_world/helloworld_config
+++ b/xos/observers/helloworld/helloworld_config
@@ -37,7 +37,7 @@
 images_directory=/opt/xos/images
 dependency_graph=/opt/xos/model-deps
 logfile=/var/log/xos_backend.log
-steps_dir=/opt/xos/observers/hello_world/steps
+steps_dir=/opt/xos/observers/helloworld/steps
 
 [gui]
 disable_minidashboard=True
diff --git a/xos/observers/hello_world/model-deps b/xos/observers/helloworld/model-deps
similarity index 100%
rename from xos/observers/hello_world/model-deps
rename to xos/observers/helloworld/model-deps
diff --git a/xos/observers/hello_world/nohup.out b/xos/observers/helloworld/nohup.out
similarity index 100%
rename from xos/observers/hello_world/nohup.out
rename to xos/observers/helloworld/nohup.out
diff --git a/xos/observers/helloworld/run.sh b/xos/observers/helloworld/run.sh
new file mode 100755
index 0000000..f56ffe3
--- /dev/null
+++ b/xos/observers/helloworld/run.sh
@@ -0,0 +1,6 @@
+#if [[ ! -e ./hpc-backend.py ]]; then
+#    ln -s ../xos-observer.py hpc-backend.py
+#fi
+
+export XOS_DIR=/opt/xos
+python helloworld-observer.py  -C $XOS_DIR/observers/helloworld/helloworld_config
diff --git a/xos/observers/hello_world/start.sh b/xos/observers/helloworld/start.sh
similarity index 100%
rename from xos/observers/hello_world/start.sh
rename to xos/observers/helloworld/start.sh
diff --git a/xos/observers/hello_world/steps/sync_hello.py b/xos/observers/helloworld/steps/sync_hello.py
similarity index 65%
rename from xos/observers/hello_world/steps/sync_hello.py
rename to xos/observers/helloworld/steps/sync_hello.py
index f59ec5c..1fb8c2b 100644
--- a/xos/observers/hello_world/steps/sync_hello.py
+++ b/xos/observers/helloworld/steps/sync_hello.py
@@ -18,7 +18,9 @@
     requested_interval=0
     
     def sync_record(self, record):
-        open('/tmp/hello-synchronizer','w').write(record.name)	
+        instance = record.sliver_backref        
+        instance.userData="packages:\n  - apache2\nruncmd:\n  - update-rc.d apache2 enable\n  - service apache2 start\nwrite_files:\n-   content: Hello %s\n    path: /var/www/html/hello.txt"%record.name
+        instance.save()
         
     def delete_record(self, m):
         return
diff --git a/xos/observers/hello_world/steps/sync_world.py b/xos/observers/helloworld/steps/sync_world.py
similarity index 100%
rename from xos/observers/hello_world/steps/sync_world.py
rename to xos/observers/helloworld/steps/sync_world.py
diff --git a/xos/observers/hello_world/stop.sh b/xos/observers/helloworld/stop.sh
similarity index 100%
rename from xos/observers/hello_world/stop.sh
rename to xos/observers/helloworld/stop.sh
diff --git a/xos/observers/monitoring_channel/files/docker.list b/xos/observers/monitoring_channel/files/docker.list
new file mode 100644
index 0000000..0ee9ae0
--- /dev/null
+++ b/xos/observers/monitoring_channel/files/docker.list
@@ -0,0 +1 @@
+deb https://get.docker.com/ubuntu docker main
diff --git a/xos/observers/monitoring_channel/files/vm-resolv.conf b/xos/observers/monitoring_channel/files/vm-resolv.conf
new file mode 100644
index 0000000..cae093a
--- /dev/null
+++ b/xos/observers/monitoring_channel/files/vm-resolv.conf
@@ -0,0 +1 @@
+nameserver 8.8.8.8
diff --git a/xos/observers/monitoring_channel/model-deps b/xos/observers/monitoring_channel/model-deps
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/xos/observers/monitoring_channel/model-deps
@@ -0,0 +1 @@
+{}
diff --git a/xos/observers/hello_world/helloworld-observer.py b/xos/observers/monitoring_channel/monitoring_channel_observer.py
similarity index 100%
copy from xos/observers/hello_world/helloworld-observer.py
copy to xos/observers/monitoring_channel/monitoring_channel_observer.py
diff --git a/xos/observers/monitoring_channel/monitoring_channel_observer_config b/xos/observers/monitoring_channel/monitoring_channel_observer_config
new file mode 100644
index 0000000..922a019
--- /dev/null
+++ b/xos/observers/monitoring_channel/monitoring_channel_observer_config
@@ -0,0 +1,40 @@
+
+[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=monitoring_channel
+dependency_graph=/opt/xos/observers/monitoring_channel/model-deps
+steps_dir=/opt/xos/observers/monitoring_channel/steps
+sys_dir=/opt/xos/observers/monitoring_channel/sys
+deleters_dir=/opt/xos/observers/monitoring_channel/deleters
+log_file=console
+driver=None
+pretend=False
+backoff_disabled=True
+save_ansible_output=True
+proxy_ssh=True
+full_setup=True
+
+[feefie]
+client_id='vicci_dev_central'
+user_id='pl'
diff --git a/xos/observers/monitoring_channel/steps/sync_monitoringchannel.py b/xos/observers/monitoring_channel/steps/sync_monitoringchannel.py
new file mode 100644
index 0000000..919b4ea
--- /dev/null
+++ b/xos/observers/monitoring_channel/steps/sync_monitoringchannel.py
@@ -0,0 +1,74 @@
+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 observer.syncstep import SyncStep
+from observer.ansible import run_template_ssh
+from observers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
+from core.models import Service, Slice
+from ceilometer.models import MonitoringChannel
+from util.logger import Logger, logging
+
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+logger = Logger(level=logging.INFO)
+
+class SyncMonitoringChannel(SyncInstanceUsingAnsible):
+    provides=[MonitoringChannel]
+    observes=MonitoringChannel
+    requested_interval=0
+    template_name = "sync_monitoringchannel.yaml"
+    service_key_name = "/opt/xos/observers/monitoring_channel/monitoring_channel_private_key"
+
+    def __init__(self, *args, **kwargs):
+        super(SyncMonitoringChannel, self).__init__(*args, **kwargs)
+
+    def fetch_pending(self, deleted):
+        if (not deleted):
+            objs = MonitoringChannel.get_tenant_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
+        else:
+            objs = MonitoringChannel.get_deleted_tenant_objects()
+
+        return objs
+
+    def get_extra_attributes(self, o):
+        # This is a place to include extra attributes. In the case of Monitoring Channel, we need to know
+        #   1) Allowed tenant ids
+        #   2) Ceilometer API service endpoint URL if running externally
+        #   3) Credentials to access Ceilometer API service
+
+        instance = self.get_instance(o)
+
+        try:
+            full_setup = Config().observer_full_setup
+        except:
+            full_setup = True
+
+        fields = {"unique_id": o.id,
+                  "allowed_tenant_ids": o.tenant_list,
+                  "auth_url":instance.controller.auth_url,
+                  "admin_user":instance.controller.admin_user,
+                  "admin_password":instance.controller.admin_password,
+                  "admin_tenant":instance.controller.admin_tenant,
+                  "full_setup": full_setup}
+
+        return fields
+
+    def run_playbook(self, o, fields):
+        #ansible_hash = hashlib.md5(repr(sorted(fields.items()))).hexdigest()
+        #quick_update = (o.last_ansible_hash == ansible_hash)
+
+        #if quick_update:
+        #    logger.info("quick_update triggered; skipping ansible recipe")
+        #else:
+        super(SyncMonitoringChannel, self).run_playbook(o, fields)
+
+        #o.last_ansible_hash = ansible_hash
+
+    def delete_record(self, m):
+        pass
diff --git a/xos/observers/monitoring_channel/steps/sync_monitoringchannel.yaml b/xos/observers/monitoring_channel/steps/sync_monitoringchannel.yaml
new file mode 100644
index 0000000..bbe284f
--- /dev/null
+++ b/xos/observers/monitoring_channel/steps/sync_monitoringchannel.yaml
@@ -0,0 +1,89 @@
+---
+- hosts: {{ instance_name }}
+  gather_facts: False
+  connection: ssh
+  user: ubuntu
+  sudo: yes
+  vars:
+      unique_id: {{ unique_id }}
+      auth_url: {{ auth_url }}
+      admin_user: {{ admin_user }}
+      admin_password: {{ admin_password }}
+      admin_tenant: {{ admin_tenant }}
+      shared_lan_ip: {{ private_ip }}
+      shared_lan_mac: {{ private_mac }}
+      headnode_flat_lan_ip: {{ rabbit_host }}
+      ceilometer_client_acess_ip: {{ ceilometer_ip }}
+      ceilometer_client_acess_mac: {{ ceilometer_mac }}
+      allowed_tenant_ids:
+        {% for allowed_tenant_id in allowed_tenant_ids %}
+        - {{ allowed_tenant_id }}
+        {% endfor %}
+
+  tasks:
+{% if full_setup %}
+  - name: Docker repository
+    copy: src=/opt/xos/observers/monitoring_channel/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-1.5.0 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: 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/observers/monitoring_channel/files/vm-resolv.conf
+      dest=/etc/resolv.conf
+{% endif %}
+
+  - name: ceilometer proxy config
+    template: src=/opt/xos/observers/monitoring_channel/templates/ceilometer_proxy_config.j2 dest=/usr/local/share/monitoring-channel-{{ unique_id }}_ceilometer_proxy_config mode=0777
+
+  - name: Monitoring channel upstart
+    template: src=/opt/xos/observers/monitoring_channel/templates/monitoring-channel.conf.j2 dest=/etc/init/monitoring-channel-{{ unique_id }}.conf
+
+  - name: Monitoring channel startup script
+    template: src=/opt/xos/observers/monitoring_channel/templates/start-monitoring-channel.sh.j2 dest=/usr/local/sbin/start-monitoring-channel-{{ unique_id }}.sh mode=0755
+    notify:
+#    - restart monitoring-channel
+     - stop monitoring-channel
+     - remove container
+     - start monitoring-channel
+
+# These are samples, not necessary for correct function of demo
+
+  - name: Make sure Monitoring channel service is running
+    service: name=monitoring-channel-{{ unique_id }} state=started
+
+  handlers:
+  - name: restart monitoring-channel
+    shell: service monitoring-channel-{{ unique_id }} stop; sleep 1; service vcpe-{{ unique_id }} start
+
+  - name: stop monitoring-channel
+    service: name=monitoring-channel-{{ unique_id }} state=stopped
+
+  - name: remove container
+    docker: name=monitoring-channel-{{ unique_id }} state=absent image=docker-vcpe
+
+  - name: start monitoring-channel
+    service: name=monitoring-channel-{{ unique_id }} state=started
diff --git a/xos/observers/monitoring_channel/supervisor/monitoring_channel_observer.conf b/xos/observers/monitoring_channel/supervisor/monitoring_channel_observer.conf
new file mode 100644
index 0000000..1b78703
--- /dev/null
+++ b/xos/observers/monitoring_channel/supervisor/monitoring_channel_observer.conf
@@ -0,0 +1,2 @@
+[program:monitoring_channel_observer]
+command=python /opt/xos/observers/monitoring_channel/monitoring_channel_observer.py -C /opt/xos/observers/monitoring_channel/monitoring_channel_observer_config
diff --git a/xos/observers/monitoring_channel/templates/Dockerfile.monitoring_channel b/xos/observers/monitoring_channel/templates/Dockerfile.monitoring_channel
new file mode 100644
index 0000000..45defb8
--- /dev/null
+++ b/xos/observers/monitoring_channel/templates/Dockerfile.monitoring_channel
@@ -0,0 +1,26 @@
+FROM       ubuntu:14.04.2
+MAINTAINER Andy Bavier <acb@cs.princeton.edu>
+
+# XXX Workaround for docker bug:
+# https://github.com/docker/docker/issues/6345
+# Kernel 3.15 breaks docker, uss the line below as a workaround
+# until there is a fix
+RUN ln -s -f /bin/true /usr/bin/chfn
+# XXX End workaround
+
+# Install.
+RUN apt-get update && apt-get install -y \
+    python-pip \
+    python-dev
+
+RUN pip install web.py
+RUN pip install wsgilog
+RUN pip install python-ceilometerclient
+RUN mkdir -p /usr/local/share
+ADD ceilometer_proxy_server.py /usr/local/share/
+RUN chmod +x /usr/local/share/ceilometer_proxy_server.py
+ADD start_ceilometer_proxy /usr/local/sbin/
+RUN chmod +x /usr/local/sbin/start_ceilometer_proxy
+EXPOSE 8000
+WORKDIR /usr/local/share
+CMD /usr/local/sbin/start_ceilometer_proxy
diff --git a/xos/observers/monitoring_channel/templates/ceilometer_proxy_config.j2 b/xos/observers/monitoring_channel/templates/ceilometer_proxy_config.j2
new file mode 100644
index 0000000..cba6f2a
--- /dev/null
+++ b/xos/observers/monitoring_channel/templates/ceilometer_proxy_config.j2
@@ -0,0 +1,14 @@
+# This file autogenerated by monitoring-channel observer
+# It contains a list of attributes to be used by ceilometer proxy web server
+# syntax: key=value
+
+[default]
+auth_url={{ auth_url }}
+admin_user={{ admin_user }}
+admin_tenant={{ admin_tenant }}
+admin_password={{ admin_password }}
+
+[allowed_tenants]
+{% for tenant_id in allowed_tenant_ids %}
+{{ tenant_id }}
+{% endfor %}
diff --git a/xos/observers/monitoring_channel/templates/ceilometer_proxy_server.py b/xos/observers/monitoring_channel/templates/ceilometer_proxy_server.py
new file mode 100644
index 0000000..711e996
--- /dev/null
+++ b/xos/observers/monitoring_channel/templates/ceilometer_proxy_server.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python
+import web
+import ConfigParser
+import io
+import json
+from ceilometerclient import client
+import logging
+from wsgilog import WsgiLog
+
+web.config.debug=False
+
+logfile = "ceilometer_proxy_server.log"
+level=logging.INFO
+logger=logging.getLogger('ceilometer_proxy_server')
+logger.setLevel(level)
+handler=logging.handlers.RotatingFileHandler(logfile,maxBytes=1000000, backupCount=1)
+logger.addHandler(handler)
+
+class FileLog(WsgiLog):
+    def __init__(self, application):
+        WsgiLog.__init__(
+            self,
+            application,
+            logformat = '%(message)s',
+            tofile = True,
+            toprint = True,
+            prnlevel = level,
+            file = logfile,
+            backups =1
+            )
+    def __call__(self, environ, start_response):
+        def hstart_response(status, response_headers, *args):
+             out = start_response(status, response_headers, *args)
+             try:
+                 logline=environ["SERVER_PROTOCOL"]+" "+environ["REQUEST_METHOD"]+" "+environ["REQUEST_URI"]+" - "+status
+             except err:
+                 logline="Could not log <%s> due to err <%s>" % (str(environ), err)
+             logger.info(logline)
+
+             return out
+
+        return super(FileLog, self).__call__(environ, hstart_response)
+
+#TODOs:
+#-See if we can avoid using python-ceilometerclient and instead use the REST calls directly with AuthToken
+#
+urls = (
+    r'^/v2/meters$', 'meter_list',
+    r'^/v2/meters/(?P<meter_name>[A-Za-z0-9_:.\-]+)/statistics$', 'statistics_list',
+    r'^/v2/samples$', 'sample_list',
+    r'^/v2/resources$', 'resource_list',
+)
+
+app = web.application(urls, globals())
+
+config = None
+ceilometer_client = None
+
+
+def parse_ceilometer_proxy_config():
+    global config
+    config = ConfigParser.RawConfigParser(allow_no_value=True)
+    config.read('ceilometer_proxy_config')
+ 
+def ceilometerclient():
+    global config, ceilometer_client
+    if ceilometer_client:
+         return ceilometer_client
+
+    if not config:
+         parse_ceilometer_proxy_config()
+
+    keystone = {}
+    keystone['os_username']=config.get('default','admin_user')
+    keystone['os_password']=config.get('default','admin_password')
+    keystone['os_auth_url']=config.get('default','auth_url')
+    keystone['os_tenant_name']=config.get('default','admin_tenant')
+    ceilometer_client = client.get_client(2,**keystone)
+    logger.info('ceilometer get_client is successful')
+    return ceilometer_client
+
+def make_query(user_id=None, tenant_id=None, resource_id=None,
+               user_ids=None, tenant_ids=None, resource_ids=None):
+    """Returns query built from given parameters.
+
+    This query can be then used for querying resources, meters and
+    statistics.
+
+    :Parameters:
+      - `user_id`: user_id, has a priority over list of ids
+      - `tenant_id`: tenant_id, has a priority over list of ids
+      - `resource_id`: resource_id, has a priority over list of ids
+      - `user_ids`: list of user_ids
+      - `tenant_ids`: list of tenant_ids
+      - `resource_ids`: list of resource_ids
+    """
+    user_ids = user_ids or []
+    tenant_ids = tenant_ids or []
+    resource_ids = resource_ids or []
+
+    query = []
+    if user_id:
+        user_ids = [user_id]
+    for u_id in user_ids:
+        query.append({"field": "user_id", "op": "eq", "value": u_id})
+
+    if tenant_id:
+        tenant_ids = [tenant_id]
+    for t_id in tenant_ids:
+        query.append({"field": "project_id", "op": "eq", "value": t_id})
+
+    if resource_id:
+        resource_ids = [resource_id]
+    for r_id in resource_ids:
+        query.append({"field": "resource_id", "op": "eq", "value": r_id})
+
+    return query
+
+def filter_query_params(query_params):
+    new_query=[]
+    i=0
+    user_specified_tenants=[]
+    for field in query_params['q.field']:
+        if field != 'project_id':
+            query = {}
+            query['field']=field
+            if query_params['q.op'][i] != '':
+                 query['op']=query_params['q.op'][i]
+            query['value']=query_params['q.value'][i]
+            new_query.append(query)
+        else:
+            user_specified_tenants.append(query_params['q.value'][i])
+        i=i+1
+    return new_query,user_specified_tenants
+
+class meter_list:
+    def GET(self):
+        global config
+        keyword_args = {
+             "q.field": [],
+             "q.op": [],
+             "q.type": [],
+             "q.value": [],
+        }
+        query_params = web.input(**keyword_args)
+        new_query, user_specified_tenants = filter_query_params(query_params)
+
+        client = ceilometerclient()
+        meters=[]
+        for (k,v) in config.items('allowed_tenants'):
+             if user_specified_tenants and (k not in user_specified_tenants):
+                 continue
+             final_query=[]
+             final_query.extend(new_query)
+             query = make_query(tenant_id=k)
+             final_query.extend(query)
+             logger.debug('final query=%s',final_query)
+             results = client.meters.list(q=final_query)
+             meters.extend(results)
+        return json.dumps([ob._info for ob in meters])
+
+class statistics_list:
+    def GET(self, meter_name):
+        global config
+        keyword_args = {
+             "q.field": [],
+             "q.op": [],
+             "q.type": [],
+             "q.value": [],
+             "period": None
+        }
+        query_params = web.input(**keyword_args)
+        new_query, user_specified_tenants = filter_query_params(query_params)
+
+        client = ceilometerclient()
+        period = query_params.period
+        statistics = []
+        for (k,v) in config.items('allowed_tenants'):
+              if user_specified_tenants and (k not in user_specified_tenants):
+                  continue
+              final_query=[]
+              final_query.extend(new_query)
+              query = make_query(tenant_id=k)
+              final_query.extend(query)
+              logger.debug('final query=%s',final_query)
+              results = client.statistics.list(meter_name=meter_name, q=final_query, period=period)
+              statistics.extend(results)
+        return json.dumps([ob._info for ob in statistics])
+
+class sample_list:
+    def GET(self):
+        global config
+        keyword_args = {
+             "q.field": [],
+             "q.op": [],
+             "q.type": [],
+             "q.value": [],
+        }
+        query_params = web.input(**keyword_args)
+        new_query, user_specified_tenants = filter_query_params(query_params)
+
+        client = ceilometerclient()
+        samples=[]
+        for (k,v) in config.items('allowed_tenants'):
+              if user_specified_tenants and (k not in user_specified_tenants):
+                  continue
+              final_query=[]
+              final_query.extend(new_query)
+              query = make_query(tenant_id=k)
+              final_query.extend(query)
+              logger.debug('final query=%s',final_query)
+              results = client.samples.list(q=query)
+              samples.extend(results)
+        return json.dumps([ob._info for ob in samples])
+
+class resource_list:
+    def GET(self):
+        global config
+        keyword_args = {
+             "q.field": [],
+             "q.op": [],
+             "q.type": [],
+             "q.value": [],
+        }
+        query_params = web.input(**keyword_args)
+        new_query, user_specified_tenants = filter_query_params(query_params)
+
+        client = ceilometerclient()
+        resources=[]
+        for (k,v) in config.items('allowed_tenants'):
+              if user_specified_tenants and (k not in user_specified_tenants):
+                  continue
+              final_query=[]
+              final_query.extend(new_query)
+              query = make_query(tenant_id=k)
+              final_query.extend(query)
+              logger.debug('final query=%s',final_query)
+              results = client.resources.list(q=query)
+              resources.extend(results)
+        return json.dumps([ob._info for ob in resources])
+
+if __name__ == "__main__":
+    app.run(FileLog)
diff --git a/xos/observers/monitoring_channel/templates/monitoring-channel.conf.j2 b/xos/observers/monitoring_channel/templates/monitoring-channel.conf.j2
new file mode 100644
index 0000000..eb937ac
--- /dev/null
+++ b/xos/observers/monitoring_channel/templates/monitoring-channel.conf.j2
@@ -0,0 +1,10 @@
+# Upstart script for Monitoring channel
+description "Upstart script for Monitoring channel container"
+author "andy@onlab.us"
+start on filesystem and started docker
+stop on runlevel [!2345]
+respawn
+
+script
+  /usr/local/sbin/start-monitoring-channel-{{ unique_id }}.sh
+end script
diff --git a/xos/observers/monitoring_channel/templates/start-monitoring-channel.sh.j2 b/xos/observers/monitoring_channel/templates/start-monitoring-channel.sh.j2
new file mode 100755
index 0000000..10d9ef5
--- /dev/null
+++ b/xos/observers/monitoring_channel/templates/start-monitoring-channel.sh.j2
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+function mac_to_iface {
+    MAC=$1
+    ifconfig|grep $MAC| awk '{print $1}'|grep -v '\.'
+}
+
+function generate_mac_from_ip {
+    IP=$1
+    printf "02:42:%02x:%02x:%02x:%02x\n" `echo $IP|awk -F '.' '{print $1, $2, $3, $4}'`
+}
+
+iptables -L > /dev/null
+ip6tables -L > /dev/null
+
+MONITORING_CHANNEL=monitoring-channel-{{ unique_id }}
+HEADNODEFLATLANIP={{ headnode_flat_lan_ip }}
+
+docker inspect $MONITORING_CHANNEL > /dev/null 2>&1
+if [ "$?" == 1 ]
+then
+    #sudo docker build -t monitoring-channel -f Dockerfile.monitoring_channel .
+    sudo docker pull srikanthvavila/monitoring-channel
+    docker run -d --name=$MONITORING_CHANNEL --add-host="ctl:$HEADNODEFLATLANIP" --privileged=true -p 8888:8000 srikanthvavila/monitoring-channel
+else
+    docker start $MONITORING_CHANNEL
+fi
+
+# Set up networking via pipework
+#SHARED_LAN_IFACE=$( mac_to_iface {{ shared_lan_mac }} )
+#docker exec $MONITORING_CHANNEL ifconfig eth0 >> /dev/null || pipework $SHARED_LAN_IFACE -i eth0 $MONITORING_CHANNEL 192.168.0.1/24
+
+# Make sure VM's eth0 (hpc_client) has no IP address
+#ifconfig $HPC_IFACE 0.0.0.0
+
+# Now copy ceilometer proxy configuration to container
+cat /usr/local/share/monitoring-channel-{{ unique_id }}_ceilometer_proxy_config | docker exec -i $MONITORING_CHANNEL bash -c 'cat > /usr/local/share/ceilometer_proxy_config'
+
+# Attach to container
+docker start -a $MONITORING_CHANNEL
diff --git a/xos/observers/monitoring_channel/templates/start_ceilometer_proxy b/xos/observers/monitoring_channel/templates/start_ceilometer_proxy
new file mode 100644
index 0000000..ddaa9c8
--- /dev/null
+++ b/xos/observers/monitoring_channel/templates/start_ceilometer_proxy
@@ -0,0 +1 @@
+/usr/local/share/ceilometer_proxy_server.py 8000
diff --git a/xos/observers/onos/model-deps b/xos/observers/onos/model-deps
new file mode 100644
index 0000000..2da80e0
--- /dev/null
+++ b/xos/observers/onos/model-deps
@@ -0,0 +1,5 @@
+{
+    "ONOSApp": [
+        "ONOSService"
+    ]
+}
diff --git a/xos/observers/hello_world/helloworld-observer.py b/xos/observers/onos/onos-observer.py
similarity index 100%
copy from xos/observers/hello_world/helloworld-observer.py
copy to xos/observers/onos/onos-observer.py
diff --git a/xos/observers/onos/onos_observer_config b/xos/observers/onos/onos_observer_config
new file mode 100644
index 0000000..3c6d63d
--- /dev/null
+++ b/xos/observers/onos/onos_observer_config
@@ -0,0 +1,41 @@
+
+[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=onos
+dependency_graph=/opt/xos/observers/onos/model-deps
+steps_dir=/opt/xos/observers/onos/steps
+sys_dir=/opt/xos/observers/onos/sys
+deleters_dir=/opt/xos/observers/onos/deleters
+log_file=console
+driver=None
+pretend=False
+backoff_disabled=True
+save_ansible_output=True
+# set proxy_ssh to false on cloudlab
+proxy_ssh=False
+full_setup=True
+
+[feefie]
+client_id='vicci_dev_central'
+user_id='pl'
diff --git a/xos/observers/onos/run.sh b/xos/observers/onos/run.sh
new file mode 100755
index 0000000..ea4c511
--- /dev/null
+++ b/xos/observers/onos/run.sh
@@ -0,0 +1,6 @@
+#if [[ ! -e ./vcpe-observer.py ]]; then
+#    ln -s ../../xos-observer.py vcpe-observer.py
+#fi
+
+export XOS_DIR=/opt/xos
+python onos-observer.py  -C $XOS_DIR/observers/onos/onos_observer_config
diff --git a/xos/observers/onos/scripts/dockerip.sh b/xos/observers/onos/scripts/dockerip.sh
new file mode 100644
index 0000000..7684f3e
--- /dev/null
+++ b/xos/observers/onos/scripts/dockerip.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+docker inspect --format '{{ .NetworkSettings.IPAddress }}' $1
diff --git a/xos/observers/onos/start.sh b/xos/observers/onos/start.sh
new file mode 100755
index 0000000..c13ffbe
--- /dev/null
+++ b/xos/observers/onos/start.sh
@@ -0,0 +1,6 @@
+#if [[ ! -e ./vcpe-observer.py ]]; then
+#    ln -s ../../xos-observer.py vcpe-observer.py
+#fi
+
+export XOS_DIR=/opt/xos
+nohup python onos-observer.py  -C $XOS_DIR/observers/onos/onos_observer_config > /dev/null 2>&1 &
diff --git a/xos/observers/onos/steps/sync_onosapp.py b/xos/observers/onos/steps/sync_onosapp.py
new file mode 100644
index 0000000..8c97391
--- /dev/null
+++ b/xos/observers/onos/steps/sync_onosapp.py
@@ -0,0 +1,110 @@
+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 observer.syncstep import SyncStep
+from observer.ansible import run_template_ssh
+from observers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
+from core.models import Service, Slice
+from services.onos.models import ONOSService, ONOSApp
+from util.logger import Logger, logging
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+logger = Logger(level=logging.INFO)
+
+class SyncONOSApp(SyncInstanceUsingAnsible):
+    provides=[ONOSApp]
+    observes=ONOSApp
+    requested_interval=0
+    template_name = "sync_onosapp.yaml"
+    service_key_name = "/opt/xos/observers/onos/onos_key"
+
+    def __init__(self, *args, **kwargs):
+        super(SyncONOSApp, self).__init__(*args, **kwargs)
+
+    def fetch_pending(self, deleted):
+        if (not deleted):
+            objs = ONOSApp.get_tenant_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
+        else:
+            objs = ONOSApp.get_deleted_tenant_objects()
+
+        return objs
+
+    def get_instance(self, o):
+        # We assume the ONOS service owns a slice, so pick one of the instances
+        # inside that slice to sync to.
+
+        serv = self.get_onos_service(o)
+
+        if serv.use_external_host:
+            return serv.use_external_host
+
+        if serv.slices.exists():
+            slice = serv.slices.all()[0]
+            if slice.instances.exists():
+                return slice.instances.all()[0]
+
+        return None
+
+    def get_onos_service(self, o):
+        if not o.provider_service:
+            return None
+
+        onoses = ONOSService.get_service_objects().filter(id=o.provider_service.id)
+        if not onoses:
+            return None
+
+        return onoses[0]
+
+    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
+            # be defined.
+            raise Exception("observer_steps_dir is not defined in config file")
+
+        step_dir = Config().observer_steps_dir
+
+        return os.path.join(step_dir, "..", "files", str(self.get_onos_service(o).id), o.name)
+
+    def write_configs(self, o):
+        o.config_fns = []
+        o.files_dir = self.get_files_dir(o)
+
+        if not os.path.exists(o.files_dir):
+            os.makedirs(o.files_dir)
+
+        for attr in o.tenantattributes.all():
+            if attr.name.startswith("config_"):
+                fn = attr.name[7:] # .replace("_json",".json")
+                o.config_fns.append(fn)
+                file(os.path.join(o.files_dir, fn),"w").write(attr.value)
+
+    def prepare_record(self, o):
+        self.write_configs(o)
+
+    def get_extra_attributes(self, o):
+        fields={}
+        fields["files_dir"] = o.files_dir
+        fields["appname"] = o.name
+        fields["nat_ip"] = self.get_instance(o).get_ssh_ip()
+        fields["config_fns"] = o.config_fns
+        fields["dependencies"] = [x.strip() for x in o.dependencies.split(",")]
+        fields["ONOS_container"] = "ONOS"
+        return fields
+
+    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)
+
+    def delete_record(self, m):
+        pass
diff --git a/xos/observers/onos/steps/sync_onosapp.yaml b/xos/observers/onos/steps/sync_onosapp.yaml
new file mode 100644
index 0000000..2c5eb0f
--- /dev/null
+++ b/xos/observers/onos/steps/sync_onosapp.yaml
@@ -0,0 +1,49 @@
+---
+- hosts: {{ instance_name }}
+  gather_facts: False
+  connection: ssh
+  user: ubuntu
+  sudo: yes
+  vars:
+    appname: {{ appname }}
+    dependencies: {{ dependencies }}
+
+  tasks:
+
+  - name: Config file directory
+    file:

+      path=/home/ubuntu/{{ appname }}/

+      state=directory

+

+  - name: Copy over configuration files

+    copy:

+      src={{ files_dir }}/{{ '{{' }} item {{ '}}' }}

+      dest=/home/ubuntu/{{ appname }}/{{ '{{' }} item {{ '}}' }}

+    with_items:

+        {% for config_fn in config_fns %}

+        - {{ config_fn }}
+        {% endfor %}

+

+  - name: Copy config files into container

+    shell: docker cp {{ appname }}/{{ '{{' }} item {{ '}}' }} {{ ONOS_container }}:/root/onos/config/

+    sudo: yes

+    with_items:

+        {% for config_fn in config_fns %}

+        - {{ config_fn }}
+        {% endfor %}

+

+  # Don't know how to check for this condition, just wait

+  - name: Wait for ONOS to install the apps

+    wait_for: timeout=15

+

+  - name: Add dependencies to ONOS

+    uri:

+      url: http://localhost:8181/onos/v1/applications/{{ '{{' }} item {{ '}}' }}/active

+      method: POST

+      user: karaf

+      password: karaf

+    with_items:

+        {% for dependency in dependencies %}

+        - {{ dependency }}
+        {% endfor %}

+

diff --git a/xos/observers/onos/steps/sync_onosservice.py b/xos/observers/onos/steps/sync_onosservice.py
new file mode 100644
index 0000000..65fa44e
--- /dev/null
+++ b/xos/observers/onos/steps/sync_onosservice.py
@@ -0,0 +1,73 @@
+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 observer.syncstep import SyncStep
+from observer.ansible import run_template_ssh
+from observers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
+from core.models import Service, Slice
+from services.onos.models import ONOSService, ONOSApp
+from util.logger import Logger, logging
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+logger = Logger(level=logging.INFO)
+
+class SyncONOSService(SyncInstanceUsingAnsible):
+    provides=[ONOSService]
+    observes=ONOSService
+    requested_interval=0
+    template_name = "sync_onosservice.yaml"
+    service_key_name = "/opt/xos/observers/onos/onos_key"
+
+    def __init__(self, *args, **kwargs):
+        super(SyncONOSService, self).__init__(*args, **kwargs)
+
+    def fetch_pending(self, deleted):
+        if (not deleted):
+            objs = ONOSService.get_service_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
+        else:
+            objs = ONOSService.get_deleted_service_objects()
+
+        return objs
+
+    def get_instance(self, o):
+        # We assume the ONOS service owns a slice, so pick one of the instances
+        # inside that slice to sync to.
+
+        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():
+                return slice.instances.all()[0]
+
+        return None
+
+    def get_extra_attributes(self, o):
+        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_fields(self, o, fields):
+        # the super causes the playbook to be run
+
+        super(SyncONOSService, self).sync_fields(o, fields)
+
+    def run_playbook(self, o, fields):
+        super(SyncONOSService, self).run_playbook(o, fields)
+
+    def delete_record(self, m):
+        pass
diff --git a/xos/observers/onos/steps/sync_onosservice.yaml b/xos/observers/onos/steps/sync_onosservice.yaml
new file mode 100644
index 0000000..fd9c3db
--- /dev/null
+++ b/xos/observers/onos/steps/sync_onosservice.yaml
@@ -0,0 +1,66 @@
+---
+- hosts: {{ instance_name }}
+  gather_facts: False
+  connection: ssh
+  user: ubuntu
+  sudo: yes
+
+  tasks:
+
+  - name: Fix /etc/hosts
+    lineinfile:
+      dest=/etc/hosts
+      regexp="127.0.0.1 localhost"
+      line="127.0.0.1 localhost {{ instance_hostname }}"
+
+  - name: Add repo key
+    apt_key:
+      keyserver=hkp://pgp.mit.edu:80
+      id=58118E89F3A912897C070ADBF76221572C52609D
+
+  - name: Install Docker repo
+    apt_repository:
+      repo="deb https://apt.dockerproject.org/repo ubuntu-trusty main"
+      state=present
+
+  - name: Install Docker
+    apt:
+      name={{ '{{' }} item {{ '}}' }}
+      state=latest
+      update_cache=yes
+    with_items:
+    - docker-engine
+    - python-pip
+    - python-httplib2
+
+  - name: Install docker-py
+    pip:
+      name=docker-py
+      state=latest
+
+  - name: Start ONOS container
+    docker:
+      docker_api_version: "1.18"
+      name: {{ ONOS_container }}
+      # was: reloaded
+      state: running
+      image: onosproject/onos
+      ports:
+      - "6653:6653"
+      - "8101:8101"
+      - "8181:8181"
+      - "9876:9876"
+
+  - name: Get Docker IP
+    script: /opt/xos/observers/onos/scripts/dockerip.sh {{ ONOS_container }}
+    register: dockerip
+
+  - name: Wait for ONOS to come up
+    wait_for:
+      host={{ '{{' }} dockerip.stdout {{ '}}' }}
+      port={{ '{{' }} item {{ '}}' }}
+      state=present
+    with_items:
+    - 8101
+    - 8181
+    - 9876
diff --git a/xos/observers/onos/stop.sh b/xos/observers/onos/stop.sh
new file mode 100755
index 0000000..17d6eb7
--- /dev/null
+++ b/xos/observers/onos/stop.sh
@@ -0,0 +1 @@
+pkill -9 -f onos-observer.py
diff --git a/xos/observers/onos/supervisor/onos-observer.conf b/xos/observers/onos/supervisor/onos-observer.conf
new file mode 100644
index 0000000..16afa8c
--- /dev/null
+++ b/xos/observers/onos/supervisor/onos-observer.conf
@@ -0,0 +1,2 @@
+[program:onos-observer]
+command=python /opt/xos/observers/onos/onos-observer.py -C /opt/xos/observers/onos/onos_observer_config
diff --git a/xos/observers/vbng/steps/sync_vbngtenant.py b/xos/observers/vbng/steps/sync_vbngtenant.py
index 8868d6f..b603ed6 100644
--- a/xos/observers/vbng/steps/sync_vbngtenant.py
+++ b/xos/observers/vbng/steps/sync_vbngtenant.py
@@ -50,9 +50,38 @@
 
     def get_vbng_url(self, o):
         service = self.get_vbng_service(o)
-        if not service.vbng_url:
-            raise Exception("vBNG service does not have vbng_url set")
-        return service.vbng_url
+
+        # if the service object specifies a vbng_url, then use it
+        if service.vbng_url:
+            return service.vbng_url
+
+        # otherwise, see if the service has tenancy in ONOS
+        for tenant in service.subscribed_tenants.all():
+            if tenant.provider_service and tenant.provider_service.kind == "onos":
+                onos_service = tenant.provider_service
+                if not onos_service.slices.exists():
+                    raise Exception("vBNG service is linked to an ONOSApp, but the App's Service has no slices")
+                onos_slice = onos_service.slices.all()[0]
+                if not onos_slice.instances.exists():
+                    raise Exception("vBNG service is linked to an ONOSApp, but the App's Service's Slice has no instances")
+                instance = onos_slice.instances.all()[0]
+
+                #onos_app = ONOSApp.objects.filter(id = tenant.id)
+                #instance = onos_app.instance
+                #if not instance:
+                #    raise Exception("ONOSApp has no instance")
+
+                if not instance.instance_name:
+                    raise Exception("vBNG service is linked to an ONOSApp, but the App's Service's Slice's first instance is not instantiated")
+                ip = instance.get_network_ip("nat")
+                if not ip:
+                    raise Exception("vBNG service is linked to an ONOSApp, but the App's Service's Slice's first instance does not have an ip")
+
+                logger.info("Using ip %s from ONOS Instance %s" % (ip, instance))
+
+                return "http://%s:8181/onos/virtualbng/" % ip
+
+        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()
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant.yaml b/xos/observers/vcpe/steps/sync_vcpetenant.yaml
index 1a30656..fac78d5 100644
--- a/xos/observers/vcpe/steps/sync_vcpetenant.yaml
+++ b/xos/observers/vcpe/steps/sync_vcpetenant.yaml
@@ -35,6 +35,11 @@
       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 }}
 
   tasks:
 {% if full_setup %}
@@ -73,6 +78,25 @@
   - name: Install resolv.conf
     copy: src=/opt/xos/observers/vcpe/files/vm-resolv.conf
       dest=/etc/resolv.conf
+
+  - name: make sure ~/bin exists
+    file: path=~/bin state=directory owner=root group=root
+
+  - name: Copy cron job to destination
+    copy: src=/opt/xos/observers/vcpe/vcpe_stats_notifier.py
+      dest=~/bin/vcpe_stats_notifier.py
+
+  - name: install python-kombu
+    apt: name=python-kombu state=present
+
+  - name: Clean any running vcpe_stats_notifier cron jobs
+    command: pkill vcpe_stats_notifier.py
+    ignore_errors: yes
+
+  - name: Initiate vcpe_stats_notifier cron job
+    command: python ~/bin/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: 999999999999999999
+    poll: 0
 {% endif %}
 
   - name: vCPE upstart
diff --git a/xos/observers/vcpe/vcpe_stats_notifier.py b/xos/observers/vcpe/vcpe_stats_notifier.py
new file mode 100644
index 0000000..f4bb923
--- /dev/null
+++ b/xos/observers/vcpe/vcpe_stats_notifier.py
@@ -0,0 +1,250 @@
+import six
+import uuid
+import datetime
+from kombu.connection import BrokerConnection
+from kombu.messaging import Exchange, Queue, Consumer, Producer
+import subprocess
+import re
+import time, threading
+import sys, getopt
+import logging
+
+
+logfile = "vcpe_stats_notifier.log"
+level=logging.INFO
+logger=logging.getLogger('vcpe_stats_notifier')
+logger.setLevel(level)
+handler=logging.handlers.RotatingFileHandler(logfile,maxBytes=1000000, backupCount=1)
+logger.addHandler(handler)
+
+def extract_dns_stats_from_all_vcpes():
+    p = subprocess.Popen('docker ps', shell=True, stdout=subprocess.PIPE) 
+    firstline = True
+    dockercontainers = {}
+    while True:
+        out = p.stdout.readline()
+        if out == '' and p.poll() != None:
+            break
+        if out != '':
+            if firstline is True:
+                firstline = False
+            else:
+                fields = out.split()
+                container_fields = {}
+                container_fields['id'] = fields[0]
+                dockercontainers[fields[-1]] = container_fields
+    for k,v in dockercontainers.iteritems():
+         cmd = 'docker exec ' + v['id'] + ' killall -10 dnsmasq'
+         p = subprocess.Popen (cmd, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
+         (output, error) = p.communicate()
+         if error:
+             logger.error("killall dnsmasq command failed with error = %s",error)
+             continue
+         cmd = 'docker exec ' + v['id'] + ' tail -7 /var/log/syslog'
+         p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
+         (output, error) = p.communicate()
+         if error:
+             logger.error("tail on dnsmasq log command failed with error = %s",error)
+             continue
+         log_list = output.splitlines()
+         i = 0
+         while i < len(log_list):
+             m = re.search('(?<=:\scache size\s)(\S*)(?=,\s),\s(\S*)(?=/)/(\S*)(?=\scache insertions re-used unexpired cache entries)', log_list[i])
+             if m == None:
+                 i = i+1
+                 continue;
+             v['cache_size'] = m.group(1)
+             v['replaced_unexpired_entries'] = m.group(2)
+             v['total_inserted_entries'] = m.group(3)
+             i = i+1
+             m = re.search('(?<=:\squeries forwarded\s)(\S*)(?=,),\squeries answered locally\s(\S*)(?=$)', log_list[i])
+             v['queries_forwarded'] = m.group(1)
+             v['queries_answered_locally'] = m.group(2)
+             break;
+         i = i+2
+         v['server_stats'] = []
+         while i < len(log_list):
+             m = re.search('(?<=:\sserver\s)(\S*)(?=#)#\d*:\squeries sent\s(\S*)(?=,),\sretried or failed\s(\S*)(?=$)', log_list[i])
+             if m == None:
+                 i = i+1
+                 continue
+             dns_server = {}
+             dns_server['id'] = m.group(1)
+             dns_server['queries_sent'] = m.group(2)
+             dns_server['queries_failed'] = m.group(3)
+             v['server_stats'].append(dns_server)
+             i = i+1
+    return dockercontainers
+
+
+keystone_tenant_id='3a397e70f64e4e40b69b6266c634d9d0'
+keystone_user_id='1e3ce043029547f1a61c1996d1a531a2'
+rabbit_user='openstack'
+rabbit_password='80608318c273f348a7c3'
+rabbit_host='10.11.10.1'
+vcpeservice_rabbit_exchange='vcpeservice'
+cpe_publisher_id='vcpe_publisher'
+
+producer = None
+
+def setup_rabbit_mq_channel():
+     global producer
+     global rabbit_user, rabbit_password, rabbit_host, vcpeservice_rabbit_exchange,cpe_publisher_id
+     vcpeservice_exchange = Exchange(vcpeservice_rabbit_exchange, "topic", durable=False)
+     # connections/channels
+     connection = BrokerConnection(rabbit_host, rabbit_user, rabbit_password)
+     logger.info('Connection to RabbitMQ server successful')
+     channel = connection.channel()
+     # produce
+     producer = Producer(channel, exchange=vcpeservice_exchange, routing_key='notifications.info')
+     p = subprocess.Popen('hostname', shell=True, stdout=subprocess.PIPE)
+     (hostname, error) = p.communicate()
+     cpe_publisher_id = cpe_publisher_id + '_on_' + hostname
+     logger.info('cpe_publisher_id=%s',cpe_publisher_id)
+
+def publish_cpe_stats():
+     global producer
+     global keystone_tenant_id, keystone_user_id, cpe_publisher_id
+     cpe_container_stats = extract_dns_stats_from_all_vcpes()
+
+     for k,v in cpe_container_stats.iteritems():
+          msg = {'event_type': 'vcpe', 
+                 'message_id':six.text_type(uuid.uuid4()),
+                 'publisher_id': cpe_publisher_id,
+                 'timestamp':datetime.datetime.now().isoformat(),
+                 'priority':'INFO',
+                 'payload': {'vcpe_id':k, 
+                             'user_id':keystone_user_id, 
+                             'tenant_id':keystone_tenant_id 
+                            }
+                }
+          producer.publish(msg)
+          if 'cache_size' in v:
+               msg = {'event_type': 'vcpe.dns.cache.size', 
+                      'message_id':six.text_type(uuid.uuid4()),
+                      'publisher_id': cpe_publisher_id,
+                      'timestamp':datetime.datetime.now().isoformat(),
+                      'priority':'INFO',
+                      'payload': {'vcpe_id':k, 
+                                  'user_id':keystone_user_id,
+                                  'tenant_id':keystone_tenant_id, 
+                                  'cache_size':v['cache_size'] 
+                                 }
+                     }
+               producer.publish(msg)
+          if 'total_inserted_entries' in v:
+               msg = {'event_type': 'vcpe.dns.total_inserted_entries', 
+                      'message_id':six.text_type(uuid.uuid4()),
+                      'publisher_id': cpe_publisher_id,
+                      'timestamp':datetime.datetime.now().isoformat(),
+                      'priority':'INFO',
+                      'payload': {'vcpe_id':k, 
+                                  'user_id':keystone_user_id,
+                                  'tenant_id':keystone_tenant_id, 
+                                  'total_inserted_entries':v['total_inserted_entries'] 
+                                 }
+                     }
+               producer.publish(msg)
+          if 'replaced_unexpired_entries' in v:
+               msg = {'event_type': 'vcpe.dns.replaced_unexpired_entries', 
+                      'message_id':six.text_type(uuid.uuid4()),
+                      'publisher_id': cpe_publisher_id,
+                      'timestamp':datetime.datetime.now().isoformat(),
+                      'priority':'INFO',
+                      'payload': {'vcpe_id':k, 
+                                  'user_id':keystone_user_id,
+                                  'tenant_id':keystone_tenant_id, 
+                                  'replaced_unexpired_entries':v['replaced_unexpired_entries'] 
+                                 }
+                     }
+               producer.publish(msg)
+
+          if 'queries_forwarded' in v:
+               msg = {'event_type': 'vcpe.dns.queries_forwarded', 
+                      'message_id':six.text_type(uuid.uuid4()),
+                      'publisher_id': cpe_publisher_id,
+                      'timestamp':datetime.datetime.now().isoformat(),
+                      'priority':'INFO',
+                      'payload': {'vcpe_id':k, 
+                                  'user_id':keystone_user_id,
+                                  'tenant_id':keystone_tenant_id, 
+                                  'queries_forwarded':v['queries_forwarded'] 
+                                 }
+                     }
+               producer.publish(msg)
+
+          if 'queries_answered_locally' in v:
+               msg = {'event_type': 'vcpe.dns.queries_answered_locally', 
+                      'message_id':six.text_type(uuid.uuid4()),
+                      'publisher_id': cpe_publisher_id,
+                      'timestamp':datetime.datetime.now().isoformat(),
+                      'priority':'INFO',
+                      'payload': {'vcpe_id':k, 
+                                  'user_id':keystone_user_id,
+                                  'tenant_id':keystone_tenant_id, 
+                                  'queries_answered_locally':v['queries_answered_locally'] 
+                                 }
+                     }
+               producer.publish(msg)
+
+          if 'server_stats' in v:
+               for server in v['server_stats']:
+                   msg = {'event_type': 'vcpe.dns.server.queries_sent', 
+                          'message_id':six.text_type(uuid.uuid4()),
+                          'publisher_id': cpe_publisher_id,
+                          'timestamp':datetime.datetime.now().isoformat(),
+                          'priority':'INFO',
+                          'payload': {'vcpe_id':k, 
+                                      'user_id':keystone_user_id,
+                                      'tenant_id':keystone_tenant_id, 
+                                      'upstream_server':server['id'],
+                                      'queries_sent':server['queries_sent'] 
+                                     }
+                         }
+                   producer.publish(msg)
+
+                   msg = {'event_type': 'vcpe.dns.server.queries_failed', 
+                          'message_id':six.text_type(uuid.uuid4()),
+                          'publisher_id': cpe_publisher_id,
+                          'timestamp':datetime.datetime.now().isoformat(),
+                          'priority':'INFO',
+                          'payload': {'vcpe_id':k, 
+                                      'user_id':keystone_user_id,
+                                      'tenant_id':keystone_tenant_id, 
+                                      'upstream_server':server['id'],
+                                      'queries_failed':server['queries_failed'] 
+                                     }
+                         }
+                   producer.publish(msg)
+
+def periodic_publish():
+     publish_cpe_stats()
+     #Publish every 5minutes
+     threading.Timer(300, periodic_publish).start()
+
+def main(argv):
+   global keystone_tenant_id, keystone_user_id, rabbit_user, rabbit_password, rabbit_host, vcpeservice_rabbit_exchange
+   try:
+      opts, args = getopt.getopt(argv,"",["keystone_tenant_id=","keystone_user_id=","rabbit_host=","rabbit_user=","rabbit_password=","vcpeservice_rabbit_exchange="])
+   except getopt.GetoptError:
+      print 'vcpe_stats_notifier.py keystone_tenant_id=<keystone_tenant_id> keystone_user_id=<keystone_user_id> rabbit_host=<IP addr> rabbit_user=<user> rabbit_password=<password> vcpeservice_rabbit_exchange=<exchange name>'
+      sys.exit(2)
+   for opt, arg in opts:
+      if opt in ("--keystone_tenant_id"):
+         keystone_tenant_id = arg
+      elif opt in ("--keystone_user_id"):
+         keystone_user_id = arg
+      elif opt in ("--rabbit_user"):
+         rabbit_user = arg
+      elif opt in ("--rabbit_password"):
+         rabbit_password = arg
+      elif opt in ("--rabbit_host"):
+         rabbit_host = arg
+      elif opt in ("--vcpeservice_rabbit_exchange"):
+         vcpeservice_rabbit_exchange = arg
+   logger.info("vcpe_stats_notifier args:keystone_tenant_id=%s keystone_user_id=%s rabbit_user=%s rabbit_host=%s vcpeservice_rabbit_exchange=%s",keystone_tenant_id,keystone_user_id,rabbit_user,rabbit_host,vcpeservice_rabbit_exchange)
+   setup_rabbit_mq_channel()
+   periodic_publish()
+
+if __name__ == "__main__":
+   main(sys.argv[1:])
diff --git a/xos/openstack_observer/steps/sync_controller_networks.py b/xos/openstack_observer/steps/sync_controller_networks.py
index 1e7805f..ef238ee 100644
--- a/xos/openstack_observer/steps/sync_controller_networks.py
+++ b/xos/openstack_observer/steps/sync_controller_networks.py
@@ -69,7 +69,7 @@
         if (controller_network.network.template.name!='Private'):
             logger.info("skipping network controller %s because it is not private" % controller_network)
             # We only sync private networks
-            return
+            return SyncStep.SYNC_WITHOUT_RUNNING
         
         if not controller_network.controller.admin_user:
             logger.info("controller %r has no admin_user, skipping" % controller_network.controller)
diff --git a/xos/openstack_observer/steps/sync_instances.py b/xos/openstack_observer/steps/sync_instances.py
index 7aa4bb7..1209448 100644
--- a/xos/openstack_observer/steps/sync_instances.py
+++ b/xos/openstack_observer/steps/sync_instances.py
@@ -56,10 +56,12 @@
                                                                 controller=instance.node.site_deployment.controller)
 
         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 Exception("Private Network %s has no id; Try again later" % controller_network.network.name)
+                        raise DeferredException("Private Network %s has no id; Try again later" % controller_network.network.name)
                    nics.append(controller_network.net_id)
 
         # now include network template
@@ -108,7 +110,7 @@
 
         userData = self.get_userdata(instance, pubkeys)
         if instance.userData:
-            userData = instance.userData
+            userData += instance.userData
 
         controller = instance.node.site_deployment.controller
         fields = {'endpoint':controller.auth_url,
diff --git a/xos/openstack_observer/syncstep.py b/xos/openstack_observer/syncstep.py
index 66225d0..7accbfa 100644
--- a/xos/openstack_observer/syncstep.py
+++ b/xos/openstack_observer/syncstep.py
@@ -45,6 +45,12 @@
         psmodel        Model name the step synchronizes
         dependencies    list of names of models that must be synchronized first if the current model depends on them
     """
+
+    # map_sync_outputs can return this value to cause a step to be marked
+    # successful without running ansible. Used for sync_network_controllers
+    # on nat networks.
+    SYNC_WITHOUT_RUNNING = "sync_without_running"
+
     slow=False
     def get_prop(self, prop):
         try:
@@ -128,6 +134,8 @@
             pass
 
         tenant_fields = self.map_sync_inputs(o)
+        if tenant_fields == SyncStep.SYNC_WITHOUT_RUNNING:
+            return
         main_objs=self.observes
         if (type(main_objs) is list):
             main_objs=main_objs[0]
diff --git a/xos/scripts/opencloud b/xos/scripts/opencloud
index 6705b20..b936ce3 100755
--- a/xos/scripts/opencloud
+++ b/xos/scripts/opencloud
@@ -147,6 +147,7 @@
     python ./manage.py makemigrations syndicate_storage
     python ./manage.py makemigrations cord
     python ./manage.py makemigrations ceilometer
+    python ./manage.py makemigrations onos
     #python ./manage.py makemigrations servcomp
 }
 
diff --git a/xos/services/__init__.py b/xos/services/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/xos/services/__init__.py
@@ -0,0 +1 @@
+
diff --git a/xos/services/onos/__init__.py b/xos/services/onos/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/xos/services/onos/__init__.py
@@ -0,0 +1 @@
+
diff --git a/xos/services/onos/admin.py b/xos/services/onos/admin.py
new file mode 100644
index 0000000..d13a991
--- /dev/null
+++ b/xos/services/onos/admin.py
@@ -0,0 +1,112 @@
+from django.contrib import admin
+
+from services.onos.models import *
+from django import forms
+from django.utils.safestring import mark_safe
+from django.contrib.auth.admin import UserAdmin
+from django.contrib.admin.widgets import FilteredSelectMultiple
+from django.contrib.auth.forms import ReadOnlyPasswordHashField
+from django.contrib.auth.signals import user_logged_in
+from django.utils import timezone
+from django.contrib.contenttypes import generic
+from suit.widgets import LinkedSelect
+from core.admin import ServiceAppAdmin,SliceInline,ServiceAttrAsTabInline, ReadOnlyAwareAdmin, XOSTabularInline, ServicePrivilegeInline, TenantRootTenantInline, TenantRootPrivilegeInline, TenantAttrAsTabInline
+from core.middleware import get_request
+
+from functools import update_wrapper
+from django.contrib.admin.views.main import ChangeList
+from django.core.urlresolvers import reverse
+from django.contrib.admin.utils import quote
+
+class ONOSServiceForm(forms.ModelForm):
+    use_external_host = 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

+

+    def save(self, commit=True):

+        self.instance.use_external_host = self.cleaned_data.get("use_external_host")

+        return super(ONOSServiceForm, self).save(commit=commit)

+

+    class Meta:

+        model = ONOSService

+

+class ONOSServiceAdmin(ReadOnlyAwareAdmin):
+    model = ONOSService
+    verbose_name = "ONOS Service"
+    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']})]
+    readonly_fields = ('backend_status_text', )
+    inlines = [SliceInline,ServiceAttrAsTabInline,ServicePrivilegeInline]
+    form = ONOSServiceForm
+
+    extracontext_registered_admins = True
+
+    user_readonly_fields = ["name", "enabled", "versionNumber", "description"]
+
+    suit_form_tabs =(('general', 'ONOS Service Details'),
+        ('administration', 'Administration'),
+        ('slices','Slices'),
+        ('serviceattrs','Additional Attributes'),
+        ('serviceprivileges','Privileges'),
+    )
+
+    suit_form_includes = (('onosadmin.html', 'top', 'administration'),
+                           )
+
+    def queryset(self, request):
+        return ONOSService.get_service_objects_by_user(request.user)
+
+class ONOSAppForm(forms.ModelForm):
+    creator = forms.ModelChoiceField(queryset=User.objects.all())
+    name = forms.CharField()
+    dependencies = forms.CharField(required=False)
+
+    def __init__(self,*args,**kwargs):

+        super (ONOSAppForm,self ).__init__(*args,**kwargs)

+        self.fields['kind'].widget.attrs['readonly'] = True

+        self.fields['provider_service'].queryset = ONOSService.get_service_objects().all()

+        if self.instance:

+            # fields for the attributes

+            self.fields['creator'].initial = self.instance.creator

+            self.fields['name'].initial = self.instance.name

+            self.fields['dependencies'].initial = self.instance.dependencies

+        if (not self.instance) or (not self.instance.pk):

+            # default fields for an 'add' form

+            self.fields['kind'].initial = ONOS_KIND

+            self.fields['creator'].initial = get_request().user

+            if ONOSService.get_service_objects().exists():

+               self.fields["provider_service"].initial = ONOSService.get_service_objects().all()[0]

+

+    def save(self, commit=True):

+        self.instance.creator = self.cleaned_data.get("creator")

+        self.instance.name = self.cleaned_data.get("name")

+        self.instance.dependencies = self.cleaned_data.get("dependencies")

+        return super(ONOSAppForm, self).save(commit=commit)

+

+    class Meta:

+        model = ONOSApp
+
+class ONOSAppAdmin(ReadOnlyAwareAdmin):
+    list_display = ('backend_status_icon', 'name', )
+    list_display_links = ('backend_status_icon', 'name')
+    fieldsets = [ (None, {'fields': ['backend_status_text', 'kind', 'name', 'provider_service', 'subscriber_service', 'service_specific_attribute', "dependencies",
+                                     'creator'],
+                          'classes':['suit-tab suit-tab-general']})]
+    readonly_fields = ('backend_status_text', 'instance', 'service_specific_attribute')
+    inlines = [TenantAttrAsTabInline]
+    form = ONOSAppForm
+
+    suit_form_tabs = (('general','Details'), ('tenantattrs', 'Attributes'))
+
+    def queryset(self, request):
+        return ONOSApp.get_tenant_objects_by_user(request.user)
+
+admin.site.register(ONOSService, ONOSServiceAdmin)
+admin.site.register(ONOSApp, ONOSAppAdmin)
+
diff --git a/xos/services/onos/models.py b/xos/services/onos/models.py
new file mode 100644
index 0000000..80e903e
--- /dev/null
+++ b/xos/services/onos/models.py
@@ -0,0 +1,122 @@
+from django.db import models
+from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber
+from core.models.plcorebase import StrippedCharField
+import os
+from django.db import models, transaction
+from django.forms.models import model_to_dict
+from django.db.models import Q
+from operator import itemgetter, attrgetter, methodcaller
+import traceback
+from xos.exceptions import *
+from core.models import SlicePrivilege, SitePrivilege
+from sets import Set
+
+ONOS_KIND = "onos"
+
+class ONOSService(Service):
+    KIND = ONOS_KIND
+
+    class Meta:
+        app_label = "onos"
+        verbose_name = "ONOS Service"
+        proxy = True
+
+    default_attributes = {"use_external_host": ""}
+
+    @property
+    def use_external_host(self):
+        return self.get_attribute("use_external_host", self.default_attributes["use_external_host"])
+
+    @use_external_host.setter
+    def use_external_host(self, value):
+        self.set_attribute("use_external_host", value)
+
+class ONOSApp(Tenant):   # aka 'ONOSTenant'
+    class Meta:
+        proxy = True
+
+    KIND = ONOS_KIND
+
+    default_attributes = {"name": "",
+                          "dependencies": ""}
+    def __init__(self, *args, **kwargs):
+        onos_services = ONOSService.get_service_objects().all()
+        if onos_services:
+            self._meta.get_field("provider_service").default = onos_services[0].id
+        super(ONOSApp, self).__init__(*args, **kwargs)
+
+    @property
+    def creator(self):
+        from core.models import User
+        if getattr(self, "cached_creator", None):
+            return self.cached_creator
+        creator_id=self.get_attribute("creator_id")
+        if not creator_id:
+            return None
+        users=User.objects.filter(id=creator_id)
+        if not users:
+            return None
+        user=users[0]
+        self.cached_creator = users[0]
+        return user
+
+    @creator.setter
+    def creator(self, value):
+        if value:
+            value = value.id
+        if (value != self.get_attribute("creator_id", None)):
+            self.cached_creator=None
+        self.set_attribute("creator_id", value)
+
+    @property
+    def name(self):
+        return self.get_attribute("name", self.default_attributes["name"])
+
+    @name.setter
+    def name(self, value):
+        self.set_attribute("name", value)
+
+    @property
+    def dependencies(self):
+        return self.get_attribute("dependencies", self.default_attributes["dependencies"])
+
+    @dependencies.setter
+    def dependencies(self, value):
+        self.set_attribute("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):
+                # caller must be set when creating a vCPE since it creates a slice
+                raise XOSProgrammingError("ONOSApp's self.caller was not set")
+            self.creator = self.caller
+            if not self.creator:
+                raise XOSProgrammingError("ONOSApp's self.creator was not set")
+
+        super(ONOSApp, self).save(*args, **kwargs)
+        model_policy_onos_app(self.pk)
+
+# TODO: Probably don't need this...
+def model_policy_onos_app(pk):
+    # TODO: this should be made in to a real model_policy
+    with transaction.atomic():
+        oa = ONOSApp.objects.select_for_update().filter(pk=pk)
+        if not oa:
+            return
+        oa = oa[0]
+        #oa.manage_container()
+
+
diff --git a/xos/services/onos/templates/onosadmin.html b/xos/services/onos/templates/onosadmin.html
new file mode 100644
index 0000000..1e8d42c
--- /dev/null
+++ b/xos/services/onos/templates/onosadmin.html
@@ -0,0 +1,6 @@
+<div class = "left-nav">
+<ul>
+<li><a href="/admin/onos/onosapp/">ONOS Apps</a></li>
+</ul>
+</div>
+
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index 81d1333..7a94d8b 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -12,7 +12,11 @@
             no-create:
                 type: boolean
                 default: false
-                description: Do not allow Tosca to create this object)
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object)
 # Service
 define(xos_base_service_caps,
             scalable:
@@ -59,7 +63,7 @@
             service_specific_id:
                 type: string
                 required: false
-                description: Service specific ID, opaque to XOS but meaningful to service)
+                description: Service specific ID opaque to XOS but meaningful to service)
 define(xos_base_tenant_props,
             kind:
                 type: string
@@ -68,7 +72,7 @@
             service_specific_id:
                 type: string
                 required: false
-                description: Service specific ID, opaque to XOS but meaningful to service)
+                description: Service specific ID opaque to XOS but meaningful to service)
 
 # end m4 macros
 #
@@ -85,6 +89,41 @@
         properties:
             xos_base_service_props
 
+    tosca.nodes.ONOSService:
+        derived_from: tosca.nodes.Root
+        description: >
+            ONOS Service
+        capabilities:
+            xos_base_service_caps
+        properties:
+            xos_base_service_props
+
+    tosca.nodes.ONOSApp:
+        derived_from: tosca.nodes.Root
+        description: >
+            An ONOS Application.
+        properties:
+            xos_base_tenant_props
+            dependencies:
+                type: string
+                required: false
+
+    tosca.nodes.ONOSvBNGApp:
+        derived_from: tosca.nodes.Root
+        description: >
+            An ONOS Application.
+        properties:
+            xos_base_tenant_props
+            dependencies:
+                type: string
+                required: false
+            config_addresses.json:
+                type: string
+                required: false
+            config_virtualbng.json:
+                type: string
+                required: false
+
     tosca.nodes.VCPEService:
         description: >
             CORD: The vCPE Service.
@@ -410,6 +449,7 @@
             controller:
                 type: tosca.capabilities.xos.Controller
         properties:
+            xos_base_props
             backend_type:
                 type: string
                 required: false
@@ -447,29 +487,30 @@
             site:
                 type: tosca.capabilities.xos.Site
         properties:
-             display_name:
-                 type: string
-                 required: false
-                 description: Name of the site.
-             site_url:
-                 type: string
-                 required: false
-                 description: URL of site web page.
-             enabled:
-                 type: boolean
-                 default: true
-             hosts_nodes:
-                 type: boolean
-                 default: true
-                 description: If True, then this site hosts nodes where Instances may be instantiated.
-             hosts_users:
-                 type: boolean
-                 default: true
-                 description: If True, then this site hosts users who may use XOS.
-             is_public:
-                 type: boolean
-                 default: true
-             # location, longitude, latitude
+            xos_base_props
+            display_name:
+                type: string
+                required: false
+                description: Name of the site.
+            site_url:
+                type: string
+                required: false
+                description: URL of site web page.
+            enabled:
+                type: boolean
+                default: true
+            hosts_nodes:
+                type: boolean
+                default: true
+                description: If True, then this site hosts nodes where Instances may be instantiated.
+            hosts_users:
+                type: boolean
+                default: true
+                description: If True, then this site hosts users who may use XOS.
+            is_public:
+                type: boolean
+                default: true
+            # location, longitude, latitude
 
     tosca.nodes.Slice:
         derived_from: tosca.nodes.Root
@@ -480,6 +521,7 @@
             slice:
                 type: tosca.capabilities.xos.Slice
         properties:
+            xos_base_props
             enabled:
                 type: boolean
                 default: true
@@ -501,7 +543,9 @@
         description: >
             An XOS Node. Nodes are physical machines that host virtual machines
             and/or containers.
-        capability:
+        properties:
+            xos_base_props
+        capabilities:
             node:
                 type: tosca.capabilities.xos.Node
 
@@ -525,6 +569,10 @@
         derived_from: tosca.relationships.Root
         valid_target_types: [ tosca.capabilities.xos.Service ]
 
+    tosca.relationships.UsedByService:
+        derived_from: tosca.relationships.Root
+        valid_target_types: [ tosca.capabilities.xos.Service ]
+
     tosca.relationships.ControllerDeployment:
         derived_from: tosca.relationships.Root
         valid_target_types: [ tosca.capabilities.xos.Deployment ]
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 9e25754..9b307d6 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -57,6 +57,84 @@
                 required: false
                 description: Version number of Service.
 
+    tosca.nodes.ONOSService:
+        derived_from: tosca.nodes.Root
+        description: >
+            ONOS Service
+        capabilities:
+            scalable:
+                type: tosca.capabilities.Scalable
+            service:
+                type: tosca.capabilities.xos.Service
+        properties:
+            kind:
+                type: string
+                default: generic
+                description: Type of service.
+            view_url:
+                type: string
+                required: false
+                description: URL to follow when icon is clicked in the Service Directory.
+            icon_url:
+                type: string
+                required: false
+                description: ICON to display in the Service Directory.
+            enabled:
+                type: boolean
+                default: true
+            published:
+                type: boolean
+                default: true
+                description: If True then display this Service in the Service Directory.
+            public_key:
+                type: string
+                required: false
+                description: Public key to install into Instances to allows Services to SSH into them.
+            versionNumber:
+                type: string
+                required: false
+                description: Version number of Service.
+
+    tosca.nodes.ONOSApp:
+        derived_from: tosca.nodes.Root
+        description: >
+            An ONOS Application.
+        properties:
+            kind:
+                type: string
+                default: generic
+                description: Kind of tenant
+            service_specific_id:
+                type: string
+                required: false
+                description: Service specific ID opaque to XOS but meaningful to service
+            dependencies:
+                type: string
+                required: false
+
+    tosca.nodes.ONOSvBNGApp:
+        derived_from: tosca.nodes.Root
+        description: >
+            An ONOS Application.
+        properties:
+            kind:
+                type: string
+                default: generic
+                description: Kind of tenant
+            service_specific_id:
+                type: string
+                required: false
+                description: Service specific ID opaque to XOS but meaningful to service
+            dependencies:
+                type: string
+                required: false
+            config_addresses.json:
+                type: string
+                required: false
+            config_virtualbng.json:
+                type: string
+                required: false
+
     tosca.nodes.VCPEService:
         description: >
             CORD: The vCPE Service.
@@ -193,7 +271,7 @@
             service_specific_id:
                 type: string
                 required: false
-                description: Service specific ID
+                description: Service specific ID opaque to XOS but meaningful to service
 
     tosca.nodes.CORDSubscriber:
         derived_from: tosca.nodes.Root
@@ -212,7 +290,7 @@
             service_specific_id:
                 type: string
                 required: false
-                description: Service specific ID
+                description: Service specific ID opaque to XOS but meaningful to service
             firewall_enable:
                 type: boolean
                 default: false
@@ -262,7 +340,7 @@
             service_specific_id:
                 type: string
                 required: false
-                description: Service specific ID
+                description: Service specific ID opaque to XOS but meaningful to service
             vlan_id:
                 type: string
                 required: false
@@ -460,6 +538,10 @@
                 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
             accessControl:
                 type: string
                 default: allow all
@@ -499,6 +581,18 @@
             controller:
                 type: tosca.capabilities.xos.Controller
         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
             backend_type:
                 type: string
                 required: false
@@ -536,29 +630,41 @@
             site:
                 type: tosca.capabilities.xos.Site
         properties:
-             display_name:
-                 type: string
-                 required: false
-                 description: Name of the site.
-             site_url:
-                 type: string
-                 required: false
-                 description: URL of site web page.
-             enabled:
-                 type: boolean
-                 default: true
-             hosts_nodes:
-                 type: boolean
-                 default: true
-                 description: If True, then this site hosts nodes where Instances may be instantiated.
-             hosts_users:
-                 type: boolean
-                 default: true
-                 description: If True, then this site hosts users who may use XOS.
-             is_public:
-                 type: boolean
-                 default: true
-             # location, longitude, latitude
+            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
+            display_name:
+                type: string
+                required: false
+                description: Name of the site.
+            site_url:
+                type: string
+                required: false
+                description: URL of site web page.
+            enabled:
+                type: boolean
+                default: true
+            hosts_nodes:
+                type: boolean
+                default: true
+                description: If True, then this site hosts nodes where Instances may be instantiated.
+            hosts_users:
+                type: boolean
+                default: true
+                description: If True, then this site hosts users who may use XOS.
+            is_public:
+                type: boolean
+                default: true
+            # location, longitude, latitude
 
     tosca.nodes.Slice:
         derived_from: tosca.nodes.Root
@@ -569,6 +675,18 @@
             slice:
                 type: tosca.capabilities.xos.Slice
         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
             enabled:
                 type: boolean
                 default: true
@@ -590,7 +708,20 @@
         description: >
             An XOS Node. Nodes are physical machines that host virtual machines
             and/or containers.
-        capability:
+        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
+        capabilities:
             node:
                 type: tosca.capabilities.xos.Node
 
@@ -614,6 +745,10 @@
         derived_from: tosca.relationships.Root
         valid_target_types: [ tosca.capabilities.xos.Service ]
 
+    tosca.relationships.UsedByService:
+        derived_from: tosca.relationships.Root
+        valid_target_types: [ tosca.capabilities.xos.Service ]
+
     tosca.relationships.ControllerDeployment:
         derived_from: tosca.relationships.Root
         valid_target_types: [ tosca.capabilities.xos.Deployment ]
diff --git a/xos/tosca/resources/controller.py b/xos/tosca/resources/controller.py
index da4ed64..9a20ea5 100644
--- a/xos/tosca/resources/controller.py
+++ b/xos/tosca/resources/controller.py
@@ -44,6 +44,10 @@
         if obj.controllersite.exists():
             self.info("Controller %s has active sites; skipping delete" % obj.name)
             return
+        for sd in obj.sitedeployments.all():
+            if sd.nodes.exists():
+                self.info("Controller %s has active nodes; skipping delete" % obj.name)
+                return
         super(XOSController, self).delete(obj)
 
 
diff --git a/xos/tosca/resources/deployment.py b/xos/tosca/resources/deployment.py
index 152b1f9..ed6734c 100644
--- a/xos/tosca/resources/deployment.py
+++ b/xos/tosca/resources/deployment.py
@@ -51,13 +51,13 @@
         self.postprocess_privileges(DeploymentRole, DeploymentPrivilege, rolemap, obj, "deployment")
 
     def delete(self, obj):
-        if self.get_property("no-delete"):
-            self.info("Deployment %s is marked no-delete")
-            return
-
         if obj.sites.exists():
             self.info("Deployment %s has active sites; skipping delete" % obj.name)
             return
+        for sd in obj.sitedeployments.all():
+            if sd.nodes.exists():
+                self.info("Deployment %s has active nodes; skipping delete" % obj.name)
+                return
         #if obj.nodes.exists():
         #    self.info("Deployment %s has active nodes; skipping delete" % obj.name)
         #    return
diff --git a/xos/tosca/resources/onosapp.py b/xos/tosca/resources/onosapp.py
new file mode 100644
index 0000000..03c4eb5
--- /dev/null
+++ b/xos/tosca/resources/onosapp.py
@@ -0,0 +1,63 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+import pdb
+
+from core.models import User, TenantAttribute, Service
+from services.onos.models import ONOSApp, ONOSService
+
+from xosresource import XOSResource
+
+class XOSONOSApp(XOSResource):
+    provides = ["tosca.nodes.ONOSApp", "tosca.nodes.ONOSvBNGApp"]
+    xos_model = ONOSApp
+    copyin_props = ["service_specific_id", "dependencies"]
+
+    def get_xos_args(self, throw_exception=True):
+        args = super(XOSONOSApp, self).get_xos_args()
+
+        # provider_service is mandatory and must be the ONOS Service
+        provider_name = self.get_requirement("tosca.relationships.TenantOfService", throw_exception=throw_exception)
+        if provider_name:
+            args["provider_service"] = self.get_xos_object(ONOSService, throw_exception=throw_exception, name=provider_name)
+
+        # subscriber_service is optional and can be any service
+        subscriber_name = self.get_requirement("tosca.relationships.UsedByService", throw_exception=False)
+        if subscriber_name:
+            args["subscriber_service"] = self.get_xos_object(Service, throw_exception=throw_exception, name=subscriber_name)
+
+        return args
+
+    def get_existing_objs(self):
+        objs = ONOSApp.get_tenant_objects().all()
+        objs = [x for x in objs if x.name == self.nodetemplate.name]
+        return objs
+
+    def set_tenant_attr(self, obj, prop_name, value):
+        value = self.try_intrinsic_function(value)
+        if value:
+            attrs = TenantAttribute.objects.filter(tenant=obj, name=prop_name)
+            if attrs:
+                attr = attrs[0]
+                if attr.value != value:
+                    self.info("updating attribute %s" % k)
+                    attrs.value = value
+                    attrs.save()
+            else:
+                self.info("adding attribute %s" % prop_name)
+                ta = TenantAttribute(tenant=obj, name=prop_name, value=value)
+                ta.save()
+
+    def postprocess(self, obj):
+        props = self.nodetemplate.get_properties()
+        for (k,d) in props.items():
+            v = d.value
+            if k.startswith("config_"):
+                self.set_tenant_attr(obj, k, v)
+
+    def can_delete(self, obj):
+        return super(XOSONOSApp, self).can_delete(obj)
+
diff --git a/xos/tosca/resources/onosservice.py b/xos/tosca/resources/onosservice.py
new file mode 100644
index 0000000..1275fab
--- /dev/null
+++ b/xos/tosca/resources/onosservice.py
@@ -0,0 +1,16 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from services.onos.models import ONOSService
+
+from service import XOSService
+
+class XOSONOSService(XOSService):
+    provides = "tosca.nodes.ONOSService"
+    xos_model = ONOSService
+    copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "versionNumber"]
+
diff --git a/xos/tosca/resources/xosresource.py b/xos/tosca/resources/xosresource.py
index 62f18db..3553ab1 100644
--- a/xos/tosca/resources/xosresource.py
+++ b/xos/tosca/resources/xosresource.py
@@ -60,6 +60,12 @@
     def get_property(self, name):
         return self.nodetemplate.get_property_value(name)
 
+    def get_property_default(self, name, default=None):
+        props = self.nodetemplate.get_properties()
+        if props and name in props.keys():

+            return props[name].value
+        return default
+
     def get_xos_object(self, cls, throw_exception=True, **kwargs):
         objs = cls.objects.filter(**kwargs)
         if not objs:
@@ -80,12 +86,21 @@
     def create_or_update(self):
         existing_objs = self.get_existing_objs()
         if existing_objs:
-            self.info("%s %s already exists" % (self.get_model_class_name(), self.nodetemplate.name))
-            self.update(existing_objs[0])
+            if self.get_property_default("no-update", False):
+                self.info("%s %s already exists. Skipping update due to 'no-update' property" % (self.get_model_class_name(), self.nodetemplate.name))
+            else:
+                self.info("%s %s already exists" % (self.get_model_class_name(), self.nodetemplate.name))
+                self.update(existing_objs[0])
         else:
-            self.create()
+            if self.get_property_default("no-create", False):
+                self.info("%s %s does not exist, but 'no-create' is specified" % (self.get_model_class_name(), self.nodetemplate.name))
+            else:
+                self.create()
 
     def can_delete(self, obj):
+        if self.get_property_default("no-delete",False):
+            self.info("%s %s is marked 'no-delete'. Skipping delete." % (self.get_model_class_name(), self.nodetemplate.name))
+            return False
         return True
 
     def postprocess_privileges(self, roleclass, privclass, rolemap, obj, toFieldName):
diff --git a/xos/tosca/samples/onos.yaml b/xos/tosca/samples/onos.yaml
new file mode 100644
index 0000000..fc6a3d5
--- /dev/null
+++ b/xos/tosca/samples/onos.yaml
@@ -0,0 +1,94 @@
+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:
+    ONOS:
+      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/observers/onos/onos_key.pub
+
+    vBNG:
+      type: tosca.nodes.ONOSvBNGApp
+      requirements:
+          - onos_tenant:
+              node: ONOS
+              relationship: tosca.relationships.TenantOfService
+      properties:
+          dependencies: org.onosproject.proxyarp, org.onosproject.virtualbng, org.onosproject.openflow, org.onosproject.fwd
+          config_addresses.json: >
+            {
+                "addresses" : [

+                            {

+                                "dpid" : "00:00:00:00:00:00:00:a1",

+                                "port" : "2",

+                                "ips" : ["192.0.0.1/24"],

+                                "mac" : "00:00:00:00:00:99"

+

+                            },

+                            {

+                                "dpid" : "00:00:00:00:00:00:00:a5",

+                                "port" : "4",

+                                "ips" : ["200.0.0.5/24"],

+                                "mac" : "00:00:00:00:00:98"

+                            }

+                ]

+            }
+          config_virtualbng.json: >
+            {
+                "localPublicIpPrefixes" : [

+                    "200.0.0.0/32",

+                    "201.0.0.0/30",

+                    "202.0.0.0/30"

+                ],

+                "nextHopIpAddress" : "200.0.0.5",

+                "publicFacingMac" : "00:00:00:00:00:66",

+                "xosIpAddress" : "10.11.10.1",

+                "xosRestPort" : "9999"

+            }
+
+    mysite:
+      type: tosca.nodes.Site
+
+    mysite_onos:
+      description: ONOS Controller Slice
+      type: tosca.nodes.Slice
+      requirements:
+          - ONOS:
+              node: ONOS
+              relationship: tosca.relationships.MemberOfService
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
+    my_server:
+      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
+                relationship: tosca.relationships.MemberOfSlice
+
diff --git a/xos/tosca/tests/controllertest.py b/xos/tosca/tests/controllertest.py
index 5746ada..2b7ba55 100644
--- a/xos/tosca/tests/controllertest.py
+++ b/xos/tosca/tests/controllertest.py
@@ -5,7 +5,9 @@
 class ControllerTest(BaseToscaTest):
     tests = ["create_controller_minimal",
              "create_controller_maximal",
-             "destroy_controller"]
+             "create_controller_nocreate",
+             "destroy_controller",
+             "destroy_controller_nodelete"]
 
     def cleanup(self):
         self.try_to_delete(Controller, name="testcon")
@@ -53,6 +55,45 @@
                         domain="mydomain",
                         deployment=dep)
 
+    def create_controller_nocreate(self):
+        self.assert_noobj(Controller, "testcon")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+                                            reqs=[("testdep", "tosca.relationships.ControllerDeployment")],
+                                            props={"no-create": True}))
+        dep = self.assert_obj(Deployment, "testdep")
+        self.assert_noobj(Controller, "testcon")
+
+    def update_controller(self):
+        self.assert_noobj(Controller, "testcon")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+                                            reqs=[("testdep", "tosca.relationships.ControllerDeployment")]))
+        dep = self.assert_obj(Deployment, "testdep")
+        orig_con = self.assert_obj(Controller, "testcon",
+                        backend_type="",
+                        version="",
+                        auth_url=None,
+                        admin_user=None,
+                        admin_password=None,
+                        admin_tenant=None,
+                        domain=None,
+                        deployment=dep)
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+                                            reqs=[("testdep", "tosca.relationships.ControllerDeployment")],
+                                            props={"version": "1.1"}))
+        con = self.assert_obj(Controller, "testcon",
+                        backend_type="",
+                        version="1.1",
+                        auth_url=None,
+                        admin_user=None,
+                        admin_password=None,
+                        admin_tenant=None,
+                        domain=None,
+                        deployment=dep)
+        assert(orig_con.id == con.id)
+
     def destroy_controller(self):
         self.assert_noobj(Controller, "testcon")
         self.execute(self.get_base_templates() +
@@ -64,6 +105,24 @@
                                             reqs=[("testdep", "tosca.relationships.ControllerDeployment")]))
         self.assert_noobj(Controller, "testcon")
 
+    def destroy_controller_nodelete(self):
+        self.assert_noobj(Controller, "testcon")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+                                            reqs=[("testdep", "tosca.relationships.ControllerDeployment")]))
+        orig_con = self.assert_obj(Controller, "testcon")
+        # NOTE: Had to specify no-delete on the deployment as well, otherwise
+        # the deployment deletion would cause the controller to be deleted
+        # as well. I'm thinking this is as it should be, but it's a little
+        # counter-inutitive.
+        self.destroy(self.make_nodetemplate("testdep", "tosca.nodes.Deployment",
+                                            props={"no-delete": True}) +
+                     self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+                                            reqs=[("testdep", "tosca.relationships.ControllerDeployment")],
+                                            props={"no-delete": True}))
+        con = self.assert_obj(Controller, "testcon")
+        assert(orig_con.id == con.id)
+
 if __name__ == "__main__":
     ControllerTest()
 
diff --git a/xos/tosca/tests/deploymenttest.py b/xos/tosca/tests/deploymenttest.py
index 0156613..91caf75 100644
--- a/xos/tosca/tests/deploymenttest.py
+++ b/xos/tosca/tests/deploymenttest.py
@@ -10,6 +10,9 @@
              "create_deployment_one_image",
              "create_deployment_two_images",
              "create_deployment_privilege",
+             "create_deployment_nocreate",
+             "update_deployment",
+             "update_deployment_noupdate",
              "destroy_deployment",
              "destroy_deployment_nodelete"
                            ]
@@ -88,6 +91,35 @@
         dps = DeploymentPrivilege.objects.filter(user=user, deployment=dep)
         assert(len(dps) == 1)
 
+    def create_deployment_nocreate(self):
+        self.assert_noobj(Deployment, "testdep")
+        self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment",
+                                            props={"no-create": True}))
+        self.assert_noobj(Deployment, "testdep")
+
+    def update_deployment(self):
+        self.assert_noobj(Deployment, "testdep")
+        self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment"))
+        orig_dep = self.assert_obj(Deployment, "testdep",
+                                   accessControl="allow all")
+        self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment",
+                                            props={"accessControl": "allow padmin@vicci.org"}))
+        dep = self.assert_obj(Deployment, "testdep",
+                                   accessControl="allow padmin@vicci.org")
+        assert(dep.id == orig_dep.id)
+
+    def update_deployment_noupdate(self):
+        self.assert_noobj(Deployment, "testdep")
+        self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment"))
+        orig_dep = self.assert_obj(Deployment, "testdep",
+                                   accessControl="allow all")
+        self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment",
+                                            props={"accessControl": "allow padmin@vicci.org",
+                                                   "no-update": True}))
+        dep = self.assert_obj(Deployment, "testdep",
+                                   accessControl="allow all")
+        assert(dep.id == orig_dep.id)
+
     def destroy_deployment(self):
         self.assert_noobj(Deployment, "testdep")
         self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment"))
diff --git a/xos/tosca/tests/nodetest.py b/xos/tosca/tests/nodetest.py
index d49dab1..76c56a8 100644
--- a/xos/tosca/tests/nodetest.py
+++ b/xos/tosca/tests/nodetest.py
@@ -4,7 +4,9 @@
 
 class NodeTest(BaseToscaTest):
     tests = ["create_node_minimal",
+             "create_node_nocreate",
              "destroy_node",
+             "destroy_node_nodelete",
                            ]
 
     def cleanup(self):
@@ -43,7 +45,18 @@
                      self.make_nodetemplate("testnode", "tosca.nodes.Node",
                        reqs=[("testsite", "tosca.relationships.MemberOfSite"),
                              ("testdep", "tosca.relationships.MemberOfDeployment")]))
-        self.assert_obj(Node, "testnode")
+        node = self.assert_obj(Node, "testnode")
+        assert(node.site_deployment is not None)
+        assert(node.site is not None)
+
+    def create_node_nocreate(self):
+        self.assert_noobj(Node, "testnode")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testnode", "tosca.nodes.Node",
+                       reqs=[("testsite", "tosca.relationships.MemberOfSite"),
+                             ("testdep", "tosca.relationships.MemberOfDeployment")],
+                       props={"no-create": True}))
+        self.assert_noobj(Node, "testnode")
 
     def destroy_node(self):
         self.assert_noobj(Node, "testnode")
@@ -58,6 +71,20 @@
                              ("testdep", "tosca.relationships.MemberOfDeployment")]))
         self.assert_noobj(Node, "testnode")
 
+    def destroy_node_nodelete(self):
+        self.assert_noobj(Node, "testnode")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testnode", "tosca.nodes.Node",
+                       reqs=[("testsite", "tosca.relationships.MemberOfSite"),
+                             ("testdep", "tosca.relationships.MemberOfDeployment")]))
+        self.assert_obj(Node, "testnode")
+        self.destroy(self.get_base_templates() +
+                     self.make_nodetemplate("testnode", "tosca.nodes.Node",
+                       reqs=[("testsite", "tosca.relationships.MemberOfSite"),
+                             ("testdep", "tosca.relationships.MemberOfDeployment")],
+                       props={"no-delete": True}))
+        self.assert_obj(Node, "testnode")
+
 if __name__ == "__main__":
     NodeTest()
 
diff --git a/xos/tosca/tests/sitetest.py b/xos/tosca/tests/sitetest.py
index c9a4743..5321159 100644
--- a/xos/tosca/tests/sitetest.py
+++ b/xos/tosca/tests/sitetest.py
@@ -7,7 +7,11 @@
              "create_site_privilege_tech",
              "create_site_privilege_admin",
              "create_site_privilege_pi",
+             "create_site_nocreate",
+             "update_site",
+             "update_site_noupdate",
              "destroy_site",
+             "destroy_site_nodelete"
                            ]
 
     def cleanup(self):
@@ -60,6 +64,31 @@
         assert(len(sps) == 1)
         assert(sps[0].role.role == "pi")
 
+    def create_site_nocreate(self):
+        self.assert_noobj(Site, "testsite")
+        self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site",
+                                            props={"no-create": True}))
+        site = self.assert_noobj(Site, "testsite")
+
+    def update_site(self):
+        self.assert_noobj(Site, "testsite")
+        self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
+        orig_site = self.assert_obj(Site, "testsite", site_url=None)
+        self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site",
+                                            props={"site_url": "http://foo.com/"}))
+        site = self.assert_obj(Site, "testsite", site_url="http://foo.com/")
+        assert(orig_site.id == site.id)
+
+    def update_site_noupdate(self):
+        self.assert_noobj(Site, "testsite")
+        self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
+        orig_site = self.assert_obj(Site, "testsite", site_url=None)
+        self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site",
+                                            props={"site_url": "http://foo.com/",
+                                                   "no-update": True}))
+        site = self.assert_obj(Site, "testsite", site_url=None)
+        assert(orig_site.id == site.id)
+
     def destroy_site(self):
         self.assert_noobj(Site, "testsite")
         self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
@@ -67,6 +96,14 @@
         self.destroy(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
         self.assert_noobj(Site, "testsite")
 
+    def destroy_site_nodelete(self):
+        self.assert_noobj(Site, "testsite")
+        self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
+        site = self.assert_obj(Site, "testsite")
+        self.destroy(self.make_nodetemplate("testsite", "tosca.nodes.Site",
+                                            props={"no-delete": True}))
+        self.assert_obj(Site, "testsite")
+
 if __name__ == "__main__":
     SiteTest()
 
diff --git a/xos/tosca/tests/slicetest.py b/xos/tosca/tests/slicetest.py
index 315f862..98de4e6 100644
--- a/xos/tosca/tests/slicetest.py
+++ b/xos/tosca/tests/slicetest.py
@@ -6,7 +6,11 @@
     tests = ["create_slice_minimal",
              "create_slice_maximal",
              "create_slice_privilege",
-             "destroy_slice"]
+             "create_slice_nocreate",
+             "update_slice",
+             "update_slice_noupdate",
+             "destroy_slice",
+             "destroy_slice_nodelete"]
 
     def cleanup(self):
         self.try_to_delete(Slice, name="testsite_testslice")
@@ -43,6 +47,41 @@
         dps = SlicePrivilege.objects.filter(user=user, slice=slice)
         assert(len(dps) == 1)
 
+    def create_slice_nocreate(self):
+        self.assert_noobj(Slice, "testsite_testslice")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+                                            reqs=[("testsite", "tosca.relationships.MemberOfSite")],
+                                            props={"no-create": True}))
+        self.assert_noobj(Slice, "testsite_testslice")
+
+    def update_slice(self):
+        self.assert_noobj(Slice, "testsite_testslice")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+                                            reqs=[("testsite", "tosca.relationships.MemberOfSite")]))
+        orig_slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+                                            reqs=[("testsite", "tosca.relationships.MemberOfSite")],
+                                            props={"description": "foo"}))
+        slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="foo", slice_url="", max_instances=10)
+        assert(orig_slice.id == slice.id)
+
+    def update_slice_noupdate(self):
+        self.assert_noobj(Slice, "testsite_testslice")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+                                            reqs=[("testsite", "tosca.relationships.MemberOfSite")]))
+        orig_slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+                                            reqs=[("testsite", "tosca.relationships.MemberOfSite")],
+                                            props={"description": "foo",
+                                                   "no-update": True}))
+        slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+        assert(orig_slice.id == slice.id)
+
     def destroy_slice(self):
         self.assert_noobj(Slice, "testsite_testslice")
         self.execute(self.get_base_templates() +
@@ -54,6 +93,19 @@
                                             reqs=[("testsite", "tosca.relationships.MemberOfSite")]))
         self.assert_noobj(Slice, "testsite_testslice")
 
+    def destroy_slice_nodelete(self):
+        self.assert_noobj(Slice, "testsite_testslice")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+                                            reqs=[("testsite", "tosca.relationships.MemberOfSite")]))
+        orig_slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+        self.destroy(self.get_base_templates() +
+                     self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+                                            reqs=[("testsite", "tosca.relationships.MemberOfSite")],
+                                            props={"no-delete": True}))
+        slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+        assert(slice.id == orig_slice.id)
+
 if __name__ == "__main__":
     SliceTest()
 
diff --git a/xos/xos/hpcapi.py b/xos/xos/hpcapi.py
new file mode 100644
index 0000000..389238e
--- /dev/null
+++ b/xos/xos/hpcapi.py
@@ -0,0 +1,1000 @@
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from rest_framework.reverse import reverse
+from rest_framework import serializers
+from rest_framework import generics
+from rest_framework import status
+from rest_framework.generics import GenericAPIView
+from hpc.models import *
+from django.forms import widgets
+from rest_framework import filters
+from django.conf.urls import patterns, url
+from rest_framework.exceptions import PermissionDenied as RestFrameworkPermissionDenied
+from django.core.exceptions import PermissionDenied as DjangoPermissionDenied
+from apibase import XOSRetrieveUpdateDestroyAPIView, XOSListCreateAPIView, XOSNotAuthenticated
+
+if hasattr(serializers, "ReadOnlyField"):
+    # rest_framework 3.x
+    IdField = serializers.ReadOnlyField
+else:
+    # rest_framework 2.x
+    IdField = serializers.Field
+
+"""
+    Schema of the generator object:
+        all: Set of all Model objects
+        all_if(regex): Set of Model objects that match regex
+
+    Model object:
+        plural: English plural of object name
+        camel: CamelCase version of object name
+        refs: list of references to other Model objects
+        props: list of properties minus refs
+
+    TODO: Deal with subnets
+"""
+
+def get_hpc_REST_patterns():
+    return patterns('',
+        url(r'^hpcapi/$', hpc_api_root),
+    
+        url(r'hpcapi/services/$', ServiceList.as_view(), name='service-list'),
+        url(r'hpcapi/services/(?P<pk>[a-zA-Z0-9\-]+)/$', ServiceDetail.as_view(), name ='service-detail'),
+    
+        url(r'hpcapi/hpchealthchecks/$', HpcHealthCheckList.as_view(), name='hpchealthcheck-list'),
+        url(r'hpcapi/hpchealthchecks/(?P<pk>[a-zA-Z0-9\-]+)/$', HpcHealthCheckDetail.as_view(), name ='hpchealthcheck-detail'),
+    
+        url(r'hpcapi/hpcservices/$', HpcServiceList.as_view(), name='hpcservice-list'),
+        url(r'hpcapi/hpcservices/(?P<pk>[a-zA-Z0-9\-]+)/$', HpcServiceDetail.as_view(), name ='hpcservice-detail'),
+    
+        url(r'hpcapi/originservers/$', OriginServerList.as_view(), name='originserver-list'),
+        url(r'hpcapi/originservers/(?P<pk>[a-zA-Z0-9\-]+)/$', OriginServerDetail.as_view(), name ='originserver-detail'),
+    
+        url(r'hpcapi/cdnprefixs/$', CDNPrefixList.as_view(), name='cdnprefix-list'),
+        url(r'hpcapi/cdnprefixs/(?P<pk>[a-zA-Z0-9\-]+)/$', CDNPrefixDetail.as_view(), name ='cdnprefix-detail'),
+    
+        url(r'hpcapi/users/$', UserList.as_view(), name='user-list'),
+        url(r'hpcapi/users/(?P<pk>[a-zA-Z0-9\-]+)/$', UserDetail.as_view(), name ='user-detail'),
+    
+        url(r'hpcapi/serviceproviders/$', ServiceProviderList.as_view(), name='serviceprovider-list'),
+        url(r'hpcapi/serviceproviders/(?P<pk>[a-zA-Z0-9\-]+)/$', ServiceProviderDetail.as_view(), name ='serviceprovider-detail'),
+    
+        url(r'hpcapi/contentproviders/$', ContentProviderList.as_view(), name='contentprovider-list'),
+        url(r'hpcapi/contentproviders/(?P<pk>[a-zA-Z0-9\-]+)/$', ContentProviderDetail.as_view(), name ='contentprovider-detail'),
+    
+        url(r'hpcapi/accessmaps/$', AccessMapList.as_view(), name='accessmap-list'),
+        url(r'hpcapi/accessmaps/(?P<pk>[a-zA-Z0-9\-]+)/$', AccessMapDetail.as_view(), name ='accessmap-detail'),
+    
+        url(r'hpcapi/sitemaps/$', SiteMapList.as_view(), name='sitemap-list'),
+        url(r'hpcapi/sitemaps/(?P<pk>[a-zA-Z0-9\-]+)/$', SiteMapDetail.as_view(), name ='sitemap-detail'),
+    
+    )
+
+@api_view(['GET'])
+def hpc_api_root(request, format=None):
+    return Response({
+        'services': reverse('service-list', request=request, format=format),
+        'hpchealthchecks': reverse('hpchealthcheck-list', request=request, format=format),
+        'hpcservices': reverse('hpcservice-list', request=request, format=format),
+        'originservers': reverse('originserver-list', request=request, format=format),
+        'cdnprefixs': reverse('cdnprefix-list', request=request, format=format),
+        'users': reverse('user-list', request=request, format=format),
+        'serviceproviders': reverse('serviceprovider-list', request=request, format=format),
+        'contentproviders': reverse('contentprovider-list', request=request, format=format),
+        'accessmaps': reverse('accessmap-list', request=request, format=format),
+        'sitemaps': reverse('sitemap-list', request=request, format=format),
+        
+    })
+
+# Based on serializers.py
+
+class XOSModelSerializer(serializers.ModelSerializer):
+    def save_object(self, obj, **kwargs):
+
+        """ rest_framework can't deal with ManyToMany relations that have a
+            through table. In xos, most of the through tables we have
+            use defaults or blank fields, so there's no reason why we shouldn't
+            be able to save these objects.
+
+            So, let's strip out these m2m relations, and deal with them ourself.
+        """
+        obj._complex_m2m_data={};
+        if getattr(obj, '_m2m_data', None):
+            for relatedObject in obj._meta.get_all_related_many_to_many_objects():
+                if (relatedObject.field.rel.through._meta.auto_created):
+                    # These are non-trough ManyToMany relations and
+                    # can be updated just fine
+                    continue
+                fieldName = relatedObject.get_accessor_name()
+                if fieldName in obj._m2m_data.keys():
+                    obj._complex_m2m_data[fieldName] = (relatedObject, obj._m2m_data[fieldName])
+                    del obj._m2m_data[fieldName]
+
+        serializers.ModelSerializer.save_object(self, obj, **kwargs);
+
+        for (accessor, stuff) in obj._complex_m2m_data.items():
+            (relatedObject, data) = stuff
+            through = relatedObject.field.rel.through
+            local_fieldName = relatedObject.field.m2m_reverse_field_name()
+            remote_fieldName = relatedObject.field.m2m_field_name()
+
+            # get the current set of existing relations
+            existing = through.objects.filter(**{local_fieldName: obj});
+
+            data_ids = [item.id for item in data]
+            existing_ids = [getattr(item,remote_fieldName).id for item in existing]
+
+            #print "data_ids", data_ids
+            #print "existing_ids", existing_ids
+
+            # remove relations that are in 'existing' but not in 'data'
+            for item in list(existing):
+               if (getattr(item,remote_fieldName).id not in data_ids):
+                   print "delete", getattr(item,remote_fieldName)
+                   item.delete() #(purge=True)
+
+            # add relations that are in 'data' but not in 'existing'
+            for item in data:
+               if (item.id not in existing_ids):
+                   #print "add", item
+                   newModel = through(**{local_fieldName: obj, remote_fieldName: item})
+                   newModel.save()
+
+
+
+class ServiceSerializer(serializers.HyperlinkedModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = Service
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','service_specific_id','service_specific_attribute',)
+
+class ServiceIdSerializer(XOSModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = Service
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','service_specific_id','service_specific_attribute',)
+
+
+
+
+class HpcHealthCheckSerializer(serializers.HyperlinkedModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = HpcHealthCheck
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','kind','resource_name','result_contains','result_min_size','result_max_size',)
+
+class HpcHealthCheckIdSerializer(XOSModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = HpcHealthCheck
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','kind','resource_name','result_contains','result_min_size','result_max_size',)
+
+
+
+
+class HpcServiceSerializer(serializers.HyperlinkedModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = HpcService
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','service_specific_id','service_specific_attribute','service_ptr','cmi_hostname','hpc_port80','watcher_hpc_network','watcher_dnsdemux_network','watcher_dnsredir_network',)
+
+class HpcServiceIdSerializer(XOSModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = HpcService
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','service_specific_id','service_specific_attribute','service_ptr','cmi_hostname','hpc_port80','watcher_hpc_network','watcher_dnsdemux_network','watcher_dnsredir_network',)
+
+
+
+
+class OriginServerSerializer(serializers.HyperlinkedModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = OriginServer
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','origin_server_id','url','contentProvider','authenticated','enabled','protocol','redirects','description',)
+
+class OriginServerIdSerializer(XOSModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = OriginServer
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','origin_server_id','url','contentProvider','authenticated','enabled','protocol','redirects','description',)
+
+
+
+
+class CDNPrefixSerializer(serializers.HyperlinkedModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = CDNPrefix
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','cdn_prefix_id','prefix','contentProvider','description','defaultOriginServer','enabled',)
+
+class CDNPrefixIdSerializer(XOSModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = CDNPrefix
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','cdn_prefix_id','prefix','contentProvider','description','defaultOriginServer','enabled',)
+
+
+
+
+class UserSerializer(serializers.HyperlinkedModelSerializer):
+    id = IdField()
+    
+    
+    contentproviders = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='contentprovider-detail')
+    
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = User
+        fields = ('humanReadableName', 'validators', 'id','password','last_login','email','username','firstname','lastname','phone','user_url','site','public_key','is_active','is_admin','is_staff','is_readonly','is_registering','is_appuser','login_page','created','updated','enacted','policed','backend_status','deleted','write_protect','timezone','contentproviders',)
+
+class UserIdSerializer(XOSModelSerializer):
+    id = IdField()
+    
+    
+    contentproviders = serializers.PrimaryKeyRelatedField(many=True,  queryset = ContentProvider.objects.all())
+    
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = User
+        fields = ('humanReadableName', 'validators', 'id','password','last_login','email','username','firstname','lastname','phone','user_url','site','public_key','is_active','is_admin','is_staff','is_readonly','is_registering','is_appuser','login_page','created','updated','enacted','policed','backend_status','deleted','write_protect','timezone','contentproviders',)
+
+
+
+
+class ServiceProviderSerializer(serializers.HyperlinkedModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = ServiceProvider
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','service_provider_id','name','description','enabled',)
+
+class ServiceProviderIdSerializer(XOSModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = ServiceProvider
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','service_provider_id','name','description','enabled',)
+
+
+
+
+class ContentProviderSerializer(serializers.HyperlinkedModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = ContentProvider
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','content_provider_id','name','enabled','description','serviceProvider',)
+
+class ContentProviderIdSerializer(XOSModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = ContentProvider
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','content_provider_id','name','enabled','description','serviceProvider',)
+
+
+
+
+class AccessMapSerializer(serializers.HyperlinkedModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = AccessMap
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','name','description','map',)
+
+class AccessMapIdSerializer(XOSModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = AccessMap
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','name','description','map',)
+
+
+
+
+class SiteMapSerializer(serializers.HyperlinkedModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = SiteMap
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','serviceProvider','cdnPrefix','hpcService','name','description','map','map_id',)
+
+class SiteMapIdSerializer(XOSModelSerializer):
+    id = IdField()
+    
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = SiteMap
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','serviceProvider','cdnPrefix','hpcService','name','description','map','map_id',)
+
+
+
+
+serializerLookUp = {
+
+                 Service: ServiceSerializer,
+
+                 HpcHealthCheck: HpcHealthCheckSerializer,
+
+                 HpcService: HpcServiceSerializer,
+
+                 OriginServer: OriginServerSerializer,
+
+                 CDNPrefix: CDNPrefixSerializer,
+
+                 User: UserSerializer,
+
+                 ServiceProvider: ServiceProviderSerializer,
+
+                 ContentProvider: ContentProviderSerializer,
+
+                 AccessMap: AccessMapSerializer,
+
+                 SiteMap: SiteMapSerializer,
+
+                 None: None,
+                }
+
+# Based on core/views/*.py
+
+
+class ServiceList(XOSListCreateAPIView):
+    queryset = Service.objects.select_related().all()
+    serializer_class = ServiceSerializer
+    id_serializer_class = ServiceIdSerializer
+    filter_backends = (filters.DjangoFilterBackend,)
+    filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','service_specific_id','service_specific_attribute',)
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return Service.select_by_user(self.request.user)
+
+
+class ServiceDetail(XOSRetrieveUpdateDestroyAPIView):
+    queryset = Service.objects.select_related().all()
+    serializer_class = ServiceSerializer
+    id_serializer_class = ServiceIdSerializer
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return Service.select_by_user(self.request.user)
+
+    # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+    # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class HpcHealthCheckList(XOSListCreateAPIView):
+    queryset = HpcHealthCheck.objects.select_related().all()
+    serializer_class = HpcHealthCheckSerializer
+    id_serializer_class = HpcHealthCheckIdSerializer
+    filter_backends = (filters.DjangoFilterBackend,)
+    filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','kind','resource_name','result_contains','result_min_size','result_max_size',)
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return HpcHealthCheck.select_by_user(self.request.user)
+
+
+class HpcHealthCheckDetail(XOSRetrieveUpdateDestroyAPIView):
+    queryset = HpcHealthCheck.objects.select_related().all()
+    serializer_class = HpcHealthCheckSerializer
+    id_serializer_class = HpcHealthCheckIdSerializer
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return HpcHealthCheck.select_by_user(self.request.user)
+
+    # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+    # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class HpcServiceList(XOSListCreateAPIView):
+    queryset = HpcService.objects.select_related().all()
+    serializer_class = HpcServiceSerializer
+    id_serializer_class = HpcServiceIdSerializer
+    filter_backends = (filters.DjangoFilterBackend,)
+    filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','service_specific_id','service_specific_attribute','service_ptr','cmi_hostname','hpc_port80','watcher_hpc_network','watcher_dnsdemux_network','watcher_dnsredir_network',)
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return HpcService.select_by_user(self.request.user)
+
+
+class HpcServiceDetail(XOSRetrieveUpdateDestroyAPIView):
+    queryset = HpcService.objects.select_related().all()
+    serializer_class = HpcServiceSerializer
+    id_serializer_class = HpcServiceIdSerializer
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return HpcService.select_by_user(self.request.user)
+
+    # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+    # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class OriginServerList(XOSListCreateAPIView):
+    queryset = OriginServer.objects.select_related().all()
+    serializer_class = OriginServerSerializer
+    id_serializer_class = OriginServerIdSerializer
+    filter_backends = (filters.DjangoFilterBackend,)
+    filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','origin_server_id','url','contentProvider','authenticated','enabled','protocol','redirects','description',)
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return OriginServer.select_by_user(self.request.user)
+
+
+class OriginServerDetail(XOSRetrieveUpdateDestroyAPIView):
+    queryset = OriginServer.objects.select_related().all()
+    serializer_class = OriginServerSerializer
+    id_serializer_class = OriginServerIdSerializer
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return OriginServer.select_by_user(self.request.user)
+
+    # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+    # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class CDNPrefixList(XOSListCreateAPIView):
+    queryset = CDNPrefix.objects.select_related().all()
+    serializer_class = CDNPrefixSerializer
+    id_serializer_class = CDNPrefixIdSerializer
+    filter_backends = (filters.DjangoFilterBackend,)
+    filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','cdn_prefix_id','prefix','contentProvider','description','defaultOriginServer','enabled',)
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return CDNPrefix.select_by_user(self.request.user)
+
+
+class CDNPrefixDetail(XOSRetrieveUpdateDestroyAPIView):
+    queryset = CDNPrefix.objects.select_related().all()
+    serializer_class = CDNPrefixSerializer
+    id_serializer_class = CDNPrefixIdSerializer
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return CDNPrefix.select_by_user(self.request.user)
+
+    # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+    # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class UserList(XOSListCreateAPIView):
+    queryset = User.objects.select_related().all()
+    serializer_class = UserSerializer
+    id_serializer_class = UserIdSerializer
+    filter_backends = (filters.DjangoFilterBackend,)
+    filter_fields = ('id','password','last_login','email','username','firstname','lastname','phone','user_url','site','public_key','is_active','is_admin','is_staff','is_readonly','is_registering','is_appuser','login_page','created','updated','enacted','policed','backend_status','deleted','write_protect','timezone','contentproviders',)
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return User.select_by_user(self.request.user)
+
+
+class UserDetail(XOSRetrieveUpdateDestroyAPIView):
+    queryset = User.objects.select_related().all()
+    serializer_class = UserSerializer
+    id_serializer_class = UserIdSerializer
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return User.select_by_user(self.request.user)
+
+    # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+    # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class ServiceProviderList(XOSListCreateAPIView):
+    queryset = ServiceProvider.objects.select_related().all()
+    serializer_class = ServiceProviderSerializer
+    id_serializer_class = ServiceProviderIdSerializer
+    filter_backends = (filters.DjangoFilterBackend,)
+    filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','service_provider_id','name','description','enabled',)
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return ServiceProvider.select_by_user(self.request.user)
+
+
+class ServiceProviderDetail(XOSRetrieveUpdateDestroyAPIView):
+    queryset = ServiceProvider.objects.select_related().all()
+    serializer_class = ServiceProviderSerializer
+    id_serializer_class = ServiceProviderIdSerializer
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return ServiceProvider.select_by_user(self.request.user)
+
+    # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+    # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class ContentProviderList(XOSListCreateAPIView):
+    queryset = ContentProvider.objects.select_related().all()
+    serializer_class = ContentProviderSerializer
+    id_serializer_class = ContentProviderIdSerializer
+    filter_backends = (filters.DjangoFilterBackend,)
+    filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','content_provider_id','name','enabled','description','serviceProvider',)
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return ContentProvider.select_by_user(self.request.user)
+
+
+class ContentProviderDetail(XOSRetrieveUpdateDestroyAPIView):
+    queryset = ContentProvider.objects.select_related().all()
+    serializer_class = ContentProviderSerializer
+    id_serializer_class = ContentProviderIdSerializer
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return ContentProvider.select_by_user(self.request.user)
+
+    # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+    # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class AccessMapList(XOSListCreateAPIView):
+    queryset = AccessMap.objects.select_related().all()
+    serializer_class = AccessMapSerializer
+    id_serializer_class = AccessMapIdSerializer
+    filter_backends = (filters.DjangoFilterBackend,)
+    filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','name','description','map',)
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return AccessMap.select_by_user(self.request.user)
+
+
+class AccessMapDetail(XOSRetrieveUpdateDestroyAPIView):
+    queryset = AccessMap.objects.select_related().all()
+    serializer_class = AccessMapSerializer
+    id_serializer_class = AccessMapIdSerializer
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return AccessMap.select_by_user(self.request.user)
+
+    # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+    # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class SiteMapList(XOSListCreateAPIView):
+    queryset = SiteMap.objects.select_related().all()
+    serializer_class = SiteMapSerializer
+    id_serializer_class = SiteMapIdSerializer
+    filter_backends = (filters.DjangoFilterBackend,)
+    filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','serviceProvider','cdnPrefix','hpcService','name','description','map','map_id',)
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return SiteMap.select_by_user(self.request.user)
+
+
+class SiteMapDetail(XOSRetrieveUpdateDestroyAPIView):
+    queryset = SiteMap.objects.select_related().all()
+    serializer_class = SiteMapSerializer
+    id_serializer_class = SiteMapIdSerializer
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return SiteMap.select_by_user(self.request.user)
+
+    # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+    # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
diff --git a/xos/xos/settings.py b/xos/xos/settings.py
index ba5ff1d..e660352 100644
--- a/xos/xos/settings.py
+++ b/xos/xos/settings.py
@@ -148,6 +148,7 @@
     'core',
     'hpc',
     'cord',
+    'services.onos',
     'ceilometer',
     'requestrouter',
 #    'urlfilter',
diff --git a/xos/xos/urls.py b/xos/xos/urls.py
index 0adf32d..3660282 100644
--- a/xos/xos/urls.py
+++ b/xos/xos/urls.py
@@ -5,9 +5,11 @@
 
 # This is the generated API
 from xosapi import *
+from hpcapi import *
 
 from core.views.legacyapi import LegacyXMLRPC
 from core.views.services import ServiceGridView, ServiceGraphView
+from helloworld.view import *
 #from core.views.analytics import AnalyticsAjaxView
 from core.models import *
 from rest_framework import generics
@@ -27,6 +29,7 @@
     # Examples:
     url(r'^stats', 'core.views.stats.Stats', name='stats'),
     url(r'^observer', 'core.views.observer.Observer', name='observer'),
+    url(r'^helloworld', HelloWorldView.as_view(), name='helloWorld'),
     url(r'^serviceGrid', ServiceGridView.as_view(), name='serviceGrid'),
     url(r'^serviceGraph.png', ServiceGraphView.as_view(), name='serviceGraph'),
     url(r'^hpcConfig', 'core.views.hpc_config.HpcConfig', name='hpcConfig'),
@@ -54,5 +57,5 @@
 
     # XOSLib rest methods
     url(r'^xoslib/', include('core.xoslib.methods', namespace='xoslib')),
-  ) + get_REST_patterns()
+  ) + get_REST_patterns() + get_hpc_REST_patterns()
 
diff --git a/xos/xos/xosapi.py b/xos/xos/xosapi.py
index bec3ea2..6532c72 100644
--- a/xos/xos/xosapi.py
+++ b/xos/xos/xosapi.py
@@ -1806,7 +1806,7 @@
             return None
     class Meta:
         model = Controller
-        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','backend_type','version','auth_url','admin_user','admin_password','admin_tenant','domain','deployment','dashboardviews',)
+        fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','backend_type','version','auth_url','admin_user','admin_password','admin_tenant','domain','rabbit_host','rabbit_user','rabbit_password','deployment','dashboardviews',)
 
 class ControllerIdSerializer(XOSModelSerializer):
     id = IdField()
@@ -4644,7 +4644,7 @@
     serializer_class = ControllerSerializer
     id_serializer_class = ControllerIdSerializer
     filter_backends = (filters.DjangoFilterBackend,)
-    filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','backend_type','version','auth_url','admin_user','admin_password','admin_tenant','domain','deployment','dashboardviews',)
+    filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','backend_type','version','auth_url','admin_user','admin_password','admin_tenant','domain','rabbit_host','rabbit_user','rabbit_password','deployment','dashboardviews',)
 
     def get_serializer_class(self):
         no_hyperlinks=False