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/cloudlab-init.sh b/cloudlab-init.sh
new file mode 100755
index 0000000..45ad726
--- /dev/null
+++ b/cloudlab-init.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# This script assumes that XOS is running in a Docker container on the local machine,
+# using an image called 'xos'.
+
+# CHANGE THE FOLLOWING FOR YOUR CLOUDLAB INSTALLATION
+# URL for your XOS installation
+XOS="http://192.168.59.103:8000/"
+# The IP address of the OpenStack head node on CloudLab
+CTL_IP="128.104.222.18"
+# The DNS name of the OpenStack compute node, as shown by 'nova hypervisor-list'
+NODE="cp1.acb-qv7993.planetcloud-pg0.wisc.cloudlab.us"
+# The public key that you want to use to login to instances
+PUBKEY="ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArlgZWcRP75W2/e5bKG1FEeec1OJQuw9dGVyo3TdUgVu4F0/JgBsgR2BrTuQ+mzm+N47ZkSrYwLdAJGuvL7ECxc6aouQ6AtQ/biU1gsrfuPnnUBjfAGqlP/L77lYxpLAPglx/HCCBu53gLKVt8lRDyyGZaWnB7fGlnwrn5AMjcfXsz5Ia8W6oBmxy2fxDSR9SpTs5yAzfcj37mCBtOZBwdjb54B36WpFq9BwFrEXxbvxH4aU0WSneJagicZuCUXnTg2YSURBD0jBmTrYOVRfTZzNPyagOvuIhnnakOSGkGa8s4SrC5zymZsVPdQbp6icRsu6OjKZ83Y0oiTQ4rTaeUw== acb@cadenza.cs.princeton.edu"
+# END CHANGES
+
+AUTH="padmin@vicci.org:letmein"
+CONTAINER=$( docker ps|grep xos|awk '{print $(NF)}' )
+
+# Copy public key
+# BUG: Shouldn't have to set the 'enacted' field...
+http --auth $AUTH PATCH $XOS/xos/users/1/ public_key="$PUBKEY" enacted=$( date "+%Y-%m-%dT%T")
+
+# Fix /etc/hosts, necessary for OpenStack endpoints
+docker exec $CONTAINER bash -c "echo $CTL_IP ctl >> /etc/hosts"
+
+# Set up controller
+http --auth $AUTH POST $XOS/xos/controllers/ name=CloudLab deployment=$XOS/xos/deployments/1/ backend_type=OpenStack version=Juno auth_url="http://ctl:5000/v2.0" admin_user=admin admin_tenant=admin admin_password="N!ceD3m0"
+
+# Add controller to site
+http --auth $AUTH PATCH $XOS/xos/sitedeployments/1/ controller=$XOS/xos/controllers/1/
+
+# Add image
+http --auth $AUTH POST $XOS/xos/images/ name=trusty-server-multi-nic disk_format=QCOW2 container_format=BARE
+
+# Activate image
+http --auth $AUTH POST $XOS/xos/imagedeploymentses/ deployment=$XOS/xos/deployments/1/ image=$XOS/xos/images/1/
+
+# Add node
+http --auth $AUTH POST $XOS/xos/nodes/ name=$NODE site_deployment=$XOS/xos/sitedeployments/1/
+
+# Modify networktemplate/2
+# BUG: Shouldn't have to set the controller_kind field, it's invalid in the initial fixture
+http --auth $AUTH PATCH $XOS/xos/networktemplates/2/ shared_network_name=tun-data-net controller_kind=""
diff --git a/xos/core/admin.py b/xos/core/admin.py
index a83ec7d..82239bd 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -487,6 +487,14 @@
def queryset(self, request):
return Site.select_by_user(request.user)
+class SiteHostsNodesInline(SiteInline):
+ def queryset(self, request):
+ return Site.select_by_user(request.user).filter(hosts_nodes=True)
+
+class SiteHostsUsersInline(SiteInline):
+ def queryset(self, request):
+ return Site.select_by_user(request.user).filter(hosts_users=True)
+
class UserInline(XOSTabularInline):
model = User
fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
@@ -916,7 +924,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 +933,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', )
@@ -1126,7 +1134,7 @@
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'site':
- kwargs['queryset'] = Site.select_by_user(request.user)
+ kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_users=True)
kwargs['widget'] = forms.Select(attrs={'onChange': "update_slice_prefix(this, $($(this).closest('fieldset')[0]).find('.field-name input')[0].id)"})
return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
@@ -1239,6 +1247,9 @@
suit_form_tabs =(('details','Node Details'),('slivers','Slivers'))
+ def formfield_for_foreignkey(self, db_field, request, **kwargs):
+ if db_field.name == 'site':
+ kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_nodes=True)
class SliverForm(forms.ModelForm):
class Meta:
@@ -1452,7 +1463,7 @@
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'site':
- kwargs['queryset'] = Site.select_by_user(request.user)
+ kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_users=True)
return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
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: