Django-suit, add in Roles for specific classes site, slice, deployment, planetstack...
authorSiobhan Tully <stully@verivue.com>
Tue, 3 Sep 2013 16:59:24 +0000 (12:59 -0400)
committerSiobhan Tully <stully@verivue.com>
Tue, 3 Sep 2013 16:59:24 +0000 (12:59 -0400)
22 files changed:
planetstack/core/admin.py
planetstack/core/fixtures/initial_data.json
planetstack/core/models/__init__.py
planetstack/core/models/deployment.py
planetstack/core/models/planetstack.py [new file with mode: 0644]
planetstack/core/models/role.py
planetstack/core/models/site.py
planetstack/core/models/slice.py
planetstack/core/models/user.py
planetstack/core/serializers.py
planetstack/core/static/planetstack.css
planetstack/core/views/slice_memberships.py [deleted file]
planetstack/core/views/slice_privileges.py [new file with mode: 0644]
planetstack/hpc/__init__.py [new file with mode: 0644]
planetstack/hpc/admin.py [new file with mode: 0644]
planetstack/hpc/models.py [new file with mode: 0644]
planetstack/hpc/tests.py [new file with mode: 0644]
planetstack/hpc/views.py [new file with mode: 0644]
planetstack/planetstack/settings.py
planetstack/planetstack/urls.py
planetstack/templates/admin/base.html [new file with mode: 0644]
planetstack/templates/admin/base_site.html

index a1a21d6..730937a 100644 (file)
@@ -12,12 +12,19 @@ from django.contrib.auth.forms import ReadOnlyPasswordHashField
 from django.contrib.auth.signals import user_logged_in
 from django.utils import timezone
 from django.contrib.contenttypes import generic
 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']
 
 
 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
 class ReadonlyTabularInline(PlStackTabularInline):
     can_delete = False
     extra = 0
@@ -34,10 +41,27 @@ class ReadonlyTabularInline(PlStackTabularInline):
     def has_add_permission(self, request):
         return False
 
     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']
 class TagInline(generic.GenericTabularInline):
     model = Tag
     exclude = ['enacted']
-    extra = 1
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-tags'
 
 class SliverInline(PlStackTabularInline):
     model = Sliver
 
 class SliverInline(PlStackTabularInline):
     model = Sliver
@@ -45,32 +69,51 @@ class SliverInline(PlStackTabularInline):
     extra = 0
     #readonly_fields = ['ip', 'instance_name', 'image']
     readonly_fields = ['ip', 'instance_name']
     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
     
 
 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
 
 class UserInline(PlStackTabularInline):
     model = User
     fields = ['email', 'firstname', 'lastname']
     extra = 0
+    suit_classes = 'suit-tab suit-tab-users'
 
 class SliceInline(PlStackTabularInline):
     model = Slice
 
 class SliceInline(PlStackTabularInline):
     model = Slice
+    fields = ['name','enabled','description','slice_url']
     extra = 0
     extra = 0
+    suit_classes = 'suit-tab suit-tab-slices'
+
 
 class RoleInline(PlStackTabularInline):
     model = Role
     extra = 0 
 
 class RoleInline(PlStackTabularInline):
     model = Role
     extra = 0 
+    suit_classes = 'suit-tab suit-tab-roles'
 
 class NodeInline(PlStackTabularInline):
     model = Node
     extra = 0
 
 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
 
 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':
 
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
         if db_field.name == 'site':
@@ -94,10 +137,17 @@ class SitePrivilegeInline(PlStackTabularInline):
                 kwargs['queryset'] = users
         return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
 
                 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', 'site','role')
+
+class SlicePrivilegeInline(PlStackTabularInline):
+    model = SlicePrivilege
+    suit_classes = 'suit-tab suit-tab-sliceprivileges'
     extra = 0
     extra = 0
-    fields = ('user', 'role')
+    fields = ('user', 'slice','role')
 
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
         if db_field.name == 'slice':
 
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
         if db_field.name == 'slice':
@@ -119,7 +169,7 @@ class SliceMembershipInline(PlStackTabularInline):
                 users = User.objects.filter(email__in=emails) 
                 kwargs['queryset'] = list(users)
 
                 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
 
 class SliceTagInline(PlStackTabularInline):
     model = SliceTag
@@ -137,11 +187,38 @@ class PlanetStackBaseAdmin(admin.ModelAdmin):
     save_on_top = False
     exclude = ['enacted']
 
     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 = [
 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):
 
 
 class DeploymentAdminForm(forms.ModelForm):
@@ -155,47 +232,30 @@ class DeploymentAdminForm(forms.ModelForm):
     class Meta:
         model = Deployment
 
     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
 
 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 = [
 
 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',)
     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):
     search_fields = ['name']
 
     def queryset(self, request):
@@ -229,7 +289,7 @@ class SiteAdmin(PlanetStackBaseAdmin):
 
 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
     fieldsets = [
 
 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
     fieldsets = [
-        (None, {'fields': ['user', 'site', 'role']})
+        (None, {'fields': ['user', 'site', 'role'], 'classes':['collapse']})
     ]
     list_display = ('user', 'site', 'role')
 
     ]
     list_display = ('user', 'site', 'role')
 
@@ -269,9 +329,17 @@ class SitePrivilegeAdmin(PlanetStackBaseAdmin):
         return qs
 
 class SliceAdmin(PlanetStackBaseAdmin):
         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')
     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':
 
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
         if db_field.name == 'site':
@@ -317,7 +385,7 @@ class SliceAdmin(PlanetStackBaseAdmin):
         obj.caller = request.user
         obj.save() 
 
         obj.caller = request.user
         obj.save() 
 
-class SliceMembershipAdmin(PlanetStackBaseAdmin):
+class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
     fieldsets = [
         (None, {'fields': ['user', 'slice', 'role']})
     ]
     fieldsets = [
         (None, {'fields': ['user', 'slice', 'role']})
     ]
@@ -344,12 +412,12 @@ class SliceMembershipAdmin(PlanetStackBaseAdmin):
                 users = User.objects.filter(email__in=emails)
                 kwargs['queryset'] = users
 
                 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.
 
     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)
         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 @@ class SliceMembershipAdmin(PlanetStackBaseAdmin):
         obj.delete()
 
 
         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):
 
 class NodeAdmin(admin.ModelAdmin):
+    form = NodeForm
+    exclude = ['enacted']
     list_display = ('name', 'site', 'deployment')
     list_filter = ('deployment',)
     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):
 
 
 class SliverForm(forms.ModelForm):
@@ -391,26 +479,41 @@ class SliverForm(forms.ModelForm):
         widgets = {
             'ip': PlainTextWidget(),
             'instance_name': PlainTextWidget(),
         widgets = {
             'ip': PlainTextWidget(),
             'instance_name': PlainTextWidget(),
+            'slice': LinkedSelect,
+            'deploymentNetwork': LinkedSelect,
+            'node': LinkedSelect,
+            'image': LinkedSelect
         }
 
 class ProjectAdmin(admin.ModelAdmin):
     exclude = ['enacted']
         }
 
 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']
 
 class TagAdmin(admin.ModelAdmin):
     exclude = ['enacted']
+    list_display = ['project', 'name', 'value', 'content_type', 'content_object',]
 
 class SliverAdmin(PlanetStackBaseAdmin):
     form = SliverForm
     fieldsets = [
 
 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']
     ]
     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:
     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)
                 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 UserCreationForm(forms.ModelForm):
 
     class Meta:
         model = User
 
     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
 
     def clean_password2(self):
         # Check that the two password entries match
@@ -518,24 +621,26 @@ class UserAdmin(UserAdmin):
     # 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.
     # 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 = (
     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',),
         #('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 = ()
 
         ),
     )
     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:
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
         if db_field.name == 'site':
             if not request.user.is_admin:
@@ -562,6 +667,7 @@ class ReservedResourceInline(admin.TabularInline):
     exclude = ['enacted']
     model = ReservedResource
     extra = 0
     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)
 
     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 ReservedResourceInline(admin.TabularInline):
 class ReservationChangeForm(forms.ModelForm):
     class Meta:
         model = Reservation
 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()"}))
 
 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 ReservationAddForm(forms.ModelForm):
 
     class Meta:
         model = Reservation
 
     class Meta:
         model = Reservation
+        widgets = {
+            'slice' : LinkedSelect
+        }
+
 
 class ReservationAddRefreshForm(ReservationAddForm):
     """ This form is displayed when the Reservation Form receives an update
 
 class ReservationAddRefreshForm(ReservationAddForm):
     """ This form is displayed when the Reservation Form receives an update
@@ -629,10 +742,14 @@ class ReservationAddRefreshForm(ReservationAddForm):
 
 class ReservationAdmin(admin.ModelAdmin):
     exclude = ['enacted']
 
 class ReservationAdmin(admin.ModelAdmin):
     exclude = ['enacted']
+    fieldsets = [('Reservation Details', {'fields': ['startTime', 'duration','slice'], 'classes': ['suit-tab suit-tab-general']})]
     list_display = ('startTime', 'duration')
     list_display = ('startTime', 'duration')
-    inlines = [ReservedResourceInline]
     form = ReservationAddForm
 
     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
     def add_view(self, request, form_url='', extra_context=None):
         timezone.activate(request.user.timezone)
         request._refresh = False
@@ -700,7 +817,7 @@ admin.site.unregister(Evolution)
 
 # When debugging it is often easier to see all the classes, but for regular use 
 # only the top-levels should be displayed
 
 # 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)
 
 admin.site.register(Deployment, DeploymentAdmin)
 admin.site.register(Site, SiteAdmin)
@@ -708,13 +825,19 @@ admin.site.register(Slice, SliceAdmin)
 admin.site.register(Project, ProjectAdmin)
 admin.site.register(ServiceClass, ServiceClassAdmin)
 admin.site.register(Reservation, ReservationAdmin)
 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:
 
 if showAll:
+    #admin.site.register(PlanetStack)
     admin.site.register(Tag, TagAdmin)
     admin.site.register(Node, NodeAdmin)
     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(Role, RoleAdmin)
+    admin.site.register(Member, MemberAdmin)
     admin.site.register(Sliver, SliverAdmin)
     admin.site.register(Image, ImageAdmin)
 
     admin.site.register(Sliver, SliverAdmin)
     admin.site.register(Image, ImageAdmin)
 
index a86728a..f034820 100644 (file)
         "maxDuration": 8760, 
         "enacted": null
     }
         "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"
+    }
 }
 ]
 }
 ]
index 2280822..1cc4d07 100644 (file)
@@ -1,17 +1,25 @@
 from .plcorebase import PlCoreBase
 from .plcorebase import PlCoreBase
-from .deployment import Deployment
+from .planetstack import PlanetStack
 from .project import Project
 from .tag import Tag
 from .project import Project
 from .tag import Tag
+from .role import Role
+from .deployment import Deployment
 from .site import Site
 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 .site import SitePrivilege
 from .image import Image
-from .user import User
-from .role import Role
 from .node import Node
 from .node import Node
-from .serviceclass import ServiceClass
 from .serviceresource import ServiceResource
 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
 from .slicetag import SliceTag
 from .sliver import Sliver
 from .reservation import ReservedResource
index d38115f..4e835d0 100644 (file)
@@ -1,11 +1,30 @@
 import os
 from django.db import models
 from core.models import PlCoreBase
 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")
 
 # 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)
 
 
     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 (file)
index 0000000..9007a51
--- /dev/null
@@ -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)
+
+
+
index fd29848..234868e 100644 (file)
@@ -2,14 +2,16 @@ import os
 import datetime
 from django.db import models
 from core.models import PlCoreBase
 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):
 
 
 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):
 
 
     def save(self, *args, **kwds):
index 8a6d7c4..aee3843 100644 (file)
@@ -24,11 +24,18 @@ class Site(PlCoreBase):
 
     def __unicode__(self):  return u'%s' % (self.name)
 
 
     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')
 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)
 
 
     def __unicode__(self):  return u'%s %s %s' % (self.site, self.user, self.role)
 
index 74815b2..e584c07 100644 (file)
@@ -40,15 +40,16 @@ class Slice(PlCoreBase):
             self.creator = self.caller
         super(Slice, self).save(*args, **kwds)
 
             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'))
 
 
-    def __unicode__(self):  return u'%s %s %s' % (self.slice, self.user, self.role)
+    role = models.CharField(choices=ROLE_CHOICES, unique=True, max_length=30)
 
 
-    def save(self, *args, **kwds):
-        super(SliceMembership, self).save(*args, **kwds)
+    def __unicode__(self):  return u'%s' % (self.role)
 
 
-    def delete(self, *args, **kwds):
-        super(SliceMembership, self).delete(*args, **kwds)
+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)
index 758bcbf..2b63dda 100644 (file)
@@ -2,11 +2,11 @@ import os
 import datetime
 from collections import defaultdict
 from django.db import models
 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
 
 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):
 # Create your models here.
 class UserManager(BaseUserManager):
     def create_user(self, email, firstname, lastname, password=None):
@@ -54,6 +54,7 @@ class User(AbstractBaseUser):
         unique=True,
         db_index=True,
     )
         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)
 
     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 @@ class User(AbstractBaseUser):
 
     phone = models.CharField(null=True, blank=True, help_text="phone number contact", max_length=100)
     user_url = models.URLField(null=True, blank=True)
 
     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)
     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 @@ class User(AbstractBaseUser):
         # Simplest possible answer: Yes, always
         return True
 
         # Simplest possible answer: Yes, always
         return True
 
-    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 is_superuser(self):
+        return False
+
+#    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:
 
     def save(self, *args, **kwds):
         if not self.id:
index 94f5c3c..b83157b 100644 (file)
@@ -3,38 +3,108 @@ from rest_framework import serializers
 from core.models import *
 
 
 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', 
 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()
     # 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:
     class Meta:
-        model = User
+        model = ServiceClass
         fields = ('id',
         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()
 class SliceSerializer(serializers.HyperlinkedModelSerializer):
     # HyperlinkedModelSerializer doesn't include the id by default
     id = serializers.Field()
@@ -43,6 +113,7 @@ class SliceSerializer(serializers.HyperlinkedModelSerializer):
     class Meta:
         model = Slice
         fields = ('id',
     class Meta:
         model = Slice
         fields = ('id',
+                  'url',
                   'tenant_id',
                   'enabled',
                   'name',
                   'tenant_id',
                   'enabled',
                   'name',
@@ -58,14 +129,15 @@ class SliceSerializer(serializers.HyperlinkedModelSerializer):
                   'updated',
                   'created')
 
                   '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:
     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',
         fields = ('id',
+                  'url',
                   'user',
                   'slice',
                   'role')
                   'user',
                   'slice',
                   'role')
@@ -105,75 +177,90 @@ class SitePrivilegeSerializer(serializers.HyperlinkedModelSerializer):
     class Meta:
         model = SitePrivilege
         fields = ('id',
     class Meta:
         model = SitePrivilege
         fields = ('id',
+                  'url',
                   'user',
                   'site',
                   'role')
 
                   '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')
 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')
     
     node = serializers.HyperlinkedRelatedField(view_name='node-detail')
     
-    
     #slice = serializers.PrimaryKeyRelatedField(read_only=True)
 
     class Meta:
         model = Sliver
         fields = ('id',
     #slice = serializers.PrimaryKeyRelatedField(read_only=True)
 
     class Meta:
         model = Sliver
         fields = ('id',
+                  'url',
                   'instance_id',
                   'name',
                   'instance_name',
                   'ip',
                   'image',
                   'slice',
                   'instance_id',
                   'name',
                   'instance_name',
                   'ip',
                   'image',
                   'slice',
-                  'deployment',
+                  'deploymentNetwork',
                   'node')
 
                   'node')
 
-class NodeSerializer(serializers.HyperlinkedModelSerializer):
+class UserSerializer(serializers.HyperlinkedModelSerializer):
     # HyperlinkedModelSerializer doesn't include the id by default
     id = serializers.Field()
     # 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:
     class Meta:
-        model = Node
+        model = User
         fields = ('id',
         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()
     # 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:
     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 = { 
 
 serializerLookUp = { 
+                 Deployment: DeploymentSerializer,
+                 Image: ImageSerializer,
+                 Node: NodeSerializer,
+                 Project: ProjectSerializer,
+                 Reservation: ReservationSerializer,
                  Role: RoleSerializer,
                  Role: RoleSerializer,
-                 User: UserSerializer,
+                 ServiceClass: ServiceClassSerializer,
+                 ServiceResource: ServiceResourceSerializer,
                  Site: SiteSerializer,
                  SitePrivilege: SitePrivilegeSerializer,
                  Slice: SliceSerializer,
                  Site: SiteSerializer,
                  SitePrivilege: SitePrivilegeSerializer,
                  Slice: SliceSerializer,
-                 SliceMembership: SliceMembershipSerializer,
-                 Node: NodeSerializer,
+                 SlicePrivilege: SlicePrivilegeSerializer,
                  Sliver: SliverSerializer,
                  Sliver: SliverSerializer,
-                 Deployment: DeploymentSerializer,
-                 Image: ImageSerializer,
+                 Tag: TagSerializer,
+                 User: UserSerializer,
                  None: None,
                 }
 
                  None: None,
                 }
 
index 2034708..b517eac 100644 (file)
@@ -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 (file)
index 13f0707..0000000
+++ /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 (file)
index 0000000..4dd1f93
--- /dev/null
@@ -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
+
+
diff --git a/planetstack/hpc/__init__.py b/planetstack/hpc/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/planetstack/hpc/admin.py b/planetstack/hpc/admin.py
new file mode 100644 (file)
index 0000000..3afb448
--- /dev/null
@@ -0,0 +1,68 @@
+from django.contrib import admin
+
+from hpc.models import *
+from django import forms
+from django.utils.safestring import mark_safe
+from django.contrib.auth.admin import UserAdmin
+from django.contrib.admin.widgets import FilteredSelectMultiple
+from django.contrib.auth.forms import ReadOnlyPasswordHashField
+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
+
+#class HPCRRBaseAdmin(admin.ModelAdmin):
+    #exclude = ['enacted']
+
+class CDNPrefixInline(admin.TabularInline):
+    model = CDNPrefix
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-prefixes'
+
+class ContentProviderInline(admin.TabularInline):
+    model = ContentProvider
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-cps'
+
+class OriginServerAdmin(admin.ModelAdmin):
+    list_display = ('url','protocol','redirects','contentProvider','authenticated','enabled' )
+
+class ContentProviderForm(forms.ModelForm):
+    class Meta:
+        widgets = {
+            'serviceProvider' : LinkedSelect
+        }
+
+class ContentProviderAdmin(admin.ModelAdmin):
+    form = ContentProviderForm
+    list_display = ('name','description','enabled' )
+    fieldsets = [ (None, {'fields': ['name','enabled','description','serviceProvider','users'], 'classes':['suit-tab suit-tab-general']})]
+
+    inlines = [CDNPrefixInline]
+
+    suit_form_tabs = (('general','Details'),('prefixes','CDN Prefixes'))
+
+class ServiceProviderAdmin(admin.ModelAdmin):
+    list_display = ('name', 'description', 'enabled')
+    fieldsets = [
+        (None, {'fields': ['name','description','enabled'], 'classes':['suit-tab suit-tab-general']})]
+#, ('Content Providers', {'fields':['contentProviders'],'classes':['suit-tab suit-tab-cps']})] 
+
+    suit_form_tabs = (('general','Details'),('cps','Content Providers'))
+    inlines = [ContentProviderInline]
+
+class CDNPrefixForm(forms.ModelForm):
+    class Meta:
+        widgets = {
+            'contentProvider' : LinkedSelect
+        }
+
+class CDNPrefixAdmin(admin.ModelAdmin):
+    form = CDNPrefixForm
+    list_display = ['prefix','contentProvider']
+
+admin.site.register(ServiceProvider, ServiceProviderAdmin)
+admin.site.register(ContentProvider, ContentProviderAdmin)
+admin.site.register(CDNPrefix, CDNPrefixAdmin)
+admin.site.register(OriginServer,OriginServerAdmin)
+
diff --git a/planetstack/hpc/models.py b/planetstack/hpc/models.py
new file mode 100644 (file)
index 0000000..d257032
--- /dev/null
@@ -0,0 +1,92 @@
+from django.db import models
+from core.models import User
+import os
+from django.db import models
+from django.forms.models import model_to_dict
+
+
+# Create your models here.
+
+class HpcCoreBase(models.Model):
+
+    created = models.DateTimeField(auto_now_add=True)
+    updated = models.DateTimeField(auto_now=True)
+
+    class Meta:
+        abstract = True
+        app_label = "hpc"
+
+    def __init__(self, *args, **kwargs):
+        super(HpcCoreBase, self).__init__(*args, **kwargs)
+        self.__initial = self._dict
+
+    @property
+    def diff(self):
+        d1 = self.__initial
+        d2 = self._dict
+        diffs = [(k, (v, d2[k])) for k, v in d1.items() if v != d2[k]]
+        return dict(diffs)
+
+    @property
+    def has_changed(self):
+        return bool(self.diff)
+
+    @property
+    def changed_fields(self):
+        return self.diff.keys()
+
+    def get_field_diff(self, field_name):
+        return self.diff.get(field_name, None)
+
+    def save(self, *args, **kwargs):
+        super(HpcCoreBase, self).save(*args, **kwargs)
+
+        self.__initial = self._dict
+
+    @property
+    def _dict(self):
+        return model_to_dict(self, fields=[field.name for field in
+                             self._meta.fields])
+
+    
+class ServiceProvider(HpcCoreBase):
+    name = models.CharField(max_length=254,help_text="Service Provider Name")
+    description = models.TextField(max_length=254,null=True, blank=True, help_text="Description of Service Provider")
+    enabled = models.BooleanField(default=True)
+
+    def __unicode__(self):  return u'%s' % (self.name)
+
+class ContentProvider(HpcCoreBase):
+    name = models.CharField(max_length=254)
+    enabled = models.BooleanField(default=True)
+    description = models.TextField(max_length=254,null=True, blank=True,help_text="Description of Content Provider")
+    serviceProvider = models.ForeignKey(ServiceProvider)
+
+    # Note user relationships are directed not requiring a role.
+    users = models.ManyToManyField(User)
+
+    def __unicode__(self):  return u'%s' % (self.name)
+
+class OriginServer(HpcCoreBase):
+    url = models.URLField()
+    contentProvider = models.ForeignKey(ContentProvider)
+
+    authenticated = models.BooleanField(default=False, help_text="Status for this Site")
+    enabled = models.BooleanField(default=True, help_text="Status for this Site")
+    PROTOCOL_CHOICES = (('http', 'HTTP'),('rtmp', 'RTMP'), ('rtp', 'RTP'),('shout', 'SHOUTcast')) 
+    protocol = models.CharField(default="HTTP", max_length = 12, choices=PROTOCOL_CHOICES)
+    redirects = models.BooleanField(default=True, help_text="Indicates whether Origin Server redirects should be used for this Origin Server")
+    description = models.TextField(null=True, blank=True, max_length=255)
+    
+    def __unicode__(self):  return u'%s' % (self.url)
+
+class CDNPrefix(HpcCoreBase):
+    prefix = models.CharField(max_length=200, help_text="Registered Prefix for Domain")
+    contentProvider = models.ForeignKey(ContentProvider)
+    description = models.TextField(max_length=254,null=True, blank=True,help_text="Description of Content Provider")
+
+    defaultOriginServer = models.ForeignKey(OriginServer)
+    enabled = models.BooleanField(default=True)
+
+    def __unicode__(self):  return u'%s' % (self.prefix)
+
diff --git a/planetstack/hpc/tests.py b/planetstack/hpc/tests.py
new file mode 100644 (file)
index 0000000..501deb7
--- /dev/null
@@ -0,0 +1,16 @@
+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+
+class SimpleTest(TestCase):
+    def test_basic_addition(self):
+        """
+        Tests that 1 + 1 always equals 2.
+        """
+        self.assertEqual(1 + 1, 2)
diff --git a/planetstack/hpc/views.py b/planetstack/hpc/views.py
new file mode 100644 (file)
index 0000000..60f00ef
--- /dev/null
@@ -0,0 +1 @@
+# Create your views here.
index 91be3dc..7593650 100644 (file)
@@ -1,3 +1,5 @@
+from django.conf.global_settings import TEMPLATE_CONTEXT_PROCESSORS as TCP
+
 # Django settings for planetstack project.
 from config import Config
 config = Config()
 # Django settings for planetstack project.
 from config import Config
 config = Config()
@@ -25,6 +27,7 @@ DATABASES = {
 
 AUTH_USER_MODEL = 'core.User'
 
 
 AUTH_USER_MODEL = 'core.User'
 
+
 # Hosts/domain names that are valid for this site; required if DEBUG is False
 # See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
 ALLOWED_HOSTS = []
 # Hosts/domain names that are valid for this site; required if DEBUG is False
 # See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
 ALLOWED_HOSTS = []
@@ -72,7 +75,7 @@ STATIC_ROOT = ''
 STATIC_URL = '/static/'
 
 # Additional locations of static files
 STATIC_URL = '/static/'
 
 # Additional locations of static files
-STATICFILES_DIRS = (
+STATICFILES_DIRS = ( "/opt/planetstack/core/static/",
     # Put strings here, like "/home/html/static" or "C:/www/django/static".
     # Always use forward slashes, even on Windows.
     # Don't forget to use absolute paths, not relative paths.
     # Put strings here, like "/home/html/static" or "C:/www/django/static".
     # Always use forward slashes, even on Windows.
     # Don't forget to use absolute paths, not relative paths.
@@ -126,6 +129,7 @@ INSTALLED_APPS = (
     'django.contrib.messages',
     'django.contrib.staticfiles',
     # Uncomment the next line to enable the admin:
     'django.contrib.messages',
     'django.contrib.staticfiles',
     # Uncomment the next line to enable the admin:
+    'suit',
     'django.contrib.admin',
     # Uncomment the next line to enable admin documentation:
     'django.contrib.admindocs',
     'django.contrib.admin',
     # Uncomment the next line to enable admin documentation:
     'django.contrib.admindocs',
@@ -133,9 +137,47 @@ INSTALLED_APPS = (
     'django_extensions',
     'django_evolution',
     'core',
     'django_extensions',
     'django_evolution',
     'core',
-    'geoposition'
+    'hpc',
+    'geoposition',
 )
 
 )
 
+
+# Added for django-suit form 
+TEMPLATE_CONTEXT_PROCESSORS = TCP + (
+    'django.core.context_processors.request',
+)
+
+# Django Suit configuration example
+SUIT_CONFIG = {
+    # header
+    'ADMIN_NAME': 'PlanetStack',
+    # 'HEADER_DATE_FORMAT': 'l, j. F Y',
+    # 'HEADER_TIME_FORMAT': 'H:i',
+
+    # forms
+    #'SHOW_REQUIRED_ASTERISK': True,  # Default True
+    'CONFIRM_UNSAVED_CHANGES': True, # Default True
+
+    # menu
+    # 'SEARCH_URL': '/admin/auth/user/',
+    # 'MENU_ICONS': {
+    #    'sites': 'icon-leaf',
+    #    'auth': 'icon-lock',
+    # },
+    # 'MENU_OPEN_FIRST_CHILD': True, # Default True
+    'MENU_EXCLUDE': ('auth.group','auth'),
+    'MENU': (
+    ),
+    #     'sites',
+    #     {'app': 'auth', 'icon':'icon-lock', 'models': ('user', 'group')},
+    #     {'label': 'Settings', 'icon':'icon-cog', 'models': ('auth.user', 'auth.group')},
+    #     {'label': 'Support', 'icon':'icon-question-sign', 'url': '/support/'},
+    # ),
+
+    # misc
+    # 'LIST_PER_PAGE': 15
+}
+
 # A sample logging configuration. The only tangible logging
 # performed by this configuration is to send an email to
 # the site admins on every HTTP 500 error when DEBUG=False.
 # A sample logging configuration. The only tangible logging
 # performed by this configuration is to send an email to
 # the site admins on every HTTP 500 error when DEBUG=False.
index 638e3b1..039c206 100644 (file)
@@ -2,16 +2,21 @@ from django.conf.urls import patterns, include, url
 
 # Uncomment the next two lines to enable the admin:
 from django.contrib import admin
 
 # Uncomment the next two lines to enable the admin:
 from django.contrib import admin
+from core.views.deployment import DeploymentList, DeploymentDetail
+from core.views.images import ImageList, ImageDetail
+from core.views.nodes import NodeList, NodeDetail
+from core.views.projects import ProjectList, ProjectDetail
+from core.views.reservations import ReservationList, ReservationDetail
 from core.views.roles import RoleList, RoleDetail
 from core.views.roles import RoleList, RoleDetail
+from core.views.serviceclasses import ServiceClassList, ServiceClassDetail
+from core.views.serviceresources import ServiceResourceList, ServiceResourceDetail
 from core.views.sites import SiteList, SiteDetail
 from core.views.site_privileges import SitePrivilegeList, SitePrivilegeDetail
 from core.views.sites import SiteList, SiteDetail
 from core.views.site_privileges import SitePrivilegeList, SitePrivilegeDetail
-from core.views.users import UserList, UserDetail
 from core.views.slices import SliceList, SliceDetail
 from core.views.slices import SliceList, SliceDetail
-from core.views.slice_memberships import SliceMembershipList, SliceMembershipDetail
+from core.views.slice_privileges import SlicePrivilegeList, SlicePrivilegeDetail
 from core.views.slivers import SliverList, SliverDetail
 from core.views.slivers import SliverList, SliverDetail
-from core.views.deployment_networks import DeploymentList, DeploymentDetail
-from core.views.images import ImageList, ImageDetail
-from core.views.nodes import NodeList, NodeDetail
+from core.views.tags import TagList, TagDetail
+from core.views.users import UserList, UserDetail
 from core.models import *
 from core.api_root import api_root
 from rest_framework import generics
 from core.models import *
 from core.api_root import api_root
 from rest_framework import generics
@@ -31,36 +36,51 @@ urlpatterns = patterns('',
 
     url(r'^plstackapi/$', api_root),
     
 
     url(r'^plstackapi/$', api_root),
     
+    url(r'^plstackapi/deployments/$', DeploymentList.as_view(), name='deployment-list'),
+    url(r'^plstackapi/deployments/(?P<pk>[a-zA-Z0-9\-]+)/$', DeploymentDetail.as_view(), name='deployment-detail'),
+
+    url(r'^plstackapi/images/$', ImageList.as_view(), name='image-list'),
+    url(r'^plstackapi/images/(?P<pk>[a-zA-Z0-9_\-]+)/$', ImageDetail.as_view(), name='image-detail'),
+
+    url(r'^plstackapi/nodes/$', NodeList.as_view(), name='node-list'),
+    url(r'^plstackapi/nodes/(?P<pk>[a-zA-Z0-9_\-]+)/$', NodeDetail.as_view(), name='node-detail'),
+    
+    url(r'^plstackapi/projects/$', ProjectList.as_view(), name='project-list'),
+    url(r'^plstackapi/projects/(?P<pk>[a-zA-Z0-9_\-]+)/$', ProjectDetail.as_view(), name='project-detail'),
+    
+    url(r'^plstackapi/reservations/$', ReservationList.as_view(), name='reservation-list'),
+    url(r'^plstackapi/reservations/(?P<pk>[a-zA-Z0-9_\-]+)/$', ReservationDetail.as_view(), name='reservation-detail'),
+    
     url(r'^plstackapi/roles/$', RoleList.as_view(), name='role-list'),
     url(r'^plstackapi/roles/(?P<pk>[a-zA-Z0-9]+)/$', RoleDetail.as_view(), name='role-detail'),
 
     url(r'^plstackapi/roles/$', RoleList.as_view(), name='role-list'),
     url(r'^plstackapi/roles/(?P<pk>[a-zA-Z0-9]+)/$', RoleDetail.as_view(), name='role-detail'),
 
-    url(r'^plstackapi/users/$', UserList.as_view(), name='user-list'),
-    url(r'^plstackapi/users/(?P<pk>[a-zA-Z0-9_\-]+)/$', UserDetail.as_view(), name='user-detail'),
+    url(r'^plstackapi/serviceclasses/$', ServiceClassList.as_view(), name='serviceclass-list'),
+    url(r'^plstackapi/serviceclasses/(?P<pk>[a-zA-Z0-9]+)/$', ServiceClassDetail.as_view(), name='serviceclass-detail'),
 
 
-    url(r'^plstackapi/sites/$', SiteList.as_view(), name='site-list'),
-    url(r'^plstackapi/sites/(?P<pk>[a-zA-Z0-9_\-]+)/$', SiteDetail.as_view(), name='site-detail'),
+    url(r'^plstackapi/serviceresources/$', ServiceResourceList.as_view(), name='serviceresource-list'),
+    url(r'^plstackapi/serviceresources/(?P<pk>[a-zA-Z0-9]+)/$', ServiceResourceDetail.as_view(), name='serviceresource-detail'),
 
     url(r'^plstackapi/site_privileges/$', SitePrivilegeList.as_view(), name='siteprivilege-list'),
     url(r'^plstackapi/site_privileges/(?P<pk>[a-zA-Z0-9_]+)/$', SitePrivilegeDetail.as_view(), name='siteprivilege-detail'),
   
 
     url(r'^plstackapi/site_privileges/$', SitePrivilegeList.as_view(), name='siteprivilege-list'),
     url(r'^plstackapi/site_privileges/(?P<pk>[a-zA-Z0-9_]+)/$', SitePrivilegeDetail.as_view(), name='siteprivilege-detail'),
   
+    url(r'^plstackapi/sites/$', SiteList.as_view(), name='site-list'),
+    url(r'^plstackapi/sites/(?P<pk>[a-zA-Z0-9_\-]+)/$', SiteDetail.as_view(), name='site-detail'),
+
     url(r'^plstackapi/slices/$', SliceList.as_view(), name='slice-list'),
 
     url(r'^plstackapi/slices/(?P<pk>[a-zA-Z0-9_\-]+)/$', SliceDetail.as_view(), name='slice-detail'),
 
     url(r'^plstackapi/slices/$', SliceList.as_view(), name='slice-list'),
 
     url(r'^plstackapi/slices/(?P<pk>[a-zA-Z0-9_\-]+)/$', SliceDetail.as_view(), name='slice-detail'),
 
-    url(r'^plstackapi/slice_memberships/$', SliceMembershipList.as_view(), name='slice-membership-list'),
-    url(r'^plstackapi/slice_memberships/(?P<pk>[0-9]+)/$', SliceMembershipDetail.as_view(), name='slice-membership-detail'),
+    url(r'^plstackapi/slice_memberships/$', SlicePrivilegeList.as_view(), name='sliceprivilege-list'),
+    url(r'^plstackapi/slice_memberships/(?P<pk>[0-9]+)/$', SlicePrivilegeDetail.as_view(), name='sliceprivilege-detail'),
     
     url(r'^plstackapi/slivers/$', SliverList.as_view(), name='sliver-list'),
     url(r'^plstackapi/slivers/(?P<pk>[a-zA-Z0-9_\-]+)/$', SliverDetail.as_view(), name='sliver-detail'),
 
     
     url(r'^plstackapi/slivers/$', SliverList.as_view(), name='sliver-list'),
     url(r'^plstackapi/slivers/(?P<pk>[a-zA-Z0-9_\-]+)/$', SliverDetail.as_view(), name='sliver-detail'),
 
-    url(r'^plstackapi/nodes/$', NodeList.as_view(), name='node-list'),
-    url(r'^plstackapi/nodes/(?P<pk>[a-zA-Z0-9_\-]+)/$', NodeDetail.as_view(), name='node-detail'),
-    
-    url(r'^plstackapi/deployments/$', DeploymentList.as_view(), name='deployment-list'),
-    url(r'^plstackapi/deployments/(?P<pk>[a-zA-Z0-9\-]+)/$', DeploymentDetail.as_view(), name='deployment-detail'),
+    url(r'^plstackapi/tags/$', TagList.as_view(), name='tag-list'),
+    url(r'^plstackapi/tags/(?P<pk>[a-zA-Z0-9_\-]+)/$', TagDetail.as_view(), name='tag-detail'),
 
 
-    url(r'^plstackapi/images/$', ImageList.as_view(), name='image-list'),
-    url(r'^plstackapi/images/(?P<pk>[a-zA-Z0-9_\-]+)/$', ImageDetail.as_view(), name='image-detail'),
+    url(r'^plstackapi/users/$', UserList.as_view(), name='user-list'),
+    url(r'^plstackapi/users/(?P<pk>[a-zA-Z0-9_\-]+)/$', UserDetail.as_view(), name='user-detail'),
 
     #Adding in rest_framework urls
     url(r'^plstackapi/', include('rest_framework.urls', namespace='rest_framework')),
 
     #Adding in rest_framework urls
     url(r'^plstackapi/', include('rest_framework.urls', namespace='rest_framework')),
diff --git a/planetstack/templates/admin/base.html b/planetstack/templates/admin/base.html
new file mode 100644 (file)
index 0000000..477e941
--- /dev/null
@@ -0,0 +1,212 @@
+{% load admin_static %}{% load suit_tags %}{% load url from future %}<!DOCTYPE html>
+<html lang="{{ LANGUAGE_CODE|default:"en-us" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
+<head>
+  <title>{% block title %}{{ title }} | {{ 'ADMIN_NAME'|suit_conf }}{% endblock %}</title>
+  <link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% endblock %}"/>
+  <link rel="stylesheet" type="text/css" href="{% static 'suit/bootstrap/css/bootstrap.min.css' %}" media="all"/>
+  <link rel="stylesheet" type="text/css" href="{% static 'suit/css/suit.css' %}" media="all">
+  <link rel="stylesheet" type="text/css" href="{% static 'planetstack.css' %}" media="all">
+  {% block extrastyle %}{% endblock %}
+  {% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% static "admin/css/rtl.css" %}{% endblock %}"/>{% endif %}
+  <script type="text/javascript">window.__admin_media_prefix__ = "{% filter escapejs %}{% static "admin/" %}{% endfilter %}";</script>
+  <script src="{% static 'suit/js/jquery-1.8.3.min.js' %}"></script>
+  <script type="text/javascript">var Suit = { $: $.noConflict() }; if (!$) $ = Suit.$; </script>
+  {% if 'SHOW_REQUIRED_ASTERISK'|suit_conf %}
+  <style type="text/css">.required:after { content: '*'; margin: 0 0 0 5px; position: absolute; color: #ccc;}</style>
+  {% endif %}
+  {% block extrahead %}{% endblock %}
+  {% block blockbots %}
+    <meta name="robots" content="NONE,NOARCHIVE"/>{% endblock %}
+</head>
+{% load i18n %}
+
+<body class="{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}">
+
+<!-- Sticky footer wrap -->
+<div id="wrap">
+
+  <!-- Container -->
+  {% block container %}
+    <div id="container">
+
+      {% block header %}
+        {% if not is_popup %}
+          <!-- Header -->
+          <div id="header" class="header">
+            <div id="branding">
+              <a href="{% url 'admin:index' %}"><h1 id="site-name">{% block branding %}{{ 'ADMIN_NAME'|suit_conf }}{% endblock %}</h1></a>
+            </div>
+
+            {% block header_time %}
+            <div class="header-content header-content-first">
+              <div class="header-column icon">
+                <i class="icon-time"></i>
+              </div>
+              <div class="header-column">
+                <span class="date"> {% suit_date %}</span><br>
+                <span class="time" id="clock">{% suit_time %}</span>
+              </div>
+            </div>
+            {% endblock %}
+
+            {% block header_content %}
+              <!--<div class="header-content">
+                <div class="header-column icon">
+                  <i class="icon-comment"></i>
+                </div>
+                <div class="header-column">
+                  <a href="" class="grey"><b>2</b> new messages</a>
+                </div>
+              </div>-->
+            {% endblock %}
+
+            {% if user.is_active and user.is_staff %}
+              <div id="user-tools">
+                {% trans 'Welcome,' %}
+                <strong>
+                  {% filter force_escape %}
+                    {% firstof user.first_name user.username %}{% endfilter %}</strong>.
+                <span class="user-links">
+                {% block userlinks %}
+                  {% url 'django-admindocs-docroot' as docsroot %}
+                  {% if docsroot %}
+                    <a href="{{ docsroot }}">{% trans 'Documentation' %}</a>
+                   <span class="separator">|</span>
+                  {% endif %}
+                  <a href="{% url 'admin:password_change' %}">{% trans 'Change password' %}</a>
+                  <span class="separator">|</span>
+                  <a href="{% url 'admin:logout' %}">{% trans 'Log out' %}</a>
+                  </span>
+                {% endblock %}
+              </div>
+            {% endif %}
+            {% block nav-global %}{% endblock %}
+          </div>
+        {% endif %}
+        <!-- END Header -->
+      {% endblock %}
+
+
+      <div class="suit-columns {{ is_popup|yesno:'one-column,two-columns' }}">
+
+        {% block content-center %}
+          <div id="suit-center" class="suit-column">
+
+            {% if not is_popup %}
+              {% block breadcrumbs %}
+                <ul class="breadcrumb">
+                  <li><a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+                    {% if title %}
+                      <span class="divider">&raquo;</span>
+                      </li>
+                      <li class="active">
+                      {{ title }}
+                    {% endif %}
+                    </li>
+                </ul>
+              {% endblock %}
+            {% endif %}
+
+            {% block messages %}
+              {% if messages %}
+
+                {% for message in messages %}
+                  <div class="alert alert-{% firstof message.tags 'info' %}">
+                    <button class="close" data-dismiss="alert">×</button>
+                    <strong>
+                      {% if message.tags %}{{ message.tags|capfirst }}{% else %}
+                        Message{% endif %}!</strong>
+                    {{ message }}
+                  </div>
+                {% endfor %}
+              {% endif %}
+            {% endblock messages %}
+
+            <!-- Content -->
+            <div id="content" class="{% block coltype %}colM{% endblock %} row-fluid">
+              {% block pretitle %}{% endblock %}
+              {% block content_title %}{% if title %}
+                <h2 class="content-title">{{ title }}</h2>
+              {% endif %}{% endblock %}
+              {% block content %}
+                {% block object-tools %}{% endblock %}
+                {{ content }}
+              {% endblock %}
+              {% block sidebar_content %}
+                {% block sidebar %}{% endblock %}
+              {% endblock %}
+            </div>
+            <!-- END Content -->
+          </div>
+        {% endblock %}
+
+
+        {% block content-left %}
+          {% if not is_popup %}
+            <div id="suit-left" class="suit-column">
+              {% block quick-search %}
+                {% with 'SEARCH_URL'|suit_conf as search_url %}
+                  {% if search_url %}
+                    <form class="form-search nav-quick-search" autocomplete="off" action="{% if '/' in search_url %}{{ search_url }}{% else %}{% url search_url %}{% endif %}" method="GET">
+                      <input type="text" name="q" class="input-medium search-query" id="quick-search">
+                      <i class="input-icon icon-search"></i>
+                      <input type="submit" class="submit" value="">
+                    </form>
+                  {% endif %}
+                {% endwith %}
+              {% endblock %}
+
+              {% include 'suit/menu.html' %}
+
+            </div>
+          {% endif %}
+        {% endblock %}
+
+      </div>
+    </div>
+  {% endblock %}
+
+  {% if not is_popup %}
+  <!-- Sticky footer push -->
+  <div id="push"></div>
+  {% endif %}
+
+</div>
+
+{% block footer %}
+  {% if not is_popup %}
+  <div id="footer" class="footer">
+    <div class="content">
+      <div class="tools">
+        {% block footer_links %}
+          <a href="http://djangosuit.com/support/" target="_blank" class="icon"><i class="icon-question-sign"></i>Support</a>
+          <a href="http://djangosuit.com/pricing/" target="_blank" class="icon"><i class="icon-bookmark"></i>Licence</a>
+          <a href="http://github.com/darklow/django-suit/issues" target="_blank" class="icon"><i class="icon-comment"></i>Report a bug</a>
+        {% endblock %}
+      </div>
+
+      <div class="copyright">
+        {% block copyright %}
+          Copyright &copy; 2013 DjangoSuit.com<br>Developed by <a href="http://djangosuit.com" target="_blank">DjangoSuit.com</a>
+        {% endblock %}
+      </div>
+
+      <div class="branding">{% block footer_branding %}
+        {% with 'ADMIN_NAME'|suit_conf as admin_name %}
+          {{ admin_name }}
+          {% if admin_name == 'Django Suit' %}
+            v{{ 'VERSION'|suit_conf }}
+          {% endif %}
+        {% endwith %}
+      {% endblock %}</div>
+    </div>
+  </div>
+  {% endif %}
+{% endblock %}
+
+  <script src="{% static 'suit/bootstrap/js/bootstrap.min.js' %}"></script>
+  <script src="{% static 'suit/js/suit.js' %}"></script>
+  {% block extrajs %}{% endblock %}
+
+</body>
+</html>
index 2bd6c82..eae91f6 100644 (file)
@@ -1,18 +1,57 @@
 {% extends "admin/base.html" %}
 {% extends "admin/base.html" %}
-{% load i18n %}
-{% block title %}{{ title }} | {% trans 'PlanetStack' %}{% endblock %}
-{% block extrastyle %}
-<style>
-#header{ background-color: #333940; border-bottom: solid 3px #999; }
-#branding h1{ color: #fff; }
-.module h2, .module caption, .inline-group h2 { background:#ccc url(/admin_media/img/admin/nav-bg.gif) bottom left repeat-x; color: #333940; }
-a.section:link, a.section:visited { color: #666666; }
-</style>
-{% endblock %}
+{% load admin_static %}
+
+{# Additional <head> content here, some extra meta tags or favicon #}
+{#{% block extrahead %}#}
+{#{% endblock %}#}
+
+
+{# Additional CSS includes #}
+{# {% block extrastyle %} #}
+{# {% endblock %} #}
+
+
+{# Additional JS files in footer, right before </body> #}
+{#{% block extrajs %}#}
+{#  <script type="text/javascript" src="{% static 'js/my_project.js' %}"></script>#}
+{#{% endblock %}#}
 
 
-{% block branding %}
-<h1 id="site-name">{% trans 'PlanetStack Administration' %}</h1>
+
+{# Footer links (left side) #}
+{#{% block footer_links %}#}
+{#  <a href="/docs/" class="icon"><i class="icon-question-sign"></i>Documentation</a>#}
+{#{% endblock %}#}
+
+{# Additional header content like notifications or language switcher #}
+{#{% block header_content %}#}
+{#    {{ block.super }}#}
+{#    <div class="header-content">#}
+{#        <!-- First icon column -->#}
+{#        <div class="header-column icon">#}
+{#            <i class="icon-home"></i><br>#}
+{#            <i class="icon-cog"></i>#}
+{#        </div>#}
+{#        <div class="header-column" style="margin-right: 20px">#}
+{#            <a href="/" class="grey">Front-end</a><br>#}
+{#            <a href="" class="grey">One more link</a>#}
+{#        </div>#}
+{#        <!-- Second icon column -->#}
+{#        <div class="header-column icon">#}
+{#            <i class="icon-comment"></i>#}
+{#        </div>#}
+{#        <div class="header-column">#}
+{#            <a href="" class="grey">5 new messages</a>#}
+{#        </div>#}
+{#    </div>#}
+{#{% endblock %}#}
+
+{# Footer branding name (center) #}
+{% block footer_branding %}
+PlanetStack
 {% endblock %}
 
 {% endblock %}
 
-{% block nav-global %}{% endblock %}
+
+{# Footer copyright (right side) #}
+{% block copyright %}
+{#  Copyright &copy; 2013 Client<br>Developed by <a href="http://yoursite.com" target="_blank">YourName</a> #}
+{% endblock %}