blob: a5d4c2b200886b492be8874f391387854904ae06 [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 Bakerf41fe2c2015-07-09 19:06:08 -070022from django.core.exceptions import PermissionDenied, ValidationError
Scott Baker92d22172014-10-21 21:03:08 -070023from cgi import escape as html_escape
Scott Bakerf41fe2c2015-07-09 19:06:08 -070024from django.contrib import messages
Tony Mack7130ac32013-03-22 21:58:00 -040025
Scott Baker36f50872014-08-21 13:01:25 -070026import django_evolution
Scott Baker6a995352014-10-06 17:51:20 -070027import threading
28
29# thread locals necessary to work around a django-suit issue
30_thread_locals = threading.local()
Scott Baker36f50872014-08-21 13:01:25 -070031
Scott Bakere5f9d7d2015-02-10 18:24:20 -080032ICON_URLS = {"success": "/static/admin/img/icon_success.gif",
33 "clock": "/static/admin/img/icon_clock.gif",
34 "error": "/static/admin/img/icon_error.gif"}
35
36def backend_icon(obj):
37 (icon, tooltip) = obj.get_backend_icon()
38 icon_url = ICON_URLS.get(icon, "unknown")
39
40 if tooltip:
41 return '<span style="min-width:16px;" title="%s"><img src="%s"></span>' % (tooltip, icon_url)
Scott Baker40c00762014-08-21 16:55:59 -070042 else:
Scott Bakere5f9d7d2015-02-10 18:24:20 -080043 return '<span style="min-width:16px;"><img src="%s"></span>' % icon_url
Scott Baker40c00762014-08-21 16:55:59 -070044
45def backend_text(obj):
Scott Bakere5f9d7d2015-02-10 18:24:20 -080046 (icon, tooltip) = obj.get_backend_icon()
47 icon_url = ICON_URLS.get(icon, "unknown")
48
49 return '<img src="%s"> %s' % (icon_url, tooltip)
Scott Baker63d1a552014-08-21 15:19:07 -070050
Scott Baker69e045d2014-11-17 23:44:03 -080051class UploadTextareaWidget(AdminTextareaWidget):
52 def render(self, name, value, attrs=None):
53 if value is None:
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -050054 value = ''
55 final_attrs = self.build_attrs(attrs, name=name)
56 return format_html('<input type="file" style="width: 0; height: 0" id="btn_upload_%s" onChange="uploadTextarea(event,\'%s\');">' \
57 '<button onClick="$(\'#btn_upload_%s\').click(); return false;">Upload</button>' \
58 '<br><textarea{0}>\r\n{1}</textarea>' % (attrs["id"], attrs["id"], attrs["id"]),
59 flatatt(final_attrs),
Scott Baker69e045d2014-11-17 23:44:03 -080060 force_text(value))
61
Scott Baker50ac4192015-05-11 16:36:58 -070062class SliderWidget(forms.HiddenInput):
63 def render(self, name, value, attrs=None):
64 if value is None:
65 value = '0'
66 final_attrs = self.build_attrs(attrs, name=name)
67 attrs = attrs or attrs[:]
68 attrs["name"] = name
69 attrs["value"] = value
70 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>
71 <script>
72 $(function() {
73 $("#%(id)s_slider").slider({
74 value: %(value)s,
75 slide: function(event, ui) { $("#%(id)s").val( ui.value ); $("#%(id)s_label").html(ui.value); },
76 });
77 });
78 </script>
79 <input type="hidden" id="%(id)s" name="%(name)s" value="%(value)s"></input>
80 """ % attrs
81 html = html.replace("{","{{").replace("}","}}")
82 return format_html(html,
83 flatatt(final_attrs),
84 force_text(value))
85
86
Scott Baker36f50872014-08-21 13:01:25 -070087class PlainTextWidget(forms.HiddenInput):
88 input_type = 'hidden'
89
90 def render(self, name, value, attrs=None):
91 if value is None:
92 value = ''
93 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
94
Scott Baker3a8aed62015-02-27 12:21:22 -080095class XOSAdminMixin(object):
Scott Baker1a6a3902014-10-03 00:32:37 -070096 # call save_by_user and delete_by_user instead of save and delete
Siobhan Tullycf04fb62014-01-11 11:25:57 -050097
98 def has_add_permission(self, request, obj=None):
99 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -0700100
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500101 def has_delete_permission(self, request, obj=None):
102 return (not self.__user_is_readonly(request))
103
104 def save_model(self, request, obj, form, change):
105 if self.__user_is_readonly(request):
Scott Baker1a6a3902014-10-03 00:32:37 -0700106 # this 'if' might be redundant if save_by_user is implemented right
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500107 raise PermissionDenied
Scott Baker1a6a3902014-10-03 00:32:37 -0700108
109 obj.caller = request.user
110 # update openstack connection to use this site/tenant
111 obj.save_by_user(request.user)
112
113 def delete_model(self, request, obj):
114 obj.delete_by_user(request.user)
115
116 def save_formset(self, request, form, formset, change):
117 instances = formset.save(commit=False)
118 for instance in instances:
Scott Bakerf8cbac72015-07-08 18:23:17 -0700119 instance.caller = request.user
Scott Baker1a6a3902014-10-03 00:32:37 -0700120 instance.save_by_user(request.user)
121
122 # BUG in django 1.7? Objects are not deleted by formset.save if
123 # commit is False. So let's delete them ourselves.
124 #
125 # code from forms/models.py save_existing_objects()
126 try:
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -0500127 forms_to_delete = formset.deleted_forms
128 except AttributeError:
Scott Baker1a6a3902014-10-03 00:32:37 -0700129 forms_to_delete = []
130 if formset.initial_forms:
131 for form in formset.initial_forms:
132 obj = form.instance
133 if form in forms_to_delete:
134 if obj.pk is None:
135 continue
136 formset.deleted_objects.append(obj)
137 obj.delete()
138
139 formset.save_m2m()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500140
141 def get_actions(self,request):
Scott Baker3a8aed62015-02-27 12:21:22 -0800142 actions = super(XOSAdminMixin,self).get_actions(request)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500143
144 if self.__user_is_readonly(request):
145 if 'delete_selected' in actions:
146 del actions['delete_selected']
147
148 return actions
149
Scott Bakerfbe0f652015-04-03 17:44:31 -0700150 def url_for_model_changelist(self, request, model):
151 # used in add_extra_context
152 return reverse('admin:%s_%s_changelist' % (model._meta.app_label, model._meta.model_name), current_app=model._meta.app_label)
153
Scott Bakera8ef2742015-04-02 22:32:40 -0700154 def add_extra_context(self, request, extra_context):
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800155 # allow custom application breadcrumb url and name
156 extra_context["custom_app_breadcrumb_url"] = getattr(self, "custom_app_breadcrumb_url", None)
157 extra_context["custom_app_breadcrumb_name"] = getattr(self, "custom_app_breadcrumb_name", None)
Scott Bakerf85c0092015-04-02 22:07:18 -0700158 extra_context["custom_changelist_breadcrumb_url"] = getattr(self, "custom_changelist_breadcrumb_url", None)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800159
160 # for Service admins to render their Administration page
161 if getattr(self, "extracontext_registered_admins", False):
162 admins=[]
163 for model, model_admin in admin.site._registry.items():
164 if model == self.model:
165 continue
166 if model._meta.app_label == self.model._meta.app_label:
167 info = {"app": model._meta.app_label,
168 "model": model._meta.model_name,
169 "name": capfirst(model._meta.verbose_name_plural),
Scott Bakerfbe0f652015-04-03 17:44:31 -0700170 "url": self.url_for_model_changelist(request,model) }
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800171 admins.append(info)
172 extra_context["registered_admins"] = admins
173
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500174 def change_view(self,request,object_id, extra_context=None):
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800175 extra_context = extra_context or {}
176
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500177 if self.__user_is_readonly(request):
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -0500178 if not hasattr(self, "readonly_save"):
179 # save the original readonly fields
180 self.readonly_save = self.readonly_fields
181 self.inlines_save = self.inlines
182 if hasattr(self, "user_readonly_fields"):
183 self.readonly_fields=self.user_readonly_fields
184 if hasattr(self, "user_readonly_inlines"):
185 self.inlines = self.user_readonly_inlines
186 else:
187 if hasattr(self, "readonly_save"):
188 # restore the original readonly fields
189 self.readonly_fields = self.readonly_save
190 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -0700191 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500192
Scott Bakera8ef2742015-04-02 22:32:40 -0700193 self.add_extra_context(request, extra_context)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800194
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500195 try:
Scott Baker3a8aed62015-02-27 12:21:22 -0800196 return super(XOSAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500197 except PermissionDenied:
198 pass
Scott Bakerf41fe2c2015-07-09 19:06:08 -0700199 except ValidationError as e:
200 if (e.params is None):
201 # Validation errors that don't reference a specific field will
202 # often throw a non-descriptive 500 page to the user. The code
203 # below will cause an error message to be printed and the
204 # page refreshed instead.
205 # As a side-effect it turns the request back into a 'GET' which
206 # may wipe anything the user had changed on the page. But, at
207 # least the user gets a real error message.
208 # TODO: revisit this and display some kind of error view
209 request.method = 'GET'
210 messages.error(request, e.message)
211 return super(XOSAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
212 else:
213 raise
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500214 if request.method == 'POST':
215 raise PermissionDenied
216 request.readonly = True
Scott Baker3a8aed62015-02-27 12:21:22 -0800217 return super(XOSAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500218
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800219 def changelist_view(self, request, extra_context = None):
220 extra_context = extra_context or {}
221
Scott Bakera8ef2742015-04-02 22:32:40 -0700222 self.add_extra_context(request, extra_context)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800223
Scott Baker3a8aed62015-02-27 12:21:22 -0800224 return super(XOSAdminMixin, self).changelist_view(request, extra_context=extra_context)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800225
Scott Baker88ac9d62015-04-14 17:01:18 -0700226 def add_view(self, request, form_url='', extra_context = None):
Scott Bakerfbe0f652015-04-03 17:44:31 -0700227 extra_context = extra_context or {}
228
229 self.add_extra_context(request, extra_context)
230
Scott Baker88ac9d62015-04-14 17:01:18 -0700231 return super(XOSAdminMixin, self).add_view(request, form_url, extra_context=extra_context)
Scott Bakerfbe0f652015-04-03 17:44:31 -0700232
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500233 def __user_is_readonly(self, request):
234 return request.user.isReadOnlyUser()
235
Scott Baker40c00762014-08-21 16:55:59 -0700236 def backend_status_text(self, obj):
237 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -0700238
Scott Baker63d1a552014-08-21 15:19:07 -0700239 def backend_status_icon(self, obj):
Scott Baker40c00762014-08-21 16:55:59 -0700240 return mark_safe(backend_icon(obj))
Scott Baker63d1a552014-08-21 15:19:07 -0700241 backend_status_icon.short_description = ""
242
Scott Bakerdc4724c2014-11-05 09:05:38 -0800243 def get_form(self, request, obj=None, **kwargs):
Scott Baker9b3c1af2014-10-16 00:57:55 -0700244 # Save obj and request in thread-local storage, so suit_form_tabs can
245 # use it to determine whether we're in edit or add mode, and can
246 # determine whether the user is an admin.
247 _thread_locals.request = request
248 _thread_locals.obj = obj
Scott Baker3a8aed62015-02-27 12:21:22 -0800249 return super(XOSAdminMixin, self).get_form(request, obj, **kwargs)
Scott Baker9b3c1af2014-10-16 00:57:55 -0700250
251 def get_inline_instances(self, request, obj=None):
Scott Baker3a8aed62015-02-27 12:21:22 -0800252 inlines = super(XOSAdminMixin, self).get_inline_instances(request, obj)
Scott Baker9b3c1af2014-10-16 00:57:55 -0700253
254 # inlines that should only be shown to an admin user
255 if request.user.is_admin:
256 for inline_class in getattr(self, "admin_inlines", []):
257 inlines.append(inline_class(self.model, self.admin_site))
258
259 return inlines
260
Scott Baker3a8aed62015-02-27 12:21:22 -0800261class ReadOnlyAwareAdmin(XOSAdminMixin, admin.ModelAdmin):
262 # Note: Make sure XOSAdminMixin is listed before
Scott Bakerf4aeedc2014-10-03 13:10:47 -0700263 # admin.ModelAdmin in the class declaration.
264
Scott Baker1a6a3902014-10-03 00:32:37 -0700265 pass
266
Scott Baker67db95f2015-02-18 15:50:11 -0800267class XOSBaseAdmin(ReadOnlyAwareAdmin):
Scott Baker1a6a3902014-10-03 00:32:37 -0700268 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700269
Scott Bakere8859f92014-05-23 12:42:40 -0700270class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400271 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700272 if not super(SingletonAdmin, self).has_add_permission(request):
273 return False
274
Siobhan Tullyce652d02013-10-08 21:52:35 -0400275 num_objects = self.model.objects.count()
276 if num_objects >= 1:
277 return False
278 else:
279 return True
280
Scott Bakera9b8f612015-02-26 20:42:11 -0800281class ServiceAppAdmin (SingletonAdmin):
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800282 extracontext_registered_admins = True
Scott Bakera9b8f612015-02-26 20:42:11 -0800283
Scott Baker67db95f2015-02-18 15:50:11 -0800284class XOSTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800285 def __init__(self, *args, **kwargs):
Scott Baker67db95f2015-02-18 15:50:11 -0800286 super(XOSTabularInline, self).__init__(*args, **kwargs)
Scott Baker86568322014-01-12 16:53:31 -0800287
288 # InlineModelAdmin as no get_fields() method, so in order to add
289 # the selflink field, we override __init__ to modify self.fields and
290 # self.readonly_fields.
291
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800292 self.setup_selflink()
293
Scott Baker54c9b9b2015-07-24 09:32:14 -0700294 @property
295 def selflink_model(self):
296 if hasattr(self, "selflink_fieldname"):
297 """ self.selflink_model can be defined to punch through a relation
298 to its target object. For example, in SliceNetworkInline, set
299 selflink_model = "network", and the URL will lead to the Network
300 object instead of trying to bring up a change view of the
301 SliceNetwork object.
302 """
303 return getattr(self.model,self.selflink_fieldname).field.rel.to
304 else:
305 return self.model
306
307 @property
308 def selflink_reverse_path(self):
309 return "admin:%s_change" % (self.selflink_model._meta.db_table)
310
311 def get_change_url(self, id):
Scott Baker874936e2014-01-13 18:15:34 -0800312 """ Get the URL to a change form in the admin for this model """
Scott Bakerb581a462015-07-27 08:53:05 -0700313 reverse_path = self.selflink_reverse_path # "admin:%s_change" % (self.selflink_model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800314 try:
Scott Baker874936e2014-01-13 18:15:34 -0800315 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800316 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800317 return None
318
319 return url
320
321 def setup_selflink(self):
Scott Baker54c9b9b2015-07-24 09:32:14 -0700322 url = self.get_change_url(0)
Scott Baker874936e2014-01-13 18:15:34 -0800323
324 # We don't have an admin for this object, so don't create the
325 # selflink.
326 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800327 return
328
Scott Baker874936e2014-01-13 18:15:34 -0800329 # Since we need to add "selflink" to the field list, we need to create
330 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800331 if (self.fields is None):
332 self.fields = []
333 for f in self.model._meta.fields:
334 if f.editable and f.name != "id":
335 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800336
Scott Baker874936e2014-01-13 18:15:34 -0800337 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800338
Scott Baker874936e2014-01-13 18:15:34 -0800339 if self.readonly_fields is None:
340 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800341
Scott Baker874936e2014-01-13 18:15:34 -0800342 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800343
344 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800345 if hasattr(self, "selflink_fieldname"):
346 obj = getattr(obj, self.selflink_fieldname)
347
Scott Baker86568322014-01-12 16:53:31 -0800348 if obj.id:
Scott Baker54c9b9b2015-07-24 09:32:14 -0700349 url = self.get_change_url(obj.id)
Scott Baker874936e2014-01-13 18:15:34 -0800350 return "<a href='%s'>Details</a>" % str(url)
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -0500351 else:
352 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800353
354 selflink.allow_tags = True
355 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400356
Scott Bakerb27b62c2014-08-15 16:29:16 -0700357 def has_add_permission(self, request):
358 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500359
360 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700361 readonly_fields = list(self.readonly_fields)[:]
362 if request.user.isReadOnlyUser():
363 for field in self.fields:
364 if not field in readonly_fields:
365 readonly_fields.append(field)
366 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500367
Scott Baker40c00762014-08-21 16:55:59 -0700368 def backend_status_icon(self, obj):
369 return mark_safe(backend_icon(obj))
370 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700371
Scott Bakerb27b62c2014-08-15 16:29:16 -0700372class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500373 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700374 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500375
Scott Bakerb27b62c2014-08-15 16:29:16 -0700376 def get_readonly_fields(self, request, obj=None):
377 readonly_fields = list(self.readonly_fields)[:]
378 if request.user.isReadOnlyUser():
379 for field in self.fields:
380 if not field in readonly_fields:
381 readonly_fields.append(field)
382 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500383
Scott Baker40c00762014-08-21 16:55:59 -0700384 def backend_status_icon(self, obj):
385 return mark_safe(backend_icon(obj))
386 backend_status_icon.short_description = ""
387
Scott Baker67db95f2015-02-18 15:50:11 -0800388class ReservationInline(XOSTabularInline):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400389 model = Reservation
390 extra = 0
391 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700392
Tony Mack5b061472014-02-04 07:57:10 -0500393 def queryset(self, request):
394 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400395
Scott Bakerb27b62c2014-08-15 16:29:16 -0700396class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400397 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400398 extra = 0
399 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500400 fields = ['service', 'name', 'value']
401
402 def queryset(self, request):
403 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400404
Scott Baker67db95f2015-02-18 15:50:11 -0800405class SliverInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400406 model = Sliver
Scott Baker19b45632015-08-19 16:12:31 -0700407 fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node', 'no_sync']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400408 extra = 0
Scott Baker9d856052015-01-19 11:32:20 -0800409 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400410 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700411
Tony Mack5b061472014-02-04 07:57:10 -0500412 def queryset(self, request):
413 return Sliver.select_by_user(request.user)
414
Scott Bakerb24cc932014-06-09 10:51:16 -0700415 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Tony Mackb2dba4b2014-12-26 13:38:02 -0500416 if db_field.name == 'deployment':
Tony Mack1b8975c2015-02-24 15:41:49 -0500417
418 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
Scott Baker7a61dc42014-09-02 17:08:20 -0700419 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
Tony Mackb2dba4b2014-12-26 13:38:02 -0500420 if db_field.name == 'flavor':
Scott Baker4b6d9442014-09-08 12:14:14 -0700421 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700422
423 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700424
425 return field
426
Scott Baker25881992015-06-12 10:40:15 -0700427class CordSliverInline(XOSTabularInline):
428 model = Sliver
429 fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'flavor', 'image', 'node']
430 extra = 0
431 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name']
432 suit_classes = 'suit-tab suit-tab-slivers'
433
434 def queryset(self, request):
435 return Sliver.select_by_user(request.user)
436
437 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
438 if db_field.name == 'deployment':
439
440 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
441 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
442 if db_field.name == 'flavor':
443 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
444
445 field = super(CordSliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
446
447 return field
448
Scott Baker67db95f2015-02-18 15:50:11 -0800449class SiteInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400450 model = Site
451 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400452 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400453
Tony Mack5b061472014-02-04 07:57:10 -0500454 def queryset(self, request):
455 return Site.select_by_user(request.user)
456
Tony Mack8d60ba32015-08-04 17:53:23 -0400457class SiteHostsNodesInline(SiteInline):
458 def queryset(self, request):
459 return Site.select_by_user(request.user).filter(hosts_nodes=True)
460
461class SiteHostsUsersInline(SiteInline):
462 def queryset(self, request):
463 return Site.select_by_user(request.user).filter(hosts_users=True)
464
Scott Baker67db95f2015-02-18 15:50:11 -0800465class UserInline(XOSTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400466 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700467 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
468 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400469 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400470 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400471
Tony Mack5b061472014-02-04 07:57:10 -0500472 def queryset(self, request):
473 return User.select_by_user(request.user)
474
Scott Baker67db95f2015-02-18 15:50:11 -0800475class SliceInline(XOSTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400476 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700477 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
478 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400479 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400480 suit_classes = 'suit-tab suit-tab-slices'
481
Tony Mack5b061472014-02-04 07:57:10 -0500482 def queryset(self, request):
483 return Slice.select_by_user(request.user)
484
Scott Baker67db95f2015-02-18 15:50:11 -0800485class NodeInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400486 model = Node
487 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400488 suit_classes = 'suit-tab suit-tab-nodes'
Tony Mack68a1e422014-12-08 16:43:02 -0500489 fields = ['backend_status_icon', 'name', 'site_deployment']
Scott Baker40c00762014-08-21 16:55:59 -0700490 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400491
Scott Baker67db95f2015-02-18 15:50:11 -0800492class DeploymentPrivilegeInline(XOSTabularInline):
Tony Mack68a1e422014-12-08 16:43:02 -0500493 model = DeploymentPrivilege
494 extra = 0
Tony Mack88c89902015-02-09 21:41:57 -0500495 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Tony Mack68a1e422014-12-08 16:43:02 -0500496 fields = ['backend_status_icon', 'user','role','deployment']
497 readonly_fields = ('backend_status_icon', )
498
499 def queryset(self, request):
500 return DeploymentPrivilege.select_by_user(request.user)
501
Scott Baker67db95f2015-02-18 15:50:11 -0800502class ControllerSiteInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -0500503 model = ControllerSite
504 extra = 0
505 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Macke2363c12015-01-06 15:08:20 -0500506 fields = ['controller', 'site', 'tenant_id']
Tony Mack3066a952015-01-05 22:48:11 -0500507
508
Scott Baker67db95f2015-02-18 15:50:11 -0800509class SitePrivilegeInline(XOSTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400510 model = SitePrivilege
511 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400512 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700513 fields = ['backend_status_icon', 'user','site', 'role']
514 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400515
Tony Mackc2835a92013-05-28 09:18:49 -0400516 def formfield_for_foreignkey(self, db_field, request, **kwargs):
517 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500518 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400519
520 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500521 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400522 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
523
Tony Mack5b061472014-02-04 07:57:10 -0500524 def queryset(self, request):
525 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400526
Tony Mack60789ac2015-05-11 20:39:32 -0400527
528class ServicePrivilegeInline(XOSTabularInline):
529 model = ServicePrivilege
530 extra = 0
531 suit_classes = 'suit-tab suit-tab-serviceprivileges'
532 fields = ['backend_status_icon', 'user','service', 'role']
533 readonly_fields = ('backend_status_icon', )
534
535 def formfield_for_foreignkey(self, db_field, request, **kwargs):
536 if db_field.name == 'service':
537 kwargs['queryset'] = Service.select_by_user(request.user)
Tony Mack5fa0f402015-05-15 06:33:45 -0400538 if db_field.name == 'user':
539 kwargs['queryset'] = User.select_by_user(request.user)
540 return super(ServicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mack60789ac2015-05-11 20:39:32 -0400541
542 def queryset(self, request):
543 return ServicePrivilege.select_by_user(request.user)
544
Scott Baker67db95f2015-02-18 15:50:11 -0800545class SiteDeploymentInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -0500546 model = SiteDeployment
Tony Macke4be32f2014-03-11 20:45:25 -0400547 extra = 0
Tony Mackb81d5e42015-01-30 10:58:29 -0500548 suit_classes = 'suit-tab suit-tab-sitedeployments'
Tony Mackd14d48f2014-12-05 17:13:08 -0500549 fields = ['backend_status_icon', 'deployment','site', 'controller']
Scott Baker40c00762014-08-21 16:55:59 -0700550 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400551
552 def formfield_for_foreignkey(self, db_field, request, **kwargs):
553 if db_field.name == 'site':
554 kwargs['queryset'] = Site.select_by_user(request.user)
555
556 if db_field.name == 'deployment':
557 kwargs['queryset'] = Deployment.select_by_user(request.user)
Tony Mackd14d48f2014-12-05 17:13:08 -0500558
559 if db_field.name == 'controller':
Tony Mack5817cb42015-02-16 19:54:24 -0500560 kwargs['queryset'] = Controller.select_by_user(request.user).filter(deployment__id=int(resolve(request.path).args[0]))
Tony Mackd14d48f2014-12-05 17:13:08 -0500561
Tony Mack3066a952015-01-05 22:48:11 -0500562 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Macke4be32f2014-03-11 20:45:25 -0400563
564 def queryset(self, request):
Tony Mack3066a952015-01-05 22:48:11 -0500565 return SiteDeployment.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400566
567
Scott Baker67db95f2015-02-18 15:50:11 -0800568class SlicePrivilegeInline(XOSTabularInline):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400569 model = SlicePrivilege
570 suit_classes = 'suit-tab suit-tab-sliceprivileges'
571 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700572 fields = ('backend_status_icon', 'user', 'slice', 'role')
573 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400574
Tony Mackc2835a92013-05-28 09:18:49 -0400575 def formfield_for_foreignkey(self, db_field, request, **kwargs):
576 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700577 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400578 if db_field.name == 'user':
Scott Baker31ba9ca2015-07-16 12:40:07 -0700579 # all users are available to be granted SlicePrivilege
580 kwargs['queryset'] = User.objects.all()
Tony Mackc2835a92013-05-28 09:18:49 -0400581
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400582 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400583
Tony Mack5b061472014-02-04 07:57:10 -0500584 def queryset(self, request):
585 return SlicePrivilege.select_by_user(request.user)
586
Scott Baker67db95f2015-02-18 15:50:11 -0800587class SliceNetworkInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700588 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800589 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700590 extra = 0
591 verbose_name = "Network Connection"
592 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400593 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700594 fields = ['backend_status_icon', 'network']
595 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700596
Scott Baker67db95f2015-02-18 15:50:11 -0800597class ImageDeploymentsInline(XOSTabularInline):
Sapan Bhatia1b6bba22014-11-19 15:10:16 -0500598 model = ImageDeployments
Scott Baker2170b972014-06-03 12:14:07 -0700599 extra = 0
600 verbose_name = "Image Deployments"
601 verbose_name_plural = "Image Deployments"
602 suit_classes = 'suit-tab suit-tab-imagedeployments'
Tony Mack06c8e472014-11-30 15:53:08 -0500603 fields = ['backend_status_icon', 'image', 'deployment']
604 readonly_fields = ['backend_status_icon']
605
Scott Baker67db95f2015-02-18 15:50:11 -0800606class ControllerImagesInline(XOSTabularInline):
Tony Mack06c8e472014-11-30 15:53:08 -0500607 model = ControllerImages
608 extra = 0
609 verbose_name = "Controller Images"
610 verbose_name_plural = "Controller Images"
611 suit_classes = 'suit-tab suit-tab-admin-only'
612 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
Scott Baker40c00762014-08-21 16:55:59 -0700613 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700614
Scott Baker67db95f2015-02-18 15:50:11 -0800615class SliceRoleAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400616 model = SliceRole
617 pass
618
Scott Baker67db95f2015-02-18 15:50:11 -0800619class SiteRoleAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400620 model = SiteRole
621 pass
622
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400623class DeploymentAdminForm(forms.ModelForm):
Scott Bakerde0f4412014-06-11 15:40:26 -0700624 images = forms.ModelMultipleChoiceField(
625 queryset=Image.objects.all(),
626 required=False,
627 help_text="Select which images should be deployed on this deployment",
628 widget=FilteredSelectMultiple(
629 verbose_name=('Images'), is_stacked=False
630 )
631 )
Scott Baker37b47902014-09-02 14:37:41 -0700632 flavors = forms.ModelMultipleChoiceField(
633 queryset=Flavor.objects.all(),
634 required=False,
635 help_text="Select which flavors should be usable on this deployment",
636 widget=FilteredSelectMultiple(
637 verbose_name=('Flavors'), is_stacked=False
638 )
639 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400640 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400641 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700642 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400643
Siobhan Tully320b4622014-01-17 15:11:14 -0500644 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700645 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500646 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
647
Scott Baker5380c522014-06-06 14:49:43 -0700648 self.fields['accessControl'].initial = "allow site " + request.user.site.name
649
Siobhan Tully320b4622014-01-17 15:11:14 -0500650 if self.instance and self.instance.pk:
Scott Baker69e045d2014-11-17 23:44:03 -0800651 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700652 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700653
654 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
655 """ helper function for handling m2m relations from the MultipleChoiceField
656
657 this_obj: the source object we want to link from
658
659 selected_objs: a list of destination objects we want to link to
660
661 all_relations: the full set of relations involving this_obj, including ones we don't want
662
663 relation_class: the class that implements the relation from source to dest
664
665 local_attrname: field name representing this_obj in relation_class
666
667 foreign_attrname: field name representing selected_objs in relation_class
668
669 This function will remove all newobjclass relations from this_obj
670 that are not contained in selected_objs, and add any relations that
671 are in selected_objs but don't exist in the data model yet.
672 """
673
674 existing_dest_objs = []
675 for relation in list(all_relations):
676 if getattr(relation, foreign_attrname) not in selected_objs:
677 #print "deleting site", sdp.site
678 relation.delete()
679 else:
680 existing_dest_objs.append(getattr(relation, foreign_attrname))
681
682 for dest_obj in selected_objs:
683 if dest_obj not in existing_dest_objs:
684 #print "adding site", site
685 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
686 relation = relation_class(**kwargs)
687 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500688
689 def save(self, commit=True):
690 deployment = super(DeploymentAdminForm, self).save(commit=False)
691
692 if commit:
693 deployment.save()
Scott Baker0057d052014-10-06 17:17:40 -0700694 # this has to be done after save() if/when a deployment is first created
695 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500696
697 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700698 # save_m2m() doesn't seem to work with 'through' relations. So we
699 # create/destroy the through models ourselves. There has to be
700 # a better way...
701
Tony Mackb2fde612014-12-15 11:45:02 -0500702 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ImageDeployments, "deployment", "image")
703 # manipulate_m2m_objs doesn't work for Flavor/Deployment relationship
704 # so well handle that manually here
705 for flavor in deployment.flavors.all():
706 if getattr(flavor, 'name') not in self.cleaned_data['flavors']:
Tony Mackd4ab7822014-12-15 12:37:59 -0500707 deployment.flavors.remove(flavor)
Tony Mackb2fde612014-12-15 11:45:02 -0500708 for flavor in self.cleaned_data['flavors']:
709 if flavor not in deployment.flavors.all():
710 flavor.deployments.add(deployment)
Scott Bakerc9b14f72014-05-22 13:44:20 -0700711
Scott Baker37b47902014-09-02 14:37:41 -0700712 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500713
714 return deployment
715
Scott Bakerff5e0f32014-05-22 14:40:27 -0700716class DeploymentAdminROForm(DeploymentAdminForm):
717 def save(self, commit=True):
718 raise PermissionDenied
719
Scott Baker67db95f2015-02-18 15:50:11 -0800720class SiteAssocInline(XOSTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500721 model = Site.deployments.through
722 extra = 0
723 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400724
Scott Baker67db95f2015-02-18 15:50:11 -0800725class DeploymentAdmin(XOSBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500726 model = Deployment
Scott Baker622bcf02015-02-10 08:40:34 -0800727 fieldList = ['backend_status_text', 'name', 'images', 'flavors', 'accessControl']
728 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack68a1e422014-12-08 16:43:02 -0500729 # node no longer directly connected to deployment
730 #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
Tony Mackb81d5e42015-01-30 10:58:29 -0500731 inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline,SiteDeploymentInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700732 list_display = ['backend_status_icon', 'name']
733 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700734 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500735
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500736 user_readonly_fields = ['name']
737
Tony Mack68a1e422014-12-08 16:43:02 -0500738 # nodes no longer direclty connected to deployments
Scott Baker622bcf02015-02-10 08:40:34 -0800739 suit_form_tabs =(('general','Deployment Details'),('deploymentprivileges','Privileges'), ('sitedeployments', 'Sites'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500740
Scott Bakerff5e0f32014-05-22 14:40:27 -0700741 def get_form(self, request, obj=None, **kwargs):
Tony Mackcf29cfa2015-02-05 06:13:04 -0500742 if request.user.isReadOnlyUser() or not request.user.is_admin:
Scott Bakerff5e0f32014-05-22 14:40:27 -0700743 kwargs["form"] = DeploymentAdminROForm
744 else:
745 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700746 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
747
748 # from stackexchange: pass the request object into the form
749
750 class AdminFormMetaClass(adminForm):
751 def __new__(cls, *args, **kwargs):
752 kwargs['request'] = request
753 return adminForm(*args, **kwargs)
754
755 return AdminFormMetaClass
756
Scott Baker67db95f2015-02-18 15:50:11 -0800757class ControllerAdmin(XOSBaseAdmin):
Scott Baker622bcf02015-02-10 08:40:34 -0800758 model = Controller
Scott Baker180148a2015-02-16 11:55:09 -0800759 fieldList = ['deployment', 'name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password', 'domain']
Scott Baker622bcf02015-02-10 08:40:34 -0800760 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack3066a952015-01-05 22:48:11 -0500761 inlines = [ControllerSiteInline] # ,ControllerImagesInline]
Tony Mackd14d48f2014-12-05 17:13:08 -0500762 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
763 list_display_links = ('backend_status_icon', 'name', )
764 readonly_fields = ('backend_status_text',)
765
766 user_readonly_fields = []
767
Tony Mack2e897fa2015-01-13 17:33:08 -0500768 def save_model(self, request, obj, form, change):
769 # update openstack connection to use this site/tenant
770 obj.save_by_user(request.user)
771
772 def delete_model(self, request, obj):
Scott Baker622bcf02015-02-10 08:40:34 -0800773 obj.delete_by_user(request.user)
774
Tony Mack78fc1362015-02-18 11:41:36 -0500775 def queryset(self, request):
776 return Controller.select_by_user(request.user)
777
Scott Baker622bcf02015-02-10 08:40:34 -0800778 @property
779 def suit_form_tabs(self):
780 tabs = [('general', 'Controller Details'),
781 ]
782
783 request=getattr(_thread_locals, "request", None)
784 if request and request.user.is_admin:
785 tabs.append( ('admin-only', 'Admin-Only') )
786
787 return tabs
Tony Mack2e897fa2015-01-13 17:33:08 -0500788
Scott Bakerb3cf9212015-07-06 14:40:20 -0700789class TenantRootRoleAdmin(XOSBaseAdmin):
790 model = TenantRootRole
791 fields = ('role',)
792
793class TenantRootTenantInline(XOSTabularInline):
794 model = Tenant
795 fields = ['provider_service', 'subscriber_root']
796 extra = 0
797 suit_classes = 'suit-tab suit-tab-tenantroots'
798 fk_name = 'subscriber_root'
799 verbose_name = 'subscribed tenant'
800 verbose_name_plural = 'subscribed tenants'
801
802 #def queryset(self, request):
803 # qs = super(TenantRootTenantInline, self).queryset(request)
804 # return qs.filter(kind="coarse")
805
806class TenantRootPrivilegeInline(XOSTabularInline):
807 model = TenantRootPrivilege
808 extra = 0
809 suit_classes = 'suit-tab suit-tab-tenantrootprivileges'
810 fields = ['backend_status_icon', 'user', 'role', 'tenant_root']
811 readonly_fields = ('backend_status_icon', )
812
813 def queryset(self, request):
814 return TenantRootPrivilege.select_by_user(request.user)
815
816class TenantRootAdmin(XOSBaseAdmin):
817 model = TenantRoot
818 list_display = ('backend_status_icon', 'name', 'kind')
819 list_display_links = ('backend_status_icon', 'name')
820 fieldList = ('backend_status_text', 'name', 'kind', )
821 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
822 inlines = (TenantRootTenantInline, TenantRootPrivilegeInline)
823 readonly_fields = ('backend_status_text', )
824
825 suit_form_tabs =(('general', 'Tenant Root Details'),
826 ('tenantroots','Tenancy'),
827 ('tenantrootprivileges','Privileges')
828 )
829
Scott Bakeref58a842015-04-26 20:30:40 -0700830class ProviderTenantInline(XOSTabularInline):
831 model = CoarseTenant
832 fields = ['provider_service', 'subscriber_service', 'connect_method']
833 extra = 0
834 suit_classes = 'suit-tab suit-tab-servicetenants'
835 fk_name = 'provider_service'
836 verbose_name = 'provided tenant'
837 verbose_name_plural = 'provided tenants'
838
839 def queryset(self, request):
840 qs = super(ProviderTenantInline, self).queryset(request)
841 return qs.filter(kind="coarse")
842
843class SubscriberTenantInline(XOSTabularInline):
844 model = CoarseTenant
845 fields = ['provider_service', 'subscriber_service', 'connect_method']
846 extra = 0
847 suit_classes = 'suit-tab suit-tab-servicetenants'
848 fk_name = 'subscriber_service'
849 verbose_name = 'subscribed tenant'
850 verbose_name_plural = 'subscribed tenants'
851
852 def queryset(self, request):
853 qs = super(SubscriberTenantInline, self).queryset(request)
854 return qs.filter(kind="coarse")
855
Scott Baker67db95f2015-02-18 15:50:11 -0800856class ServiceAttrAsTabInline(XOSTabularInline):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400857 model = ServiceAttribute
858 fields = ['name','value']
859 extra = 0
860 suit_classes = 'suit-tab suit-tab-serviceattrs'
861
Scott Baker67db95f2015-02-18 15:50:11 -0800862class ServiceAdmin(XOSBaseAdmin):
Scott Baker0d306722015-04-15 20:58:20 -0700863 list_display = ("backend_status_icon","name","kind","versionNumber","enabled","published")
Scott Baker63d1a552014-08-21 15:19:07 -0700864 list_display_links = ('backend_status_icon', 'name', )
Scott Bakerb9040e92015-07-13 12:33:28 -0700865 fieldList = ["backend_status_text","name","kind","description","versionNumber","enabled","published","view_url","icon_url","public_key","service_specific_attribute","service_specific_id"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500866 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack60789ac2015-05-11 20:39:32 -0400867 inlines = [ServiceAttrAsTabInline,SliceInline,ProviderTenantInline,SubscriberTenantInline,ServicePrivilegeInline]
Scott Baker40c00762014-08-21 16:55:59 -0700868 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500869
870 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500871
872 suit_form_tabs =(('general', 'Service Details'),
873 ('slices','Slices'),
874 ('serviceattrs','Additional Attributes'),
Scott Bakeref58a842015-04-26 20:30:40 -0700875 ('servicetenants','Tenancy'),
Scott Bakerb3cf9212015-07-06 14:40:20 -0700876 ('serviceprivileges','Privileges')
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500877 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400878
Scott Baker67db95f2015-02-18 15:50:11 -0800879class SiteNodeInline(XOSTabularInline):
Tony Mack4f134e62015-01-14 20:58:38 -0500880 model = Node
881 fields = ['name', 'site_deployment']
882 extra = 0
883 suit_classes = 'suit-tab suit-tab-nodes'
884
Tony Mackc2a0d312015-02-25 11:39:34 -0500885 def formfield_for_foreignkey(self, db_field, request, **kwargs):
886 # only display site deployments associated with this site
887 if db_field.name == 'site_deployment':
888 kwargs['queryset'] = SiteDeployment.objects.filter(site__id=int(request.path.split('/')[-2]))
889
890 return super(SiteNodeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
891
Scott Baker67db95f2015-02-18 15:50:11 -0800892class SiteAdmin(XOSBaseAdmin):
Tony Mack450b6e02015-01-25 12:35:29 -0500893 #fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Tony Mack6a388472015-08-04 17:21:55 -0400894 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'login_base', 'location', 'is_public', 'hosts_nodes', 'hosts_users']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400895 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500896 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400897 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400898 ]
Tony Mack450b6e02015-01-25 12:35:29 -0500899 #readonly_fields = ['backend_status_text', 'accountLink']
900 readonly_fields = ['backend_status_text']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500901
Tony Mack450b6e02015-01-25 12:35:29 -0500902 #user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Tony Mack6a388472015-08-04 17:21:55 -0400903 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'hosts_nodes', 'hosts_users']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500904
Scott Baker63d1a552014-08-21 15:19:07 -0700905 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
906 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400907 filter_horizontal = ('deployments',)
Tony Mackb81d5e42015-01-30 10:58:29 -0500908 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteNodeInline]
Tony Mackde100182015-01-14 12:11:05 -0500909 admin_inlines = [ControllerSiteInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400910 search_fields = ['name']
911
Tony Mack30dfcd72015-01-10 23:08:10 -0500912 @property
913 def suit_form_tabs(self):
914 tabs = [('general', 'Site Details'),
915 ('users','Users'),
916 ('siteprivileges','Privileges'),
Tony Mack30dfcd72015-01-10 23:08:10 -0500917 ('slices','Slices'),
Tony Mack4f134e62015-01-14 20:58:38 -0500918 ('nodes','Nodes'),
Tony Mack30dfcd72015-01-10 23:08:10 -0500919 ]
920
921 request=getattr(_thread_locals, "request", None)
922 if request and request.user.is_admin:
923 tabs.append( ('admin-only', 'Admin-Only') )
924
925 return tabs
926
Tony Mack04062832013-05-10 08:22:44 -0400927 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500928 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400929
Tony Mack5cd13202013-05-01 21:48:38 -0400930 def get_formsets(self, request, obj=None):
931 for inline in self.get_inline_instances(request, obj):
932 # hide MyInline in the add view
933 if obj is None:
934 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400935 if isinstance(inline, SliverInline):
936 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400937 yield inline.get_formset(request, obj)
938
Scott Baker545db2a2013-12-09 18:44:43 -0800939 def accountLink(self, obj):
940 link_obj = obj.accounts.all()
941 if link_obj:
942 reverse_path = "admin:core_account_change"
943 url = reverse(reverse_path, args =(link_obj[0].id,))
944 return "<a href='%s'>%s</a>" % (url, "view billing details")
945 else:
946 return "no billing data for this site"
947 accountLink.allow_tags = True
948 accountLink.short_description = "Billing"
949
Tony Mack332ee1d2014-02-04 15:33:45 -0500950 def save_model(self, request, obj, form, change):
951 # update openstack connection to use this site/tenant
952 obj.save_by_user(request.user)
953
954 def delete_model(self, request, obj):
955 obj.delete_by_user(request.user)
956
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500957
Scott Baker67db95f2015-02-18 15:50:11 -0800958class SitePrivilegeAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700959 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400960 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500961 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400962 ]
Scott Baker40c00762014-08-21 16:55:59 -0700963 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700964 list_display = ('backend_status_icon', 'user', 'site', 'role')
965 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500966 user_readonly_fields = fieldList
967 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400968
Tony Mackc2835a92013-05-28 09:18:49 -0400969 def formfield_for_foreignkey(self, db_field, request, **kwargs):
970 if db_field.name == 'site':
971 if not request.user.is_admin:
972 # only show sites where user is an admin or pi
973 sites = set()
974 for site_privilege in SitePrivilege.objects.filer(user=request.user):
975 if site_privilege.role.role_type in ['admin', 'pi']:
976 sites.add(site_privilege.site)
977 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
978
979 if db_field.name == 'user':
980 if not request.user.is_admin:
981 # only show users from sites where caller has admin or pi role
982 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
983 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
984 sites = [site_privilege.site for site_privilege in site_privileges]
985 site_privileges = SitePrivilege.objects.filter(site__in=sites)
986 emails = [site_privilege.user.email for site_privilege in site_privileges]
987 users = User.objects.filter(email__in=emails)
988 kwargs['queryset'] = users
989
990 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
991
Tony Mack04062832013-05-10 08:22:44 -0400992 def queryset(self, request):
993 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400994 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400995 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500996 #if not request.user.is_admin:
997 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
998 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
999 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
1000 # sites = Site.objects.filter(login_base__in=login_bases)
1001 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -04001002 return qs
1003
Siobhan Tullyce652d02013-10-08 21:52:35 -04001004class SliceForm(forms.ModelForm):
1005 class Meta:
1006 model = Slice
1007 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -07001008 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -04001009 }
1010
Tony Macke75841e2014-09-29 16:10:52 -04001011 def clean(self):
1012 cleaned_data = super(SliceForm, self).clean()
1013 name = cleaned_data.get('name')
Scott Baker3cb382c2014-10-06 23:09:59 -07001014 site = cleaned_data.get('site')
Tony Mackcc9e2592014-10-22 12:54:19 -04001015 slice_id = self.instance.id
1016 if not site and slice_id:
1017 site = Slice.objects.get(id=slice_id).site
Scott Baker3cb382c2014-10-06 23:09:59 -07001018 if (not isinstance(site,Site)):
1019 # previous code indicates 'site' could be a site_id and not a site?
1020 site = Slice.objects.get(id=site.id)
Tony Macke75841e2014-09-29 16:10:52 -04001021 if not name.startswith(site.login_base):
1022 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
1023 return cleaned_data
1024
Scott Baker67db95f2015-02-18 15:50:11 -08001025class ControllerSliceInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -05001026 model = ControllerSlice
Scott Bakerf9f1ef42014-10-15 16:54:04 -07001027 extra = 0
Tony Mack06c8e472014-11-30 15:53:08 -05001028 verbose_name = "Controller Slices"
1029 verbose_name_plural = "Controller Slices"
Scott Bakerf9f1ef42014-10-15 16:54:04 -07001030 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack06c8e472014-11-30 15:53:08 -05001031 fields = ['backend_status_icon', 'controller', 'tenant_id']
Tony Mack30dfcd72015-01-10 23:08:10 -05001032 readonly_fields = ('backend_status_icon', 'controller' )
Scott Bakerf9f1ef42014-10-15 16:54:04 -07001033
Scott Baker67db95f2015-02-18 15:50:11 -08001034class SliceAdmin(XOSBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -04001035 form = SliceForm
Tony Mackfbb26fc2014-09-02 07:03:27 -04001036 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001037 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -07001038 readonly_fields = ('backend_status_text', )
Tony Mack7d459902014-09-03 13:18:57 -04001039 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
1040 list_display_links = ('backend_status_icon', 'name', )
Scott Baker25881992015-06-12 10:40:15 -07001041 normal_inlines = [SlicePrivilegeInline, SliverInline, TagInline, ReservationInline, SliceNetworkInline]
1042 inlines = normal_inlines
Tony Mack3066a952015-01-05 22:48:11 -05001043 admin_inlines = [ControllerSliceInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001044
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001045 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001046
Scott Bakerf9f1ef42014-10-15 16:54:04 -07001047 @property
1048 def suit_form_tabs(self):
1049 tabs =[('general', 'Slice Details'),
1050 ('slicenetworks','Networks'),
1051 ('sliceprivileges','Privileges'),
1052 ('slivers','Slivers'),
Tony Mack450b6e02015-01-25 12:35:29 -05001053 #('reservations','Reservations'),
Tony Mackd2433382015-01-15 14:44:06 -05001054 ('tags','Tags'),
Scott Bakerf9f1ef42014-10-15 16:54:04 -07001055 ]
1056
1057 request=getattr(_thread_locals, "request", None)
1058 if request and request.user.is_admin:
1059 tabs.append( ('admin-only', 'Admin-Only') )
1060
1061 return tabs
Tony Mack0aa732a2014-10-22 11:54:29 -04001062
1063 def add_view(self, request, form_url='', extra_context=None):
Scott Baker25881992015-06-12 10:40:15 -07001064 # Ugly hack for CORD
1065 self.inlines = self.normal_inlines
Tony Mack0aa732a2014-10-22 11:54:29 -04001066 # revert to default read-only fields
1067 self.readonly_fields = ('backend_status_text',)
1068 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
1069
1070 def change_view(self, request, object_id, form_url='', extra_context=None):
Tony Mack0aa732a2014-10-22 11:54:29 -04001071 # cannot change the site of an existing slice so make the site field read only
1072 if object_id:
1073 self.readonly_fields = ('backend_status_text','site')
Scott Baker25881992015-06-12 10:40:15 -07001074
Tony Mack0aa732a2014-10-22 11:54:29 -04001075 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001076
Scott Baker510fdbb2014-08-05 17:19:24 -07001077 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -07001078 deployment_nodes = []
1079 for node in Node.objects.all():
Scott Baker66b11e22015-01-21 16:24:07 -08001080 deployment_nodes.append( (node.site_deployment.deployment.id, node.id, node.name) )
Scott Baker510fdbb2014-08-05 17:19:24 -07001081
Scott Baker7a61dc42014-09-02 17:08:20 -07001082 deployment_flavors = []
1083 for flavor in Flavor.objects.all():
1084 for deployment in flavor.deployments.all():
1085 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
1086
Tony Mack68a1e422014-12-08 16:43:02 -05001087 deployment_images = []
Scott Bakeraf36c4d2014-09-09 09:58:49 -07001088 for image in Image.objects.all():
Tony Mack68a1e422014-12-08 16:43:02 -05001089 for deployment_image in image.imagedeployments.all():
Scott Bakerf2c0c512014-12-22 17:35:34 -08001090 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
Scott Bakeraf36c4d2014-09-09 09:58:49 -07001091
Tony Mackec23b992014-09-02 21:18:45 -04001092 site_login_bases = []
1093 for site in Site.objects.all():
Scott Bakeraf36c4d2014-09-09 09:58:49 -07001094 site_login_bases.append((site.id, site.login_base))
1095
Scott Baker510fdbb2014-08-05 17:19:24 -07001096 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -07001097 context["deployment_flavors"] = deployment_flavors
Scott Bakeraf36c4d2014-09-09 09:58:49 -07001098 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -04001099 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -07001100 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
1101
Tony Mackc2835a92013-05-28 09:18:49 -04001102 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1103 if db_field.name == 'site':
Tony Mack8d60ba32015-08-04 17:53:23 -04001104 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_users=True)
Tony Mackec23b992014-09-02 21:18:45 -04001105 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 -07001106
Tony Mackc2835a92013-05-28 09:18:49 -04001107 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1108
Tony Mack04062832013-05-10 08:22:44 -04001109 def queryset(self, request):
1110 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001111 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -04001112
Tony Mack79748612013-05-01 14:52:03 -04001113 def get_formsets(self, request, obj=None):
1114 for inline in self.get_inline_instances(request, obj):
1115 # hide MyInline in the add view
1116 if obj is None:
1117 continue
Tony Mack2bd5b412013-06-11 21:05:06 -04001118 if isinstance(inline, SliverInline):
1119 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -04001120 yield inline.get_formset(request, obj)
1121
Scott Baker25881992015-06-12 10:40:15 -07001122 def UNUSED_get_inline_instances(self, request, obj=None):
1123 # HACK for CORD to do something special on vcpe slice page
1124 # this was a good idea, but failed miserably, as something still
1125 # expects there to be a deployment field.
1126 # XXX this approach is better than clobbering self.inlines, so
1127 # try to make this work post-demo.
1128 if (obj is not None) and (obj.name == "mysite_vcpe"):
1129 cord_vcpe_inlines = [ SlicePrivilegeInline, CordSliverInline, TagInline, ReservationInline,SliceNetworkInline]
1130
1131 inlines=[]
1132 for inline_class in cord_vcpe_inlines:
1133 inlines.append(inline_class(self.model, self.admin_site))
1134 else:
1135 inlines = super(SliceAdmin, self).get_inline_instances(request, obj)
1136
1137 return inlines
1138
Scott Baker67db95f2015-02-18 15:50:11 -08001139class SlicePrivilegeAdmin(XOSBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -04001140 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -07001141 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -04001142 ]
Scott Baker40c00762014-08-21 16:55:59 -07001143 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -07001144 list_display = ('backend_status_icon', 'user', 'slice', 'role')
1145 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -04001146
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001147 user_readonly_fields = ['user', 'slice', 'role']
1148 user_readonly_inlines = []
1149
Tony Mackc2835a92013-05-28 09:18:49 -04001150 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1151 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001152 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001153
1154 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -05001155 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001156
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001157 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001158
Tony Mack04062832013-05-10 08:22:44 -04001159 def queryset(self, request):
1160 # admins can see all memberships. Users can only see memberships of
1161 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -05001162 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001163
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001164 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -04001165 # update openstack connection to use this site/tenant
1166 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001167 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001168 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001169 obj.save()
1170
1171 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -04001172 # update openstack connection to use this site/tenant
1173 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001174 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001175 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001176 obj.delete()
1177
Scott Baker67db95f2015-02-18 15:50:11 -08001178class ImageAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001179
Scott Baker36f50872014-08-21 13:01:25 -07001180 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -07001181 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001182 'classes': ['suit-tab suit-tab-general']})
1183 ]
Scott Baker40c00762014-08-21 16:55:59 -07001184 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001185
Tony Mack06c8e472014-11-30 15:53:08 -05001186 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001187
Tony Mack06c8e472014-11-30 15:53:08 -05001188 inlines = [SliverInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -07001189
Tony Mack32e1ce32014-05-07 13:29:41 -04001190 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -07001191
Scott Baker63d1a552014-08-21 15:19:07 -07001192 list_display = ['backend_status_icon', 'name']
1193 list_display_links = ('backend_status_icon', 'name', )
1194
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001195class NodeForm(forms.ModelForm):
1196 class Meta:
1197 widgets = {
1198 'site': LinkedSelect,
1199 'deployment': LinkedSelect
1200 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001201
Scott Baker67db95f2015-02-18 15:50:11 -08001202class NodeAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001203 form = NodeForm
Tony Mack68a1e422014-12-08 16:43:02 -05001204 list_display = ('backend_status_icon', 'name', 'site_deployment')
Scott Baker63d1a552014-08-21 15:19:07 -07001205 list_display_links = ('backend_status_icon', 'name', )
Tony Mack68a1e422014-12-08 16:43:02 -05001206 list_filter = ('site_deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001207
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001208 inlines = [TagInline,SliverInline]
Tony Mack68a1e422014-12-08 16:43:02 -05001209 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site_deployment'], 'classes':['suit-tab suit-tab-details']})]
Scott Baker40c00762014-08-21 16:55:59 -07001210 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001211
Tony Mack68a1e422014-12-08 16:43:02 -05001212 user_readonly_fields = ['name','site_deployment']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001213 user_readonly_inlines = [TagInline,SliverInline]
1214
Tony Mack5fecf712015-01-12 21:40:09 -05001215 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001216
Tony Mack02b8f142015-08-04 17:32:32 -04001217 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1218 if db_field.name == 'site':
1219 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_nodes=True)
Siobhan Tully567e3e62013-06-21 18:03:16 -04001220
Tony Mackd90cdbf2013-04-16 22:48:40 -04001221class SliverForm(forms.ModelForm):
1222 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -04001223 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -04001224 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001225 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001226 widgets = {
1227 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001228 'instance_name': PlainTextWidget(),
Scott Baker9d856052015-01-19 11:32:20 -08001229 'instance_id': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001230 'slice': LinkedSelect,
Tony Mackb2dba4b2014-12-26 13:38:02 -05001231 'deployment': LinkedSelect,
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001232 'node': LinkedSelect,
1233 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001234 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001235
Scott Baker67db95f2015-02-18 15:50:11 -08001236class TagAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001237 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1238 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001239 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1240 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001241
Scott Bakerfbade882015-08-25 18:00:15 -07001242class SliverPortInline(XOSTabularInline):
1243 fields = ['backend_status_icon', 'network', 'sliver', 'ip']
1244 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker5a7d9312015-08-26 09:43:33 -07001245 model = Port
Scott Bakerfbade882015-08-25 18:00:15 -07001246 selflink_fieldname = "network"
1247 extra = 0
1248 verbose_name_plural = "Ports"
1249 verbose_name = "Port"
1250 suit_classes = 'suit-tab suit-tab-ports'
1251
Scott Baker67db95f2015-02-18 15:50:11 -08001252class SliverAdmin(XOSBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001253 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -04001254 fieldsets = [
Scott Baker19b45632015-08-19 16:12:31 -07001255 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'node', 'all_ips_string', 'instance_id', 'instance_name', 'flavor', 'image', 'ssh_command', 'no_sync'], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -04001256 ]
Tony Mackdb8580b2015-01-30 17:20:46 -05001257 readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Tony Mack707f7d72015-01-30 12:52:46 -05001258 list_display = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deployment']
Scott Baker2f295402015-02-13 14:38:21 -08001259 list_display_links = ('backend_status_icon', 'all_ips_string', 'instance_id', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001260
Scott Bakerfbade882015-08-25 18:00:15 -07001261 suit_form_tabs =(('general', 'Sliver Details'), ('ports', 'Ports'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001262
Scott Bakerfbade882015-08-25 18:00:15 -07001263 inlines = [TagInline, SliverPortInline]
Tony Mack53106f32013-04-27 16:43:01 -04001264
Tony Mackb2dba4b2014-12-26 13:38:02 -05001265 user_readonly_fields = ['slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001266
Scott Baker7ccc6ad2015-01-25 22:16:13 -08001267 def ssh_command(self, obj):
1268 ssh_command = obj.get_ssh_command()
1269 if ssh_command:
1270 return ssh_command
1271 else:
1272 return "(not available)"
1273
Tony Mackc2835a92013-05-28 09:18:49 -04001274 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1275 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001276 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001277
1278 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1279
Tony Mack04062832013-05-10 08:22:44 -04001280 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -07001281 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -04001282 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001283 return Sliver.select_by_user(request.user)
1284
Tony Mack04062832013-05-10 08:22:44 -04001285
Tony Mack1d6b85f2013-05-07 18:49:14 -04001286 def get_formsets(self, request, obj=None):
1287 # make some fields read only if we are updating an existing record
1288 if obj == None:
Tony Mackb428feb2015-01-30 17:42:10 -05001289 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001290 else:
Tony Mackb428feb2015-01-30 17:42:10 -05001291 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string', 'slice', 'flavor', 'image', 'node')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001292
1293 for inline in self.get_inline_instances(request, obj):
1294 # hide MyInline in the add view
1295 if obj is None:
1296 continue
Scott Baker526b71e2014-05-13 13:18:01 -07001297 if isinstance(inline, SliverInline):
1298 inline.model.caller = request.user
1299 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -04001300
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001301 #def save_model(self, request, obj, form, change):
1302 # # update openstack connection to use this site/tenant
1303 # auth = request.session.get('auth', {})
1304 # auth['tenant'] = obj.slice.name
1305 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1306 # obj.creator = request.user
1307 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001308
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001309 #def delete_model(self, request, obj):
1310 # # update openstack connection to use this site/tenant
1311 # auth = request.session.get('auth', {})
1312 # auth['tenant'] = obj.slice.name
1313 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1314 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001315
Siobhan Tully53437282013-04-26 19:30:27 -04001316class UserCreationForm(forms.ModelForm):
1317 """A form for creating new users. Includes all the required
1318 fields, plus a repeated password."""
1319 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1320 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1321
1322 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001323 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001324 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001325
1326 def clean_password2(self):
1327 # Check that the two password entries match
1328 password1 = self.cleaned_data.get("password1")
1329 password2 = self.cleaned_data.get("password2")
1330 if password1 and password2 and password1 != password2:
1331 raise forms.ValidationError("Passwords don't match")
1332 return password2
1333
1334 def save(self, commit=True):
1335 # Save the provided password in hashed format
1336 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001337 user.password = self.cleaned_data["password1"]
1338 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001339 if commit:
1340 user.save()
1341 return user
1342
Siobhan Tully567e3e62013-06-21 18:03:16 -04001343
Siobhan Tully53437282013-04-26 19:30:27 -04001344class UserChangeForm(forms.ModelForm):
1345 """A form for updating users. Includes all the fields on
1346 the user, but replaces the password field with admin's
1347 password hash display field.
1348 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001349 password = ReadOnlyPasswordHashField(label='Password',
1350 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001351
Scott Bakerb6043c22015-05-19 16:39:48 -07001352 PROFILE_CHOICES = ((None, '------'), ('regular', 'Regular user'), ('cp', 'Content Provider'))
1353 profile = forms.ChoiceField(choices=PROFILE_CHOICES, required=False, label="Quick Profile")
1354
Siobhan Tully53437282013-04-26 19:30:27 -04001355 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001356 model = User
Scott Baker69e045d2014-11-17 23:44:03 -08001357 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001358
1359 def clean_password(self):
1360 # Regardless of what the user provides, return the initial value.
1361 # This is done here, rather than on the field, because the
1362 # field does not have access to the initial value
1363 return self.initial["password"]
1364
Scott Bakerb6043c22015-05-19 16:39:48 -07001365 def save(self, *args, **kwargs):
1366 if self.cleaned_data['profile']:
1367 self.instance.apply_profile(self.cleaned_data['profile'])
1368
1369 return super(UserChangeForm, self).save(*args, **kwargs)
1370
Scott Baker67db95f2015-02-18 15:50:11 -08001371class UserDashboardViewInline(XOSTabularInline):
Scott Baker2c3cb642014-05-19 17:55:56 -07001372 model = UserDashboardView
1373 extra = 0
1374 suit_classes = 'suit-tab suit-tab-dashboards'
1375 fields = ['user', 'dashboardView', 'order']
1376
Scott Baker67db95f2015-02-18 15:50:11 -08001377class ControllerUserInline(XOSTabularInline):
Tony Mack30dfcd72015-01-10 23:08:10 -05001378 model = ControllerUser
1379 extra = 0
1380 suit_classes = 'suit-tab suit-tab-admin-only'
1381 fields = ['controller', 'user', 'kuser_id']
1382 readonly_fields=['controller']
1383
1384
Scott Baker3a8aed62015-02-27 12:21:22 -08001385class UserAdmin(XOSAdminMixin, UserAdmin):
1386 # Note: Make sure XOSAdminMixin is listed before
Scott Bakerf4aeedc2014-10-03 13:10:47 -07001387 # admin.ModelAdmin in the class declaration.
1388
Siobhan Tully53437282013-04-26 19:30:27 -04001389 class Meta:
1390 app_label = "core"
1391
1392 # The forms to add and change user instances
1393 form = UserChangeForm
1394 add_form = UserCreationForm
1395
1396 # The fields to be used in displaying the User model.
1397 # These override the definitions on the base UserAdmin
1398 # that reference specific fields on auth.User.
Scott Bakera111f442015-01-24 13:33:26 -08001399 list_display = ('backend_status_icon', 'email', 'firstname', 'lastname', 'site', 'last_login')
1400 list_display_links = ("email",)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001401 list_filter = ('site',)
Scott Baker90472612015-01-29 10:55:53 -08001402 inlines = [SlicePrivilegeInline,SitePrivilegeInline]
Tony Mack30dfcd72015-01-10 23:08:10 -05001403 admin_inlines = [ControllerUserInline]
Scott Baker6da26a52015-06-10 16:14:58 -07001404 fieldListLoginDetails = ['backend_status_text', 'email', 'site','password','is_active','is_readonly','is_admin','is_appuser', 'public_key', 'login_page', 'profile']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001405 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1406
Siobhan Tully53437282013-04-26 19:30:27 -04001407 fieldsets = (
Scott Baker034232d2015-05-15 09:19:36 -07001408 ('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 -04001409 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001410 #('Important dates', {'fields': ('last_login',)}),
1411 )
1412 add_fieldsets = (
1413 (None, {
1414 'classes': ('wide',),
Scott Baker034232d2015-05-15 09:19:36 -07001415 'fields': ('site', 'email', 'firstname', 'lastname', 'is_admin', 'is_readonly', 'is_appuser', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001416 ),
1417 )
Scott Baker40c00762014-08-21 16:55:59 -07001418 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001419 search_fields = ('email',)
1420 ordering = ('email',)
1421 filter_horizontal = ()
1422
Scott Baker3ca51f62014-05-23 12:05:11 -07001423 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001424
Scott Baker6a995352014-10-06 17:51:20 -07001425 @property
1426 def suit_form_tabs(self):
1427 if getattr(_thread_locals, "obj", None) is None:
1428 return []
1429 else:
Tony Mack30dfcd72015-01-10 23:08:10 -05001430 tabs = [('general','Login Details'),
Scott Baker6a995352014-10-06 17:51:20 -07001431 ('contact','Contact Information'),
1432 ('sliceprivileges','Slice Privileges'),
Scott Baker90472612015-01-29 10:55:53 -08001433 ('siteprivileges','Site Privileges')]
Tony Mack30dfcd72015-01-10 23:08:10 -05001434
1435 request=getattr(_thread_locals, "request", None)
1436 if request and request.user.is_admin:
1437 tabs.append( ('admin-only', 'Admin-Only') )
1438
1439 return tabs
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001440
Tony Mackc2835a92013-05-28 09:18:49 -04001441 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1442 if db_field.name == 'site':
Tony Mack02b8f142015-08-04 17:32:32 -04001443 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_users=True)
Tony Mackc2835a92013-05-28 09:18:49 -04001444
1445 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1446
Tony Mack5b061472014-02-04 07:57:10 -05001447 def queryset(self, request):
1448 return User.select_by_user(request.user)
1449
Tony Mackc8f443d2015-01-25 21:58:30 -05001450 def get_form(self, request, obj=None, **kwargs):
Tony Mack03b92292015-01-28 12:37:12 -05001451 # copy login details list
1452 login_details_fields = list(self.fieldListLoginDetails)
Tony Mack933b2912015-01-28 12:49:58 -05001453 if not request.user.is_admin:
Scott Baker90472612015-01-29 10:55:53 -08001454 # only admins can see 'is_admin' and 'is_readonly' fields
Tony Mack03b92292015-01-28 12:37:12 -05001455 if 'is_admin' in login_details_fields:
1456 login_details_fields.remove('is_admin')
1457 if 'is_readonly' in login_details_fields:
Scott Bakerb6043c22015-05-19 16:39:48 -07001458 login_details_fields.remove('is_readonly')
1459 if 'is_appuser' in login_details_fields:
1460 login_details_fields.remove('is_admin')
1461 if 'profile' in login_details_fields:
1462 login_details_fields.remove('profile')
Tony Mack933b2912015-01-28 12:49:58 -05001463 #if len(request.user.siteprivileges.filter(role__role = 'pi')) > 0:
Scott Bakerb6043c22015-05-19 16:39:48 -07001464 # only admins and pis can change a user's site
Tony Mack933b2912015-01-28 12:49:58 -05001465 # self.readonly_fields = ('backend_status_text', 'site')
Tony Mack03b92292015-01-28 12:37:12 -05001466 self.fieldsets = (
1467 ('Login Details', {'fields': login_details_fields, 'classes':['suit-tab suit-tab-general']}),
1468 ('Contact Information', {'fields': self.fieldListContactInfo, 'classes':['suit-tab suit-tab-contact']}),
1469 )
Tony Mackc8f443d2015-01-25 21:58:30 -05001470 return super(UserAdmin, self).get_form(request, obj, **kwargs)
1471
Scott Baker67db95f2015-02-18 15:50:11 -08001472class ControllerDashboardViewInline(XOSTabularInline):
Scott Bakerf2c0c512014-12-22 17:35:34 -08001473 model = ControllerDashboardView
Scott Baker786a9c12014-12-19 16:41:12 -08001474 extra = 0
1475 fields = ["controller", "url"]
1476 suit_classes = 'suit-tab suit-tab-controllers'
1477
Scott Baker67db95f2015-02-18 15:50:11 -08001478class DashboardViewAdmin(XOSBaseAdmin):
Scott Baker2c3cb642014-05-19 17:55:56 -07001479 fieldsets = [('Dashboard View Details',
Scott Baker59248182015-02-17 13:34:32 -08001480 {'fields': ['backend_status_text', 'name', 'url', 'enabled', 'deployments'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001481 'classes': ['suit-tab suit-tab-general']})
1482 ]
Scott Baker2c44e6e2015-01-18 16:46:26 -08001483 list_display = ["name", "enabled", "url"]
Scott Baker40c00762014-08-21 16:55:59 -07001484 readonly_fields = ('backend_status_text', )
Scott Bakerf2c0c512014-12-22 17:35:34 -08001485 inlines = [ControllerDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001486
Scott Baker786a9c12014-12-19 16:41:12 -08001487 suit_form_tabs =(('general','Dashboard View Details'),
1488 ('controllers', 'Per-controller Dashboard Details'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001489
Scott Baker67db95f2015-02-18 15:50:11 -08001490class ServiceResourceInline(XOSTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001491 model = ServiceResource
1492 extra = 0
1493
Scott Baker67db95f2015-02-18 15:50:11 -08001494class ServiceClassAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001495 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1496 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001497 inlines = [ServiceResourceInline]
1498
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001499 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1500 user_readonly_inlines = []
1501
Scott Baker67db95f2015-02-18 15:50:11 -08001502class ReservedResourceInline(XOSTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001503 model = ReservedResource
1504 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001505 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001506
1507 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1508 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1509
1510 if db_field.name == 'resource':
1511 # restrict resources to those that the slice's service class allows
1512 if request._slice is not None:
1513 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1514 if len(field.queryset) > 0:
1515 field.initial = field.queryset.all()[0]
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -05001516 else:
1517 field.queryset = field.queryset.none()
1518 elif db_field.name == 'sliver':
1519 # restrict slivers to those that belong to the slice
1520 if request._slice is not None:
Scott Baker133c9212013-05-17 09:09:11 -07001521 field.queryset = field.queryset.filter(slice = request._slice)
1522 else:
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -05001523 field.queryset = field.queryset.none()
1524
Scott Baker133c9212013-05-17 09:09:11 -07001525 return field
1526
Tony Mack5b061472014-02-04 07:57:10 -05001527 def queryset(self, request):
1528 return ReservedResource.select_by_user(request.user)
1529
Scott Baker133c9212013-05-17 09:09:11 -07001530class ReservationChangeForm(forms.ModelForm):
1531 class Meta:
1532 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001533 widgets = {
1534 'slice' : LinkedSelect
1535 }
Scott Baker133c9212013-05-17 09:09:11 -07001536
1537class ReservationAddForm(forms.ModelForm):
1538 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1539 refresh = forms.CharField(widget=forms.HiddenInput())
1540
1541 class Media:
Scott Baker06868952015-02-18 15:15:58 -08001542 css = {'all': ('xos.css',)} # .field-refresh { display: none; }
Scott Baker133c9212013-05-17 09:09:11 -07001543
1544 def clean_slice(self):
1545 slice = self.cleaned_data.get("slice")
1546 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1547 if len(x) == 0:
1548 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1549 return slice
1550
1551 class Meta:
1552 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001553 widgets = {
1554 'slice' : LinkedSelect
1555 }
1556
Scott Baker133c9212013-05-17 09:09:11 -07001557
1558class ReservationAddRefreshForm(ReservationAddForm):
1559 """ This form is displayed when the Reservation Form receives an update
1560 from the Slice dropdown onChange handler. It doesn't validate the
1561 data and doesn't save the data. This will cause the form to be
1562 redrawn.
1563 """
1564
Scott Baker8737e5f2013-05-17 09:35:32 -07001565 """ don't validate anything other than slice """
1566 dont_validate_fields = ("startTime", "duration")
1567
Scott Baker133c9212013-05-17 09:09:11 -07001568 def full_clean(self):
1569 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001570
1571 for fieldname in self.dont_validate_fields:
1572 if fieldname in self._errors:
1573 del self._errors[fieldname]
1574
Scott Baker133c9212013-05-17 09:09:11 -07001575 return result
1576
1577 """ don't save anything """
1578 def is_valid(self):
1579 return False
1580
Scott Baker67db95f2015-02-18 15:50:11 -08001581class ReservationAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001582 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001583 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001584 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001585 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001586 form = ReservationAddForm
1587
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001588 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1589
1590 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001591 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001592
Scott Baker133c9212013-05-17 09:09:11 -07001593 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001594 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001595 request._refresh = False
1596 request._slice = None
1597 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001598 # "refresh" will be set to "1" if the form was submitted due to
1599 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001600 if request.POST.get("refresh","1") == "1":
1601 request._refresh = True
1602 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001603
1604 # Keep track of the slice that was selected, so the
1605 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001606 request._slice = request.POST.get("slice",None)
1607 if (request._slice is not None):
1608 request._slice = Slice.objects.get(id=request._slice)
1609
1610 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1611 return result
1612
Scott Bakeracd45142013-05-19 16:19:16 -07001613 def changelist_view(self, request, extra_context = None):
1614 timezone.activate(request.user.timezone)
1615 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1616
Scott Baker133c9212013-05-17 09:09:11 -07001617 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001618 request._obj_ = obj
1619 if obj is not None:
1620 # For changes, set request._slice to the slice already set in the
1621 # object.
1622 request._slice = obj.slice
1623 self.form = ReservationChangeForm
1624 else:
1625 if getattr(request, "_refresh", False):
1626 self.form = ReservationAddRefreshForm
1627 else:
1628 self.form = ReservationAddForm
1629 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1630
Scott Baker133c9212013-05-17 09:09:11 -07001631 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001632 if (obj is not None):
1633 # Prevent slice from being changed after the reservation has been
1634 # created.
1635 return ['slice']
1636 else:
Scott Baker133c9212013-05-17 09:09:11 -07001637 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001638
Tony Mack5b061472014-02-04 07:57:10 -05001639 def queryset(self, request):
1640 return Reservation.select_by_user(request.user)
1641
Scott Baker67db95f2015-02-18 15:50:11 -08001642class NetworkParameterTypeAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001643 list_display = ("backend_status_icon", "name", )
1644 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001645 user_readonly_fields = ['name']
1646 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001647
Scott Baker67db95f2015-02-18 15:50:11 -08001648class RouterAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001649 list_display = ("backend_status_icon", "name", )
1650 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001651 user_readonly_fields = ['name']
1652 user_readonly_inlines = []
1653
Scott Baker67db95f2015-02-18 15:50:11 -08001654class RouterInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001655 model = Router.networks.through
1656 extra = 0
1657 verbose_name_plural = "Routers"
1658 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001659 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001660
Scott Bakerb27b62c2014-08-15 16:29:16 -07001661class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001662 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001663 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001664 verbose_name_plural = "Parameters"
1665 verbose_name = "Parameter"
1666 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001667 fields = ['backend_status_icon', 'parameter', 'value']
1668 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001669
Scott Bakerfbade882015-08-25 18:00:15 -07001670class NetworkPortInline(XOSTabularInline):
1671 fields = ['backend_status_icon', 'network', 'sliver', 'ip']
Scott Baker40c00762014-08-21 16:55:59 -07001672 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker5a7d9312015-08-26 09:43:33 -07001673 model = Port
Scott Baker874936e2014-01-13 18:15:34 -08001674 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001675 extra = 0
Scott Baker5f1068a2015-08-25 17:11:30 -07001676 verbose_name_plural = "Ports"
1677 verbose_name = "Port"
1678 suit_classes = 'suit-tab suit-tab-ports'
Scott Baker74d8e622013-07-29 16:04:22 -07001679
Scott Baker67db95f2015-02-18 15:50:11 -08001680class NetworkSlicesInline(XOSTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001681 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001682 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001683 extra = 0
1684 verbose_name_plural = "Slices"
1685 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001686 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001687 fields = ['backend_status_icon', 'network','slice']
1688 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001689
Scott Baker67db95f2015-02-18 15:50:11 -08001690class ControllerNetworkInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -05001691 model = ControllerNetwork
Scott Baker8806cdf2014-10-17 16:27:23 -07001692 extra = 0
Tony Mack06c8e472014-11-30 15:53:08 -05001693 verbose_name_plural = "Controller Networks"
1694 verbose_name = "Controller Network"
Scott Baker8806cdf2014-10-17 16:27:23 -07001695 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack06c8e472014-11-30 15:53:08 -05001696 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Baker8806cdf2014-10-17 16:27:23 -07001697 readonly_fields = ('backend_status_icon', )
1698
Scott Baker69e045d2014-11-17 23:44:03 -08001699class NetworkForm(forms.ModelForm):
1700 class Meta:
1701 model = Network
1702 widgets = {
1703 'topologyParameters': UploadTextareaWidget,
1704 'controllerParameters': UploadTextareaWidget,
1705 }
1706
Scott Baker67db95f2015-02-18 15:50:11 -08001707class NetworkAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001708 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1709 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001710 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001711
Scott Bakerfbade882015-08-25 18:00:15 -07001712 inlines = [NetworkParameterInline, NetworkPortInline, NetworkSlicesInline, RouterInline]
Tony Mack3066a952015-01-05 22:48:11 -05001713 admin_inlines = [ControllerNetworkInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001714
Scott Baker69e045d2014-11-17 23:44:03 -08001715 form=NetworkForm
1716
Siobhan Tully2d95e482013-09-06 10:56:06 -04001717 fieldsets = [
Scott Baker3789cb22015-08-21 16:40:53 -07001718 (None, {'fields': ['backend_status_text', 'name','template','ports','labels',
1719 'owner','guaranteed_bandwidth', 'permit_all_slices',
1720 'permitted_slices','network_id','router_id','subnet_id',
1721 'subnet', 'autoconnect'],
Scott Baker3e28dd72014-11-17 16:04:45 -08001722 'classes':['suit-tab suit-tab-general']}),
Scott Baker549aa252015-01-03 12:29:29 -08001723 (None, {'fields': ['topology_parameters', 'controller_url', 'controller_parameters'],
Scott Baker3e28dd72014-11-17 16:04:45 -08001724 'classes':['suit-tab suit-tab-sdn']}),
1725 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001726
Scott Baker40c00762014-08-21 16:55:59 -07001727 readonly_fields = ('backend_status_text', )
Scott Baker3789cb22015-08-21 16:40:53 -07001728 user_readonly_fields = ['name','template','ports','labels','owner','guaranteed_bandwidth',
1729 'permit_all_slices','permitted_slices','network_id','router_id',
1730 'subnet_id','subnet','autoconnect']
Siobhan Tully2d95e482013-09-06 10:56:06 -04001731
Scott Baker8806cdf2014-10-17 16:27:23 -07001732 @property
1733 def suit_form_tabs(self):
1734 tabs=[('general','Network Details'),
Scott Baker3e28dd72014-11-17 16:04:45 -08001735 ('sdn', 'SDN Configuration'),
Scott Baker8806cdf2014-10-17 16:27:23 -07001736 ('netparams', 'Parameters'),
Scott Baker5f1068a2015-08-25 17:11:30 -07001737 ('ports','Ports'),
Scott Baker8806cdf2014-10-17 16:27:23 -07001738 ('networkslices','Slices'),
1739 ('routers','Routers'),
1740 ]
1741
1742 request=getattr(_thread_locals, "request", None)
1743 if request and request.user.is_admin:
1744 tabs.append( ('admin-only', 'Admin-Only') )
1745
1746 return tabs
1747
1748
Scott Baker67db95f2015-02-18 15:50:11 -08001749class NetworkTemplateAdmin(XOSBaseAdmin):
Scott Baker369f9b92015-01-03 12:03:38 -08001750 list_display = ("backend_status_icon", "name", "guaranteed_bandwidth", "visibility")
Scott Baker63d1a552014-08-21 15:19:07 -07001751 list_display_links = ('backend_status_icon', 'name', )
Scott Baker369f9b92015-01-03 12:03:38 -08001752 user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001753 user_readonly_inlines = []
Scott Baker3e28dd72014-11-17 16:04:45 -08001754 fieldsets = [
Scott Baker369f9b92015-01-03 12:03:38 -08001755 (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 -08001756 'classes':['suit-tab suit-tab-general']}),]
1757 suit_form_tabs = (('general','Network Template Details'), )
Scott Baker74d8e622013-07-29 16:04:22 -07001758
Scott Baker67db95f2015-02-18 15:50:11 -08001759class FlavorAdmin(XOSBaseAdmin):
Scott Baker37b47902014-09-02 14:37:41 -07001760 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1761 list_display_links = ("backend_status_icon", "name")
1762 user_readonly_fields = ("name", "flavor")
1763 fields = ("name", "description", "flavor", "order", "default")
1764
Tony Mack31c2b8f2013-04-26 20:01:42 -04001765# register a signal that caches the user's credentials when they log in
1766def cache_credentials(sender, user, request, **kwds):
1767 auth = {'username': request.POST['username'],
1768 'password': request.POST['password']}
1769 request.session['auth'] = auth
1770user_logged_in.connect(cache_credentials)
1771
Scott Baker15cddfa2013-12-09 13:45:19 -08001772def dollar_field(fieldName, short_description):
1773 def newFunc(self, obj):
1774 try:
1775 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1776 except:
1777 x=getattr(obj, fieldName, 0.0)
1778 return x
1779 newFunc.short_description = short_description
1780 return newFunc
1781
1782def right_dollar_field(fieldName, short_description):
1783 def newFunc(self, obj):
1784 try:
1785 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1786 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1787 except:
1788 x=getattr(obj, fieldName, 0.0)
1789 return x
1790 newFunc.short_description = short_description
1791 newFunc.allow_tags = True
1792 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001793
Scott Baker67db95f2015-02-18 15:50:11 -08001794class InvoiceChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001795 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001796 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001797 verbose_name_plural = "Charges"
1798 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001799 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001800 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1801 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1802 can_delete = False
1803 max_num = 0
1804
1805 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001806
1807class InvoiceAdmin(admin.ModelAdmin):
1808 list_display = ("date", "account")
1809
1810 inlines = [InvoiceChargeInline]
1811
Scott Baker9cb88a22013-12-09 18:56:00 -08001812 fields = ["date", "account", "dollar_amount"]
1813 readonly_fields = ["date", "account", "dollar_amount"]
1814
1815 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001816
Scott Baker67db95f2015-02-18 15:50:11 -08001817class InvoiceInline(XOSTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001818 model = Invoice
1819 extra = 0
1820 verbose_name_plural = "Invoices"
1821 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001822 fields = ["date", "dollar_amount"]
1823 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001824 suit_classes = 'suit-tab suit-tab-accountinvoice'
1825 can_delete=False
1826 max_num=0
1827
1828 dollar_amount = right_dollar_field("amount", "Amount")
1829
Scott Baker67db95f2015-02-18 15:50:11 -08001830class PendingChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001831 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001832 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001833 verbose_name_plural = "Charges"
1834 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001835 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001836 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1837 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001838 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001839 can_delete=False
1840 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001841
1842 def queryset(self, request):
1843 qs = super(PendingChargeInline, self).queryset(request)
1844 qs = qs.filter(state="pending")
1845 return qs
1846
Scott Baker15cddfa2013-12-09 13:45:19 -08001847 dollar_amount = right_dollar_field("amount", "Amount")
1848
Scott Baker67db95f2015-02-18 15:50:11 -08001849class PaymentInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001850 model=Payment
1851 extra = 1
1852 verbose_name_plural = "Payments"
1853 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001854 fields = ["date", "dollar_amount"]
1855 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001856 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001857 can_delete=False
1858 max_num=0
1859
1860 dollar_amount = right_dollar_field("amount", "Amount")
1861
Scott Baker43105042013-12-06 23:23:36 -08001862class AccountAdmin(admin.ModelAdmin):
1863 list_display = ("site", "balance_due")
1864
1865 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1866
1867 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001868 (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 -08001869
Scott Baker15cddfa2013-12-09 13:45:19 -08001870 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001871
1872 suit_form_tabs =(
1873 ('general','Account Details'),
1874 ('accountinvoice', 'Invoices'),
1875 ('accountpayments', 'Payments'),
1876 ('accountpendingcharges','Pending Charges'),
1877 )
1878
Scott Baker15cddfa2013-12-09 13:45:19 -08001879 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1880 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1881 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1882
Scott Bakerc24f86d2015-08-14 09:10:11 -07001883class ProgramForm(forms.ModelForm):
1884 class Meta:
1885 model = Program
1886 widgets = {
1887 'contents': UploadTextareaWidget(attrs={'rows': 20, 'cols': 80, 'class': "input-xxlarge"}),
1888 'description': forms.Textarea(attrs={'rows': 3, 'cols': 80, 'class': 'input-xxlarge'}),
1889 'messages': forms.Textarea(attrs={'rows': 20, 'cols': 80, 'class': 'input-xxlarge'}),
1890 'output': forms.Textarea(attrs={'rows': 3, 'cols': 80, 'class': 'input-xxlarge'})
1891 }
1892
1893class ProgramAdmin(XOSBaseAdmin):
1894 list_display = ("name", "status")
1895 list_display_links = ('name', "status")
1896
1897 form=ProgramForm
1898
1899 fieldsets = [
1900 (None, {'fields': ['name', 'command', 'kind', 'description', 'output', 'status'],
1901 'classes':['suit-tab suit-tab-general']}),
1902 (None, {'fields': ['contents'],
1903 'classes':['suit-tab suit-tab-contents']}),
1904 (None, {'fields': ['messages'],
1905 'classes':['suit-tab suit-tab-messages']}),
1906 ]
1907
1908 readonly_fields = ("status",)
1909
1910 @property
1911 def suit_form_tabs(self):
1912 tabs=[('general','Program Details'),
1913 ('contents','Program Source'),
1914 ('messages','Messages'),
1915 ]
1916
1917 request=getattr(_thread_locals, "request", None)
1918 if request and request.user.is_admin:
1919 tabs.append( ('admin-only', 'Admin-Only') )
1920
1921 return tabs
1922
Siobhan Tully53437282013-04-26 19:30:27 -04001923# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001924admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001925# ... and, since we're not using Django's builtin permissions,
1926# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001927#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001928
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001929#Do not show django evolution in the admin interface
1930from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001931#admin.site.unregister(Version)
1932#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001933
1934
1935# When debugging it is often easier to see all the classes, but for regular use
1936# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001937showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001938
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001939admin.site.register(Deployment, DeploymentAdmin)
Tony Mack06c8e472014-11-30 15:53:08 -05001940admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001941admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001942admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001943admin.site.register(Service, ServiceAdmin)
Tony Mack450b6e02015-01-25 12:35:29 -05001944#admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001945admin.site.register(Network, NetworkAdmin)
1946admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001947admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Scott Bakerc24f86d2015-08-14 09:10:11 -07001948admin.site.register(Program, ProgramAdmin)
Tony Mack450b6e02015-01-25 12:35:29 -05001949#admin.site.register(Account, AccountAdmin)
1950#admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001951
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001952if True:
1953 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1954 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001955 admin.site.register(Tag, TagAdmin)
Tony Mack06c8e472014-11-30 15:53:08 -05001956 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001957 admin.site.register(SiteRole)
1958 admin.site.register(SliceRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001959 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001960 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1961 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001962 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001963 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001964 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001965 admin.site.register(Flavor, FlavorAdmin)
Scott Bakerb3cf9212015-07-06 14:40:20 -07001966 admin.site.register(TenantRoot, TenantRootAdmin)
1967 admin.site.register(TenantRootRole, TenantRootRoleAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001968