Merge branch 'master' of https://github.com/open-cloud/xos
diff --git a/xos/configurations/cord/cord.yaml b/xos/configurations/cord/cord.yaml
index 9d850b2..c66f7bc 100644
--- a/xos/configurations/cord/cord.yaml
+++ b/xos/configurations/cord/cord.yaml
@@ -40,7 +40,7 @@
 # generate a URL from its IP address
 #          vbng_url: http://10.11.10.24:8181/onos/virtualbng/
 
-    service_ONOS:
+    service_ONOS_vBNG:
       type: tosca.nodes.ONOSService
       requirements:
       properties:
@@ -54,30 +54,33 @@
       type: tosca.nodes.ONOSvBNGApp
       requirements:
           - onos_tenant:
-              node: service_ONOS
+              node: service_ONOS_vBNG
               relationship: tosca.relationships.TenantOfService
           - vbng_service:
               node: service_vbng
               relationship: tosca.relationships.UsedByService
       properties:
           dependencies: org.onosproject.proxyarp, org.onosproject.virtualbng, org.onosproject.openflow, org.onosproject.fwd
-          config_addresses.json: >
+          config_network-cfg.json: >
             {
-                "addresses" : [
-                            {
-                                "dpid" : "00:00:00:00:00:00:00:a1",
-                                "port" : "1",
-                                "ips" : [10.0.1.253/24"],
-                                "mac" : "00:00:00:00:00:99"
-
-                            },
-                            {
-                                "dpid" : "00:00:00:00:00:00:00:a5",
-                                "port" : "2",
-                                "ips" : ["10.254.0.1/24"],
-                                "mac" : "00:00:00:00:00:98"
-                            }
-                ]
+              "ports" : {
+                "of:00000000000000a1/1" : {
+                  "interfaces" : [
+                    {
+                      "ips"  : [ "10.0.1.253/24" ],
+                      "mac"  : "00:00:00:00:00:99"
+                    }
+                  ]
+                },
+                "of:00000000000000a5/2" : {
+                  "interfaces" : [
+                    {
+                      "ips"  : [ "10.254.0.1/24" ],
+                      "mac"  : "00:00:00:00:00:98"
+                    }
+                  ]
+                }
+              }
             }
           config_virtualbng.json: >
             {
@@ -90,6 +93,27 @@
                 "xosRestPort" : "9999"
             }
 
+    service_ONOS_vOLT:
+      type: tosca.nodes.ONOSService
+      requirements:
+      properties:
+          kind: onos
+          view_url: /admin/onos/onosservice/$id$/
+          public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+      artifacts:
+          pubkey: /opt/xos/observers/onos/onos_key.pub
+
+    vOLT_ONOS_app:
+      type: tosca.nodes.ONOSvOLTApp
+      requirements:
+          - onos_tenant:
+              node: service_ONOS_vOLT
+              relationship: tosca.relationships.TenantOfService
+          - volt_service:
+              node: service_volt
+              relationship: tosca.relationships.UsedByService
+      properties:
+          dependencies: org.onosproject.olt
 
     # Network templates
     Private:
@@ -189,12 +213,23 @@
               node: mysite
               relationship: tosca.relationships.MemberOfSite
 
-    mysite_onos:
-      description: ONOS Controller Slice
+    mysite_onos_vbng:
+      description: ONOS Controller Slice for vBNG
       type: tosca.nodes.Slice
       requirements:
           - ONOS:
-              node: service_ONOS
+              node: service_ONOS_vBNG
+              relationship: tosca.relationships.MemberOfService
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
+    mysite_onos_volt:
+      description: ONOS Controller Slice for vOLT
+      type: tosca.nodes.Slice
+      requirements:
+          - ONOS:
+              node: service_ONOS_vOLT
               relationship: tosca.relationships.MemberOfService
           - site:
               node: mysite
@@ -217,7 +252,7 @@
               relationship: tosca.relationships.MemberOfSite
 
     mysite_clients:
-      description: slice for clients at the subscriber 
+      description: slice for clients at the subscriber
       type: tosca.nodes.Slice
       requirements:
           - site:
@@ -245,7 +280,7 @@
             version: 14.10
       requirements:
           - slice:
-                node: mysite_onos
+                node: mysite_onos_vbng
                 relationship: tosca.relationships.MemberOfSlice
 
     onos_app_2:
@@ -267,7 +302,7 @@
             version: 14.10
       requirements:
           - slice:
-                node: mysite_onos
+                node: mysite_onos_volt
                 relationship: tosca.relationships.MemberOfSlice
 
     # VM for running the OVS controlled by vBNG
diff --git a/xos/configurations/opencloud/Dockerfile b/xos/configurations/opencloud/Dockerfile
index 5b09507..5a4db61 100644
--- a/xos/configurations/opencloud/Dockerfile
+++ b/xos/configurations/opencloud/Dockerfile
@@ -3,7 +3,6 @@
 
 # Set environment variables.
 ENV HOME /root
-ENV TOSCA_CONFIG_PATH=/opt/xos/configurations/opencloud/opencloud.tosca
 
 # XXX Workaround for docker bug:
 # https://github.com/docker/docker/issues/6345
@@ -14,23 +13,20 @@
 
 # Install.
 RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \
+    curl \
     git \
-    postgresql \
+    geoip-database \
     graphviz \
     graphviz-dev \
+    libgeoip1 \
     libxslt1.1 \
     libxslt1-dev \
-    tar \
-    gcc \
-    geoip-database \
-    libgeoip1 \
-    openssh-client \
-    wget \
-    curl \
-    python-dev \
     libyaml-dev \
+    nginx \
+    openssh-client \
+    python-dev \
     pkg-config \
-    supervisor \
+    postgresql \
     python-crypto \
     python-httplib2>=0.9.1 \
     python-jinja2 \
@@ -44,7 +40,10 @@
     python-novaclient \
     python-neutronclient \
     python-glanceclient \
-    python-ceilometerclient
+    python-ceilometerclient \
+    supervisor \
+    tar \
+    wget 
 
 RUN pip install \
     django==1.7 \
@@ -67,7 +66,8 @@
     django_rest_swagger \
     python-keyczar \
     pygraphviz \
-    dnslib
+    dnslib \
+    uwsgi
 
 RUN easy_install --upgrade httplib2
 
@@ -84,11 +84,15 @@
 RUN git clone -b release1.8.2 git://github.com/ansible/ansible-modules-extras.git /opt/ansible/v2/ansible/modules/extras
 RUN git clone git://github.com/sb98052/ansible-modules-core.git /opt/ansible/lib/ansible/modules/core
 RUN git clone git://github.com/sb98052/ansible-modules-core.git /opt/ansible/v2/ansible/modules/core
+# git clone uses cached copy, doesn't pick up latest
+RUN git -C /opt/ansible pull
+RUN git -C /opt/ansible/lib/ansible/modules/core pull
+RUN git -C /opt/ansible/v2/ansible/modules/core pull
 ADD ansible-hosts /etc/ansible/hosts
 
-ADD http://code.jquery.com/jquery-1.9.1.min.js /usr/local/lib/python2.7/dist-packages/suit/static/suit/js/
 
 # For Observer
+ADD http://code.jquery.com/jquery-1.9.1.min.js /usr/local/lib/python2.7/dist-packages/suit/static/suit/js/
 RUN git clone git://git.planet-lab.org/fofum.git /tmp/fofum
 RUN cd /tmp/fofum; python setup.py install
 RUN rm -rf /tmp/fofum
@@ -100,16 +104,11 @@
 RUN ln -s /usr/local/share/phantomjs-1.7.0-linux-x86_64 /usr/local/share/phantomjs
 RUN ln -s /usr/local/share/phantomjs/bin/phantomjs /bin/phantomjs
 
-# Supervisor
-ADD observer.conf /etc/supervisor/conf.d/
-
 # Get XOS
 RUN git clone git://github.com/open-cloud/xos.git /tmp/xos && mv /tmp/xos/xos /opt/
 
-# Initscript is broken in Ubuntu
-#ADD observer-initscript /etc/init.d/xosobserver
-
 RUN chmod +x /opt/xos/scripts/opencloud
+
 RUN /opt/xos/scripts/opencloud genkeys
 
 # Workaround for AUFS issue
@@ -126,24 +125,19 @@
 # Cruft to workaround problems with migrations, should go away...
 RUN /opt/xos/scripts/opencloud remigrate
 
-# git clone uses cached copy, doesn't pick up latest
-RUN git -C /opt/ansible pull
-RUN git -C /opt/ansible/lib/ansible/modules/core pull
-RUN git -C /opt/ansible/v2/ansible/modules/core pull
 
 # install Tosca engine
 RUN apt-get install -y m4
 RUN pip install python-dateutil
 RUN bash /opt/xos/tosca/install_tosca.sh
 
+# configure nginx
+RUN cp /opt/xos/nginx/xos.conf /etc/nginx/sites-enabled/default
+
+# Supervisor configuration
+RUN cp /opt/xos/configurations/opencloud/supervisord.conf  /etc/supervisor/conf.d/xos-all.conf
+
 EXPOSE 8000
 
-# Set environment variables.
-ENV HOME /root
-
-# Define working directory.
-WORKDIR /root
-
 # Define default command.
-#CMD ["/bin/bash"]
-CMD /opt/xos/scripts/docker_start_xos
+CMD /usr/bin/supervisord -c /etc/supervisor/conf.d/xos-all.conf
diff --git a/xos/configurations/opencloud/Makefile b/xos/configurations/opencloud/Makefile
index 863f2b7..ab88aea 100644
--- a/xos/configurations/opencloud/Makefile
+++ b/xos/configurations/opencloud/Makefile
@@ -1,16 +1,16 @@
-RUNNING_CONTAINER:=$(shell sudo docker ps|grep "opencloud_server"|awk '{print $$NF}')
+RUNNING_CONTAINER:=$(shell sudo docker ps|grep "opencloud-server"|awk '{print $$NF}')
 
 .PHONY: build
 build: ; docker build --rm -t opencloud .
 
 .PHONY: run
-run: ; docker run --rm  --name opencloud_server opencloud
+run: ; docker run --rm  --name opencloud-server opencloud
 
 .PHONY: runtosca
 runtosca: ; docker exec -it $RUNNING_CONTAINER /usr/bin/python /opt/xos/tosca/run.py padmin@vicci.org $TOSCA_CONFIG_PATH
 
 .PHONY: stop
-stop: ; docker stop opencloud_server
+stop: ; docker stop opencloud-server
 
 .PHONY: rmcontainer
-rmcontainer: ; docker rm opencloud_server
+rmcontainer: ; docker rm opencloud-server
diff --git a/xos/configurations/opencloud/supervisord.conf b/xos/configurations/opencloud/supervisord.conf
new file mode 100644
index 0000000..1fe737d
--- /dev/null
+++ b/xos/configurations/opencloud/supervisord.conf
@@ -0,0 +1,21 @@
+[supervisord]
+nodaemon=true
+logfile=/var/log/supervisord.log
+
+[program:observer]
+autorestart=true
+command=python /opt/xos/xos-observer.py
+stderr_logfile=/var/log/supervisor/observer.err.log
+stdout_logfile=/var/log/supervisor/observer.out.log
+
+program:uwsgi]
+autorestart=true
+command=uwsgi --ini /opt/xos/uwsgi/xos.ini
+stderr_logfile=/var/log/supervisor/uwsgi.err.log
+stdout_logfile=/var/log/supervisor/uwsgi.out.log
+
+[program:nginx]
+command=/usr/sbin/nginx
+autorestart=true
+stderr_logfile=/var/log/supervisor/nginx.err.log
+stdout_logfile=/var/log/supervisor/nginx.out.log
diff --git a/xos/core/admin.py b/xos/core/admin.py
index a5b89be..338f993 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -1372,6 +1372,39 @@
     #    obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
     #    obj.delete()
 
+class ContainerPortInline(XOSTabularInline):
+    fields = ['backend_status_icon', 'network', 'container', 'ip', 'mac', 'segmentation_id']
+    readonly_fields = ("backend_status_icon", "ip", "mac", "segmentation_id")
+    model = Port
+    selflink_fieldname = "network"
+    extra = 0
+    verbose_name_plural = "Ports"
+    verbose_name = "Port"
+    suit_classes = 'suit-tab suit-tab-ports'
+
+class ContainerAdmin(XOSBaseAdmin):
+    fieldsets = [
+        ('Instance Details', {'fields': ['backend_status_text', 'slice', 'node', 'docker_image', 'no_sync'], 'classes': ['suit-tab suit-tab-general'], })
+    ]
+    readonly_fields = ('backend_status_text', )
+    list_display = ['backend_status_icon', 'id']
+    list_display_links = ('backend_status_icon', 'id', )
+
+    suit_form_tabs =(('general', 'Instance Details'), ('ports', 'Ports'))
+
+    inlines = [TagInline, ContainerPortInline]
+
+    def formfield_for_foreignkey(self, db_field, request, **kwargs):
+        if db_field.name == 'slice':
+            kwargs['queryset'] = Slice.select_by_user(request.user)
+
+        return super(ContainerAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
+
+    def queryset(self, request):
+        # admins can see all instances. Users can only see instances of
+        # the slices they belong to.
+        return Container.select_by_user(request.user)
+
 class UserCreationForm(forms.ModelForm):
     """A form for creating new users. Includes all the required
     fields, plus a repeated password."""
@@ -1727,7 +1760,7 @@
     readonly_fields = ('backend_status_icon', )
 
 class NetworkPortInline(XOSTabularInline):
-    fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
+    fields = ['backend_status_icon', 'network', 'instance', 'container', 'ip', 'mac']
     readonly_fields = ("backend_status_icon", "ip", "mac")
     model = Port
     selflink_fieldname = "instance"
@@ -2024,4 +2057,5 @@
     admin.site.register(TenantRoot, TenantRootAdmin)
     admin.site.register(TenantRootRole, TenantRootRoleAdmin)
     admin.site.register(TenantAttribute, TenantAttributeAdmin)
+    admin.site.register(Container, ContainerAdmin)
 
diff --git a/xos/core/models/__init__.py b/xos/core/models/__init__.py
index c380e9c..bc97dab 100644
--- a/xos/core/models/__init__.py
+++ b/xos/core/models/__init__.py
@@ -24,6 +24,7 @@
 from .node import Node
 from .slicetag import SliceTag
 from .instance import Instance
+from .container import Container
 from .reservation import ReservedResource
 from .reservation import Reservation
 from .network import Network, NetworkParameterType, NetworkParameter, Port, NetworkTemplate, Router, NetworkSlice, ControllerNetwork
diff --git a/xos/core/models/container.py b/xos/core/models/container.py
new file mode 100644
index 0000000..151b576
--- /dev/null
+++ b/xos/core/models/container.py
@@ -0,0 +1,83 @@
+import os
+from django.db import models
+from django.db.models import Q
+from django.core import exceptions
+from core.models import PlCoreBase,PlCoreBaseManager,PlCoreBaseDeletionManager
+from core.models.plcorebase import StrippedCharField
+from core.models import Image
+from core.models import Slice, SlicePrivilege
+from core.models import Node
+from core.models import Site
+from core.models import Deployment
+from core.models import Controller
+from core.models import User
+from core.models import Tag
+from core.models import Flavor
+from django.contrib.contenttypes import generic
+from xos.config import Config
+from monitor import driver as monitor
+from django.core.exceptions import PermissionDenied, ValidationError
+
+config = Config()
+
+
+# Create your models here.
+class Container(PlCoreBase):
+    name = StrippedCharField(max_length=200, help_text="Container name")
+    slice = models.ForeignKey(Slice, related_name='containers')
+    node = models.ForeignKey(Node, related_name='containers')
+    creator = models.ForeignKey(User, related_name='containers', blank=True, null=True)
+    docker_image = StrippedCharField(null=True, blank=True, max_length=200, help_text="name of docker container to instantiate")
+
+    def __unicode__(self):
+        return u'container-%s' % str(self.id)
+
+    def save(self, *args, **kwds):
+        if not self.name:
+            self.name = self.slice.name
+        if not self.creator and hasattr(self, 'caller'):
+            self.creator = self.caller
+        if not self.creator:
+            raise ValidationError('container has no creator')
+
+        if (self.slice.creator != self.creator):
+            # Check to make sure there's a slice_privilege for the user. If there
+            # isn't, then keystone will throw an exception inside the observer.
+            slice_privs = SlicePrivilege.objects.filter(slice=self.slice, user=self.creator)
+            if not slice_privs:
+                raise ValidationError('container creator has no privileges on slice')
+
+# XXX smbaker - disabled for now, was causing fault in tenant view create slice
+#        if not self.controllerNetwork.test_acl(slice=self.slice):
+#            raise exceptions.ValidationError("Deployment %s's ACL does not allow any of this slice %s's users" % (self.controllerNetwork.name, self.slice.name))
+
+        super(Container, self).save(*args, **kwds)
+
+    def can_update(self, user):
+        return True
+
+    @staticmethod
+    def select_by_user(user):
+        if user.is_admin:
+            qs = Container.objects.all()
+        else:
+            slices = Slice.select_by_user(user)
+            qs = Container.objects.filter(slice__in=slices)
+        return qs
+
+    def get_public_keys(self):
+        slice_memberships = SlicePrivilege.objects.filter(slice=self.slice)
+        pubkeys = set([sm.user.public_key for sm in slice_memberships if sm.user.public_key])
+
+        if self.creator.public_key:
+            pubkeys.add(self.creator.public_key)
+
+        if self.slice.creator.public_key:
+            pubkeys.add(self.slice.creator.public_key)
+
+        if self.slice.service and self.slice.service.public_key:
+            pubkeys.add(self.slice.service.public_key)
+
+        return pubkeys
+
+
diff --git a/xos/core/models/network.py b/xos/core/models/network.py
index 2bdf17f..48af5a6 100644
--- a/xos/core/models/network.py
+++ b/xos/core/models/network.py
@@ -2,7 +2,7 @@
 import socket
 import sys
 from django.db import models
-from core.models import PlCoreBase, Site, Slice, Instance, Controller
+from core.models import PlCoreBase, Site, Slice, Instance, Controller, Container
 from core.models import ControllerLinkManager,ControllerLinkDeletionManager
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes import generic
@@ -158,7 +158,7 @@
     router_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum router id")
     subnet_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum subnet id")
     subnet = models.CharField(max_length=32, blank=True)
-     
+
     class Meta:
         unique_together = ('network', 'controller')
         
@@ -211,9 +211,12 @@
 class Port(PlCoreBase):
     network = models.ForeignKey(Network,related_name='links')
     instance = models.ForeignKey(Instance, null=True, blank=True, related_name='ports')
+    container = models.ForeignKey(Container, null=True, blank=True, related_name='ports')
     ip = models.GenericIPAddressField(help_text="Instance ip address", blank=True, null=True)
     port_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum port id")
     mac = models.CharField(null=True, blank=True, max_length=256, help_text="MAC address associated with this port")
+    #unattached = models.BooleanField(default=False, help_text="create this port even if no Instance is attached")
+    segmentation_id = models.CharField(null=True, blank=True, max_length=256, help_text="GRE segmentation id for port")
 
     class Meta:
         unique_together = ('network', 'instance')
diff --git a/xos/nginx/xos.conf b/xos/nginx/xos.conf
index 9b5d996..64d9d5e 100644
--- a/xos/nginx/xos.conf
+++ b/xos/nginx/xos.conf
@@ -6,8 +6,9 @@
 
 
 server {
-    listen 80;
-    server_name 128.112.139.48;
+    listen   80;
+    listen   [::]:80 default ipv6only=on; ## listen for ipv6
+    server_name 127.0.0.1;
 
     location /static/ {
         alias /opt/xos/core/static/;
diff --git a/xos/observers/onos/steps/sync_onosapp.yaml b/xos/observers/onos/steps/sync_onosapp.yaml
index 2c5eb0f..ad3718c 100644
--- a/xos/observers/onos/steps/sync_onosapp.yaml
+++ b/xos/observers/onos/steps/sync_onosapp.yaml
@@ -11,39 +11,44 @@
   tasks:
 
   - name: Config file directory
-    file:

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

-      state=directory

-

-  - name: Copy over configuration files

-    copy:

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

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

-    with_items:

-        {% for config_fn in config_fns %}

+    file:
+      path=/home/ubuntu/{{ appname }}/
+      state=directory
+
+{% if config_fns %}
+  - name: Copy over configuration files
+    copy:
+      src={{ files_dir }}/{{ '{{' }} item {{ '}}' }}
+      dest=/home/ubuntu/{{ appname }}/{{ '{{' }} item {{ '}}' }}
+    with_items:
+        {% for config_fn in config_fns %}
         - {{ config_fn }}
-        {% endfor %}

-

-  - name: Copy config files into container

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

-    sudo: yes

-    with_items:

-        {% for config_fn in config_fns %}

+        {% endfor %}
+
+  - name: Make sure config directory exists
+    shell: docker exec {{ ONOS_container }} mkdir -p /root/onos/config/
+    sudo: yes
+
+  - name: Copy config files into container
+    shell: docker cp {{ appname }}/{{ '{{' }} item {{ '}}' }} {{ ONOS_container }}:/root/onos/config/
+    sudo: yes
+    with_items:
+        {% for config_fn in config_fns %}
         - {{ config_fn }}
-        {% endfor %}

-

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

-  - name: Wait for ONOS to install the apps

-    wait_for: timeout=15

-

-  - name: Add dependencies to ONOS

-    uri:

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

-      method: POST

-      user: karaf

-      password: karaf

-    with_items:

-        {% for dependency in dependencies %}

+        {% endfor %}
+{% endif %}
+
+  # Don't know how to check for this condition, just wait
+  - name: Wait for ONOS to install the apps
+    wait_for: timeout=15
+
+  - name: Add dependencies to ONOS
+    uri:
+      url: http://localhost:8181/onos/v1/applications/{{ '{{' }} item {{ '}}' }}/active
+      method: POST
+      user: karaf
+      password: karaf
+    with_items:
+        {% for dependency in dependencies %}
         - {{ dependency }}
-        {% endfor %}

-

+        {% endfor %}
diff --git a/xos/observers/vcpe/vcpe_stats_notifier.py b/xos/observers/vcpe/vcpe_stats_notifier.py
index f4bb923..d726e3c 100644
--- a/xos/observers/vcpe/vcpe_stats_notifier.py
+++ b/xos/observers/vcpe/vcpe_stats_notifier.py
@@ -14,7 +14,11 @@
 level=logging.INFO
 logger=logging.getLogger('vcpe_stats_notifier')
 logger.setLevel(level)
+# create formatter
+formatter = logging.Formatter("%(asctime)s;%(levelname)s;%(message)s")
 handler=logging.handlers.RotatingFileHandler(logfile,maxBytes=1000000, backupCount=1)
+# add formatter to handler
+handler.setFormatter(formatter)
 logger.addHandler(handler)
 
 def extract_dns_stats_from_all_vcpes():
@@ -105,6 +109,9 @@
 def publish_cpe_stats():
      global producer
      global keystone_tenant_id, keystone_user_id, cpe_publisher_id
+
+     logger.debug('publish_cpe_stats invoked')
+
      cpe_container_stats = extract_dns_stats_from_all_vcpes()
 
      for k,v in cpe_container_stats.iteritems():
@@ -119,6 +126,8 @@
                             }
                 }
           producer.publish(msg)
+          logger.debug('Publishing vcpe event: %s', msg)
+
           if 'cache_size' in v:
                msg = {'event_type': 'vcpe.dns.cache.size', 
                       'message_id':six.text_type(uuid.uuid4()),
@@ -132,6 +141,8 @@
                                  }
                      }
                producer.publish(msg)
+               logger.debug('Publishing vcpe.dns.cache.size event: %s', msg)
+
           if 'total_inserted_entries' in v:
                msg = {'event_type': 'vcpe.dns.total_inserted_entries', 
                       'message_id':six.text_type(uuid.uuid4()),
@@ -145,6 +156,8 @@
                                  }
                      }
                producer.publish(msg)
+               logger.debug('Publishing vcpe.dns.total_inserted_entries event: %s', msg)
+
           if 'replaced_unexpired_entries' in v:
                msg = {'event_type': 'vcpe.dns.replaced_unexpired_entries', 
                       'message_id':six.text_type(uuid.uuid4()),
@@ -158,6 +171,7 @@
                                  }
                      }
                producer.publish(msg)
+               logger.debug('Publishing vcpe.dns.replaced_unexpired_entries event: %s', msg)
 
           if 'queries_forwarded' in v:
                msg = {'event_type': 'vcpe.dns.queries_forwarded', 
@@ -172,6 +186,7 @@
                                  }
                      }
                producer.publish(msg)
+               logger.debug('Publishing vcpe.dns.queries_forwarded event: %s', msg)
 
           if 'queries_answered_locally' in v:
                msg = {'event_type': 'vcpe.dns.queries_answered_locally', 
@@ -186,6 +201,7 @@
                                  }
                      }
                producer.publish(msg)
+               logger.debug('Publishing vcpe.dns.queries_answered_locally event: %s', msg)
 
           if 'server_stats' in v:
                for server in v['server_stats']:
@@ -202,6 +218,7 @@
                                      }
                          }
                    producer.publish(msg)
+                   logger.debug('Publishing vcpe.dns.server.queries_sent event: %s', msg)
 
                    msg = {'event_type': 'vcpe.dns.server.queries_failed', 
                           'message_id':six.text_type(uuid.uuid4()),
@@ -216,6 +233,7 @@
                                      }
                          }
                    producer.publish(msg)
+                   logger.debug('Publishing vcpe.dns.server.queries_failed event: %s', msg)
 
 def periodic_publish():
      publish_cpe_stats()
diff --git a/xos/openstack_observer/ansible.py b/xos/openstack_observer/ansible.py
index ee110c9..94b09bd 100644
--- a/xos/openstack_observer/ansible.py
+++ b/xos/openstack_observer/ansible.py
@@ -144,16 +144,21 @@
     return ok_results
 
 def run_template_ssh(name, opts, path='', expected_num=None):
-    instance_id = opts["instance_id"]
     instance_name = opts["instance_name"]
     hostname = opts["hostname"]
     private_key = opts["private_key"]
-    nat_ip = opts["nat_ip"]
-
-    try:
-        proxy_ssh = Config().observer_proxy_ssh
-    except:
-        proxy_ssh = True
+    baremetal_ssh = opts.get("baremetal_ssh",False)
+    if baremetal_ssh:
+        # no instance_id or nat_ip for baremetal
+        # we never proxy to baremetal
+        proxy_ssh = False
+    else:
+        instance_id = opts["instance_id"]
+        nat_ip = opts["nat_ip"]
+        try:
+            proxy_ssh = Config().observer_proxy_ssh
+        except:
+            proxy_ssh = True
 
     (opts, fqp) = get_playbook_fn(opts, path)
     private_key_pathname = fqp + ".key"
@@ -177,7 +182,7 @@
 
     f = open(hosts_pathname, "w")
     f.write("[%s]\n" % instance_name)
-    if proxy_ssh:
+    if proxy_ssh or baremetal_ssh:
         f.write("%s ansible_ssh_private_key_file=%s\n" % (hostname, private_key_pathname))
     else:
         # acb: Login user is hardcoded, this is not great
diff --git a/xos/openstack_observer/steps/sync_container.py b/xos/openstack_observer/steps/sync_container.py
new file mode 100644
index 0000000..c6ac936
--- /dev/null
+++ b/xos/openstack_observer/steps/sync_container.py
@@ -0,0 +1,90 @@
+import hashlib
+import os
+import socket
+import sys
+import base64
+import time
+from django.db.models import F, Q
+from xos.config import Config
+from observer.syncstep import SyncStep
+from observer.ansible import run_template_ssh
+from core.models import Service, Slice, Container
+from services.onos.models import ONOSService, ONOSApp
+from util.logger import Logger, logging
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+logger = Logger(level=logging.INFO)
+
+class SyncContainer(SyncStep):
+    provides=[Container]
+    observes=Container
+    requested_interval=0
+    template_name = "sync_container.yaml"
+
+    def __init__(self, *args, **kwargs):
+        super(SyncContainer, self).__init__(*args, **kwargs)
+
+#    def fetch_pending(self, deleted):
+#        if (not deleted):
+#            objs = ONOSService.get_service_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
+#        else:
+#            objs = ONOSService.get_deleted_service_objects()
+#
+#        return objs
+
+    def get_node(self,o):
+        return o.node
+
+    def get_node_key(self, node):
+        return "/opt/xos/node-key"
+
+    def get_extra_attributes(self, o):
+        fields={}
+        fields["ansible_tag"] = "container-%s" % str(o.id)
+        fields["baremetal_ssh"] = True
+        fields["instance_name"] = "rootcontext"
+        fields["container_name"] = o.name
+        fields["docker_image"] = o.docker_image
+        fields["username"] = "xos"
+        return fields
+
+    def sync_fields(self, o, fields):
+        self.run_playbook(o, fields)
+
+    def sync_record(self, o):
+        logger.info("sync'ing object %s" % str(o))
+
+        node = self.get_node(o)
+        node_key_name = self.get_node_key(node)
+
+        if not os.path.exists(node_key_name):
+            raise Exception("Node key %s does not exist" % node_key_name)
+
+        node_key = file(node_key_name).read()
+
+        fields = { "hostname": node.name,
+                   "private_key": node_key,
+                 }
+
+        # If 'o' defines a 'sync_attributes' list, then we'll copy those
+        # attributes into the Ansible recipe's field list automatically.
+        if hasattr(o, "sync_attributes"):
+            for attribute_name in o.sync_attributes:
+                fields[attribute_name] = getattr(o, attribute_name)
+
+        fields.update(self.get_extra_attributes(o))
+
+        self.sync_fields(o, fields)
+
+        o.save()
+
+    def run_playbook(self, o, fields):
+        tStart = time.time()
+        run_template_ssh(self.template_name, fields, path="container")
+        logger.info("playbook execution time %d" % int(time.time()-tStart))
+
+    def delete_record(self, m):
+        pass
diff --git a/xos/openstack_observer/steps/sync_container.yaml b/xos/openstack_observer/steps/sync_container.yaml
new file mode 100644
index 0000000..d97f0df
--- /dev/null
+++ b/xos/openstack_observer/steps/sync_container.yaml
@@ -0,0 +1,48 @@
+---
+- hosts: {{ instance_name }}
+  gather_facts: False
+  connection: ssh
+  user: {{ username }}
+  sudo: yes
+
+  tasks:
+
+#  - name: Fix /etc/hosts
+#    lineinfile:
+#      dest=/etc/hosts
+#      regexp="127.0.0.1 localhost"
+#      line="127.0.0.1 localhost {{ instance_hostname }}"
+
+  - name: Add repo key
+    apt_key:
+      keyserver=hkp://pgp.mit.edu:80
+      id=58118E89F3A912897C070ADBF76221572C52609D
+
+  - name: Install Docker repo
+    apt_repository:
+      repo="deb https://apt.dockerproject.org/repo ubuntu-trusty main"
+      state=present
+
+  - name: Install Docker
+    apt:
+      name={{ '{{' }} item {{ '}}' }}
+      state=latest
+      update_cache=yes
+    with_items:
+    - docker-engine
+    - python-pip
+    - python-httplib2
+
+  - name: Install docker-py
+    pip:
+      name=docker-py
+      state=latest
+
+  - name: Start Container
+    docker:
+      docker_api_version: "1.18"
+      name: {{ container_name }}
+      # was: reloaded
+      state: running
+      image: {{ docker_image }}
+
diff --git a/xos/openstack_observer/steps/sync_ports.py b/xos/openstack_observer/steps/sync_ports.py
index 967bef9..7b20d29 100644
--- a/xos/openstack_observer/steps/sync_ports.py
+++ b/xos/openstack_observer/steps/sync_ports.py
@@ -144,9 +144,14 @@
 
         # For ports that were created by the user, find that ones
         # that don't have neutron ports, and create them.
-        for port in Port.objects.filter(port_id__isnull=True, instance__isnull=False):
-            #logger.info("XXX working on port %s" % port)
-            controller = port.instance.node.site_deployment.controller
+        for port in Port.objects.filter(Q(port_id__isnull=True), Q(instance__isnull=False) | Q(container__isnull=False)):
+            logger.info("XXX working on port %s" % port)
+            if port.instance:
+                controller = port.instance.node.site_deployment.controller
+                slice = port.instance.slice
+            else:
+                controller = port.container.node.site_deployment.controller
+                slice = port.container.slice
             if controller:
                 cn=port.network.controllernetworks.filter(controller=controller)
                 if not cn:
@@ -174,7 +179,7 @@
                     caller = port.network.owner.creator
                     auth = {'username': caller.email,
                             'password': caller.remote_password,
-                            'tenant': port.instance.slice.name} # port.network.owner.name}
+                            'tenant': slice.name}
                     client = OpenStackClient(controller=controller, **auth) # cacert=self.config.nova_ca_ssl_cert,
                     driver = OpenStackDriver(client=client)
 
@@ -183,6 +188,10 @@
                     if neutron_port["fixed_ips"]:
                         port.ip = neutron_port["fixed_ips"][0]["ip_address"]
                     port.mac = neutron_port["mac_address"]
+
+                    neutron_network = driver.shell.quantum.list_networks(cn.net_id)["networks"][0]
+                    if "provider:segmentation_id" in neutron_network:
+                        port.segmentation_id = neutron_network["provider:segmentation_id"]
                 except:
                     logger.log_exc("failed to create neutron port for %s" % port)
                     continue
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index 055817d..039b433 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -120,6 +120,9 @@
             config_addresses.json:
                 type: string
                 required: false
+            config_network-cfg.json:
+                type: string
+                required: false
             config_virtualbng.json:
                 type: string
                 required: false
@@ -686,4 +689,3 @@
     tosca.capabilities.xos.Image:
         derived_from: tosca.capabilities.Root
         description: An XOS Image
-
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 409e86b..3cbe4b2 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -131,6 +131,9 @@
             config_addresses.json:
                 type: string
                 required: false
+            config_network-cfg.json:
+                type: string
+                required: false
             config_virtualbng.json:
                 type: string
                 required: false
@@ -869,4 +872,3 @@
     tosca.capabilities.xos.Image:
         derived_from: tosca.capabilities.Root
         description: An XOS Image
-
diff --git a/xos/uwsgi/xos.ini b/xos/uwsgi/xos.ini
index da7f7dd..4c3f62c 100644
--- a/xos/uwsgi/xos.ini
+++ b/xos/uwsgi/xos.ini
@@ -2,9 +2,9 @@
 chdir = /opt/xos
 module = xos.wsgi:application
 env = DJANGO_SETTINGS_MODULE=xos.settings
-socket = /var/run/uwsgi/xos.sock
+socket = /var/run/uwsgi.xos.sock
 socket = 127.0.0.1:9001
-http = 128.112.139.48:9002
+http = 127.0.0.1:9002
 stats = 127.0.0.1:9003
 workers = 3
 master = true
@@ -12,7 +12,7 @@
 uid = uwsgi
 gid = webserver 
 harakiri = 20
-daemonize=/var/log/uwsgi/xos.log 
+daemonize=/var/log/uwsgi.xos.log 
 static-map = /static=/var/www/xos/static
-pidfile = /var/run/uwsgi/uwsgi.pid
+pidfile = /var/run/uwsgi.xos.pid
 buffer-size = 8192
diff --git a/xos/xos/settings.py b/xos/xos/settings.py
index e660352..3e64d15 100644
--- a/xos/xos/settings.py
+++ b/xos/xos/settings.py
@@ -120,6 +120,12 @@
 
 # Python dotted path to the WSGI application used by Django's runserver.
 WSGI_APPLICATION = 'xos.wsgi.application'
+# Default: 'csrftoken'
+CSRF_COOKIE_NAME = 'xoscsrftoken'
+# Default: 'django_language'
+LANGUAGE_COOKIE_NAME = 'xos_django_language'
+# Default: 'sessionid'
+SESSION_COOKIE_NAME = 'xossessionid'
 
 TEMPLATE_DIRS = (
     # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".