blob: a3aea48a3d8e15a135a24385a12ae5f95549aa73 [file] [log] [blame]
Siobhan Tully30fd4292013-05-10 08:59:56 -04001from core.models import Site
2from core.models import *
3from openstack.manager import OpenStackManager
Tony Macke59a7c82013-04-27 11:08:10 -04004
Tony Mack7130ac32013-03-22 21:58:00 -04005from django.contrib import admin
Siobhan Tully53437282013-04-26 19:30:27 -04006from django.contrib.auth.models import Group
Siobhan Tully4bc09f22013-04-10 21:15:21 -04007from django import forms
Tony Mackd90cdbf2013-04-16 22:48:40 -04008from django.utils.safestring import mark_safe
Tony Mack7130ac32013-03-22 21:58:00 -04009from django.contrib.auth.admin import UserAdmin
Scott Baker69e045d2014-11-17 23:44:03 -080010from django.contrib.admin.widgets import FilteredSelectMultiple, AdminTextareaWidget
Scott Baker1a6a3902014-10-03 00:32:37 -070011from django.contrib.auth.forms import ReadOnlyPasswordHashField, AdminPasswordChangeForm
Scott Bakeracd45142013-05-19 16:19:16 -070012from django.contrib.auth.signals import user_logged_in
13from django.utils import timezone
Siobhan Tullyde5450d2013-06-21 11:35:33 -040014from django.contrib.contenttypes import generic
Siobhan Tullybfd11dc2013-09-03 12:59:24 -040015from suit.widgets import LinkedSelect
Siobhan Tullycf04fb62014-01-11 11:25:57 -050016from django.core.exceptions import PermissionDenied
Tony Mack5817cb42015-02-16 19:54:24 -050017from django.core.urlresolvers import reverse, resolve, NoReverseMatch
Scott Baker69e045d2014-11-17 23:44:03 -080018from django.utils.encoding import force_text, python_2_unicode_compatible
19from django.utils.html import conditional_escape, format_html
Scott Bakera9b8f612015-02-26 20:42:11 -080020from django.utils.text import capfirst
Scott Baker69e045d2014-11-17 23:44:03 -080021from django.forms.utils import flatatt, to_current_timezone
Scott Baker92d22172014-10-21 21:03:08 -070022from cgi import escape as html_escape
Tony Mack7130ac32013-03-22 21:58:00 -040023
Scott Baker36f50872014-08-21 13:01:25 -070024import django_evolution
Scott Baker6a995352014-10-06 17:51:20 -070025import threading
26
27# thread locals necessary to work around a django-suit issue
28_thread_locals = threading.local()
Scott Baker36f50872014-08-21 13:01:25 -070029
Scott Bakere5f9d7d2015-02-10 18:24:20 -080030ICON_URLS = {"success": "/static/admin/img/icon_success.gif",
31 "clock": "/static/admin/img/icon_clock.gif",
32 "error": "/static/admin/img/icon_error.gif"}
33
34def backend_icon(obj):
35 (icon, tooltip) = obj.get_backend_icon()
36 icon_url = ICON_URLS.get(icon, "unknown")
37
38 if tooltip:
39 return '<span style="min-width:16px;" title="%s"><img src="%s"></span>' % (tooltip, icon_url)
Scott Baker40c00762014-08-21 16:55:59 -070040 else:
Scott Bakere5f9d7d2015-02-10 18:24:20 -080041 return '<span style="min-width:16px;"><img src="%s"></span>' % icon_url
Scott Baker40c00762014-08-21 16:55:59 -070042
43def backend_text(obj):
Scott Bakere5f9d7d2015-02-10 18:24:20 -080044 (icon, tooltip) = obj.get_backend_icon()
45 icon_url = ICON_URLS.get(icon, "unknown")
46
47 return '<img src="%s"> %s' % (icon_url, tooltip)
Scott Baker63d1a552014-08-21 15:19:07 -070048
Scott Baker69e045d2014-11-17 23:44:03 -080049class UploadTextareaWidget(AdminTextareaWidget):
50 def render(self, name, value, attrs=None):
51 if value is None:
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -050052 value = ''
53 final_attrs = self.build_attrs(attrs, name=name)
54 return format_html('<input type="file" style="width: 0; height: 0" id="btn_upload_%s" onChange="uploadTextarea(event,\'%s\');">' \
55 '<button onClick="$(\'#btn_upload_%s\').click(); return false;">Upload</button>' \
56 '<br><textarea{0}>\r\n{1}</textarea>' % (attrs["id"], attrs["id"], attrs["id"]),
57 flatatt(final_attrs),
Scott Baker69e045d2014-11-17 23:44:03 -080058 force_text(value))
59
Scott Baker50ac4192015-05-11 16:36:58 -070060class SliderWidget(forms.HiddenInput):
61 def render(self, name, value, attrs=None):
62 if value is None:
63 value = '0'
64 final_attrs = self.build_attrs(attrs, name=name)
65 attrs = attrs or attrs[:]
66 attrs["name"] = name
67 attrs["value"] = value
68 html = """<div style="width:640px"><span id="%(id)s_label">%(value)s</span><div id="%(id)s_slider" style="float:right;width:610px;margin-top:5px"></div></div>
69 <script>
70 $(function() {
71 $("#%(id)s_slider").slider({
72 value: %(value)s,
73 slide: function(event, ui) { $("#%(id)s").val( ui.value ); $("#%(id)s_label").html(ui.value); },
74 });
75 });
76 </script>
77 <input type="hidden" id="%(id)s" name="%(name)s" value="%(value)s"></input>
78 """ % attrs
79 html = html.replace("{","{{").replace("}","}}")
80 return format_html(html,
81 flatatt(final_attrs),
82 force_text(value))
83
84
Scott Baker36f50872014-08-21 13:01:25 -070085class PlainTextWidget(forms.HiddenInput):
86 input_type = 'hidden'
87
88 def render(self, name, value, attrs=None):
89 if value is None:
90 value = ''
91 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
92
Scott Baker3a8aed62015-02-27 12:21:22 -080093class XOSAdminMixin(object):
Scott Baker1a6a3902014-10-03 00:32:37 -070094 # call save_by_user and delete_by_user instead of save and delete
Siobhan Tullycf04fb62014-01-11 11:25:57 -050095
96 def has_add_permission(self, request, obj=None):
97 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -070098
Siobhan Tullycf04fb62014-01-11 11:25:57 -050099 def has_delete_permission(self, request, obj=None):
100 return (not self.__user_is_readonly(request))
101
102 def save_model(self, request, obj, form, change):
103 if self.__user_is_readonly(request):
Scott Baker1a6a3902014-10-03 00:32:37 -0700104 # this 'if' might be redundant if save_by_user is implemented right
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500105 raise PermissionDenied
Scott Baker1a6a3902014-10-03 00:32:37 -0700106
107 obj.caller = request.user
108 # update openstack connection to use this site/tenant
109 obj.save_by_user(request.user)
110
111 def delete_model(self, request, obj):
112 obj.delete_by_user(request.user)
113
114 def save_formset(self, request, form, formset, change):
115 instances = formset.save(commit=False)
116 for instance in instances:
117 instance.save_by_user(request.user)
118
119 # BUG in django 1.7? Objects are not deleted by formset.save if
120 # commit is False. So let's delete them ourselves.
121 #
122 # code from forms/models.py save_existing_objects()
123 try:
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -0500124 forms_to_delete = formset.deleted_forms
125 except AttributeError:
Scott Baker1a6a3902014-10-03 00:32:37 -0700126 forms_to_delete = []
127 if formset.initial_forms:
128 for form in formset.initial_forms:
129 obj = form.instance
130 if form in forms_to_delete:
131 if obj.pk is None:
132 continue
133 formset.deleted_objects.append(obj)
134 obj.delete()
135
136 formset.save_m2m()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500137
138 def get_actions(self,request):
Scott Baker3a8aed62015-02-27 12:21:22 -0800139 actions = super(XOSAdminMixin,self).get_actions(request)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500140
141 if self.__user_is_readonly(request):
142 if 'delete_selected' in actions:
143 del actions['delete_selected']
144
145 return actions
146
Scott Bakerfbe0f652015-04-03 17:44:31 -0700147 def url_for_model_changelist(self, request, model):
148 # used in add_extra_context
149 return reverse('admin:%s_%s_changelist' % (model._meta.app_label, model._meta.model_name), current_app=model._meta.app_label)
150
Scott Bakera8ef2742015-04-02 22:32:40 -0700151 def add_extra_context(self, request, extra_context):
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800152 # allow custom application breadcrumb url and name
153 extra_context["custom_app_breadcrumb_url"] = getattr(self, "custom_app_breadcrumb_url", None)
154 extra_context["custom_app_breadcrumb_name"] = getattr(self, "custom_app_breadcrumb_name", None)
Scott Bakerf85c0092015-04-02 22:07:18 -0700155 extra_context["custom_changelist_breadcrumb_url"] = getattr(self, "custom_changelist_breadcrumb_url", None)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800156
157 # for Service admins to render their Administration page
158 if getattr(self, "extracontext_registered_admins", False):
159 admins=[]
160 for model, model_admin in admin.site._registry.items():
161 if model == self.model:
162 continue
163 if model._meta.app_label == self.model._meta.app_label:
164 info = {"app": model._meta.app_label,
165 "model": model._meta.model_name,
166 "name": capfirst(model._meta.verbose_name_plural),
Scott Bakerfbe0f652015-04-03 17:44:31 -0700167 "url": self.url_for_model_changelist(request,model) }
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800168 admins.append(info)
169 extra_context["registered_admins"] = admins
170
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500171 def change_view(self,request,object_id, extra_context=None):
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800172 extra_context = extra_context or {}
173
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500174 if self.__user_is_readonly(request):
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -0500175 if not hasattr(self, "readonly_save"):
176 # save the original readonly fields
177 self.readonly_save = self.readonly_fields
178 self.inlines_save = self.inlines
179 if hasattr(self, "user_readonly_fields"):
180 self.readonly_fields=self.user_readonly_fields
181 if hasattr(self, "user_readonly_inlines"):
182 self.inlines = self.user_readonly_inlines
183 else:
184 if hasattr(self, "readonly_save"):
185 # restore the original readonly fields
186 self.readonly_fields = self.readonly_save
187 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -0700188 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500189
Scott Bakera8ef2742015-04-02 22:32:40 -0700190 self.add_extra_context(request, extra_context)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800191
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500192 try:
Scott Baker3a8aed62015-02-27 12:21:22 -0800193 return super(XOSAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500194 except PermissionDenied:
195 pass
196 if request.method == 'POST':
197 raise PermissionDenied
198 request.readonly = True
Scott Baker3a8aed62015-02-27 12:21:22 -0800199 return super(XOSAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500200
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800201 def changelist_view(self, request, extra_context = None):
202 extra_context = extra_context or {}
203
Scott Bakera8ef2742015-04-02 22:32:40 -0700204 self.add_extra_context(request, extra_context)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800205
Scott Baker3a8aed62015-02-27 12:21:22 -0800206 return super(XOSAdminMixin, self).changelist_view(request, extra_context=extra_context)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800207
Scott Baker88ac9d62015-04-14 17:01:18 -0700208 def add_view(self, request, form_url='', extra_context = None):
Scott Bakerfbe0f652015-04-03 17:44:31 -0700209 extra_context = extra_context or {}
210
211 self.add_extra_context(request, extra_context)
212
Scott Baker88ac9d62015-04-14 17:01:18 -0700213 return super(XOSAdminMixin, self).add_view(request, form_url, extra_context=extra_context)
Scott Bakerfbe0f652015-04-03 17:44:31 -0700214
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500215 def __user_is_readonly(self, request):
216 return request.user.isReadOnlyUser()
217
Scott Baker40c00762014-08-21 16:55:59 -0700218 def backend_status_text(self, obj):
219 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -0700220
Scott Baker63d1a552014-08-21 15:19:07 -0700221 def backend_status_icon(self, obj):
Scott Baker40c00762014-08-21 16:55:59 -0700222 return mark_safe(backend_icon(obj))
Scott Baker63d1a552014-08-21 15:19:07 -0700223 backend_status_icon.short_description = ""
224
Scott Bakerdc4724c2014-11-05 09:05:38 -0800225 def get_form(self, request, obj=None, **kwargs):
Scott Baker9b3c1af2014-10-16 00:57:55 -0700226 # Save obj and request in thread-local storage, so suit_form_tabs can
227 # use it to determine whether we're in edit or add mode, and can
228 # determine whether the user is an admin.
229 _thread_locals.request = request
230 _thread_locals.obj = obj
Scott Baker3a8aed62015-02-27 12:21:22 -0800231 return super(XOSAdminMixin, self).get_form(request, obj, **kwargs)
Scott Baker9b3c1af2014-10-16 00:57:55 -0700232
233 def get_inline_instances(self, request, obj=None):
Scott Baker3a8aed62015-02-27 12:21:22 -0800234 inlines = super(XOSAdminMixin, self).get_inline_instances(request, obj)
Scott Baker9b3c1af2014-10-16 00:57:55 -0700235
236 # inlines that should only be shown to an admin user
237 if request.user.is_admin:
238 for inline_class in getattr(self, "admin_inlines", []):
239 inlines.append(inline_class(self.model, self.admin_site))
240
241 return inlines
242
Scott Baker3a8aed62015-02-27 12:21:22 -0800243class ReadOnlyAwareAdmin(XOSAdminMixin, admin.ModelAdmin):
244 # Note: Make sure XOSAdminMixin is listed before
Scott Bakerf4aeedc2014-10-03 13:10:47 -0700245 # admin.ModelAdmin in the class declaration.
246
Scott Baker1a6a3902014-10-03 00:32:37 -0700247 pass
248
Scott Baker67db95f2015-02-18 15:50:11 -0800249class XOSBaseAdmin(ReadOnlyAwareAdmin):
Scott Baker1a6a3902014-10-03 00:32:37 -0700250 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700251
Scott Bakere8859f92014-05-23 12:42:40 -0700252class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400253 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700254 if not super(SingletonAdmin, self).has_add_permission(request):
255 return False
256
Siobhan Tullyce652d02013-10-08 21:52:35 -0400257 num_objects = self.model.objects.count()
258 if num_objects >= 1:
259 return False
260 else:
261 return True
262
Scott Bakera9b8f612015-02-26 20:42:11 -0800263class ServiceAppAdmin (SingletonAdmin):
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800264 extracontext_registered_admins = True
Scott Bakera9b8f612015-02-26 20:42:11 -0800265
Scott Baker67db95f2015-02-18 15:50:11 -0800266class XOSTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800267 def __init__(self, *args, **kwargs):
Scott Baker67db95f2015-02-18 15:50:11 -0800268 super(XOSTabularInline, self).__init__(*args, **kwargs)
Scott Baker86568322014-01-12 16:53:31 -0800269
270 # InlineModelAdmin as no get_fields() method, so in order to add
271 # the selflink field, we override __init__ to modify self.fields and
272 # self.readonly_fields.
273
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800274 self.setup_selflink()
275
Scott Baker874936e2014-01-13 18:15:34 -0800276 def get_change_url(self, model, id):
277 """ Get the URL to a change form in the admin for this model """
278 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800279 try:
Scott Baker874936e2014-01-13 18:15:34 -0800280 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800281 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800282 return None
283
284 return url
285
286 def setup_selflink(self):
287 if hasattr(self, "selflink_fieldname"):
288 """ self.selflink_model can be defined to punch through a relation
289 to its target object. For example, in SliceNetworkInline, set
290 selflink_model = "network", and the URL will lead to the Network
291 object instead of trying to bring up a change view of the
292 SliceNetwork object.
293 """
294 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
295 else:
296 self.selflink_model = self.model
297
298 url = self.get_change_url(self.selflink_model, 0)
299
300 # We don't have an admin for this object, so don't create the
301 # selflink.
302 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800303 return
304
Scott Baker874936e2014-01-13 18:15:34 -0800305 # Since we need to add "selflink" to the field list, we need to create
306 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800307 if (self.fields is None):
308 self.fields = []
309 for f in self.model._meta.fields:
310 if f.editable and f.name != "id":
311 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800312
Scott Baker874936e2014-01-13 18:15:34 -0800313 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800314
Scott Baker874936e2014-01-13 18:15:34 -0800315 if self.readonly_fields is None:
316 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800317
Scott Baker874936e2014-01-13 18:15:34 -0800318 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800319
320 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800321 if hasattr(self, "selflink_fieldname"):
322 obj = getattr(obj, self.selflink_fieldname)
323
Scott Baker86568322014-01-12 16:53:31 -0800324 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800325 url = self.get_change_url(self.selflink_model, obj.id)
326 return "<a href='%s'>Details</a>" % str(url)
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -0500327 else:
328 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800329
330 selflink.allow_tags = True
331 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400332
Scott Bakerb27b62c2014-08-15 16:29:16 -0700333 def has_add_permission(self, request):
334 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500335
336 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700337 readonly_fields = list(self.readonly_fields)[:]
338 if request.user.isReadOnlyUser():
339 for field in self.fields:
340 if not field in readonly_fields:
341 readonly_fields.append(field)
342 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500343
Scott Baker40c00762014-08-21 16:55:59 -0700344 def backend_status_icon(self, obj):
345 return mark_safe(backend_icon(obj))
346 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700347
Scott Bakerb27b62c2014-08-15 16:29:16 -0700348class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500349 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700350 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500351
Scott Bakerb27b62c2014-08-15 16:29:16 -0700352 def get_readonly_fields(self, request, obj=None):
353 readonly_fields = list(self.readonly_fields)[:]
354 if request.user.isReadOnlyUser():
355 for field in self.fields:
356 if not field in readonly_fields:
357 readonly_fields.append(field)
358 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500359
Scott Baker40c00762014-08-21 16:55:59 -0700360 def backend_status_icon(self, obj):
361 return mark_safe(backend_icon(obj))
362 backend_status_icon.short_description = ""
363
Scott Baker67db95f2015-02-18 15:50:11 -0800364class ReservationInline(XOSTabularInline):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400365 model = Reservation
366 extra = 0
367 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700368
Tony Mack5b061472014-02-04 07:57:10 -0500369 def queryset(self, request):
370 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400371
Scott Bakerb27b62c2014-08-15 16:29:16 -0700372class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400373 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400374 extra = 0
375 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500376 fields = ['service', 'name', 'value']
377
378 def queryset(self, request):
379 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400380
Scott Baker74d8e622013-07-29 16:04:22 -0700381class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400382 """ This is a callable that looks up a network name in a sliver and returns
383 the ip address for that network.
384 """
385
Scott Baker434ca7e2014-08-15 12:29:20 -0700386 byNetworkName = {} # class variable
387
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400388 def __init__(self, name):
389 self.short_description = name
390 self.__name__ = name
391 self.network_name = name
392
393 def __call__(self, obj):
394 if obj is not None:
395 for nbs in obj.networksliver_set.all():
396 if (nbs.network.name == self.network_name):
397 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700398 return ""
399
400 def __str__(self):
401 return self.network_name
402
Scott Baker434ca7e2014-08-15 12:29:20 -0700403 @staticmethod
404 def get(network_name):
405 """ We want to make sure we alwars return the same NetworkLookerUpper
406 because sometimes django will cause them to be instantiated multiple
407 times (and we don't want different ones in form.fields vs
408 SliverInline.readonly_fields).
409 """
410 if network_name not in NetworkLookerUpper.byNetworkName:
411 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
412 return NetworkLookerUpper.byNetworkName[network_name]
413
Scott Baker67db95f2015-02-18 15:50:11 -0800414class SliverInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400415 model = Sliver
Scott Baker9d856052015-01-19 11:32:20 -0800416 fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400417 extra = 0
Scott Baker9d856052015-01-19 11:32:20 -0800418 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400419 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700420
Tony Mack5b061472014-02-04 07:57:10 -0500421 def queryset(self, request):
422 return Sliver.select_by_user(request.user)
423
Scott Bakerb24cc932014-06-09 10:51:16 -0700424 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Tony Mackb2dba4b2014-12-26 13:38:02 -0500425 if db_field.name == 'deployment':
Tony Mack1b8975c2015-02-24 15:41:49 -0500426
427 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
Scott Baker7a61dc42014-09-02 17:08:20 -0700428 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
Tony Mackb2dba4b2014-12-26 13:38:02 -0500429 if db_field.name == 'flavor':
Scott Baker4b6d9442014-09-08 12:14:14 -0700430 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700431
432 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700433
434 return field
435
Scott Baker67db95f2015-02-18 15:50:11 -0800436class SiteInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400437 model = Site
438 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400439 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400440
Tony Mack5b061472014-02-04 07:57:10 -0500441 def queryset(self, request):
442 return Site.select_by_user(request.user)
443
Scott Baker67db95f2015-02-18 15:50:11 -0800444class UserInline(XOSTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400445 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700446 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
447 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400448 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400449 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400450
Tony Mack5b061472014-02-04 07:57:10 -0500451 def queryset(self, request):
452 return User.select_by_user(request.user)
453
Scott Baker67db95f2015-02-18 15:50:11 -0800454class SliceInline(XOSTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400455 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700456 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
457 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400458 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400459 suit_classes = 'suit-tab suit-tab-slices'
460
Tony Mack5b061472014-02-04 07:57:10 -0500461 def queryset(self, request):
462 return Slice.select_by_user(request.user)
463
Scott Baker67db95f2015-02-18 15:50:11 -0800464class NodeInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400465 model = Node
466 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400467 suit_classes = 'suit-tab suit-tab-nodes'
Tony Mack68a1e422014-12-08 16:43:02 -0500468 fields = ['backend_status_icon', 'name', 'site_deployment']
Scott Baker40c00762014-08-21 16:55:59 -0700469 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400470
Scott Baker67db95f2015-02-18 15:50:11 -0800471class DeploymentPrivilegeInline(XOSTabularInline):
Tony Mack68a1e422014-12-08 16:43:02 -0500472 model = DeploymentPrivilege
473 extra = 0
Tony Mack88c89902015-02-09 21:41:57 -0500474 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Tony Mack68a1e422014-12-08 16:43:02 -0500475 fields = ['backend_status_icon', 'user','role','deployment']
476 readonly_fields = ('backend_status_icon', )
477
478 def queryset(self, request):
479 return DeploymentPrivilege.select_by_user(request.user)
480
Scott Baker67db95f2015-02-18 15:50:11 -0800481class ControllerSiteInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -0500482 model = ControllerSite
483 extra = 0
484 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Macke2363c12015-01-06 15:08:20 -0500485 fields = ['controller', 'site', 'tenant_id']
Tony Mack3066a952015-01-05 22:48:11 -0500486
487
Scott Baker67db95f2015-02-18 15:50:11 -0800488class SitePrivilegeInline(XOSTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400489 model = SitePrivilege
490 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400491 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700492 fields = ['backend_status_icon', 'user','site', 'role']
493 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400494
Tony Mackc2835a92013-05-28 09:18:49 -0400495 def formfield_for_foreignkey(self, db_field, request, **kwargs):
496 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500497 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400498
499 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500500 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400501 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
502
Tony Mack5b061472014-02-04 07:57:10 -0500503 def queryset(self, request):
504 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400505
Tony Mack60789ac2015-05-11 20:39:32 -0400506
507class ServicePrivilegeInline(XOSTabularInline):
508 model = ServicePrivilege
509 extra = 0
510 suit_classes = 'suit-tab suit-tab-serviceprivileges'
511 fields = ['backend_status_icon', 'user','service', 'role']
512 readonly_fields = ('backend_status_icon', )
513
514 def formfield_for_foreignkey(self, db_field, request, **kwargs):
515 if db_field.name == 'service':
516 kwargs['queryset'] = Service.select_by_user(request.user)
Tony Mack5fa0f402015-05-15 06:33:45 -0400517 if db_field.name == 'user':
518 kwargs['queryset'] = User.select_by_user(request.user)
519 return super(ServicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mack60789ac2015-05-11 20:39:32 -0400520
521 def queryset(self, request):
522 return ServicePrivilege.select_by_user(request.user)
523
Scott Baker67db95f2015-02-18 15:50:11 -0800524class SiteDeploymentInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -0500525 model = SiteDeployment
Tony Macke4be32f2014-03-11 20:45:25 -0400526 extra = 0
Tony Mackb81d5e42015-01-30 10:58:29 -0500527 suit_classes = 'suit-tab suit-tab-sitedeployments'
Tony Mackd14d48f2014-12-05 17:13:08 -0500528 fields = ['backend_status_icon', 'deployment','site', 'controller']
Scott Baker40c00762014-08-21 16:55:59 -0700529 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400530
531 def formfield_for_foreignkey(self, db_field, request, **kwargs):
532 if db_field.name == 'site':
533 kwargs['queryset'] = Site.select_by_user(request.user)
534
535 if db_field.name == 'deployment':
536 kwargs['queryset'] = Deployment.select_by_user(request.user)
Tony Mackd14d48f2014-12-05 17:13:08 -0500537
538 if db_field.name == 'controller':
Tony Mack5817cb42015-02-16 19:54:24 -0500539 kwargs['queryset'] = Controller.select_by_user(request.user).filter(deployment__id=int(resolve(request.path).args[0]))
Tony Mackd14d48f2014-12-05 17:13:08 -0500540
Tony Mack3066a952015-01-05 22:48:11 -0500541 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Macke4be32f2014-03-11 20:45:25 -0400542
543 def queryset(self, request):
Tony Mack3066a952015-01-05 22:48:11 -0500544 return SiteDeployment.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400545
546
Scott Baker67db95f2015-02-18 15:50:11 -0800547class SlicePrivilegeInline(XOSTabularInline):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400548 model = SlicePrivilege
549 suit_classes = 'suit-tab suit-tab-sliceprivileges'
550 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700551 fields = ('backend_status_icon', 'user', 'slice', 'role')
552 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400553
Tony Mackc2835a92013-05-28 09:18:49 -0400554 def formfield_for_foreignkey(self, db_field, request, **kwargs):
555 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700556 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400557 if db_field.name == 'user':
Scott Baker36f50872014-08-21 13:01:25 -0700558 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400559
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400560 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400561
Tony Mack5b061472014-02-04 07:57:10 -0500562 def queryset(self, request):
563 return SlicePrivilege.select_by_user(request.user)
564
Scott Baker67db95f2015-02-18 15:50:11 -0800565class SliceNetworkInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700566 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800567 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700568 extra = 0
569 verbose_name = "Network Connection"
570 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400571 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700572 fields = ['backend_status_icon', 'network']
573 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700574
Scott Baker67db95f2015-02-18 15:50:11 -0800575class ImageDeploymentsInline(XOSTabularInline):
Sapan Bhatia1b6bba22014-11-19 15:10:16 -0500576 model = ImageDeployments
Scott Baker2170b972014-06-03 12:14:07 -0700577 extra = 0
578 verbose_name = "Image Deployments"
579 verbose_name_plural = "Image Deployments"
580 suit_classes = 'suit-tab suit-tab-imagedeployments'
Tony Mack06c8e472014-11-30 15:53:08 -0500581 fields = ['backend_status_icon', 'image', 'deployment']
582 readonly_fields = ['backend_status_icon']
583
Scott Baker67db95f2015-02-18 15:50:11 -0800584class ControllerImagesInline(XOSTabularInline):
Tony Mack06c8e472014-11-30 15:53:08 -0500585 model = ControllerImages
586 extra = 0
587 verbose_name = "Controller Images"
588 verbose_name_plural = "Controller Images"
589 suit_classes = 'suit-tab suit-tab-admin-only'
590 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
Scott Baker40c00762014-08-21 16:55:59 -0700591 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700592
Scott Baker67db95f2015-02-18 15:50:11 -0800593class SliceRoleAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400594 model = SliceRole
595 pass
596
Scott Baker67db95f2015-02-18 15:50:11 -0800597class SiteRoleAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400598 model = SiteRole
599 pass
600
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400601class DeploymentAdminForm(forms.ModelForm):
Scott Bakerde0f4412014-06-11 15:40:26 -0700602 images = forms.ModelMultipleChoiceField(
603 queryset=Image.objects.all(),
604 required=False,
605 help_text="Select which images should be deployed on this deployment",
606 widget=FilteredSelectMultiple(
607 verbose_name=('Images'), is_stacked=False
608 )
609 )
Scott Baker37b47902014-09-02 14:37:41 -0700610 flavors = forms.ModelMultipleChoiceField(
611 queryset=Flavor.objects.all(),
612 required=False,
613 help_text="Select which flavors should be usable on this deployment",
614 widget=FilteredSelectMultiple(
615 verbose_name=('Flavors'), is_stacked=False
616 )
617 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400618 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400619 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700620 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400621
Siobhan Tully320b4622014-01-17 15:11:14 -0500622 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700623 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500624 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
625
Scott Baker5380c522014-06-06 14:49:43 -0700626 self.fields['accessControl'].initial = "allow site " + request.user.site.name
627
Siobhan Tully320b4622014-01-17 15:11:14 -0500628 if self.instance and self.instance.pk:
Scott Baker69e045d2014-11-17 23:44:03 -0800629 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700630 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700631
632 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
633 """ helper function for handling m2m relations from the MultipleChoiceField
634
635 this_obj: the source object we want to link from
636
637 selected_objs: a list of destination objects we want to link to
638
639 all_relations: the full set of relations involving this_obj, including ones we don't want
640
641 relation_class: the class that implements the relation from source to dest
642
643 local_attrname: field name representing this_obj in relation_class
644
645 foreign_attrname: field name representing selected_objs in relation_class
646
647 This function will remove all newobjclass relations from this_obj
648 that are not contained in selected_objs, and add any relations that
649 are in selected_objs but don't exist in the data model yet.
650 """
651
652 existing_dest_objs = []
653 for relation in list(all_relations):
654 if getattr(relation, foreign_attrname) not in selected_objs:
655 #print "deleting site", sdp.site
656 relation.delete()
657 else:
658 existing_dest_objs.append(getattr(relation, foreign_attrname))
659
660 for dest_obj in selected_objs:
661 if dest_obj not in existing_dest_objs:
662 #print "adding site", site
663 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
664 relation = relation_class(**kwargs)
665 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500666
667 def save(self, commit=True):
668 deployment = super(DeploymentAdminForm, self).save(commit=False)
669
670 if commit:
671 deployment.save()
Scott Baker0057d052014-10-06 17:17:40 -0700672 # this has to be done after save() if/when a deployment is first created
673 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500674
675 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700676 # save_m2m() doesn't seem to work with 'through' relations. So we
677 # create/destroy the through models ourselves. There has to be
678 # a better way...
679
Tony Mackb2fde612014-12-15 11:45:02 -0500680 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ImageDeployments, "deployment", "image")
681 # manipulate_m2m_objs doesn't work for Flavor/Deployment relationship
682 # so well handle that manually here
683 for flavor in deployment.flavors.all():
684 if getattr(flavor, 'name') not in self.cleaned_data['flavors']:
Tony Mackd4ab7822014-12-15 12:37:59 -0500685 deployment.flavors.remove(flavor)
Tony Mackb2fde612014-12-15 11:45:02 -0500686 for flavor in self.cleaned_data['flavors']:
687 if flavor not in deployment.flavors.all():
688 flavor.deployments.add(deployment)
Scott Bakerc9b14f72014-05-22 13:44:20 -0700689
Scott Baker37b47902014-09-02 14:37:41 -0700690 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500691
692 return deployment
693
Scott Bakerff5e0f32014-05-22 14:40:27 -0700694class DeploymentAdminROForm(DeploymentAdminForm):
695 def save(self, commit=True):
696 raise PermissionDenied
697
Scott Baker67db95f2015-02-18 15:50:11 -0800698class SiteAssocInline(XOSTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500699 model = Site.deployments.through
700 extra = 0
701 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400702
Scott Baker67db95f2015-02-18 15:50:11 -0800703class DeploymentAdmin(XOSBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500704 model = Deployment
Scott Baker622bcf02015-02-10 08:40:34 -0800705 fieldList = ['backend_status_text', 'name', 'images', 'flavors', 'accessControl']
706 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack68a1e422014-12-08 16:43:02 -0500707 # node no longer directly connected to deployment
708 #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
Tony Mackb81d5e42015-01-30 10:58:29 -0500709 inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline,SiteDeploymentInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700710 list_display = ['backend_status_icon', 'name']
711 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700712 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500713
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500714 user_readonly_fields = ['name']
715
Tony Mack68a1e422014-12-08 16:43:02 -0500716 # nodes no longer direclty connected to deployments
Scott Baker622bcf02015-02-10 08:40:34 -0800717 suit_form_tabs =(('general','Deployment Details'),('deploymentprivileges','Privileges'), ('sitedeployments', 'Sites'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500718
Scott Bakerff5e0f32014-05-22 14:40:27 -0700719 def get_form(self, request, obj=None, **kwargs):
Tony Mackcf29cfa2015-02-05 06:13:04 -0500720 if request.user.isReadOnlyUser() or not request.user.is_admin:
Scott Bakerff5e0f32014-05-22 14:40:27 -0700721 kwargs["form"] = DeploymentAdminROForm
722 else:
723 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700724 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
725
726 # from stackexchange: pass the request object into the form
727
728 class AdminFormMetaClass(adminForm):
729 def __new__(cls, *args, **kwargs):
730 kwargs['request'] = request
731 return adminForm(*args, **kwargs)
732
733 return AdminFormMetaClass
734
Scott Baker67db95f2015-02-18 15:50:11 -0800735class ControllerAdmin(XOSBaseAdmin):
Scott Baker622bcf02015-02-10 08:40:34 -0800736 model = Controller
Scott Baker180148a2015-02-16 11:55:09 -0800737 fieldList = ['deployment', 'name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password', 'domain']
Scott Baker622bcf02015-02-10 08:40:34 -0800738 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack3066a952015-01-05 22:48:11 -0500739 inlines = [ControllerSiteInline] # ,ControllerImagesInline]
Tony Mackd14d48f2014-12-05 17:13:08 -0500740 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
741 list_display_links = ('backend_status_icon', 'name', )
742 readonly_fields = ('backend_status_text',)
743
744 user_readonly_fields = []
745
Tony Mack2e897fa2015-01-13 17:33:08 -0500746 def save_model(self, request, obj, form, change):
747 # update openstack connection to use this site/tenant
748 obj.save_by_user(request.user)
749
750 def delete_model(self, request, obj):
Scott Baker622bcf02015-02-10 08:40:34 -0800751 obj.delete_by_user(request.user)
752
Tony Mack78fc1362015-02-18 11:41:36 -0500753 def queryset(self, request):
754 return Controller.select_by_user(request.user)
755
Scott Baker622bcf02015-02-10 08:40:34 -0800756 @property
757 def suit_form_tabs(self):
758 tabs = [('general', 'Controller Details'),
759 ]
760
761 request=getattr(_thread_locals, "request", None)
762 if request and request.user.is_admin:
763 tabs.append( ('admin-only', 'Admin-Only') )
764
765 return tabs
Tony Mack2e897fa2015-01-13 17:33:08 -0500766
Scott Bakeref58a842015-04-26 20:30:40 -0700767class ProviderTenantInline(XOSTabularInline):
768 model = CoarseTenant
769 fields = ['provider_service', 'subscriber_service', 'connect_method']
770 extra = 0
771 suit_classes = 'suit-tab suit-tab-servicetenants'
772 fk_name = 'provider_service'
773 verbose_name = 'provided tenant'
774 verbose_name_plural = 'provided tenants'
775
776 def queryset(self, request):
777 qs = super(ProviderTenantInline, self).queryset(request)
778 return qs.filter(kind="coarse")
779
780class SubscriberTenantInline(XOSTabularInline):
781 model = CoarseTenant
782 fields = ['provider_service', 'subscriber_service', 'connect_method']
783 extra = 0
784 suit_classes = 'suit-tab suit-tab-servicetenants'
785 fk_name = 'subscriber_service'
786 verbose_name = 'subscribed tenant'
787 verbose_name_plural = 'subscribed tenants'
788
789 def queryset(self, request):
790 qs = super(SubscriberTenantInline, self).queryset(request)
791 return qs.filter(kind="coarse")
792
Scott Baker67db95f2015-02-18 15:50:11 -0800793class ServiceAttrAsTabInline(XOSTabularInline):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400794 model = ServiceAttribute
795 fields = ['name','value']
796 extra = 0
797 suit_classes = 'suit-tab suit-tab-serviceattrs'
798
Scott Baker67db95f2015-02-18 15:50:11 -0800799class ServiceAdmin(XOSBaseAdmin):
Scott Baker0d306722015-04-15 20:58:20 -0700800 list_display = ("backend_status_icon","name","kind","versionNumber","enabled","published")
Scott Baker63d1a552014-08-21 15:19:07 -0700801 list_display_links = ('backend_status_icon', 'name', )
Scott Bakerfa8a40d2015-05-05 17:50:54 -0700802 fieldList = ["backend_status_text","name","kind","description","versionNumber","enabled","published","view_url","icon_url","public_key"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500803 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack60789ac2015-05-11 20:39:32 -0400804 inlines = [ServiceAttrAsTabInline,SliceInline,ProviderTenantInline,SubscriberTenantInline,ServicePrivilegeInline]
Scott Baker40c00762014-08-21 16:55:59 -0700805 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500806
807 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500808
809 suit_form_tabs =(('general', 'Service Details'),
810 ('slices','Slices'),
811 ('serviceattrs','Additional Attributes'),
Scott Bakeref58a842015-04-26 20:30:40 -0700812 ('servicetenants','Tenancy'),
Tony Mack60789ac2015-05-11 20:39:32 -0400813 ('serviceprivileges','Privileges')
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500814 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400815
Scott Baker67db95f2015-02-18 15:50:11 -0800816class SiteNodeInline(XOSTabularInline):
Tony Mack4f134e62015-01-14 20:58:38 -0500817 model = Node
818 fields = ['name', 'site_deployment']
819 extra = 0
820 suit_classes = 'suit-tab suit-tab-nodes'
821
Tony Mackc2a0d312015-02-25 11:39:34 -0500822 def formfield_for_foreignkey(self, db_field, request, **kwargs):
823 # only display site deployments associated with this site
824 if db_field.name == 'site_deployment':
825 kwargs['queryset'] = SiteDeployment.objects.filter(site__id=int(request.path.split('/')[-2]))
826
827 return super(SiteNodeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
828
Scott Baker67db95f2015-02-18 15:50:11 -0800829class SiteAdmin(XOSBaseAdmin):
Tony Mack450b6e02015-01-25 12:35:29 -0500830 #fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
831 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400832 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500833 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400834 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400835 ]
Tony Mack450b6e02015-01-25 12:35:29 -0500836 #readonly_fields = ['backend_status_text', 'accountLink']
837 readonly_fields = ['backend_status_text']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500838
Tony Mack450b6e02015-01-25 12:35:29 -0500839 #user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
840 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500841
Scott Baker63d1a552014-08-21 15:19:07 -0700842 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
843 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400844 filter_horizontal = ('deployments',)
Tony Mackb81d5e42015-01-30 10:58:29 -0500845 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteNodeInline]
Tony Mackde100182015-01-14 12:11:05 -0500846 admin_inlines = [ControllerSiteInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400847 search_fields = ['name']
848
Tony Mack30dfcd72015-01-10 23:08:10 -0500849 @property
850 def suit_form_tabs(self):
851 tabs = [('general', 'Site Details'),
852 ('users','Users'),
853 ('siteprivileges','Privileges'),
Tony Mack30dfcd72015-01-10 23:08:10 -0500854 ('slices','Slices'),
Tony Mack4f134e62015-01-14 20:58:38 -0500855 ('nodes','Nodes'),
Tony Mack30dfcd72015-01-10 23:08:10 -0500856 ]
857
858 request=getattr(_thread_locals, "request", None)
859 if request and request.user.is_admin:
860 tabs.append( ('admin-only', 'Admin-Only') )
861
862 return tabs
863
Tony Mack04062832013-05-10 08:22:44 -0400864 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500865 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400866
Tony Mack5cd13202013-05-01 21:48:38 -0400867 def get_formsets(self, request, obj=None):
868 for inline in self.get_inline_instances(request, obj):
869 # hide MyInline in the add view
870 if obj is None:
871 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400872 if isinstance(inline, SliverInline):
873 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400874 yield inline.get_formset(request, obj)
875
Scott Baker545db2a2013-12-09 18:44:43 -0800876 def accountLink(self, obj):
877 link_obj = obj.accounts.all()
878 if link_obj:
879 reverse_path = "admin:core_account_change"
880 url = reverse(reverse_path, args =(link_obj[0].id,))
881 return "<a href='%s'>%s</a>" % (url, "view billing details")
882 else:
883 return "no billing data for this site"
884 accountLink.allow_tags = True
885 accountLink.short_description = "Billing"
886
Tony Mack332ee1d2014-02-04 15:33:45 -0500887 def save_model(self, request, obj, form, change):
888 # update openstack connection to use this site/tenant
889 obj.save_by_user(request.user)
890
891 def delete_model(self, request, obj):
892 obj.delete_by_user(request.user)
893
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500894
Scott Baker67db95f2015-02-18 15:50:11 -0800895class SitePrivilegeAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700896 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400897 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500898 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400899 ]
Scott Baker40c00762014-08-21 16:55:59 -0700900 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700901 list_display = ('backend_status_icon', 'user', 'site', 'role')
902 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500903 user_readonly_fields = fieldList
904 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400905
Tony Mackc2835a92013-05-28 09:18:49 -0400906 def formfield_for_foreignkey(self, db_field, request, **kwargs):
907 if db_field.name == 'site':
908 if not request.user.is_admin:
909 # only show sites where user is an admin or pi
910 sites = set()
911 for site_privilege in SitePrivilege.objects.filer(user=request.user):
912 if site_privilege.role.role_type in ['admin', 'pi']:
913 sites.add(site_privilege.site)
914 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
915
916 if db_field.name == 'user':
917 if not request.user.is_admin:
918 # only show users from sites where caller has admin or pi role
919 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
920 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
921 sites = [site_privilege.site for site_privilege in site_privileges]
922 site_privileges = SitePrivilege.objects.filter(site__in=sites)
923 emails = [site_privilege.user.email for site_privilege in site_privileges]
924 users = User.objects.filter(email__in=emails)
925 kwargs['queryset'] = users
926
927 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
928
Tony Mack04062832013-05-10 08:22:44 -0400929 def queryset(self, request):
930 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400931 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400932 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500933 #if not request.user.is_admin:
934 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
935 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
936 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
937 # sites = Site.objects.filter(login_base__in=login_bases)
938 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400939 return qs
940
Siobhan Tullyce652d02013-10-08 21:52:35 -0400941class SliceForm(forms.ModelForm):
942 class Meta:
943 model = Slice
944 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700945 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400946 }
947
Tony Macke75841e2014-09-29 16:10:52 -0400948 def clean(self):
949 cleaned_data = super(SliceForm, self).clean()
950 name = cleaned_data.get('name')
Scott Baker3cb382c2014-10-06 23:09:59 -0700951 site = cleaned_data.get('site')
Tony Mackcc9e2592014-10-22 12:54:19 -0400952 slice_id = self.instance.id
953 if not site and slice_id:
954 site = Slice.objects.get(id=slice_id).site
Scott Baker3cb382c2014-10-06 23:09:59 -0700955 if (not isinstance(site,Site)):
956 # previous code indicates 'site' could be a site_id and not a site?
957 site = Slice.objects.get(id=site.id)
Tony Macke75841e2014-09-29 16:10:52 -0400958 if not name.startswith(site.login_base):
959 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
960 return cleaned_data
961
Scott Baker67db95f2015-02-18 15:50:11 -0800962class ControllerSliceInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -0500963 model = ControllerSlice
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700964 extra = 0
Tony Mack06c8e472014-11-30 15:53:08 -0500965 verbose_name = "Controller Slices"
966 verbose_name_plural = "Controller Slices"
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700967 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack06c8e472014-11-30 15:53:08 -0500968 fields = ['backend_status_icon', 'controller', 'tenant_id']
Tony Mack30dfcd72015-01-10 23:08:10 -0500969 readonly_fields = ('backend_status_icon', 'controller' )
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700970
Scott Baker67db95f2015-02-18 15:50:11 -0800971class SliceAdmin(XOSBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400972 form = SliceForm
Tony Mackfbb26fc2014-09-02 07:03:27 -0400973 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500974 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -0700975 readonly_fields = ('backend_status_text', )
Tony Mack7d459902014-09-03 13:18:57 -0400976 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
977 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully2d95e482013-09-06 10:56:06 -0400978 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Tony Mack3066a952015-01-05 22:48:11 -0500979 admin_inlines = [ControllerSliceInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400980
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500981 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400982
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700983 @property
984 def suit_form_tabs(self):
985 tabs =[('general', 'Slice Details'),
986 ('slicenetworks','Networks'),
987 ('sliceprivileges','Privileges'),
988 ('slivers','Slivers'),
Tony Mack450b6e02015-01-25 12:35:29 -0500989 #('reservations','Reservations'),
Tony Mackd2433382015-01-15 14:44:06 -0500990 ('tags','Tags'),
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700991 ]
992
993 request=getattr(_thread_locals, "request", None)
994 if request and request.user.is_admin:
995 tabs.append( ('admin-only', 'Admin-Only') )
996
997 return tabs
Tony Mack0aa732a2014-10-22 11:54:29 -0400998
999 def add_view(self, request, form_url='', extra_context=None):
1000 # revert to default read-only fields
1001 self.readonly_fields = ('backend_status_text',)
1002 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
1003
1004 def change_view(self, request, object_id, form_url='', extra_context=None):
Tony Mack0aa732a2014-10-22 11:54:29 -04001005 # cannot change the site of an existing slice so make the site field read only
1006 if object_id:
1007 self.readonly_fields = ('backend_status_text','site')
1008 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001009
Scott Baker510fdbb2014-08-05 17:19:24 -07001010 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -07001011 deployment_nodes = []
1012 for node in Node.objects.all():
Scott Baker66b11e22015-01-21 16:24:07 -08001013 deployment_nodes.append( (node.site_deployment.deployment.id, node.id, node.name) )
Scott Baker510fdbb2014-08-05 17:19:24 -07001014
Scott Baker7a61dc42014-09-02 17:08:20 -07001015 deployment_flavors = []
1016 for flavor in Flavor.objects.all():
1017 for deployment in flavor.deployments.all():
1018 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
1019
Tony Mack68a1e422014-12-08 16:43:02 -05001020 deployment_images = []
Scott Bakeraf36c4d2014-09-09 09:58:49 -07001021 for image in Image.objects.all():
Tony Mack68a1e422014-12-08 16:43:02 -05001022 for deployment_image in image.imagedeployments.all():
Scott Bakerf2c0c512014-12-22 17:35:34 -08001023 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
Scott Bakeraf36c4d2014-09-09 09:58:49 -07001024
Tony Mackec23b992014-09-02 21:18:45 -04001025 site_login_bases = []
1026 for site in Site.objects.all():
Scott Bakeraf36c4d2014-09-09 09:58:49 -07001027 site_login_bases.append((site.id, site.login_base))
1028
Scott Baker510fdbb2014-08-05 17:19:24 -07001029 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -07001030 context["deployment_flavors"] = deployment_flavors
Scott Bakeraf36c4d2014-09-09 09:58:49 -07001031 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -04001032 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -07001033 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
1034
Tony Mackc2835a92013-05-28 09:18:49 -04001035 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1036 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001037 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackec23b992014-09-02 21:18:45 -04001038 kwargs['widget'] = forms.Select(attrs={'onChange': "update_slice_prefix(this, $($(this).closest('fieldset')[0]).find('.field-name input')[0].id)"})
Scott Baker40c00762014-08-21 16:55:59 -07001039
Tony Mackc2835a92013-05-28 09:18:49 -04001040 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1041
Tony Mack04062832013-05-10 08:22:44 -04001042 def queryset(self, request):
1043 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001044 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -04001045
Tony Mack79748612013-05-01 14:52:03 -04001046 def get_formsets(self, request, obj=None):
1047 for inline in self.get_inline_instances(request, obj):
1048 # hide MyInline in the add view
1049 if obj is None:
1050 continue
Tony Mack2bd5b412013-06-11 21:05:06 -04001051 if isinstance(inline, SliverInline):
1052 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -04001053 yield inline.get_formset(request, obj)
1054
Scott Baker67db95f2015-02-18 15:50:11 -08001055class SlicePrivilegeAdmin(XOSBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -04001056 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -07001057 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -04001058 ]
Scott Baker40c00762014-08-21 16:55:59 -07001059 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -07001060 list_display = ('backend_status_icon', 'user', 'slice', 'role')
1061 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -04001062
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001063 user_readonly_fields = ['user', 'slice', 'role']
1064 user_readonly_inlines = []
1065
Tony Mackc2835a92013-05-28 09:18:49 -04001066 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1067 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001068 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001069
1070 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -05001071 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001072
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001073 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001074
Tony Mack04062832013-05-10 08:22:44 -04001075 def queryset(self, request):
1076 # admins can see all memberships. Users can only see memberships of
1077 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -05001078 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001079
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001080 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -04001081 # update openstack connection to use this site/tenant
1082 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001083 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001084 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001085 obj.save()
1086
1087 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -04001088 # update openstack connection to use this site/tenant
1089 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001090 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001091 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001092 obj.delete()
1093
Siobhan Tully567e3e62013-06-21 18:03:16 -04001094
Scott Baker67db95f2015-02-18 15:50:11 -08001095class ImageAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001096
Scott Baker36f50872014-08-21 13:01:25 -07001097 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -07001098 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001099 'classes': ['suit-tab suit-tab-general']})
1100 ]
Scott Baker40c00762014-08-21 16:55:59 -07001101 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001102
Tony Mack06c8e472014-11-30 15:53:08 -05001103 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001104
Tony Mack06c8e472014-11-30 15:53:08 -05001105 inlines = [SliverInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -07001106
Tony Mack32e1ce32014-05-07 13:29:41 -04001107 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -07001108
Scott Baker63d1a552014-08-21 15:19:07 -07001109 list_display = ['backend_status_icon', 'name']
1110 list_display_links = ('backend_status_icon', 'name', )
1111
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001112class NodeForm(forms.ModelForm):
1113 class Meta:
1114 widgets = {
1115 'site': LinkedSelect,
1116 'deployment': LinkedSelect
1117 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001118
Scott Baker67db95f2015-02-18 15:50:11 -08001119class NodeAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001120 form = NodeForm
Tony Mack68a1e422014-12-08 16:43:02 -05001121 list_display = ('backend_status_icon', 'name', 'site_deployment')
Scott Baker63d1a552014-08-21 15:19:07 -07001122 list_display_links = ('backend_status_icon', 'name', )
Tony Mack68a1e422014-12-08 16:43:02 -05001123 list_filter = ('site_deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001124
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001125 inlines = [TagInline,SliverInline]
Tony Mack68a1e422014-12-08 16:43:02 -05001126 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site_deployment'], 'classes':['suit-tab suit-tab-details']})]
Scott Baker40c00762014-08-21 16:55:59 -07001127 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001128
Tony Mack68a1e422014-12-08 16:43:02 -05001129 user_readonly_fields = ['name','site_deployment']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001130 user_readonly_inlines = [TagInline,SliverInline]
1131
Tony Mack5fecf712015-01-12 21:40:09 -05001132 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001133
Siobhan Tully567e3e62013-06-21 18:03:16 -04001134
Tony Mackd90cdbf2013-04-16 22:48:40 -04001135class SliverForm(forms.ModelForm):
1136 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -04001137 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -04001138 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001139 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001140 widgets = {
1141 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001142 'instance_name': PlainTextWidget(),
Scott Baker9d856052015-01-19 11:32:20 -08001143 'instance_id': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001144 'slice': LinkedSelect,
Tony Mackb2dba4b2014-12-26 13:38:02 -05001145 'deployment': LinkedSelect,
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001146 'node': LinkedSelect,
1147 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001148 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001149
Scott Baker67db95f2015-02-18 15:50:11 -08001150class TagAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001151 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1152 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001153 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1154 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001155
Scott Baker67db95f2015-02-18 15:50:11 -08001156class SliverAdmin(XOSBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001157 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -04001158 fieldsets = [
Tony Mack707f7d72015-01-30 12:52:46 -05001159 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'node', 'all_ips_string', 'instance_id', 'instance_name', 'flavor', 'image', 'ssh_command'], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -04001160 ]
Tony Mackdb8580b2015-01-30 17:20:46 -05001161 readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Tony Mack707f7d72015-01-30 12:52:46 -05001162 list_display = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deployment']
Scott Baker2f295402015-02-13 14:38:21 -08001163 list_display_links = ('backend_status_icon', 'all_ips_string', 'instance_id', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001164
Scott Bakerc72997a2015-01-19 08:24:08 -08001165 suit_form_tabs =(('general', 'Sliver Details'),)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001166
Siobhan Tullyde5450d2013-06-21 11:35:33 -04001167 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -04001168
Tony Mackb2dba4b2014-12-26 13:38:02 -05001169 user_readonly_fields = ['slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001170
Scott Baker7ccc6ad2015-01-25 22:16:13 -08001171 def ssh_command(self, obj):
1172 ssh_command = obj.get_ssh_command()
1173 if ssh_command:
1174 return ssh_command
1175 else:
1176 return "(not available)"
1177
Tony Mackc2835a92013-05-28 09:18:49 -04001178 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1179 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001180 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001181
1182 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1183
Tony Mack04062832013-05-10 08:22:44 -04001184 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -07001185 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -04001186 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001187 return Sliver.select_by_user(request.user)
1188
Tony Mack04062832013-05-10 08:22:44 -04001189
Tony Mack1d6b85f2013-05-07 18:49:14 -04001190 def get_formsets(self, request, obj=None):
1191 # make some fields read only if we are updating an existing record
1192 if obj == None:
Tony Mackb428feb2015-01-30 17:42:10 -05001193 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001194 else:
Tony Mackb428feb2015-01-30 17:42:10 -05001195 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string', 'slice', 'flavor', 'image', 'node')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001196
1197 for inline in self.get_inline_instances(request, obj):
1198 # hide MyInline in the add view
1199 if obj is None:
1200 continue
Scott Baker526b71e2014-05-13 13:18:01 -07001201 if isinstance(inline, SliverInline):
1202 inline.model.caller = request.user
1203 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -04001204
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001205 #def save_model(self, request, obj, form, change):
1206 # # update openstack connection to use this site/tenant
1207 # auth = request.session.get('auth', {})
1208 # auth['tenant'] = obj.slice.name
1209 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1210 # obj.creator = request.user
1211 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001212
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001213 #def delete_model(self, request, obj):
1214 # # update openstack connection to use this site/tenant
1215 # auth = request.session.get('auth', {})
1216 # auth['tenant'] = obj.slice.name
1217 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1218 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001219
Siobhan Tully53437282013-04-26 19:30:27 -04001220class UserCreationForm(forms.ModelForm):
1221 """A form for creating new users. Includes all the required
1222 fields, plus a repeated password."""
1223 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1224 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1225
1226 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001227 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001228 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001229
1230 def clean_password2(self):
1231 # Check that the two password entries match
1232 password1 = self.cleaned_data.get("password1")
1233 password2 = self.cleaned_data.get("password2")
1234 if password1 and password2 and password1 != password2:
1235 raise forms.ValidationError("Passwords don't match")
1236 return password2
1237
1238 def save(self, commit=True):
1239 # Save the provided password in hashed format
1240 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001241 user.password = self.cleaned_data["password1"]
1242 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001243 if commit:
1244 user.save()
1245 return user
1246
Siobhan Tully567e3e62013-06-21 18:03:16 -04001247
Siobhan Tully53437282013-04-26 19:30:27 -04001248class UserChangeForm(forms.ModelForm):
1249 """A form for updating users. Includes all the fields on
1250 the user, but replaces the password field with admin's
1251 password hash display field.
1252 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001253 password = ReadOnlyPasswordHashField(label='Password',
1254 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001255
Scott Bakerb6043c22015-05-19 16:39:48 -07001256 PROFILE_CHOICES = ((None, '------'), ('regular', 'Regular user'), ('cp', 'Content Provider'))
1257 profile = forms.ChoiceField(choices=PROFILE_CHOICES, required=False, label="Quick Profile")
1258
Siobhan Tully53437282013-04-26 19:30:27 -04001259 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001260 model = User
Scott Baker69e045d2014-11-17 23:44:03 -08001261 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001262
1263 def clean_password(self):
1264 # Regardless of what the user provides, return the initial value.
1265 # This is done here, rather than on the field, because the
1266 # field does not have access to the initial value
1267 return self.initial["password"]
1268
Scott Bakerb6043c22015-05-19 16:39:48 -07001269 def save(self, *args, **kwargs):
1270 if self.cleaned_data['profile']:
1271 self.instance.apply_profile(self.cleaned_data['profile'])
1272
1273 return super(UserChangeForm, self).save(*args, **kwargs)
1274
Scott Baker67db95f2015-02-18 15:50:11 -08001275class UserDashboardViewInline(XOSTabularInline):
Scott Baker2c3cb642014-05-19 17:55:56 -07001276 model = UserDashboardView
1277 extra = 0
1278 suit_classes = 'suit-tab suit-tab-dashboards'
1279 fields = ['user', 'dashboardView', 'order']
1280
Scott Baker67db95f2015-02-18 15:50:11 -08001281class ControllerUserInline(XOSTabularInline):
Tony Mack30dfcd72015-01-10 23:08:10 -05001282 model = ControllerUser
1283 extra = 0
1284 suit_classes = 'suit-tab suit-tab-admin-only'
1285 fields = ['controller', 'user', 'kuser_id']
1286 readonly_fields=['controller']
1287
1288
Scott Baker3a8aed62015-02-27 12:21:22 -08001289class UserAdmin(XOSAdminMixin, UserAdmin):
1290 # Note: Make sure XOSAdminMixin is listed before
Scott Bakerf4aeedc2014-10-03 13:10:47 -07001291 # admin.ModelAdmin in the class declaration.
1292
Siobhan Tully53437282013-04-26 19:30:27 -04001293 class Meta:
1294 app_label = "core"
1295
1296 # The forms to add and change user instances
1297 form = UserChangeForm
1298 add_form = UserCreationForm
1299
1300 # The fields to be used in displaying the User model.
1301 # These override the definitions on the base UserAdmin
1302 # that reference specific fields on auth.User.
Scott Bakera111f442015-01-24 13:33:26 -08001303 list_display = ('backend_status_icon', 'email', 'firstname', 'lastname', 'site', 'last_login')
1304 list_display_links = ("email",)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001305 list_filter = ('site',)
Scott Baker90472612015-01-29 10:55:53 -08001306 inlines = [SlicePrivilegeInline,SitePrivilegeInline]
Tony Mack30dfcd72015-01-10 23:08:10 -05001307 admin_inlines = [ControllerUserInline]
Scott Bakerb6043c22015-05-19 16:39:48 -07001308 fieldListLoginDetails = ['backend_status_text', 'email', 'site','password','is_active','is_readonly','is_admin','is_appuser', 'public_key', 'profile']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001309 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1310
Siobhan Tully53437282013-04-26 19:30:27 -04001311 fieldsets = (
Scott Baker034232d2015-05-15 09:19:36 -07001312 ('Login Details', {'fields': ['backend_status_text', 'email', 'site','password', 'is_active', 'is_readonly', 'is_admin', 'is_appuser', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001313 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001314 #('Important dates', {'fields': ('last_login',)}),
1315 )
1316 add_fieldsets = (
1317 (None, {
1318 'classes': ('wide',),
Scott Baker034232d2015-05-15 09:19:36 -07001319 'fields': ('site', 'email', 'firstname', 'lastname', 'is_admin', 'is_readonly', 'is_appuser', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001320 ),
1321 )
Scott Baker40c00762014-08-21 16:55:59 -07001322 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001323 search_fields = ('email',)
1324 ordering = ('email',)
1325 filter_horizontal = ()
1326
Scott Baker3ca51f62014-05-23 12:05:11 -07001327 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001328
Scott Baker6a995352014-10-06 17:51:20 -07001329 @property
1330 def suit_form_tabs(self):
1331 if getattr(_thread_locals, "obj", None) is None:
1332 return []
1333 else:
Tony Mack30dfcd72015-01-10 23:08:10 -05001334 tabs = [('general','Login Details'),
Scott Baker6a995352014-10-06 17:51:20 -07001335 ('contact','Contact Information'),
1336 ('sliceprivileges','Slice Privileges'),
Scott Baker90472612015-01-29 10:55:53 -08001337 ('siteprivileges','Site Privileges')]
Tony Mack30dfcd72015-01-10 23:08:10 -05001338
1339 request=getattr(_thread_locals, "request", None)
1340 if request and request.user.is_admin:
1341 tabs.append( ('admin-only', 'Admin-Only') )
1342
1343 return tabs
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001344
Tony Mackc2835a92013-05-28 09:18:49 -04001345 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1346 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001347 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001348
1349 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1350
Tony Mack5b061472014-02-04 07:57:10 -05001351 def queryset(self, request):
1352 return User.select_by_user(request.user)
1353
Tony Mackc8f443d2015-01-25 21:58:30 -05001354 def get_form(self, request, obj=None, **kwargs):
Tony Mack03b92292015-01-28 12:37:12 -05001355 # copy login details list
1356 login_details_fields = list(self.fieldListLoginDetails)
Tony Mack933b2912015-01-28 12:49:58 -05001357 if not request.user.is_admin:
Scott Baker90472612015-01-29 10:55:53 -08001358 # only admins can see 'is_admin' and 'is_readonly' fields
Tony Mack03b92292015-01-28 12:37:12 -05001359 if 'is_admin' in login_details_fields:
1360 login_details_fields.remove('is_admin')
1361 if 'is_readonly' in login_details_fields:
Scott Bakerb6043c22015-05-19 16:39:48 -07001362 login_details_fields.remove('is_readonly')
1363 if 'is_appuser' in login_details_fields:
1364 login_details_fields.remove('is_admin')
1365 if 'profile' in login_details_fields:
1366 login_details_fields.remove('profile')
Tony Mack933b2912015-01-28 12:49:58 -05001367 #if len(request.user.siteprivileges.filter(role__role = 'pi')) > 0:
Scott Bakerb6043c22015-05-19 16:39:48 -07001368 # only admins and pis can change a user's site
Tony Mack933b2912015-01-28 12:49:58 -05001369 # self.readonly_fields = ('backend_status_text', 'site')
Tony Mack03b92292015-01-28 12:37:12 -05001370 self.fieldsets = (
1371 ('Login Details', {'fields': login_details_fields, 'classes':['suit-tab suit-tab-general']}),
1372 ('Contact Information', {'fields': self.fieldListContactInfo, 'classes':['suit-tab suit-tab-contact']}),
1373 )
Tony Mackc8f443d2015-01-25 21:58:30 -05001374 return super(UserAdmin, self).get_form(request, obj, **kwargs)
1375
Scott Baker67db95f2015-02-18 15:50:11 -08001376class ControllerDashboardViewInline(XOSTabularInline):
Scott Bakerf2c0c512014-12-22 17:35:34 -08001377 model = ControllerDashboardView
Scott Baker786a9c12014-12-19 16:41:12 -08001378 extra = 0
1379 fields = ["controller", "url"]
1380 suit_classes = 'suit-tab suit-tab-controllers'
1381
Scott Baker67db95f2015-02-18 15:50:11 -08001382class DashboardViewAdmin(XOSBaseAdmin):
Scott Baker2c3cb642014-05-19 17:55:56 -07001383 fieldsets = [('Dashboard View Details',
Scott Baker59248182015-02-17 13:34:32 -08001384 {'fields': ['backend_status_text', 'name', 'url', 'enabled', 'deployments'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001385 'classes': ['suit-tab suit-tab-general']})
1386 ]
Scott Baker2c44e6e2015-01-18 16:46:26 -08001387 list_display = ["name", "enabled", "url"]
Scott Baker40c00762014-08-21 16:55:59 -07001388 readonly_fields = ('backend_status_text', )
Scott Bakerf2c0c512014-12-22 17:35:34 -08001389 inlines = [ControllerDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001390
Scott Baker786a9c12014-12-19 16:41:12 -08001391 suit_form_tabs =(('general','Dashboard View Details'),
1392 ('controllers', 'Per-controller Dashboard Details'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001393
Scott Baker67db95f2015-02-18 15:50:11 -08001394class ServiceResourceInline(XOSTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001395 model = ServiceResource
1396 extra = 0
1397
Scott Baker67db95f2015-02-18 15:50:11 -08001398class ServiceClassAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001399 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1400 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001401 inlines = [ServiceResourceInline]
1402
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001403 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1404 user_readonly_inlines = []
1405
Scott Baker67db95f2015-02-18 15:50:11 -08001406class ReservedResourceInline(XOSTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001407 model = ReservedResource
1408 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001409 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001410
1411 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1412 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1413
1414 if db_field.name == 'resource':
1415 # restrict resources to those that the slice's service class allows
1416 if request._slice is not None:
1417 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1418 if len(field.queryset) > 0:
1419 field.initial = field.queryset.all()[0]
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -05001420 else:
1421 field.queryset = field.queryset.none()
1422 elif db_field.name == 'sliver':
1423 # restrict slivers to those that belong to the slice
1424 if request._slice is not None:
Scott Baker133c9212013-05-17 09:09:11 -07001425 field.queryset = field.queryset.filter(slice = request._slice)
1426 else:
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -05001427 field.queryset = field.queryset.none()
1428
Scott Baker133c9212013-05-17 09:09:11 -07001429 return field
1430
Tony Mack5b061472014-02-04 07:57:10 -05001431 def queryset(self, request):
1432 return ReservedResource.select_by_user(request.user)
1433
Scott Baker133c9212013-05-17 09:09:11 -07001434class ReservationChangeForm(forms.ModelForm):
1435 class Meta:
1436 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001437 widgets = {
1438 'slice' : LinkedSelect
1439 }
Scott Baker133c9212013-05-17 09:09:11 -07001440
1441class ReservationAddForm(forms.ModelForm):
1442 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1443 refresh = forms.CharField(widget=forms.HiddenInput())
1444
1445 class Media:
Scott Baker06868952015-02-18 15:15:58 -08001446 css = {'all': ('xos.css',)} # .field-refresh { display: none; }
Scott Baker133c9212013-05-17 09:09:11 -07001447
1448 def clean_slice(self):
1449 slice = self.cleaned_data.get("slice")
1450 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1451 if len(x) == 0:
1452 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1453 return slice
1454
1455 class Meta:
1456 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001457 widgets = {
1458 'slice' : LinkedSelect
1459 }
1460
Scott Baker133c9212013-05-17 09:09:11 -07001461
1462class ReservationAddRefreshForm(ReservationAddForm):
1463 """ This form is displayed when the Reservation Form receives an update
1464 from the Slice dropdown onChange handler. It doesn't validate the
1465 data and doesn't save the data. This will cause the form to be
1466 redrawn.
1467 """
1468
Scott Baker8737e5f2013-05-17 09:35:32 -07001469 """ don't validate anything other than slice """
1470 dont_validate_fields = ("startTime", "duration")
1471
Scott Baker133c9212013-05-17 09:09:11 -07001472 def full_clean(self):
1473 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001474
1475 for fieldname in self.dont_validate_fields:
1476 if fieldname in self._errors:
1477 del self._errors[fieldname]
1478
Scott Baker133c9212013-05-17 09:09:11 -07001479 return result
1480
1481 """ don't save anything """
1482 def is_valid(self):
1483 return False
1484
Scott Baker67db95f2015-02-18 15:50:11 -08001485class ReservationAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001486 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001487 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001488 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001489 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001490 form = ReservationAddForm
1491
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001492 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1493
1494 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001495 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001496
Scott Baker133c9212013-05-17 09:09:11 -07001497 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001498 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001499 request._refresh = False
1500 request._slice = None
1501 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001502 # "refresh" will be set to "1" if the form was submitted due to
1503 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001504 if request.POST.get("refresh","1") == "1":
1505 request._refresh = True
1506 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001507
1508 # Keep track of the slice that was selected, so the
1509 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001510 request._slice = request.POST.get("slice",None)
1511 if (request._slice is not None):
1512 request._slice = Slice.objects.get(id=request._slice)
1513
1514 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1515 return result
1516
Scott Bakeracd45142013-05-19 16:19:16 -07001517 def changelist_view(self, request, extra_context = None):
1518 timezone.activate(request.user.timezone)
1519 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1520
Scott Baker133c9212013-05-17 09:09:11 -07001521 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001522 request._obj_ = obj
1523 if obj is not None:
1524 # For changes, set request._slice to the slice already set in the
1525 # object.
1526 request._slice = obj.slice
1527 self.form = ReservationChangeForm
1528 else:
1529 if getattr(request, "_refresh", False):
1530 self.form = ReservationAddRefreshForm
1531 else:
1532 self.form = ReservationAddForm
1533 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1534
Scott Baker133c9212013-05-17 09:09:11 -07001535 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001536 if (obj is not None):
1537 # Prevent slice from being changed after the reservation has been
1538 # created.
1539 return ['slice']
1540 else:
Scott Baker133c9212013-05-17 09:09:11 -07001541 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001542
Tony Mack5b061472014-02-04 07:57:10 -05001543 def queryset(self, request):
1544 return Reservation.select_by_user(request.user)
1545
Scott Baker67db95f2015-02-18 15:50:11 -08001546class NetworkParameterTypeAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001547 list_display = ("backend_status_icon", "name", )
1548 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001549 user_readonly_fields = ['name']
1550 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001551
Scott Baker67db95f2015-02-18 15:50:11 -08001552class RouterAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001553 list_display = ("backend_status_icon", "name", )
1554 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001555 user_readonly_fields = ['name']
1556 user_readonly_inlines = []
1557
Scott Baker67db95f2015-02-18 15:50:11 -08001558class RouterInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001559 model = Router.networks.through
1560 extra = 0
1561 verbose_name_plural = "Routers"
1562 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001563 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001564
Scott Bakerb27b62c2014-08-15 16:29:16 -07001565class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001566 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001567 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001568 verbose_name_plural = "Parameters"
1569 verbose_name = "Parameter"
1570 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001571 fields = ['backend_status_icon', 'parameter', 'value']
1572 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001573
Scott Baker67db95f2015-02-18 15:50:11 -08001574class NetworkSliversInline(XOSTabularInline):
Scott Baker40c00762014-08-21 16:55:59 -07001575 fields = ['backend_status_icon', 'network','sliver','ip']
1576 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker74d8e622013-07-29 16:04:22 -07001577 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001578 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001579 extra = 0
1580 verbose_name_plural = "Slivers"
1581 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001582 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001583
Scott Baker67db95f2015-02-18 15:50:11 -08001584class NetworkSlicesInline(XOSTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001585 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001586 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001587 extra = 0
1588 verbose_name_plural = "Slices"
1589 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001590 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001591 fields = ['backend_status_icon', 'network','slice']
1592 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001593
Scott Baker67db95f2015-02-18 15:50:11 -08001594class ControllerNetworkInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -05001595 model = ControllerNetwork
Scott Baker8806cdf2014-10-17 16:27:23 -07001596 extra = 0
Tony Mack06c8e472014-11-30 15:53:08 -05001597 verbose_name_plural = "Controller Networks"
1598 verbose_name = "Controller Network"
Scott Baker8806cdf2014-10-17 16:27:23 -07001599 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack06c8e472014-11-30 15:53:08 -05001600 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Baker8806cdf2014-10-17 16:27:23 -07001601 readonly_fields = ('backend_status_icon', )
1602
Scott Baker69e045d2014-11-17 23:44:03 -08001603class NetworkForm(forms.ModelForm):
1604 class Meta:
1605 model = Network
1606 widgets = {
1607 'topologyParameters': UploadTextareaWidget,
1608 'controllerParameters': UploadTextareaWidget,
1609 }
1610
Scott Baker67db95f2015-02-18 15:50:11 -08001611class NetworkAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001612 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1613 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001614 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001615
Scott Bakerd7d2a392013-08-06 08:57:30 -07001616 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Tony Mack3066a952015-01-05 22:48:11 -05001617 admin_inlines = [ControllerNetworkInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001618
Scott Baker69e045d2014-11-17 23:44:03 -08001619 form=NetworkForm
1620
Siobhan Tully2d95e482013-09-06 10:56:06 -04001621 fieldsets = [
Scott Baker549aa252015-01-03 12:29:29 -08001622 (None, {'fields': ['backend_status_text', 'name','template','ports','labels','owner','guaranteed_bandwidth', 'permit_all_slices','permitted_slices','network_id','router_id','subnet_id','subnet'],
Scott Baker3e28dd72014-11-17 16:04:45 -08001623 'classes':['suit-tab suit-tab-general']}),
Scott Baker549aa252015-01-03 12:29:29 -08001624 (None, {'fields': ['topology_parameters', 'controller_url', 'controller_parameters'],
Scott Baker3e28dd72014-11-17 16:04:45 -08001625 'classes':['suit-tab suit-tab-sdn']}),
1626 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001627
Scott Baker40c00762014-08-21 16:55:59 -07001628 readonly_fields = ('backend_status_text', )
Scott Baker549aa252015-01-03 12:29:29 -08001629 user_readonly_fields = ['name','template','ports','labels','owner','guaranteed_bandwidth', 'permit_all_slices','permitted_slices','network_id','router_id','subnet_id','subnet']
Siobhan Tully2d95e482013-09-06 10:56:06 -04001630
Scott Baker8806cdf2014-10-17 16:27:23 -07001631 @property
1632 def suit_form_tabs(self):
1633 tabs=[('general','Network Details'),
Scott Baker3e28dd72014-11-17 16:04:45 -08001634 ('sdn', 'SDN Configuration'),
Scott Baker8806cdf2014-10-17 16:27:23 -07001635 ('netparams', 'Parameters'),
1636 ('networkslivers','Slivers'),
1637 ('networkslices','Slices'),
1638 ('routers','Routers'),
1639 ]
1640
1641 request=getattr(_thread_locals, "request", None)
1642 if request and request.user.is_admin:
1643 tabs.append( ('admin-only', 'Admin-Only') )
1644
1645 return tabs
1646
1647
Scott Baker67db95f2015-02-18 15:50:11 -08001648class NetworkTemplateAdmin(XOSBaseAdmin):
Scott Baker369f9b92015-01-03 12:03:38 -08001649 list_display = ("backend_status_icon", "name", "guaranteed_bandwidth", "visibility")
Scott Baker63d1a552014-08-21 15:19:07 -07001650 list_display_links = ('backend_status_icon', 'name', )
Scott Baker369f9b92015-01-03 12:03:38 -08001651 user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001652 user_readonly_inlines = []
Scott Baker3e28dd72014-11-17 16:04:45 -08001653 fieldsets = [
Scott Baker369f9b92015-01-03 12:03:38 -08001654 (None, {'fields': ['name', 'description', 'guaranteed_bandwidth', 'visibility', 'translation', 'shared_network_name', 'shared_network_id', 'topology_kind', 'controller_kind'],
Scott Baker3e28dd72014-11-17 16:04:45 -08001655 'classes':['suit-tab suit-tab-general']}),]
1656 suit_form_tabs = (('general','Network Template Details'), )
Scott Baker74d8e622013-07-29 16:04:22 -07001657
Scott Baker67db95f2015-02-18 15:50:11 -08001658class FlavorAdmin(XOSBaseAdmin):
Scott Baker37b47902014-09-02 14:37:41 -07001659 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1660 list_display_links = ("backend_status_icon", "name")
1661 user_readonly_fields = ("name", "flavor")
1662 fields = ("name", "description", "flavor", "order", "default")
1663
Tony Mack31c2b8f2013-04-26 20:01:42 -04001664# register a signal that caches the user's credentials when they log in
1665def cache_credentials(sender, user, request, **kwds):
1666 auth = {'username': request.POST['username'],
1667 'password': request.POST['password']}
1668 request.session['auth'] = auth
1669user_logged_in.connect(cache_credentials)
1670
Scott Baker15cddfa2013-12-09 13:45:19 -08001671def dollar_field(fieldName, short_description):
1672 def newFunc(self, obj):
1673 try:
1674 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1675 except:
1676 x=getattr(obj, fieldName, 0.0)
1677 return x
1678 newFunc.short_description = short_description
1679 return newFunc
1680
1681def right_dollar_field(fieldName, short_description):
1682 def newFunc(self, obj):
1683 try:
1684 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1685 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1686 except:
1687 x=getattr(obj, fieldName, 0.0)
1688 return x
1689 newFunc.short_description = short_description
1690 newFunc.allow_tags = True
1691 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001692
Scott Baker67db95f2015-02-18 15:50:11 -08001693class InvoiceChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001694 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001695 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001696 verbose_name_plural = "Charges"
1697 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001698 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001699 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1700 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1701 can_delete = False
1702 max_num = 0
1703
1704 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001705
1706class InvoiceAdmin(admin.ModelAdmin):
1707 list_display = ("date", "account")
1708
1709 inlines = [InvoiceChargeInline]
1710
Scott Baker9cb88a22013-12-09 18:56:00 -08001711 fields = ["date", "account", "dollar_amount"]
1712 readonly_fields = ["date", "account", "dollar_amount"]
1713
1714 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001715
Scott Baker67db95f2015-02-18 15:50:11 -08001716class InvoiceInline(XOSTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001717 model = Invoice
1718 extra = 0
1719 verbose_name_plural = "Invoices"
1720 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001721 fields = ["date", "dollar_amount"]
1722 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001723 suit_classes = 'suit-tab suit-tab-accountinvoice'
1724 can_delete=False
1725 max_num=0
1726
1727 dollar_amount = right_dollar_field("amount", "Amount")
1728
Scott Baker67db95f2015-02-18 15:50:11 -08001729class PendingChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001730 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001731 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001732 verbose_name_plural = "Charges"
1733 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001734 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001735 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1736 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001737 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001738 can_delete=False
1739 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001740
1741 def queryset(self, request):
1742 qs = super(PendingChargeInline, self).queryset(request)
1743 qs = qs.filter(state="pending")
1744 return qs
1745
Scott Baker15cddfa2013-12-09 13:45:19 -08001746 dollar_amount = right_dollar_field("amount", "Amount")
1747
Scott Baker67db95f2015-02-18 15:50:11 -08001748class PaymentInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001749 model=Payment
1750 extra = 1
1751 verbose_name_plural = "Payments"
1752 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001753 fields = ["date", "dollar_amount"]
1754 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001755 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001756 can_delete=False
1757 max_num=0
1758
1759 dollar_amount = right_dollar_field("amount", "Amount")
1760
Scott Baker43105042013-12-06 23:23:36 -08001761class AccountAdmin(admin.ModelAdmin):
1762 list_display = ("site", "balance_due")
1763
1764 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1765
1766 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001767 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
Scott Baker43105042013-12-06 23:23:36 -08001768
Scott Baker15cddfa2013-12-09 13:45:19 -08001769 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001770
1771 suit_form_tabs =(
1772 ('general','Account Details'),
1773 ('accountinvoice', 'Invoices'),
1774 ('accountpayments', 'Payments'),
1775 ('accountpendingcharges','Pending Charges'),
1776 )
1777
Scott Baker15cddfa2013-12-09 13:45:19 -08001778 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1779 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1780 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1781
Siobhan Tully53437282013-04-26 19:30:27 -04001782# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001783admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001784# ... and, since we're not using Django's builtin permissions,
1785# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001786#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001787
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001788#Do not show django evolution in the admin interface
1789from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001790#admin.site.unregister(Version)
1791#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001792
1793
1794# When debugging it is often easier to see all the classes, but for regular use
1795# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001796showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001797
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001798admin.site.register(Deployment, DeploymentAdmin)
Tony Mack06c8e472014-11-30 15:53:08 -05001799admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001800admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001801admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001802admin.site.register(Service, ServiceAdmin)
Tony Mack450b6e02015-01-25 12:35:29 -05001803#admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001804admin.site.register(Network, NetworkAdmin)
1805admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001806admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Tony Mack450b6e02015-01-25 12:35:29 -05001807#admin.site.register(Account, AccountAdmin)
1808#admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001809
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001810if True:
1811 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1812 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001813 admin.site.register(Tag, TagAdmin)
Tony Mack06c8e472014-11-30 15:53:08 -05001814 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001815 admin.site.register(SiteRole)
1816 admin.site.register(SliceRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001817 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001818 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1819 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001820 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001821 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001822 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001823 admin.site.register(Flavor, FlavorAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001824