1 from core.models import Site
2 from core.models import *
3 from openstack.manager import OpenStackManager
5 from django.contrib import admin
6 from django.contrib.auth.models import Group
7 from django import forms
8 from django.utils.safestring import mark_safe
9 from django.contrib.auth.admin import UserAdmin
10 from django.contrib.admin.widgets import FilteredSelectMultiple, AdminTextareaWidget
11 from django.contrib.auth.forms import ReadOnlyPasswordHashField, AdminPasswordChangeForm
12 from django.contrib.auth.signals import user_logged_in
13 from django.utils import timezone
14 from django.contrib.contenttypes import generic
15 from suit.widgets import LinkedSelect
16 from django.core.exceptions import PermissionDenied
17 from django.core.urlresolvers import reverse, NoReverseMatch
18 from django.utils.encoding import force_text, python_2_unicode_compatible
19 from django.utils.html import conditional_escape, format_html
20 from django.forms.utils import flatatt, to_current_timezone
21 from cgi import escape as html_escape
23 import django_evolution
26 # thread locals necessary to work around a django-suit issue
27 _thread_locals = threading.local()
29 def backend_icon(obj): # backend_status, enacted, updated):
30 #return "%s %s %s" % (str(obj.updated), str(obj.enacted), str(obj.backend_status))
31 if (obj.enacted is not None) and obj.enacted >= obj.updated:
32 return '<span style="min-width:16px;"><img src="/static/admin/img/icon_success.gif"></span>'
34 if obj.backend_status == "Provisioning in progress" or obj.backend_status=="":
35 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_clock.gif"></span>' % obj.backend_status
37 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_error.gif"></span>' % html_escape(obj.backend_status, quote=True)
39 def backend_text(obj):
40 icon = backend_icon(obj)
41 if (obj.enacted is not None) and obj.enacted >= obj.updated:
42 return "%s %s" % (icon, "successfully enacted")
44 return "%s %s" % (icon, html_escape(obj.backend_status, quote=True))
46 class UploadTextareaWidget(AdminTextareaWidget):
47 def render(self, name, value, attrs=None):
50 final_attrs = self.build_attrs(attrs, name=name)
\r
51 return format_html('<input type="file" style="width: 0; height: 0" id="btn_upload_%s" onChange="uploadTextarea(event,\'%s\');">' \
\r
52 '<button onClick="$(\'#btn_upload_%s\').click(); return false;">Upload</button>' \
\r
53 '<br><textarea{0}>\r\n{1}</textarea>' % (attrs["id"], attrs["id"], attrs["id"]),
\r
54 flatatt(final_attrs),
\r
57 class PlainTextWidget(forms.HiddenInput):
60 def render(self, name, value, attrs=None):
63 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
65 class PermissionCheckingAdminMixin(object):
66 # call save_by_user and delete_by_user instead of save and delete
68 def has_add_permission(self, request, obj=None):
69 return (not self.__user_is_readonly(request))
71 def has_delete_permission(self, request, obj=None):
72 return (not self.__user_is_readonly(request))
74 def save_model(self, request, obj, form, change):
75 if self.__user_is_readonly(request):
76 # this 'if' might be redundant if save_by_user is implemented right
77 raise PermissionDenied
79 obj.caller = request.user
80 # update openstack connection to use this site/tenant
81 obj.save_by_user(request.user)
83 def delete_model(self, request, obj):
84 obj.delete_by_user(request.user)
86 def save_formset(self, request, form, formset, change):
87 instances = formset.save(commit=False)
88 for instance in instances:
89 instance.save_by_user(request.user)
91 # BUG in django 1.7? Objects are not deleted by formset.save if
92 # commit is False. So let's delete them ourselves.
94 # code from forms/models.py save_existing_objects()
96 forms_to_delete = formset.deleted_forms
\r
97 except AttributeError:
\r
99 if formset.initial_forms:
100 for form in formset.initial_forms:
102 if form in forms_to_delete:
105 formset.deleted_objects.append(obj)
110 def get_actions(self,request):
111 actions = super(PermissionCheckingAdminMixin,self).get_actions(request)
113 if self.__user_is_readonly(request):
114 if 'delete_selected' in actions:
115 del actions['delete_selected']
119 def change_view(self,request,object_id, extra_context=None):
120 if self.__user_is_readonly(request):
121 if not hasattr(self, "readonly_save"):
\r
122 # save the original readonly fields
\r
123 self.readonly_save = self.readonly_fields
\r
124 self.inlines_save = self.inlines
\r
125 if hasattr(self, "user_readonly_fields"):
\r
126 self.readonly_fields=self.user_readonly_fields
\r
127 if hasattr(self, "user_readonly_inlines"):
\r
128 self.inlines = self.user_readonly_inlines
\r
130 if hasattr(self, "readonly_save"):
\r
131 # restore the original readonly fields
\r
132 self.readonly_fields = self.readonly_save
\r
133 if hasattr(self, "inlines_save"):
\r
134 self.inlines = self.inlines_save
137 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
138 except PermissionDenied:
140 if request.method == 'POST':
141 raise PermissionDenied
142 request.readonly = True
143 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
145 def __user_is_readonly(self, request):
146 return request.user.isReadOnlyUser()
148 def backend_status_text(self, obj):
149 return mark_safe(backend_text(obj))
151 def backend_status_icon(self, obj):
152 return mark_safe(backend_icon(obj))
153 backend_status_icon.short_description = ""
155 def get_form(self, request, obj=None, **kwargs):
156 # Save obj and request in thread-local storage, so suit_form_tabs can
157 # use it to determine whether we're in edit or add mode, and can
158 # determine whether the user is an admin.
159 _thread_locals.request = request
160 _thread_locals.obj = obj
161 return super(PermissionCheckingAdminMixin, self).get_form(request, obj, **kwargs)
163 def get_inline_instances(self, request, obj=None):
164 inlines = super(PermissionCheckingAdminMixin, self).get_inline_instances(request, obj)
166 # inlines that should only be shown to an admin user
167 if request.user.is_admin:
168 for inline_class in getattr(self, "admin_inlines", []):
169 inlines.append(inline_class(self.model, self.admin_site))
173 class ReadOnlyAwareAdmin(PermissionCheckingAdminMixin, admin.ModelAdmin):
174 # Note: Make sure PermissionCheckingAdminMixin is listed before
175 # admin.ModelAdmin in the class declaration.
179 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
182 class SingletonAdmin (ReadOnlyAwareAdmin):
183 def has_add_permission(self, request):
184 if not super(SingletonAdmin, self).has_add_permission(request):
187 num_objects = self.model.objects.count()
193 class PlStackTabularInline(admin.TabularInline):
194 def __init__(self, *args, **kwargs):
195 super(PlStackTabularInline, self).__init__(*args, **kwargs)
197 # InlineModelAdmin as no get_fields() method, so in order to add
198 # the selflink field, we override __init__ to modify self.fields and
199 # self.readonly_fields.
201 self.setup_selflink()
203 def get_change_url(self, model, id):
204 """ Get the URL to a change form in the admin for this model """
205 reverse_path = "admin:%s_change" % (model._meta.db_table)
207 url = reverse(reverse_path, args=(id,))
208 except NoReverseMatch:
213 def setup_selflink(self):
214 if hasattr(self, "selflink_fieldname"):
215 """ self.selflink_model can be defined to punch through a relation
216 to its target object. For example, in SliceNetworkInline, set
217 selflink_model = "network", and the URL will lead to the Network
218 object instead of trying to bring up a change view of the
221 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
223 self.selflink_model = self.model
225 url = self.get_change_url(self.selflink_model, 0)
227 # We don't have an admin for this object, so don't create the
232 # Since we need to add "selflink" to the field list, we need to create
233 # self.fields if it is None.
234 if (self.fields is None):
236 for f in self.model._meta.fields:
237 if f.editable and f.name != "id":
238 self.fields.append(f.name)
240 self.fields = tuple(self.fields) + ("selflink", )
242 if self.readonly_fields is None:
243 self.readonly_fields = ()
245 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
247 def selflink(self, obj):
248 if hasattr(self, "selflink_fieldname"):
249 obj = getattr(obj, self.selflink_fieldname)
252 url = self.get_change_url(self.selflink_model, obj.id)
253 return "<a href='%s'>Details</a>" % str(url)
255 return "Not present"
\r
257 selflink.allow_tags = True
258 selflink.short_description = "Details"
260 def has_add_permission(self, request):
261 return not request.user.isReadOnlyUser()
263 def get_readonly_fields(self, request, obj=None):
264 readonly_fields = list(self.readonly_fields)[:]
265 if request.user.isReadOnlyUser():
266 for field in self.fields:
267 if not field in readonly_fields:
268 readonly_fields.append(field)
269 return readonly_fields
271 def backend_status_icon(self, obj):
272 return mark_safe(backend_icon(obj))
273 backend_status_icon.short_description = ""
275 class PlStackGenericTabularInline(generic.GenericTabularInline):
276 def has_add_permission(self, request):
277 return not request.user.isReadOnlyUser()
279 def get_readonly_fields(self, request, obj=None):
280 readonly_fields = list(self.readonly_fields)[:]
281 if request.user.isReadOnlyUser():
282 for field in self.fields:
283 if not field in readonly_fields:
284 readonly_fields.append(field)
285 return readonly_fields
287 def backend_status_icon(self, obj):
288 return mark_safe(backend_icon(obj))
289 backend_status_icon.short_description = ""
291 class ReservationInline(PlStackTabularInline):
294 suit_classes = 'suit-tab suit-tab-reservations'
296 def queryset(self, request):
297 return Reservation.select_by_user(request.user)
299 class TagInline(PlStackGenericTabularInline):
302 suit_classes = 'suit-tab suit-tab-tags'
303 fields = ['service', 'name', 'value']
305 def queryset(self, request):
306 return Tag.select_by_user(request.user)
308 class NetworkLookerUpper:
309 """ This is a callable that looks up a network name in a sliver and returns
310 the ip address for that network.
313 byNetworkName = {} # class variable
315 def __init__(self, name):
316 self.short_description = name
318 self.network_name = name
320 def __call__(self, obj):
322 for nbs in obj.networksliver_set.all():
323 if (nbs.network.name == self.network_name):
328 return self.network_name
331 def get(network_name):
332 """ We want to make sure we alwars return the same NetworkLookerUpper
333 because sometimes django will cause them to be instantiated multiple
334 times (and we don't want different ones in form.fields vs
335 SliverInline.readonly_fields).
337 if network_name not in NetworkLookerUpper.byNetworkName:
338 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
339 return NetworkLookerUpper.byNetworkName[network_name]
341 class SliverInline(PlStackTabularInline):
343 fields = ['backend_status_icon', 'all_ips_string', 'instance_name', 'slice', 'deploymentNetwork', 'flavor', 'image', 'node']
345 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_name']
346 suit_classes = 'suit-tab suit-tab-slivers'
348 def queryset(self, request):
349 return Sliver.select_by_user(request.user)
351 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
352 if db_field.name == 'deploymentNetwork':
353 kwargs['queryset'] = Deployment.select_by_acl(request.user)
354 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
355 elif db_field.name == 'flavor':
356 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
358 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
362 class SiteInline(PlStackTabularInline):
365 suit_classes = 'suit-tab suit-tab-sites'
367 def queryset(self, request):
368 return Site.select_by_user(request.user)
370 class UserInline(PlStackTabularInline):
372 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
373 readonly_fields = ('backend_status_icon', )
375 suit_classes = 'suit-tab suit-tab-users'
377 def queryset(self, request):
378 return User.select_by_user(request.user)
380 class SliceInline(PlStackTabularInline):
382 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
383 readonly_fields = ('backend_status_icon', )
385 suit_classes = 'suit-tab suit-tab-slices'
387 def queryset(self, request):
388 return Slice.select_by_user(request.user)
390 class NodeInline(PlStackTabularInline):
393 suit_classes = 'suit-tab suit-tab-nodes'
394 fields = ['backend_status_icon', 'name','deployment','site']
395 readonly_fields = ('backend_status_icon', )
397 class ControllerPrivilegeInline(PlStackTabularInline):
398 model = ControllerPrivilege
400 suit_classes = 'suit-tab suit-tab-admin-only'
401 fields = ['backend_status_icon', 'user','role','controller']
402 readonly_fields = ('backend_status_icon', )
404 def queryset(self, request):
405 return ControllerPrivilege.select_by_user(request.user)
407 class SitePrivilegeInline(PlStackTabularInline):
408 model = SitePrivilege
410 suit_classes = 'suit-tab suit-tab-siteprivileges'
411 fields = ['backend_status_icon', 'user','site', 'role']
412 readonly_fields = ('backend_status_icon', )
414 def formfield_for_foreignkey(self, db_field, request, **kwargs):
415 if db_field.name == 'site':
416 kwargs['queryset'] = Site.select_by_user(request.user)
418 if db_field.name == 'user':
419 kwargs['queryset'] = User.select_by_user(request.user)
420 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
422 def queryset(self, request):
423 return SitePrivilege.select_by_user(request.user)
425 class SiteDeploymentsInline(PlStackTabularInline):
426 model = SiteDeployments
428 suit_classes = 'suit-tab suit-tab-deployments'
429 fields = ['backend_status_icon', 'deployment','site']
430 readonly_fields = ('backend_status_icon', )
432 def formfield_for_foreignkey(self, db_field, request, **kwargs):
433 if db_field.name == 'site':
434 kwargs['queryset'] = Site.select_by_user(request.user)
436 if db_field.name == 'deployment':
437 kwargs['queryset'] = Deployment.select_by_user(request.user)
438 return super(SiteDeploymentsInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
440 def queryset(self, request):
441 return SiteDeployments.select_by_user(request.user)
443 class ControllerSitesInline(PlStackTabularInline):
444 model = ControllerSites
446 suit_classes = 'suit-tab suit-tab-admin-only'
447 fields = ['backend_status_icon', 'controller','site']
448 readonly_fields = ('backend_status_icon', )
450 def formfield_for_foreignkey(self, db_field, request, **kwargs):
451 if db_field.name == 'site':
452 kwargs['queryset'] = Site.select_by_user(request.user)
454 if db_field.name == 'controller':
455 kwargs['queryset'] = Controller.select_by_user(request.user)
456 return super(ControllerSitesInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
458 def queryset(self, request):
459 return ControllerSites.select_by_user(request.user)
462 class SlicePrivilegeInline(PlStackTabularInline):
463 model = SlicePrivilege
464 suit_classes = 'suit-tab suit-tab-sliceprivileges'
466 fields = ('backend_status_icon', 'user', 'slice', 'role')
467 readonly_fields = ('backend_status_icon', )
469 def formfield_for_foreignkey(self, db_field, request, **kwargs):
470 if db_field.name == 'slice':
471 kwargs['queryset'] = Slice.select_by_user(request.user)
472 if db_field.name == 'user':
473 kwargs['queryset'] = User.select_by_user(request.user)
475 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
477 def queryset(self, request):
478 return SlicePrivilege.select_by_user(request.user)
480 class SliceNetworkInline(PlStackTabularInline):
481 model = Network.slices.through
482 selflink_fieldname = "network"
484 verbose_name = "Network Connection"
485 verbose_name_plural = "Network Connections"
486 suit_classes = 'suit-tab suit-tab-slicenetworks'
487 fields = ['backend_status_icon', 'network']
488 readonly_fields = ('backend_status_icon', )
490 class ImageDeploymentsInline(PlStackTabularInline):
491 model = ImageDeployments
493 verbose_name = "Image Deployments"
494 verbose_name_plural = "Image Deployments"
495 suit_classes = 'suit-tab suit-tab-imagedeployments'
496 fields = ['backend_status_icon', 'image', 'deployment']
497 readonly_fields = ['backend_status_icon']
499 class ControllerImagesInline(PlStackTabularInline):
500 model = ControllerImages
502 verbose_name = "Controller Images"
503 verbose_name_plural = "Controller Images"
504 suit_classes = 'suit-tab suit-tab-admin-only'
505 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
506 readonly_fields = ['backend_status_icon', 'glance_image_id']
508 class SliceRoleAdmin(PlanetStackBaseAdmin):
512 class SiteRoleAdmin(PlanetStackBaseAdmin):
516 class DeploymentAdminForm(forms.ModelForm):
517 sites = forms.ModelMultipleChoiceField(
518 queryset=Site.objects.all(),
520 help_text="Select which sites are allowed to host nodes in this deployment",
521 widget=FilteredSelectMultiple(
522 verbose_name=('Sites'), is_stacked=False
525 images = forms.ModelMultipleChoiceField(
526 queryset=Image.objects.all(),
528 help_text="Select which images should be deployed on this deployment",
529 widget=FilteredSelectMultiple(
530 verbose_name=('Images'), is_stacked=False
533 flavors = forms.ModelMultipleChoiceField(
534 queryset=Flavor.objects.all(),
536 help_text="Select which flavors should be usable on this deployment",
537 widget=FilteredSelectMultiple(
538 verbose_name=('Flavors'), is_stacked=False
543 many_to_many = ["flavors",]
545 def __init__(self, *args, **kwargs):
546 request = kwargs.pop('request', None)
547 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
549 self.fields['accessControl'].initial = "allow site " + request.user.site.name
551 if self.instance and self.instance.pk:
552 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments.all()]
553 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
554 self.fields['flavors'].initial = self.instance.flavors.all()
556 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
557 """ helper function for handling m2m relations from the MultipleChoiceField
559 this_obj: the source object we want to link from
561 selected_objs: a list of destination objects we want to link to
563 all_relations: the full set of relations involving this_obj, including ones we don't want
565 relation_class: the class that implements the relation from source to dest
567 local_attrname: field name representing this_obj in relation_class
569 foreign_attrname: field name representing selected_objs in relation_class
571 This function will remove all newobjclass relations from this_obj
572 that are not contained in selected_objs, and add any relations that
573 are in selected_objs but don't exist in the data model yet.
576 existing_dest_objs = []
577 for relation in list(all_relations):
578 if getattr(relation, foreign_attrname) not in selected_objs:
579 #print "deleting site", sdp.site
582 existing_dest_objs.append(getattr(relation, foreign_attrname))
584 for dest_obj in selected_objs:
585 if dest_obj not in existing_dest_objs:
586 #print "adding site", site
587 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
588 relation = relation_class(**kwargs)
591 def save(self, commit=True):
592 deployment = super(DeploymentAdminForm, self).save(commit=False)
596 # this has to be done after save() if/when a deployment is first created
597 deployment.flavors = self.cleaned_data['flavors']
600 # save_m2m() doesn't seem to work with 'through' relations. So we
601 # create/destroy the through models ourselves. There has to be
604 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments.all(), SiteDeployments, "deployment", "site")
605 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ControllerImages, "deployment", "image")
611 class DeploymentAdminROForm(DeploymentAdminForm):
612 def save(self, commit=True):
613 raise PermissionDenied
615 class SiteAssocInline(PlStackTabularInline):
616 model = Site.deployments.through
618 suit_classes = 'suit-tab suit-tab-sites'
620 class DeploymentAdmin(PlanetStackBaseAdmin):
622 fieldList = ['backend_status_text', 'name', 'availability_zone', 'sites', 'images', 'flavors', 'accessControl']
623 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
624 inlines = [ControllerPrivilegeInline,NodeInline,TagInline] # ,ControllerImagesInline]
625 list_display = ['backend_status_icon', 'name']
626 list_display_links = ('backend_status_icon', 'name', )
627 readonly_fields = ('backend_status_text', )
629 user_readonly_fields = ['name']
631 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
633 def get_form(self, request, obj=None, **kwargs):
634 if request.user.isReadOnlyUser():
635 kwargs["form"] = DeploymentAdminROForm
637 kwargs["form"] = DeploymentAdminForm
638 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
640 # from stackexchange: pass the request object into the form
642 class AdminFormMetaClass(adminForm):
643 def __new__(cls, *args, **kwargs):
644 kwargs['request'] = request
645 return adminForm(*args, **kwargs)
647 return AdminFormMetaClass
649 class ServiceAttrAsTabInline(PlStackTabularInline):
650 model = ServiceAttribute
651 fields = ['name','value']
653 suit_classes = 'suit-tab suit-tab-serviceattrs'
655 class ServiceAdmin(PlanetStackBaseAdmin):
656 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
657 list_display_links = ('backend_status_icon', 'name', )
658 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
659 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
660 inlines = [ServiceAttrAsTabInline,SliceInline]
661 readonly_fields = ('backend_status_text', )
663 user_readonly_fields = fieldList
665 suit_form_tabs =(('general', 'Service Details'),
667 ('serviceattrs','Additional Attributes'),
670 class SiteAdmin(PlanetStackBaseAdmin):
671 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
673 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
674 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
676 suit_form_tabs =(('general', 'Site Details'),
678 ('siteprivileges','Privileges'),
679 ('deployments','Deployments'),
684 readonly_fields = ['backend_status_text', 'accountLink']
686 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
688 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
689 list_display_links = ('backend_status_icon', 'name', )
690 filter_horizontal = ('deployments',)
691 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentsInline]
692 search_fields = ['name']
694 def queryset(self, request):
695 return Site.select_by_user(request.user)
697 def get_formsets(self, request, obj=None):
698 for inline in self.get_inline_instances(request, obj):
699 # hide MyInline in the add view
702 if isinstance(inline, SliverInline):
703 inline.model.caller = request.user
704 yield inline.get_formset(request, obj)
706 def accountLink(self, obj):
707 link_obj = obj.accounts.all()
709 reverse_path = "admin:core_account_change"
710 url = reverse(reverse_path, args =(link_obj[0].id,))
711 return "<a href='%s'>%s</a>" % (url, "view billing details")
713 return "no billing data for this site"
714 accountLink.allow_tags = True
715 accountLink.short_description = "Billing"
717 def save_model(self, request, obj, form, change):
718 # update openstack connection to use this site/tenant
719 obj.save_by_user(request.user)
721 def delete_model(self, request, obj):
722 obj.delete_by_user(request.user)
725 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
726 fieldList = ['backend_status_text', 'user', 'site', 'role']
728 (None, {'fields': fieldList, 'classes':['collapse']})
730 readonly_fields = ('backend_status_text', )
731 list_display = ('backend_status_icon', 'user', 'site', 'role')
732 list_display_links = list_display
733 user_readonly_fields = fieldList
734 user_readonly_inlines = []
736 def formfield_for_foreignkey(self, db_field, request, **kwargs):
737 if db_field.name == 'site':
738 if not request.user.is_admin:
739 # only show sites where user is an admin or pi
741 for site_privilege in SitePrivilege.objects.filer(user=request.user):
742 if site_privilege.role.role_type in ['admin', 'pi']:
743 sites.add(site_privilege.site)
744 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
746 if db_field.name == 'user':
747 if not request.user.is_admin:
748 # only show users from sites where caller has admin or pi role
749 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
750 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
751 sites = [site_privilege.site for site_privilege in site_privileges]
752 site_privileges = SitePrivilege.objects.filter(site__in=sites)
753 emails = [site_privilege.user.email for site_privilege in site_privileges]
754 users = User.objects.filter(email__in=emails)
755 kwargs['queryset'] = users
757 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
759 def queryset(self, request):
760 # admins can see all privileges. Users can only see privileges at sites
761 # where they have the admin role or pi role.
762 qs = super(SitePrivilegeAdmin, self).queryset(request)
763 #if not request.user.is_admin:
764 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
765 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
766 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
767 # sites = Site.objects.filter(login_base__in=login_bases)
768 # qs = qs.filter(site__in=sites)
771 class SliceForm(forms.ModelForm):
775 'service': LinkedSelect
779 cleaned_data = super(SliceForm, self).clean()
780 name = cleaned_data.get('name')
781 site = cleaned_data.get('site')
782 slice_id = self.instance.id
783 if not site and slice_id:
784 site = Slice.objects.get(id=slice_id).site
785 if (not isinstance(site,Site)):
786 # previous code indicates 'site' could be a site_id and not a site?
787 site = Slice.objects.get(id=site.id)
788 if not name.startswith(site.login_base):
789 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
792 class ControllerSlicesInline(PlStackTabularInline):
793 model = ControllerSlices
795 verbose_name = "Controller Slices"
796 verbose_name_plural = "Controller Slices"
797 suit_classes = 'suit-tab suit-tab-admin-only'
798 fields = ['backend_status_icon', 'controller', 'tenant_id']
799 readonly_fields = ('backend_status_icon', )
801 class SliceAdmin(PlanetStackBaseAdmin):
803 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
804 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
805 readonly_fields = ('backend_status_text', )
806 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
807 list_display_links = ('backend_status_icon', 'name', )
808 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
809 admin_inlines = [ControllerSlicesInline]
811 user_readonly_fields = fieldList
814 def suit_form_tabs(self):
815 tabs =[('general', 'Slice Details'),
816 ('slicenetworks','Networks'),
817 ('sliceprivileges','Privileges'),
818 ('slivers','Slivers'),
820 ('reservations','Reservations'),
823 request=getattr(_thread_locals, "request", None)
824 if request and request.user.is_admin:
825 tabs.append( ('admin-only', 'Admin-Only') )
829 def add_view(self, request, form_url='', extra_context=None):
830 # revert to default read-only fields
831 self.readonly_fields = ('backend_status_text',)
832 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
834 def change_view(self, request, object_id, form_url='', extra_context=None):
836 # cannot change the site of an existing slice so make the site field read only
838 self.readonly_fields = ('backend_status_text','site')
839 return super(SliceAdmin, self).change_view(request, object_id, form_url)
841 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
842 deployment_nodes = []
843 for node in Node.objects.all():
844 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
846 deployment_flavors = []
847 for flavor in Flavor.objects.all():
848 for deployment in flavor.deployments.all():
849 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
851 controller_images = []
852 for image in Image.objects.all():
853 for controller_image in image.controllerimages.all():
854 controller_images.append( (controller_image.controller.id, image.id, image.name) )
856 site_login_bases = []
857 for site in Site.objects.all():
858 site_login_bases.append((site.id, site.login_base))
860 context["deployment_nodes"] = deployment_nodes
861 context["deployment_flavors"] = deployment_flavors
862 context["deployment_images"] = deployment_images
863 context["site_login_bases"] = site_login_bases
864 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
866 def formfield_for_foreignkey(self, db_field, request, **kwargs):
867 if db_field.name == 'site':
868 kwargs['queryset'] = Site.select_by_user(request.user)
869 kwargs['widget'] = forms.Select(attrs={'onChange': "update_slice_prefix(this, $($(this).closest('fieldset')[0]).find('.field-name input')[0].id)"})
871 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
873 def queryset(self, request):
874 # admins can see all keys. Users can only see slices they belong to.
875 return Slice.select_by_user(request.user)
877 def get_formsets(self, request, obj=None):
878 for inline in self.get_inline_instances(request, obj):
879 # hide MyInline in the add view
882 if isinstance(inline, SliverInline):
883 inline.model.caller = request.user
884 yield inline.get_formset(request, obj)
886 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
888 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
890 readonly_fields = ('backend_status_text', )
891 list_display = ('backend_status_icon', 'user', 'slice', 'role')
892 list_display_links = list_display
894 user_readonly_fields = ['user', 'slice', 'role']
895 user_readonly_inlines = []
897 def formfield_for_foreignkey(self, db_field, request, **kwargs):
898 if db_field.name == 'slice':
899 kwargs['queryset'] = Slice.select_by_user(request.user)
901 if db_field.name == 'user':
902 kwargs['queryset'] = User.select_by_user(request.user)
904 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
906 def queryset(self, request):
907 # admins can see all memberships. Users can only see memberships of
908 # slices where they have the admin role.
909 return SlicePrivilege.select_by_user(request.user)
911 def save_model(self, request, obj, form, change):
912 # update openstack connection to use this site/tenant
913 auth = request.session.get('auth', {})
914 auth['tenant'] = obj.slice.slicename
915 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
918 def delete_model(self, request, obj):
919 # update openstack connection to use this site/tenant
920 auth = request.session.get('auth', {})
921 auth['tenant'] = obj.slice.slicename
922 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
926 class ImageAdmin(PlanetStackBaseAdmin):
928 fieldsets = [('Image Details',
929 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
930 'classes': ['suit-tab suit-tab-general']})
932 readonly_fields = ('backend_status_text', )
934 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
936 inlines = [SliverInline, ControllerImagesInline]
938 user_readonly_fields = ['name', 'disk_format', 'container_format']
940 list_display = ['backend_status_icon', 'name']
941 list_display_links = ('backend_status_icon', 'name', )
943 class NodeForm(forms.ModelForm):
946 'site': LinkedSelect,
947 'deployment': LinkedSelect
950 class NodeAdmin(PlanetStackBaseAdmin):
952 list_display = ('backend_status_icon', 'name', 'site', 'deployment')
953 list_display_links = ('backend_status_icon', 'name', )
954 list_filter = ('deployment',)
956 inlines = [TagInline,SliverInline]
957 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
958 readonly_fields = ('backend_status_text', )
960 user_readonly_fields = ['name','site','deployment']
961 user_readonly_inlines = [TagInline,SliverInline]
963 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
966 class SliverForm(forms.ModelForm):
969 ip = forms.CharField(widget=PlainTextWidget)
970 instance_name = forms.CharField(widget=PlainTextWidget)
972 'ip': PlainTextWidget(),
973 'instance_name': PlainTextWidget(),
974 'slice': LinkedSelect,
975 'deploymentNetwork': LinkedSelect,
976 'node': LinkedSelect,
977 'image': LinkedSelect
980 class TagAdmin(PlanetStackBaseAdmin):
981 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
982 list_display_links = list_display
983 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
984 user_readonly_inlines = []
986 class SliverAdmin(PlanetStackBaseAdmin):
989 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
991 readonly_fields = ('backend_status_text', )
992 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deploymentNetwork']
993 list_display_links = ('backend_status_icon', 'ip',)
995 suit_form_tabs =(('general', 'Sliver Details'),
999 inlines = [TagInline]
1001 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image']
1003 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1004 if db_field.name == 'slice':
1005 kwargs['queryset'] = Slice.select_by_user(request.user)
1007 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1009 def queryset(self, request):
1010 # admins can see all slivers. Users can only see slivers of
1011 # the slices they belong to.
1012 return Sliver.select_by_user(request.user)
1015 def get_formsets(self, request, obj=None):
1016 # make some fields read only if we are updating an existing record
1018 #self.readonly_fields = ('ip', 'instance_name')
1019 self.readonly_fields = ('backend_status_text',)
1021 self.readonly_fields = ('backend_status_text',)
1022 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
1024 for inline in self.get_inline_instances(request, obj):
1025 # hide MyInline in the add view
1028 if isinstance(inline, SliverInline):
1029 inline.model.caller = request.user
1030 yield inline.get_formset(request, obj)
1032 #def save_model(self, request, obj, form, change):
1033 # # update openstack connection to use this site/tenant
1034 # auth = request.session.get('auth', {})
1035 # auth['tenant'] = obj.slice.name
1036 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1037 # obj.creator = request.user
1040 #def delete_model(self, request, obj):
1041 # # update openstack connection to use this site/tenant
1042 # auth = request.session.get('auth', {})
1043 # auth['tenant'] = obj.slice.name
1044 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1047 class UserCreationForm(forms.ModelForm):
1048 """A form for creating new users. Includes all the required
1049 fields, plus a repeated password."""
1050 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1051 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1055 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
1057 def clean_password2(self):
1058 # Check that the two password entries match
1059 password1 = self.cleaned_data.get("password1")
1060 password2 = self.cleaned_data.get("password2")
1061 if password1 and password2 and password1 != password2:
1062 raise forms.ValidationError("Passwords don't match")
1065 def save(self, commit=True):
1066 # Save the provided password in hashed format
1067 user = super(UserCreationForm, self).save(commit=False)
1068 user.password = self.cleaned_data["password1"]
1069 #user.set_password(self.cleaned_data["password1"])
1075 class UserChangeForm(forms.ModelForm):
1076 """A form for updating users. Includes all the fields on
1077 the user, but replaces the password field with admin's
1078 password hash display field.
1080 password = ReadOnlyPasswordHashField(label='Password',
1081 help_text= '<a href=\"password/\">Change Password</a>.')
1085 widgets = { 'public_key': UploadTextareaWidget, }
1087 def clean_password(self):
1088 # Regardless of what the user provides, return the initial value.
1089 # This is done here, rather than on the field, because the
1090 # field does not have access to the initial value
1091 return self.initial["password"]
1093 class UserDashboardViewInline(PlStackTabularInline):
1094 model = UserDashboardView
1096 suit_classes = 'suit-tab suit-tab-dashboards'
1097 fields = ['user', 'dashboardView', 'order']
1099 class UserAdmin(PermissionCheckingAdminMixin, UserAdmin):
1100 # Note: Make sure PermissionCheckingAdminMixin is listed before
1101 # admin.ModelAdmin in the class declaration.
1106 # The forms to add and change user instances
1107 form = UserChangeForm
1108 add_form = UserCreationForm
1110 # The fields to be used in displaying the User model.
1111 # These override the definitions on the base UserAdmin
1112 # that reference specific fields on auth.User.
1113 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
1114 list_filter = ('site',)
1115 inlines = [SlicePrivilegeInline,SitePrivilegeInline,ControllerPrivilegeInline,UserDashboardViewInline]
1117 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
1118 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1121 ('Login Details', {'fields': ['backend_status_text', 'email', 'site','password', 'is_active', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
1122 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
1123 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
1124 #('Important dates', {'fields': ('last_login',)}),
1128 'classes': ('wide',),
1129 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')},
1132 readonly_fields = ('backend_status_text', )
1133 search_fields = ('email',)
1134 ordering = ('email',)
1135 filter_horizontal = ()
1137 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
1140 def suit_form_tabs(self):
1141 if getattr(_thread_locals, "obj", None) is None:
1144 return (('general','Login Details'),
1145 ('contact','Contact Information'),
1146 ('sliceprivileges','Slice Privileges'),
1147 ('siteprivileges','Site Privileges'),
1148 ('controllerprivileges','Controller Privileges'),
1149 ('dashboards','Dashboard Views'))
1151 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1152 if db_field.name == 'site':
1153 kwargs['queryset'] = Site.select_by_user(request.user)
1155 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1157 def queryset(self, request):
1158 return User.select_by_user(request.user)
1160 class DashboardViewAdmin(PlanetStackBaseAdmin):
1161 fieldsets = [('Dashboard View Details',
1162 {'fields': ['backend_status_text', 'name', 'url'],
1163 'classes': ['suit-tab suit-tab-general']})
1165 readonly_fields = ('backend_status_text', )
1167 suit_form_tabs =(('general','Dashboard View Details'),)
1169 class ServiceResourceInline(PlStackTabularInline):
1170 model = ServiceResource
1173 class ServiceClassAdmin(PlanetStackBaseAdmin):
1174 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1175 list_display_links = ('backend_status_icon', 'name', )
1176 inlines = [ServiceResourceInline]
1178 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1179 user_readonly_inlines = []
1181 class ReservedResourceInline(PlStackTabularInline):
1182 model = ReservedResource
1184 suit_classes = 'suit-tab suit-tab-reservedresources'
1186 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1187 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1189 if db_field.name == 'resource':
1190 # restrict resources to those that the slice's service class allows
1191 if request._slice is not None:
1192 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1193 if len(field.queryset) > 0:
1194 field.initial = field.queryset.all()[0]
1196 field.queryset = field.queryset.none()
\r
1197 elif db_field.name == 'sliver':
\r
1198 # restrict slivers to those that belong to the slice
\r
1199 if request._slice is not None:
\r
1200 field.queryset = field.queryset.filter(slice = request._slice)
1202 field.queryset = field.queryset.none()
\r
1206 def queryset(self, request):
1207 return ReservedResource.select_by_user(request.user)
1209 class ReservationChangeForm(forms.ModelForm):
1213 'slice' : LinkedSelect
1216 class ReservationAddForm(forms.ModelForm):
1217 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1218 refresh = forms.CharField(widget=forms.HiddenInput())
1221 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1223 def clean_slice(self):
1224 slice = self.cleaned_data.get("slice")
1225 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1227 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1233 'slice' : LinkedSelect
1237 class ReservationAddRefreshForm(ReservationAddForm):
1238 """ This form is displayed when the Reservation Form receives an update
1239 from the Slice dropdown onChange handler. It doesn't validate the
1240 data and doesn't save the data. This will cause the form to be
1244 """ don't validate anything other than slice """
1245 dont_validate_fields = ("startTime", "duration")
1247 def full_clean(self):
1248 result = super(ReservationAddForm, self).full_clean()
1250 for fieldname in self.dont_validate_fields:
1251 if fieldname in self._errors:
1252 del self._errors[fieldname]
1256 """ don't save anything """
1260 class ReservationAdmin(PlanetStackBaseAdmin):
1261 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
1262 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1263 readonly_fields = ('backend_status_text', )
1264 list_display = ('startTime', 'duration')
1265 form = ReservationAddForm
1267 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1269 inlines = [ReservedResourceInline]
1270 user_readonly_fields = fieldList
1272 def add_view(self, request, form_url='', extra_context=None):
1273 timezone.activate(request.user.timezone)
1274 request._refresh = False
1275 request._slice = None
1276 if request.method == 'POST':
1277 # "refresh" will be set to "1" if the form was submitted due to
1278 # a change in the Slice dropdown.
1279 if request.POST.get("refresh","1") == "1":
1280 request._refresh = True
1281 request.POST["refresh"] = "0"
1283 # Keep track of the slice that was selected, so the
1284 # reservedResource inline can filter items for the slice.
1285 request._slice = request.POST.get("slice",None)
1286 if (request._slice is not None):
1287 request._slice = Slice.objects.get(id=request._slice)
1289 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1292 def changelist_view(self, request, extra_context = None):
1293 timezone.activate(request.user.timezone)
1294 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1296 def get_form(self, request, obj=None, **kwargs):
1299 # For changes, set request._slice to the slice already set in the
1301 request._slice = obj.slice
1302 self.form = ReservationChangeForm
1304 if getattr(request, "_refresh", False):
1305 self.form = ReservationAddRefreshForm
1307 self.form = ReservationAddForm
1308 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1310 def get_readonly_fields(self, request, obj=None):
1311 if (obj is not None):
1312 # Prevent slice from being changed after the reservation has been
1318 def queryset(self, request):
1319 return Reservation.select_by_user(request.user)
1321 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1322 list_display = ("backend_status_icon", "name", )
1323 list_display_links = ('backend_status_icon', 'name', )
1324 user_readonly_fields = ['name']
1325 user_readonly_inlines = []
1327 class RouterAdmin(PlanetStackBaseAdmin):
1328 list_display = ("backend_status_icon", "name", )
1329 list_display_links = ('backend_status_icon', 'name', )
1330 user_readonly_fields = ['name']
1331 user_readonly_inlines = []
1333 class RouterInline(PlStackTabularInline):
1334 model = Router.networks.through
1336 verbose_name_plural = "Routers"
1337 verbose_name = "Router"
1338 suit_classes = 'suit-tab suit-tab-routers'
1340 class NetworkParameterInline(PlStackGenericTabularInline):
1341 model = NetworkParameter
1343 verbose_name_plural = "Parameters"
1344 verbose_name = "Parameter"
1345 suit_classes = 'suit-tab suit-tab-netparams'
1346 fields = ['backend_status_icon', 'parameter', 'value']
1347 readonly_fields = ('backend_status_icon', )
1349 class NetworkSliversInline(PlStackTabularInline):
1350 fields = ['backend_status_icon', 'network','sliver','ip']
1351 readonly_fields = ("backend_status_icon", "ip", )
1352 model = NetworkSliver
1353 selflink_fieldname = "sliver"
1355 verbose_name_plural = "Slivers"
1356 verbose_name = "Sliver"
1357 suit_classes = 'suit-tab suit-tab-networkslivers'
1359 class NetworkSlicesInline(PlStackTabularInline):
1360 model = NetworkSlice
1361 selflink_fieldname = "slice"
1363 verbose_name_plural = "Slices"
1364 verbose_name = "Slice"
1365 suit_classes = 'suit-tab suit-tab-networkslices'
1366 fields = ['backend_status_icon', 'network','slice']
1367 readonly_fields = ('backend_status_icon', )
1369 class ControllerNetworksInline(PlStackTabularInline):
1370 model = ControllerNetworks
1372 verbose_name_plural = "Controller Networks"
1373 verbose_name = "Controller Network"
1374 suit_classes = 'suit-tab suit-tab-admin-only'
1375 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
1376 readonly_fields = ('backend_status_icon', )
1378 class NetworkForm(forms.ModelForm):
1382 'topologyParameters': UploadTextareaWidget,
1383 'controllerParameters': UploadTextareaWidget,
1386 class NetworkAdmin(PlanetStackBaseAdmin):
1387 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1388 list_display_links = ('backend_status_icon', 'name', )
1389 readonly_fields = ("subnet", )
1391 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1392 admin_inlines = [ControllerNetworksInline]
1397 (None, {'fields': ['backend_status_text', 'name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'],
1398 'classes':['suit-tab suit-tab-general']}),
1399 (None, {'fields': ['topologyParameters', 'controllerUrl', 'controllerParameters'],
1400 'classes':['suit-tab suit-tab-sdn']}),
1403 readonly_fields = ('backend_status_text', )
1404 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1407 def suit_form_tabs(self):
1408 tabs=[('general','Network Details'),
1409 ('sdn', 'SDN Configuration'),
1410 ('netparams', 'Parameters'),
1411 ('networkslivers','Slivers'),
1412 ('networkslices','Slices'),
1413 ('routers','Routers'),
1416 request=getattr(_thread_locals, "request", None)
1417 if request and request.user.is_admin:
1418 tabs.append( ('admin-only', 'Admin-Only') )
1423 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1424 list_display = ("backend_status_icon", "name", "guaranteedBandwidth", "visibility")
1425 list_display_links = ('backend_status_icon', 'name', )
1426 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1427 user_readonly_inlines = []
1429 (None, {'fields': ['name', 'description', 'guaranteedBandwidth', 'visibility', 'translation', 'sharedNetworkName', 'sharedNetworkId', 'topologyKind', 'controllerKind'],
1430 'classes':['suit-tab suit-tab-general']}),]
1431 suit_form_tabs = (('general','Network Template Details'), )
1433 class FlavorAdmin(PlanetStackBaseAdmin):
1434 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1435 list_display_links = ("backend_status_icon", "name")
1436 user_readonly_fields = ("name", "flavor")
1437 fields = ("name", "description", "flavor", "order", "default")
1439 # register a signal that caches the user's credentials when they log in
1440 def cache_credentials(sender, user, request, **kwds):
1441 auth = {'username': request.POST['username'],
1442 'password': request.POST['password']}
1443 request.session['auth'] = auth
1444 user_logged_in.connect(cache_credentials)
1446 def dollar_field(fieldName, short_description):
1447 def newFunc(self, obj):
1449 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1451 x=getattr(obj, fieldName, 0.0)
1453 newFunc.short_description = short_description
1456 def right_dollar_field(fieldName, short_description):
1457 def newFunc(self, obj):
1459 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1460 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1462 x=getattr(obj, fieldName, 0.0)
1464 newFunc.short_description = short_description
1465 newFunc.allow_tags = True
1468 class InvoiceChargeInline(PlStackTabularInline):
1471 verbose_name_plural = "Charges"
1472 verbose_name = "Charge"
1473 exclude = ['account']
1474 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1475 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1479 dollar_amount = right_dollar_field("amount", "Amount")
1481 class InvoiceAdmin(admin.ModelAdmin):
1482 list_display = ("date", "account")
1484 inlines = [InvoiceChargeInline]
1486 fields = ["date", "account", "dollar_amount"]
1487 readonly_fields = ["date", "account", "dollar_amount"]
1489 dollar_amount = dollar_field("amount", "Amount")
1491 class InvoiceInline(PlStackTabularInline):
1494 verbose_name_plural = "Invoices"
1495 verbose_name = "Invoice"
1496 fields = ["date", "dollar_amount"]
1497 readonly_fields = ["date", "dollar_amount"]
1498 suit_classes = 'suit-tab suit-tab-accountinvoice'
1502 dollar_amount = right_dollar_field("amount", "Amount")
1504 class PendingChargeInline(PlStackTabularInline):
1507 verbose_name_plural = "Charges"
1508 verbose_name = "Charge"
1509 exclude = ["invoice"]
1510 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1511 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1512 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1516 def queryset(self, request):
1517 qs = super(PendingChargeInline, self).queryset(request)
1518 qs = qs.filter(state="pending")
1521 dollar_amount = right_dollar_field("amount", "Amount")
1523 class PaymentInline(PlStackTabularInline):
1526 verbose_name_plural = "Payments"
1527 verbose_name = "Payment"
1528 fields = ["date", "dollar_amount"]
1529 readonly_fields = ["date", "dollar_amount"]
1530 suit_classes = 'suit-tab suit-tab-accountpayments'
1534 dollar_amount = right_dollar_field("amount", "Amount")
1536 class AccountAdmin(admin.ModelAdmin):
1537 list_display = ("site", "balance_due")
1539 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1542 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1544 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1547 ('general','Account Details'),
1548 ('accountinvoice', 'Invoices'),
1549 ('accountpayments', 'Payments'),
1550 ('accountpendingcharges','Pending Charges'),
1553 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1554 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1555 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1557 # Now register the new UserAdmin...
1558 admin.site.register(User, UserAdmin)
1559 # ... and, since we're not using Django's builtin permissions,
1560 # unregister the Group model from admin.
1561 #admin.site.unregister(Group)
1563 #Do not show django evolution in the admin interface
1564 from django_evolution.models import Version, Evolution
1565 #admin.site.unregister(Version)
1566 #admin.site.unregister(Evolution)
1569 # When debugging it is often easier to see all the classes, but for regular use
1570 # only the top-levels should be displayed
1573 admin.site.register(Deployment, DeploymentAdmin)
1574 admin.site.register(Controller, ControllerAdmin)
1575 admin.site.register(Site, SiteAdmin)
1576 admin.site.register(Slice, SliceAdmin)
1577 admin.site.register(Service, ServiceAdmin)
1578 admin.site.register(Reservation, ReservationAdmin)
1579 admin.site.register(Network, NetworkAdmin)
1580 admin.site.register(Router, RouterAdmin)
1581 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1582 admin.site.register(Account, AccountAdmin)
1583 admin.site.register(Invoice, InvoiceAdmin)
1586 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1587 admin.site.register(ServiceClass, ServiceClassAdmin)
1588 #admin.site.register(PlanetStack)
1589 admin.site.register(Tag, TagAdmin)
1590 admin.site.register(ControllerRole)
1591 admin.site.register(SiteRole)
1592 admin.site.register(SliceRole)
1593 admin.site.register(PlanetStackRole)
1594 admin.site.register(Node, NodeAdmin)
1595 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1596 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1597 admin.site.register(Sliver, SliverAdmin)
1598 admin.site.register(Image, ImageAdmin)
1599 admin.site.register(DashboardView, DashboardViewAdmin)
1600 admin.site.register(Flavor, FlavorAdmin)