Merge branch 'master' of https://github.com/open-cloud/xos
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/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 1107a7a..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 helloworld-observer.py  -C $XOS_DIR/observers/hello_world/helloworld_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 81d1333..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:
@@ -59,7 +63,7 @@
             service_specific_id:
                 type: string
                 required: false
-                description: Service specific ID, opaque to XOS but meaningful to service)
+                description: Service specific ID opaque to XOS but meaningful to service)
 define(xos_base_tenant_props,
             kind:
                 type: string
@@ -68,7 +72,7 @@
             service_specific_id:
                 type: string
                 required: false
-                description: Service specific ID, opaque to XOS but meaningful to service)
+                description: Service specific ID opaque to XOS but meaningful to service)
 
 # end m4 macros
 #
@@ -410,6 +414,7 @@
             controller:
                 type: tosca.capabilities.xos.Controller
         properties:
+            xos_base_props
             backend_type:
                 type: string
                 required: false
@@ -447,29 +452,30 @@
             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
@@ -480,6 +486,7 @@
             slice:
                 type: tosca.capabilities.xos.Slice
         properties:
+            xos_base_props
             enabled:
                 type: boolean
                 default: true
@@ -501,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
 
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 9e25754..2a904c1 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -193,7 +193,7 @@
             service_specific_id:
                 type: string
                 required: false
-                description: Service specific ID
+                description: Service specific ID opaque to XOS but meaningful to service
 
     tosca.nodes.CORDSubscriber:
         derived_from: tosca.nodes.Root
@@ -212,7 +212,7 @@
             service_specific_id:
                 type: string
                 required: false
-                description: Service specific ID
+                description: Service specific ID opaque to XOS but meaningful to service
             firewall_enable:
                 type: boolean
                 default: false
@@ -262,7 +262,7 @@
             service_specific_id:
                 type: string
                 required: false
-                description: Service specific ID
+                description: Service specific ID opaque to XOS but meaningful to service
             vlan_id:
                 type: string
                 required: false
@@ -460,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
@@ -499,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
@@ -536,29 +552,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
+            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
@@ -569,6 +597,18 @@
             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
@@ -590,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
 
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'),