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', 'controllerNetwork', '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 == 'controllerNetwork':
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', 'site_deployment']
395 readonly_fields = ('backend_status_icon', )
397 class DeploymentPrivilegeInline(PlStackTabularInline):
398 model = DeploymentPrivilege
400 suit_classes = 'suit-tab suit-tab-admin-only'
401 fields = ['backend_status_icon', 'user','role','deployment']
402 readonly_fields = ('backend_status_icon', )
404 def queryset(self, request):
405 return DeploymentPrivilege.select_by_user(request.user)
407 class ControllerPrivilegeInline(PlStackTabularInline):
408 model = ControllerPrivilege
410 suit_classes = 'suit-tab suit-tab-admin-only'
411 fields = ['backend_status_icon', 'user','role','controller']
412 readonly_fields = ('backend_status_icon', )
414 def queryset(self, request):
415 return ControllerPrivilege.select_by_user(request.user)
417 class ControllerSiteDeploymentsInline(PlStackTabularInline):
418 model = ControllerSiteDeployments
420 suit_classes = 'suit-tab suit-tab-admin-only'
421 fields = ['controller', 'site_deployment', 'tenant_id']
423 class SitePrivilegeInline(PlStackTabularInline):
424 model = SitePrivilege
426 suit_classes = 'suit-tab suit-tab-siteprivileges'
427 fields = ['backend_status_icon', 'user','site', 'role']
428 readonly_fields = ('backend_status_icon', )
430 def formfield_for_foreignkey(self, db_field, request, **kwargs):
431 if db_field.name == 'site':
432 kwargs['queryset'] = Site.select_by_user(request.user)
434 if db_field.name == 'user':
435 kwargs['queryset'] = User.select_by_user(request.user)
436 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
438 def queryset(self, request):
439 return SitePrivilege.select_by_user(request.user)
441 class SiteDeploymentsInline(PlStackTabularInline):
442 model = SiteDeployments
444 suit_classes = 'suit-tab suit-tab-deployments'
445 fields = ['backend_status_icon', 'deployment','site', 'controller']
446 readonly_fields = ('backend_status_icon', )
448 def formfield_for_foreignkey(self, db_field, request, **kwargs):
449 if db_field.name == 'site':
450 kwargs['queryset'] = Site.select_by_user(request.user)
452 if db_field.name == 'deployment':
453 kwargs['queryset'] = Deployment.select_by_user(request.user)
455 if db_field.name == 'controller':
456 kwargs['queryset'] = Controller.select_by_user(request.user)
458 return super(SiteDeploymentsInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
460 def queryset(self, request):
461 return SiteDeployments.select_by_user(request.user)
464 class SlicePrivilegeInline(PlStackTabularInline):
465 model = SlicePrivilege
466 suit_classes = 'suit-tab suit-tab-sliceprivileges'
468 fields = ('backend_status_icon', 'user', 'slice', 'role')
469 readonly_fields = ('backend_status_icon', )
471 def formfield_for_foreignkey(self, db_field, request, **kwargs):
472 if db_field.name == 'slice':
473 kwargs['queryset'] = Slice.select_by_user(request.user)
474 if db_field.name == 'user':
475 kwargs['queryset'] = User.select_by_user(request.user)
477 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
479 def queryset(self, request):
480 return SlicePrivilege.select_by_user(request.user)
482 class SliceNetworkInline(PlStackTabularInline):
483 model = Network.slices.through
484 selflink_fieldname = "network"
486 verbose_name = "Network Connection"
487 verbose_name_plural = "Network Connections"
488 suit_classes = 'suit-tab suit-tab-slicenetworks'
489 fields = ['backend_status_icon', 'network']
490 readonly_fields = ('backend_status_icon', )
492 class ImageDeploymentsInline(PlStackTabularInline):
493 model = ImageDeployments
495 verbose_name = "Image Deployments"
496 verbose_name_plural = "Image Deployments"
497 suit_classes = 'suit-tab suit-tab-imagedeployments'
498 fields = ['backend_status_icon', 'image', 'deployment']
499 readonly_fields = ['backend_status_icon']
501 class ControllerImagesInline(PlStackTabularInline):
502 model = ControllerImages
504 verbose_name = "Controller Images"
505 verbose_name_plural = "Controller Images"
506 suit_classes = 'suit-tab suit-tab-admin-only'
507 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
508 readonly_fields = ['backend_status_icon', 'glance_image_id']
510 class SliceRoleAdmin(PlanetStackBaseAdmin):
514 class SiteRoleAdmin(PlanetStackBaseAdmin):
518 class DeploymentAdminForm(forms.ModelForm):
519 sites = forms.ModelMultipleChoiceField(
520 queryset=Site.objects.all(),
522 help_text="Select which sites are allowed to host nodes in this deployment",
523 widget=FilteredSelectMultiple(
524 verbose_name=('Sites'), is_stacked=False
527 images = forms.ModelMultipleChoiceField(
528 queryset=Image.objects.all(),
530 help_text="Select which images should be deployed on this deployment",
531 widget=FilteredSelectMultiple(
532 verbose_name=('Images'), is_stacked=False
535 flavors = forms.ModelMultipleChoiceField(
536 queryset=Flavor.objects.all(),
538 help_text="Select which flavors should be usable on this deployment",
539 widget=FilteredSelectMultiple(
540 verbose_name=('Flavors'), is_stacked=False
545 many_to_many = ["flavors",]
547 def __init__(self, *args, **kwargs):
548 request = kwargs.pop('request', None)
549 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
551 self.fields['accessControl'].initial = "allow site " + request.user.site.name
553 if self.instance and self.instance.pk:
554 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments.all()]
555 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
556 self.fields['flavors'].initial = self.instance.flavors.all()
558 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
559 """ helper function for handling m2m relations from the MultipleChoiceField
561 this_obj: the source object we want to link from
563 selected_objs: a list of destination objects we want to link to
565 all_relations: the full set of relations involving this_obj, including ones we don't want
567 relation_class: the class that implements the relation from source to dest
569 local_attrname: field name representing this_obj in relation_class
571 foreign_attrname: field name representing selected_objs in relation_class
573 This function will remove all newobjclass relations from this_obj
574 that are not contained in selected_objs, and add any relations that
575 are in selected_objs but don't exist in the data model yet.
578 existing_dest_objs = []
579 for relation in list(all_relations):
580 if getattr(relation, foreign_attrname) not in selected_objs:
581 #print "deleting site", sdp.site
584 existing_dest_objs.append(getattr(relation, foreign_attrname))
586 for dest_obj in selected_objs:
587 if dest_obj not in existing_dest_objs:
588 #print "adding site", site
589 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
590 relation = relation_class(**kwargs)
593 def save(self, commit=True):
594 deployment = super(DeploymentAdminForm, self).save(commit=False)
598 # this has to be done after save() if/when a deployment is first created
599 deployment.flavors = self.cleaned_data['flavors']
602 # save_m2m() doesn't seem to work with 'through' relations. So we
603 # create/destroy the through models ourselves. There has to be
606 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments.all(), SiteDeployments, "deployment", "site")
607 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), DeploymentImages, "deployment", "image")
613 class DeploymentAdminROForm(DeploymentAdminForm):
614 def save(self, commit=True):
615 raise PermissionDenied
617 class SiteAssocInline(PlStackTabularInline):
618 model = Site.deployments.through
620 suit_classes = 'suit-tab suit-tab-sites'
622 class DeploymentAdmin(PlanetStackBaseAdmin):
624 fieldList = ['backend_status_text', 'name', 'sites', 'images', 'flavors', 'accessControl']
625 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
626 # node no longer directly connected to deployment
627 #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
628 inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline]
629 list_display = ['backend_status_icon', 'name']
630 list_display_links = ('backend_status_icon', 'name', )
631 readonly_fields = ('backend_status_text', )
633 user_readonly_fields = ['name']
635 # nodes no longer direclty connected to deployments
636 #suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'),('imagedeployments','Images'))
637 suit_form_tabs =(('sites','Deployment Details'),('deploymentprivileges','Privileges'),('tags','Tags'),('imagedeployments','Images'))
639 def get_form(self, request, obj=None, **kwargs):
640 if request.user.isReadOnlyUser():
641 kwargs["form"] = DeploymentAdminROForm
643 kwargs["form"] = DeploymentAdminForm
644 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
646 # from stackexchange: pass the request object into the form
648 class AdminFormMetaClass(adminForm):
649 def __new__(cls, *args, **kwargs):
650 kwargs['request'] = request
651 return adminForm(*args, **kwargs)
653 return AdminFormMetaClass
655 class ControllerAdminForm(forms.ModelForm):
656 site_deployments = forms.ModelMultipleChoiceField(
657 queryset=SiteDeployments.objects.all(),
659 help_text="Select which sites deployments are managed by this controller",
660 widget=FilteredSelectMultiple(
661 verbose_name=('Site Deployments'), is_stacked=False
668 def __init__(self, *args, **kwargs):
669 request = kwargs.pop('request', None)
670 super(ControllerAdminForm, self).__init__(*args, **kwargs)
672 if self.instance and self.instance.pk:
673 self.fields['site_deployments'].initial = [x.site_deployment for x in self.instance.controllersitedeployments.all()]
675 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
676 """ helper function for handling m2m relations from the MultipleChoiceField
677 this_obj: the source object we want to link from
678 selected_objs: a list of destination objects we want to link to
679 all_relations: the full set of relations involving this_obj, including ones we don't want
680 relation_class: the class that implements the relation from source to dest
681 local_attrname: field name representing this_obj in relation_class
682 foreign_attrname: field name representing selected_objs in relation_class
683 This function will remove all newobjclass relations from this_obj
684 that are not contained in selected_objs, and add any relations that
685 are in selected_objs but don't exist in the data model yet.
687 existing_dest_objs = []
688 for relation in list(all_relations):
689 if getattr(relation, foreign_attrname) not in selected_objs:
690 #print "deleting site", sdp.site
693 existing_dest_objs.append(getattr(relation, foreign_attrname))
695 for dest_obj in selected_objs:
696 if dest_obj not in existing_dest_objs:
697 #print "adding site", site
698 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
699 relation = relation_class(**kwargs)
702 def save(self, commit=True):
703 controller = super(ControllerAdminForm, self).save(commit=False)
708 # save_m2m() doesn't seem to work with 'through' relations. So we
709 # create/destroy the through models ourselves. There has to be
711 self.manipulate_m2m_objs(controller, self.cleaned_data['site_deployments'], controller.controllersitedeployments.all(), ControllerSiteDeployments, "controller", "site_deployment")
717 class ControllerAdmin(PlanetStackBaseAdmin):
719 fieldList = ['name', 'version', 'backend_type', 'auth_url', 'admin_user', 'admin_tenant','admin_password']
720 #fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
721 inlines = [ControllerSiteDeploymentsInline] # ,ControllerImagesInline]
722 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
723 list_display_links = ('backend_status_icon', 'name', )
724 readonly_fields = ('backend_status_text',)
726 user_readonly_fields = []
728 def get_form(self, request, obj=None, **kwargs):
730 if request.user.isReadOnlyUser():
731 kwargs["form"] = ControllerAdminROForm
733 kwargs["form"] = ControllerAdminForm
734 adminForm = super(ControllerAdmin,self).get_form(request, obj, **kwargs)
736 # from stackexchange: pass the request object into the form
738 class AdminFormMetaClass(adminForm):
739 def __new__(cls, *args, **kwargs):
740 kwargs['request'] = request
741 return adminForm(*args, **kwargs)
743 return AdminFormMetaClass
745 class ServiceAttrAsTabInline(PlStackTabularInline):
746 model = ServiceAttribute
747 fields = ['name','value']
749 suit_classes = 'suit-tab suit-tab-serviceattrs'
751 class ServiceAdmin(PlanetStackBaseAdmin):
752 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
753 list_display_links = ('backend_status_icon', 'name', )
754 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
755 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
756 inlines = [ServiceAttrAsTabInline,SliceInline]
757 readonly_fields = ('backend_status_text', )
759 user_readonly_fields = fieldList
761 suit_form_tabs =(('general', 'Service Details'),
763 ('serviceattrs','Additional Attributes'),
766 class SiteAdmin(PlanetStackBaseAdmin):
767 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
769 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
770 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
772 suit_form_tabs =(('general', 'Site Details'),
774 ('siteprivileges','Privileges'),
775 ('deployments','Deployments'),
780 readonly_fields = ['backend_status_text', 'accountLink']
782 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
784 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
785 list_display_links = ('backend_status_icon', 'name', )
786 filter_horizontal = ('deployments',)
787 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteDeploymentsInline]
788 search_fields = ['name']
790 def queryset(self, request):
791 return Site.select_by_user(request.user)
793 def get_formsets(self, request, obj=None):
794 for inline in self.get_inline_instances(request, obj):
795 # hide MyInline in the add view
798 if isinstance(inline, SliverInline):
799 inline.model.caller = request.user
800 yield inline.get_formset(request, obj)
802 def accountLink(self, obj):
803 link_obj = obj.accounts.all()
805 reverse_path = "admin:core_account_change"
806 url = reverse(reverse_path, args =(link_obj[0].id,))
807 return "<a href='%s'>%s</a>" % (url, "view billing details")
809 return "no billing data for this site"
810 accountLink.allow_tags = True
811 accountLink.short_description = "Billing"
813 def save_model(self, request, obj, form, change):
814 # update openstack connection to use this site/tenant
815 obj.save_by_user(request.user)
817 def delete_model(self, request, obj):
818 obj.delete_by_user(request.user)
821 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
822 fieldList = ['backend_status_text', 'user', 'site', 'role']
824 (None, {'fields': fieldList, 'classes':['collapse']})
826 readonly_fields = ('backend_status_text', )
827 list_display = ('backend_status_icon', 'user', 'site', 'role')
828 list_display_links = list_display
829 user_readonly_fields = fieldList
830 user_readonly_inlines = []
832 def formfield_for_foreignkey(self, db_field, request, **kwargs):
833 if db_field.name == 'site':
834 if not request.user.is_admin:
835 # only show sites where user is an admin or pi
837 for site_privilege in SitePrivilege.objects.filer(user=request.user):
838 if site_privilege.role.role_type in ['admin', 'pi']:
839 sites.add(site_privilege.site)
840 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
842 if db_field.name == 'user':
843 if not request.user.is_admin:
844 # only show users from sites where caller has admin or pi role
845 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
846 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
847 sites = [site_privilege.site for site_privilege in site_privileges]
848 site_privileges = SitePrivilege.objects.filter(site__in=sites)
849 emails = [site_privilege.user.email for site_privilege in site_privileges]
850 users = User.objects.filter(email__in=emails)
851 kwargs['queryset'] = users
853 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
855 def queryset(self, request):
856 # admins can see all privileges. Users can only see privileges at sites
857 # where they have the admin role or pi role.
858 qs = super(SitePrivilegeAdmin, self).queryset(request)
859 #if not request.user.is_admin:
860 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
861 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
862 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
863 # sites = Site.objects.filter(login_base__in=login_bases)
864 # qs = qs.filter(site__in=sites)
867 class SliceForm(forms.ModelForm):
871 'service': LinkedSelect
875 cleaned_data = super(SliceForm, self).clean()
876 name = cleaned_data.get('name')
877 site = cleaned_data.get('site')
878 slice_id = self.instance.id
879 if not site and slice_id:
880 site = Slice.objects.get(id=slice_id).site
881 if (not isinstance(site,Site)):
882 # previous code indicates 'site' could be a site_id and not a site?
883 site = Slice.objects.get(id=site.id)
884 if not name.startswith(site.login_base):
885 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
888 class ControllerSlicesInline(PlStackTabularInline):
889 model = ControllerSlices
891 verbose_name = "Controller Slices"
892 verbose_name_plural = "Controller Slices"
893 suit_classes = 'suit-tab suit-tab-admin-only'
894 fields = ['backend_status_icon', 'controller', 'tenant_id']
895 readonly_fields = ('backend_status_icon', )
897 class SliceAdmin(PlanetStackBaseAdmin):
899 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
900 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
901 readonly_fields = ('backend_status_text', )
902 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
903 list_display_links = ('backend_status_icon', 'name', )
904 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
905 admin_inlines = [ControllerSlicesInline]
907 user_readonly_fields = fieldList
910 def suit_form_tabs(self):
911 tabs =[('general', 'Slice Details'),
912 ('slicenetworks','Networks'),
913 ('sliceprivileges','Privileges'),
914 ('slivers','Slivers'),
916 ('reservations','Reservations'),
919 request=getattr(_thread_locals, "request", None)
920 if request and request.user.is_admin:
921 tabs.append( ('admin-only', 'Admin-Only') )
925 def add_view(self, request, form_url='', extra_context=None):
926 # revert to default read-only fields
927 self.readonly_fields = ('backend_status_text',)
928 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
930 def change_view(self, request, object_id, form_url='', extra_context=None):
931 # cannot change the site of an existing slice so make the site field read only
933 self.readonly_fields = ('backend_status_text','site')
934 return super(SliceAdmin, self).change_view(request, object_id, form_url)
936 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
937 deployment_nodes = []
938 for node in Node.objects.all():
939 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
941 deployment_flavors = []
942 for flavor in Flavor.objects.all():
943 for deployment in flavor.deployments.all():
944 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
946 deployment_images = []
947 for image in Image.objects.all():
948 for deployment_image in image.imagedeployments.all():
949 deployment_images.append( (deployment_image.controller.id, image.id, image.name) )
951 site_login_bases = []
952 for site in Site.objects.all():
953 site_login_bases.append((site.id, site.login_base))
955 context["deployment_nodes"] = deployment_nodes
956 context["deployment_flavors"] = deployment_flavors
957 context["deployment_images"] = deployment_images
958 context["site_login_bases"] = site_login_bases
959 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
961 def formfield_for_foreignkey(self, db_field, request, **kwargs):
962 if db_field.name == 'site':
963 kwargs['queryset'] = Site.select_by_user(request.user)
964 kwargs['widget'] = forms.Select(attrs={'onChange': "update_slice_prefix(this, $($(this).closest('fieldset')[0]).find('.field-name input')[0].id)"})
966 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
968 def queryset(self, request):
969 # admins can see all keys. Users can only see slices they belong to.
970 return Slice.select_by_user(request.user)
972 def get_formsets(self, request, obj=None):
973 for inline in self.get_inline_instances(request, obj):
974 # hide MyInline in the add view
977 if isinstance(inline, SliverInline):
978 inline.model.caller = request.user
979 yield inline.get_formset(request, obj)
981 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
983 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
985 readonly_fields = ('backend_status_text', )
986 list_display = ('backend_status_icon', 'user', 'slice', 'role')
987 list_display_links = list_display
989 user_readonly_fields = ['user', 'slice', 'role']
990 user_readonly_inlines = []
992 def formfield_for_foreignkey(self, db_field, request, **kwargs):
993 if db_field.name == 'slice':
994 kwargs['queryset'] = Slice.select_by_user(request.user)
996 if db_field.name == 'user':
997 kwargs['queryset'] = User.select_by_user(request.user)
999 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1001 def queryset(self, request):
1002 # admins can see all memberships. Users can only see memberships of
1003 # slices where they have the admin role.
1004 return SlicePrivilege.select_by_user(request.user)
1006 def save_model(self, request, obj, form, change):
1007 # update openstack connection to use this site/tenant
1008 auth = request.session.get('auth', {})
1009 auth['tenant'] = obj.slice.slicename
1010 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1013 def delete_model(self, request, obj):
1014 # update openstack connection to use this site/tenant
1015 auth = request.session.get('auth', {})
1016 auth['tenant'] = obj.slice.slicename
1017 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1021 class ImageAdmin(PlanetStackBaseAdmin):
1023 fieldsets = [('Image Details',
1024 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
1025 'classes': ['suit-tab suit-tab-general']})
1027 readonly_fields = ('backend_status_text', )
1029 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
1031 inlines = [SliverInline, ControllerImagesInline]
1033 user_readonly_fields = ['name', 'disk_format', 'container_format']
1035 list_display = ['backend_status_icon', 'name']
1036 list_display_links = ('backend_status_icon', 'name', )
1038 class NodeForm(forms.ModelForm):
1041 'site': LinkedSelect,
1042 'deployment': LinkedSelect
1045 class NodeAdmin(PlanetStackBaseAdmin):
1047 list_display = ('backend_status_icon', 'name', 'site_deployment')
1048 list_display_links = ('backend_status_icon', 'name', )
1049 list_filter = ('site_deployment',)
1051 inlines = [TagInline,SliverInline]
1052 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site_deployment'], 'classes':['suit-tab suit-tab-details']})]
1053 readonly_fields = ('backend_status_text', )
1055 user_readonly_fields = ['name','site_deployment']
1056 user_readonly_inlines = [TagInline,SliverInline]
1058 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
1061 class SliverForm(forms.ModelForm):
1064 ip = forms.CharField(widget=PlainTextWidget)
1065 instance_name = forms.CharField(widget=PlainTextWidget)
1067 'ip': PlainTextWidget(),
1068 'instance_name': PlainTextWidget(),
1069 'slice': LinkedSelect,
1070 'controllerNetwork': LinkedSelect,
1071 'node': LinkedSelect,
1072 'image': LinkedSelect
1075 class TagAdmin(PlanetStackBaseAdmin):
1076 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1077 list_display_links = list_display
1078 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1079 user_readonly_inlines = []
1081 class SliverAdmin(PlanetStackBaseAdmin):
1084 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'controllerNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
1086 readonly_fields = ('backend_status_text', )
1087 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'flavor', 'image', 'node', 'controllerNetwork']
1088 list_display_links = ('backend_status_icon', 'ip',)
1090 suit_form_tabs =(('general', 'Sliver Details'),
1094 inlines = [TagInline]
1096 user_readonly_fields = ['slice', 'controllerNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image']
1098 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1099 if db_field.name == 'slice':
1100 kwargs['queryset'] = Slice.select_by_user(request.user)
1102 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1104 def queryset(self, request):
1105 # admins can see all slivers. Users can only see slivers of
1106 # the slices they belong to.
1107 return Sliver.select_by_user(request.user)
1110 def get_formsets(self, request, obj=None):
1111 # make some fields read only if we are updating an existing record
1113 #self.readonly_fields = ('ip', 'instance_name')
1114 self.readonly_fields = ('backend_status_text',)
1116 self.readonly_fields = ('backend_status_text',)
1117 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
1119 for inline in self.get_inline_instances(request, obj):
1120 # hide MyInline in the add view
1123 if isinstance(inline, SliverInline):
1124 inline.model.caller = request.user
1125 yield inline.get_formset(request, obj)
1127 #def save_model(self, request, obj, form, change):
1128 # # update openstack connection to use this site/tenant
1129 # auth = request.session.get('auth', {})
1130 # auth['tenant'] = obj.slice.name
1131 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1132 # obj.creator = request.user
1135 #def delete_model(self, request, obj):
1136 # # update openstack connection to use this site/tenant
1137 # auth = request.session.get('auth', {})
1138 # auth['tenant'] = obj.slice.name
1139 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1142 class UserCreationForm(forms.ModelForm):
1143 """A form for creating new users. Includes all the required
1144 fields, plus a repeated password."""
1145 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1146 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1150 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
1152 def clean_password2(self):
1153 # Check that the two password entries match
1154 password1 = self.cleaned_data.get("password1")
1155 password2 = self.cleaned_data.get("password2")
1156 if password1 and password2 and password1 != password2:
1157 raise forms.ValidationError("Passwords don't match")
1160 def save(self, commit=True):
1161 # Save the provided password in hashed format
1162 user = super(UserCreationForm, self).save(commit=False)
1163 user.password = self.cleaned_data["password1"]
1164 #user.set_password(self.cleaned_data["password1"])
1170 class UserChangeForm(forms.ModelForm):
1171 """A form for updating users. Includes all the fields on
1172 the user, but replaces the password field with admin's
1173 password hash display field.
1175 password = ReadOnlyPasswordHashField(label='Password',
1176 help_text= '<a href=\"password/\">Change Password</a>.')
1180 widgets = { 'public_key': UploadTextareaWidget, }
1182 def clean_password(self):
1183 # Regardless of what the user provides, return the initial value.
1184 # This is done here, rather than on the field, because the
1185 # field does not have access to the initial value
1186 return self.initial["password"]
1188 class UserDashboardViewInline(PlStackTabularInline):
1189 model = UserDashboardView
1191 suit_classes = 'suit-tab suit-tab-dashboards'
1192 fields = ['user', 'dashboardView', 'order']
1194 class UserAdmin(PermissionCheckingAdminMixin, UserAdmin):
1195 # Note: Make sure PermissionCheckingAdminMixin is listed before
1196 # admin.ModelAdmin in the class declaration.
1201 # The forms to add and change user instances
1202 form = UserChangeForm
1203 add_form = UserCreationForm
1205 # The fields to be used in displaying the User model.
1206 # These override the definitions on the base UserAdmin
1207 # that reference specific fields on auth.User.
1208 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
1209 list_filter = ('site',)
1210 inlines = [SlicePrivilegeInline,SitePrivilegeInline,ControllerPrivilegeInline,UserDashboardViewInline]
1212 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
1213 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1216 ('Login Details', {'fields': ['backend_status_text', 'email', 'site','password', 'is_active', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
1217 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
1218 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
1219 #('Important dates', {'fields': ('last_login',)}),
1223 'classes': ('wide',),
1224 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')},
1227 readonly_fields = ('backend_status_text', )
1228 search_fields = ('email',)
1229 ordering = ('email',)
1230 filter_horizontal = ()
1232 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
1235 def suit_form_tabs(self):
1236 if getattr(_thread_locals, "obj", None) is None:
1239 return (('general','Login Details'),
1240 ('contact','Contact Information'),
1241 ('sliceprivileges','Slice Privileges'),
1242 ('siteprivileges','Site Privileges'),
1243 ('controllerprivileges','Controller Privileges'),
1244 ('dashboards','Dashboard Views'))
1246 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1247 if db_field.name == 'site':
1248 kwargs['queryset'] = Site.select_by_user(request.user)
1250 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1252 def queryset(self, request):
1253 return User.select_by_user(request.user)
1255 class DashboardViewAdmin(PlanetStackBaseAdmin):
1256 fieldsets = [('Dashboard View Details',
1257 {'fields': ['backend_status_text', 'name', 'url'],
1258 'classes': ['suit-tab suit-tab-general']})
1260 readonly_fields = ('backend_status_text', )
1262 suit_form_tabs =(('general','Dashboard View Details'),)
1264 class ServiceResourceInline(PlStackTabularInline):
1265 model = ServiceResource
1268 class ServiceClassAdmin(PlanetStackBaseAdmin):
1269 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1270 list_display_links = ('backend_status_icon', 'name', )
1271 inlines = [ServiceResourceInline]
1273 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1274 user_readonly_inlines = []
1276 class ReservedResourceInline(PlStackTabularInline):
1277 model = ReservedResource
1279 suit_classes = 'suit-tab suit-tab-reservedresources'
1281 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1282 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1284 if db_field.name == 'resource':
1285 # restrict resources to those that the slice's service class allows
1286 if request._slice is not None:
1287 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1288 if len(field.queryset) > 0:
1289 field.initial = field.queryset.all()[0]
1291 field.queryset = field.queryset.none()
\r
1292 elif db_field.name == 'sliver':
\r
1293 # restrict slivers to those that belong to the slice
\r
1294 if request._slice is not None:
\r
1295 field.queryset = field.queryset.filter(slice = request._slice)
1297 field.queryset = field.queryset.none()
\r
1301 def queryset(self, request):
1302 return ReservedResource.select_by_user(request.user)
1304 class ReservationChangeForm(forms.ModelForm):
1308 'slice' : LinkedSelect
1311 class ReservationAddForm(forms.ModelForm):
1312 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1313 refresh = forms.CharField(widget=forms.HiddenInput())
1316 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1318 def clean_slice(self):
1319 slice = self.cleaned_data.get("slice")
1320 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1322 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1328 'slice' : LinkedSelect
1332 class ReservationAddRefreshForm(ReservationAddForm):
1333 """ This form is displayed when the Reservation Form receives an update
1334 from the Slice dropdown onChange handler. It doesn't validate the
1335 data and doesn't save the data. This will cause the form to be
1339 """ don't validate anything other than slice """
1340 dont_validate_fields = ("startTime", "duration")
1342 def full_clean(self):
1343 result = super(ReservationAddForm, self).full_clean()
1345 for fieldname in self.dont_validate_fields:
1346 if fieldname in self._errors:
1347 del self._errors[fieldname]
1351 """ don't save anything """
1355 class ReservationAdmin(PlanetStackBaseAdmin):
1356 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
1357 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1358 readonly_fields = ('backend_status_text', )
1359 list_display = ('startTime', 'duration')
1360 form = ReservationAddForm
1362 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1364 inlines = [ReservedResourceInline]
1365 user_readonly_fields = fieldList
1367 def add_view(self, request, form_url='', extra_context=None):
1368 timezone.activate(request.user.timezone)
1369 request._refresh = False
1370 request._slice = None
1371 if request.method == 'POST':
1372 # "refresh" will be set to "1" if the form was submitted due to
1373 # a change in the Slice dropdown.
1374 if request.POST.get("refresh","1") == "1":
1375 request._refresh = True
1376 request.POST["refresh"] = "0"
1378 # Keep track of the slice that was selected, so the
1379 # reservedResource inline can filter items for the slice.
1380 request._slice = request.POST.get("slice",None)
1381 if (request._slice is not None):
1382 request._slice = Slice.objects.get(id=request._slice)
1384 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1387 def changelist_view(self, request, extra_context = None):
1388 timezone.activate(request.user.timezone)
1389 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1391 def get_form(self, request, obj=None, **kwargs):
1394 # For changes, set request._slice to the slice already set in the
1396 request._slice = obj.slice
1397 self.form = ReservationChangeForm
1399 if getattr(request, "_refresh", False):
1400 self.form = ReservationAddRefreshForm
1402 self.form = ReservationAddForm
1403 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1405 def get_readonly_fields(self, request, obj=None):
1406 if (obj is not None):
1407 # Prevent slice from being changed after the reservation has been
1413 def queryset(self, request):
1414 return Reservation.select_by_user(request.user)
1416 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1417 list_display = ("backend_status_icon", "name", )
1418 list_display_links = ('backend_status_icon', 'name', )
1419 user_readonly_fields = ['name']
1420 user_readonly_inlines = []
1422 class RouterAdmin(PlanetStackBaseAdmin):
1423 list_display = ("backend_status_icon", "name", )
1424 list_display_links = ('backend_status_icon', 'name', )
1425 user_readonly_fields = ['name']
1426 user_readonly_inlines = []
1428 class RouterInline(PlStackTabularInline):
1429 model = Router.networks.through
1431 verbose_name_plural = "Routers"
1432 verbose_name = "Router"
1433 suit_classes = 'suit-tab suit-tab-routers'
1435 class NetworkParameterInline(PlStackGenericTabularInline):
1436 model = NetworkParameter
1438 verbose_name_plural = "Parameters"
1439 verbose_name = "Parameter"
1440 suit_classes = 'suit-tab suit-tab-netparams'
1441 fields = ['backend_status_icon', 'parameter', 'value']
1442 readonly_fields = ('backend_status_icon', )
1444 class NetworkSliversInline(PlStackTabularInline):
1445 fields = ['backend_status_icon', 'network','sliver','ip']
1446 readonly_fields = ("backend_status_icon", "ip", )
1447 model = NetworkSliver
1448 selflink_fieldname = "sliver"
1450 verbose_name_plural = "Slivers"
1451 verbose_name = "Sliver"
1452 suit_classes = 'suit-tab suit-tab-networkslivers'
1454 class NetworkSlicesInline(PlStackTabularInline):
1455 model = NetworkSlice
1456 selflink_fieldname = "slice"
1458 verbose_name_plural = "Slices"
1459 verbose_name = "Slice"
1460 suit_classes = 'suit-tab suit-tab-networkslices'
1461 fields = ['backend_status_icon', 'network','slice']
1462 readonly_fields = ('backend_status_icon', )
1464 class ControllerNetworksInline(PlStackTabularInline):
1465 model = ControllerNetworks
1467 verbose_name_plural = "Controller Networks"
1468 verbose_name = "Controller Network"
1469 suit_classes = 'suit-tab suit-tab-admin-only'
1470 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
1471 readonly_fields = ('backend_status_icon', )
1473 class NetworkForm(forms.ModelForm):
1477 'topologyParameters': UploadTextareaWidget,
1478 'controllerParameters': UploadTextareaWidget,
1481 class NetworkAdmin(PlanetStackBaseAdmin):
1482 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1483 list_display_links = ('backend_status_icon', 'name', )
1484 readonly_fields = ("subnet", )
1486 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1487 admin_inlines = [ControllerNetworksInline]
1492 (None, {'fields': ['backend_status_text', 'name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'],
1493 'classes':['suit-tab suit-tab-general']}),
1494 (None, {'fields': ['topologyParameters', 'controllerUrl', 'controllerParameters'],
1495 'classes':['suit-tab suit-tab-sdn']}),
1498 readonly_fields = ('backend_status_text', )
1499 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1502 def suit_form_tabs(self):
1503 tabs=[('general','Network Details'),
1504 ('sdn', 'SDN Configuration'),
1505 ('netparams', 'Parameters'),
1506 ('networkslivers','Slivers'),
1507 ('networkslices','Slices'),
1508 ('routers','Routers'),
1511 request=getattr(_thread_locals, "request", None)
1512 if request and request.user.is_admin:
1513 tabs.append( ('admin-only', 'Admin-Only') )
1518 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1519 list_display = ("backend_status_icon", "name", "guaranteedBandwidth", "visibility")
1520 list_display_links = ('backend_status_icon', 'name', )
1521 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1522 user_readonly_inlines = []
1524 (None, {'fields': ['name', 'description', 'guaranteedBandwidth', 'visibility', 'translation', 'sharedNetworkName', 'sharedNetworkId', 'topologyKind', 'controllerKind'],
1525 'classes':['suit-tab suit-tab-general']}),]
1526 suit_form_tabs = (('general','Network Template Details'), )
1528 class FlavorAdmin(PlanetStackBaseAdmin):
1529 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1530 list_display_links = ("backend_status_icon", "name")
1531 user_readonly_fields = ("name", "flavor")
1532 fields = ("name", "description", "flavor", "order", "default")
1534 # register a signal that caches the user's credentials when they log in
1535 def cache_credentials(sender, user, request, **kwds):
1536 auth = {'username': request.POST['username'],
1537 'password': request.POST['password']}
1538 request.session['auth'] = auth
1539 user_logged_in.connect(cache_credentials)
1541 def dollar_field(fieldName, short_description):
1542 def newFunc(self, obj):
1544 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1546 x=getattr(obj, fieldName, 0.0)
1548 newFunc.short_description = short_description
1551 def right_dollar_field(fieldName, short_description):
1552 def newFunc(self, obj):
1554 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1555 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1557 x=getattr(obj, fieldName, 0.0)
1559 newFunc.short_description = short_description
1560 newFunc.allow_tags = True
1563 class InvoiceChargeInline(PlStackTabularInline):
1566 verbose_name_plural = "Charges"
1567 verbose_name = "Charge"
1568 exclude = ['account']
1569 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1570 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1574 dollar_amount = right_dollar_field("amount", "Amount")
1576 class InvoiceAdmin(admin.ModelAdmin):
1577 list_display = ("date", "account")
1579 inlines = [InvoiceChargeInline]
1581 fields = ["date", "account", "dollar_amount"]
1582 readonly_fields = ["date", "account", "dollar_amount"]
1584 dollar_amount = dollar_field("amount", "Amount")
1586 class InvoiceInline(PlStackTabularInline):
1589 verbose_name_plural = "Invoices"
1590 verbose_name = "Invoice"
1591 fields = ["date", "dollar_amount"]
1592 readonly_fields = ["date", "dollar_amount"]
1593 suit_classes = 'suit-tab suit-tab-accountinvoice'
1597 dollar_amount = right_dollar_field("amount", "Amount")
1599 class PendingChargeInline(PlStackTabularInline):
1602 verbose_name_plural = "Charges"
1603 verbose_name = "Charge"
1604 exclude = ["invoice"]
1605 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1606 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1607 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1611 def queryset(self, request):
1612 qs = super(PendingChargeInline, self).queryset(request)
1613 qs = qs.filter(state="pending")
1616 dollar_amount = right_dollar_field("amount", "Amount")
1618 class PaymentInline(PlStackTabularInline):
1621 verbose_name_plural = "Payments"
1622 verbose_name = "Payment"
1623 fields = ["date", "dollar_amount"]
1624 readonly_fields = ["date", "dollar_amount"]
1625 suit_classes = 'suit-tab suit-tab-accountpayments'
1629 dollar_amount = right_dollar_field("amount", "Amount")
1631 class AccountAdmin(admin.ModelAdmin):
1632 list_display = ("site", "balance_due")
1634 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1637 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1639 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1642 ('general','Account Details'),
1643 ('accountinvoice', 'Invoices'),
1644 ('accountpayments', 'Payments'),
1645 ('accountpendingcharges','Pending Charges'),
1648 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1649 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1650 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1652 # Now register the new UserAdmin...
1653 admin.site.register(User, UserAdmin)
1654 # ... and, since we're not using Django's builtin permissions,
1655 # unregister the Group model from admin.
1656 #admin.site.unregister(Group)
1658 #Do not show django evolution in the admin interface
1659 from django_evolution.models import Version, Evolution
1660 #admin.site.unregister(Version)
1661 #admin.site.unregister(Evolution)
1664 # When debugging it is often easier to see all the classes, but for regular use
1665 # only the top-levels should be displayed
1668 admin.site.register(Deployment, DeploymentAdmin)
1669 admin.site.register(Controller, ControllerAdmin)
1670 admin.site.register(Site, SiteAdmin)
1671 admin.site.register(Slice, SliceAdmin)
1672 admin.site.register(Service, ServiceAdmin)
1673 admin.site.register(Reservation, ReservationAdmin)
1674 admin.site.register(Network, NetworkAdmin)
1675 admin.site.register(Router, RouterAdmin)
1676 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1677 admin.site.register(Account, AccountAdmin)
1678 admin.site.register(Invoice, InvoiceAdmin)
1681 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1682 admin.site.register(ServiceClass, ServiceClassAdmin)
1683 #admin.site.register(PlanetStack)
1684 admin.site.register(Tag, TagAdmin)
1685 admin.site.register(ControllerRole)
1686 admin.site.register(SiteRole)
1687 admin.site.register(SliceRole)
1688 admin.site.register(PlanetStackRole)
1689 admin.site.register(Node, NodeAdmin)
1690 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1691 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1692 admin.site.register(Sliver, SliverAdmin)
1693 admin.site.register(Image, ImageAdmin)
1694 admin.site.register(DashboardView, DashboardViewAdmin)
1695 admin.site.register(Flavor, FlavorAdmin)