Merge branch 'master' of github.com:open-cloud/xos
diff --git a/.gitignore b/.gitignore
index 9fbdd02..b03d344 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
-*orig
-*pyc
+*.orig
+*.pyc
+*.swp
profile
*.moved-aside
diff --git a/xos/core/admin.py b/xos/core/admin.py
index a83ec7d..919952a 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -916,7 +916,7 @@
class SiteAdmin(XOSBaseAdmin):
#fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
- fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'location']
+ fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'login_base', 'location', 'is_public', 'hosts_nodes', 'hosts_users']
fieldsets = [
(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
#('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
@@ -925,7 +925,7 @@
readonly_fields = ['backend_status_text']
#user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
- user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base']
+ user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'hosts_nodes', 'hosts_users']
list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
list_display_links = ('backend_status_icon', 'name', )
diff --git a/xos/core/models/site.py b/xos/core/models/site.py
index 1e8c7ca..e3c275f 100644
--- a/xos/core/models/site.py
+++ b/xos/core/models/site.py
@@ -99,6 +99,8 @@
name = StrippedCharField(max_length=200, help_text="Name for this Site")
site_url = models.URLField(null=True, blank=True, max_length=512, help_text="Site's Home URL Page")
enabled = models.BooleanField(default=True, help_text="Status for this Site")
+ hosts_nodes = models.BooleanField(default=True, help_text="Indicates whether or not the site host nodes")
+ hosts_users = models.BooleanField(default=True, help_text="Indicates whether or not the site manages user accounts")
location = GeopositionField()
longitude = models.FloatField(null=True, blank=True)
latitude = models.FloatField(null=True, blank=True)
diff --git a/xos/core/models/user.py b/xos/core/models/user.py
index 795aa92..fc195d2 100644
--- a/xos/core/models/user.py
+++ b/xos/core/models/user.py
@@ -344,8 +344,94 @@
readable_objects = []
for model in models:
readable_objects.extend(model.select_by_user(self))
- return readable_objects
+ return readable_objects
+ def get_permissions(self, filter=None):
+ """ Return a list of objects for which the user has read or read/write
+ access. The object will be an instance of a django model object.
+ Permissions will be either 'r' or 'rw'.
+
+ e.g.
+ [{'object': django_object_instance, 'permissions': 'rw'}, ...]
+
+ Returns:
+ list of dicts
+
+ """
+ from core.models import *
+ READ = 'r'
+ READWRITE = 'rw'
+
+ deployment_priv_objs = [Image, NetworkTemplate, Flavor]
+ site_priv_objs = [Node, Slice, User]
+ slice_priv_objs = [Sliver, Network]
+
+ # maps the set of objects a paticular role has write access
+ write_map = {
+ DeploymentPrivilege : {
+ 'admin': deployment_priv_objects,
+ },
+ SitePrivilege : {
+ 'admin' : site_priv_objs,
+ 'pi' : [Slice, User],
+ 'tech': [Node],
+ },
+ SlicePrivilege : {
+ 'admin': slice_priv_objs,
+ },
+ }
+
+ privilege_map = {
+ DeploymentPrivilege : (Deployment, deployment_priv_objs),
+ SitePrivilege : (Site, site_priv_objs),
+ SlicePrivilege : (Slice, slice_priv_objs)
+ }
+ permissions = []
+ permission_dict = lambda x,y: {'object': x, 'permission': y}
+ for privilege_model, (model, affected_models) in privileg_map.items():
+ # get the objects affected by this privilege model
+ affected_objects = []
+ for affected_model in affected_models:
+ affected_objects.extend(affected_model.select_by_user(self))
+
+ if self.is_admin:
+ # assume admin users have read/write access to all objects
+ for affected_object in affected_objects:
+ permissions.append(permission_dict(affected_object, READWRITE))
+ else:
+ # create a dict of the user's per object privileges
+ # ex: {princeton_tmack : ['admin']
+ privileges = privilege_model.objects.filter(user=self)
+ for privilege in privileges:
+ object_roles = defaultdict(list)
+ obj = None
+ roles = []
+ for field in dir(privilege):
+ if field == model.__name__.lower():
+ obj = getattr(privilege, field)
+ if obj:
+ object_roles[obj].append(privilege.role.role)
+
+ # loop through all objects the user has access to and determine
+ # if they also have write access
+ for affected_object in affected_objects:
+ if affected_object not in objects_roles:
+ permissions.append(permission_dict(affected_object, READ))
+ else:
+ has_write_permission = False
+ for write_role, models in write_dict.items():
+ if affected_object._meta.model in models and \
+ write_role in object_roles[affected_object]:
+ has_write_permission = True
+ break
+ if has_write_permission:
+ permissions.append(permission_dict(affected_object, WRITE))
+ else:
+ permissions.append(permission_dict(affected_object, READ))
+
+ return permissions
+
+
@staticmethod
def select_by_user(user):
if user.is_admin:
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..2ecf165 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,12 @@
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)
- 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..db65406
--- /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
+ sliver.save()
+
+ self.resource = sliver
+
+ 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/service.py b/xos/tosca/resources/service.py
new file mode 100644
index 0000000..0ef64ba
--- /dev/null
+++ b/xos/tosca/resources/service.py
@@ -0,0 +1,47 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from core.models import Service,User,CoarseTenant
+
+from xosresource import XOSResource
+
+class XOSService(XOSResource):
+ provides = "tosca.nodes.Service"
+
+ def process_nodetemplate(self):
+ nodetemplate = self.nodetemplate
+ serviceName = nodetemplate.name
+
+ existing_services = Service.objects.filter(name=serviceName)
+ if existing_services:
+ self.info("Service %s already exists" % serviceName)
+ service = existing_services[0]
+ else:
+ service = Service(name = serviceName)
+ service.caller = self.user
+ service.save()
+
+ self.info("Created Service '%s'" % (str(service), ))
+
+ for provider_service_name in self.get_requirements("tosca.relationships.TenantOfService"):
+ provider_service = self.get_xos_object(Service, name=provider_service_name)
+
+ existing_tenancy = CoarseTenant.get_tenant_objects().filter(provider_service = provider_service, subscriber_service = service)
+ if existing_tenancy:
+ self.info("Tenancy relationship from %s to %s already exists" % (str(service), str(provider_service)))
+ else:
+ tenancy = CoarseTenant(provider_service = provider_service,
+ subscriber_service = service)
+ tenancy.save()
+
+ self.info("Created Tenancy relationship from %s to %s" % (str(service), str(provider_service)))
+
+ self.resource = service
+
+ 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..bbcd861
--- /dev/null
+++ b/xos/tosca/resources/slice.py
@@ -0,0 +1,39 @@
+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)
+ slice = existing_slices[0]
+ else:
+ 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
+ slice.save()
+
+ self.info("Created Slice '%s' on Site '%s'" % (str(slice), str(site)))
+
+ self.resource = slice
+
+
+ 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..1bb555c
--- /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_requirements(self, relationship_name, throw_exception=False):
+ """ helper to search the list of requirements for a particular relationship
+ type.
+ """
+
+ results = []
+ for reqs in self.nodetemplate.requirements:
+ for (k,v) in reqs.items():
+ if (v["relationship"] == relationship_name):
+ results.append(v["node"])
+
+ if (not results) and throw_exception:
+ raise Exception("Failed to find requirement in %s using relationship %s" % (self.nodetemplate.name, relationship_name))
+
+ return results
+
+ def get_requirement(self, relationship_name, throw_exception=False):
+ reqs = self.get_requirements(relationship_name, throw_exception)
+ if not reqs:
+ return None
+ return reqs[0]
+
+ 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 info(self, s):
+ print s
+
diff --git a/xos/tosca/samples/composition.yaml b/xos/tosca/samples/cord.yaml
similarity index 100%
rename from xos/tosca/samples/composition.yaml
rename to xos/tosca/samples/cord.yaml
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