resolve merge conflicts
diff --git a/Dockerfile.cord b/Dockerfile.cord
new file mode 100644
index 0000000..5a2074d
--- /dev/null
+++ b/Dockerfile.cord
@@ -0,0 +1,5 @@
+FROM       xos:latest
+MAINTAINER Andy Bavier <acb@cs.princeton.edu>
+
+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
diff --git a/cloudlab-init.sh b/cloudlab-init.sh
index 9810d16..b946249 100755
--- a/cloudlab-init.sh
+++ b/cloudlab-init.sh
@@ -1,23 +1,40 @@
 #!/bin/bash
 set -x
 
-# This script assumes that it is being run on the ctl node of the Tutorial-OpenStack
+# This script assumes that it is being run on the ctl node of the OpenStack
 # profile on CloudLab.
 
 XOS="http://ctl:9999/"
 AUTH="padmin@vicci.org:letmein"
+CORD=0
+IMAGE="xos"
+
+# Create public key if none present
+[ -e ~/.ssh/id_rsa ] || cat /dev/zero | ssh-keygen -q -N ""
 
 # Install Docker
-wget -qO- https://get.docker.com/ | sh
+which docker > /dev/null || wget -qO- https://get.docker.com/ | sh
 sudo usermod -aG docker $(whoami)
 
 sudo apt-get install httpie
 
+if [ "$CORD" -ne 0 ]
+then
+    cp ~/.ssh/id_rsa.pub xos/observers/vcpe/vcpe_public_key
+    cp ~/.ssh/id_rsa     xos/observers/vcpe/vcpe_private_key
+fi
+
 sudo docker build -t xos .
 
+if [ "$CORD" -ne 0 ]
+then
+    sudo docker build -t cord -f Dockerfile.cord .
+    IMAGE="cord"
+fi
+
 # OpenStack is using port 8000...
 MYIP=$( hostname -i )
-sudo docker run -d --add-host="ctl:$MYIP" -p 9999:8000 xos
+sudo docker run -d --add-host="ctl:$MYIP" -p 9999:8000 $IMAGE
 
 echo "Waiting for XOS to come up"
 until http $XOS &> /dev/null
@@ -25,12 +42,9 @@
     sleep 1
 done
 
-# Create public key if none present
-cat /dev/zero | ssh-keygen -q -N ""
-PUBKEY=$( cat ~/.ssh/id_rsa.pub )
-
 # Copy public key
 # BUG: Shouldn't have to set the 'enacted' field...
+PUBKEY=$( cat ~/.ssh/id_rsa.pub )
 http --auth $AUTH PATCH $XOS/xos/users/1/ public_key="$PUBKEY" enacted=$( date "+%Y-%m-%dT%T")
 
 # Set up controller
@@ -56,3 +70,9 @@
 # BUG: Shouldn't have to set the controller_kind field, it's invalid in the initial fixture
 FLATNET=$( sudo bash -c "source /root/setup/admin-openrc.sh ; neutron net-list" |grep flat|awk '{print $4}' )
 http --auth $AUTH PATCH $XOS/xos/networktemplates/2/ shared_network_name=$FLATNET controller_kind=""
+
+if [ "$CORD" -ne 0 ]
+then
+    DOCKER=$( docker ps|grep $IMAGE|awk '{print $NF}' )
+    docker exec $DOCKER bash -c "cd /opt/xos/tosca; python run.py padmin@vicci.org samples/cord-cloudlab.yaml"
+fi
diff --git a/xos/ceilometer/__init__.py b/xos/ceilometer/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/xos/ceilometer/__init__.py
@@ -0,0 +1 @@
+
diff --git a/xos/ceilometer/admin.py b/xos/ceilometer/admin.py
new file mode 100644
index 0000000..8b6f469
--- /dev/null
+++ b/xos/ceilometer/admin.py
@@ -0,0 +1,83 @@
+from django.contrib import admin
+
+from ceilometer.models import *
+from django import forms
+from django.utils.safestring import mark_safe
+from django.contrib.auth.admin import UserAdmin
+from django.contrib.admin.widgets import FilteredSelectMultiple
+from django.contrib.auth.forms import ReadOnlyPasswordHashField
+from django.contrib.auth.signals import user_logged_in
+from django.utils import timezone
+from django.contrib.contenttypes import generic
+from suit.widgets import LinkedSelect
+from core.admin import ServiceAppAdmin,SliceInline,ServiceAttrAsTabInline, ReadOnlyAwareAdmin, XOSTabularInline, ServicePrivilegeInline, TenantRootTenantInline, TenantRootPrivilegeInline
+
+from functools import update_wrapper
+from django.contrib.admin.views.main import ChangeList
+from django.core.urlresolvers import reverse
+from django.contrib.admin.utils import quote
+
+class CeilometerServiceAdmin(ReadOnlyAwareAdmin):
+    model = CeilometerService
+    verbose_name = "Ceilometer Service"
+    verbose_name_plural = "Ceilometer Service"
+    list_display = ("backend_status_icon", "name", "enabled")
+    list_display_links = ('backend_status_icon', 'name', )
+    fieldsets = [(None, {'fields': ['backend_status_text', 'name','enabled','versionNumber', 'description',"view_url","icon_url" ], 'classes':['suit-tab suit-tab-general']})]
+    readonly_fields = ('backend_status_text', )
+    inlines = [SliceInline,ServiceAttrAsTabInline,ServicePrivilegeInline]
+
+    extracontext_registered_admins = True
+
+    user_readonly_fields = ["name", "enabled", "versionNumber", "description"]
+
+    suit_form_tabs =(('general', 'Ceilometer Service Details'),
+        ('administration', 'Administration'),
+        ('slices','Slices'),
+        ('serviceattrs','Additional Attributes'),
+        ('serviceprivileges','Privileges'),
+    )
+
+    suit_form_includes = (('ceilometeradmin.html', 'top', 'administration'),
+                           )
+
+    def queryset(self, request):
+        return CeilometerService.get_service_objects_by_user(request.user)
+
+class MonitoringChannelForm(forms.ModelForm):
+    creator = forms.ModelChoiceField(queryset=User.objects.all())
+
+    def __init__(self,*args,**kwargs):

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

+        self.fields['kind'].default = CEILOMETER_KIND

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

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

+        if self.instance:

+            # fields for the attributes

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

+

+    def save(self, commit=True):

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

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

+

+    class Meta:

+        model = MonitoringChannel
+
+class MonitoringChannelAdmin(ReadOnlyAwareAdmin):
+    list_display = ('backend_status_icon', 'id', )
+    list_display_links = ('backend_status_icon', 'id')
+    fieldsets = [ (None, {'fields': ['backend_status_text', 'kind', 'provider_service', 'service_specific_attribute',
+                                     'sliver',
+                                     'creator'],
+                          'classes':['suit-tab suit-tab-general']})]
+    readonly_fields = ('backend_status_text', 'sliver', 'service_specific_attribute')
+    form = MonitoringChannelForm
+
+    suit_form_tabs = (('general','Details'),)
+
+    def queryset(self, request):
+        return MonitoringChannel.get_tenant_objects_by_user(request.user)
+
+admin.site.register(CeilometerService, CeilometerServiceAdmin)
+admin.site.register(MonitoringChannel, MonitoringChannelAdmin)
+
diff --git a/xos/ceilometer/models.py b/xos/ceilometer/models.py
new file mode 100644
index 0000000..2b2e990
--- /dev/null
+++ b/xos/ceilometer/models.py
@@ -0,0 +1,60 @@
+from django.db import models
+from core.models import Service, PlCoreBase, Slice, Sliver, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber
+from core.models.plcorebase import StrippedCharField
+import os
+from django.db import models, transaction
+from django.forms.models import model_to_dict
+from django.db.models import Q
+from operator import itemgetter, attrgetter, methodcaller
+import traceback
+from xos.exceptions import *
+
+CEILOMETER_KIND = "ceilometer"
+
+class CeilometerService(Service):
+    KIND = CEILOMETER_KIND
+
+    class Meta:
+        app_label = "ceilometer"
+        verbose_name = "Ceilometer Service"
+        proxy = True
+
+class MonitoringChannel(TenantWithContainer):   # aka 'CeilometerTenant'
+    class Meta:
+        proxy = True
+
+    KIND = CEILOMETER_KIND
+
+    default_attributes = {}
+    def __init__(self, *args, **kwargs):
+        ceilometer_services = CeilometerService.get_service_objects().all()
+        if ceilometer_services:
+            self._meta.get_field("provider_service").default = ceilometer_services[0].id
+        super(MonitoringChannel, self).__init__(*args, **kwargs)
+
+    def save(self, *args, **kwargs):
+        if not self.creator:
+            if not getattr(self, "caller", None):
+                # caller must be set when creating a vCPE since it creates a slice
+                raise XOSProgrammingError("MonitoringChannel's self.caller was not set")
+            self.creator = self.caller
+            if not self.creator:
+                raise XOSProgrammingError("MonitoringChannel's self.creator was not set")
+
+        super(MonitoringChannel, self).save(*args, **kwargs)
+        model_policy_monitoring_channel(self.pk)
+
+    def delete(self, *args, **kwargs):
+        self.cleanup_container()
+        super(MonitoringChannel, self).delete(*args, **kwargs)
+
+def model_policy_monitoring_channel(pk):
+    # TODO: this should be made in to a real model_policy
+    with transaction.atomic():
+        mc = MonitoringChannel.objects.select_for_update().filter(pk=pk)
+        if not mc:
+            return
+        mc = mc[0]
+        mc.manage_container()
+
+
diff --git a/xos/ceilometer/templates/ceilometeradmin.html b/xos/ceilometer/templates/ceilometeradmin.html
new file mode 100644
index 0000000..8149666
--- /dev/null
+++ b/xos/ceilometer/templates/ceilometeradmin.html
@@ -0,0 +1,6 @@
+<div class = "left-nav">
+<ul>
+<li><a href="/admin/ceilometer/monitoringchannel/">Monitoring Channels</a></li>
+</ul>
+</div>
+
diff --git a/xos/cord/models.py b/xos/cord/models.py
index f94befc..68bd2ba 100644
--- a/xos/cord/models.py
+++ b/xos/cord/models.py
@@ -1,5 +1,5 @@
 from django.db import models
-from core.models import Service, PlCoreBase, Slice, Instance, Tenant, Node, Image, User, Flavor, Subscriber
+from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber
 from core.models.plcorebase import StrippedCharField
 import os
 from django.db import models, transaction
@@ -457,18 +457,17 @@
 VCPEService.setup_simple_attributes()
 
 
-class VCPETenant(Tenant):
+class VCPETenant(TenantWithContainer):
     class Meta:
         proxy = True
 
     KIND = VCPE_KIND
 
-    sync_attributes = ("nat_ip",
-                       "lan_ip",
-                       "wan_ip",
-                       "private_ip",
-                       "hpc_client_ip",
-                       "wan_mac")
+    sync_attributes = ("nat_ip", "nat_mac",
+                       "lan_ip", "lan_mac",
+                       "wan_ip", "wan_mac",
+                       "private_ip", "private_mac",
+                       "hpc_client_ip", "hpc_client_mac")
 
     default_attributes = {"instance_id": None,
                           "users": [],
@@ -599,50 +598,58 @@
             return {}
 
         addresses = {}
-        for ns in self.sliver.ports.all():
+        for ns in self.instance.ports.all():
             if "lan" in ns.network.name.lower():
-                addresses["lan"] = ns.ip
+                addresses["lan"] = (ns.ip, ns.mac)
             elif "wan" in ns.network.name.lower():
-                addresses["wan"] = ns.ip
+                addresses["wan"] = (ns.ip, ns.mac)
             elif "private" in ns.network.name.lower():
-                addresses["private"] = ns.ip
+                addresses["private"] = (ns.ip, ns.mac)
             elif "nat" in ns.network.name.lower():
-                addresses["nat"] = ns.ip
+                addresses["nat"] = (ns.ip, ns.mac)
             elif "hpc_client" in ns.network.name.lower():
-                addresses["hpc_client"] = ns.ip
+                addresses["hpc_client"] = (ns.ip, ns.mac)
         return addresses
 
     @property
     def nat_ip(self):
-        return self.addresses.get("nat",None)
+        return self.addresses.get("nat", (None,None) )[0]
+
+    @property
+    def nat_mac(self):
+        return self.addresses.get("nat", (None,None) )[1]
 
     @property
     def lan_ip(self):
-        return self.addresses.get("lan",None)
+        return self.addresses.get("lan", (None, None) )[0]
+
+    @property
+    def lan_mac(self):
+        return self.addresses.get("lan", (None, None) )[1]
 
     @property
     def wan_ip(self):
-        return self.addresses.get("wan",None)
+        return self.addresses.get("wan", (None, None) )[0]
 
     @property
     def wan_mac(self):
-        ip = self.wan_ip
-        if not ip:
-           return None
-        try:
-           (a,b,c,d) = ip.split('.')
-           wan_mac = "02:42:%2x:%2x:%2x:%2x" % (int(a), int(b), int(c), int(d))
-        except:
-           wan_mac = "Exception"
-        return wan_mac
+        return self.addresses.get("wan", (None, None) )[1]
 
     @property
     def private_ip(self):
-        return self.addresses.get("private",None)
+        return self.addresses.get("private", (None, None) )[0]
+
+    @property
+    def private_mac(self):
+        return self.addresses.get("private", (None, None) )[1]
 
     @property
     def hpc_client_ip(self):
-        return self.addresses.get("hpc_client",None)
+        return self.addresses.get("hpc_client", (None, None) )[0]
+
+    @property
+    def hpc_client_mac(self):
+        return self.addresses.get("hpc_client", (None, None) )[1]
 
     @property
     def is_synced(self):
@@ -767,7 +774,7 @@
 
     def delete(self, *args, **kwargs):
         self.cleanup_vbng()
-        self.cleanup_instance()
+        self.cleanup_container()
         super(VCPETenant, self).delete(*args, **kwargs)
 
 def model_policy_vcpe(pk):
@@ -777,7 +784,7 @@
         if not vcpe:
             return
         vcpe = vcpe[0]
-        vcpe.manage_instance()
+        vcpe.manage_container()
         vcpe.manage_vbng()
         vcpe.manage_bbs_account()
         vcpe.cleanup_orphans()
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 2dae51b..d9f285b 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -1273,8 +1273,8 @@
     user_readonly_inlines = []
 
 class InstancePortInline(XOSTabularInline):
-    fields = ['backend_status_icon', 'network', 'instance', 'ip']
-    readonly_fields = ("backend_status_icon", "ip", )
+    fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
+    readonly_fields = ("backend_status_icon", "ip", "mac")
     model = Port
     selflink_fieldname = "network"
     extra = 0
@@ -1701,8 +1701,8 @@
     readonly_fields = ('backend_status_icon', )
 
 class NetworkPortInline(XOSTabularInline):
-    fields = ['backend_status_icon', 'network', 'instance', 'ip']
-    readonly_fields = ("backend_status_icon", "ip", )
+    fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
+    readonly_fields = ("backend_status_icon", "ip", "mac")
     model = Port
     selflink_fieldname = "instance"
     extra = 0
diff --git a/xos/core/models/__init__.py b/xos/core/models/__init__.py
index 4a2051f..0efe37b 100644
--- a/xos/core/models/__init__.py
+++ b/xos/core/models/__init__.py
@@ -1,7 +1,7 @@
 from .plcorebase import PlCoreBase,PlCoreBaseManager,PlCoreBaseDeletionManager,PlModelMixIn
 from .project import Project
 from .singletonmodel import SingletonModel
-from .service import Service, Tenant, CoarseTenant, ServicePrivilege, TenantRoot, TenantRootPrivilege, TenantRootRole, Subscriber, Provider
+from .service import Service, Tenant, TenantWithContainer, CoarseTenant, ServicePrivilege, TenantRoot, TenantRootPrivilege, TenantRootRole, Subscriber, Provider
 from .service import ServiceAttribute
 from .tag import Tag
 from .role import Role
diff --git a/xos/core/models/network.py b/xos/core/models/network.py
index f7032b0..dfda072 100644
--- a/xos/core/models/network.py
+++ b/xos/core/models/network.py
@@ -219,6 +219,7 @@
     instance = models.ForeignKey(Instance, null=True, blank=True, related_name='networkinstances')
     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")
 
     class Meta:
         unique_together = ('network', 'instance')
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 6eed241..b69acf1 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -332,6 +332,130 @@
             return None
         return sorted(st, key=attrgetter('id'))[0]
 
+class TenantWithContainer(Tenant):
+    """ A tenant that manages a container """
+
+    # this is a hack and should be replaced by something smarter...
+    LOOK_FOR_IMAGES=["ubuntu-vcpe4",        # ONOS demo machine -- preferred vcpe image
+                     "Ubuntu 14.04 LTS",    # portal
+                     "Ubuntu-14.04-LTS",    # ONOS demo machine
+                     "trusty-server-multi-nic", # CloudLab
+                    ]
+
+    class Meta:
+        proxy = True
+
+    def __init__(self, *args, **kwargs):
+        super(TenantWithContainer, self).__init__(*args, **kwargs)
+        self.cached_sliver=None
+        self.orig_sliver_id = self.get_initial_attribute("sliver_id")
+
+    @property
+    def sliver(self):
+        from core.models import Sliver
+        if getattr(self, "cached_sliver", None):
+            return self.cached_sliver
+        sliver_id=self.get_attribute("sliver_id")
+        if not sliver_id:
+            return None
+        slivers=Sliver.objects.filter(id=sliver_id)
+        if not slivers:
+            return None
+        sliver=slivers[0]
+        sliver.caller = self.creator
+        self.cached_sliver = sliver
+        return sliver
+
+    @sliver.setter
+    def sliver(self, value):
+        if value:
+            value = value.id
+        if (value != self.get_attribute("sliver_id", None)):
+            self.cached_sliver=None
+        self.set_attribute("sliver_id", value)
+
+    @property
+    def creator(self):
+        from core.models import User
+        if getattr(self, "cached_creator", None):
+            return self.cached_creator
+        creator_id=self.get_attribute("creator_id")
+        if not creator_id:
+            return None
+        users=User.objects.filter(id=creator_id)
+        if not users:
+            return None
+        user=users[0]
+        self.cached_creator = users[0]
+        return user
+
+    @creator.setter
+    def creator(self, value):
+        if value:
+            value = value.id
+        if (value != self.get_attribute("creator_id", None)):
+            self.cached_creator=None
+        self.set_attribute("creator_id", value)
+
+    @property
+    def image(self):
+        from core.models import Image
+        # Implement the logic here to pick the image that should be used when
+        # instantiating the VM that will hold the container.
+        for image_name in self.LOOK_FOR_IMAGES:
+            images = Image.objects.filter(name = image_name)
+            if images:
+                return images[0]
+
+        raise XOSProgrammingError("No VPCE image (looked for %s)" % str(self.LOOK_FOR_IMAGES))
+
+    def pick_node(self):
+        from core.models import Node
+        nodes = list(Node.objects.all())
+        # TODO: logic to filter nodes by which nodes are up, and which
+        #   nodes the slice can instantiate on.
+        nodes = sorted(nodes, key=lambda node: node.slivers.all().count())
+        return nodes[0]
+
+    def manage_container(self):
+        from core.models import Sliver, Flavor
+
+        if self.deleted:
+            return
+
+        if (self.sliver is not None) and (self.sliver.image != self.image):
+            self.sliver.delete()
+            self.sliver = None
+
+        if self.sliver is None:
+            if not self.provider_service.slices.count():
+                raise XOSConfigurationError("The VCPE service has no slices")
+
+            flavors = Flavor.objects.filter(name="m1.small")
+            if not flavors:
+                raise XOSConfigurationError("No m1.small flavor")
+
+            node =self.pick_node()
+            sliver = Sliver(slice = self.provider_service.slices.all()[0],
+                            node = node,
+                            image = self.image,
+                            creator = self.creator,
+                            deployment = node.site_deployment.deployment,
+                            flavor = flavors[0])
+            sliver.save()
+
+            try:
+                self.sliver = sliver
+                super(TenantWithContainer, self).save()
+            except:
+                sliver.delete()
+                raise
+
+    def cleanup_container(self):
+        if self.sliver:
+            # print "XXX cleanup sliver", self.sliver
+            self.sliver.delete()
+            self.sliver = None
 
 class CoarseTenant(Tenant):
     """ TODO: rename "CoarseTenant" --> "StaticTenant" """
diff --git a/xos/observers/vcpe/files/vm-resolv.conf b/xos/observers/vcpe/files/vm-resolv.conf
new file mode 100644
index 0000000..cae093a
--- /dev/null
+++ b/xos/observers/vcpe/files/vm-resolv.conf
@@ -0,0 +1 @@
+nameserver 8.8.8.8
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant.py b/xos/observers/vcpe/steps/sync_vcpetenant.py
index 7ce3eaa..1a45b54 100644
--- a/xos/observers/vcpe/steps/sync_vcpetenant.py
+++ b/xos/observers/vcpe/steps/sync_vcpetenant.py
@@ -116,10 +116,16 @@
         if o.volt:
             vlan_ids.append(o.volt.vlan_id)
 
+        try:
+            full_setup = Config().observer_full_setup
+        except:
+            full_setup = True
+
         fields = {"vlan_ids": vlan_ids,
                 "dnsdemux_ip": dnsdemux_ip,
                 "cdn_prefixes": cdn_prefixes,
-                "bbs_addrs": bbs_addrs}
+                "bbs_addrs": bbs_addrs,
+                "full_setup": full_setup}
 
         # add in the sync_attributes that come from the SubscriberRoot object
 
@@ -203,4 +209,3 @@
 
     def delete_record(self, m):
         pass
-
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant.yaml b/xos/observers/vcpe/steps/sync_vcpetenant.yaml
index cb8f92b..063ab06 100644
--- a/xos/observers/vcpe/steps/sync_vcpetenant.yaml
+++ b/xos/observers/vcpe/steps/sync_vcpetenant.yaml
@@ -26,11 +26,15 @@
         - {{ bbs_addr }}
         {% endfor %}
       nat_ip: {{ nat_ip }}
+      nat_mac: {{ nat_mac }}
       lan_ip: {{ lan_ip }}
+      lan_mac: {{ lan_mac }}
       wan_ip: {{ wan_ip }}
-      private_ip: {{ private_ip }}
-      hpc_client_ip: {{ hpc_client_ip }}
       wan_mac: {{ wan_mac }}
+      private_ip: {{ private_ip }}
+      private_mac: {{ private_mac }}
+      hpc_client_ip: {{ hpc_client_ip }}
+      hpc_client_mac: {{ hpc_client_mac }}
 
   tasks:
 {% if full_setup %}
@@ -67,7 +71,7 @@
     shell: rm -f /etc/resolv.conf
 
   - name: Install resolv.conf
-    copy src=/opt/xos/observers/vcpe/files/vm-resolv.conf
+    copy: src=/opt/xos/observers/vcpe/files/vm-resolv.conf
       dest=/etc/resolv.conf
 {% endif %}
 
diff --git a/xos/observers/vcpe/templates/start-vcpe.sh.j2 b/xos/observers/vcpe/templates/start-vcpe.sh.j2
index 565adaf..7e714de 100755
--- a/xos/observers/vcpe/templates/start-vcpe.sh.j2
+++ b/xos/observers/vcpe/templates/start-vcpe.sh.j2
@@ -1,5 +1,15 @@
 #!/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
 
@@ -15,12 +25,18 @@
 fi
 
 # Set up networking via pipework
-docker exec $VCPE ifconfig eth0 >> /dev/null || pipework eth4 -i eth0 $VCPE {{ wan_ip }}/17@192.168.128.1 {{ wan_mac }}
-docker exec $VCPE ifconfig eth1 >> /dev/null || pipework eth3 -i eth1 $VCPE 192.168.0.1/24 @{{ vlan_ids[0] }}
-docker exec $VCPE ifconfig eth2 >> /dev/null || pipework eth0 -i eth2 $VCPE {{ hpc_client_ip }}/24
+WAN_CONTAINER_MAC=$( generate_mac_from_ip {{ wan_ip }} )
+WAN_IFACE=$( mac_to_iface {{ wan_mac }} )
+docker exec $VCPE ifconfig eth0 >> /dev/null || pipework $WAN_IFACE -i eth0 $VCPE {{ wan_ip }}/17@192.168.128.1 $WAN_CONTAINER_MAC
+
+LAN_IFACE=$( mac_to_iface {{ lan_mac }} )
+docker exec $VCPE ifconfig eth1 >> /dev/null || pipework $LAN_IFACE -i eth1 $VCPE 192.168.0.1/24 @{{ vlan_ids[0] }}
+
+HPC_IFACE=$( mac_to_iface {{ hpc_client_mac }} )
+docker exec $VCPE ifconfig eth2 >> /dev/null || pipework $HPC_IFACE -i eth2 $VCPE {{ hpc_client_ip }}/24
 
 # Make sure VM's eth0 (hpc_client) has no IP address
-ifconfig eth0 0.0.0.0
+ifconfig $HPC_IFACE 0.0.0.0
 
 # Now can start up dnsmasq
 docker exec $VCPE service dnsmasq start
diff --git a/xos/observers/vcpe/vcpe_observer_config b/xos/observers/vcpe/vcpe_observer_config
index 6d58340..afd1501 100644
--- a/xos/observers/vcpe/vcpe_observer_config
+++ b/xos/observers/vcpe/vcpe_observer_config
@@ -33,6 +33,8 @@
 pretend=False
 backoff_disabled=True
 save_ansible_output=True
+proxy_ssh=True
+full_setup=True
 
 [feefie]
 client_id='vicci_dev_central'
diff --git a/xos/openstack_observer/ansible.py b/xos/openstack_observer/ansible.py
index 74af590..fad7610 100755
--- a/xos/openstack_observer/ansible.py
+++ b/xos/openstack_observer/ansible.py
@@ -109,7 +109,7 @@
             except:
                 # fail silently
                 pass
-        
+
     else:
         msg = open(fqp+'.out').read()
 
@@ -139,28 +139,40 @@
     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
 
     (opts, fqp) = get_playbook_fn(opts, path)
     private_key_pathname = fqp + ".key"
     config_pathname = fqp + ".config"
     hosts_pathname = fqp + ".hosts"
 
-    proxy_command = "ProxyCommand ssh -q -i %s -o StrictHostKeyChecking=no %s@%s" % (private_key_pathname, instance_id, hostname)
-
     f = open(private_key_pathname, "w")
     f.write(private_key)
     f.close()
 
     f = open(config_pathname, "w")
     f.write("[ssh_connection]\n")
-    f.write('ssh_args = -o "%s" -o StrictHostKeyChecking=no\n' % proxy_command)
+    if proxy_ssh:
+        proxy_command = "ProxyCommand ssh -q -i %s -o StrictHostKeyChecking=no %s@%s" % (private_key_pathname, instance_id, hostname)
+        f.write('ssh_args = -o "%s"\n' % proxy_command)
     f.write('scp_if_ssh = True\n')
     f.write('pipelining = True\n')
+    f.write('\n[defaults]\n')
+    f.write('host_key_checking = False\n')
     f.close()
 
     f = open(hosts_pathname, "w")
     f.write("[%s]\n" % instance_name)
-    f.write("%s ansible_ssh_private_key_file=%s\n" % (hostname, private_key_pathname))
+    if proxy_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
+        f.write("%s ansible_ssh_private_key_file=%s ansible_ssh_user=ubuntu\n" % (nat_ip, private_key_pathname))
     f.close()
 
     # SSH will complain if private key is world or group readable
diff --git a/xos/openstack_observer/steps/sync_controller_slices.py b/xos/openstack_observer/steps/sync_controller_slices.py
index 0eceb95..c456a2f 100644
--- a/xos/openstack_observer/steps/sync_controller_slices.py
+++ b/xos/openstack_observer/steps/sync_controller_slices.py
@@ -60,7 +60,7 @@
         if (not controller_slice.tenant_id):
             try:
                 driver = OpenStackDriver().admin_driver(controller=controller_slice.controller)
-                driver.shell.nova.quotas.update(tenant_id=controller_slice.tenant_id, instances=int(controller_slice.slice.max_instances))
+                driver.shell.nova.quotas.update(tenant_id=tenant_id, instances=int(controller_slice.slice.max_instances))
             except:
                 logger.log_exc('Could not update quota for %s'%controller_slice.slice.name)
                 raise Exception('Could not update quota for %s'%controller_slice.slice.name)
diff --git a/xos/openstack_observer/steps/sync_ports.py b/xos/openstack_observer/steps/sync_ports.py
index da08e36..15fd4a1 100644
--- a/xos/openstack_observer/steps/sync_ports.py
+++ b/xos/openstack_observer/steps/sync_ports.py
@@ -127,11 +127,13 @@
                 continue
 
             ip=port["fixed_ips"][0]["ip_address"]
+            mac=port["mac_address"]
             logger.info("creating Port (%s, %s, %s, %s)" % (str(network), str(instance), ip, str(port["id"])))
 
             ns = Port(network=network,
                                instance=instance,
                                ip=ip,
+                               mac=mac,
                                port_id=port["id"])
 
             try:
diff --git a/xos/scripts/opencloud b/xos/scripts/opencloud
index c4cb2d6..6705b20 100755
--- a/xos/scripts/opencloud
+++ b/xos/scripts/opencloud
@@ -146,6 +146,7 @@
     python ./manage.py makemigrations requestrouter
     python ./manage.py makemigrations syndicate_storage
     python ./manage.py makemigrations cord
+    python ./manage.py makemigrations ceilometer
     #python ./manage.py makemigrations servcomp
 }
 
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index 9ba9973..0a68c9d 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -13,6 +13,7 @@
                 type: boolean
                 default: false
                 description: do not allow Tosca to create this object)
+# Service
 define(xos_base_service_caps,
             scalable:
                 type: tosca.capabilities.Scalable
@@ -36,6 +37,27 @@
                 default: true
             public_key:
                 type: string
+                required: false
+            versionNumber:
+                type: string
+                required: false)
+# Subscriber
+define(xos_base_subscriber_caps,
+            subscriber:
+                type: tosca.capabilities.xos.Subscriber)
+define(xos_base_subscriber_props,
+            kind:
+                type: string
+                default: generic
+            service_specific_id:
+                type: string
+                required: false)
+define(xos_base_tenant_props,
+            kind:
+                type: string
+                default: generic
+            service_specific_id:
+                type: string
                 required: false)
 
 # end m4 macros
@@ -77,6 +99,47 @@
         properties:
             xos_base_service_props
 
+    tosca.nodes.Subscriber:
+        derived_from: tosca.nodes.Root
+        capabilities:
+            xos_base_subscriber_caps
+        properties:
+            xos_base_subscriber_props
+
+    tosca.nodes.CORDSubscriber:
+        derived_from: tosca.nodes.Root
+        capabilities:
+            xos_base_subscriber_caps
+        properties:
+            xos_base_subscriber_props
+            firewall_enable:
+                type: boolean
+                default: false
+            url_filter_enable:
+                type: boolean
+                default: false
+            url_filter_level:
+                type: string
+                default: PG
+            cdn_enable:
+                type: boolean
+                default: true
+
+    tosca.nodes.CORDUser:
+        derived_from: tosca.nodes.Root
+        properties:
+            level:
+                type: string
+                default: PG_13
+            mac:
+                type: string
+                required: true
+
+    tosca.nodes.VOLTTenant:
+        derived_from: tosca.nodes.Root
+        properties:
+            xos_base_tenant_props
+
     tosca.nodes.User:
         derived_from: tosca.nodes.Root
 
@@ -222,7 +285,7 @@
                 # In the data model, this is defaulted to false. However, to
                 # preserve Tosca semantics, we default it to true instead.
                 default: true
-          capabilities:

+          capabilities:
             link:

               type: tosca.capabilities.network.Linkable
 
@@ -346,6 +409,9 @@
         derived_from: tosca.relationships.Root
         valid_target_types: [ tosca.capabilities.xos.Network ]
 
+    tosca.relationships.ConnectsToSlice:
+        derived_from: tosca.relationships.Root
+
     #    tosca.relationships.OwnsNetwork:
     #        derived_from: tosca.relationships.Root
     #        valid_target_types: [ tosca.capabilities.xos.Network ]
@@ -370,6 +436,12 @@
         derived_from: tosca.relationships.Root
         valid_target_types: [ tosca.capabiltys.xos.Site ]
 
+    tosca.relationships.SubscriberDevice:
+        derived_from: tosca.relationships.Root
+
+    tosca.relationships.BelongsToSubscriber:
+        derived_from: tosca.relationships.Root
+
     tosca.capabilities.xos.Service:
         derived_from: tosca.capabilities.Root
         description: An XOS Service
@@ -401,3 +473,8 @@
     tosca.capabilities.xos.User:
         derived_from: tosca.capabilities.Root
         description: An XOS user
+
+    tosca.capabilities.xos.Subscriber:
+        derived_from: tosca.capabilities.Root
+        description: An XOS Subscriber
+
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 3a7e133..c59842f 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -5,6 +5,11 @@
 #    m4 macros do our inheritance
 
 
+# Service
+
+
+# Subscriber
+
 
 
 
@@ -21,12 +26,27 @@
             service:
                 type: tosca.capabilities.xos.Service
         properties:
+            kind:
+                type: string
+                default: generic
             view_url:
                 type: string
                 required: false
             icon_url:
                 type: string
                 required: false
+            enabled:
+                type: boolean
+                default: true
+            published:
+                type: boolean
+                default: true
+            public_key:
+                type: string
+                required: false
+            versionNumber:
+                type: string
+                required: false
 
     tosca.nodes.VCPEService:
         derived_from: tosca.nodes.Root
@@ -36,12 +56,27 @@
             service:
                 type: tosca.capabilities.xos.Service
         properties:
+            kind:
+                type: string
+                default: generic
             view_url:
                 type: string
                 required: false
             icon_url:
                 type: string
                 required: false
+            enabled:
+                type: boolean
+                default: true
+            published:
+                type: boolean
+                default: true
+            public_key:
+                type: string
+                required: false
+            versionNumber:
+                type: string
+                required: false
             backend_network_label:
                 type: string
                 required: false
@@ -54,12 +89,27 @@
             service:
                 type: tosca.capabilities.xos.Service
         properties:
+            kind:
+                type: string
+                default: generic
             view_url:
                 type: string
                 required: false
             icon_url:
                 type: string
                 required: false
+            enabled:
+                type: boolean
+                default: true
+            published:
+                type: boolean
+                default: true
+            public_key:
+                type: string
+                required: false
+            versionNumber:
+                type: string
+                required: false
             vbng_url:
                 type: string
                 required: false
@@ -72,12 +122,85 @@
             service:
                 type: tosca.capabilities.xos.Service
         properties:
+            kind:
+                type: string
+                default: generic
             view_url:
                 type: string
                 required: false
             icon_url:
                 type: string
                 required: false
+            enabled:
+                type: boolean
+                default: true
+            published:
+                type: boolean
+                default: true
+            public_key:
+                type: string
+                required: false
+            versionNumber:
+                type: string
+                required: false
+
+    tosca.nodes.Subscriber:
+        derived_from: tosca.nodes.Root
+        capabilities:
+            subscriber:
+                type: tosca.capabilities.xos.Subscriber
+        properties:
+            kind:
+                type: string
+                default: generic
+            service_specific_id:
+                type: string
+                required: false
+
+    tosca.nodes.CORDSubscriber:
+        derived_from: tosca.nodes.Root
+        capabilities:
+            subscriber:
+                type: tosca.capabilities.xos.Subscriber
+        properties:
+            kind:
+                type: string
+                default: generic
+            service_specific_id:
+                type: string
+                required: false
+            firewall_enable:
+                type: boolean
+                default: false
+            url_filter_enable:
+                type: boolean
+                default: false
+            url_filter_level:
+                type: string
+                default: PG
+            cdn_enable:
+                type: boolean
+                default: true
+
+    tosca.nodes.CORDUser:
+        derived_from: tosca.nodes.Root
+        properties:
+            level:
+                type: string
+                default: PG_13
+            mac:
+                type: string
+                required: true
+
+    tosca.nodes.VOLTTenant:
+        derived_from: tosca.nodes.Root
+        properties:
+            kind:
+                type: string
+                default: generic
+            service_specific_id:
+                type: string
+                required: false
 
     tosca.nodes.User:
         derived_from: tosca.nodes.Root
@@ -224,7 +347,7 @@
                 # In the data model, this is defaulted to false. However, to
                 # preserve Tosca semantics, we default it to true instead.
                 default: true
-          capabilities:

+          capabilities:
             link:

               type: tosca.capabilities.network.Linkable
 
@@ -348,6 +471,9 @@
         derived_from: tosca.relationships.Root
         valid_target_types: [ tosca.capabilities.xos.Network ]
 
+    tosca.relationships.ConnectsToSlice:
+        derived_from: tosca.relationships.Root
+
     #    tosca.relationships.OwnsNetwork:
     #        derived_from: tosca.relationships.Root
     #        valid_target_types: [ tosca.capabilities.xos.Network ]
@@ -372,6 +498,12 @@
         derived_from: tosca.relationships.Root
         valid_target_types: [ tosca.capabiltys.xos.Site ]
 
+    tosca.relationships.SubscriberDevice:
+        derived_from: tosca.relationships.Root
+
+    tosca.relationships.BelongsToSubscriber:
+        derived_from: tosca.relationships.Root
+
     tosca.capabilities.xos.Service:
         derived_from: tosca.capabilities.Root
         description: An XOS Service
@@ -403,3 +535,8 @@
     tosca.capabilities.xos.User:
         derived_from: tosca.capabilities.Root
         description: An XOS user
+
+    tosca.capabilities.xos.Subscriber:
+        derived_from: tosca.capabilities.Root
+        description: An XOS Subscriber
+
diff --git a/xos/tosca/engine.py b/xos/tosca/engine.py
index 3efb5ef..efce829 100644
--- a/xos/tosca/engine.py
+++ b/xos/tosca/engine.py
@@ -44,7 +44,7 @@
 
         self.ordered_nodetemplates = []
         self.ordered_names = self.topsort_dependencies()
-        print "ordered_names", self.ordered_names
+        self.log("ordered_names: %s" % self.ordered_names)
         for name in self.ordered_names:
             if name in self.nodetemplates_by_name:
                 self.ordered_nodetemplates.append(self.nodetemplates_by_name[name])
diff --git a/xos/tosca/flavorselect.py b/xos/tosca/flavorselect.py
index a79f8a5..cab8e56 100644
--- a/xos/tosca/flavorselect.py
+++ b/xos/tosca/flavorselect.py
@@ -18,7 +18,11 @@
         return int(s)
 
     def get_mb(self, s):
-        return self.get_gb(s) * 1024
+        if "GB" in s:
+            return int(s.split("GB")[0].strip())*1024
+        if "MB" in s:
+            return int(s.split("MB")[0].strip())
+        return int(s)
 
     def get_flavor(self):
         flavor = "m1.tiny"
diff --git a/xos/tosca/resources/CORDSubscriber.py b/xos/tosca/resources/CORDSubscriber.py
new file mode 100644
index 0000000..45f5ee3
--- /dev/null
+++ b/xos/tosca/resources/CORDSubscriber.py
@@ -0,0 +1,24 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+import pdb
+
+from core.models import User
+from cord.models import CordSubscriberRoot
+
+from xosresource import XOSResource
+
+class XOSCORDSubscriber(XOSResource):
+    provides = "tosca.nodes.CORDSubscriber"
+    xos_model = CordSubscriberRoot
+    copyin_props = ["service_specific_id", "firewall_enable", "url_filter_enable", "cdn_enable", "url_filter_level"]
+
+    def postprocess(self, obj):
+        pass
+
+    def can_delete(self, obj):
+        return super(XOSCORDSubscriber, self).can_delete(obj)
+
diff --git a/xos/tosca/resources/CORDUser.py b/xos/tosca/resources/CORDUser.py
new file mode 100644
index 0000000..78883f2
--- /dev/null
+++ b/xos/tosca/resources/CORDUser.py
@@ -0,0 +1,63 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+import pdb
+
+from core.models import User
+from cord.models import CordSubscriberRoot
+
+from xosresource import XOSResource
+
+class XOSCORDUser(XOSResource):
+    provides = "tosca.nodes.CORDUser"
+
+    def get_model_class_name(self):
+        return "CORDUser"
+
+    def get_subscriber_root(self, throw_exception=True):
+        sub_name = self.get_requirement("tosca.relationships.SubscriberDevice", throw_exception=throw_exception)
+        sub = self.get_xos_object(CordSubscriberRoot, name=sub_name, throw_exception=throw_exception)
+        return sub
+
+    def get_existing_objs(self):
+        result = []
+        sub = self.get_subscriber_root(throw_exception=False)
+        if not sub:
+           return []
+        for user in sub.users:
+            if user["name"] == self.nodetemplate.name:
+                result.append(user)
+        return result
+
+    def get_xos_args(self):
+        args = {"name": self.nodetemplate.name,
+                "level": self.get_property("level"),
+                "mac": self.get_property("mac")}
+        return args
+
+
+    def create(self):
+        xos_args = self.get_xos_args()
+        sub = self.get_subscriber_root()
+
+        sub.create_user(**xos_args)
+        sub.save()
+
+        self.info("Created CORDUser %s for Subscriber %s" % (self.nodetemplate.name, sub.name))
+
+    def update(self, obj):
+        pass
+
+    def delete(self, obj):
+        if (self.can_delete(obj)):
+            self.info("destroying CORDUser %s" % obj["name"])
+            sub = self.get_subscriber_root()
+            sub.delete_user(obj["id"])
+            sub.save()
+
+    def can_delete(self, obj):
+        return True
+
diff --git a/xos/tosca/resources/VOLTTenant.py b/xos/tosca/resources/VOLTTenant.py
new file mode 100644
index 0000000..611bf23
--- /dev/null
+++ b/xos/tosca/resources/VOLTTenant.py
@@ -0,0 +1,46 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+import pdb
+
+from core.models import User
+from cord.models import VOLTTenant, VOLTService, CordSubscriberRoot
+
+from xosresource import XOSResource
+
+class XOSVOLTTenant(XOSResource):
+    provides = "tosca.nodes.VOLTTenant"
+    xos_model = VOLTTenant
+    copyin_props = ["service_specific_id"]
+    name_field = None
+
+    def get_xos_args(self, throw_exception=True):
+        args = super(XOSVOLTTenant, self).get_xos_args()
+
+        provider_name = self.get_requirement("tosca.relationships.MemberOfService", throw_exception=throw_exception)
+        if provider_name:
+            args["provider_service"] = self.get_xos_object(VOLTService, throw_exception=throw_exception, name=provider_name)
+
+        subscriber_name = self.get_requirement("tosca.relationships.BelongsToSubscriber")
+        if subscriber_name:
+            args["subscriber_root"] = self.get_xos_object(CordSubscriberRoot, throw_exception=throw_exception, name=subscriber_name)
+
+        return args
+
+    def get_existing_objs(self):
+        args = self.get_xos_args(throw_exception=False)
+        provider_service = args.get("provider", None)
+        service_specific_id = args.get("service_specific_id", None)
+        if (provider_service) and (service_specific_id):
+            return [ self.get_xos_object(provider_service=provider_service, service_specific_id=service_specific_id) ]
+        return []
+
+    def postprocess(self, obj):
+        pass
+
+    def can_delete(self, obj):
+        return super(XOSVOLTTenant, self).can_delete(obj)
+
diff --git a/xos/tosca/resources/network.py b/xos/tosca/resources/network.py
index e5b8b80..f483b6c 100644
--- a/xos/tosca/resources/network.py
+++ b/xos/tosca/resources/network.py
@@ -6,17 +6,19 @@
 from translator.toscalib.tosca_template import ToscaTemplate
 import pdb
 
-from core.models import Slice,User,Network,NetworkTemplate
+from core.models import Slice,User,Network,NetworkTemplate,NetworkSlice
 
 from xosresource import XOSResource
 
 class XOSNetwork(XOSResource):
     provides = ["tosca.nodes.network.Network", "tosca.nodes.network.Network.XOS"]
     xos_model = Network
+    copyin_props = ["ports", "labels"]
 
     def get_xos_args(self):
-        args = {"name": self.nodetemplate.name,
-                "autoconnect": False,}
+        args = super(XOSNetwork, self).get_xos_args()
+
+        args["autoconnect"] = False
 
         slice_name = self.get_requirement("tosca.relationships.MemberOfSlice")
         if slice_name:
@@ -40,7 +42,13 @@
         return args
 
     def postprocess(self, obj):
-        pass
+        for sliceName in self.get_requirements("tosca.relationships.ConnectsToSlice"):
+            slice = self.get_xos_object(Slice, name=sliceName)
+            netSlices = NetworkSlice.objects.filter(network=obj, slice = slice)
+            if not netSlices:
+                self.info("Attached Network %s to Slice %s" % (obj, slice))
+                ns = NetworkSlice(network = obj, slice=slice)
+                ns.save()
 
 #        v = self.get_property("permitted_slices")
 #        if v:
diff --git a/xos/tosca/resources/service.py b/xos/tosca/resources/service.py
index 4b32b8a..884c6db 100644
--- a/xos/tosca/resources/service.py
+++ b/xos/tosca/resources/service.py
@@ -13,7 +13,7 @@
 class XOSService(XOSResource):
     provides = "tosca.nodes.Service"
     xos_model = Service
-    copyin_props = ["view_url", "kind", "enabled", "published", "public_key"]
+    copyin_props = ["view_url", "icon_url", "kind", "enabled", "published", "public_key", "versionNumber"]
 
     def postprocess(self, obj):
         for provider_service_name in self.get_requirements("tosca.relationships.TenantOfService"):
@@ -21,7 +21,7 @@
 
             existing_tenancy = CoarseTenant.get_tenant_objects().filter(provider_service = provider_service, subscriber_service = obj)
             if existing_tenancy:
-                self.info("Tenancy relationship from %s to %s already exists" % (str(service), str(provider_service)))
+                self.info("Tenancy relationship from %s to %s already exists" % (str(obj), str(provider_service)))
             else:
                 tenancy = CoarseTenant(provider_service = provider_service,
                                        subscriber_service = obj)
diff --git a/xos/tosca/resources/subscriber.py b/xos/tosca/resources/subscriber.py
new file mode 100644
index 0000000..5ec0462
--- /dev/null
+++ b/xos/tosca/resources/subscriber.py
@@ -0,0 +1,23 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+import pdb
+
+from core.models import Subscriber,User
+
+from xosresource import XOSResource
+
+class XOSSubscriber(XOSResource):
+    provides = "tosca.nodes.Subscriber"
+    xos_model = Subscriber
+    copyin_props = ["service_specific_id"]
+
+    def postprocess(self, obj):
+        pass
+
+    def can_delete(self, obj):
+        return super(XOSService, self).can_delete(obj)
+
diff --git a/xos/tosca/resources/user.py b/xos/tosca/resources/user.py
index 125df4f..53323c5 100644
--- a/xos/tosca/resources/user.py
+++ b/xos/tosca/resources/user.py
@@ -12,15 +12,11 @@
 class XOSUser(XOSResource):
     provides = "tosca.nodes.User"
     xos_model = User
+    name_field = "email"
+    copyin_props = ["password", "firstname", "lastname", "phone", "user_url", "public_key", "is_active", "is_admin", "login_page"]
 
     def get_xos_args(self):
-        args = {"email": self.nodetemplate.name}
-
-        # copy simple string properties from the template into the arguments
-        for prop in ["password", "firstname", "lastname", "phone", "user_url", "public_key", "is_active", "is_admin", "login_page"]:
-            v = self.get_property(prop)
-            if v:
-                args[prop] = v
+        args = super(XOSUser, self).get_xos_args()
 
         site_name = self.get_requirement("tosca.relationships.MemberOfSite")
         if site_name:
diff --git a/xos/tosca/resources/vbngservice.py b/xos/tosca/resources/vbngservice.py
index 70f47aa..1fc8d25 100644
--- a/xos/tosca/resources/vbngservice.py
+++ b/xos/tosca/resources/vbngservice.py
@@ -12,5 +12,5 @@
 class XOSVBGNService(XOSService):
     provides = "tosca.nodes.VBNGService"
     xos_model = VBNGService
-    copyin_props = ["view_url", "icon_url", "vnbg_url"]
+    copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "versionNumber", "vbng_url"]
 
diff --git a/xos/tosca/resources/vcpeservice.py b/xos/tosca/resources/vcpeservice.py
index e163520..6cc7390 100644
--- a/xos/tosca/resources/vcpeservice.py
+++ b/xos/tosca/resources/vcpeservice.py
@@ -12,5 +12,5 @@
 class XOSVcpeService(XOSService):
     provides = "tosca.nodes.VCPEService"
     xos_model = VCPEService
-    copyin_props = ["view_url", "icon_url", "backend_network_label", "enabled", "published", "public_key"]
+    copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "versionNumber", "backend_network_label"]
 
diff --git a/xos/tosca/resources/xosresource.py b/xos/tosca/resources/xosresource.py
index 159156b..ef59485 100644
--- a/xos/tosca/resources/xosresource.py
+++ b/xos/tosca/resources/xosresource.py
@@ -71,10 +71,13 @@
     def get_xos_args(self):
         return {}
 
+    def get_model_class_name(self):
+        return self.xos_model.__name__
+
     def create_or_update(self):
         existing_objs = self.get_existing_objs()
         if existing_objs:
-            self.info("%s %s already exists" % (self.xos_model.__name__, self.nodetemplate.name))
+            self.info("%s %s already exists" % (self.get_model_class_name(), self.nodetemplate.name))
             self.update(existing_objs[0])
         else:
             self.create()
@@ -128,7 +131,7 @@
 
             v = self.try_intrinsic_function(v)
 
-            if v:
+            if v is not None:
                 args[prop] = v
 
         return args
@@ -144,7 +147,11 @@
         self.postprocess(xos_obj)
 
     def update(self, obj):
-        pass
+        xos_args = self.get_xos_args()
+        for (k,v) in xos_args.items():
+            setattr(obj, k, v)
+        self.postprocess(obj)
+        obj.save()
 
     def delete(self, obj):
         if (self.can_delete(obj)):
diff --git a/xos/tosca/samples/ceilometer.yaml b/xos/tosca/samples/ceilometer.yaml
new file mode 100644
index 0000000..aeb4acb
--- /dev/null
+++ b/xos/tosca/samples/ceilometer.yaml
@@ -0,0 +1,37 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup CORD-related services -- vOLT, vCPE, vBNG.
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    service_ceilometer:
+      type: tosca.nodes.Service
+      requirements:
+      properties:
+          view_url: /admin/ceilometer/ceilometerservice/$id$/
+          kind: ceilometer
+#          public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+#      artifacts:
+#          pubkey: /opt/xos/observers/vcpe/vcpe_public_key
+
+
+    Private:
+      type: tosca.nodes.NetworkTemplate
+
+    mysite:
+      type: tosca.nodes.Site
+
+    mysite_ceilometer:
+      description: Ceilometer Proxy Slice
+      type: tosca.nodes.Slice
+      requirements:
+          - ceilometer_service:
+              node: service_ceilometer
+              relationship: tosca.relationships.MemberOfService
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
diff --git a/xos/tosca/samples/cord-cloudlab.yaml b/xos/tosca/samples/cord-cloudlab.yaml
new file mode 100644
index 0000000..20e8057
--- /dev/null
+++ b/xos/tosca/samples/cord-cloudlab.yaml
@@ -0,0 +1,101 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup CORD-related services -- vOLT, vCPE, vBNG.
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    # CORD Services
+    service_volt:
+      type: tosca.nodes.Service
+      requirements:
+          - vcpe_tenant:
+              node: service_vcpe
+              relationship: tosca.relationships.TenantOfService
+      properties:
+          view_url: /admin/cord/voltservice/$id$/
+          kind: vOLT
+
+    service_vcpe:
+      type: tosca.nodes.VCPEService
+      requirements:
+          - vbng_tenant:
+              node: service_vbng
+              relationship: tosca.relationships.TenantOfService
+      properties:
+          view_url: /admin/cord/vcpeservice/$id$/
+          backend_network_label: hpc_client
+          public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+      artifacts:
+          pubkey: /opt/xos/observers/vcpe/vcpe_public_key
+
+    service_vbng:
+      type: tosca.nodes.VBNGService
+      properties:
+          view_url: /admin/cord/vbngservice/$id$/
+          vbng_url: http://10.0.3.136:8181/onos/virtualbng/
+
+    mysite:
+      type: tosca.nodes.Site
+
+    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
+
+    mysite_vcpe:
+      description: vCPE Controller Slice
+      type: tosca.nodes.Slice
+      requirements:
+          - vcpe_service:
+              node: service_vcpe
+              relationship: tosca.relationships.MemberOfService
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
diff --git a/xos/tosca/samples/cord.yaml b/xos/tosca/samples/cord.yaml
index 885435d..eaa536a 100644
--- a/xos/tosca/samples/cord.yaml
+++ b/xos/tosca/samples/cord.yaml
@@ -18,6 +18,25 @@
           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
+
     service_vcpe:
       type: tosca.nodes.VCPEService
       requirements:
@@ -51,4 +70,68 @@
               node: mysite
               relationship: tosca.relationships.MemberOfSite
 
+    # Now let's add a subscriber
+
+    My House:
+       type: tosca.nodes.CORDSubscriber
+       properties:
+           service_specific_id: 1234
+           firewall_enable: true
+           cdn_enable: true
+           url_filter_enable: true
+           url_filter_level: R
+
+    Mom's PC:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 010203040506
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    Dad's PC:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 90E2Ba82F975
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    Jack's Laptop:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 685B359D91D5
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    Jill's Laptop:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 34363BC9B6A6
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    My Volt:
+        type: tosca.nodes.VOLTTenant
+        properties:
+            service_specific_id: 1234
+        requirements:
+            - provider_service:
+                node: service_volt
+                relationship: tosca.relationships.MemberOfService
+            - subscriber:
+                node: My House
+                relationship: tosca.relationships.BelongsToSubscriber
+
+
 
diff --git a/xos/tosca/tests/alltests.py b/xos/tosca/tests/alltests.py
new file mode 100644
index 0000000..59ad49d
--- /dev/null
+++ b/xos/tosca/tests/alltests.py
@@ -0,0 +1,14 @@
+from coarsetenancytest import CoarseTenancyTest
+from porttest import PortTest
+from networktest import NetworkTest
+from servicetest import ServiceTest
+from usertest import UserTest
+from computetest import ComputeTest
+
+if __name__ == "__main__":
+    NetworkTest()
+    PortTest()
+    CoarseTenancyTest()
+    ServiceTest()
+    UserTest()
+    ComputeTest()
diff --git a/xos/tosca/tests/basetest.py b/xos/tosca/tests/basetest.py
new file mode 100644
index 0000000..f88a63b
--- /dev/null
+++ b/xos/tosca/tests/basetest.py
@@ -0,0 +1,130 @@
+import os
+import random
+import string
+import sys

+

+# add the parent parent directory to sys.path

+# XXX this is very hackish :(

+import os,sys,inspect

+currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))

+parentdir = os.path.dirname(currentdir)

+sys.path.append(parentdir)

+parentparentdir = os.path.dirname(parentdir)

+sys.path.append(parentparentdir)

+

+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")

+import django

+django.setup()
+
+from tosca.engine import XOSTosca
+from core.models import User
+
+class BaseToscaTest(object):
+    username = "padmin@vicci.org"
+    base_yaml = \
+"""tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: tosca test case
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+"""
+
+    def __init__(self):
+        self.runtest()
+
+    def make_nodetemplate(self, name, type, props={}, reqs=[], caps={}, artifacts={}):
+        yml = "    %s:\n      type: %s\n"  % (name, type)
+        if props:
+            yml = yml + "      properties:\n"
+            for (k,v) in props.items():
+                yml = yml + "          %s: %s\n" % (k, v)
+
+        if reqs:
+            yml = yml + "      requirements:\n"
+            i=0
+            for (name,relat) in reqs:
+                yml = yml + "        - req%d:\n" % i
+                yml = yml + "              node: %s\n" % name
+                yml = yml + "              relationship: %s\n" % relat
+                i = i + 1
+
+        if caps:
+            yml = yml + "      capabilities:\n"
+            for (cap,capdict) in caps.items():
+               yml = yml + "        %s:\n" % cap
+               yml = yml + "          properties:\n"
+               for (k,v) in capdict.items():
+                   yml = yml + "            %s: %s\n" % (k,v)
+
+        if artifacts:
+            yml = yml + "      artifacts:\n"
+            for (k,v) in artifacts.items():
+                yml = yml + "        %s: %s\n" % (k,v)
+
+        return yml
+
+    def make_compute(self, slice, name, caps={}, props={}, reqs=[], num_cpus="1", disk_size="10 GB", mem_size="4 MB"):
+        reqs = reqs[:]
+        caps = caps.copy()
+
+        caps.update( {"host": {"num_cpus": num_cpus, "disk_size": disk_size, "mem_size": mem_size},
+                      "os": {"architecture": "x86_64", "type": "linux", "distribution": "rhel", "version": "6.5"}} )
+        reqs.append( (slice, "tosca.relationships.MemberOfSlice") )
+
+        return self.make_nodetemplate(name, "tosca.nodes.Compute",
+                                      caps= caps,
+                                      props = props,
+                                      reqs= reqs)
+
+    def make_random_string(self,desired_len):
+        return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(desired_len))
+
+    def assert_noobj(self, cls, name):
+        objs = cls.objects.filter(name=name)
+        assert(not objs)
+
+    def assert_obj(self, cls, name, **kwargs):
+        obj = cls.objects.get(name=name)
+        assert(obj)
+        for (k,v) in kwargs.items():
+            if (getattr(obj,k,None) != v):
+                print "Object %s property '%s' is '%s' and should be '%s'" % (obj, k, getattr(obj,k,None), v)
+                assert(False)
+        return obj
+
+    def try_to_delete(self, cls, **kwargs):
+        objs = cls.objects.filter(**kwargs)
+        for obj in objs:
+            obj.delete(purge=True)
+
+    def execute(self, yml):
+        u = User.objects.get(email=self.username)
+
+        #print self.base_yaml+yml
+
+        xt = XOSTosca(self.base_yaml+yml, parent_dir=parentdir, log_to_console=False)
+        xt.execute(u)
+
+    def destroy(self, yml):
+        u = User.objects.get(email=self.username)
+
+        #print self.base_yaml+yml
+
+        xt = XOSTosca(self.base_yaml+yml, parent_dir=parentdir, log_to_console=False)
+        xt.destroy(u)
+
+    def runtest(self):
+        for test in self.tests:
+            print "running", test
+            self.cleanup()
+            try:
+                getattr(self,test)()
+            finally:
+                self.cleanup()
+
+    def cleanup(self):
+        pass
diff --git a/xos/tosca/tests/coarsetenancytest.py b/xos/tosca/tests/coarsetenancytest.py
new file mode 100644
index 0000000..3da288f
--- /dev/null
+++ b/xos/tosca/tests/coarsetenancytest.py
@@ -0,0 +1,49 @@
+from basetest import BaseToscaTest
+
+from core.models import Service, CoarseTenant
+
+class CoarseTenancyTest(BaseToscaTest):
+    tests = ["create_coarsetenant",
+             "update_coarsetenant"]
+
+    def cleanup(self):
+        self.try_to_delete(Service, name="test_svc1")
+        self.try_to_delete(Service, name="test_svc2")
+
+    def create_coarsetenant(self):
+        self.assert_noobj(Service, "test_svc1")
+        self.assert_noobj(Service, "test_svc2")
+        self.execute(self.make_nodetemplate("test_svc1", "tosca.nodes.Service", reqs=[("test_svc2", "tosca.relationships.TenantOfService")]) +
+                     self.make_nodetemplate("test_svc2", "tosca.nodes.Service"))
+        svc1 = self.assert_obj(Service, "test_svc1", kind="generic", published=True, enabled=True)
+        svc2 = self.assert_obj(Service, "test_svc2", kind="generic", published=True, enabled=True)
+
+        ct = CoarseTenant.objects.filter(provider_service=svc2, subscriber_service=svc1)
+        assert(ct)
+
+    def update_coarsetenant(self):
+        # first make the services without the coarse tenancy relationship
+        self.assert_noobj(Service, "test_svc1")
+        self.assert_noobj(Service, "test_svc2")
+        self.execute(self.make_nodetemplate("test_svc1", "tosca.nodes.Service") +
+                     self.make_nodetemplate("test_svc2", "tosca.nodes.Service"))
+        svc1 = self.assert_obj(Service, "test_svc1", kind="generic", published=True, enabled=True)
+        svc2 = self.assert_obj(Service, "test_svc2", kind="generic", published=True, enabled=True)
+        ct = CoarseTenant.objects.filter(provider_service=svc2, subscriber_service=svc1)
+        assert(not ct)
+
+        # now add the relationship
+        self.execute(self.make_nodetemplate("test_svc1", "tosca.nodes.Service", reqs=[("test_svc2", "tosca.relationships.TenantOfService")])+
+                                            self.make_nodetemplate("test_svc2", "tosca.nodes.Service"))
+        updated_svc1 = self.assert_obj(Service, "test_svc1", kind="generic", published=True, enabled=True)
+
+        assert(svc1.id == updated_svc1.id)
+
+        ct = CoarseTenant.objects.filter(provider_service=svc2, subscriber_service=svc1)
+        assert(ct)
+
+
+if __name__ == "__main__":
+    CoarseTenancyTest()
+
+
diff --git a/xos/tosca/tests/computetest.py b/xos/tosca/tests/computetest.py
new file mode 100644
index 0000000..43a5143
--- /dev/null
+++ b/xos/tosca/tests/computetest.py
@@ -0,0 +1,96 @@
+from basetest import BaseToscaTest
+
+from core.models import Sliver, Slice
+
+class ComputeTest(BaseToscaTest):
+    tests = [ # "create_compute_m1_tiny", XXX m1.tiny does not exist on cloudlab
+             "create_compute_m1_small",
+             "create_compute_m1_large_8192MB",
+             "create_compute_m1_large_8GB",
+             "destroy_compute",
+             "create_compute_scalable",
+             "destroy_compute_scalable",
+                           ]
+
+    def cleanup(self):
+        self.try_to_delete(Sliver, name="test_compute1")
+        self.try_to_delete(Sliver, name="test_compute1-0")
+        self.try_to_delete(Sliver, name="test_compute1-1")
+        self.try_to_delete(Sliver, name="test_compute1-2")
+        self.try_to_delete(Sliver, name="test_compute1-3")
+        self.try_to_delete(Slice, name="testsite_slice1")
+
+    def get_base_templates(self):
+        return self.make_nodetemplate("testsite", "tosca.nodes.Site") + \
+               self.make_nodetemplate("testsite_slice1", "tosca.nodes.Slice", reqs=[("testsite", "tosca.relationships.MemberOfSite")])
+
+    def create_compute_m1_tiny(self):
+        self.assert_noobj(Sliver, "test_compute1")
+        self.execute(self.get_base_templates() +
+                     self.make_compute("testsite_slice1", "test_compute1", disk_size="1 GB", mem_size="500 MB"))
+        sliver = self.assert_obj(Sliver, "test_compute1")
+        assert(sliver.flavor.name == "m1.tiny")
+
+    def create_compute_m1_small(self):
+        self.assert_noobj(Sliver, "test_compute1")
+        self.execute(self.get_base_templates() +
+                     self.make_compute("testsite_slice1", "test_compute1", disk_size="1 GB", mem_size="513 MB"))
+        sliver = self.assert_obj(Sliver, "test_compute1")
+        assert(sliver.flavor.name == "m1.small")
+
+    def create_compute_m1_large_8192MB(self):
+        self.assert_noobj(Sliver, "test_compute1")
+        self.execute(self.get_base_templates() +
+                     self.make_compute("testsite_slice1", "test_compute1", mem_size="8192 MB"))
+        sliver = self.assert_obj(Sliver, "test_compute1")
+        assert(sliver.flavor.name == "m1.large")
+
+    def create_compute_m1_large_8GB(self):
+        self.assert_noobj(Sliver, "test_compute1")
+        self.execute(self.get_base_templates() +
+                     self.make_compute("testsite_slice1", "test_compute1", mem_size="8 GB"))
+        sliver = self.assert_obj(Sliver, "test_compute1")
+        assert(sliver.flavor.name == "m1.large")
+
+    def destroy_compute(self):
+        self.execute(self.get_base_templates() +
+                     self.make_compute("testsite_slice1", "test_compute1"))
+        self.assert_obj(Sliver, "test_compute1")
+        self.destroy(self.get_base_templates() +
+                     self.make_compute("testsite_slice1", "test_compute1"))
+        self.assert_noobj(Sliver, "test_compute1")
+
+    def create_compute_scalable(self):
+        self.assert_noobj(Sliver, "test_compute1-1")
+        self.assert_noobj(Sliver, "test_compute1-2")
+        self.assert_noobj(Sliver, "test_compute1-3")
+        self.execute(self.get_base_templates() +
+                     self.make_compute("testsite_slice1", "test_compute1", mem_size="8 GB",
+                                       caps={"scalable": {"min_instances": 2, "max_instances": 3, "default_instances": 2}}))
+        # there should be two instances
+        sliver0 = self.assert_obj(Sliver, "test_compute1-0")
+        sliver1 = self.assert_obj(Sliver, "test_compute1-1")
+        self.assert_noobj(Sliver, "test_compute1-2")
+
+    def destroy_compute_scalable(self):
+        self.assert_noobj(Sliver, "test_compute1-1")
+        self.assert_noobj(Sliver, "test_compute1-2")
+        self.assert_noobj(Sliver, "test_compute1-3")
+        self.execute(self.get_base_templates() +
+                     self.make_compute("testsite_slice1", "test_compute1", mem_size="8 GB",
+                                       caps={"scalable": {"min_instances": 2, "max_instances": 3, "default_instances": 2}}))
+        # there should be two instances
+        sliver0 = self.assert_obj(Sliver, "test_compute1-0")
+        sliver1 = self.assert_obj(Sliver, "test_compute1-1")
+
+        self.destroy(self.get_base_templates() +
+                     self.make_compute("testsite_slice1", "test_compute1", mem_size="8 GB",
+                                       caps={"scalable": {"min_instances": 2, "max_instances": 3, "default_instances": 2}}))
+
+        self.assert_noobj(Sliver, "test_compute1-0")
+        self.assert_noobj(Sliver, "test_compute1-1")
+
+if __name__ == "__main__":
+    ComputeTest()
+
+
diff --git a/xos/tosca/tests/networktest.py b/xos/tosca/tests/networktest.py
new file mode 100644
index 0000000..fa446f9
--- /dev/null
+++ b/xos/tosca/tests/networktest.py
@@ -0,0 +1,151 @@
+from basetest import BaseToscaTest
+
+from core.models import Network, Slice, NetworkTemplate, NetworkSlice
+
+class NetworkTest(BaseToscaTest):
+    tests = ["create_network_minimal",
+             "create_network_maximal",
+             "create_network_connected",
+             "create_network_connected_two_slices",
+             "update_network_labels",
+             "destroy_network"]
+
+    def cleanup(self):
+        self.try_to_delete(Network, name="test_net")
+        self.try_to_delete(Slice, name="testsite_slice1")
+        self.try_to_delete(Slice, name="testsite_slice2")
+
+    @property
+    def slice1(self):
+        return Slice.objects.get(name="testsite_slice1")
+
+    @property
+    def slice2(self):
+        return Slice.objects.get(name="testsite_slice2")
+
+    @property
+    def private(Self):
+        return NetworkTemplate.objects.get(name="Private")
+
+
+    def get_base_templates(self):
+        return self.make_nodetemplate("testsite", "tosca.nodes.Site") + \
+               self.make_nodetemplate("testsite_slice1", "tosca.nodes.Slice", reqs=[("testsite", "tosca.relationships.MemberOfSite")]) + \
+               self.make_nodetemplate("testsite_slice2", "tosca.nodes.Slice", reqs=[("testsite", "tosca.relationships.MemberOfSite")]) + \
+               self.make_nodetemplate("Private", "tosca.nodes.NetworkTemplate")
+
+    def create_network_minimal(self):
+        self.assert_noobj(Network, "test_net")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+        net=self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private)
+
+        ns = NetworkSlice.objects.filter(slice=self.slice1, network=net)
+        assert(not ns)
+
+    def create_network_maximal(self):
+        self.assert_noobj(Network, "test_net")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network.XOS",
+                                            props={"ports": "tcp/1234, udp/5678",
+                                                   "labels": "foo,bar",
+                                                   "permit_all_slices": False},
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+        net=self.assert_obj(Network, "test_net",
+                            owner=self.slice1,
+                            template=self.private,
+                            ports="tcp/1234, udp/5678",
+                            labels="foo,bar",
+                            permit_all_slices=False)
+
+        ns = NetworkSlice.objects.filter(slice=self.slice1, network=net)
+        assert(not ns)
+
+    def create_network_connected(self):
+        self.assert_noobj(Network, "test_net")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate"),
+                                                  ("testsite_slice1", "tosca.relationships.ConnectsToSlice")]))
+
+        net=self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private)
+
+        ns = NetworkSlice.objects.filter(slice=self.slice1, network=net)
+        assert(ns)
+
+    def create_network_connected_two_slices(self):
+        self.assert_noobj(Network, "test_net")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate"),
+                                                  ("testsite_slice1", "tosca.relationships.ConnectsToSlice"),
+                                                  ("testsite_slice2", "tosca.relationships.ConnectsToSlice")]))
+
+        net=self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private)
+
+        ns = NetworkSlice.objects.filter(slice=self.slice1, network=net)
+        assert(ns)
+
+        ns = NetworkSlice.objects.filter(slice=self.slice1, network=net)
+        assert(ns)
+
+    def update_network_labels(self):
+        self.assert_noobj(Network, "test_net")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network.XOS",
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+        net=self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private, labels=None)
+
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network.XOS",
+                                            props={"labels": "testlabel"},
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+
+        updated_net = self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private, labels="testlabel")
+
+        assert(net.id == updated_net.id)
+
+    def update_network_ports(self):
+        self.assert_noobj(Network, "test_net")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network.XOS",
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+        net=self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private, labels=None, ports=None)
+
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network.XOS",
+                                            props={"port": "tcp/2222, udp/3333"},
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+
+        updated_net = self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private, labels=None, ports="tcp/2222, udp/3333")
+
+        assert(net.id == updated_net.id)
+
+    def destroy_network(self):
+        self.assert_noobj(Network, "test_net")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+        net=self.assert_obj(Network, "test_net", owner=self.slice1, template=self.private)
+
+        self.destroy(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+
+        self.assert_noobj(Network, "test_net")
+
+if __name__ == "__main__":
+    NetworkTest()
+
+
diff --git a/xos/tosca/tests/porttest.py b/xos/tosca/tests/porttest.py
new file mode 100644
index 0000000..a0d23e3
--- /dev/null
+++ b/xos/tosca/tests/porttest.py
@@ -0,0 +1,162 @@
+from basetest import BaseToscaTest
+
+from core.models import Network, Slice, NetworkTemplate, NetworkSlice, Port, Sliver
+
+class PortTest(BaseToscaTest):
+    tests = ["create_port_minimal",
+             "create_two_ports",
+             "create_four_ports",
+             "add_port_after_network"]
+
+    def cleanup(self):
+        self.try_to_delete(Sliver, name="test_compute1")
+        self.try_to_delete(Sliver, name="test_compute2")
+        self.try_to_delete(Network, name="test_net")
+        self.try_to_delete(Slice, name="testsite_slice1")
+        self.try_to_delete(Slice, name="testsite_slice2")
+
+    @property
+    def slice1(self):
+        return Slice.objects.get(name="testsite_slice1")
+
+    @property
+    def slice2(self):
+        return Slice.objects.get(name="testsite_slice2")
+
+    @property
+    def private(self):
+        return NetworkTemplate.objects.get(name="Private")
+
+    @property
+    def test_slice1_1(self):
+        return Sliver.objects.get(name="test_slice1-1")
+
+    @property
+    def test_slice1_2(self):
+        return Sliver.objects.get(name="test_slice1-2")
+
+    @property
+    def test_slice2_1(self):
+        return Sliver.objects.get(name="test_slice2-1")
+
+    @property
+    def test_slice2_2(self):
+        return Sliver.objects.get(name="test_slice2-2")
+
+    def get_base_templates(self):
+        return self.make_nodetemplate("testsite", "tosca.nodes.Site") + \
+               self.make_nodetemplate("testsite_slice1", "tosca.nodes.Slice", reqs=[("testsite", "tosca.relationships.MemberOfSite")]) + \
+               self.make_nodetemplate("testsite_slice2", "tosca.nodes.Slice", reqs=[("testsite", "tosca.relationships.MemberOfSite")]) + \
+               self.make_nodetemplate("Private", "tosca.nodes.NetworkTemplate") + \
+               self.make_compute("testsite_slice1", "test_slice1-1") + \
+               self.make_compute("testsite_slice1", "test_slice1-2") +\
+               self.make_compute("testsite_slice2", "test_slice2-1") + \
+               self.make_compute("testsite_slice2", "test_slice2-2")
+
+    def create_port_minimal(self):
+        self.assert_noobj(Network, "test_net")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate")]) +
+                     self.make_nodetemplate("test_port", "tosca.nodes.network.Port",
+                                            reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+                                                  ("test_slice1-1", "tosca.relationships.network.BindsTo")]))
+
+        net=self.assert_obj(Network, "test_net")
+
+        port=Port.objects.filter(network=net, sliver=self.test_slice1_1)
+        assert(len(port)==1)
+        port=port[0]
+
+    def create_two_ports(self):
+        self.assert_noobj(Network, "test_net")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate")]) +
+                     self.make_nodetemplate("test_port1", "tosca.nodes.network.Port",
+                                            reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+                                                  ("test_slice1-1", "tosca.relationships.network.BindsTo")]) +
+                     self.make_nodetemplate("test_port2", "tosca.nodes.network.Port",
+                                            reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+                                                  ("test_slice1-2", "tosca.relationships.network.BindsTo")]))
+
+        net=self.assert_obj(Network, "test_net")
+
+        port=Port.objects.filter(network=net, sliver=self.test_slice1_1)
+        assert(len(port)==1)
+        port=port[0]
+
+        port=Port.objects.filter(network=net, sliver=self.test_slice1_2)
+        assert(len(port)==1)
+        port=port[0]
+
+    def create_four_ports(self):
+        self.assert_noobj(Network, "test_net")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate")]) +
+                     self.make_nodetemplate("test_port1", "tosca.nodes.network.Port",
+                                            reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+                                                  ("test_slice1-1", "tosca.relationships.network.BindsTo")]) +
+                     self.make_nodetemplate("test_port2", "tosca.nodes.network.Port",
+                                            reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+                                                  ("test_slice1-2", "tosca.relationships.network.BindsTo")]) +
+                     self.make_nodetemplate("test_port3", "tosca.nodes.network.Port",
+                                            reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+                                                  ("test_slice2-1", "tosca.relationships.network.BindsTo")]) +
+                     self.make_nodetemplate("test_port4", "tosca.nodes.network.Port",
+                                            reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+                                                  ("test_slice2-2", "tosca.relationships.network.BindsTo")]))
+
+        net=self.assert_obj(Network, "test_net")
+
+        port=Port.objects.filter(network=net, sliver=self.test_slice1_1)
+        assert(len(port)==1)
+        port=port[0]
+
+        port=Port.objects.filter(network=net, sliver=self.test_slice1_2)
+        assert(len(port)==1)
+        port=port[0]
+
+        port=Port.objects.filter(network=net, sliver=self.test_slice2_2)
+        assert(len(port)==1)
+        port=port[0]
+
+        port=Port.objects.filter(network=net, sliver=self.test_slice2_2)
+        assert(len(port)==1)
+        port=port[0]
+
+    def add_port_after_network(self):
+        self.assert_noobj(Network, "test_net")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate")]))
+
+
+        orig_net=self.assert_obj(Network, "test_net")
+
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test_net", "tosca.nodes.network.Network",
+                                            reqs=[("testsite_slice1", "tosca.relationships.MemberOfSlice"),
+                                                  ("Private", "tosca.relationships.UsesNetworkTemplate")]) +
+                     self.make_nodetemplate("test_port1", "tosca.nodes.network.Port",
+                                            reqs=[("test_net", "tosca.relationships.network.LinksTo"),
+                                                  ("test_slice1-1", "tosca.relationships.network.BindsTo")]))
+
+        net=self.assert_obj(Network, "test_net")
+
+        assert(orig_net.id == net.id)
+
+        port=Port.objects.filter(network=net, sliver=self.test_slice1_1)
+        assert(len(port)==1)
+        port=port[0]
+
+
+if __name__ == "__main__":
+    PortTest()
+
+
diff --git a/xos/tosca/tests/servicetest.py b/xos/tosca/tests/servicetest.py
new file mode 100644
index 0000000..276463d
--- /dev/null
+++ b/xos/tosca/tests/servicetest.py
@@ -0,0 +1,74 @@
+from basetest import BaseToscaTest
+
+from core.models import Service
+
+class ServiceTest(BaseToscaTest):
+    tests = ["create_service_minimal",
+             "create_service_notpublished",
+             "create_service_notenabled",
+             "create_service_public_key",
+             "update_service_notpublished",
+             "create_service_maximal",
+             "destroy_service"]
+
+    def cleanup(self):
+        self.try_to_delete(Service, name="test_svc")
+
+    def create_service_minimal(self):
+        self.assert_noobj(Service, "test_svc")
+        self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service"))
+        self.assert_obj(Service, "test_svc", kind="generic", published=True, enabled=True)
+
+    def create_service_notpublished(self):
+        self.assert_noobj(Service, "test_svc")
+        self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service", {"published": False}))
+        self.assert_obj(Service, "test_svc", kind="generic", published=False, enabled=True)
+
+    def create_service_notenabled(self):
+        self.assert_noobj(Service, "test_svc")
+        self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service", {"enabled": False}))
+        self.assert_obj(Service, "test_svc", kind="generic", published=True, enabled=False)
+
+    def create_service_public_key(self):
+        self.assert_noobj(Service, "test_svc")
+        self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service", {"public_key": "foobar"}))
+        self.assert_obj(Service, "test_svc", kind="generic", published=True, enabled=True, public_key="foobar")
+
+    def update_service_notpublished(self):
+        self.assert_noobj(Service, "test_svc")
+        self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service"))
+        original_obj = self.assert_obj(Service, "test_svc", kind="generic", published=True, enabled=True)
+        self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service", {"published": False}))
+        updated_obj = self.assert_obj(Service, "test_svc", kind="generic", published=False, enabled=True)
+        assert(original_obj.id == updated_obj.id)
+
+    def create_service_maximal(self):
+        self.assert_noobj(Service, "test_svc")
+        self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service",
+                        {"kind": "testkind",
+                         "published": False,
+                         "enabled": False,
+                         "view_url": "http://foo/",
+                         "icon_url": "http://bar/",
+                         "public_key": "foobar",
+                         "versionNumber": "1.2"} ))
+        self.assert_obj(Service, "test_svc",
+                         kind="testkind",
+                         published=False,
+                         enabled=False,
+                         view_url="http://foo/",
+                         icon_url="http://bar/",
+                         public_key="foobar",
+                         versionNumber="1.2")
+
+    def destroy_service(self):
+        self.assert_noobj(Service, "test_svc")
+        self.execute(self.make_nodetemplate("test_svc", "tosca.nodes.Service"))
+        self.assert_obj(Service, "test_svc", kind="generic", published=True, enabled=True)
+        self.destroy(self.make_nodetemplate("test_svc", "tosca.nodes.Service"))
+        self.assert_noobj(Service, "test_svc")
+
+if __name__ == "__main__":
+    ServiceTest()
+
+
diff --git a/xos/tosca/tests/usertest.py b/xos/tosca/tests/usertest.py
new file mode 100644
index 0000000..f36eb06
--- /dev/null
+++ b/xos/tosca/tests/usertest.py
@@ -0,0 +1,98 @@
+from basetest import BaseToscaTest
+
+from core.models import User
+
+class UserTest(BaseToscaTest):
+    tests = ["create_user_minimal",
+             "create_user_maximal",
+             "create_user_key_artifact",
+             "destroy_user",
+                           ]
+
+    def cleanup(self):
+        self.try_to_delete(User, email="test@user.com")
+
+    def get_base_templates(self):
+        return self.make_nodetemplate("testsite", "tosca.nodes.Site")
+
+    def assert_nouser(self, email):
+        assert(not User.objects.filter(email=email))
+
+    def assert_user(self, email, **kwargs):
+        obj = User.objects.get(email=email)
+        assert(obj)
+        for (k,v) in kwargs.items():
+            if (getattr(obj,k,None) != v):
+                print "Object %s property '%s' is '%s' and should be '%s'" % (obj, k, getattr(obj,k,None), v)
+                assert(False)
+        return obj
+
+    def create_user_minimal(self):
+        self.assert_nouser("test@user.com")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test@user.com", "tosca.nodes.User",
+                         props = {"firstname": "test", "lastname": "user", "password": "letmein"},
+                         reqs = [("testsite", "tosca.relationships.MemberOfSite")]))
+        user = self.assert_user("test@user.com",
+                                 firstname="test",
+                                 lastname="user",
+                                 is_active=True,
+                                 is_admin=False)
+
+
+    def create_user_maximal(self):
+        self.assert_nouser("test@user.com")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test@user.com", "tosca.nodes.User",
+                         props = {"firstname": "test",
+                                  "lastname": "user",
+                                  "password": "letmein",
+                                  "phone": "123-456-7890",
+                                  "user_url": "http://foo.bar/",
+                                  "public_key": "thisismykey",
+                                  "is_active": False,
+                                  "is_admin": True},
+                         reqs = [("testsite", "tosca.relationships.MemberOfSite")]))
+        user = self.assert_user("test@user.com",
+                                 firstname="test",
+                                 lastname="user",
+                                 phone="123-456-7890",
+                                 user_url="http://foo.bar/",
+                                 public_key="thisismykey",
+                                 # is_active=False, XXX investigate -- this is failing
+                                 is_admin=True)
+
+    def create_user_key_artifact(self):
+        self.assert_nouser("test@user.com")
+        pubkey = self.make_random_string(400)
+        file("/tmp/pubkey", "w").write(pubkey)
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test@user.com", "tosca.nodes.User",
+                         props = {"firstname": "test", "lastname": "user", "password": "letmein", "public_key": "{ get_artifact: [ SELF, pubkey, LOCAL_FILE] }" },
+                         artifacts = {"pubkey": "/tmp/pubkey"},
+                         reqs = [("testsite", "tosca.relationships.MemberOfSite")]))
+        user = self.assert_user("test@user.com",
+                                 firstname="test",
+                                 lastname="user",
+                                 is_active=True,
+                                 is_admin=False,
+                                 public_key=pubkey)
+
+    def destroy_user(self):
+        self.assert_nouser("test@user.com")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("test@user.com", "tosca.nodes.User",
+                         props = {"firstname": "test", "lastname": "user", "password": "letmein"},
+                         reqs = [("testsite", "tosca.relationships.MemberOfSite")]))
+        user = self.assert_user("test@user.com")
+        self.destroy(self.get_base_templates() +
+                     self.make_nodetemplate("test@user.com", "tosca.nodes.User",
+                         props = {"firstname": "test", "lastname": "user", "password": "letmein"},
+                         reqs = [("testsite", "tosca.relationships.MemberOfSite")]))
+        self.assert_nouser("test@user.com")
+
+
+if __name__ == "__main__":
+    UserTest()
+
+
diff --git a/xos/xos/settings.py b/xos/xos/settings.py
index ab454e2..ba5ff1d 100644
--- a/xos/xos/settings.py
+++ b/xos/xos/settings.py
@@ -148,6 +148,7 @@
     'core',
     'hpc',
     'cord',
+    'ceilometer',
     'requestrouter',
 #    'urlfilter',
 #    'servcomp',