Django-suit, add in Roles for specific classes site, slice, deployment, planetstack, change admin to leverage suit options
diff --git a/planetstack/core/admin.py b/planetstack/core/admin.py
index a1a21d6..730937a 100644
--- a/planetstack/core/admin.py
+++ b/planetstack/core/admin.py
@@ -12,12 +12,19 @@
 from django.contrib.auth.signals import user_logged_in
 from django.utils import timezone
 from django.contrib.contenttypes import generic
+from suit.widgets import LinkedSelect
 
 import django_evolution 
 
 class PlStackTabularInline(admin.TabularInline):
     exclude = ['enacted']
 
+class ReservationInline(PlStackTabularInline):
+    model = Reservation
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-reservations'
+
+
 class ReadonlyTabularInline(PlStackTabularInline):
     can_delete = False
     extra = 0
@@ -34,10 +41,27 @@
     def has_add_permission(self, request):
         return False
 
+class UserMembershipInline(generic.GenericTabularInline):
+    model = Member
+    exclude = ['enacted']
+    extra = 1
+    suit_classes = 'suit-tab suit-tab-membership'
+
+    def queryset(self, request):
+        qs = super(UserMembershipInline, self).queryset(request)
+        return qs.filter(user=request.user)
+        
+class MemberInline(generic.GenericTabularInline):
+    model = Member
+    exclude = ['enacted']
+    extra = 1
+    suit_classes = 'suit-tab suit-tab-members'
+
 class TagInline(generic.GenericTabularInline):
     model = Tag
     exclude = ['enacted']
-    extra = 1
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-tags'
 
 class SliverInline(PlStackTabularInline):
     model = Sliver
@@ -45,32 +69,51 @@
     extra = 0
     #readonly_fields = ['ip', 'instance_name', 'image']
     readonly_fields = ['ip', 'instance_name']
+    suit_classes = 'suit-tab suit-tab-slivers'
     
 
 class SiteInline(PlStackTabularInline):
     model = Site
     extra = 0
+    suit_classes = 'suit-tab suit-tab-sites'
 
 class UserInline(PlStackTabularInline):
     model = User
     fields = ['email', 'firstname', 'lastname']
     extra = 0
+    suit_classes = 'suit-tab suit-tab-users'
 
 class SliceInline(PlStackTabularInline):
     model = Slice
+    fields = ['name','enabled','description','slice_url']
     extra = 0
+    suit_classes = 'suit-tab suit-tab-slices'
+
 
 class RoleInline(PlStackTabularInline):
     model = Role
     extra = 0 
+    suit_classes = 'suit-tab suit-tab-roles'
 
 class NodeInline(PlStackTabularInline):
     model = Node
     extra = 0
+    suit_classes = 'suit-tab suit-tab-nodes'
+
+class SlicePrivilegeInline(PlStackTabularInline):
+    model = SlicePrivilege
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-sliceprivileges'
+
+class DeploymentPrivilegeInline(PlStackTabularInline):
+    model = DeploymentPrivilege
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-deploymentprivileges'
 
 class SitePrivilegeInline(PlStackTabularInline):
     model = SitePrivilege
     extra = 0
+    suit_classes = 'suit-tab suit-tab-siteprivileges'
 
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
         if db_field.name == 'site':
@@ -94,10 +137,17 @@
                 kwargs['queryset'] = users
         return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
 
-class SliceMembershipInline(PlStackTabularInline):
-    model = SliceMembership
+class SitePrivilegeInline(PlStackTabularInline):
+    model = SitePrivilege
+    suit_classes = 'suit-tab suit-tab-siteprivileges'
     extra = 0
-    fields = ('user', 'role')
+    fields = ('user', 'site','role')
+
+class SlicePrivilegeInline(PlStackTabularInline):
+    model = SlicePrivilege
+    suit_classes = 'suit-tab suit-tab-sliceprivileges'
+    extra = 0
+    fields = ('user', 'slice','role')
 
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
         if db_field.name == 'slice':
@@ -119,7 +169,7 @@
                 users = User.objects.filter(email__in=emails) 
                 kwargs['queryset'] = list(users)
 
-        return super(SliceMembershipInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
+        return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
 
 class SliceTagInline(PlStackTabularInline):
     model = SliceTag
@@ -137,11 +187,38 @@
     save_on_top = False
     exclude = ['enacted']
 
+#class RoleMemberForm(forms.ModelForm):
+#    request=None
+#    member=forms.ModelChoiceField(queryset=Member.objects.all()) #first get all
+#
+#    def __init__(self,fata=None,files=None,auto_id='id_%s',prefix=None,initial=None,error_class=ErrorList,label_suffix=':',empty_permitted=False,instance=None):
+#        super(RoleMemberForm,self).__init__data,files,auto_id,prefix,initial,error_class,label_suffix,empty_permitted,instance)
+#
+#        self.fields["member"].queryset = member.objects.filter(
+
+class RoleMemberInline (admin.StackedInline):
+    model = Member
+#    form = RoleMemberForm
+    
+    def get_formset(self,request,obj=None, **kwargs):
+        self.form.request=request
+        return super(RoleMemberInline, self).get_formset(request, obj, **kwargs)
+
+class SliceRoleAdmin(PlanetStackBaseAdmin):
+    model = SliceRole
+    pass
+
+class SiteRoleAdmin(PlanetStackBaseAdmin):
+    model = SiteRole
+    pass
+
 class RoleAdmin(PlanetStackBaseAdmin):
     fieldsets = [
-        ('Role', {'fields': ['role_type']})
+        ('Role', {'fields': ['role_type', 'description','content_type'],
+                  'classes':['collapse']})
     ]
-    list_display = ('role_type',)
+    inlines = [ MemberInline,]
+    list_display = ('role_type','description','content_type')
 
 
 class DeploymentAdminForm(forms.ModelForm):
@@ -155,47 +232,30 @@
     class Meta:
         model = Deployment
 
-    def __init__(self, *args, **kwargs):
-        super(DeploymentAdminForm, self).__init__(*args, **kwargs)
-
-        if self.instance and self.instance.pk:
-            self.fields['sites'].initial = self.instance.sites.all()
-
-    def save(self, commit=True):
-        deploymentNetwork = super(DeploymentAdminForm, self).save(commit=False)
-        if commit:
-            deploymentNetwork.save()
-
-        if deploymentNetwork.pk:
-            deploymentNetwork.sites = self.cleaned_data['sites']
-            self.save_m2m()
-
-        return deploymentNetwork
 
 class DeploymentAdmin(PlanetStackBaseAdmin):
     form = DeploymentAdminForm
-    inlines = [NodeInline,SliverInline]
-
-    def get_formsets(self, request, obj=None):
-        for inline in self.get_inline_instances(request, obj):
-            # hide MyInline in the add view
-            if obj is None:
-                continue
-            # give inline object access to driver and caller
-            auth = request.session.get('auth', {})
-            if request.user.site:
-                auth['tenant'] = request.user.site.login_base
-            inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
-            yield inline.get_formset(request, obj)
+    inlines = [MemberInline,NodeInline,SliverInline,TagInline]
+    fieldsets = [
+        (None, {'fields': ['sites'], 'classes':['suit-tab suit-tab-sites']}),]
+    suit_form_tabs =(('sites', 'Sites'),('nodes','Nodes'),('members','Members'),('tags','Tags'))
 
 class SiteAdmin(PlanetStackBaseAdmin):
     fieldsets = [
-        (None, {'fields': ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'location']}),
-        ('Deployment Networks', {'fields': ['deployments']})
+        (None, {'fields': ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'location'], 'classes':['suit-tab suit-tab-general']}),
+        ('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
     ]
+    suit_form_tabs =(('general', 'Site Details'),
+        ('users','Users'),
+        ('members','Privileges'),
+        ('deployments','Deployments'),
+        ('slices','Slices'),
+        ('nodes','Nodes'), 
+        ('tags','Tags'),
+    )
     list_display = ('name', 'login_base','site_url', 'enabled')
     filter_horizontal = ('deployments',)
-    inlines = [TagInline, NodeInline, UserInline, SitePrivilegeInline]
+    inlines = [SliceInline,UserInline,TagInline, NodeInline, MemberInline]
     search_fields = ['name']
 
     def queryset(self, request):
@@ -229,7 +289,7 @@
 
 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
     fieldsets = [
-        (None, {'fields': ['user', 'site', 'role']})
+        (None, {'fields': ['user', 'site', 'role'], 'classes':['collapse']})
     ]
     list_display = ('user', 'site', 'role')
 
@@ -269,9 +329,17 @@
         return qs
 
 class SliceAdmin(PlanetStackBaseAdmin):
-    fields = ['name', 'site', 'serviceClass', 'description', 'slice_url']
+    fieldsets = [('Slice Details', {'fields': ['name', 'site', 'serviceClass', 'description', 'slice_url'], 'classes':['suit-tab suit-tab-general']}),]
     list_display = ('name', 'site','serviceClass', 'slice_url')
-    inlines = [SliverInline, SliceMembershipInline, TagInline, SliceTagInline]
+    inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline]
+
+
+    suit_form_tabs =(('general', 'Slice Details'),
+        ('sliceprivileges','Privileges'),
+        ('slivers','Slivers'),
+        ('tags','Tags'),
+        ('reservations','Reservations'),
+    )
 
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
         if db_field.name == 'site':
@@ -317,7 +385,7 @@
         obj.caller = request.user
         obj.save() 
 
-class SliceMembershipAdmin(PlanetStackBaseAdmin):
+class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
     fieldsets = [
         (None, {'fields': ['user', 'slice', 'role']})
     ]
@@ -344,12 +412,12 @@
                 users = User.objects.filter(email__in=emails)
                 kwargs['queryset'] = users
 
-        return super(SliceMembershipAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
+        return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
 
     def queryset(self, request):
         # admins can see all memberships. Users can only see memberships of
         # slices where they have the admin role.
-        qs = super(SliceMembershipAdmin, self).queryset(request)
+        qs = super(SlicePrivilegeAdmin, self).queryset(request)
         if not request.user.is_admin:
             roles = Role.objects.filter(role_type__in=['admin', 'pi'])
             site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
@@ -374,13 +442,33 @@
         obj.delete()
 
 
-class ImageAdmin(admin.ModelAdmin):
-    fields = ['image_id', 'name', 'disk_format', 'container_format']
+class ImageAdmin(PlanetStackBaseAdmin):
+
+    fieldsets = [('Image Details', 
+                   {'fields': ['image_id', 'name', 'disk_format', 'container_format'], 
+                    'classes': ['suit-tab suit-tab-general']})
+               ]
+
+    suit_form_tabs =(('general','Image Details'),('slivers','Slivers'))
+
+    inlines = [SliverInline]
+
+class NodeForm(forms.ModelForm):
+    class Meta:
+        widgets = {
+            'site': LinkedSelect,
+            'deployment': LinkedSelect
+        }
 
 class NodeAdmin(admin.ModelAdmin):
+    form = NodeForm
+    exclude = ['enacted']
     list_display = ('name', 'site', 'deployment')
     list_filter = ('deployment',)
-    inlines = [TagInline]
+    inlines = [TagInline,SliverInline]
+    fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
+
+    suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
 
 
 class SliverForm(forms.ModelForm):
@@ -391,26 +479,41 @@
         widgets = {
             'ip': PlainTextWidget(),
             'instance_name': PlainTextWidget(),
+            'slice': LinkedSelect,
+            'deploymentNetwork': LinkedSelect,
+            'node': LinkedSelect,
+            'image': LinkedSelect
         }
 
 class ProjectAdmin(admin.ModelAdmin):
     exclude = ['enacted']
+    inlines = [TagInline]
+
+class MemberAdmin(admin.ModelAdmin):
+    exclude = ['enacted']
+    list_display = ['role', 'rightContent_type', 'content_type', 'content_object',]
 
 class TagAdmin(admin.ModelAdmin):
     exclude = ['enacted']
+    list_display = ['project', 'name', 'value', 'content_type', 'content_object',]
 
 class SliverAdmin(PlanetStackBaseAdmin):
     form = SliverForm
     fieldsets = [
-        ('Sliver', {'fields': ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']})
+        ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
     ]
     list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
+
+    suit_form_tabs =(('general', 'Sliver Details'),
+        ('tags','Tags'),
+    )
+
     inlines = [TagInline]
 
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
         if db_field.name == 'slice':
             if not request.user.is_admin:
-                slices = set([sm.slice.name for sm in SliceMembership.objects.filter(user=request.user)]) 
+                slices = set([sm.slice.name for sm in SlicePrivilege.objects.filter(user=request.user)]) 
                 kwargs['queryset'] = Slice.objects.filter(name__in=list(slices))
 
         return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
@@ -470,7 +573,7 @@
 
     class Meta:
         model = User
-        fields = ('email', 'firstname', 'lastname', 'phone', 'public_key', 'site')
+        fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
 
     def clean_password2(self):
         # Check that the two password entries match
@@ -518,24 +621,26 @@
     # The fields to be used in displaying the User model.
     # These override the definitions on the base UserAdmin
     # that reference specific fields on auth.User.
-    list_display = ('email', 'site', 'firstname', 'lastname', 'is_admin', 'last_login')
-    list_filter = ('site',)
-    inlines = [SitePrivilegeInline, SliceMembershipInline]
+    list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
+    list_filter = ()
+    inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline]
     fieldsets = (
-        (None, {'fields': ('email', 'password', 'site', 'is_admin', 'timezone')}),
-        ('Personal info', {'fields': ('firstname','lastname','phone', 'public_key')}),
+        ('Login Details', {'fields': ('email', 'username','site','password', 'is_admin', 'public_key'), 'classes':['suit-tab suit-tab-general']}),
+        ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
         #('Important dates', {'fields': ('last_login',)}),
     )
     add_fieldsets = (
         (None, {
             'classes': ('wide',),
-            'fields': ('email', 'firstname', 'lastname', 'phone', 'site', 'public_key','password1', 'password2', 'is_admin')}
+            'fields': ('email', 'username','firstname', 'lastname', 'phone', 'public_key','password1', 'password2')}
         ),
     )
     search_fields = ('email',)
     ordering = ('email',)
     filter_horizontal = ()
 
+    suit_form_tabs =(('general','Login Details'),('contact','Contact Information'),('sliceprivileges','Slice Privileges'),('siteprivileges','Site Privileges'),('deploymentprivileges','Deployment Privileges'))
+
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
         if db_field.name == 'site':
             if not request.user.is_admin:
@@ -562,6 +667,7 @@
     exclude = ['enacted']
     model = ReservedResource
     extra = 0
+    suit_classes = 'suit-tab suit-tab-reservedresources'
 
     def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
         field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
@@ -586,6 +692,9 @@
 class ReservationChangeForm(forms.ModelForm):
     class Meta:
         model = Reservation
+        widgets = {
+            'slice' : LinkedSelect
+        }
 
 class ReservationAddForm(forms.ModelForm):
     slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
@@ -603,6 +712,10 @@
 
     class Meta:
         model = Reservation
+        widgets = {
+            'slice' : LinkedSelect
+        }
+
 
 class ReservationAddRefreshForm(ReservationAddForm):
     """ This form is displayed when the Reservation Form receives an update
@@ -629,10 +742,14 @@
 
 class ReservationAdmin(admin.ModelAdmin):
     exclude = ['enacted']
+    fieldsets = [('Reservation Details', {'fields': ['startTime', 'duration','slice'], 'classes': ['suit-tab suit-tab-general']})]
     list_display = ('startTime', 'duration')
-    inlines = [ReservedResourceInline]
     form = ReservationAddForm
 
+    suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
+
+    inlines = [ReservedResourceInline]
+
     def add_view(self, request, form_url='', extra_context=None):
         timezone.activate(request.user.timezone)
         request._refresh = False
@@ -700,7 +817,7 @@
 
 # When debugging it is often easier to see all the classes, but for regular use 
 # only the top-levels should be displayed
-showAll = False
+showAll = True
 
 admin.site.register(Deployment, DeploymentAdmin)
 admin.site.register(Site, SiteAdmin)
@@ -708,13 +825,19 @@
 admin.site.register(Project, ProjectAdmin)
 admin.site.register(ServiceClass, ServiceClassAdmin)
 admin.site.register(Reservation, ReservationAdmin)
+#admin.site.register(SliceRole, SliceRoleAdmin)
+#admin.site.register(SiteRole, SiteRoleAdmin)
+#admin.site.register(PlanetStackRole)
+#admin.site.register(DeploymentRole)
 
 if showAll:
+    #admin.site.register(PlanetStack)
     admin.site.register(Tag, TagAdmin)
     admin.site.register(Node, NodeAdmin)
-    admin.site.register(SliceMembership, SliceMembershipAdmin)
-    admin.site.register(SitePrivilege, SitePrivilegeAdmin)
+    #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
+    #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
     admin.site.register(Role, RoleAdmin)
+    admin.site.register(Member, MemberAdmin)
     admin.site.register(Sliver, SliverAdmin)
     admin.site.register(Image, ImageAdmin)
 
diff --git a/planetstack/core/fixtures/initial_data.json b/planetstack/core/fixtures/initial_data.json
index a86728a..f034820 100644
--- a/planetstack/core/fixtures/initial_data.json
+++ b/planetstack/core/fixtures/initial_data.json
@@ -314,5 +314,229 @@
         "maxDuration": 8760, 
         "enacted": null
     }
+},
+{
+    "pk": 1, 
+    "model": "core.role", 
+    "fields": {
+        "updated": "2013-07-30T10:35:50.572Z", 
+        "description": "PlanetStack Application Administrator", 
+        "created": "2013-07-30T10:30:28.633Z", 
+        "content_type": 8, 
+        "role_type": "Admin", 
+        "enacted": null
+    }
+},
+{
+    "pk": 2, 
+    "model": "core.role", 
+    "fields": {
+        "updated": "2013-07-30T10:36:30.174Z", 
+        "description": "User level role for PlanetStack Application", 
+        "created": "2013-07-30T10:31:02.627Z", 
+        "content_type": 8, 
+        "role_type": "Default", 
+        "enacted": null
+    }
+},
+{
+    "pk": 3, 
+    "model": "core.role", 
+    "fields": {
+        "updated": "2013-07-30T10:36:07.877Z", 
+        "description": "Administrative role for a Slice", 
+        "created": "2013-07-30T10:31:25.829Z", 
+        "content_type": 21, 
+        "role_type": "Admin", 
+        "enacted": null
+    }
+},
+{
+    "pk": 4, 
+    "model": "core.role", 
+    "fields": {
+        "updated": "2013-07-30T10:36:20.679Z", 
+        "description": "User level access for a particular Slice", 
+        "created": "2013-07-30T10:31:48.791Z", 
+        "content_type": 21, 
+        "role_type": "Default", 
+        "enacted": null
+    }
+},
+{
+    "pk": 5, 
+    "model": "core.role", 
+    "fields": {
+        "updated": "2013-07-30T10:35:59.388Z", 
+        "description": "Administrator role for a particular Site", 
+        "created": "2013-07-30T10:32:20.600Z", 
+        "content_type": 15, 
+        "role_type": "Admin", 
+        "enacted": null
+    }
+},
+{
+    "pk": 6, 
+    "model": "core.role", 
+    "fields": {
+        "updated": "2013-07-30T10:34:39.494Z", 
+        "description": "Principal Investigator for a particular Site", 
+        "created": "2013-07-30T10:34:39.494Z", 
+        "content_type": 15, 
+        "role_type": "PI", 
+        "enacted": null
+    }
+},
+{
+    "pk": 7, 
+    "model": "core.role", 
+    "fields": {
+        "updated": "2013-07-30T10:35:27.633Z", 
+        "description": "Technical support for a particular Site. Allows for Read/Write access to a Site's Nodes.", 
+        "created": "2013-07-30T10:35:27.633Z", 
+        "content_type": 15, 
+        "role_type": "Tech", 
+        "enacted": null
+    }
+},
+{
+    "pk": 8, 
+    "model": "core.role", 
+    "fields": {
+        "updated": "2013-07-30T10:38:04.554Z", 
+        "description": "Responsibility for a particular Site's accounting and invoices.", 
+        "created": "2013-07-30T10:38:04.554Z", 
+        "content_type": 15, 
+        "role_type": "Billing", 
+        "enacted": null
+    }
+},
+{
+    "pk": 9, 
+    "model": "core.role", 
+    "fields": {
+        "updated": "2013-07-30T10:39:07.062Z", 
+        "description": "Default access for a particular Site which allows for Read-only access to the Site's objects.", 
+        "created": "2013-07-30T10:39:07.062Z", 
+        "content_type": 15, 
+        "role_type": "Default", 
+        "enacted": null
+    }
+},
+{
+    "pk": 10, 
+    "model": "core.role", 
+    "fields": {
+        "updated": "2013-07-30T10:39:55.479Z", 
+        "description": "Represents the Site through which a particular user is managed through.", 
+        "created": "2013-07-30T10:39:55.479Z", 
+        "content_type": 15, 
+        "role_type": "Homed", 
+        "enacted": null
+    }
+},
+{
+    "pk": 11,
+    "model": "core.role",
+    "fields": {
+        "updated": "2013-07-30T17:35:59.815Z",
+        "description": "Administrative responsibility for a particular Deployment.",
+        "created": "2013-07-30T17:35:59.815Z",
+        "content_type": 9,
+        "role_type": "Admin",
+        "enacted": null
+    }
+},
+{
+    "pk": 12,
+    "model": "core.role",
+    "fields": {
+        "updated": "2013-07-30T17:36:54.728Z",
+        "description": "Default access for a particular Deployment.",
+        "created": "2013-07-30T17:36:54.728Z",
+        "content_type": 9,
+        "role_type": "Default",
+        "enacted": null
+    }
+},
+{
+    "pk": 1,
+    "model": "core.planetstackrole",
+    "fields": {
+        "updated": "2013-09-03T11:47:42.611Z",
+        "enacted": "2013-09-03T11:47:51Z",
+        "role": "admin",
+        "created": "2013-09-03T11:47:42.611Z"
+    }
+},
+{
+    "pk": 1,
+    "model": "core.siterole",
+    "fields": {
+        "updated": "2013-09-03T11:48:34.966Z",
+        "enacted": null,
+        "role": "admin",
+        "created": "2013-09-03T11:48:34.966Z"
+    }
+},
+{
+    "pk": 2,
+    "model": "core.siterole",
+    "fields": {
+        "updated": "2013-09-03T11:48:49.480Z",
+        "enacted": null,
+        "role": "pi",
+        "created": "2013-09-03T11:48:49.480Z"
+    }
+},
+{
+    "pk": 3,
+    "model": "core.siterole",
+    "fields": {
+        "updated": "2013-09-03T11:49:03.678Z",
+        "enacted": null,
+        "role": "tech",
+        "created": "2013-09-03T11:49:03.678Z"
+    }
+},
+{
+    "pk": 4,
+    "model": "core.siterole",
+    "fields": {
+        "updated": "2013-09-03T11:49:17.254Z",
+        "enacted": null,
+        "role": "billing",
+        "created": "2013-09-03T11:49:17.254Z"
+    }
+},
+{
+    "pk": 1,
+    "model": "core.slicerole",
+    "fields": {
+        "updated": "2013-09-03T11:48:02.080Z",
+        "enacted": null,
+        "role": "admin",
+        "created": "2013-09-03T11:48:02.080Z"
+    }
+},
+{
+    "pk": 2,
+    "model": "core.slicerole",
+    "fields": {
+        "updated": "2013-09-03T11:48:17.688Z",
+        "enacted": null,
+        "role": "default",
+        "created": "2013-09-03T11:48:17.688Z"
+    }
+},
+{
+    "pk": 1,
+    "model": "core.deploymentrole",
+    "fields": {
+        "updated": "2013-09-03T11:48:02.080Z",
+        "enacted": null,
+        "role": "admin",
+        "created": "2013-09-03T11:48:02.080Z"
+    }
 }
 ]
diff --git a/planetstack/core/models/__init__.py b/planetstack/core/models/__init__.py
index 2280822..1cc4d07 100644
--- a/planetstack/core/models/__init__.py
+++ b/planetstack/core/models/__init__.py
@@ -1,17 +1,25 @@
 from .plcorebase import PlCoreBase
-from .deployment import Deployment
+from .planetstack import PlanetStack
 from .project import Project
 from .tag import Tag
+from .role import Role
+from .deployment import Deployment
 from .site import Site
+from .user import User
+from .serviceclass import ServiceClass
+from .slice import Slice
 from .site import SitePrivilege
 from .image import Image
-from .user import User
-from .role import Role
 from .node import Node
-from .serviceclass import ServiceClass
 from .serviceresource import ServiceResource
-from .slice import Slice
-from .slice import SliceMembership
+from .slice import SliceRole
+from .slice import SlicePrivilege
+from .site import SiteRole
+from .site import SitePrivilege
+from .deployment import DeploymentRole
+from .deployment import DeploymentPrivilege
+from .planetstack import PlanetStackRole
+from .planetstack import PlanetStackPrivilege
 from .slicetag import SliceTag
 from .sliver import Sliver
 from .reservation import ReservedResource
diff --git a/planetstack/core/models/deployment.py b/planetstack/core/models/deployment.py
index d38115f..4e835d0 100644
--- a/planetstack/core/models/deployment.py
+++ b/planetstack/core/models/deployment.py
@@ -1,11 +1,30 @@
 import os
 from django.db import models
 from core.models import PlCoreBase
+from core.models import Member
+from django.contrib.contenttypes import generic
 
 # Create your models here.
 
 class Deployment(PlCoreBase):
     name = models.CharField(max_length=200, unique=True, help_text="Name of the Deployment")
+    members = generic.GenericRelation(Member)
 
     def __unicode__(self):  return u'%s' % (self.name)
 
+    
+class DeploymentRole(PlCoreBase):
+
+    ROLE_CHOICES = (('admin','Admin'),)
+    role = models.CharField(choices=ROLE_CHOICES, unique=True, max_length=30)
+
+    def __unicode__(self):  return u'%s' % (self.role)
+
+class DeploymentPrivilege(PlCoreBase):
+
+    user = models.ForeignKey('User', related_name='deployment_privileges')
+    deployment = models.ForeignKey('Deployment', related_name='deployment_privileges')
+    role = models.ForeignKey('DeploymentRole')
+
+    def __unicode__(self):  return u'%s %s %s' % (self.deployment, self.user, self.role)
+
diff --git a/planetstack/core/models/planetstack.py b/planetstack/core/models/planetstack.py
new file mode 100644
index 0000000..9007a51
--- /dev/null
+++ b/planetstack/core/models/planetstack.py
@@ -0,0 +1,30 @@
+import os
+from django.db import models
+from core.models import PlCoreBase
+
+# Create your models here.
+
+class PlanetStack(PlCoreBase):
+    description = models.CharField(max_length=200, unique=True, default="PlanetStack", help_text="Used for scoping of roles at the PlanetStack Application level")
+
+    class Meta:
+        verbose_name_plural = "PlanetStack"
+        app_label = "core"
+
+    def __unicode__(self):  return u'%s' % (self.description)
+
+class PlanetStackRole(PlCoreBase):
+    ROLE_CHOICES = (('admin','Admin'),)
+    role = models.CharField(choices=ROLE_CHOICES, unique=True, max_length=30)
+
+    def __unicode__(self):  return u'%s' % (self.role)
+
+class PlanetStackPrivilege(PlCoreBase):
+    user = models.ForeignKey('User', related_name='planetstack_privileges')
+    planetstack = models.ForeignKey('PlanetStack', related_name='planetstack_privileges', default=1)
+    role = models.ForeignKey('PlanetStackRole')
+
+    def __unicode__(self):  return u'%s %s %s' % (self.planetstack, self.user, self.role)
+
+
+
diff --git a/planetstack/core/models/role.py b/planetstack/core/models/role.py
index fd29848..234868e 100644
--- a/planetstack/core/models/role.py
+++ b/planetstack/core/models/role.py
@@ -2,14 +2,16 @@
 import datetime
 from django.db import models
 from core.models import PlCoreBase
+from django.contrib.contenttypes.models import ContentType
+from django.contrib.contenttypes import generic
 
 class Role(PlCoreBase):
 
-    ROLE_CHOICES = (('admin', 'Admin'), ('pi', 'Principle Investigator'), ('tech', 'Technician'), ('user','User'))
-    role = models.CharField(null=True, blank=True,max_length=256, unique=True, choices=ROLE_CHOICES)
-    role_type = models.CharField(max_length=80, unique=True)
+    role_type = models.CharField(max_length=80, verbose_name="Name")
+    description = models.CharField(max_length=120, verbose_name="Description")
+    content_type = models.ForeignKey(ContentType, verbose_name="Role Scope")
 
-    def __unicode__(self):  return u'%s' % (self.role_type)
+    def __unicode__(self):  return u'%s:%s' % (self.content_type,self.role_type)
 
 
     def save(self, *args, **kwds):
diff --git a/planetstack/core/models/site.py b/planetstack/core/models/site.py
index 8a6d7c4..aee3843 100644
--- a/planetstack/core/models/site.py
+++ b/planetstack/core/models/site.py
@@ -24,11 +24,18 @@
 
     def __unicode__(self):  return u'%s' % (self.name)
 
+class SiteRole(PlCoreBase):
+
+    ROLE_CHOICES = (('admin','Admin'),('pi','PI'),('tech','Tech'),('billing','Billing'))
+    role = models.CharField(choices=ROLE_CHOICES, unique=True, max_length=30)
+
+    def __unicode__(self):  return u'%s' % (self.role)
+
 class SitePrivilege(PlCoreBase):
 
     user = models.ForeignKey('User', related_name='site_privileges')
     site = models.ForeignKey('Site', related_name='site_privileges')
-    role = models.ForeignKey('Role')
+    role = models.ForeignKey('SiteRole')
 
     def __unicode__(self):  return u'%s %s %s' % (self.site, self.user, self.role)
 
diff --git a/planetstack/core/models/slice.py b/planetstack/core/models/slice.py
index 74815b2..e584c07 100644
--- a/planetstack/core/models/slice.py
+++ b/planetstack/core/models/slice.py
@@ -40,15 +40,16 @@
             self.creator = self.caller
         super(Slice, self).save(*args, **kwds)
 
-class SliceMembership(PlCoreBase):
-    user = models.ForeignKey('User', related_name='slice_memberships')
-    slice = models.ForeignKey('Slice', related_name='slice_memberships')
-    role = models.ForeignKey('Role')
+class SliceRole(PlCoreBase):
+    ROLE_CHOICES = (('admin','Admin'),('default','Default'))
+
+    role = models.CharField(choices=ROLE_CHOICES, unique=True, max_length=30)
+
+    def __unicode__(self):  return u'%s' % (self.role)
+
+class SlicePrivilege(PlCoreBase):
+    user = models.ForeignKey('User', related_name='slice_privileges')
+    slice = models.ForeignKey('Slice', related_name='slice_privileges')
+    role = models.ForeignKey('SliceRole')
 
     def __unicode__(self):  return u'%s %s %s' % (self.slice, self.user, self.role)
-
-    def save(self, *args, **kwds):
-        super(SliceMembership, self).save(*args, **kwds)
-
-    def delete(self, *args, **kwds):
-        super(SliceMembership, self).delete(*args, **kwds)
diff --git a/planetstack/core/models/user.py b/planetstack/core/models/user.py
index 758bcbf..2b63dda 100644
--- a/planetstack/core/models/user.py
+++ b/planetstack/core/models/user.py
@@ -2,11 +2,11 @@
 import datetime
 from collections import defaultdict
 from django.db import models
-from core.models import PlCoreBase
-from core.models import Site
+from core.models import PlCoreBase,Site
 from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
 from timezones.fields import TimeZoneField
 
+
 # Create your models here.
 class UserManager(BaseUserManager):
     def create_user(self, email, firstname, lastname, password=None):
@@ -54,6 +54,7 @@
         unique=True,
         db_index=True,
     )
+    username = models.CharField(max_length=200, default="Something" )
 
     kuser_id = models.CharField(null=True, blank=True, help_text="keystone user id", max_length=200) 
     firstname = models.CharField(help_text="person's given name", max_length=200)
@@ -61,7 +62,7 @@
 
     phone = models.CharField(null=True, blank=True, help_text="phone number contact", max_length=100)
     user_url = models.URLField(null=True, blank=True)
-    site = models.ForeignKey(Site, related_name='users', verbose_name="Site this user will be homed too", null=True)
+    site = models.ForeignKey(Site, related_name='users', help_text="Site this user will be homed too", null=True)
     public_key = models.TextField(null=True, blank=True, max_length=1024, help_text="Public key string")
 
     is_active = models.BooleanField(default=True)
@@ -104,18 +105,21 @@
         # Simplest possible answer: Yes, always
         return True
 
-    def get_roles(self):
-        from core.models.site import SitePrivilege
-        from core.models.slice import SliceMembership
+    def is_superuser(self):
+        return False
 
-        site_privileges = SitePrivilege.objects.filter(user=self)
-        slice_memberships = SliceMembership.objects.filter(user=self)
-        roles = defaultdict(list)
-        for site_privilege in site_privileges:
-            roles[site_privilege.role.role_type].append(site_privilege.site.login_base)
-        for slice_membership in slice_memberships:
-            roles[slice_membership.role.role_type].append(slice_membership.slice.name)
-        return roles   
+#    def get_roles(self):
+#        from core.models.site import SitePrivilege
+#        from core.models.slice import SliceMembership
+#
+#        site_privileges = SitePrivilege.objects.filter(user=self)
+#        slice_memberships = SliceMembership.objects.filter(user=self)
+#        roles = defaultdict(list)
+#        for site_privilege in site_privileges:
+#            roles[site_privilege.role.role_type].append(site_privilege.site.login_base)
+#        for slice_membership in slice_memberships:
+#            roles[slice_membership.role.role_type].append(slice_membership.slice.name)
+#        return roles   
 
     def save(self, *args, **kwds):
         if not self.id:
diff --git a/planetstack/core/serializers.py b/planetstack/core/serializers.py
index 94f5c3c..b83157b 100644
--- a/planetstack/core/serializers.py
+++ b/planetstack/core/serializers.py
@@ -3,38 +3,108 @@
 from core.models import *
 
 
+class DeploymentSerializer(serializers.HyperlinkedModelSerializer):
+
+    # HyperlinkedModelSerializer doesn't include the id by default
+    id = serializers.Field()
+    sites = serializers.HyperlinkedRelatedField(view_name='site-detail')
+    class Meta:
+        model = Deployment
+        fields = ('id',
+                  'url',
+                  'name',
+                  'sites'
+                 )
+
+class ImageSerializer(serializers.HyperlinkedModelSerializer):
+    # HyperlinkedModelSerializer doesn't include the id by default
+    id = serializers.Field()
+    class Meta:
+        model = Image
+        fields = ('id',
+                  'url',
+                  'image_id',
+                  'name',
+                  'disk_format',
+                  'container_format')
+
+class NodeSerializer(serializers.HyperlinkedModelSerializer):
+    # HyperlinkedModelSerializer doesn't include the id by default
+    id = serializers.Field()
+    class Meta:
+        model = Node
+        fields = ('id',
+                 'url',
+                 'name')
+
+class ProjectSerializer(serializers.HyperlinkedModelSerializer):
+    # HyperlinkedModelSerializer doesn't include the id by default
+    id = serializers.Field()
+    class Meta:
+        model = Project
+        fields = ('id',
+                 'url',
+                 'name')
+
+class ReservationSerializer(serializers.HyperlinkedModelSerializer):
+    # HyperlinkedModelSerializer doesn't include the id by default
+    id = serializers.Field()
+    class Meta:
+        model = Reservation
+        fields = ('id',
+                 'url',
+                 'startTime',
+                 'slice',
+                 'duration',
+                 'endTime',
+                 )
+
 class RoleSerializer(serializers.HyperlinkedModelSerializer):
     # HyperlinkedModelSerializer doesn't include the id by default
     id = serializers.Field()
     class Meta:
         model = Role
         fields = ('id', 
-                  'role',
-                  'role_type')
+                 'url',
+                 'role',
+                 'role_type')
 
 
-class UserSerializer(serializers.HyperlinkedModelSerializer):
+class ServiceClassSerializer(serializers.HyperlinkedModelSerializer):
     # HyperlinkedModelSerializer doesn't include the id by default
     id = serializers.Field()
-    site = serializers.HyperlinkedRelatedField(view_name='site-detail')
-    slice_memberships = serializers.HyperlinkedRelatedField(view_name='slice-membership-detail')
-    site_privileges = serializers.HyperlinkedRelatedField(view_name='siteprivilege-detail')
     class Meta:
-        model = User
+        model = ServiceClass
         fields = ('id',
-                  'kuser_id', 
-                  'firstname', 
-                  'lastname',
-                  'email', 
-                  'password',
-                  'phone',
-                  'public_key', 
-                  'user_url',
-                  'is_admin',
-                  'site',
-                  'slice_memberships',
-                  'site_privileges')
-                    
+                 'url',
+                 'name',
+                 'description',
+                 'commitment',
+                 'membershipFee',
+                 'membershipFeeMonths',
+                 'upgradeRequiresApproval',
+                 'upgradeFrom',
+                 )
+
+class ServiceResourceSerializer(serializers.HyperlinkedModelSerializer):
+    # HyperlinkedModelSerializer doesn't include the id by default
+    id = serializers.Field()
+    serviceClass = serializers.HyperlinkedRelatedField(view_name='serviceclass-detail')
+    class Meta:
+        model = ServiceResource
+        fields = ('id',
+                 'url',
+                 'name',
+                 'serviceClass',
+                 'maxUnitsDeployment',
+                 'maxUnitsNode',
+                 'maxDuration',
+                 'bucketInRate',
+                 'bucketMaxSize',
+                 'cost',
+                 'calendarReservable',
+                 )
+
 class SliceSerializer(serializers.HyperlinkedModelSerializer):
     # HyperlinkedModelSerializer doesn't include the id by default
     id = serializers.Field()
@@ -43,6 +113,7 @@
     class Meta:
         model = Slice
         fields = ('id',
+                  'url',
                   'tenant_id',
                   'enabled',
                   'name',
@@ -58,14 +129,15 @@
                   'updated',
                   'created')
 
-class SliceMembershipSerializer(serializers.HyperlinkedModelSerializer):
+class SlicePrivilegeSerializer(serializers.HyperlinkedModelSerializer):
     id = serializers.Field()
     slice = serializers.HyperlinkedRelatedField(view_name='slice-detail')
     user = serializers.HyperlinkedRelatedField(view_name='user-detail')
     role = serializers.HyperlinkedRelatedField(view_name='role-detail')
     class Meta:
-        model = SliceMembership
+        model = SlicePrivilege
         fields = ('id',
+                  'url',
                   'user',
                   'slice',
                   'role')
@@ -105,75 +177,90 @@
     class Meta:
         model = SitePrivilege
         fields = ('id',
+                  'url',
                   'user',
                   'site',
                   'role')
 
-class DeploymentSerializer(serializers.HyperlinkedModelSerializer):
-
-    # HyperlinkedModelSerializer doesn't include the id by default
-    id = serializers.Field()
-    sites = serializers.HyperlinkedRelatedField(view_name='site-detail')
-    class Meta:
-        model = Deployment
-        fields = ('id',
-                  'name',
-                  'sites'
-                 )
-
 class SliverSerializer(serializers.HyperlinkedModelSerializer):
     # HyperlinkedModelSerializer doesn't include the id by default
     id = serializers.Field()
     image = serializers.HyperlinkedRelatedField(view_name='image-detail')
     slice = serializers.HyperlinkedRelatedField(view_name='slice-detail')
-    deployment = serializers.HyperlinkedRelatedField(view_name='deployment-detail')
+    deploymentNetwork = serializers.HyperlinkedRelatedField(view_name='deployment-detail')
     node = serializers.HyperlinkedRelatedField(view_name='node-detail')
     
-    
     #slice = serializers.PrimaryKeyRelatedField(read_only=True)
 
     class Meta:
         model = Sliver
         fields = ('id',
+                  'url',
                   'instance_id',
                   'name',
                   'instance_name',
                   'ip',
                   'image',
                   'slice',
-                  'deployment',
+                  'deploymentNetwork',
                   'node')
 
-class NodeSerializer(serializers.HyperlinkedModelSerializer):
+class UserSerializer(serializers.HyperlinkedModelSerializer):
     # HyperlinkedModelSerializer doesn't include the id by default
     id = serializers.Field()
+    site = serializers.HyperlinkedRelatedField(view_name='site-detail')
+    slice_privileges = serializers.HyperlinkedRelatedField(view_name='sliceprivilege-detail')
+    site_privileges = serializers.HyperlinkedRelatedField(view_name='siteprivilege-detail')
     class Meta:
-        model = Node
+        model = User
         fields = ('id',
-                 'name')
-
-class ImageSerializer(serializers.HyperlinkedModelSerializer):
+                  'url',
+                  'kuser_id', 
+                  'firstname', 
+                  'lastname',
+                  'email', 
+                  'password',
+                  'phone',
+                  'public_key', 
+                  'user_url',
+                  'is_admin',
+                  'slice_privileges',
+                  'site_privileges')
+                    
+class TagSerializer(serializers.HyperlinkedModelSerializer):
     # HyperlinkedModelSerializer doesn't include the id by default
     id = serializers.Field()
+    project = serializers.HyperlinkedRelatedField(view_name='project-detail')
+    #content_type = serializers.PrimaryKeyRelatedField(read_only=True)
+    content_type = serializers.RelatedField(source = "content_type")
+    content_object = serializers.RelatedField(source='content_object')
     class Meta:
-        model = Image
-        fields = ('id',
-                  'image_id',
-                  'name',
-                  'disk_format',
-                  'container_format')
+        model = Tag
+        fields = ('id', 
+                  'url',
+                  'project',
+                  'value',
+                  'content_type',
+                  'object_id',
+                  'content_object',
+                  'name')
 
 serializerLookUp = { 
+                 Deployment: DeploymentSerializer,
+                 Image: ImageSerializer,
+                 Node: NodeSerializer,
+                 Project: ProjectSerializer,
+                 Reservation: ReservationSerializer,
                  Role: RoleSerializer,
-                 User: UserSerializer,
+                 ServiceClass: ServiceClassSerializer,
+                 ServiceResource: ServiceResourceSerializer,
                  Site: SiteSerializer,
                  SitePrivilege: SitePrivilegeSerializer,
                  Slice: SliceSerializer,
-                 SliceMembership: SliceMembershipSerializer,
-                 Node: NodeSerializer,
+                 SlicePrivilege: SlicePrivilegeSerializer,
                  Sliver: SliverSerializer,
-                 Deployment: DeploymentSerializer,
-                 Image: ImageSerializer,
+                 Tag: TagSerializer,
+                 User: UserSerializer,
                  None: None,
                 }
 
diff --git a/planetstack/core/static/planetstack.css b/planetstack/core/static/planetstack.css
index 2034708..b517eac 100644
--- a/planetstack/core/static/planetstack.css
+++ b/planetstack/core/static/planetstack.css
@@ -1 +1,2 @@
-.field-refresh { display: none; }
+.required:after {color: red ! important; font-size: 18px }
+#.btn-success {color:black}
diff --git a/planetstack/core/views/slice_memberships.py b/planetstack/core/views/slice_memberships.py
deleted file mode 100644
index 13f0707..0000000
--- a/planetstack/core/views/slice_memberships.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from core.serializers import SliceMembershipSerializer
-from rest_framework import generics
-from core.models import SliceMembership
-
-class SliceMembershipList(generics.ListCreateAPIView):
-    queryset = SliceMembership.objects.all()
-    serializer_class = SliceMembershipSerializer
-
-class SliceMembershipDetail(generics.RetrieveUpdateDestroyAPIView):
-    queryset = SliceMembership.objects.all()
-    serializer_class = SliceMembershipSerializer
-
-
diff --git a/planetstack/core/views/slice_privileges.py b/planetstack/core/views/slice_privileges.py
new file mode 100644
index 0000000..4dd1f93
--- /dev/null
+++ b/planetstack/core/views/slice_privileges.py
@@ -0,0 +1,13 @@
+from core.serializers import SlicePrivilegeSerializer
+from rest_framework import generics
+from core.models import SlicePrivilege
+
+class SlicePrivilegeList(generics.ListCreateAPIView):
+    queryset = SlicePrivilege.objects.all()
+    serializer_class = SlicePrivilegeSerializer
+
+class SlicePrivilegeDetail(generics.RetrieveUpdateDestroyAPIView):
+    queryset = SlicePrivilege.objects.all()
+    serializer_class = SlicePrivilegeSerializer
+
+