Merge branch 'master' of https://github.com/open-cloud/xos into AddHelloWorldService
diff --git a/xos/configurations/common/Makefile.cloudlab b/xos/configurations/common/Makefile.cloudlab
index 929bc14..5712765 100644
--- a/xos/configurations/common/Makefile.cloudlab
+++ b/xos/configurations/common/Makefile.cloudlab
@@ -1,4 +1,4 @@
-all: prereqs admin-openrc flat_name nodes_yaml public_key 
+all: prereqs admin-openrc flat_name nodes_yaml public_key private_key
 
 prereqs:
 	make -f Makefile.prereqs
@@ -7,7 +7,7 @@
 	sudo cat /root/setup/admin-openrc.sh > admin-openrc.sh
 
 flat_name:
-	sudo bash -c "source /root/setup/admin-openrc.sh ; neutron net-list" |grep flat|awk '{print $$4}' > flat_net_name
+	sudo bash -c "source /root/setup/admin-openrc.sh ; neutron net-list" |grep flat|awk '{printf "%s",$$4}' > flat_net_name
 
 nodes_yaml:
 	bash ./make-cloudlab-nodes-yaml.sh
@@ -15,6 +15,8 @@
 public_key: ~/.ssh/id_rsa.pub
 	cp ~/.ssh/id_rsa.pub .
 
+private_key: ~/.ssh/id_rsa
+	cp ~/.ssh/id_rsa .
+
 ~/.ssh/id_rsa.pub:
 	cat /dev/zero | ssh-keygen -q -N ""
-
diff --git a/xos/configurations/cord/Dockerfile.cord b/xos/configurations/cord/Dockerfile.cord
new file mode 100644
index 0000000..dad9895
--- /dev/null
+++ b/xos/configurations/cord/Dockerfile.cord
@@ -0,0 +1,13 @@
+RUN mkdir -p /root/setup
+ADD xos/configurations/common/admin-openrc.sh /root/setup/
+ADD xos/configurations/common/flat_net_name /root/setup/
+ADD xos/configurations/common/cloudlab-nodes.yaml /opt/xos/configurations/commmon/
+ADD xos/configurations/common/id_rsa.pub /root/setup/padmin_public_key
+ADD xos/configurations/common/id_rsa.pub /opt/xos/observers/vcpe/vcpe_public_key
+ADD xos/configurations/common/id_rsa /opt/xos/observers/vcpe/vcpe_private_key
+ADD xos/observers/vcpe/supervisor/vcpe-observer.conf /etc/supervisor/conf.d/
+RUN sed -i 's/proxy_ssh=True/proxy_ssh=False/' /opt/xos/observers/vcpe/vcpe_observer_config
+
+CMD /usr/bin/make -C /opt/xos/configurations/cord -f Makefile.inside; /bin/bash
+
+#CMD ["/bin/bash"]
diff --git a/xos/configurations/cord/Makefile b/xos/configurations/cord/Makefile
new file mode 100644
index 0000000..40ff5a1
--- /dev/null
+++ b/xos/configurations/cord/Makefile
@@ -0,0 +1,22 @@
+MYIP:=$(shell hostname -i)
+RUNNING_CONTAINER:=$(shell sudo docker ps|grep "xos"|awk '{print $$NF}')
+LAST_CONTAINER=$(shell sudo docker ps -l -q)
+
+test: common_cloudlab
+	echo "# Autogenerated -- do not edit" > Dockerfile
+	cat ../common/Dockerfile.common Dockerfile.cord >> Dockerfile
+	cd ../../..; sudo docker build -t xos -f xos/configurations/cord/Dockerfile .
+	sudo docker run -d --add-host="ctl:$(MYIP)" -p 9999:8000 xos
+	bash ../common/wait_for_xos.sh
+
+common_cloudlab:
+	make -C ../common -f Makefile.cloudlab
+
+stop:
+	sudo docker stop $(RUNNING_CONTAINER)
+
+showlogs:
+	sudo docker logs $(LAST_CONTAINER)
+
+enter:
+	sudo docker exec -t -i $(RUNNING_CONTAINER) bash
diff --git a/xos/configurations/cord/Makefile.inside b/xos/configurations/cord/Makefile.inside
new file mode 100644
index 0000000..a4bb5f1
--- /dev/null
+++ b/xos/configurations/cord/Makefile.inside
@@ -0,0 +1,10 @@
+all: setup_xos run_develserver
+
+setup_xos:
+	bash /opt/xos/scripts/docker_setup_xos
+	python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/cloudlab.yaml
+	python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/cloudlab-nodes.yaml
+	python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/cord/cord.yaml
+
+run_develserver:
+	cd /opt/xos; python manage.py runserver 0.0.0.0:8000 --insecure
diff --git a/xos/configurations/cord/README b/xos/configurations/cord/README
new file mode 100644
index 0000000..7c80c0f
--- /dev/null
+++ b/xos/configurations/cord/README
@@ -0,0 +1,4 @@
+This configuration configures XOS with the CORD services.  It is intended to be
+run on CloudLab, on the "ctl" node set up by the OpenStack profile.  It launches
+an XOS container on Cloudlab that runs the XOS develserver.  The container is
+left running in the background.
diff --git a/xos/configurations/cord/cord.yaml b/xos/configurations/cord/cord.yaml
new file mode 100644
index 0000000..56ab11d
--- /dev/null
+++ b/xos/configurations/cord/cord.yaml
@@ -0,0 +1,165 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup CORD-related services -- vOLT, vCPE, vBNG.
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    # CORD Services
+    service_volt:
+      type: tosca.nodes.Service
+      requirements:
+          - vcpe_tenant:
+              node: service_vcpe
+              relationship: tosca.relationships.TenantOfService
+      properties:
+          view_url: /admin/cord/voltservice/$id$/
+          kind: vOLT
+
+    Private:
+      type: tosca.nodes.NetworkTemplate
+
+    # networks required by vCPE
+    lan_network:

+      type: tosca.nodes.network.Network

+      properties:

+          ip_version: 4

+      requirements:

+          - network_template:

+              node: Private

+              relationship: tosca.relationships.UsesNetworkTemplate

+          - owner:

+              node: mysite_vcpe

+              relationship: tosca.relationships.MemberOfSlice

+          - connection:

+              node: mysite_vcpe

+              relationship: tosca.relationships.ConnectsToSlice
+
+    wan_network:
+      type: tosca.nodes.network.Network
+      properties:
+          ip_version: 4
+      requirements:
+          - network_template:
+              node: Private
+              relationship: tosca.relationships.UsesNetworkTemplate
+          - owner:
+              node: mysite_vcpe
+              relationship: tosca.relationships.MemberOfSlice
+          - connection:
+              node: mysite_vcpe
+              relationship: tosca.relationships.ConnectsToSlice
+
+    hpc_client_network:
+      type: tosca.nodes.network.Network
+      properties:
+          ip_version: 4
+      requirements:
+          - network_template:
+              node: Private
+              relationship: tosca.relationships.UsesNetworkTemplate
+          - owner:
+              node: mysite_vcpe
+              relationship: tosca.relationships.MemberOfSlice
+          - connection:
+              node: mysite_vcpe
+              relationship: tosca.relationships.ConnectsToSlice
+
+    service_vcpe:
+      type: tosca.nodes.VCPEService
+      requirements:
+          - vbng_tenant:
+              node: service_vbng
+              relationship: tosca.relationships.TenantOfService
+      properties:
+          view_url: /admin/cord/vcpeservice/$id$/
+          backend_network_label: hpc_client
+          public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+      artifacts:
+          pubkey: /opt/xos/observers/vcpe/vcpe_public_key
+
+    service_vbng:
+      type: tosca.nodes.VBNGService
+      properties:
+          view_url: /admin/cord/vbngservice/$id$/
+          vbng_url: http://10.0.3.136:8181/onos/virtualbng/
+
+    mysite:
+      type: tosca.nodes.Site
+
+    mysite_vcpe:
+      description: vCPE Controller Slice
+      type: tosca.nodes.Slice
+      requirements:
+          - vcpe_service:
+              node: service_vcpe
+              relationship: tosca.relationships.MemberOfService
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+
+    # Now let's add a subscriber
+
+    My House:
+       type: tosca.nodes.CORDSubscriber
+       properties:
+           service_specific_id: 1234
+           firewall_enable: false
+           cdn_enable: false
+           url_filter_enable: false
+           url_filter_level: R
+
+    Mom's PC:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 010203040506
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    Dad's PC:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 90E2Ba82F975
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    Jack's Laptop:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 685B359D91D5
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    Jill's Laptop:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 34363BC9B6A6
+           level: PG_13
+       requirements:
+           - household:
+               node: My House
+               relationship: tosca.relationships.SubscriberDevice
+
+    My Volt:
+        type: tosca.nodes.VOLTTenant
+        properties:
+            service_specific_id: 1234
+            vlan_id: 4321
+        requirements:
+            - provider_service:
+                node: service_volt
+                relationship: tosca.relationships.MemberOfService
+            - subscriber:
+                node: My House
+                relationship: tosca.relationships.BelongsToSubscriber
diff --git a/xos/configurations/devel/Makefile b/xos/configurations/devel/Makefile
index 90dabcc..3ce188b 100644
--- a/xos/configurations/devel/Makefile
+++ b/xos/configurations/devel/Makefile
@@ -6,7 +6,7 @@
 	echo "# Autogenerated -- do not edit" > Dockerfile
 	cat ../common/Dockerfile.common Dockerfile.devel >> Dockerfile
 	cd ../../..; sudo docker build -t xos -f xos/configurations/devel/Dockerfile .
-	sudo docker run -d --add-host="0.0.0.0:$(MYIP)" -p 9999:8000 xos
+	sudo docker run -d --add-host="ctl:$(MYIP)" -p 9999:8000 xos
 	bash ../common/wait_for_xos.sh
 
 common_cloudlab:
diff --git a/xos/configurations/kilo-install/Dockerfile.kilo-install b/xos/configurations/kilo-install/Dockerfile.kilo-install
index e4d301b..fabfcd8 100644
--- a/xos/configurations/kilo-install/Dockerfile.kilo-install
+++ b/xos/configurations/kilo-install/Dockerfile.kilo-install
@@ -1,6 +1,5 @@
 RUN mkdir -p /root/setup
 ADD xos/configurations/common/admin-openrc.sh /root/setup/
-RUN bash -c 'echo "nat-net" > /root/setup/flat_net_name'
 ADD xos/configurations/common/cloudlab-nodes.yaml /opt/xos/configurations/commmon/
 ADD xos/configurations/common/id_rsa.pub /root/setup/padmin_public_key
 
diff --git a/xos/configurations/kilo-install/Makefile b/xos/configurations/kilo-install/Makefile
index 7d11490..108df7a 100644
--- a/xos/configurations/kilo-install/Makefile
+++ b/xos/configurations/kilo-install/Makefile
@@ -2,11 +2,15 @@
 RUNNING_CONTAINER:=$(shell sudo docker ps|grep "xos"|awk '{print $$NF}')
 LAST_CONTAINER=$(shell sudo docker ps -l -q)
 
+# For installing XOS on a node that is not the OpenStack head node
+MGMT_SUBNET="192.168.122.0/24"
+HEAD_NODE_IP="130.127.133.61"
+
 test: common_cloudlab images
 	echo "# Autogenerated -- do not edit" > Dockerfile
 	cat ../common/Dockerfile.common Dockerfile.kilo-install >> Dockerfile
 	cd ../../..; sudo docker build -t xos -f xos/configurations/kilo-install/Dockerfile .
-	sudo docker run -d --add-host="0.0.0.0:$(MYIP)" -p 9999:8000 -v /usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro  xos
+	sudo docker run -d -p 9999:8000 -v /usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro  xos
 	bash ../common/wait_for_xos.sh
 
 common_cloudlab:
@@ -28,3 +32,7 @@
 			--disk-format qcow2 \
 			--file /proj/xos-PG0/acb/images/trusty-server-multi-nic.img \
 			--container-format bare"
+
+remote_head_node:
+	sudo iptables -t nat -A OUTPUT -p tcp -d $MGMT_SUBNET -j DNAT --to-destination $HEAD_NODE_IP
+	sudo iptables -t nat -A PREROUTING -p tcp -d $MGMT_SUBNET -j DNAT --to-destination $HEAD_NODE_IP
diff --git a/xos/configurations/kilo-install/Makefile.inside b/xos/configurations/kilo-install/Makefile.inside
index 40b2672..c49841a 100644
--- a/xos/configurations/kilo-install/Makefile.inside
+++ b/xos/configurations/kilo-install/Makefile.inside
@@ -2,7 +2,7 @@
 
 setup_xos:
 	bash /opt/xos/scripts/docker_setup_xos
-	python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/cloudlab.yaml
+	python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/kilo-install/cloudlab.yaml
 	python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/cloudlab-nodes.yaml
 
 update_certificates:
diff --git a/xos/configurations/kilo-install/cloudlab.yaml b/xos/configurations/kilo-install/cloudlab.yaml
new file mode 100644
index 0000000..f92442c
--- /dev/null
+++ b/xos/configurations/kilo-install/cloudlab.yaml
@@ -0,0 +1,76 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+# Note:
+#   assumes the following have been created and filled with appropriate data:
+#       /root/setup/admin_openrc
+#       /root/setup/flat_net_name
+#       /root/setup/padmin_public_key
+
+description: >
+    * Adds OpenCloud Sites, Deployments, and Controllers.
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    trusty-server-multi-nic:
+      type: tosca.nodes.Image
+      properties:
+         disk_format: QCOW2
+         container_format: BARE
+
+    MyDeployment:
+      type: tosca.nodes.Deployment
+      properties:
+          flavors: m1.large, m1.medium, m1.small
+      requirements:
+          - image:
+              node: trusty-server-multi-nic
+              relationship: tosca.relationships.SupportsImage
+
+    CloudLab:
+      type: tosca.nodes.Controller
+      requirements:
+          - deployment:
+              node: MyDeployment
+              relationship: tosca.relationships.ControllerDeployment
+      properties:
+          backend_type: OpenStack
+          version: Juno
+          auth_url: { get_script_env: [ SELF, adminrc, OS_AUTH_URL, LOCAL_FILE] }
+          admin_user: { get_script_env: [ SELF, adminrc, OS_USERNAME, LOCAL_FILE] }
+          admin_password: { get_script_env: [ SELF, adminrc, OS_PASSWORD, LOCAL_FILE] }
+          admin_tenant: { get_script_env: [ SELF, adminrc, OS_TENANT_NAME, LOCAL_FILE] }
+          domain: Default
+      artifacts:
+          adminrc: /root/setup/admin-openrc.sh
+
+    mysite:
+      type: tosca.nodes.Site
+      properties:
+          display_name: MySite
+          site_url: http://opencloud.us/
+      requirements:
+          - deployment:
+               node: MyDeployment
+               relationship: tosca.relationships.SiteDeployment
+               requirements:
+                   - controller:
+                       node: CloudLab
+                       relationship: tosca.relationships.UsesController
+
+    padmin@vicci.org:
+      type: tosca.nodes.User
+      requirements:
+          - site:
+              node: mysite
+              relationship: tosca.relationships.MemberOfSite
+      properties:
+          public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE ] }
+          is_admin: true
+          is_active: true
+          firstname: XOS
+          lastname: admin
+      artifacts:
+          pubkey: /root/setup/padmin_public_key
diff --git a/xos/configurations/opencloud/Dockerfile b/xos/configurations/opencloud/Dockerfile
index ad85718..5b09507 100644
--- a/xos/configurations/opencloud/Dockerfile
+++ b/xos/configurations/opencloud/Dockerfile
@@ -31,8 +31,8 @@
     libyaml-dev \
     pkg-config \
     supervisor \
-    python-crypto
-    python-httplib2 \
+    python-crypto \
+    python-httplib2>=0.9.1 \
     python-jinja2 \
     python-paramiko \
     python-pip \
@@ -47,7 +47,7 @@
     python-ceilometerclient
 
 RUN pip install \
-    django==1.7
+    django==1.7 \
     djangorestframework==2.4.4 \
     markdown  \ 
     pyyaml \ 
@@ -69,13 +69,14 @@
     pygraphviz \
     dnslib
 
+RUN easy_install --upgrade httplib2
+
 RUN easy_install \
-    django_evolution
+    django_evolution \
     python_gflags \
     google_api_python_client \
     httplib2.ca_certs_locater
 
-RUN easy_install --upgrade httplib2
 
 # Install custom Ansible
 RUN git clone -b release1.8.2 git://github.com/ansible/ansible.git /opt/ansible
@@ -103,7 +104,7 @@
 ADD observer.conf /etc/supervisor/conf.d/
 
 # Get XOS
-ADD xos /opt/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
@@ -134,7 +135,6 @@
 RUN apt-get install -y m4
 RUN pip install python-dateutil
 RUN bash /opt/xos/tosca/install_tosca.sh
-RUN /usr/bin/python /opt/xos/tosca/run.py padmin@vicci.org ${TOSCA_CONFIG_PATH}
 
 EXPOSE 8000
 
diff --git a/xos/configurations/opencloud/Makefile b/xos/configurations/opencloud/Makefile
index aba30dc..863f2b7 100644
--- a/xos/configurations/opencloud/Makefile
+++ b/xos/configurations/opencloud/Makefile
@@ -1,8 +1,13 @@
+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 -d -e TOSCA_CONFIG_PATH=$TOSCA_CONFIG_PATH --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
diff --git a/xos/configurations/opencloud/ansible-hosts b/xos/configurations/opencloud/ansible-hosts
new file mode 100644
index 0000000..0dd74f1
--- /dev/null
+++ b/xos/configurations/opencloud/ansible-hosts
@@ -0,0 +1,2 @@
+[localhost]
+127.0.0.1
diff --git a/xos/configurations/opencloud/observer.conf b/xos/configurations/opencloud/observer.conf
new file mode 100644
index 0000000..92545eb
--- /dev/null
+++ b/xos/configurations/opencloud/observer.conf
@@ -0,0 +1,2 @@
+[program:observer]
+command=python /opt/xos/xos-observer.py
diff --git a/xos/core/fixtures/initial_data.json b/xos/core/fixtures/initial_data.json
index 213245c..86658bb 100644
--- a/xos/core/fixtures/initial_data.json
+++ b/xos/core/fixtures/initial_data.json
@@ -334,7 +334,7 @@
         "translation": "none",
         "backend_status": "0 - Provisioning in progress",
         "shared_network_name": null,
-        "controller_kind": "none",
+        "controller_kind": null,
         "enacted": null
     },
     "model": "core.networktemplate",
@@ -356,7 +356,7 @@
         "translation": "NAT",
         "backend_status": "0 - Provisioning in progress",
         "shared_network_name": "nat-net",
-        "controller_kind": "none",
+        "controller_kind": null,
         "enacted": null
     },
     "model": "core.networktemplate",
@@ -378,7 +378,7 @@
         "translation": "none",
         "backend_status": "0 - Provisioning in progress",
         "shared_network_name": "ext-net",
-        "controller_kind": "none",
+        "controller_kind": null,
         "enacted": null
     },
     "model": "core.networktemplate",
diff --git a/xos/core/models/controlleruser.py b/xos/core/models/controlleruser.py
index 6b11b24..1031e12 100644
--- a/xos/core/models/controlleruser.py
+++ b/xos/core/models/controlleruser.py
@@ -26,7 +26,7 @@
         if user.is_admin:
             qs = ControllerUser.objects.all()
         else:
-            users = Users.select_by_user(user)
+            users = User.select_by_user(user)
             qs = ControllerUser.objects.filter(user__in=users)
         return qs
 
diff --git a/xos/core/models/flavor.py b/xos/core/models/flavor.py
index 3d6b9bb..8251eb1 100644
--- a/xos/core/models/flavor.py
+++ b/xos/core/models/flavor.py
@@ -18,6 +18,10 @@
         app_label = "core"
         ordering = ('order', 'name')
 
+    def __init__(self, *args, **kwargs):
+        super(Flavor, self).__init__(*args, **kwargs)
+        self.no_sync=True
+
     def __unicode__(self):  return u'%s' % (self.name)
 
 """ FlavorParameterType and FlavorParameter are below for completeness sake,
diff --git a/xos/core/models/network.py b/xos/core/models/network.py
index fe60fce..2bdf17f 100644
--- a/xos/core/models/network.py
+++ b/xos/core/models/network.py
@@ -7,6 +7,7 @@
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes import generic
 from django.core.exceptions import ValidationError
+from django.db.models import Q
 
 # If true, then IP addresses will be allocated by the model. If false, then
 # we will assume the observer handles it.
@@ -203,7 +204,8 @@
             qs = NetworkSlice.objects.all()
         else:
             slice_ids = [s.id for s in Slice.select_by_user(user)]
-            qs = NetworkSlice.objects.filter(id__in=slice_ids)
+            network_ids = [network.id for network in Network.select_by_user(user)]
+            qs = NetworkSlice.objects.filter(Q(slice__in=slice_ids) | Q(network__in=network_ids))
         return qs
 
 class Port(PlCoreBase):
@@ -246,8 +248,11 @@
         if user.is_admin:
             qs = Port.objects.all()
         else:
-            instance_ids = [s.id for s in Port.select_by_user(user)]
-            qs = Port.objects.filter(id__in=instance_ids)
+            instances = Instance.select_by_user(user)
+            instance_ids = [instance.id for instance in instances]
+            networks = Network.select_by_user(user)
+            network_ids = [network.id for network in networks]
+            qs = Port.objects.filter(Q(instance__in=instance_ids) | Q(network__in=network_ids))
         return qs
 
 class Router(PlCoreBase):
diff --git a/xos/core/models/site.py b/xos/core/models/site.py
index 42855a9..26ff191 100644
--- a/xos/core/models/site.py
+++ b/xos/core/models/site.py
@@ -168,6 +168,9 @@
     #    given a default of 'allow site <site_of_creator>'
     accessControl = models.TextField(max_length=200, blank=False, null=False, default="allow all",
                                      help_text="Access control list that specifies which sites/users may use nodes in this deployment")
+    def __init__(self, *args, **kwargs):
+        super(Deployment, self).__init__(*args, **kwargs)
+        self.no_sync=True
 
     def get_acl(self):
         return AccessControlList(self.accessControl)
@@ -261,7 +264,10 @@
     admin_tenant = StrippedCharField(max_length=200, null=True, blank=True, help_text="Name of the tenant the admin user belongs to")
     domain = StrippedCharField(max_length=200, null=True, blank=True, help_text="Name of the domain this controller belongs to")
     deployment = models.ForeignKey(Deployment,related_name='controllerdeployments')
-   
+
+    def __init__(self, *args, **kwargs):
+        super(Controller, self).__init__(*args, **kwargs)
+        self.no_sync=True
 
     def __unicode__(self):  return u'%s %s %s' % (self.name, self.backend_type, self.version)
 
diff --git a/xos/helloworld/view.py b/xos/helloworld/view.py
new file mode 100644
index 0000000..b3eec29
--- /dev/null
+++ b/xos/helloworld/view.py
@@ -0,0 +1,58 @@
+from django.http import HttpResponse
+from django.views.generic import TemplateView, View
+from django import template
+from monitor import driver
+from core.models import *
+from helloworld.models import *
+import json
+import os
+import time
+import tempfile
+
+class HelloWorldView(TemplateView):
+    head_template = r"""{% extends "admin/dashboard/dashboard_base.html" %}
+       {% load admin_static %}
+       {% block content %}
+    """
+
+    tail_template = r"{% endblock %}"
+
+    def get(self, request, name="root", *args, **kwargs):
+        head_template = self.head_template
+        tail_template = self.tail_template
+
+        try:
+            hello_name = request.GET['hello_name']
+            world_name = request.GET['world_name']
+            instance_id_str = request.GET['instance_id']
+            instance_id = int(instance_id_str)
+
+            i = Instance.objects.get(pk=instance_id)
+            i.pk=None
+            i.userData=None
+            i.instance_id=None
+            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.save()
+            w.save()
+
+            t = template.Template(head_template + 'Done. New instance id: %r'%i.pk + self.tail_template)
+        except KeyError:
+            html = """<form>
+                Hello string: <input type="text" name="hello_name" placeholder="Planet"><br>
+                World string: <input type="text" name="world_name" placeholder="Earth"><br>
+                Id of instance to copy: <input type="text" name="instance_id" placeholder="3"><br>
+                <input type="submit" value="Submit">
+                  </form>"""
+
+            t = template.Template(head_template + html + self.tail_template)
+
+        response_kwargs = {}
+        response_kwargs.setdefault('content_type', self.content_type)
+        return self.response_class(
+            request = request,
+            template = t,
+            **response_kwargs)
diff --git a/xos/observers/hello_world/run.sh b/xos/observers/hello_world/run.sh
deleted file mode 100755
index f77d751..0000000
--- a/xos/observers/hello_world/run.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#if [[ ! -e ./hpc-backend.py ]]; then
-#    ln -s ../xos-observer.py hpc-backend.py
-#fi
-
-export XOS_DIR=/opt/xos
-python hpc-observer.py  -C $XOS_DIR/observers/hpc/hpc_observer_config
diff --git a/xos/observers/hello_world/helloworld-observer.py b/xos/observers/helloworld/helloworld-observer.py
similarity index 100%
rename from xos/observers/hello_world/helloworld-observer.py
rename to xos/observers/helloworld/helloworld-observer.py
diff --git a/xos/observers/hello_world/helloworld_config b/xos/observers/helloworld/helloworld_config
similarity index 94%
rename from xos/observers/hello_world/helloworld_config
rename to xos/observers/helloworld/helloworld_config
index 97248ae..671af51 100644
--- a/xos/observers/hello_world/helloworld_config
+++ b/xos/observers/helloworld/helloworld_config
@@ -37,7 +37,7 @@
 images_directory=/opt/xos/images
 dependency_graph=/opt/xos/model-deps
 logfile=/var/log/xos_backend.log
-steps_dir=/opt/xos/observers/hello_world/steps
+steps_dir=/opt/xos/observers/helloworld/steps
 
 [gui]
 disable_minidashboard=True
diff --git a/xos/observers/hello_world/model-deps b/xos/observers/helloworld/model-deps
similarity index 100%
rename from xos/observers/hello_world/model-deps
rename to xos/observers/helloworld/model-deps
diff --git a/xos/observers/hello_world/nohup.out b/xos/observers/helloworld/nohup.out
similarity index 100%
rename from xos/observers/hello_world/nohup.out
rename to xos/observers/helloworld/nohup.out
diff --git a/xos/observers/helloworld/run.sh b/xos/observers/helloworld/run.sh
new file mode 100755
index 0000000..f56ffe3
--- /dev/null
+++ b/xos/observers/helloworld/run.sh
@@ -0,0 +1,6 @@
+#if [[ ! -e ./hpc-backend.py ]]; then
+#    ln -s ../xos-observer.py hpc-backend.py
+#fi
+
+export XOS_DIR=/opt/xos
+python helloworld-observer.py  -C $XOS_DIR/observers/helloworld/helloworld_config
diff --git a/xos/observers/hello_world/start.sh b/xos/observers/helloworld/start.sh
similarity index 100%
rename from xos/observers/hello_world/start.sh
rename to xos/observers/helloworld/start.sh
diff --git a/xos/observers/hello_world/steps/sync_hello.py b/xos/observers/helloworld/steps/sync_hello.py
similarity index 65%
rename from xos/observers/hello_world/steps/sync_hello.py
rename to xos/observers/helloworld/steps/sync_hello.py
index f59ec5c..1fb8c2b 100644
--- a/xos/observers/hello_world/steps/sync_hello.py
+++ b/xos/observers/helloworld/steps/sync_hello.py
@@ -18,7 +18,9 @@
     requested_interval=0
     
     def sync_record(self, record):
-        open('/tmp/hello-synchronizer','w').write(record.name)	
+        instance = record.sliver_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()
         
     def delete_record(self, m):
         return
diff --git a/xos/observers/hello_world/steps/sync_world.py b/xos/observers/helloworld/steps/sync_world.py
similarity index 100%
rename from xos/observers/hello_world/steps/sync_world.py
rename to xos/observers/helloworld/steps/sync_world.py
diff --git a/xos/observers/hello_world/stop.sh b/xos/observers/helloworld/stop.sh
similarity index 100%
rename from xos/observers/hello_world/stop.sh
rename to xos/observers/helloworld/stop.sh
diff --git a/xos/openstack_observer/steps/sync_controller_networks.py b/xos/openstack_observer/steps/sync_controller_networks.py
index 1e7805f..ef238ee 100644
--- a/xos/openstack_observer/steps/sync_controller_networks.py
+++ b/xos/openstack_observer/steps/sync_controller_networks.py
@@ -69,7 +69,7 @@
         if (controller_network.network.template.name!='Private'):
             logger.info("skipping network controller %s because it is not private" % controller_network)
             # We only sync private networks
-            return
+            return SyncStep.SYNC_WITHOUT_RUNNING
         
         if not controller_network.controller.admin_user:
             logger.info("controller %r has no admin_user, skipping" % controller_network.controller)
diff --git a/xos/openstack_observer/steps/sync_instances.py b/xos/openstack_observer/steps/sync_instances.py
index 7aa4bb7..1209448 100644
--- a/xos/openstack_observer/steps/sync_instances.py
+++ b/xos/openstack_observer/steps/sync_instances.py
@@ -56,10 +56,12 @@
                                                                 controller=instance.node.site_deployment.controller)
 
         for controller_network in controller_networks:
+
+            # Lenient exception - causes slow backoff
             if controller_network.network.template.visibility == 'private' and \
                controller_network.network.template.translation == 'none':
                    if not controller_network.net_id:
-                        raise Exception("Private Network %s has no id; Try again later" % controller_network.network.name)
+                        raise DeferredException("Private Network %s has no id; Try again later" % controller_network.network.name)
                    nics.append(controller_network.net_id)
 
         # now include network template
@@ -108,7 +110,7 @@
 
         userData = self.get_userdata(instance, pubkeys)
         if instance.userData:
-            userData = instance.userData
+            userData += instance.userData
 
         controller = instance.node.site_deployment.controller
         fields = {'endpoint':controller.auth_url,
diff --git a/xos/openstack_observer/syncstep.py b/xos/openstack_observer/syncstep.py
index 66225d0..7accbfa 100644
--- a/xos/openstack_observer/syncstep.py
+++ b/xos/openstack_observer/syncstep.py
@@ -45,6 +45,12 @@
         psmodel        Model name the step synchronizes
         dependencies    list of names of models that must be synchronized first if the current model depends on them
     """
+
+    # map_sync_outputs can return this value to cause a step to be marked
+    # successful without running ansible. Used for sync_network_controllers
+    # on nat networks.
+    SYNC_WITHOUT_RUNNING = "sync_without_running"
+
     slow=False
     def get_prop(self, prop):
         try:
@@ -128,6 +134,8 @@
             pass
 
         tenant_fields = self.map_sync_inputs(o)
+        if tenant_fields == SyncStep.SYNC_WITHOUT_RUNNING:
+            return
         main_objs=self.observes
         if (type(main_objs) is list):
             main_objs=main_objs[0]
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index b929eda..00805e7 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -12,7 +12,11 @@
             no-create:
                 type: boolean
                 default: false
-                description: Do not allow Tosca to create this object)
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object)
 # Service
 define(xos_base_service_caps,
             scalable:
@@ -55,16 +59,20 @@
             kind:
                 type: string
                 default: generic
+                description: Kind of subscriber
             service_specific_id:
                 type: string
-                required: false)
+                required: false
+                description: Service specific ID opaque to XOS but meaningful to service)
 define(xos_base_tenant_props,
             kind:
                 type: string
                 default: generic
+                description: Kind of tenant
             service_specific_id:
                 type: string
-                required: false)
+                required: false
+                description: Service specific ID opaque to XOS but meaningful to service)
 
 # end m4 macros
 #
@@ -157,6 +165,9 @@
             CORD: User. The CORD user represents an individual device beloning
             to the CORD Subscriber. Each device may have its own parental
             controls.
+        capabilities:
+            device:
+                type: tosca.capabilities.xos.Device
         properties:
             level:
                 type: string
@@ -257,7 +268,7 @@
             topology_kind:
                 type: string
                 default: BigSwitch
-                descrption: Describes the topology of the network.
+                description: Describes the topology of the network.
             controller_kind:
                 type: string
                 required: false
@@ -269,7 +280,8 @@
           # using derived_from.
           derived_from: tosca.nodes.Root
           description: >

-            The TOSCA Network node represents a simple, logical network service.

+            This is a variant of the TOSCA Network object that includes additional

+            XOS-specific properties.

           properties:

             ip_version:

               type: integer

@@ -402,6 +414,7 @@
             controller:
                 type: tosca.capabilities.xos.Controller
         properties:
+            xos_base_props
             backend_type:
                 type: string
                 required: false
@@ -439,39 +452,41 @@
             site:
                 type: tosca.capabilities.xos.Site
         properties:
-             display_name:
-                 type: string
-                 required: false
-                 description: Name of the site.
-             site_url:
-                 type: string
-                 required: false
-                 description: URL of site web page.
-             enabled:
-                 type: boolean
-                 default: true
-             hosts_nodes:
-                 type: boolean
-                 default: true
-                 description: If True, then this site hosts nodes where Instances may be instantiated.
-             hosts_users:
-                 type: boolean
-                 default: true
-                 description: If True, then this site hosts users who may use XOS.
-             is_public:
-                 type: boolean
-                 default: true
-             # location, longitude, latitude
+            xos_base_props
+            display_name:
+                type: string
+                required: false
+                description: Name of the site.
+            site_url:
+                type: string
+                required: false
+                description: URL of site web page.
+            enabled:
+                type: boolean
+                default: true
+            hosts_nodes:
+                type: boolean
+                default: true
+                description: If True, then this site hosts nodes where Instances may be instantiated.
+            hosts_users:
+                type: boolean
+                default: true
+                description: If True, then this site hosts users who may use XOS.
+            is_public:
+                type: boolean
+                default: true
+            # location, longitude, latitude
 
     tosca.nodes.Slice:
         derived_from: tosca.nodes.Root
         description: >
             An XOS Slice. A slice is a collection of instances that share
             common attributes.
-        capability:
+        capabilities:
             slice:
                 type: tosca.capabilities.xos.Slice
         properties:
+            xos_base_props
             enabled:
                 type: boolean
                 default: true
@@ -493,7 +508,9 @@
         description: >
             An XOS Node. Nodes are physical machines that host virtual machines
             and/or containers.
-        capability:
+        properties:
+            xos_base_props
+        capabilities:
             node:
                 type: tosca.capabilities.xos.Node
 
@@ -539,6 +556,7 @@
 
     tosca.relationships.ConnectsToSlice:
         derived_from: tosca.relationships.Root
+        valid_target_types: [ tosca.capabilities.xos.Slice ]
 
     #    tosca.relationships.OwnsNetwork:
     #        derived_from: tosca.relationships.Root
@@ -550,25 +568,27 @@
 
     tosca.relationships.AdminPrivilege:
         derived_from: tosca.relationships.Root
-        valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabiltys.xos.Site ]
+        valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabilities.xos.Site ]
 
     tosca.relationships.AccessPrivilege:
         derived_from: tosca.relationships.Root
-        valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabiltys.xos.Site ]
+        valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabilities.xos.Site ]
 
     tosca.relationships.PIPrivilege:
         derived_from: tosca.relationships.Root
-        valid_target_types: [ tosca.capabiltys.xos.Site ]
+        valid_target_types: [ tosca.capabilities.xos.Site ]
 
     tosca.relationships.TechPrivilege:
         derived_from: tosca.relationships.Root
-        valid_target_types: [ tosca.capabiltys.xos.Site ]
+        valid_target_types: [ tosca.capabilities.xos.Site ]
 
     tosca.relationships.SubscriberDevice:
         derived_from: tosca.relationships.Root
+        valid_target_types: [ tosca.capabilities.xos.Subscriber ]
 
     tosca.relationships.BelongsToSubscriber:
         derived_from: tosca.relationships.Root
+        valid_target_types: [ tosca.capabilities.xos.Subscriber ]
 
     tosca.capabilities.xos.Service:
         derived_from: tosca.capabilities.Root
@@ -606,6 +626,10 @@
         derived_from: tosca.capabilities.Root
         description: An XOS Subscriber
 
+    tosca.capabilities.xos.Device:
+        derived_from: tosca.capabilities.Root
+        description: A device belonging to an XOS subscriber
+
     tosca.capabilities.xos.Node:
         derived_from: tosca.capabilities.Root
         description: An XOS Node
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 2c674eb..2a904c1 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -189,9 +189,11 @@
             kind:
                 type: string
                 default: generic
+                description: Kind of subscriber
             service_specific_id:
                 type: string
                 required: false
+                description: Service specific ID opaque to XOS but meaningful to service
 
     tosca.nodes.CORDSubscriber:
         derived_from: tosca.nodes.Root
@@ -206,9 +208,11 @@
             kind:
                 type: string
                 default: generic
+                description: Kind of subscriber
             service_specific_id:
                 type: string
                 required: false
+                description: Service specific ID opaque to XOS but meaningful to service
             firewall_enable:
                 type: boolean
                 default: false
@@ -232,6 +236,9 @@
             CORD: User. The CORD user represents an individual device beloning
             to the CORD Subscriber. Each device may have its own parental
             controls.
+        capabilities:
+            device:
+                type: tosca.capabilities.xos.Device
         properties:
             level:
                 type: string
@@ -251,9 +258,11 @@
             kind:
                 type: string
                 default: generic
+                description: Kind of tenant
             service_specific_id:
                 type: string
                 required: false
+                description: Service specific ID opaque to XOS but meaningful to service
             vlan_id:
                 type: string
                 required: false
@@ -337,7 +346,7 @@
             topology_kind:
                 type: string
                 default: BigSwitch
-                descrption: Describes the topology of the network.
+                description: Describes the topology of the network.
             controller_kind:
                 type: string
                 required: false
@@ -349,7 +358,8 @@
           # using derived_from.
           derived_from: tosca.nodes.Root
           description: >

-            The TOSCA Network node represents a simple, logical network service.

+            This is a variant of the TOSCA Network object that includes additional

+            XOS-specific properties.

           properties:

             ip_version:

               type: integer

@@ -450,6 +460,10 @@
                 type: boolean
                 default: false
                 description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object
             accessControl:
                 type: string
                 default: allow all
@@ -489,6 +503,18 @@
             controller:
                 type: tosca.capabilities.xos.Controller
         properties:
+            no-delete:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to delete this object
+            no-create:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object
             backend_type:
                 type: string
                 required: false
@@ -526,39 +552,63 @@
             site:
                 type: tosca.capabilities.xos.Site
         properties:
-             display_name:
-                 type: string
-                 required: false
-                 description: Name of the site.
-             site_url:
-                 type: string
-                 required: false
-                 description: URL of site web page.
-             enabled:
-                 type: boolean
-                 default: true
-             hosts_nodes:
-                 type: boolean
-                 default: true
-                 description: If True, then this site hosts nodes where Instances may be instantiated.
-             hosts_users:
-                 type: boolean
-                 default: true
-                 description: If True, then this site hosts users who may use XOS.
-             is_public:
-                 type: boolean
-                 default: true
-             # location, longitude, latitude
+            no-delete:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to delete this object
+            no-create:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object
+            display_name:
+                type: string
+                required: false
+                description: Name of the site.
+            site_url:
+                type: string
+                required: false
+                description: URL of site web page.
+            enabled:
+                type: boolean
+                default: true
+            hosts_nodes:
+                type: boolean
+                default: true
+                description: If True, then this site hosts nodes where Instances may be instantiated.
+            hosts_users:
+                type: boolean
+                default: true
+                description: If True, then this site hosts users who may use XOS.
+            is_public:
+                type: boolean
+                default: true
+            # location, longitude, latitude
 
     tosca.nodes.Slice:
         derived_from: tosca.nodes.Root
         description: >
             An XOS Slice. A slice is a collection of instances that share
             common attributes.
-        capability:
+        capabilities:
             slice:
                 type: tosca.capabilities.xos.Slice
         properties:
+            no-delete:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to delete this object
+            no-create:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object
             enabled:
                 type: boolean
                 default: true
@@ -580,7 +630,20 @@
         description: >
             An XOS Node. Nodes are physical machines that host virtual machines
             and/or containers.
-        capability:
+        properties:
+            no-delete:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to delete this object
+            no-create:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object
+        capabilities:
             node:
                 type: tosca.capabilities.xos.Node
 
@@ -626,6 +689,7 @@
 
     tosca.relationships.ConnectsToSlice:
         derived_from: tosca.relationships.Root
+        valid_target_types: [ tosca.capabilities.xos.Slice ]
 
     #    tosca.relationships.OwnsNetwork:
     #        derived_from: tosca.relationships.Root
@@ -637,25 +701,27 @@
 
     tosca.relationships.AdminPrivilege:
         derived_from: tosca.relationships.Root
-        valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabiltys.xos.Site ]
+        valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabilities.xos.Site ]
 
     tosca.relationships.AccessPrivilege:
         derived_from: tosca.relationships.Root
-        valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabiltys.xos.Site ]
+        valid_target_types: [ tosca.capabilities.xos.Slice, tosca.capabilities.xos.Site ]
 
     tosca.relationships.PIPrivilege:
         derived_from: tosca.relationships.Root
-        valid_target_types: [ tosca.capabiltys.xos.Site ]
+        valid_target_types: [ tosca.capabilities.xos.Site ]
 
     tosca.relationships.TechPrivilege:
         derived_from: tosca.relationships.Root
-        valid_target_types: [ tosca.capabiltys.xos.Site ]
+        valid_target_types: [ tosca.capabilities.xos.Site ]
 
     tosca.relationships.SubscriberDevice:
         derived_from: tosca.relationships.Root
+        valid_target_types: [ tosca.capabilities.xos.Subscriber ]
 
     tosca.relationships.BelongsToSubscriber:
         derived_from: tosca.relationships.Root
+        valid_target_types: [ tosca.capabilities.xos.Subscriber ]
 
     tosca.capabilities.xos.Service:
         derived_from: tosca.capabilities.Root
@@ -693,6 +759,10 @@
         derived_from: tosca.capabilities.Root
         description: An XOS Subscriber
 
+    tosca.capabilities.xos.Device:
+        derived_from: tosca.capabilities.Root
+        description: A device belonging to an XOS subscriber
+
     tosca.capabilities.xos.Node:
         derived_from: tosca.capabilities.Root
         description: An XOS Node
diff --git a/xos/tosca/definitions/TOSCA_definition_1_0.yaml b/xos/tosca/definitions/TOSCA_definition_1_0.yaml
index c21f0bc..97a8c44 100644
--- a/xos/tosca/definitions/TOSCA_definition_1_0.yaml
+++ b/xos/tosca/definitions/TOSCA_definition_1_0.yaml
@@ -1,4 +1,4 @@
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 #    not use this file except in compliance with the License. You may obtain
 #    a copy of the License at
 #
@@ -47,6 +47,11 @@
 
 tosca.nodes.Compute:
   derived_from: tosca.nodes.Root
+  description: >
+    The TOSCA Compute node represents one or more real or virtual processors
+    of software applications or services along with other essential local
+    resources.  Collectively, the resources the compute node represents can
+    logically be viewed as a (real or virtual) server.
   attributes:
     private_address:
       type: string
diff --git a/xos/tosca/makedocs.py b/xos/tosca/makedocs.py
index f7dc11b..6a9e959 100644
--- a/xos/tosca/makedocs.py
+++ b/xos/tosca/makedocs.py
@@ -24,8 +24,14 @@
     def __init__(self, fn="./custom_types/xos.yaml", templatedir="./doctemplates/html", templatename="toscadoctemplate.html", destfn="tosca_reference.html"):
         self.env = jinja2.Environment(loader=jinja2.FileSystemLoader(templatedir))
 
+        self.node_types = {}
+        self.root_types = yaml.load(file("definitions/TOSCA_definition_1_0.yaml").read())
+        for x in self.root_types.keys():
+            if x in ["tosca.nodes.Compute", "tosca.nodes.network.Network"]:
+                self.node_types[x] = self.root_types[x]
+
         self.custom_types = yaml.load(file(fn).read())
-        self.node_types = self.custom_types.get("node_types")
+        self.node_types.update(self.custom_types.get("node_types"))
 
         self.destfn = destfn
         self.templatename = templatename
diff --git a/xos/tosca/resources/controller.py b/xos/tosca/resources/controller.py
index da4ed64..9a20ea5 100644
--- a/xos/tosca/resources/controller.py
+++ b/xos/tosca/resources/controller.py
@@ -44,6 +44,10 @@
         if obj.controllersite.exists():
             self.info("Controller %s has active sites; skipping delete" % obj.name)
             return
+        for sd in obj.sitedeployments.all():
+            if sd.nodes.exists():
+                self.info("Controller %s has active nodes; skipping delete" % obj.name)
+                return
         super(XOSController, self).delete(obj)
 
 
diff --git a/xos/tosca/resources/deployment.py b/xos/tosca/resources/deployment.py
index 152b1f9..ed6734c 100644
--- a/xos/tosca/resources/deployment.py
+++ b/xos/tosca/resources/deployment.py
@@ -51,13 +51,13 @@
         self.postprocess_privileges(DeploymentRole, DeploymentPrivilege, rolemap, obj, "deployment")
 
     def delete(self, obj):
-        if self.get_property("no-delete"):
-            self.info("Deployment %s is marked no-delete")
-            return
-
         if obj.sites.exists():
             self.info("Deployment %s has active sites; skipping delete" % obj.name)
             return
+        for sd in obj.sitedeployments.all():
+            if sd.nodes.exists():
+                self.info("Deployment %s has active nodes; skipping delete" % obj.name)
+                return
         #if obj.nodes.exists():
         #    self.info("Deployment %s has active nodes; skipping delete" % obj.name)
         #    return
diff --git a/xos/tosca/resources/xosresource.py b/xos/tosca/resources/xosresource.py
index 62f18db..3553ab1 100644
--- a/xos/tosca/resources/xosresource.py
+++ b/xos/tosca/resources/xosresource.py
@@ -60,6 +60,12 @@
     def get_property(self, name):
         return self.nodetemplate.get_property_value(name)
 
+    def get_property_default(self, name, default=None):
+        props = self.nodetemplate.get_properties()
+        if props and name in props.keys():

+            return props[name].value
+        return default
+
     def get_xos_object(self, cls, throw_exception=True, **kwargs):
         objs = cls.objects.filter(**kwargs)
         if not objs:
@@ -80,12 +86,21 @@
     def create_or_update(self):
         existing_objs = self.get_existing_objs()
         if existing_objs:
-            self.info("%s %s already exists" % (self.get_model_class_name(), self.nodetemplate.name))
-            self.update(existing_objs[0])
+            if self.get_property_default("no-update", False):
+                self.info("%s %s already exists. Skipping update due to 'no-update' property" % (self.get_model_class_name(), self.nodetemplate.name))
+            else:
+                self.info("%s %s already exists" % (self.get_model_class_name(), self.nodetemplate.name))
+                self.update(existing_objs[0])
         else:
-            self.create()
+            if self.get_property_default("no-create", False):
+                self.info("%s %s does not exist, but 'no-create' is specified" % (self.get_model_class_name(), self.nodetemplate.name))
+            else:
+                self.create()
 
     def can_delete(self, obj):
+        if self.get_property_default("no-delete",False):
+            self.info("%s %s is marked 'no-delete'. Skipping delete." % (self.get_model_class_name(), self.nodetemplate.name))
+            return False
         return True
 
     def postprocess_privileges(self, roleclass, privclass, rolemap, obj, toFieldName):
diff --git a/xos/tosca/tests/controllertest.py b/xos/tosca/tests/controllertest.py
index 5746ada..2b7ba55 100644
--- a/xos/tosca/tests/controllertest.py
+++ b/xos/tosca/tests/controllertest.py
@@ -5,7 +5,9 @@
 class ControllerTest(BaseToscaTest):
     tests = ["create_controller_minimal",
              "create_controller_maximal",
-             "destroy_controller"]
+             "create_controller_nocreate",
+             "destroy_controller",
+             "destroy_controller_nodelete"]
 
     def cleanup(self):
         self.try_to_delete(Controller, name="testcon")
@@ -53,6 +55,45 @@
                         domain="mydomain",
                         deployment=dep)
 
+    def create_controller_nocreate(self):
+        self.assert_noobj(Controller, "testcon")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+                                            reqs=[("testdep", "tosca.relationships.ControllerDeployment")],
+                                            props={"no-create": True}))
+        dep = self.assert_obj(Deployment, "testdep")
+        self.assert_noobj(Controller, "testcon")
+
+    def update_controller(self):
+        self.assert_noobj(Controller, "testcon")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+                                            reqs=[("testdep", "tosca.relationships.ControllerDeployment")]))
+        dep = self.assert_obj(Deployment, "testdep")
+        orig_con = self.assert_obj(Controller, "testcon",
+                        backend_type="",
+                        version="",
+                        auth_url=None,
+                        admin_user=None,
+                        admin_password=None,
+                        admin_tenant=None,
+                        domain=None,
+                        deployment=dep)
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+                                            reqs=[("testdep", "tosca.relationships.ControllerDeployment")],
+                                            props={"version": "1.1"}))
+        con = self.assert_obj(Controller, "testcon",
+                        backend_type="",
+                        version="1.1",
+                        auth_url=None,
+                        admin_user=None,
+                        admin_password=None,
+                        admin_tenant=None,
+                        domain=None,
+                        deployment=dep)
+        assert(orig_con.id == con.id)
+
     def destroy_controller(self):
         self.assert_noobj(Controller, "testcon")
         self.execute(self.get_base_templates() +
@@ -64,6 +105,24 @@
                                             reqs=[("testdep", "tosca.relationships.ControllerDeployment")]))
         self.assert_noobj(Controller, "testcon")
 
+    def destroy_controller_nodelete(self):
+        self.assert_noobj(Controller, "testcon")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+                                            reqs=[("testdep", "tosca.relationships.ControllerDeployment")]))
+        orig_con = self.assert_obj(Controller, "testcon")
+        # NOTE: Had to specify no-delete on the deployment as well, otherwise
+        # the deployment deletion would cause the controller to be deleted
+        # as well. I'm thinking this is as it should be, but it's a little
+        # counter-inutitive.
+        self.destroy(self.make_nodetemplate("testdep", "tosca.nodes.Deployment",
+                                            props={"no-delete": True}) +
+                     self.make_nodetemplate("testcon", "tosca.nodes.Controller",
+                                            reqs=[("testdep", "tosca.relationships.ControllerDeployment")],
+                                            props={"no-delete": True}))
+        con = self.assert_obj(Controller, "testcon")
+        assert(orig_con.id == con.id)
+
 if __name__ == "__main__":
     ControllerTest()
 
diff --git a/xos/tosca/tests/deploymenttest.py b/xos/tosca/tests/deploymenttest.py
index 0156613..91caf75 100644
--- a/xos/tosca/tests/deploymenttest.py
+++ b/xos/tosca/tests/deploymenttest.py
@@ -10,6 +10,9 @@
              "create_deployment_one_image",
              "create_deployment_two_images",
              "create_deployment_privilege",
+             "create_deployment_nocreate",
+             "update_deployment",
+             "update_deployment_noupdate",
              "destroy_deployment",
              "destroy_deployment_nodelete"
                            ]
@@ -88,6 +91,35 @@
         dps = DeploymentPrivilege.objects.filter(user=user, deployment=dep)
         assert(len(dps) == 1)
 
+    def create_deployment_nocreate(self):
+        self.assert_noobj(Deployment, "testdep")
+        self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment",
+                                            props={"no-create": True}))
+        self.assert_noobj(Deployment, "testdep")
+
+    def update_deployment(self):
+        self.assert_noobj(Deployment, "testdep")
+        self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment"))
+        orig_dep = self.assert_obj(Deployment, "testdep",
+                                   accessControl="allow all")
+        self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment",
+                                            props={"accessControl": "allow padmin@vicci.org"}))
+        dep = self.assert_obj(Deployment, "testdep",
+                                   accessControl="allow padmin@vicci.org")
+        assert(dep.id == orig_dep.id)
+
+    def update_deployment_noupdate(self):
+        self.assert_noobj(Deployment, "testdep")
+        self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment"))
+        orig_dep = self.assert_obj(Deployment, "testdep",
+                                   accessControl="allow all")
+        self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment",
+                                            props={"accessControl": "allow padmin@vicci.org",
+                                                   "no-update": True}))
+        dep = self.assert_obj(Deployment, "testdep",
+                                   accessControl="allow all")
+        assert(dep.id == orig_dep.id)
+
     def destroy_deployment(self):
         self.assert_noobj(Deployment, "testdep")
         self.execute(self.make_nodetemplate("testdep", "tosca.nodes.Deployment"))
diff --git a/xos/tosca/tests/nodetest.py b/xos/tosca/tests/nodetest.py
index d49dab1..76c56a8 100644
--- a/xos/tosca/tests/nodetest.py
+++ b/xos/tosca/tests/nodetest.py
@@ -4,7 +4,9 @@
 
 class NodeTest(BaseToscaTest):
     tests = ["create_node_minimal",
+             "create_node_nocreate",
              "destroy_node",
+             "destroy_node_nodelete",
                            ]
 
     def cleanup(self):
@@ -43,7 +45,18 @@
                      self.make_nodetemplate("testnode", "tosca.nodes.Node",
                        reqs=[("testsite", "tosca.relationships.MemberOfSite"),
                              ("testdep", "tosca.relationships.MemberOfDeployment")]))
-        self.assert_obj(Node, "testnode")
+        node = self.assert_obj(Node, "testnode")
+        assert(node.site_deployment is not None)
+        assert(node.site is not None)
+
+    def create_node_nocreate(self):
+        self.assert_noobj(Node, "testnode")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testnode", "tosca.nodes.Node",
+                       reqs=[("testsite", "tosca.relationships.MemberOfSite"),
+                             ("testdep", "tosca.relationships.MemberOfDeployment")],
+                       props={"no-create": True}))
+        self.assert_noobj(Node, "testnode")
 
     def destroy_node(self):
         self.assert_noobj(Node, "testnode")
@@ -58,6 +71,20 @@
                              ("testdep", "tosca.relationships.MemberOfDeployment")]))
         self.assert_noobj(Node, "testnode")
 
+    def destroy_node_nodelete(self):
+        self.assert_noobj(Node, "testnode")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testnode", "tosca.nodes.Node",
+                       reqs=[("testsite", "tosca.relationships.MemberOfSite"),
+                             ("testdep", "tosca.relationships.MemberOfDeployment")]))
+        self.assert_obj(Node, "testnode")
+        self.destroy(self.get_base_templates() +
+                     self.make_nodetemplate("testnode", "tosca.nodes.Node",
+                       reqs=[("testsite", "tosca.relationships.MemberOfSite"),
+                             ("testdep", "tosca.relationships.MemberOfDeployment")],
+                       props={"no-delete": True}))
+        self.assert_obj(Node, "testnode")
+
 if __name__ == "__main__":
     NodeTest()
 
diff --git a/xos/tosca/tests/sitetest.py b/xos/tosca/tests/sitetest.py
index c9a4743..5321159 100644
--- a/xos/tosca/tests/sitetest.py
+++ b/xos/tosca/tests/sitetest.py
@@ -7,7 +7,11 @@
              "create_site_privilege_tech",
              "create_site_privilege_admin",
              "create_site_privilege_pi",
+             "create_site_nocreate",
+             "update_site",
+             "update_site_noupdate",
              "destroy_site",
+             "destroy_site_nodelete"
                            ]
 
     def cleanup(self):
@@ -60,6 +64,31 @@
         assert(len(sps) == 1)
         assert(sps[0].role.role == "pi")
 
+    def create_site_nocreate(self):
+        self.assert_noobj(Site, "testsite")
+        self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site",
+                                            props={"no-create": True}))
+        site = self.assert_noobj(Site, "testsite")
+
+    def update_site(self):
+        self.assert_noobj(Site, "testsite")
+        self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
+        orig_site = self.assert_obj(Site, "testsite", site_url=None)
+        self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site",
+                                            props={"site_url": "http://foo.com/"}))
+        site = self.assert_obj(Site, "testsite", site_url="http://foo.com/")
+        assert(orig_site.id == site.id)
+
+    def update_site_noupdate(self):
+        self.assert_noobj(Site, "testsite")
+        self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
+        orig_site = self.assert_obj(Site, "testsite", site_url=None)
+        self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site",
+                                            props={"site_url": "http://foo.com/",
+                                                   "no-update": True}))
+        site = self.assert_obj(Site, "testsite", site_url=None)
+        assert(orig_site.id == site.id)
+
     def destroy_site(self):
         self.assert_noobj(Site, "testsite")
         self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
@@ -67,6 +96,14 @@
         self.destroy(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
         self.assert_noobj(Site, "testsite")
 
+    def destroy_site_nodelete(self):
+        self.assert_noobj(Site, "testsite")
+        self.execute(self.make_nodetemplate("testsite", "tosca.nodes.Site"))
+        site = self.assert_obj(Site, "testsite")
+        self.destroy(self.make_nodetemplate("testsite", "tosca.nodes.Site",
+                                            props={"no-delete": True}))
+        self.assert_obj(Site, "testsite")
+
 if __name__ == "__main__":
     SiteTest()
 
diff --git a/xos/tosca/tests/slicetest.py b/xos/tosca/tests/slicetest.py
index 315f862..98de4e6 100644
--- a/xos/tosca/tests/slicetest.py
+++ b/xos/tosca/tests/slicetest.py
@@ -6,7 +6,11 @@
     tests = ["create_slice_minimal",
              "create_slice_maximal",
              "create_slice_privilege",
-             "destroy_slice"]
+             "create_slice_nocreate",
+             "update_slice",
+             "update_slice_noupdate",
+             "destroy_slice",
+             "destroy_slice_nodelete"]
 
     def cleanup(self):
         self.try_to_delete(Slice, name="testsite_testslice")
@@ -43,6 +47,41 @@
         dps = SlicePrivilege.objects.filter(user=user, slice=slice)
         assert(len(dps) == 1)
 
+    def create_slice_nocreate(self):
+        self.assert_noobj(Slice, "testsite_testslice")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+                                            reqs=[("testsite", "tosca.relationships.MemberOfSite")],
+                                            props={"no-create": True}))
+        self.assert_noobj(Slice, "testsite_testslice")
+
+    def update_slice(self):
+        self.assert_noobj(Slice, "testsite_testslice")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+                                            reqs=[("testsite", "tosca.relationships.MemberOfSite")]))
+        orig_slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+                                            reqs=[("testsite", "tosca.relationships.MemberOfSite")],
+                                            props={"description": "foo"}))
+        slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="foo", slice_url="", max_instances=10)
+        assert(orig_slice.id == slice.id)
+
+    def update_slice_noupdate(self):
+        self.assert_noobj(Slice, "testsite_testslice")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+                                            reqs=[("testsite", "tosca.relationships.MemberOfSite")]))
+        orig_slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+                                            reqs=[("testsite", "tosca.relationships.MemberOfSite")],
+                                            props={"description": "foo",
+                                                   "no-update": True}))
+        slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+        assert(orig_slice.id == slice.id)
+
     def destroy_slice(self):
         self.assert_noobj(Slice, "testsite_testslice")
         self.execute(self.get_base_templates() +
@@ -54,6 +93,19 @@
                                             reqs=[("testsite", "tosca.relationships.MemberOfSite")]))
         self.assert_noobj(Slice, "testsite_testslice")
 
+    def destroy_slice_nodelete(self):
+        self.assert_noobj(Slice, "testsite_testslice")
+        self.execute(self.get_base_templates() +
+                     self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+                                            reqs=[("testsite", "tosca.relationships.MemberOfSite")]))
+        orig_slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+        self.destroy(self.get_base_templates() +
+                     self.make_nodetemplate("testsite_testslice", "tosca.nodes.Slice",
+                                            reqs=[("testsite", "tosca.relationships.MemberOfSite")],
+                                            props={"no-delete": True}))
+        slice = self.assert_obj(Slice, "testsite_testslice", enabled=True, description="", slice_url="", max_instances=10)
+        assert(slice.id == orig_slice.id)
+
 if __name__ == "__main__":
     SliceTest()
 
diff --git a/xos/xos/urls.py b/xos/xos/urls.py
index 0adf32d..6ba98d8 100644
--- a/xos/xos/urls.py
+++ b/xos/xos/urls.py
@@ -8,6 +8,7 @@
 
 from core.views.legacyapi import LegacyXMLRPC
 from core.views.services import ServiceGridView, ServiceGraphView
+from helloworld.view import *
 #from core.views.analytics import AnalyticsAjaxView
 from core.models import *
 from rest_framework import generics
@@ -27,6 +28,7 @@
     # Examples:
     url(r'^stats', 'core.views.stats.Stats', name='stats'),
     url(r'^observer', 'core.views.observer.Observer', name='observer'),
+    url(r'^helloworld', HelloWorldView.as_view(), name='helloWorld'),
     url(r'^serviceGrid', ServiceGridView.as_view(), name='serviceGrid'),
     url(r'^serviceGraph.png', ServiceGraphView.as_view(), name='serviceGraph'),
     url(r'^hpcConfig', 'core.views.hpc_config.HpcConfig', name='hpcConfig'),