Merge branch 'master' of https://github.com/open-cloud/xos
diff --git a/xos/configurations/cord/Makefile b/xos/configurations/cord/Makefile
index 4c2e423..86b4ca1 100644
--- a/xos/configurations/cord/Makefile
+++ b/xos/configurations/cord/Makefile
@@ -2,7 +2,7 @@
 RUNNING_CONTAINER:=$(shell sudo docker ps|grep "xos"|awk '{print $$NF}')
 LAST_CONTAINER=$(shell sudo docker ps -l -q)
 
-test: common_cloudlab ceilometer_dashboard
+cord: common_cloudlab ceilometer_dashboard
 	echo "# Autogenerated -- do not edit" > Dockerfile
 	cat ../common/Dockerfile.common Dockerfile.cord >> Dockerfile
 	cd ../../..; sudo docker build -t xos -f xos/configurations/cord/Dockerfile .
diff --git a/xos/configurations/cord/cord.yaml b/xos/configurations/cord/cord.yaml
index e9a6291..67d8bb6 100644
--- a/xos/configurations/cord/cord.yaml
+++ b/xos/configurations/cord/cord.yaml
@@ -58,7 +58,7 @@
 # - (Synchronizer should copy the files to ONOS container immediately)
 # - Log into service_ONOS_vBNG VM and restart ONOS Docker container
 #   (Should roll this step into a Synchronizer)
-#
+#f
     vBNG_ONOS_app:
       type: tosca.nodes.ONOSvBNGApp
       requirements:
@@ -116,6 +116,7 @@
       artifacts:
           pubkey: /opt/xos/observers/onos/onos_key.pub
 
+
     vOLT_ONOS_app:
       type: tosca.nodes.ONOSvOLTApp
       requirements:
@@ -127,6 +128,20 @@
               relationship: tosca.relationships.UsedByService
       properties:
           dependencies: org.onosproject.olt
+          config_network-cfg.json: >
+            {
+              "devices" : {
+                "of:0000000000000001" : {
+                  "accessDevice" : {
+                    "uplink" : "2",
+                    "vlan"   : "400"
+                  },
+                  "basic" : {
+                    "driver" : "default"
+                  }
+                }
+              }
+            }
 
     # Network templates
     Private:
diff --git a/xos/configurations/cord/dataplane/generate.sh b/xos/configurations/cord/dataplane/generate.sh
index 360ed67..49a12ef 100755
--- a/xos/configurations/cord/dataplane/generate.sh
+++ b/xos/configurations/cord/dataplane/generate.sh
@@ -14,5 +14,8 @@
 switch_vbng  ansible_ssh_host=$( get_ip mysite_vbng flat-lan-1-net) wan_ip=$( get_ip mysite_vbng wan_network) public_ip=$( get_ip mysite_vbng tun0-net )
 
 onos_volt    ansible_ssh_host=$( get_ip mysite_onos_volt flat-lan-1-net)
-switch_volt  ansible_ssh_host=$( get_ip mysite_volt flat-lan-1-net) subscriber_ip=$( get_ip mysite_volt subscriber_network) lan_ip=$( get_ip mysite_volt lan_network) vcpe_lan_ip=$( get_ip mysite_vcpe lan_network)
+switch_volt  ansible_ssh_host=$( get_ip mysite_volt flat-lan-1-net) subscriber_ip=$( get_ip mysite_volt subscriber_network) lan_ip=$( get_ip mysite_volt lan_network) 
+
+client       ansible_ssh_host=$( get_ip mysite_clients flat-lan-1-net) subscriber_ip=$( get_ip mysite_clients subscriber_network)
+vcpe         ansible_ssh_host=$( get_ip mysite_vcpe flat-lan-1-net) lan_ip=$( get_ip mysite_vcpe lan_network)
 EOF
diff --git a/xos/cord/admin.py b/xos/cord/admin.py
index 6137212..9e7946e 100644
--- a/xos/cord/admin.py
+++ b/xos/cord/admin.py
@@ -1,6 +1,7 @@
 from django.contrib import admin
 
 from cord.models import *
+from core.models import Container
 from django import forms
 from django.utils.safestring import mark_safe
 from django.contrib.auth.admin import UserAdmin
@@ -159,6 +160,8 @@
     bbs_account = forms.CharField(required=False)
     creator = forms.ModelChoiceField(queryset=User.objects.all())
     instance = forms.ModelChoiceField(queryset=Instance.objects.all(),required=False)
+    container = forms.ModelChoiceField(queryset=Container.objects.all(),required=False)
+    use_cobm = forms.BooleanField(required=False)
     last_ansible_hash = forms.CharField(required=False)
 
     def __init__(self,*args,**kwargs):

@@ -170,6 +173,8 @@
             self.fields['bbs_account'].initial = self.instance.bbs_account

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

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

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

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

             self.fields['last_ansible_hash'].initial = self.instance.last_ansible_hash

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

             # default fields for an 'add' form

@@ -177,11 +182,14 @@
             self.fields['creator'].initial = get_request().user

             if VCPEService.get_service_objects().exists():

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

+            self.fields['use_cobm'].initial = False

 

     def save(self, commit=True):

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

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

         self.instance.last_ansible_hash = self.cleaned_data.get("last_ansible_hash")

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

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

         return super(VCPETenantForm, self).save(commit=commit)

 

     class Meta:

@@ -191,7 +199,7 @@
     list_display = ('backend_status_icon', 'id', 'subscriber_tenant' )
     list_display_links = ('backend_status_icon', 'id')
     fieldsets = [ (None, {'fields': ['backend_status_text', 'kind', 'provider_service', 'subscriber_tenant', 'service_specific_id', # 'service_specific_attribute',
-                                     'bbs_account', 'creator', 'instance', 'last_ansible_hash'],
+                                     'bbs_account', 'creator', 'use_cobm', 'instance', 'container', 'last_ansible_hash'],
                           'classes':['suit-tab suit-tab-general']})]
     readonly_fields = ('backend_status_text', 'service_specific_attribute', 'bbs_account')
     form = VCPETenantForm
diff --git a/xos/cord/models.py b/xos/cord/models.py
index 67ffdc7..4227f52 100644
--- a/xos/cord/models.py
+++ b/xos/cord/models.py
@@ -333,6 +333,7 @@
             vcpe = VCPETenant(provider_service = vcpeServices[0],
                               subscriber_tenant = self)
             vcpe.caller = self.creator
+            # vcpe.use_cobm = True # XXX XXX XXX remove before checking XXX XXX XXX
             vcpe.save()
 
     def manage_subscriber(self):
@@ -470,6 +471,7 @@
                        "hpc_client_ip", "hpc_client_mac")
 
     default_attributes = {"instance_id": None,
+                          "container_id": None,
                           "users": [],
                           "bbs_account": None,
                           "last_ansible_hash": None}
@@ -534,11 +536,15 @@
 
     @property
     def addresses(self):
-        if not self.instance:
+        if self.instance:
+            ports = self.instance.ports.all()
+        elif self.container:
+            ports = self.container.ports.all()
+        else:
             return {}
 
         addresses = {}
-        for ns in self.instance.ports.all():
+        for ns in ports:
             if "lan" in ns.network.name.lower():
                 addresses["lan"] = (ns.ip, ns.mac)
             elif "wan" in ns.network.name.lower():
diff --git a/xos/core/admin.py b/xos/core/admin.py
index a0cabd1..317c6a5 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -1384,7 +1384,7 @@
 
 class ContainerAdmin(XOSBaseAdmin):
     fieldsets = [
-        ('Container Details', {'fields': ['backend_status_text', 'slice', 'node', 'docker_image', 'no_sync'], 'classes': ['suit-tab suit-tab-general'], })
+        ('Container Details', {'fields': ['backend_status_text', 'slice', 'node', 'docker_image', 'volumes', 'no_sync'], 'classes': ['suit-tab suit-tab-general'], })
     ]
     readonly_fields = ('backend_status_text', )
     list_display = ['backend_status_icon', 'id']
diff --git a/xos/core/models/container.py b/xos/core/models/container.py
index 151b576..ae1d198 100644
--- a/xos/core/models/container.py
+++ b/xos/core/models/container.py
@@ -28,6 +28,7 @@
     node = models.ForeignKey(Node, related_name='containers')
     creator = models.ForeignKey(User, related_name='containers', blank=True, null=True)
     docker_image = StrippedCharField(null=True, blank=True, max_length=200, help_text="name of docker container to instantiate")
+    volumes = models.TextField(null=True, blank=True, help_text="Comma-separated list of volumes")
 
     def __unicode__(self):
         return u'container-%s' % str(self.id)
diff --git a/xos/core/models/node.py b/xos/core/models/node.py
index ec67975..5496d6b 100644
--- a/xos/core/models/node.py
+++ b/xos/core/models/node.py
@@ -16,6 +16,10 @@
 
     def __unicode__(self):  return u'%s' % (self.name)
 
+    def __init__(self, *args, **kwargs):
+        super(Node, self).__init__(*args, **kwargs)
+        self.no_sync=True
+
     def save(self, *args, **kwds):
         if self.site is None and self.site_deployment is not None:
             self.site = self.site_deployment.site
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 950ce02..c263bba 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -353,6 +353,9 @@
         super(TenantWithContainer, self).__init__(*args, **kwargs)
         self.cached_instance=None
         self.orig_instance_id = self.get_initial_attribute("instance_id")
+        self.cached_container=None
+        self.orig_container_id = self.get_initial_attribute("container_id")
+
 
     @property
     def instance(self):
@@ -379,6 +382,30 @@
         self.set_attribute("instance_id", value)
 
     @property
+    def container(self):
+        from core.models import Container
+        if getattr(self, "cached_container", None):
+            return self.cached_container
+        container_id=self.get_attribute("container_id")
+        if not container_id:
+            return None
+        containers=Container.objects.filter(id=container_id)
+        if not containers:
+            return None
+        container=containers[0]
+        container.caller = self.creator
+        self.cached_container = container
+        return container
+
+    @container.setter
+    def container(self, value):
+        if value:
+            value = value.id
+        if (value != self.get_attribute("container_id", None)):
+            self.cached_container=None
+        self.set_attribute("container_id", value)
+
+    @property
     def creator(self):
         from core.models import User
         if getattr(self, "cached_creator", None):
@@ -413,7 +440,23 @@
 
         raise XOSProgrammingError("No VPCE image (looked for %s)" % str(self.LOOK_FOR_IMAGES))
 
-    def pick_node(self):
+    @property
+    def use_cobm(self):
+        return self.get_attribute("use_cobm", False)
+
+    @use_cobm.setter
+    def use_cobm(self, v):
+        self.set_attribute("use_cobm", v)
+
+    @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)
+
+    def pick_node_for_instance(self):
         from core.models import Node
         nodes = list(Node.objects.all())
         # TODO: logic to filter nodes by which nodes are up, and which
@@ -421,7 +464,15 @@
         nodes = sorted(nodes, key=lambda node: node.instances.all().count())
         return nodes[0]
 
-    def manage_container(self):
+    def pick_node_for_container_on_metal(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.containers.all().count())
+        return nodes[0]
+
+    def manage_container_in_instance(self):
         from core.models import Instance, Flavor
 
         if self.deleted:
@@ -439,7 +490,7 @@
             if not flavors:
                 raise XOSConfigurationError("No m1.small flavor")
 
-            node =self.pick_node()
+            node =self.pick_node_for_instance()
             instance = Instance(slice = self.provider_service.slices.all()[0],
                             node = node,
                             image = self.image,
@@ -455,11 +506,83 @@
                 instance.delete()
                 raise
 
+    def manage_container_on_metal(self):
+        from core.models import Container, Instance, Flavor, Port
+
+        if self.deleted:
+            return
+
+        if (self.container is not None):
+            self.container.delete()
+            self.container = None
+
+        if self.container is None:
+            if not self.provider_service.slices.count():
+                raise XOSConfigurationError("The VCPE service has no slices")
+
+            slice = self.provider_service.slices.all()[0]
+            node = self.pick_node_for_container_on_metal()
+
+            # Our current docker network strategy requires that there be some
+            # instance on the server that connects to the networks, so that
+            # the containers can piggyback off of that configuration.
+            instances = Instance.objects.filter(slice=slice, node=node)
+            if not instances:
+                flavors = Flavor.objects.filter(name="m1.small")
+                if not flavors:
+                    raise XOSConfigurationError("No m1.small flavor")
+
+                node =self.pick_node_for_instance()
+                instance = Instance(slice = self.provider_service.slices.all()[0],
+                                node = node,
+                                image = self.image,
+                                creator = self.creator,
+                                deployment = node.site_deployment.deployment,
+                                flavor = flavors[0])
+                instance.save()
+
+            # Now make the container...
+            container = Container(slice = slice,
+                            node = node,
+                            docker_image = "andybavier/docker-vcpe",
+                            creator = self.creator,
+                            no_sync=True)
+            container.save()
+
+            # ... and add the ports for the container
+            # XXX probably should be done in model_policy
+            for network in slice.networks.all():
+                if (network.name.endswith("-nat")):
+                    continue
+                port = Port(network = network,
+                            container = container)
+                port.save()
+
+            container.no_sync = False
+            container.save()
+
+            try:
+                self.container = container
+                super(TenantWithContainer, self).save()
+            except:
+                container.delete()
+                raise
+
+    def manage_container(self):
+        if self.use_cobm:
+            self.manage_container_on_metal()
+        else:
+            self.manage_container_in_instance()
+
     def cleanup_container(self):
         if self.instance:
             # print "XXX cleanup instance", self.instance
             self.instance.delete()
             self.instance = None
+        if self.container:
+            # print "XXX cleanup container", self.container
+            self.container.delete()
+            self.container = None
 
 class CoarseTenant(Tenant):
     """ TODO: rename "CoarseTenant" --> "StaticTenant" """
diff --git a/xos/helloworld/models.py b/xos/helloworld/models.py
index a657f3a..9bb343e 100644
--- a/xos/helloworld/models.py
+++ b/xos/helloworld/models.py
@@ -11,7 +11,7 @@
 
 class Hello(PlCoreBase):
     name = models.CharField(max_length=254,help_text="Salutation e.g. Hello or Bonjour")
-    instance_backref = models.ForeignKey(Instance)
+    instance_backref = models.ForeignKey(Instance,related_name="hellos")
     
 class World(PlCoreBase):
     name = models.CharField(max_length=254,help_text="Name of planet")
diff --git a/xos/helloworld/view.py b/xos/helloworld/view.py
index b3eec29..7024747 100644
--- a/xos/helloworld/view.py
+++ b/xos/helloworld/view.py
@@ -34,9 +34,9 @@
             i.instance_name=None
             i.enacted=None
             i.save()
-            h = Hello(name=hello_name,sliver_backref=i)
-            w = World(hello=h,name=world_name)
+            h = Hello(name=hello_name,instance_backref=i)
             h.save()
+            w = World(hello=h,name=world_name)
             w.save()
 
             t = template.Template(head_template + 'Done. New instance id: %r'%i.pk + self.tail_template)
diff --git a/xos/model-deps b/xos/model-deps
index ea32eb9..59bbe25 100644
--- a/xos/model-deps
+++ b/xos/model-deps
@@ -1,8 +1,6 @@
 {
     "Slice": [
         "Site", 
-        "Service", 
-        "ServiceClass", 
         "User"
     ], 
     "ImageDeployments": [
@@ -65,9 +63,6 @@
     "Reservation": [
         "Slice"
     ], 
-    "ServiceResource": [
-        "ServiceClass"
-    ], 
     "Instance": [
         "Image", 
         "User", 
@@ -79,9 +74,6 @@
     "Account": [
         "Site"
     ], 
-    "ServiceAttribute": [
-        "Service"
-    ], 
     "ControllerSlicePrivilege": [
         "Controller"
     ], 
@@ -116,9 +108,6 @@
         "Controller", 
         "DashboardView"
     ], 
-    "Tag": [
-        "Service"
-    ], 
     "Invoice": [
         "Account"
     ], 
diff --git a/xos/observers/helloworld/helloworld_config b/xos/observers/helloworld/helloworld_config
index 671af51..e32ee0c 100644
--- a/xos/observers/helloworld/helloworld_config
+++ b/xos/observers/helloworld/helloworld_config
@@ -38,6 +38,7 @@
 dependency_graph=/opt/xos/model-deps
 logfile=/var/log/xos_backend.log
 steps_dir=/opt/xos/observers/helloworld/steps
+applist=helloworld
 
 [gui]
 disable_minidashboard=True
diff --git a/xos/observers/helloworld/steps/sync_hello.py b/xos/observers/helloworld/steps/sync_hello.py
index 1fb8c2b..7071ea0 100644
--- a/xos/observers/helloworld/steps/sync_hello.py
+++ b/xos/observers/helloworld/steps/sync_hello.py
@@ -18,7 +18,7 @@
     requested_interval=0
     
     def sync_record(self, record):
-        instance = record.sliver_backref        
+        instance = record.instance_backref        
         instance.userData="packages:\n  - apache2\nruncmd:\n  - update-rc.d apache2 enable\n  - service apache2 start\nwrite_files:\n-   content: Hello %s\n    path: /var/www/html/hello.txt"%record.name
         instance.save()
         
diff --git a/xos/openstack_observer/event_loop.py b/xos/openstack_observer/event_loop.py
index 17d5c7a..db7459a 100644
--- a/xos/openstack_observer/event_loop.py
+++ b/xos/openstack_observer/event_loop.py
@@ -27,6 +27,24 @@
 from toposort import toposort
 from observer.error_mapper import *
 from openstack_observer.openstacksyncstep import OpenStackSyncStep
+from observer.steps.sync_object import SyncObject
+
+# Load app models
+
+try:
+    app_module_names = Config().observer_applist
+except AttributeError:
+    app_module_names = []
+
+if (type(app_module_names)!=list):
+    app_module_names=[app_module_names]
+
+app_modules = []
+
+for m in app_module_names:
+    model_path = m+'.models'
+    module = __import__(model_path,fromlist=[m])
+    app_modules.append(module)
 
 
 debug_mode = False
@@ -166,6 +184,7 @@
 					provides_dict[m.__name__]=[s.__name__]
 
 		step_graph = {}
+                phantom_steps = []
 		for k,v in self.model_dependency_graph.items():
 			try:
 				for source in provides_dict[k]:
@@ -183,7 +202,12 @@
 									step_graph[source]=[dest]
 						except KeyError:
 							if (not provides_dict.has_key(m)):
-								step_graph[source]='#%s'%m	
+                                                                try:
+								    step_graph[source]+=['#%s'%m]
+                                                                except:
+                                                                    step_graph[source]=['#%s'%m]
+
+                                                                phantom_steps+=['#%s'%m]
 							pass
 					
 			except KeyError:
@@ -196,7 +220,8 @@
 
 		pp = pprint.PrettyPrinter(indent=4)
                 logger.info(pp.pformat(step_graph))
-		self.ordered_steps = toposort(self.dependency_graph, map(lambda s:s.__name__,self.sync_steps))
+		self.ordered_steps = toposort(self.dependency_graph, phantom_steps+map(lambda s:s.__name__,self.sync_steps))
+		self.ordered_steps = [i for i in self.ordered_steps if i!='SyncObject']
 
 		logger.info("Order of steps=%s" % self.ordered_steps)
 
@@ -245,15 +270,31 @@
 			for e in self.ordered_steps:
 				self.last_deletion_run_times[e]=0
 
+        def lookup_step_class(self,s):
+		if ('#' in s):
+			return SyncObject
+		else:
+			step = self.step_lookup[s]
+		return step
+
 	def lookup_step(self,s):
 		if ('#' in s):
 			objname = s[1:]
 			so = SyncObject()
-			so.provides=[globals()[objname]]
-			so.observes=globals()[objname]
+			
+                        try:
+			    obj = globals()[objname]
+                        except:
+                            for m in app_modules:
+                                if (hasattr(m,objname)):
+                                    obj = getattr(m,objname)
+
+			so.provides=[obj]
+			so.observes=[obj]
 			step = so
 		else:
-			step = self.step_lookup[s]
+			step_class = self.step_lookup[s]
+                        step = step_class(driver=self.driver,error_map=self.error_mapper)
 		return step
 			
 	def save_run_times(self):
@@ -275,7 +316,7 @@
 
 	def sync(self, S, deletion):
             try:
-		step = self.lookup_step(S)
+		step = self.lookup_step_class(S)
 		start_time=time.time()
 
                 logger.info("Starting to work on step %s, deletion=%s" % (step.__name__, str(deletion)))
@@ -324,16 +365,20 @@
 			self.failed_steps.append(step)
 			my_status = STEP_STATUS_KO
 		else:
-			sync_step = step(driver=self.driver,error_map=self.error_mapper)
+			sync_step = self.lookup_step(S)
 			sync_step. __name__= step.__name__
 			sync_step.dependencies = []
 			try:
 				mlist = sync_step.provides
 
-				for m in mlist:
-				        lst =  self.model_dependency_graph[m.__name__]
-			                nlst = map(lambda(a,b):b,lst)
-					sync_step.dependencies.extend(nlst)
+                                try:
+                                    for m in mlist:
+                                            lst =  self.model_dependency_graph[m.__name__]
+                                            nlst = map(lambda(a,b):b,lst)
+                                            sync_step.dependencies.extend(nlst)
+                                except Exception,e:
+                                    raise e
+
 			except KeyError:
 				pass
 			sync_step.debug_mode = debug_mode
diff --git a/xos/openstack_observer/steps/sync_container.py b/xos/openstack_observer/steps/sync_container.py
index de4a2ce..0f5dcc4 100644
--- a/xos/openstack_observer/steps/sync_container.py
+++ b/xos/openstack_observer/steps/sync_container.py
@@ -70,6 +70,9 @@
             pd["snoop_instance_id"] = instance_port.instance.instance_id
 
             ports.append(pd)
+
+            i = i + 1
+
         return ports
 
     def get_extra_attributes(self, o):
@@ -81,6 +84,7 @@
         fields["docker_image"] = o.docker_image
         fields["username"] = "root"
         fields["ports"] = self.get_ports(o)
+        fields["volumes"] = [x.strip() for x in o.volumes.split(",")]
         return fields
 
     def sync_fields(self, o, fields):
diff --git a/xos/openstack_observer/steps/sync_container.yaml b/xos/openstack_observer/steps/sync_container.yaml
index a707d0b..e005e58 100644
--- a/xos/openstack_observer/steps/sync_container.yaml
+++ b/xos/openstack_observer/steps/sync_container.yaml
@@ -16,6 +16,10 @@
          snoop_instance_mac: {{ port.snoop_instance_mac }}
          snoop_instance_id: {{ port.snoop_instance_id }}
     {% endfor %}
+    volumes:
+    {% for volume in volumes %}
+       - {{ volume }}
+    {% endfor %}
 
   tasks:
 
diff --git a/xos/openstack_observer/steps/sync_object.py b/xos/openstack_observer/steps/sync_object.py
index 5e70464..a289c95 100644
--- a/xos/openstack_observer/steps/sync_object.py
+++ b/xos/openstack_observer/steps/sync_object.py
@@ -17,4 +17,4 @@
     observes=[] # Caller fills this in
 
     def sync_record(self, r):
-        raise Exception('Waiting for Service dependency')
+        raise DeferredException('Waiting for Service dependency: %r'%r)
diff --git a/xos/openstack_observer/syncstep.py b/xos/openstack_observer/syncstep.py
index 7accbfa..21327d7 100644
--- a/xos/openstack_observer/syncstep.py
+++ b/xos/openstack_observer/syncstep.py
@@ -8,6 +8,7 @@
 from core.models import *
 from django.db import reset_queries
 from observer.ansible import *
+from dependency_walker import *
 
 import json
 import time
@@ -101,12 +102,26 @@
         for dep in self.dependencies:
             peer_name = dep[0].lower() + dep[1:]    # django names are camelCased with the first letter lower
 
+            peer_objects=[]
             try:
-                peer_object = deepgetattr(obj, peer_name)
+                peer_names = plural(peer_name)
+                peer_object_list=[]
+
                 try:
-                    peer_objects = peer_object.all()
-                except AttributeError:
-                    peer_objects = [peer_object]
+                    peer_object_list.append(deepgetattr(obj, peer_name))
+                except:
+                    pass
+
+                try:
+                    peer_object_list.append(deepgetattr(obj, peer_names))
+                except:
+                    pass
+
+                for peer_object in peer_object_list:
+                    try:
+                        peer_objects.extend(peer_object.all())
+                    except AttributeError:
+                        peer_objects.append(peer_object)
             except:
                 peer_objects = []
 
@@ -174,6 +189,9 @@
                 pass
 
     def call(self, failed=[], deletion=False):
+        #if ('Instance' in self.__class__.__name__):
+        #    pdb.set_trace()
+
         pending = self.fetch_pending(deletion)
 
         for o in pending:
diff --git a/xos/openstack_observer/templates/start-container.sh.j2 b/xos/openstack_observer/templates/start-container.sh.j2
index 5656992..dc3b7cb 100644
--- a/xos/openstack_observer/templates/start-container.sh.j2
+++ b/xos/openstack_observer/templates/start-container.sh.j2
@@ -6,11 +6,17 @@
 CONTAINER={{ container_name }}
 IMAGE={{ docker_image }}
 
+{% for volume in volumes %}
+DEST_DIR=/var/container_volumes/$CONTAINER/{{ volume }}
+mkdir -p $DEST_DIR
+VOLUME_ARGS="$VOLUME_ARGS -v $DEST_DIR:{{ volume }}"
+{% endfor %}
+
 docker inspect $CONTAINER > /dev/null 2>&1
 if [ "$?" == 1 ]
 then
     docker pull $IMAGE
-    docker run -d --name=$CONTAINER --privileged=true --net=none $IMAGE
+    docker run -d --name=$CONTAINER --privileged=true --net=none $VOLUME_ARGS $IMAGE
 else
     docker start $CONTAINER
 fi
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index a661af7..88d8f5f 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -136,6 +136,9 @@
             dependencies:
                 type: string
                 required: false
+            config_network-cfg.json:
+                type: string
+                required: false
 
     tosca.nodes.VCPEService:
         description: >
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 9170ecf..4352ef5 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -154,6 +154,9 @@
             dependencies:
                 type: string
                 required: false
+            config_network-cfg.json:
+                type: string
+                required: false
 
     tosca.nodes.VCPEService:
         description: >
diff --git a/xos/xos_config b/xos/xos_config
index b86d618..c47fac6 100644
--- a/xos/xos_config
+++ b/xos/xos_config
@@ -37,6 +37,7 @@
 images_directory=/opt/xos/images
 dependency_graph=/opt/xos/model-deps
 logfile=/var/log/xos_backend.log
+applist=helloworld
 
 [gui]
 disable_minidashboard=True