Merge branch 'master' of github.com:open-cloud/xos
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 87c96b4..b1f6873 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -3,7 +3,10 @@
 node_types:
     tosca.nodes.Service:
         derived_from: tosca.nodes.Root
-   
+
+    tosca.nodes.Site:
+        derived_from: tosca.nodes.Root
+
     tosca.nodes.Slice:
         derived_from: tosca.nodes.Root
         properties:
@@ -17,5 +20,8 @@
     tosca.relationships.MemberOfService:
         derived_from: tosca.relationships.Root
 
+    tosca.relationships.MemberOfSite:
+        derived_from: tosca.relationships.Root
+
     tosca.relationships.TenantOfService:
         derived_from: tosca.relationships.Root
diff --git a/xos/tosca/engine.py b/xos/tosca/engine.py
index 4f74d28..e33d0f0 100644
--- a/xos/tosca/engine.py
+++ b/xos/tosca/engine.py
@@ -9,6 +9,8 @@
 from nodeselect import XOSNodeSelector
 from imageselect import XOSImageSelector
 
+import resources
+
 class XOSTosca(object):
     def __init__(self, tosca_yaml, parent_dir=None):
         # TOSCA will look for imports using a relative path from where the
@@ -32,75 +34,13 @@
         for nodetemplate in self.template.nodetemplates:
             self.execute_nodetemplate(user, nodetemplate)
 
-    def select_compute_node(self, user, v):
-        mem_size = v.get_property_value("mem_size")
-        num_cpus = v.get_property_value("num_cpus")
-        disk_size = v.get_property_value("disk_size")
-
-        # TODO: pick flavor based on parameters
-        flavor = Flavor.objects.get(name="m1.small")
-
-        compute_node = XOSNodeSelector(user, mem_size=mem_size, num_cpus=num_cpus, disk_size=disk_size).get_nodes(1)[0]
-
-        return (compute_node, flavor)
-
-    def select_image(self, user, v):
-        distribution = v.get_property_value("distribution")
-        version = v.get_property_value("version")
-        type = v.get_property_value("type")
-        architecture = v.get_property_value("architecture")
-
-        return XOSImageSelector(user, distribution=distribution, version=version, type=type, architecture=architecture).get_image()
-
     def execute_nodetemplate(self, user, nodetemplate):
-        if (nodetemplate.type == "tosca.nodes.Slice"):
-            return
+        if nodetemplate.type in resources.resources:
+            cls = resources.resources[nodetemplate.type]
+            obj = cls(user, nodetemplate)
+            obj.save_if_dirty()
 
-        if (nodetemplate.type == "tosca.nodes.Service"):
-            return
 
-        if (nodetemplate.type != "tosca.nodes.Compute"):
-            raise Exception("I Don't know how to deal with %s" % nodetemplate.type)
-
-        host=None
-        flavor=None
-        image=None
-
-        sliceName  = None
-        for reqs in nodetemplate.requirements:
-            for (k,v) in reqs.items():
-                print v
-                if (v["relationship"] == "tosca.relationships.MemberOfSlice"):
-                    sliceName = v["node"]
-        if not sliceName:
-             raise Exception("No slice requirement for node %s" % nodetemplate.name)
-
-        slice = Slice.objects.filter(name=sliceName)
-        if not slice:
-             raise Exception("Could not find slice %s" % sliceName)
-        slice = slice[0]
-
-        capabilities = nodetemplate.get_capabilities()
-        for (k,v) in capabilities.items():
-            if (k=="host"):
-                (compute_node, flavor) = self.select_compute_node(user, v)
-            elif (k=="os"):
-                image = self.select_image(user, v)
-
-        if not compute_node:
-            raise Exception("Failed to pick a host")
-        if not image:
-            raise Exception("Failed to pick an image")
-        if not flavor:
-            raise Exception("Failed to pick a flavor")
-
-        sliver = Sliver(deployment = compute_node.site_deployment.deployment,
-                        node = compute_node,
-                        flavor = flavor,
-                        slice = slice,
-                        image = image)
-        sliver.caller = user
-        print "XXX save sliver" #sliver.save()
 
 
 
diff --git a/xos/tosca/resources/__init__.py b/xos/tosca/resources/__init__.py
new file mode 100644
index 0000000..fb0a695
--- /dev/null
+++ b/xos/tosca/resources/__init__.py
@@ -0,0 +1,37 @@
+from xosresource import XOSResource
+from django.conf.urls import patterns, url
+from rest_framework.routers import DefaultRouter
+import os, sys
+import inspect
+import importlib
+
+# XXX based on core/dashboard/views/__init__.py
+
+# Find all modules in the current directory that have descendents of the XOSResource
+# object, and add them as globals to this module. Also, build up a list of urls
+# based on the "url" field of the view classes.
+
+resources = {}
+
+sys_path_save = sys.path
+try:
+    # __import__() and importlib.import_module() both import modules from
+    # sys.path. So we make sure that the path where we can find the views is
+    # the first thing in sys.path.
+    view_dir = os.path.dirname(os.path.abspath(__file__))
+    sys.path = [view_dir] + sys.path
+    view_urls = []
+    for fn in os.listdir(view_dir):
+        pathname = os.path.join(view_dir,fn)
+        if os.path.isfile(pathname) and fn.endswith(".py") and (fn!="__init__.py"):
+            module = __import__(fn[:-3])
+            for classname in dir(module):
+                c = getattr(module, classname, None)
+
+                if inspect.isclass(c) and (getattr(c,"xos_base_class",None)=="XOSResource") and (classname not in globals()):
+                    provides = getattr(c, "provides", None)
+                    if provides:
+                        globals()[classname] = c
+                        resources[provides] = c
+finally:
+    sys.path = sys_path_save
diff --git a/xos/tosca/resources/compute.py b/xos/tosca/resources/compute.py
new file mode 100644
index 0000000..71c7926
--- /dev/null
+++ b/xos/tosca/resources/compute.py
@@ -0,0 +1,76 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from core.models import Slice,Sliver,User,Flavor,Node,Image
+from nodeselect import XOSNodeSelector
+from imageselect import XOSImageSelector
+
+from xosresource import XOSResource
+
+class XOSCompute(XOSResource):
+    provides = "tosca.nodes.Compute"
+
+    def select_compute_node(self, user, v):
+        mem_size = v.get_property_value("mem_size")
+        num_cpus = v.get_property_value("num_cpus")
+        disk_size = v.get_property_value("disk_size")
+
+        # TODO: pick flavor based on parameters
+        flavor = Flavor.objects.get(name="m1.small")
+
+        compute_node = XOSNodeSelector(user, mem_size=mem_size, num_cpus=num_cpus, disk_size=disk_size).get_nodes(1)[0]
+
+        return (compute_node, flavor)
+
+    def select_image(self, user, v):
+        distribution = v.get_property_value("distribution")
+        version = v.get_property_value("version")
+        type = v.get_property_value("type")
+        architecture = v.get_property_value("architecture")
+
+        return XOSImageSelector(user, distribution=distribution, version=version, type=type, architecture=architecture).get_image()
+
+    def process_nodetemplate(self):
+        nodetemplate = self.nodetemplate
+
+        host=None
+        flavor=None
+        image=None
+
+        sliceName = self.get_requirement("tosca.relationships.MemberOfSlice", throw_exception=True)
+        slice = self.get_xos_object(Slice, name=sliceName)
+
+        capabilities = nodetemplate.get_capabilities()
+        for (k,v) in capabilities.items():
+            if (k=="host"):
+                (compute_node, flavor) = self.select_compute_node(self.user, v)
+            elif (k=="os"):
+                image = self.select_image(self.user, v)
+
+        if not compute_node:
+            raise Exception("Failed to pick a host")
+        if not image:
+            raise Exception("Failed to pick an image")
+        if not flavor:
+            raise Exception("Failed to pick a flavor")
+
+        sliver = Sliver(deployment = compute_node.site_deployment.deployment,
+                        node = compute_node,
+                        flavor = flavor,
+                        slice = slice,
+                        image = image)
+        sliver.caller = self.user
+
+        self.resource = sliver
+        self.dirty = True
+
+        self.info("Created Sliver '%s' on node '%s' using flavor '%s' and image '%s'" %
+                  (str(sliver), str(compute_node), str(flavor), str(image)))
+
+    def save(self):
+        self.resource.save()
+
diff --git a/xos/tosca/resources/slice.py b/xos/tosca/resources/slice.py
new file mode 100644
index 0000000..6fdfd35
--- /dev/null
+++ b/xos/tosca/resources/slice.py
@@ -0,0 +1,37 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from core.models import Slice,User,Site
+
+from xosresource import XOSResource
+
+class XOSSlice(XOSResource):
+    provides = "tosca.nodes.Slice"
+
+    def process_nodetemplate(self):
+        nodetemplate = self.nodetemplate
+        sliceName = nodetemplate.name
+
+        existing_slices = Slice.objects.filter(name=sliceName)
+        if existing_slices:
+            self.info("Slice %s already exists" % sliceName)
+
+        site_name = self.get_requirement("tosca.relationships.MemberOfSite", throw_exception=True)
+        site = self.get_xos_object(Site, login_base=site_name)
+
+        slice = Slice(name = sliceName,
+                      site = site)
+        slice.caller = self.user
+
+        self.resource = slice
+        self.dirty = True
+
+        self.info("Created Slice '%s' on Site '%s'" % (str(slice), str(site)))
+
+    def save(self):
+        self.resource.save()
+
diff --git a/xos/tosca/resources/xosresource.py b/xos/tosca/resources/xosresource.py
new file mode 100644
index 0000000..05adcbe
--- /dev/null
+++ b/xos/tosca/resources/xosresource.py
@@ -0,0 +1,44 @@
+class XOSResource(object):
+    xos_base_class = "XOSResource"
+    provides = None
+
+    def __init__(self, user, nodetemplate):
+        self.dirty = False
+        self.user = user
+        self.nodetemplate = nodetemplate
+        self.process_nodetemplate()
+
+    def get_requirement(self, relationship_name, throw_exception=False):
+        """ helper to search the list of requirements for a particular relationship
+            type.
+        """
+        for reqs in self.nodetemplate.requirements:
+            for (k,v) in reqs.items():
+                if (v["relationship"] == relationship_name):
+                    return v["node"]
+
+        if throw_exception:
+            raise Exception("Failed to find requirement in %s using relationship %s" % (self.nodetemplate.name, relationship_name))
+
+        return None
+
+    def get_xos_object(self, cls, **kwargs):
+        objs = cls.objects.filter(**kwargs)
+        if not objs:
+            raise Exception("Failed to find %s filtered by %s" % (cls.__name__, str(kwargs)))
+        return objs[0]
+
+    def process_nodetemplate(self):
+        pass
+
+    def save(self):
+        pass
+
+    def save_if_dirty(self):
+        if self.dirty:
+            self.save()
+            self.dirty=False
+
+    def info(self, s):
+        print s
+
diff --git a/xos/tosca/samples/one_instance.yaml b/xos/tosca/samples/one_instance.yaml
index 968c999..f8919ed 100644
--- a/xos/tosca/samples/one_instance.yaml
+++ b/xos/tosca/samples/one_instance.yaml
@@ -7,8 +7,15 @@
 
 topology_template:
   node_templates:
+    mysite:
+      type: tosca.nodes.Site
+
     mysite_tosca:
       type: tosca.nodes.Slice
+      requirements:
+          - slice:
+                node: mysite
+                relationship: tosca.relationships.MemberOfSite
 
     my_server:
       type: tosca.nodes.Compute