Merge branch 'master' of https://github.com/open-cloud/xos into AddHelloWorldService
diff --git a/xos/configurations/cord/cord.yaml b/xos/configurations/cord/cord.yaml
index 67d8bb6..8945eea 100644
--- a/xos/configurations/cord/cord.yaml
+++ b/xos/configurations/cord/cord.yaml
@@ -456,7 +456,8 @@
         type: tosca.nodes.VOLTTenant
         properties:
             service_specific_id: 123
-            vlan_id: 432
+            s_tag: 222
+            c_tag: 432
         requirements:
             - provider_service:
                 node: service_volt
diff --git a/xos/cord/admin.py b/xos/cord/admin.py
index 9e7946e..981f275 100644
--- a/xos/cord/admin.py
+++ b/xos/cord/admin.py
@@ -1,7 +1,6 @@
 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
@@ -52,7 +51,8 @@
         return VOLTService.get_service_objects_by_user(request.user)
 
 class VOLTTenantForm(forms.ModelForm):
-    vlan_id = forms.CharField()
+    s_tag = forms.CharField()
+    c_tag = forms.CharField()
     creator = forms.ModelChoiceField(queryset=User.objects.all())
 
     def __init__(self,*args,**kwargs):

@@ -61,7 +61,8 @@
         self.fields['provider_service'].queryset = VOLTService.get_service_objects().all()

         if self.instance:

             # fields for the attributes

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

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

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

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

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

             # default fields for an 'add' form

@@ -71,7 +72,8 @@
                self.fields["provider_service"].initial = VOLTService.get_service_objects().all()[0]

 

     def save(self, commit=True):

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

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

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

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

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

 

@@ -79,10 +81,10 @@
         model = VOLTTenant
 
 class VOLTTenantAdmin(ReadOnlyAwareAdmin):
-    list_display = ('backend_status_icon', 'id', 'service_specific_id', 'vlan_id', 'subscriber_root' )
+    list_display = ('backend_status_icon', 'id', 'service_specific_id', 's_tag', 'c_tag', 'subscriber_root' )
     list_display_links = ('backend_status_icon', 'id')
     fieldsets = [ (None, {'fields': ['backend_status_text', 'kind', 'provider_service', 'subscriber_root', 'service_specific_id', # 'service_specific_attribute',
-                                     'vlan_id', 'creator'],
+                                     's_tag', 'c_tag', 'creator'],
                           'classes':['suit-tab suit-tab-general']})]
     readonly_fields = ('backend_status_text', 'service_specific_attribute')
     form = VOLTTenantForm
@@ -160,7 +162,6 @@
     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)
 
@@ -173,7 +174,6 @@
             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):

@@ -188,7 +188,6 @@
         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)

 

@@ -199,7 +198,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', 'use_cobm', 'instance', 'container', 'last_ansible_hash'],
+                                     'bbs_account', 'creator', 'use_cobm', 'instance', '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 4227f52..afbe3ef 100644
--- a/xos/cord/models.py
+++ b/xos/cord/models.py
@@ -254,7 +254,7 @@
 
     KIND = VOLT_KIND
 
-    default_attributes = {"vlan_id": None, }
+    default_attributes = {"vlan_id": None, "s_tag": None, "c_tag": None}
     def __init__(self, *args, **kwargs):
         volt_services = VOLTService.get_service_objects().all()
         if volt_services:
@@ -263,12 +263,30 @@
         self.cached_vcpe = None
 
     @property
+    def s_tag(self):
+        return self.get_attribute("s_tag", self.default_attributes["s_tag"])
+
+    @s_tag.setter
+    def s_tag(self, value):
+        self.set_attribute("s_tag", value)
+
+    @property
+    def c_tag(self):
+        return self.get_attribute("c_tag", self.default_attributes["c_tag"])
+
+    @c_tag.setter
+    def c_tag(self, value):
+        self.set_attribute("c_tag", value)
+
+    # for now, vlan_id is a synonym for c_tag
+
+    @property
     def vlan_id(self):
-        return self.get_attribute("vlan_id", self.default_attributes["vlan_id"])
+        return self.c_tag
 
     @vlan_id.setter
     def vlan_id(self, value):
-        self.set_attribute("vlan_id", value)
+        self.c_tag = value
 
     @property
     def vcpe(self):
diff --git a/xos/cord/rest_examples/add_volt_tenant.sh b/xos/cord/rest_examples/add_volt_tenant.sh
index 5dd3dd4..4bbe2bb 100755
--- a/xos/cord/rest_examples/add_volt_tenant.sh
+++ b/xos/cord/rest_examples/add_volt_tenant.sh
@@ -3,8 +3,9 @@
 source ./config.sh
 
 SERVICE_SPECIFIC_ID=1238
-VLAN_ID=1238
+C_TAG=1238
+S_TAG=3333
 
-echo curl "-H \"Accept: application/json; indent=4\" -H \"Content-Type: application/json\" -u $AUTH -X POST -d \"{\\\"service_specific_id\\\": \\\"$SERVICE_SPECIFIC_ID\\\", \\\"vlan_id\\\": \\\"$VLAN_ID\\\"}\" $HOST/xoslib/volttenant/"
+echo curl "-H \"Accept: application/json; indent=4\" -H \"Content-Type: application/json\" -u $AUTH -X POST -d \"{\\\"service_specific_id\\\": \\\"$SERVICE_SPECIFIC_ID\\\", \\\"c_tag\\\": \\\"$C_TAG\\\", \\\"s_tag\\\": \\\"$S_TAG\\\"}\" $HOST/xoslib/volttenant/"
 
-curl -H "Accept: application/json; indent=4" -H "Content-Type: application/json" -u $AUTH -X POST -d "{\"service_specific_id\": \"$SERVICE_SPECIFIC_ID\", \"vlan_id\": \"$VLAN_ID\"}" $HOST/xoslib/volttenant/  
+curl -H "Accept: application/json; indent=4" -H "Content-Type: application/json" -u $AUTH -X POST -d "{\"service_specific_id\": \"$SERVICE_SPECIFIC_ID\", \"c_tag\": \"$C_TAG\", \"s_tag\": \"$S_TAG\"}" $HOST/xoslib/volttenant/  
diff --git a/xos/cord/rest_examples/config.sh b/xos/cord/rest_examples/config.sh
index 7b8c8e1..06162ee 100644
--- a/xos/cord/rest_examples/config.sh
+++ b/xos/cord/rest_examples/config.sh
@@ -1,5 +1,6 @@
 #HOST=198.0.0.44:8000
-HOST=10.254.1.22:8000
+#HOST=10.254.1.22:8000
+HOST=clnode050.clemson.cloudlab.us:9999
 
 #AUTH=scott@onlab.us:letmein
 AUTH=padmin@vicci.org:letmein
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 317c6a5..ce7759c 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -1203,7 +1203,7 @@
 class ImageAdmin(XOSBaseAdmin):
 
     fieldsets = [('Image Details',
-                   {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
+                   {'fields': ['backend_status_text', 'name', 'kind', 'disk_format', 'container_format'],
                     'classes': ['suit-tab suit-tab-general']})
                ]
     readonly_fields = ('backend_status_text', )
@@ -1214,7 +1214,7 @@
 
     user_readonly_fields = ['name', 'disk_format', 'container_format']
 
-    list_display = ['backend_status_icon', 'name']
+    list_display = ['backend_status_icon', 'name', 'kind']
     list_display_links = ('backend_status_icon', 'name', )
 
 class NodeForm(forms.ModelForm):
@@ -1282,13 +1282,14 @@
 class InstanceAdmin(XOSBaseAdmin):
     form = InstanceForm
     fieldsets = [
-        ('Instance Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'flavor', 'image', 'node', 'all_ips_string', 'instance_id', 'instance_name', 'ssh_command'], 'classes': ['suit-tab suit-tab-general'], })
+        ('Instance Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'isolation', 'flavor', 'image', 'node', 'all_ips_string', 'instance_id', 'instance_name', 'ssh_command', ], 'classes': ['suit-tab suit-tab-general'], }),
+        ('Container Settings', {'fields': ['volumes'], 'classes': ['suit-tab suit-tab-container'], }),
     ]
     readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
-    list_display = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deployment']
+    list_display = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'isolation', 'slice', 'flavor', 'image', 'node', 'deployment']
     list_display_links = ('backend_status_icon', 'all_ips_string', 'instance_id', )
 
-    suit_form_tabs =(('general', 'Instance Details'), ('ports', 'Ports'))
+    suit_form_tabs =(('general', 'Instance Details'), ('ports', 'Ports'), ('container', 'Container Settings'))
 
     inlines = [TagInline, InstancePortInline]
 
@@ -1372,38 +1373,38 @@
     #    obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
     #    obj.delete()
 
-class ContainerPortInline(XOSTabularInline):
-    fields = ['backend_status_icon', 'network', 'container', 'ip', 'mac', 'segmentation_id']
-    readonly_fields = ("backend_status_icon", "ip", "mac", "segmentation_id")
-    model = Port
-    selflink_fieldname = "network"
-    extra = 0
-    verbose_name_plural = "Ports"
-    verbose_name = "Port"
-    suit_classes = 'suit-tab suit-tab-ports'
+#class ContainerPortInline(XOSTabularInline):
+#    fields = ['backend_status_icon', 'network', 'container', 'ip', 'mac', 'segmentation_id']
+#    readonly_fields = ("backend_status_icon", "ip", "mac", "segmentation_id")
+#    model = Port
+#    selflink_fieldname = "network"
+#    extra = 0
+#    verbose_name_plural = "Ports"
+#    verbose_name = "Port"
+#    suit_classes = 'suit-tab suit-tab-ports'
 
-class ContainerAdmin(XOSBaseAdmin):
-    fieldsets = [
-        ('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']
-    list_display_links = ('backend_status_icon', 'id', )
-
-    suit_form_tabs =(('general', 'Container Details'), ('ports', 'Ports'))
-
-    inlines = [TagInline, ContainerPortInline]
-
-    def formfield_for_foreignkey(self, db_field, request, **kwargs):
-        if db_field.name == 'slice':
-            kwargs['queryset'] = Slice.select_by_user(request.user)
-
-        return super(ContainerAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
-
-    def queryset(self, request):
-        # admins can see all instances. Users can only see instances of
-        # the slices they belong to.
-        return Container.select_by_user(request.user)
+#class ContainerAdmin(XOSBaseAdmin):
+#    fieldsets = [
+#        ('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']
+#    list_display_links = ('backend_status_icon', 'id', )
+#
+#    suit_form_tabs =(('general', 'Container Details'), ('ports', 'Ports'))
+#
+#    inlines = [TagInline, ContainerPortInline]
+#
+#    def formfield_for_foreignkey(self, db_field, request, **kwargs):
+#        if db_field.name == 'slice':
+#            kwargs['queryset'] = Slice.select_by_user(request.user)
+#
+#        return super(ContainerAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
+#
+#    def queryset(self, request):
+#        # admins can see all instances. Users can only see instances of
+#        # the slices they belong to.
+#        return Container.select_by_user(request.user)
 
 class UserCreationForm(forms.ModelForm):
     """A form for creating new users. Includes all the required
@@ -1760,7 +1761,7 @@
     readonly_fields = ('backend_status_icon', )
 
 class NetworkPortInline(XOSTabularInline):
-    fields = ['backend_status_icon', 'network', 'instance', 'container', 'ip', 'mac']
+    fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
     readonly_fields = ("backend_status_icon", "ip", "mac")
     model = Port
     selflink_fieldname = "instance"
@@ -2057,5 +2058,5 @@
     admin.site.register(TenantRoot, TenantRootAdmin)
     admin.site.register(TenantRootRole, TenantRootRoleAdmin)
     admin.site.register(TenantAttribute, TenantAttributeAdmin)
-    admin.site.register(Container, ContainerAdmin)
+#    admin.site.register(Container, ContainerAdmin)
 
diff --git a/xos/core/models/__init__.py b/xos/core/models/__init__.py
index bc97dab..c380e9c 100644
--- a/xos/core/models/__init__.py
+++ b/xos/core/models/__init__.py
@@ -24,7 +24,6 @@
 from .node import Node
 from .slicetag import SliceTag
 from .instance import Instance
-from .container import Container
 from .reservation import ReservedResource
 from .reservation import Reservation
 from .network import Network, NetworkParameterType, NetworkParameter, Port, NetworkTemplate, Router, NetworkSlice, ControllerNetwork
diff --git a/xos/core/models/image.py b/xos/core/models/image.py
index 21d4f23..1a3cbf7 100644
--- a/xos/core/models/image.py
+++ b/xos/core/models/image.py
@@ -7,7 +7,10 @@
 # Create your models here.
 
 class Image(PlCoreBase):
+    KIND_CHOICES = (('vm', 'Virtual Machine'), ('container', 'Container'), )
+
     name = StrippedCharField(max_length=256, unique=True)
+    kind = models.CharField(null=False, blank=False, max_length=30, choices=KIND_CHOICES, default="vm")
     disk_format = StrippedCharField(max_length=256)
     container_format = StrippedCharField(max_length=256)
     path = StrippedCharField(max_length=256, null=True, blank=True, help_text="Path to image on local disk")
diff --git a/xos/core/models/instance.py b/xos/core/models/instance.py
index 75826f6..927c8e3 100644
--- a/xos/core/models/instance.py
+++ b/xos/core/models/instance.py
@@ -80,6 +80,8 @@
 
 # Create your models here.
 class Instance(PlCoreBase):
+    ISOLATION_CHOICES = (('vm', 'Virtual Machine'), ('container', 'Container'), )
+
     objects = InstanceManager()
     deleted_objects = InstanceDeletionManager()
     instance_id = StrippedCharField(null=True, blank=True, max_length=200, help_text="Nova instance id")
@@ -97,6 +99,8 @@
     flavor = models.ForeignKey(Flavor, help_text="Flavor of this instance", default=get_default_flavor)
     tags = generic.GenericRelation(Tag)
     userData = models.TextField(blank=True, null=True, help_text="user_data passed to instance during creation")
+    isolation = models.CharField(null=False, blank=False, max_length=30, choices=ISOLATION_CHOICES, default="vm")
+    volumes = models.TextField(null=True, blank=True, help_text="Comma-separated list of directories to expose to parent context")
 
     def __unicode__(self):
         if self.name and Slice.objects.filter(id=self.slice_id) and (self.name != self.slice.name):
diff --git a/xos/core/models/network.py b/xos/core/models/network.py
index 48af5a6..b12068c 100644
--- a/xos/core/models/network.py
+++ b/xos/core/models/network.py
@@ -2,7 +2,7 @@
 import socket
 import sys
 from django.db import models
-from core.models import PlCoreBase, Site, Slice, Instance, Controller, Container
+from core.models import PlCoreBase, Site, Slice, Instance, Controller
 from core.models import ControllerLinkManager,ControllerLinkDeletionManager
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes import generic
@@ -211,7 +211,6 @@
 class Port(PlCoreBase):
     network = models.ForeignKey(Network,related_name='links')
     instance = models.ForeignKey(Instance, null=True, blank=True, related_name='ports')
-    container = models.ForeignKey(Container, null=True, blank=True, related_name='ports')
     ip = models.GenericIPAddressField(help_text="Instance ip address", blank=True, null=True)
     port_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum port id")
     mac = models.CharField(null=True, blank=True, max_length=256, help_text="MAC address associated with this port")
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index c263bba..9646ce8 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -353,8 +353,8 @@
         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")
+#        self.cached_container=None
+#        self.orig_container_id = self.get_initial_attribute("container_id")
 
 
     @property
@@ -381,29 +381,29 @@
             self.cached_instance=None
         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 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):
@@ -464,13 +464,13 @@
         nodes = sorted(nodes, key=lambda node: node.instances.all().count())
         return nodes[0]
 
-    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 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
@@ -506,72 +506,72 @@
                 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_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:
+#        if self.use_cobm:
+#            self.manage_container_on_metal()
+#        else:
             self.manage_container_in_instance()
 
     def cleanup_container(self):
@@ -579,10 +579,10 @@
             # 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
+#        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/core/xoslib/methods/cordsubscriber.py b/xos/core/xoslib/methods/cordsubscriber.py
index c26ac54..297ac4a 100644
--- a/xos/core/xoslib/methods/cordsubscriber.py
+++ b/xos/core/xoslib/methods/cordsubscriber.py
@@ -28,7 +28,9 @@
 class CordSubscriberIdSerializer(serializers.ModelSerializer, PlusSerializerMixin):
         id = ReadOnlyField()
         service_specific_id = ReadOnlyField()
-        vlan_id = ReadOnlyField()
+        vlan_id = ReadOnlyField()      # XXX remove this
+        c_tag = ReadOnlyField()
+        s_tag = ReadOnlyField()
         vcpe_id = ReadOnlyField()
         instance = ReadOnlyField()
         image = ReadOnlyField()
@@ -59,7 +61,7 @@
         class Meta:
             model = CordSubscriber
             fields = ('humanReadableName', 'id',
-                      'service_specific_id', 'vlan_id',
+                      'service_specific_id', 'vlan_id', 's_tag', 'c_tag',
                       'vcpe_id', 'instance', 'instance_name', 'image', 'image_name',
                       'firewall_enable', 'firewall_rules',
                       'url_filter_enable', 'url_filter_rules', 'url_filter_level',
diff --git a/xos/core/xoslib/methods/volttenant.py b/xos/core/xoslib/methods/volttenant.py
index e5998da..bf48290 100644
--- a/xos/core/xoslib/methods/volttenant.py
+++ b/xos/core/xoslib/methods/volttenant.py
@@ -26,7 +26,9 @@
 class VOLTTenantIdSerializer(serializers.ModelSerializer, PlusSerializerMixin):
         id = ReadOnlyField()
         service_specific_id = serializers.CharField()
-        vlan_id = serializers.CharField()
+        #vlan_id = serializers.CharField()
+        s_tag = serializers.CharField()
+        c_tag = serializers.CharField()
         provider_service = serializers.PrimaryKeyRelatedField(queryset=VOLTService.get_service_objects().all(), default=get_default_volt_service)
 
         humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
@@ -35,7 +37,7 @@
 
         class Meta:
             model = VOLTTenant
-            fields = ('humanReadableName', 'id', 'provider_service', 'service_specific_id', 'vlan_id', 'computeNodeName' )
+            fields = ('humanReadableName', 'id', 'provider_service', 'service_specific_id', 's_tag', 'c_tag', 'computeNodeName' )
 
         def getHumanReadableName(self, obj):
             return obj.__unicode__()
@@ -62,9 +64,19 @@
         if service_specific_id is not None:
             queryset = queryset.filter(service_specific_id=service_specific_id)
 
-        vlan_id = self.request.QUERY_PARAMS.get('vlan_id', None)
-        if vlan_id is not None:
-            ids = [x.id for x in queryset if x.get_attribute("vlan_id", None)==vlan_id]
+#        vlan_id = self.request.QUERY_PARAMS.get('vlan_id', None)
+#        if vlan_id is not None:
+#            ids = [x.id for x in queryset if x.get_attribute("vlan_id", None)==vlan_id]
+#            queryset = queryset.filter(id__in=ids)
+
+        c_tag = self.request.QUERY_PARAMS.get('c_tag', None)
+        if c_tag is not None:
+            ids = [x.id for x in queryset if x.get_attribute("c_tag", None)==c_tag]
+            queryset = queryset.filter(id__in=ids)
+
+        s_tag = self.request.QUERY_PARAMS.get('s_tag', None)
+        if s_tag is not None:
+            ids = [x.id for x in queryset if x.get_attribute("s_tag", None)==s_tag]
             queryset = queryset.filter(id__in=ids)
 
         return queryset
@@ -74,7 +86,7 @@
 
         existing_obj = None
         for obj in VOLTTenant.get_tenant_objects().all():
-            if (obj.vlan_id == data.get("vlan_id", None)) and (obj.service_specific_id == data.get("service_specific_id",None)):
+            if (obj.c_tag == data.get("c_tag", None)) and (obj.s_tag == data.get("s_tag", None)) and  (obj.service_specific_id == data.get("service_specific_id",None)):
                existing_obj = obj
 
         if existing_obj:
diff --git a/xos/core/xoslib/objects/cordsubscriber.py b/xos/core/xoslib/objects/cordsubscriber.py
index 318d54c..089c91b 100644
--- a/xos/core/xoslib/objects/cordsubscriber.py
+++ b/xos/core/xoslib/objects/cordsubscriber.py
@@ -113,7 +113,9 @@
                      # ("services", "vcpe.services"),
                      # ("cdn_enable", "vcpe.cdn_enable"),
 
-                     ("vlan_id", "volt.vlan_id"),
+                     ("vlan_id", "volt.vlan_id"),      # XXX remove this
+                     ("c_tag", "volt.c_tag"),
+                     ("s_tag", "volt.s_tag"),
 
                      ("bbs_account", "volt.vcpe.bbs_account"),
                      ("ssh_command", "volt.vcpe.ssh_command"),
diff --git a/xos/core/xoslib/templates/xosCordSubscriber.html b/xos/core/xoslib/templates/xosCordSubscriber.html
index b7e2163..db42fb8 100644
--- a/xos/core/xoslib/templates/xosCordSubscriber.html
+++ b/xos/core/xoslib/templates/xosCordSubscriber.html
@@ -7,7 +7,8 @@
   <table class="xos-detail-table cord-subscriber-table">
   <tr><td class="xos-label-cell">Id:</td><td><%= model.attributes.id %></td></tr>
   <tr><td class="xos-label-cell">Service Specific Id:</td><td><%= model.attributes.service_specific_id %></td></tr>

-  <tr><td class="xos-label-cell">VLAN Id:</td><td><%= model.attributes.vlan_id %></td></tr>

+  <tr><td class="xos-label-cell">S-Tag:</td><td><%= model.attributes.s_tag %></td></tr>

+  <tr><td class="xos-label-cell">C-Tag:</td><td><%= model.attributes.c_tag %></td></tr>

   </table>

   </div>

 

diff --git a/xos/model_policies/model_policy_Image.py b/xos/model_policies/model_policy_Image.py
index 72f76fa..c77d5bb 100644
--- a/xos/model_policies/model_policy_Image.py
+++ b/xos/model_policies/model_policy_Image.py
@@ -2,6 +2,10 @@
     from core.models import Controller, ControllerImages, Image
     from collections import defaultdict
 
+    if (image.kind == "container"):
+        # container images do not get instantiated
+        return
+
     controller_images = ControllerImages.objects.filter(image=image)
     existing_controllers = [cs.controller for cs in controller_images] 
     
diff --git a/xos/model_policies/model_policy_Instance.py b/xos/model_policies/model_policy_Instance.py
index a13428d..23761f3 100644
--- a/xos/model_policies/model_policy_Instance.py
+++ b/xos/model_policies/model_policy_Instance.py
@@ -1,3 +1,35 @@
+def handle_container_on_metal(instance):
+        from core.models import Instance, Flavor, Port, Image
+
+        if instance.deleted:
+            return
+
+        # Our current docker network strategy requires that there be some
+        # VM on the server that connects to the networks, so that
+        # the containers can piggyback off of that configuration.
+        if not Instance.objects.filter(slice=instance.slice, node=instance.node, isolation="vm").exists():
+            flavors = Flavor.objects.filter(name="m1.small")
+            if not flavors:
+                raise XOSConfigurationError("No m1.small flavor")
+
+            images = Image.objects.filter(kind="vm")
+
+            companion_instance = Instance(slice = instance.slice,
+                            node = instance.node,
+                            image = images[0],
+                            creator = instance.creator,
+                            deployment = instance.node.site_deployment.deployment,
+                            flavor = flavors[0])
+            companion_instance.save()
+
+        # Add the ports for the container
+        for network in instance.slice.networks.all():
+            if (network.name.endswith("-nat")):
+                continue
+
+            if not Port.objects.filter(network=network, instance=instance).exists():
+                port = Port(network = network, instance=instance)
+                port.save()
 
 def handle(instance):
     from core.models import Controller, ControllerSlice, ControllerNetwork, NetworkSlice
@@ -7,7 +39,10 @@
                                                                 controller=instance.node.site_deployment.controller)
 
     for cn in controller_networks:
-        if (cn.lazy_blocked):	
+        if (cn.lazy_blocked):
 		cn.lazy_blocked=False
 		cn.backend_register = '{}'
 		cn.save()
+
+    if (instance.isolation=="container"):
+        handle_container_on_metal(instance)
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant.py b/xos/observers/vcpe/steps/sync_vcpetenant.py
index 1a45b54..120954b 100644
--- a/xos/observers/vcpe/steps/sync_vcpetenant.py
+++ b/xos/observers/vcpe/steps/sync_vcpetenant.py
@@ -113,15 +113,21 @@
             logger.info("neither bbs_slice nor bbs_server is configured in the vCPE")
 
         vlan_ids = []
+        s_tags = []
+        c_tags = []
         if o.volt:
-            vlan_ids.append(o.volt.vlan_id)
+            vlan_ids.append(o.volt.vlan_id)  # XXX remove this
+            s_tags.append(o.volt.s_tag)
+            c_tags.append(o.volt.c_tag)
 
         try:
             full_setup = Config().observer_full_setup
         except:
             full_setup = True
 
-        fields = {"vlan_ids": vlan_ids,
+        fields = {"vlan_ids": vlan_ids,   # XXX remove this
+                "s_tags": s_tags,
+                "c_tags": c_tags,
                 "dnsdemux_ip": dnsdemux_ip,
                 "cdn_prefixes": cdn_prefixes,
                 "bbs_addrs": bbs_addrs,
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant.yaml b/xos/observers/vcpe/steps/sync_vcpetenant.yaml
index b5a112a..e4d3167 100644
--- a/xos/observers/vcpe/steps/sync_vcpetenant.yaml
+++ b/xos/observers/vcpe/steps/sync_vcpetenant.yaml
@@ -13,6 +13,14 @@
         {% for vlan_id in vlan_ids %}
         - {{ vlan_id }}
         {% endfor %}
+      c_tags:
+        {% for c_tag in c_tags %}
+        - {{ c_tag }}
+        {% endfor %}
+      s_tags:
+        {% for s_tag in s_tags %}
+        - {{ s_tag }}
+        {% endfor %}
       firewall_rules:
         {% for firewall_rule in firewall_rules.split("\n") %}
         - {{ firewall_rule }}
diff --git a/xos/openstack_observer/steps/sync_container.py b/xos/openstack_observer/steps/sync_container.py
index 0f5dcc4..26e03e5 100644
--- a/xos/openstack_observer/steps/sync_container.py
+++ b/xos/openstack_observer/steps/sync_container.py
@@ -8,7 +8,7 @@
 from xos.config import Config
 from observer.syncstep import SyncStep
 from observer.ansible import run_template_ssh
-from core.models import Service, Slice, Container
+from core.models import Service, Slice, Instance
 from services.onos.models import ONOSService, ONOSApp
 from util.logger import Logger, logging
 
@@ -19,34 +19,28 @@
 logger = Logger(level=logging.INFO)
 
 class SyncContainer(SyncStep):
-    provides=[Container]
-    observes=Container
+    provides=[Instance]
+    observes=Instance
     requested_interval=0
     template_name = "sync_container.yaml"
 
     def __init__(self, *args, **kwargs):
         super(SyncContainer, self).__init__(*args, **kwargs)
 
-#    def fetch_pending(self, deleted):
-#        if (not deleted):
-#            objs = ONOSService.get_service_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
-#        else:
-#            objs = ONOSService.get_deleted_service_objects()
-#
-#        return objs
+    def fetch_pending(self, deletion=False):
+        objs = super(SyncContainer, self).fetch_pending(deletion)
+        objs = [x for x in objs if x.isolation=="container"]
+        return objs
 
     def get_node(self,o):
         return o.node
 
     def get_node_key(self, node):
         return "/root/setup/node_key"
-        #return "/opt/xos/node-key"
 
     def get_instance_port(self, container_port):
-        print container_port
-        print container_port.network
         for p in container_port.network.links.all():
-            if (p.instance) and (p.instance.node == container_port.container.node) and (p.mac):
+            if (p.instance) and (p.instance.isolation=="vm") and (p.instance.node == container_port.instance.node) and (p.mac):
                 return p
         return None
 
@@ -81,7 +75,7 @@
         fields["baremetal_ssh"] = True
         fields["instance_name"] = "rootcontext"
         fields["container_name"] = "%s-%s" % (o.slice.name, str(o.id))
-        fields["docker_image"] = o.docker_image
+        fields["docker_image"] = o.image.name
         fields["username"] = "root"
         fields["ports"] = self.get_ports(o)
         fields["volumes"] = [x.strip() for x in o.volumes.split(",")]
diff --git a/xos/openstack_observer/steps/sync_instances.py b/xos/openstack_observer/steps/sync_instances.py
index 1209448..1130c24 100644
--- a/xos/openstack_observer/steps/sync_instances.py
+++ b/xos/openstack_observer/steps/sync_instances.py
@@ -22,6 +22,11 @@
     observes=Instance
     playbook='sync_instances.yaml'
 
+    def fetch_pending(self, deletion=False):
+        objs = super(SyncInstances, self).fetch_pending(deletion)
+        objs = [x for x in objs if x.isolation=="vm"]
+        return objs
+
     def get_userdata(self, instance, pubkeys):
         userdata = '#cloud-config\n\nopencloud:\n   slicename: "%s"\n   hostname: "%s"\n   restapi_hostname: "%s"\n   restapi_port: "%s"\n' % (instance.slice.name, instance.node.name, RESTAPI_HOSTNAME, str(RESTAPI_PORT))
         userdata += 'ssh_authorized_keys:\n'
diff --git a/xos/openstack_observer/steps/sync_ports.py b/xos/openstack_observer/steps/sync_ports.py
index 7b20d29..178fa86 100644
--- a/xos/openstack_observer/steps/sync_ports.py
+++ b/xos/openstack_observer/steps/sync_ports.py
@@ -144,14 +144,11 @@
 
         # For ports that were created by the user, find that ones
         # that don't have neutron ports, and create them.
-        for port in Port.objects.filter(Q(port_id__isnull=True), Q(instance__isnull=False) | Q(container__isnull=False)):
+        for port in Port.objects.filter(Q(port_id__isnull=True), Q(instance__isnull=False) ):
             logger.info("XXX working on port %s" % port)
-            if port.instance:
-                controller = port.instance.node.site_deployment.controller
-                slice = port.instance.slice
-            else:
-                controller = port.container.node.site_deployment.controller
-                slice = port.container.slice
+            controller = port.instance.node.site_deployment.controller
+            slice = port.instance.slice
+
             if controller:
                 cn=port.network.controllernetworks.filter(controller=controller)
                 if not cn:
diff --git a/xos/openstack_observer/templates/start-container.sh.j2 b/xos/openstack_observer/templates/start-container.sh.j2
index dc3b7cb..86491eb 100644
--- a/xos/openstack_observer/templates/start-container.sh.j2
+++ b/xos/openstack_observer/templates/start-container.sh.j2
@@ -6,11 +6,13 @@
 CONTAINER={{ container_name }}
 IMAGE={{ docker_image }}
 
+{% if volumes %}
 {% for volume in volumes %}
 DEST_DIR=/var/container_volumes/$CONTAINER/{{ volume }}
 mkdir -p $DEST_DIR
 VOLUME_ARGS="$VOLUME_ARGS -v $DEST_DIR:{{ volume }}"
 {% endfor %}
+{% endif %}
 
 docker inspect $CONTAINER > /dev/null 2>&1
 if [ "$?" == 1 ]
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index 88d8f5f..aca3e3e 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -236,10 +236,14 @@
             specific vlan_id.
         properties:
             xos_base_tenant_props
-            vlan_id:
+            s_tag:
                 type: string
                 required: false
-                description: vlan_id for connection to subscriber household.
+                description: s_tag, identifies which volt port
+            c_tag:
+                type: string
+                required: false
+                description: c_tag, identifies which subscriber within s_tag
 
     tosca.nodes.User:
         derived_from: tosca.nodes.Root
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 4352ef5..63b4e0c 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -364,10 +364,14 @@
                 type: string
                 required: false
                 description: Service specific ID opaque to XOS but meaningful to service
-            vlan_id:
+            s_tag:
                 type: string
                 required: false
-                description: vlan_id for connection to subscriber household.
+                description: s_tag, identifies which volt port
+            c_tag:
+                type: string
+                required: false
+                description: c_tag, identifies which subscriber within s_tag
 
     tosca.nodes.User:
         derived_from: tosca.nodes.Root
diff --git a/xos/tosca/resources/VOLTTenant.py b/xos/tosca/resources/VOLTTenant.py
index f00b515..89c24e3 100644
--- a/xos/tosca/resources/VOLTTenant.py
+++ b/xos/tosca/resources/VOLTTenant.py
@@ -14,7 +14,7 @@
 class XOSVOLTTenant(XOSResource):
     provides = "tosca.nodes.VOLTTenant"
     xos_model = VOLTTenant
-    copyin_props = ["service_specific_id", "vlan_id"]
+    copyin_props = ["service_specific_id", "s_tag", "c_tag"]
     name_field = None
 
     def get_xos_args(self, throw_exception=True):
diff --git a/xos/tosca/samples/cord.yaml b/xos/tosca/samples/cord.yaml
index 477be2f..567ced0 100644
--- a/xos/tosca/samples/cord.yaml
+++ b/xos/tosca/samples/cord.yaml
@@ -125,7 +125,8 @@
         type: tosca.nodes.VOLTTenant
         properties:
             service_specific_id: 1234
-            vlan_id: 4321
+            s_tag: 222
+            c_tag: 432
         requirements:
             - provider_service:
                 node: service_volt