Merge branch 'AddHelloWorldService' of https://github.com/jermowery/xos into AddHelloWorldService
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/synchronizers b/synchronizers
new file mode 120000
index 0000000..d587898
--- /dev/null
+++ b/synchronizers
@@ -0,0 +1 @@
+observers
\ No newline at end of file
diff --git a/xos/apigen/hpc-api.template.py b/xos/apigen/hpc-api.template.py
new file mode 100644
index 0000000..301346b
--- /dev/null
+++ b/xos/apigen/hpc-api.template.py
@@ -0,0 +1,211 @@
+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),
+    {% for object in generator.rest_models %}
+        url(r'hpcapi/{{ object.rest_name }}/$', {{ object.camel }}List.as_view(), name='{{ object.singular }}-list'),
+        url(r'hpcapi/{{ object.rest_name }}/(?P<pk>[a-zA-Z0-9\-]+)/$', {{ object.camel }}Detail.as_view(), name ='{{ object.singular }}-detail'),
+    {% endfor %}
+    )
+
+@api_view(['GET'])
+def hpc_api_root(request, format=None):
+    return Response({
+        {% for object in generator.rest_models %}'{{ object.plural }}': reverse('{{ object }}-list', request=request, format=format),
+        {% endfor %}
+    })
+
+# 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()
+
+{% for object in generator.rest_models %}
+
+class {{ object.camel }}Serializer(serializers.HyperlinkedModelSerializer):
+    id = IdField()
+    {% for ref in object.refs %}
+    {% if ref.multi %}
+    {{ ref.plural }} = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='{{ ref }}-detail')
+    {% else %}
+    {{ ref }} = serializers.HyperlinkedRelatedField(read_only=True, view_name='{{ ref }}-detail')
+    {% endif %}
+    {% endfor %}
+    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 = {{ object.camel }}
+        fields = ('humanReadableName', 'validators', {% for prop in object.props %}'{{ prop }}',{% endfor %}{% for ref in object.refs %}{%if ref.multi %}'{{ ref.plural }}'{% else %}'{{ ref }}'{% endif %},{% endfor %})
+
+class {{ object.camel }}IdSerializer(XOSModelSerializer):
+    id = IdField()
+    {% for ref in object.refs %}
+    {% if ref.multi %}
+    {{ ref.plural }} = serializers.PrimaryKeyRelatedField(many=True,  queryset = {{ ref.camel }}.objects.all())
+    {% else %}
+    {{ ref }} = serializers.PrimaryKeyRelatedField( queryset = {{ ref.camel }}.objects.all())
+    {% endif %}
+    {% endfor %}
+    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 = {{ object.camel }}
+        fields = ('humanReadableName', 'validators', {% for prop in object.props %}'{{ prop }}',{% endfor %}{% for ref in object.refs %}{%if ref.multi %}'{{ ref.plural }}'{% else %}'{{ ref }}'{% endif %},{% endfor %})
+
+
+{% endfor %}
+
+serializerLookUp = {
+{% for object in generator.rest_models %}
+                 {{ object.camel }}: {{ object.camel }}Serializer,
+{% endfor %}
+                 None: None,
+                }
+
+# Based on core/views/*.py
+{% for object in generator.rest_models %}
+
+class {{ object.camel }}List(XOSListCreateAPIView):
+    queryset = {{ object.camel }}.objects.select_related().all()
+    serializer_class = {{ object.camel }}Serializer
+    id_serializer_class = {{ object.camel }}IdSerializer
+    filter_backends = (filters.DjangoFilterBackend,)
+    filter_fields = ({% for prop in object.props %}'{{ prop }}',{% endfor %}{% for ref in object.refs %}{%if ref.multi %}'{{ ref.plural }}'{% else %}'{{ ref }}'{% endif %},{% endfor %})
+
+    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 {{ object.camel }}.select_by_user(self.request.user)
+
+
+class {{ object.camel }}Detail(XOSRetrieveUpdateDestroyAPIView):
+    queryset = {{ object.camel }}.objects.select_related().all()
+    serializer_class = {{ object.camel }}Serializer
+    id_serializer_class = {{ object.camel }}IdSerializer
+
+    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 {{ object.camel }}.select_by_user(self.request.user)
+
+    # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+    # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+{% endfor %}
diff --git a/xos/apigen/list.txt b/xos/apigen/list.txt
new file mode 100644
index 0000000..3e115e3
--- /dev/null
+++ b/xos/apigen/list.txt
@@ -0,0 +1,3 @@
+{% for o in generator.all %}
+{{ o.camel }}
+{% endfor %}
diff --git a/xos/apigen/modelgen b/xos/apigen/modelgen
index ec08c01..8ae0afb 100644
--- a/xos/apigen/modelgen
+++ b/xos/apigen/modelgen
@@ -7,15 +7,15 @@
 import json
 import re
 from django.template import Context, Template
-
-blacklist = ['SingletonModel','PlCoreBase']
+from optparse import OptionParser
 
 # Django set up
 
 sys.path.append('.')
 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
 from django.db.models.fields.related import ForeignKey, ManyToManyField
-from core.models import *
+
+options = None
 
 def singular(foo, keys):
 	for k in keys:
@@ -27,13 +27,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 options.blacklist:
+                            model_classes.append(c)
+
+    return model_classes
 
 class GenObj(object):
 	def __str__(self):
@@ -80,6 +91,10 @@
 class Generator(dict):
 	def all(self):
 		return self.values()
+
+        def rest_models(self):
+                norest = [x.lower() for x in options.norest]
+                return [v for v in self.values() if not (str(v) in norest)]
 	
 	def regex(self, r):
 		filtered = filter(lambda o:re.match(r,str(o)), self.values())
@@ -134,9 +149,9 @@
                                 if (related_name.endswith("+")):
                                     continue
 
-				if (related_name!='+' and related_name.lower()!=str(obj).lower()):
-                                        #print "XXX2", obj, f, related_name, related_model_name, refobj.plural_name
+				if (related_name!='+') and related_model_name in self: # and related_name.lower()!=str(obj).lower()):
                                         refobj = self[related_model_name]
+                                        #print "XXX2", obj, f, related_name, related_model_name, refobj.plural_name
 					cobj = copy.deepcopy(refobj)
 					cobj.multi = True
 
@@ -144,17 +159,32 @@
 
 
 
-			
+
 def main():
-	try:
-		output = sys.argv[1]
-	except:
-		print 'Usage: modelgen <output template>'
-		exit(1)
+        global options
+        parser = OptionParser(usage="modelgen [options] <template_fn>", )
+
+        parser.add_option("-a", "--app", dest="apps",
+             help="list of applications to parse", metavar="APP", default=[], action="append")
+        parser.add_option("-b", "--blacklist", dest="blacklist",
+             help="add model name to blacklist", metavar="MODEL", default=["SingletonModel", "PlCoreBase"], action="append")
+        parser.add_option("-n", "--no-rest", dest="norest",
+             help="do not generate rest api for model", metavar="MODEL", default=["SingletonModel", "PlCoreBase"], action="append")
+
+        (options, args) = parser.parse_args(sys.argv[1:])
+
+        if not options.apps:
+            options.apps = ["core"]
+
+        if len(args)!=1:
+            print 'Usage: modelgen [options] <template_fn>'
+            exit(1)
+
+        output = args[0]
 
 	generator = Generator()
 
-	models = enum_classes()
+	models = enum_classes(options.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/common/Dockerfile.common b/xos/configurations/common/Dockerfile.common
index 5329142..03b479a 100644
--- a/xos/configurations/common/Dockerfile.common
+++ b/xos/configurations/common/Dockerfile.common
@@ -48,7 +48,7 @@
 RUN pip install django-ipware
 RUN pip install django-encrypted-fields
 RUN pip install python-keyczar
-RUN pip install pygraphviz
+RUN pip install pygraphviz --install-option="--include-path=/usr/include/graphviz" --install-option="--library-path=/usr/lib/graphviz/"
 RUN pip install dnslib
 
 RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python-keystoneclient
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/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..efabcb2
--- /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).
+It launches an XOS container on Cloudlab that runs the XOS develserver.  The container is left running in the background.
+
+To get started on CloudLab:
+* Create an experiment using the *OpenStack-CORD* profile.  (You can also use the *OpenStack* profile, but choose *Kilo*
+and disable security groups.)
+* Wait until you get an email from CloudLab with title "OpenStack Instance Finished Setting Up".
+* Login to the *ctl* node of your experiment and run:
+```
+$ git clone https://github.com/open-cloud/xos.git
+$ cd xos/xos/configurations/cord/
+$ make
+```
+
+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
+1. *Slice mysite_vcpe*: runs the vCPE Docker container
+
+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 for the vCPE. 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..c66f7bc 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,172 @@
       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_vBNG:
+      type: tosca.nodes.ONOSService
+      requirements:
+      properties:
+          kind: onos
+          view_url: /admin/onos/onosservice/$id$/
+          public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+      artifacts:
+          pubkey: /opt/xos/observers/onos/onos_key.pub
+
+    vBNG_ONOS_app:
+      type: tosca.nodes.ONOSvBNGApp
+      requirements:
+          - onos_tenant:
+              node: service_ONOS_vBNG
+              relationship: tosca.relationships.TenantOfService
+          - vbng_service:
+              node: service_vbng
+              relationship: tosca.relationships.UsedByService
+      properties:
+          dependencies: org.onosproject.proxyarp, org.onosproject.virtualbng, org.onosproject.openflow, org.onosproject.fwd
+          config_network-cfg.json: >
+            {
+              "ports" : {
+                "of:00000000000000a1/1" : {
+                  "interfaces" : [
+                    {
+                      "ips"  : [ "10.0.1.253/24" ],
+                      "mac"  : "00:00:00:00:00:99"
+                    }
+                  ]
+                },
+                "of:00000000000000a5/2" : {
+                  "interfaces" : [
+                    {
+                      "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"
+            }
+
+    service_ONOS_vOLT:
+      type: tosca.nodes.ONOSService
+      requirements:
+      properties:
+          kind: onos
+          view_url: /admin/onos/onosservice/$id$/
+          public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+      artifacts:
+          pubkey: /opt/xos/observers/onos/onos_key.pub
+
+    vOLT_ONOS_app:
+      type: tosca.nodes.ONOSvOLTApp
+      requirements:
+          - onos_tenant:
+              node: service_ONOS_vOLT
+              relationship: tosca.relationships.TenantOfService
+          - volt_service:
+              node: service_volt
+              relationship: tosca.relationships.UsedByService
+      properties:
+          dependencies: org.onosproject.olt
+
+    # 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,12 +213,172 @@
               node: mysite
               relationship: tosca.relationships.MemberOfSite
 
-    # Now let's add a subscriber
+    mysite_onos_vbng:
+      description: ONOS Controller Slice for vBNG
+      type: tosca.nodes.Slice
+      requirements:
+          - ONOS:
+              node: service_ONOS_vBNG
+              relationship: tosca.relationships.MemberOfService
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
 
+    mysite_onos_volt:
+      description: ONOS Controller Slice for vOLT
+      type: tosca.nodes.Slice
+      requirements:
+          - ONOS:
+              node: service_ONOS_vOLT
+              relationship: tosca.relationships.MemberOfService
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
+    mysite_vbng:
+      description: slice running OVS controlled by vBNG
+      type: tosca.nodes.Slice
+      requirements:
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
+    mysite_volt:
+      description: OVS controlled by vOLT
+      type: tosca.nodes.Slice
+      requirements:
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
+    mysite_clients:
+      description: slice for clients at the subscriber
+      type: tosca.nodes.Slice
+      requirements:
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
+
+    # Virtual machines
+    onos_app_1:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: Ubuntu
+            version: 14.10
+      requirements:
+          - slice:
+                node: mysite_onos_vbng
+                relationship: tosca.relationships.MemberOfSlice
+
+    onos_app_2:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: Ubuntu
+            version: 14.10
+      requirements:
+          - slice:
+                node: mysite_onos_volt
+                relationship: tosca.relationships.MemberOfSlice
+
+    # VM for running the OVS controlled by vBNG
+    ovs_vbng:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: ubuntu
+            version: 14.04
+      requirements:
+          - slice:
+                node: mysite_vbng
+                relationship: tosca.relationships.MemberOfSlice
+
+    # VM for running the OVS controlled by vOLT
+    ovs_volt:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: ubuntu
+            version: 14.04
+      requirements:
+          - slice:
+                node: mysite_volt
+                relationship: tosca.relationships.MemberOfSlice
+
+    # A subscriber client VM
+    client1:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: ubuntu
+            version: 14.04
+      requirements:
+          - slice:
+                node: mysite_clients
+                relationship: tosca.relationships.MemberOfSlice
+
+    # A subscriber
     My House:
        type: tosca.nodes.CORDSubscriber
        properties:
-           service_specific_id: 1234
+           service_specific_id: 123
            firewall_enable: false
            cdn_enable: false
            url_filter_enable: false
@@ -154,8 +427,8 @@
     My Volt:
         type: tosca.nodes.VOLTTenant
         properties:
-            service_specific_id: 1234
-            vlan_id: 4321
+            service_specific_id: 123
+            vlan_id: 432
         requirements:
             - provider_service:
                 node: service_volt
diff --git a/xos/configurations/cord/dataplane/ansible.cfg b/xos/configurations/cord/dataplane/ansible.cfg
new file mode 100644
index 0000000..9100590
--- /dev/null
+++ b/xos/configurations/cord/dataplane/ansible.cfg
@@ -0,0 +1,4 @@
+[defaults]
+remote_user = ubuntu
+private_key_file = ~/.ssh/id_rsa
+host_key_checking = false
\ No newline at end of file
diff --git a/xos/configurations/cord/dataplane/dataplane.yaml b/xos/configurations/cord/dataplane/dataplane.yaml
new file mode 100644
index 0000000..ca6d9d5
--- /dev/null
+++ b/xos/configurations/cord/dataplane/dataplane.yaml
@@ -0,0 +1,92 @@
+---
+- hosts: switch_vbng
+  sudo: yes
+  vars:
+    controller:
+    - ip: "{{ hostvars['onos_vbng']['ansible_ssh_host'] }}"
+    - port: 6653
+  tasks:
+  - name: Fix /etc/hosts
+    lineinfile:
+      dest=/etc/hosts
+      regexp="127.0.0.1 localhost"
+      line="127.0.0.1 localhost {{ ansible_hostname }}"
+
+  - name: Install packages
+    apt: name={{ item }}
+      state=latest
+      update_cache=yes
+    with_items:
+    - openvswitch-switch
+    - python-netifaces
+
+  - name: Create br-vbng
+    openvswitch_bridge:
+      bridge=br-vbng
+      state=present
+
+  - name: Find wan_network interface
+    script: scripts/if_from_ip.py {{ wan_ip }}
+    register: wan_net
+
+  - name: Find public_network interface
+    script: scripts/if_from_ip.py {{ public_ip }}
+    register: public_net
+
+  - name: Hook up wan-network to br-vbng
+    openvswitch_port:
+      bridge=br-vbng
+      port={{ wan_net.stdout }}
+      state=present
+    
+  - name: Hook up public-network to OvS
+    openvswitch_port:
+      bridge=br-vbng
+      port={{ public_net.stdout }}
+      state=present
+
+  - name: Add controller to switch
+    debug: msg="Not implemented yet"
+
+- hosts: switch_volt
+  sudo: yes
+  vars:
+    controller:
+    - ip: "{{ hostvars['onos_volt']['ansible_ssh_host'] }}"
+    - port: 6653
+  tasks:
+
+  - name: Fix /etc/hosts
+    lineinfile:
+      dest=/etc/hosts
+      regexp="127.0.0.1 localhost"
+      line="127.0.0.1 localhost {{ ansible_hostname }}"
+
+  - name: Install packages
+    apt: name={{ item }} state=present update_cache=yes
+    with_items:
+    - git
+    - python-netifaces
+    
+  - name: Checkout the Mininet repo
+    git: repo=https://github.com/mininet/mininet.git
+      dest=/tmp/mininet
+
+  - name: Install the CPqD switch using Mininet install script
+    shell: /tmp/mininet/util/install.sh -3x
+    ignore_errors: true
+
+  - name: Find subscriber_network interface
+    script: scripts/if_from_ip.py {{ subscriber_ip }}
+    register: subscriber_net
+
+  - name: Find lan_network interface
+    script: scripts/if_from_ip.py {{ lan_ip }}
+    register: lan_net
+
+  - name: Run the datapath
+    shell: ofdatapath -i {{ subscriber_net.stdout }},{{ lan_net.stdout }} punix:/tmp/s1 -d 000000000001 --no-slicing
+
+  - name: Run the control program
+    shell: sudo ofprotocol unix:/tmp/s1 tcp:{{ controller.ip }}:{{ controller.port }} --fail=closed --listen=punix:/tmp/s1.listen
+
diff --git a/xos/configurations/cord/dataplane/generate.sh b/xos/configurations/cord/dataplane/generate.sh
new file mode 100755
index 0000000..360ed67
--- /dev/null
+++ b/xos/configurations/cord/dataplane/generate.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+# set -x
+
+source ~/admin-openrc.sh
+
+get_ip () {
+    LABEL=$1
+    NETWORK=$2
+    nova list --all-tenants|grep $LABEL|sed "s/^.*$NETWORK=//g"|sed 's/; .*$//g'|awk '{print $1}'
+}
+
+cat <<EOF
+onos_vbng    ansible_ssh_host=$( get_ip mysite_onos_vbng flat-lan-1-net)
+switch_vbng  ansible_ssh_host=$( get_ip mysite_vbng flat-lan-1-net) wan_ip=$( get_ip mysite_vbng wan_network) public_ip=$( get_ip mysite_vbng tun0-net )
+
+onos_volt    ansible_ssh_host=$( get_ip mysite_onos_volt flat-lan-1-net)
+switch_volt  ansible_ssh_host=$( get_ip mysite_volt flat-lan-1-net) subscriber_ip=$( get_ip mysite_volt subscriber_network) lan_ip=$( get_ip mysite_volt lan_network) vcpe_lan_ip=$( get_ip mysite_vcpe lan_network)
+EOF
diff --git a/xos/configurations/cord/dataplane/scripts/if_from_ip.py b/xos/configurations/cord/dataplane/scripts/if_from_ip.py
new file mode 100644
index 0000000..be1da48
--- /dev/null
+++ b/xos/configurations/cord/dataplane/scripts/if_from_ip.py
@@ -0,0 +1,14 @@
+#!/usr/bin/python
+
+import sys
+import netifaces
+
+def main (argv):
+    addr = argv[0]
+    for iface in netifaces.interfaces():
+        addrs = netifaces.ifaddresses(iface)
+        if 2 in addrs and addrs[2][0]['addr'] == addr:
+            print iface
+    
+if __name__ == "__main__":
+    main(sys.argv[1:])
diff --git a/xos/configurations/opencloud/Dockerfile b/xos/configurations/opencloud/Dockerfile
index 5b09507..cc8cd5a 100644
--- a/xos/configurations/opencloud/Dockerfile
+++ b/xos/configurations/opencloud/Dockerfile
@@ -3,7 +3,6 @@
 
 # Set environment variables.
 ENV HOME /root
-ENV TOSCA_CONFIG_PATH=/opt/xos/configurations/opencloud/opencloud.tosca
 
 # XXX Workaround for docker bug:
 # https://github.com/docker/docker/issues/6345
@@ -14,23 +13,21 @@
 
 # Install.
 RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \
+    curl \
     git \
-    postgresql \
+    geoip-database \
     graphviz \
     graphviz-dev \
+    libgeoip1 \
     libxslt1.1 \
     libxslt1-dev \
-    tar \
-    gcc \
-    geoip-database \
-    libgeoip1 \
-    openssh-client \
-    wget \
-    curl \
-    python-dev \
     libyaml-dev \
+    m4 \
+    nginx \
+    openssh-client \
+    python-dev \
     pkg-config \
-    supervisor \
+    postgresql \
     python-crypto \
     python-httplib2>=0.9.1 \
     python-jinja2 \
@@ -44,7 +41,10 @@
     python-novaclient \
     python-neutronclient \
     python-glanceclient \
-    python-ceilometerclient
+    python-ceilometerclient \
+    supervisor \
+    tar \
+    wget 
 
 RUN pip install \
     django==1.7 \
@@ -65,9 +65,11 @@
     django-ipware \
     django-encrypted-fields \
     django_rest_swagger \
+    python-dateutil \
     python-keyczar \
     pygraphviz \
-    dnslib
+    dnslib \
+    uwsgi
 
 RUN easy_install --upgrade httplib2
 
@@ -84,11 +86,15 @@
 RUN git clone -b release1.8.2 git://github.com/ansible/ansible-modules-extras.git /opt/ansible/v2/ansible/modules/extras
 RUN git clone git://github.com/sb98052/ansible-modules-core.git /opt/ansible/lib/ansible/modules/core
 RUN git clone git://github.com/sb98052/ansible-modules-core.git /opt/ansible/v2/ansible/modules/core
+# git clone uses cached copy, doesn't pick up latest
+RUN git -C /opt/ansible pull
+RUN git -C /opt/ansible/lib/ansible/modules/core pull
+RUN git -C /opt/ansible/v2/ansible/modules/core pull
 ADD ansible-hosts /etc/ansible/hosts
 
-ADD http://code.jquery.com/jquery-1.9.1.min.js /usr/local/lib/python2.7/dist-packages/suit/static/suit/js/
 
 # For Observer
+ADD http://code.jquery.com/jquery-1.9.1.min.js /usr/local/lib/python2.7/dist-packages/suit/static/suit/js/
 RUN git clone git://git.planet-lab.org/fofum.git /tmp/fofum
 RUN cd /tmp/fofum; python setup.py install
 RUN rm -rf /tmp/fofum
@@ -100,16 +106,11 @@
 RUN ln -s /usr/local/share/phantomjs-1.7.0-linux-x86_64 /usr/local/share/phantomjs
 RUN ln -s /usr/local/share/phantomjs/bin/phantomjs /bin/phantomjs
 
-# Supervisor
-ADD observer.conf /etc/supervisor/conf.d/
-
 # Get 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
-
 RUN chmod +x /opt/xos/scripts/opencloud
+
 RUN /opt/xos/scripts/opencloud genkeys
 
 # Workaround for AUFS issue
@@ -117,7 +118,7 @@
 RUN mkdir /etc/ssl/private-copy; mv /etc/ssl/private/* /etc/ssl/private-copy/; rm -r /etc/ssl/private; mv /etc/ssl/private-copy /etc/ssl/private; chmod -R 0700 /etc/ssl/private; chown -R postgres /etc/ssl/private
 
 # Set postgres password to match default value in settings.py
-RUN service postgresql start; sudo -u postgres psql -c "alter user postgres with password 'password';"
+RUN service postgresql start && sudo -u postgres psql -c "alter user postgres with password 'password';"
 
 # Turn DEBUG on so that devel server will serve static files
 #    (not necessary if --insecure is passed to 'manage.py runserver')
@@ -126,24 +127,17 @@
 # Cruft to workaround problems with migrations, should go away...
 RUN /opt/xos/scripts/opencloud remigrate
 
-# git clone uses cached copy, doesn't pick up latest
-RUN git -C /opt/ansible pull
-RUN git -C /opt/ansible/lib/ansible/modules/core pull
-RUN git -C /opt/ansible/v2/ansible/modules/core pull
 
 # install Tosca engine
-RUN apt-get install -y m4
-RUN pip install python-dateutil
 RUN bash /opt/xos/tosca/install_tosca.sh
 
+# configure nginx
+RUN cp /opt/xos/nginx/xos.conf /etc/nginx/sites-enabled/default
+
+# Supervisor configuration
+RUN cp /opt/xos/configurations/opencloud/supervisord.conf  /etc/supervisor/conf.d/xos-all.conf
+
 EXPOSE 8000
 
-# Set environment variables.
-ENV HOME /root
-
-# Define working directory.
-WORKDIR /root
-
 # Define default command.
-#CMD ["/bin/bash"]
-CMD /opt/xos/scripts/docker_start_xos
+CMD /usr/bin/supervisord -c /etc/supervisor/conf.d/xos-all.conf
diff --git a/xos/configurations/opencloud/Makefile b/xos/configurations/opencloud/Makefile
index 863f2b7..ab88aea 100644
--- a/xos/configurations/opencloud/Makefile
+++ b/xos/configurations/opencloud/Makefile
@@ -1,16 +1,16 @@
-RUNNING_CONTAINER:=$(shell sudo docker ps|grep "opencloud_server"|awk '{print $$NF}')
+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  --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
+stop: ; docker stop opencloud-server
 
 .PHONY: rmcontainer
-rmcontainer: ; docker rm opencloud_server
+rmcontainer: ; docker rm opencloud-server
diff --git a/xos/configurations/opencloud/supervisord.conf b/xos/configurations/opencloud/supervisord.conf
new file mode 100644
index 0000000..1fe737d
--- /dev/null
+++ b/xos/configurations/opencloud/supervisord.conf
@@ -0,0 +1,21 @@
+[supervisord]
+nodaemon=true
+logfile=/var/log/supervisord.log
+
+[program:observer]
+autorestart=true
+command=python /opt/xos/xos-observer.py
+stderr_logfile=/var/log/supervisor/observer.err.log
+stdout_logfile=/var/log/supervisor/observer.out.log
+
+program:uwsgi]
+autorestart=true
+command=uwsgi --ini /opt/xos/uwsgi/xos.ini
+stderr_logfile=/var/log/supervisor/uwsgi.err.log
+stdout_logfile=/var/log/supervisor/uwsgi.out.log
+
+[program:nginx]
+command=/usr/sbin/nginx
+autorestart=true
+stderr_logfile=/var/log/supervisor/nginx.err.log
+stdout_logfile=/var/log/supervisor/nginx.out.log
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 5d2996a..338f993 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']
@@ -1372,6 +1372,39 @@
     #    obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
     #    obj.delete()
 
+class ContainerPortInline(XOSTabularInline):
+    fields = ['backend_status_icon', 'network', 'container', 'ip', 'mac', 'segmentation_id']
+    readonly_fields = ("backend_status_icon", "ip", "mac", "segmentation_id")
+    model = Port
+    selflink_fieldname = "network"
+    extra = 0
+    verbose_name_plural = "Ports"
+    verbose_name = "Port"
+    suit_classes = 'suit-tab suit-tab-ports'
+
+class ContainerAdmin(XOSBaseAdmin):
+    fieldsets = [
+        ('Instance Details', {'fields': ['backend_status_text', 'slice', 'node', 'docker_image', 'no_sync'], 'classes': ['suit-tab suit-tab-general'], })
+    ]
+    readonly_fields = ('backend_status_text', )
+    list_display = ['backend_status_icon', 'id']
+    list_display_links = ('backend_status_icon', 'id', )
+
+    suit_form_tabs =(('general', 'Instance Details'), ('ports', 'Ports'))
+
+    inlines = [TagInline, ContainerPortInline]
+
+    def formfield_for_foreignkey(self, db_field, request, **kwargs):
+        if db_field.name == 'slice':
+            kwargs['queryset'] = Slice.select_by_user(request.user)
+
+        return super(ContainerAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
+
+    def queryset(self, request):
+        # admins can see all instances. Users can only see instances of
+        # the slices they belong to.
+        return Container.select_by_user(request.user)
+
 class UserCreationForm(forms.ModelForm):
     """A form for creating new users. Includes all the required
     fields, plus a repeated password."""
@@ -1727,7 +1760,7 @@
     readonly_fields = ('backend_status_icon', )
 
 class NetworkPortInline(XOSTabularInline):
-    fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
+    fields = ['backend_status_icon', 'network', 'instance', 'container', 'ip', 'mac']
     readonly_fields = ("backend_status_icon", "ip", "mac")
     model = Port
     selflink_fieldname = "instance"
@@ -2024,4 +2057,5 @@
     admin.site.register(TenantRoot, TenantRootAdmin)
     admin.site.register(TenantRootRole, TenantRootRoleAdmin)
     admin.site.register(TenantAttribute, TenantAttributeAdmin)
+    admin.site.register(Container, ContainerAdmin)
 
diff --git a/xos/core/models/__init__.py b/xos/core/models/__init__.py
index c380e9c..bc97dab 100644
--- a/xos/core/models/__init__.py
+++ b/xos/core/models/__init__.py
@@ -24,6 +24,7 @@
 from .node import Node
 from .slicetag import SliceTag
 from .instance import Instance
+from .container import Container
 from .reservation import ReservedResource
 from .reservation import Reservation
 from .network import Network, NetworkParameterType, NetworkParameter, Port, NetworkTemplate, Router, NetworkSlice, ControllerNetwork
diff --git a/xos/core/models/container.py b/xos/core/models/container.py
new file mode 100644
index 0000000..151b576
--- /dev/null
+++ b/xos/core/models/container.py
@@ -0,0 +1,83 @@
+import os
+from django.db import models
+from django.db.models import Q
+from django.core import exceptions
+from core.models import PlCoreBase,PlCoreBaseManager,PlCoreBaseDeletionManager
+from core.models.plcorebase import StrippedCharField
+from core.models import Image
+from core.models import Slice, SlicePrivilege
+from core.models import Node
+from core.models import Site
+from core.models import Deployment
+from core.models import Controller
+from core.models import User
+from core.models import Tag
+from core.models import Flavor
+from django.contrib.contenttypes import generic
+from xos.config import Config
+from monitor import driver as monitor
+from django.core.exceptions import PermissionDenied, ValidationError
+
+config = Config()
+
+
+# Create your models here.
+class Container(PlCoreBase):
+    name = StrippedCharField(max_length=200, help_text="Container name")
+    slice = models.ForeignKey(Slice, related_name='containers')
+    node = models.ForeignKey(Node, related_name='containers')
+    creator = models.ForeignKey(User, related_name='containers', blank=True, null=True)
+    docker_image = StrippedCharField(null=True, blank=True, max_length=200, help_text="name of docker container to instantiate")
+
+    def __unicode__(self):
+        return u'container-%s' % str(self.id)
+
+    def save(self, *args, **kwds):
+        if not self.name:
+            self.name = self.slice.name
+        if not self.creator and hasattr(self, 'caller'):
+            self.creator = self.caller
+        if not self.creator:
+            raise ValidationError('container has no creator')
+
+        if (self.slice.creator != self.creator):
+            # Check to make sure there's a slice_privilege for the user. If there
+            # isn't, then keystone will throw an exception inside the observer.
+            slice_privs = SlicePrivilege.objects.filter(slice=self.slice, user=self.creator)
+            if not slice_privs:
+                raise ValidationError('container creator has no privileges on slice')
+
+# XXX smbaker - disabled for now, was causing fault in tenant view create slice
+#        if not self.controllerNetwork.test_acl(slice=self.slice):
+#            raise exceptions.ValidationError("Deployment %s's ACL does not allow any of this slice %s's users" % (self.controllerNetwork.name, self.slice.name))
+
+        super(Container, self).save(*args, **kwds)
+
+    def can_update(self, user):
+        return True
+
+    @staticmethod
+    def select_by_user(user):
+        if user.is_admin:
+            qs = Container.objects.all()
+        else:
+            slices = Slice.select_by_user(user)
+            qs = Container.objects.filter(slice__in=slices)
+        return qs
+
+    def get_public_keys(self):
+        slice_memberships = SlicePrivilege.objects.filter(slice=self.slice)
+        pubkeys = set([sm.user.public_key for sm in slice_memberships if sm.user.public_key])
+
+        if self.creator.public_key:
+            pubkeys.add(self.creator.public_key)
+
+        if self.slice.creator.public_key:
+            pubkeys.add(self.slice.creator.public_key)
+
+        if self.slice.service and self.slice.service.public_key:
+            pubkeys.add(self.slice.service.public_key)
+
+        return pubkeys
+
+
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/network.py b/xos/core/models/network.py
index 2bdf17f..48af5a6 100644
--- a/xos/core/models/network.py
+++ b/xos/core/models/network.py
@@ -2,7 +2,7 @@
 import socket
 import sys
 from django.db import models
-from core.models import PlCoreBase, Site, Slice, Instance, Controller
+from core.models import PlCoreBase, Site, Slice, Instance, Controller, Container
 from core.models import ControllerLinkManager,ControllerLinkDeletionManager
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes import generic
@@ -158,7 +158,7 @@
     router_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum router id")
     subnet_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum subnet id")
     subnet = models.CharField(max_length=32, blank=True)
-     
+
     class Meta:
         unique_together = ('network', 'controller')
         
@@ -211,9 +211,12 @@
 class Port(PlCoreBase):
     network = models.ForeignKey(Network,related_name='links')
     instance = models.ForeignKey(Instance, null=True, blank=True, related_name='ports')
+    container = models.ForeignKey(Container, null=True, blank=True, related_name='ports')
     ip = models.GenericIPAddressField(help_text="Instance ip address", blank=True, null=True)
     port_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum port id")
     mac = models.CharField(null=True, blank=True, max_length=256, help_text="MAC address associated with this port")
+    #unattached = models.BooleanField(default=False, help_text="create this port even if no Instance is attached")
+    segmentation_id = models.CharField(null=True, blank=True, max_length=256, help_text="GRE segmentation id for port")
 
     class Meta:
         unique_together = ('network', 'instance')
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 8d2180c..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)
 
@@ -489,7 +493,7 @@
     KIND = "Provider"
 
 class TenantAttribute(PlCoreBase):
-    name = models.SlugField(help_text="Attribute Name", max_length=128)
+    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")
 
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/views/services.py b/xos/core/views/services.py
index 6d18c26..e11988b 100644
--- a/xos/core/views/services.py
+++ b/xos/core/views/services.py
@@ -121,14 +121,14 @@
         g.graph_attr.update(graphdir="TB")
 
         for service in Service.objects.all():
-            provided_tenants = CoarseTenant.get_tenant_objects().filter(provider_service=service)
-            subscribed_tenants = CoarseTenant.get_tenant_objects().filter(subscriber_service=service)
+            provided_tenants = Tenant.objects.filter(provider_service=service, subscriber_service__isnull=False)
+            subscribed_tenants = Tenant.objects.filter(subscriber_service=service, provider_service__isnull=False)
             if not (provided_tenants or subscribed_tenants):
                # nodes with no edges aren't interesting
                continue
             g.add_node(service.id, label=service.name)
 
-        for tenant in CoarseTenant.get_tenant_objects().all():
+        for tenant in Tenant.objects.all():
             if (not tenant.provider_service) or (not tenant.subscriber_service):
                 continue
             g.add_edge(tenant.subscriber_service.id, tenant.provider_service.id)
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..f438aad 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/models.py b/xos/helloworld/models.py
index 37f6751..a657f3a 100644
--- a/xos/helloworld/models.py
+++ b/xos/helloworld/models.py
@@ -11,7 +11,7 @@
 
 class Hello(PlCoreBase):
     name = models.CharField(max_length=254,help_text="Salutation e.g. Hello or Bonjour")
-    sliver_backref = models.ForeignKey(Instance)
+    instance_backref = models.ForeignKey(Instance)
     
 class World(PlCoreBase):
     name = models.CharField(max_length=254,help_text="Name of planet")
diff --git a/xos/nginx/xos.conf b/xos/nginx/xos.conf
index 9b5d996..64d9d5e 100644
--- a/xos/nginx/xos.conf
+++ b/xos/nginx/xos.conf
@@ -6,8 +6,9 @@
 
 
 server {
-    listen 80;
-    server_name 128.112.139.48;
+    listen   80;
+    listen   [::]:80 default ipv6only=on; ## listen for ipv6
+    server_name 127.0.0.1;
 
     location /static/ {
         alias /opt/xos/core/static/;
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/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/monitoring_channel/monitoring_channel_observer.py b/xos/observers/monitoring_channel/monitoring_channel_observer.py
new file mode 100755
index 0000000..d6a71ff
--- /dev/null
+++ b/xos/observers/monitoring_channel/monitoring_channel_observer.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+# This imports and runs ../../xos-observer.py
+
+import importlib
+import os
+import sys
+observer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"../..")
+sys.path.append(observer_path)
+mod = importlib.import_module("xos-observer")
+mod.main()
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..c6be286
--- /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=final_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=final_query, links=1)
+              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/onos/onos-observer.py b/xos/observers/onos/onos-observer.py
new file mode 100755
index 0000000..d6a71ff
--- /dev/null
+++ b/xos/observers/onos/onos-observer.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+# This imports and runs ../../xos-observer.py
+
+import importlib
+import os
+import sys
+observer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"../..")
+sys.path.append(observer_path)
+mod = importlib.import_module("xos-observer")
+mod.main()
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..ad3718c
--- /dev/null
+++ b/xos/observers/onos/steps/sync_onosapp.yaml
@@ -0,0 +1,54 @@
+---
+- 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
+
+{% if config_fns %}
+  - 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: Make sure config directory exists
+    shell: docker exec {{ ONOS_container }} mkdir -p /root/onos/config/
+    sudo: yes
+
+  - 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 %}
+{% endif %}
+
+  # 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..b485c0f 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,32 @@
   - name: Install resolv.conf
     copy: src=/opt/xos/observers/vcpe/files/vm-resolv.conf
       dest=/etc/resolv.conf
+
+  - name: Verify if vcpe_stats_notifier ([] is to avoid capturing the shell process) cron job is already running
+    shell: pgrep -f [v]cpe_stats_notifier | wc -l
+    register: cron_job_pids_count
+
+#  - name: DEBUG
+#    debug: var=cron_job_pids_count.stdout
+
+  - name: make sure ~/bin exists
+    file: path=~/bin state=directory owner=root group=root
+    when: cron_job_pids_count.stdout == "0"
+
+  - name: Copy cron job to destination
+    copy: src=/opt/xos/observers/vcpe/vcpe_stats_notifier.py
+      dest=~/bin/vcpe_stats_notifier.py
+    when: cron_job_pids_count.stdout == "0"
+
+  - name: install python-kombu
+    apt: name=python-kombu state=present
+    when: cron_job_pids_count.stdout == "0"
+
+  - name: Initiate vcpe_stats_notifier cron job
+    command: 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: 9999999999999999
+    poll: 0
+    when: cron_job_pids_count.stdout == "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..d726e3c
--- /dev/null
+++ b/xos/observers/vcpe/vcpe_stats_notifier.py
@@ -0,0 +1,268 @@
+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)
+# create formatter
+formatter = logging.Formatter("%(asctime)s;%(levelname)s;%(message)s")
+handler=logging.handlers.RotatingFileHandler(logfile,maxBytes=1000000, backupCount=1)
+# add formatter to handler
+handler.setFormatter(formatter)
+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
+
+     logger.debug('publish_cpe_stats invoked')
+
+     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)
+          logger.debug('Publishing vcpe event: %s', 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)
+               logger.debug('Publishing vcpe.dns.cache.size event: %s', 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)
+               logger.debug('Publishing vcpe.dns.total_inserted_entries event: %s', 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)
+               logger.debug('Publishing vcpe.dns.replaced_unexpired_entries event: %s', 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)
+               logger.debug('Publishing vcpe.dns.queries_forwarded event: %s', 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)
+               logger.debug('Publishing vcpe.dns.queries_answered_locally event: %s', 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)
+                   logger.debug('Publishing vcpe.dns.server.queries_sent event: %s', 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)
+                   logger.debug('Publishing vcpe.dns.server.queries_failed event: %s', 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/ansible.py b/xos/openstack_observer/ansible.py
index ee110c9..94b09bd 100644
--- a/xos/openstack_observer/ansible.py
+++ b/xos/openstack_observer/ansible.py
@@ -144,16 +144,21 @@
     return ok_results
 
 def run_template_ssh(name, opts, path='', expected_num=None):
-    instance_id = opts["instance_id"]
     instance_name = opts["instance_name"]
     hostname = opts["hostname"]
     private_key = opts["private_key"]
-    nat_ip = opts["nat_ip"]
-
-    try:
-        proxy_ssh = Config().observer_proxy_ssh
-    except:
-        proxy_ssh = True
+    baremetal_ssh = opts.get("baremetal_ssh",False)
+    if baremetal_ssh:
+        # no instance_id or nat_ip for baremetal
+        # we never proxy to baremetal
+        proxy_ssh = False
+    else:
+        instance_id = opts["instance_id"]
+        nat_ip = opts["nat_ip"]
+        try:
+            proxy_ssh = Config().observer_proxy_ssh
+        except:
+            proxy_ssh = True
 
     (opts, fqp) = get_playbook_fn(opts, path)
     private_key_pathname = fqp + ".key"
@@ -177,7 +182,7 @@
 
     f = open(hosts_pathname, "w")
     f.write("[%s]\n" % instance_name)
-    if proxy_ssh:
+    if proxy_ssh or baremetal_ssh:
         f.write("%s ansible_ssh_private_key_file=%s\n" % (hostname, private_key_pathname))
     else:
         # acb: Login user is hardcoded, this is not great
diff --git a/xos/openstack_observer/steps/sync_container.py b/xos/openstack_observer/steps/sync_container.py
new file mode 100644
index 0000000..c6ac936
--- /dev/null
+++ b/xos/openstack_observer/steps/sync_container.py
@@ -0,0 +1,90 @@
+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 core.models import Service, Slice, Container
+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 SyncContainer(SyncStep):
+    provides=[Container]
+    observes=Container
+    requested_interval=0
+    template_name = "sync_container.yaml"
+
+    def __init__(self, *args, **kwargs):
+        super(SyncContainer, 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_node(self,o):
+        return o.node
+
+    def get_node_key(self, node):
+        return "/opt/xos/node-key"
+
+    def get_extra_attributes(self, o):
+        fields={}
+        fields["ansible_tag"] = "container-%s" % str(o.id)
+        fields["baremetal_ssh"] = True
+        fields["instance_name"] = "rootcontext"
+        fields["container_name"] = o.name
+        fields["docker_image"] = o.docker_image
+        fields["username"] = "xos"
+        return fields
+
+    def sync_fields(self, o, fields):
+        self.run_playbook(o, fields)
+
+    def sync_record(self, o):
+        logger.info("sync'ing object %s" % str(o))
+
+        node = self.get_node(o)
+        node_key_name = self.get_node_key(node)
+
+        if not os.path.exists(node_key_name):
+            raise Exception("Node key %s does not exist" % node_key_name)
+
+        node_key = file(node_key_name).read()
+
+        fields = { "hostname": node.name,
+                   "private_key": node_key,
+                 }
+
+        # If 'o' defines a 'sync_attributes' list, then we'll copy those
+        # attributes into the Ansible recipe's field list automatically.
+        if hasattr(o, "sync_attributes"):
+            for attribute_name in o.sync_attributes:
+                fields[attribute_name] = getattr(o, attribute_name)
+
+        fields.update(self.get_extra_attributes(o))
+
+        self.sync_fields(o, fields)
+
+        o.save()
+
+    def run_playbook(self, o, fields):
+        tStart = time.time()
+        run_template_ssh(self.template_name, fields, path="container")
+        logger.info("playbook execution time %d" % int(time.time()-tStart))
+
+    def delete_record(self, m):
+        pass
diff --git a/xos/openstack_observer/steps/sync_container.yaml b/xos/openstack_observer/steps/sync_container.yaml
new file mode 100644
index 0000000..d97f0df
--- /dev/null
+++ b/xos/openstack_observer/steps/sync_container.yaml
@@ -0,0 +1,48 @@
+---
+- hosts: {{ instance_name }}
+  gather_facts: False
+  connection: ssh
+  user: {{ username }}
+  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 Container
+    docker:
+      docker_api_version: "1.18"
+      name: {{ container_name }}
+      # was: reloaded
+      state: running
+      image: {{ docker_image }}
+
diff --git a/xos/openstack_observer/steps/sync_ports.py b/xos/openstack_observer/steps/sync_ports.py
index 967bef9..7b20d29 100644
--- a/xos/openstack_observer/steps/sync_ports.py
+++ b/xos/openstack_observer/steps/sync_ports.py
@@ -144,9 +144,14 @@
 
         # For ports that were created by the user, find that ones
         # that don't have neutron ports, and create them.
-        for port in Port.objects.filter(port_id__isnull=True, instance__isnull=False):
-            #logger.info("XXX working on port %s" % port)
-            controller = port.instance.node.site_deployment.controller
+        for port in Port.objects.filter(Q(port_id__isnull=True), Q(instance__isnull=False) | Q(container__isnull=False)):
+            logger.info("XXX working on port %s" % port)
+            if port.instance:
+                controller = port.instance.node.site_deployment.controller
+                slice = port.instance.slice
+            else:
+                controller = port.container.node.site_deployment.controller
+                slice = port.container.slice
             if controller:
                 cn=port.network.controllernetworks.filter(controller=controller)
                 if not cn:
@@ -174,7 +179,7 @@
                     caller = port.network.owner.creator
                     auth = {'username': caller.email,
                             'password': caller.remote_password,
-                            'tenant': port.instance.slice.name} # port.network.owner.name}
+                            'tenant': slice.name}
                     client = OpenStackClient(controller=controller, **auth) # cacert=self.config.nova_ca_ssl_cert,
                     driver = OpenStackDriver(client=client)
 
@@ -183,6 +188,10 @@
                     if neutron_port["fixed_ips"]:
                         port.ip = neutron_port["fixed_ips"][0]["ip_address"]
                     port.mac = neutron_port["mac_address"]
+
+                    neutron_network = driver.shell.quantum.list_networks(cn.net_id)["networks"][0]
+                    if "provider:segmentation_id" in neutron_network:
+                        port.segmentation_id = neutron_network["provider:segmentation_id"]
                 except:
                     logger.log_exc("failed to create neutron port for %s" % port)
                     continue
diff --git a/xos/openstack_synchronizer b/xos/openstack_synchronizer
new file mode 120000
index 0000000..ae75af5
--- /dev/null
+++ b/xos/openstack_synchronizer
@@ -0,0 +1 @@
+openstack_observer
\ No newline at end of file
diff --git a/xos/services/onos/admin.py b/xos/services/onos/admin.py
index b2e5349..d13a991 100644
--- a/xos/services/onos/admin.py
+++ b/xos/services/onos/admin.py
@@ -18,15 +18,32 @@
 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" ], 'classes':['suit-tab suit-tab-general']})]
+    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
 
@@ -48,6 +65,7 @@
 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)

@@ -57,6 +75,7 @@
             # 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

@@ -64,10 +83,10 @@
             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:

@@ -76,7 +95,7 @@
 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', 'service_specific_attribute',
+    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')
diff --git a/xos/services/onos/models.py b/xos/services/onos/models.py
index 018bd25..80e903e 100644
--- a/xos/services/onos/models.py
+++ b/xos/services/onos/models.py
@@ -21,13 +21,24 @@
         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": ""}
+    default_attributes = {"name": "",
+                          "dependencies": ""}
     def __init__(self, *args, **kwargs):
         onos_services = ONOSService.get_service_objects().all()
         if onos_services:
@@ -65,6 +76,27 @@
     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):
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index 3d766f1..e125150 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -104,17 +104,36 @@
             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.
+            An ONOS vBNG Application.
         properties:
             xos_base_tenant_props
-            config_addresses_json:
+            dependencies:
                 type: string
                 required: false
-            config_virtualbng_json:
+            config_addresses.json:
+                type: string
+                required: false
+            config_network-cfg.json:
+                type: string
+                required: false
+            config_virtualbng.json:
+                type: string
+                required: false
+
+    tosca.nodes.ONOSvOLTApp:
+        derived_from: tosca.nodes.Root
+        description: >
+            An ONOS vOLT Application.
+        properties:
+            xos_base_tenant_props
+            dependencies:
                 type: string
                 required: false
 
@@ -472,6 +491,18 @@
                 type: string
                 required: false
                 description: OpenStack domain (or "Default")
+            rabbit_host:
+                type: string
+                required: false
+                description: Rabbit host
+            rabbit_user:
+                type: string
+                required: false
+                description: Rabbit user
+            rabbit_password:
+                type: string
+                required: false
+                description: Rabbit password
 
     tosca.nodes.Site:
         derived_from: tosca.nodes.Root
@@ -563,6 +594,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 ]
@@ -666,4 +701,3 @@
     tosca.capabilities.xos.Image:
         derived_from: tosca.capabilities.Root
         description: An XOS Image
-
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 97031a6..24be6af 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -108,11 +108,14 @@
                 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.
+            An ONOS vBNG Application.
         properties:
             kind:
                 type: string
@@ -122,10 +125,33 @@
                 type: string
                 required: false
                 description: Service specific ID opaque to XOS but meaningful to service
-            config_addresses_json:
+            dependencies:
                 type: string
                 required: false
-            config_virtualbng_json:
+            config_addresses.json:
+                type: string
+                required: false
+            config_network-cfg.json:
+                type: string
+                required: false
+            config_virtualbng.json:
+                type: string
+                required: false
+
+    tosca.nodes.ONOSvOLTApp:
+        derived_from: tosca.nodes.Root
+        description: >
+            An ONOS vOLT 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
 
@@ -615,6 +641,18 @@
                 type: string
                 required: false
                 description: OpenStack domain (or "Default")
+            rabbit_host:
+                type: string
+                required: false
+                description: Rabbit host
+            rabbit_user:
+                type: string
+                required: false
+                description: Rabbit user
+            rabbit_password:
+                type: string
+                required: false
+                description: Rabbit password
 
     tosca.nodes.Site:
         derived_from: tosca.nodes.Root
@@ -739,6 +777,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 ]
@@ -842,4 +884,3 @@
     tosca.capabilities.xos.Image:
         derived_from: tosca.capabilities.Root
         description: An XOS Image
-
diff --git a/xos/tosca/resources/controller.py b/xos/tosca/resources/controller.py
index 9a20ea5..2aa208c 100644
--- a/xos/tosca/resources/controller.py
+++ b/xos/tosca/resources/controller.py
@@ -15,7 +15,7 @@
 class XOSController(XOSResource):
     provides = "tosca.nodes.Controller"
     xos_model = Controller
-    copyin_props = ["backend_type", "version", "auth_url", "admin_user", "admin_password", "admin_tenant", "domain"]
+    copyin_props = ["backend_type", "version", "auth_url", "admin_user", "admin_password", "admin_tenant", "domain", "rabbit_host", "rabbit_user", "rabbit_password"]
 
     def get_xos_args(self):
         args = super(XOSController, self).get_xos_args()
diff --git a/xos/tosca/resources/onosapp.py b/xos/tosca/resources/onosapp.py
index 50af543..111cf9a 100644
--- a/xos/tosca/resources/onosapp.py
+++ b/xos/tosca/resources/onosapp.py
@@ -6,23 +6,29 @@
 from translator.toscalib.tosca_template import ToscaTemplate
 import pdb
 
-from core.models import User, TenantAttribute
+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"]
+    provides = ["tosca.nodes.ONOSApp", "tosca.nodes.ONOSvBNGApp", "tosca.nodes.ONOSvOLTApp"]
     xos_model = ONOSApp
-    copyin_props = ["service_specific_id"]
+    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):
diff --git a/xos/tosca/samples/new_site_deploy_slice.yaml b/xos/tosca/samples/new_site_deploy_slice.yaml
index 6ccadc1..5e36bcb 100644
--- a/xos/tosca/samples/new_site_deploy_slice.yaml
+++ b/xos/tosca/samples/new_site_deploy_slice.yaml
@@ -27,6 +27,9 @@
            admin_password: letmeout
            admin_tenant: 12345678
            domain: mydomain
+           rabbit_host: rabhost
+           rabbit_user: rabuser
+           rabbit_password: rabpw
 
     newsite:
       type: tosca.nodes.Site
diff --git a/xos/tosca/samples/onos.yaml b/xos/tosca/samples/onos.yaml
index 311819e..a549515 100644
--- a/xos/tosca/samples/onos.yaml
+++ b/xos/tosca/samples/onos.yaml
@@ -13,6 +13,9 @@
       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
@@ -21,7 +24,8 @@
               node: ONOS
               relationship: tosca.relationships.TenantOfService
       properties:
-          config_addresses_json: >
+          dependencies: org.onosproject.proxyarp, org.onosproject.virtualbng, org.onosproject.openflow, org.onosproject.fwd
+          config_addresses.json: >
             {
                 "addresses" : [

                             {

@@ -39,7 +43,7 @@
                             }

                 ]

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

                     "200.0.0.0/32",

@@ -52,3 +56,48 @@
                 "xosRestPort" : "9999"

             }
 
+    vOLT:
+      type: tosca.nodes.ONOSvOLTApp
+      requirements:
+          - onos_tenant:
+              node: ONOS
+              relationship: tosca.relationships.TenantOfService
+      properties:
+          dependencies: org.onosproject.olt
+
+    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/uwsgi/xos.ini b/xos/uwsgi/xos.ini
index da7f7dd..63eb6b2 100644
--- a/xos/uwsgi/xos.ini
+++ b/xos/uwsgi/xos.ini
@@ -2,17 +2,17 @@
 chdir = /opt/xos
 module = xos.wsgi:application
 env = DJANGO_SETTINGS_MODULE=xos.settings
-socket = /var/run/uwsgi/xos.sock
+socket = /var/run/uwsgi.xos.sock
 socket = 127.0.0.1:9001
-http = 128.112.139.48:9002
+http = 127.0.0.1:9002
 stats = 127.0.0.1:9003
 workers = 3
 master = true
 processes = 8
-uid = uwsgi
-gid = webserver 
+uid = root
+gid = root
 harakiri = 20
-daemonize=/var/log/uwsgi/xos.log 
+daemonize=/var/log/uwsgi.xos.log 
 static-map = /static=/var/www/xos/static
-pidfile = /var/run/uwsgi/uwsgi.pid
+pidfile = /var/run/uwsgi.xos.pid
 buffer-size = 8192
diff --git a/xos/xos/hpcapi.py b/xos/xos/hpcapi.py
new file mode 100644
index 0000000..f0e3a6c
--- /dev/null
+++ b/xos/xos/hpcapi.py
@@ -0,0 +1,824 @@
+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/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/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({
+        '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),
+        '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 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 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()
+    
+    
+    users = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='user-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 = 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','users',)
+
+class ContentProviderIdSerializer(XOSModelSerializer):
+    id = IdField()
+    
+    
+    users = serializers.PrimaryKeyRelatedField(many=True,  queryset = User.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 = 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','users',)
+
+
+
+
+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 = {
+
+                 HpcHealthCheck: HpcHealthCheckSerializer,
+
+                 HpcService: HpcServiceSerializer,
+
+                 OriginServer: OriginServerSerializer,
+
+                 CDNPrefix: CDNPrefixSerializer,
+
+                 ServiceProvider: ServiceProviderSerializer,
+
+                 ContentProvider: ContentProviderSerializer,
+
+                 AccessMap: AccessMapSerializer,
+
+                 SiteMap: SiteMapSerializer,
+
+                 None: None,
+                }
+
+# Based on core/views/*.py
+
+
+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 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','users',)
+
+    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 bf2e1c2..d7ec5e7 100644
--- a/xos/xos/settings.py
+++ b/xos/xos/settings.py
@@ -120,6 +120,12 @@
 
 # Python dotted path to the WSGI application used by Django's runserver.
 WSGI_APPLICATION = 'xos.wsgi.application'
+# Default: 'csrftoken'
+CSRF_COOKIE_NAME = 'xoscsrftoken'
+# Default: 'django_language'
+LANGUAGE_COOKIE_NAME = 'xos_django_language'
+# Default: 'sessionid'
+SESSION_COOKIE_NAME = 'xossessionid'
 
 TEMPLATE_DIRS = (
     # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
diff --git a/xos/xos/urls.py b/xos/xos/urls.py
index 6ba98d8..3660282 100644
--- a/xos/xos/urls.py
+++ b/xos/xos/urls.py
@@ -5,6 +5,7 @@
 
 # This is the generated API
 from xosapi import *
+from hpcapi import *
 
 from core.views.legacyapi import LegacyXMLRPC
 from core.views.services import ServiceGridView, ServiceGraphView
@@ -56,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