blob: f0d402e990d83f2900578c9dfbee3f33c7406301 [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 Baker9f6b8ed2014-11-17 23:44:03 -080010from django.contrib.admin.widgets import FilteredSelectMultiple, AdminTextareaWidget
Scott Bakercbfb6002014-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 Mack7b6400d2015-02-16 19:54:24 -050017from django.core.urlresolvers import reverse, resolve, NoReverseMatch
Scott Baker9f6b8ed2014-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 Baker7a056af2015-02-26 20:42:11 -080020from django.utils.text import capfirst
Scott Baker9f6b8ed2014-11-17 23:44:03 -080021from django.forms.utils import flatatt, to_current_timezone
Scott Baker2d281a92015-07-09 19:06:08 -070022from django.core.exceptions import PermissionDenied, ValidationError
Scott Baker3cde7372014-10-21 21:03:08 -070023from cgi import escape as html_escape
Scott Baker2d281a92015-07-09 19:06:08 -070024from django.contrib import messages
Tony Mack7130ac32013-03-22 21:58:00 -040025
Scott Baker0a5633b2014-10-06 17:51:20 -070026import threading
27
28# thread locals necessary to work around a django-suit issue
29_thread_locals = threading.local()
Scott Baker36f50872014-08-21 13:01:25 -070030
Scott Bakerb6b474d2015-02-10 18:24:20 -080031ICON_URLS = {"success": "/static/admin/img/icon_success.gif",
32 "clock": "/static/admin/img/icon_clock.gif",
33 "error": "/static/admin/img/icon_error.gif"}
34
35def backend_icon(obj):
36 (icon, tooltip) = obj.get_backend_icon()
Sapan Bhatia5ebfe8c2015-12-22 18:37:47 +010037
Scott Bakerb6b474d2015-02-10 18:24:20 -080038 icon_url = ICON_URLS.get(icon, "unknown")
39
Sapan Bhatia5ebfe8c2015-12-22 18:37:47 +010040 (exponent,last_success,last_failure,failures) = obj.get_backend_details()
41
Sapan Bhatia0253cf22015-12-22 18:38:36 +010042 # FIXME: Need to clean this up by separating Javascript from Python
Sapan Bhatia5ebfe8c2015-12-22 18:37:47 +010043 if (obj.pk):
44 script = """
45 <script type="text/javascript">
46 $(document).ready(function () {
47 $("#show_details_%d").click(function () {
48 $("#status%d").dialog({modal: true, height: 200, width: 200 });
49 });
50 });
51 </script>
52 """%(obj.pk,obj.pk)
53
54 div = """
55 <div style="display:none;" id="status%d" title="Details">
56 <p>Backoff Exponent: %r</p>
57 <p>Last Success: %r</p>
58 <p>Failures: %r</p>
59 <p>Last Failure: %r</p>
60 </div>
61 """%(obj.pk,exponent,last_success,failures,last_failure)
62 a = '<a id="show_details_%d" href="#">'%obj.pk
63 astop = '</a>'
64 else:
65 div = ''
66 script = ''
67 a = ''
68 astop = ''
69
Scott Bakerb6b474d2015-02-10 18:24:20 -080070 if tooltip:
Sapan Bhatia5ebfe8c2015-12-22 18:37:47 +010071 return '%s %s <span style="min-width:16px;" title="%s">%s<img src="%s">%s</span>' % (script, div, tooltip, a, icon_url, astop)
Scott Baker40c00762014-08-21 16:55:59 -070072 else:
Scott Bakerb6b474d2015-02-10 18:24:20 -080073 return '<span style="min-width:16px;"><img src="%s"></span>' % icon_url
Scott Baker40c00762014-08-21 16:55:59 -070074
75def backend_text(obj):
Scott Bakerb6b474d2015-02-10 18:24:20 -080076 (icon, tooltip) = obj.get_backend_icon()
77 icon_url = ICON_URLS.get(icon, "unknown")
78
79 return '<img src="%s"> %s' % (icon_url, tooltip)
Scott Baker63d1a552014-08-21 15:19:07 -070080
Scott Baker9f6b8ed2014-11-17 23:44:03 -080081class UploadTextareaWidget(AdminTextareaWidget):
82 def render(self, name, value, attrs=None):
83 if value is None:
S.Çağlar Onur0e591832015-02-24 17:28:09 -050084 value = ''
85 final_attrs = self.build_attrs(attrs, name=name)
86 return format_html('<input type="file" style="width: 0; height: 0" id="btn_upload_%s" onChange="uploadTextarea(event,\'%s\');">' \
87 '<button onClick="$(\'#btn_upload_%s\').click(); return false;">Upload</button>' \
88 '<br><textarea{0}>\r\n{1}</textarea>' % (attrs["id"], attrs["id"], attrs["id"]),
89 flatatt(final_attrs),
Scott Baker9f6b8ed2014-11-17 23:44:03 -080090 force_text(value))
91
Scott Bakerb92b5c72015-05-11 16:36:58 -070092class SliderWidget(forms.HiddenInput):
93 def render(self, name, value, attrs=None):
94 if value is None:
95 value = '0'
96 final_attrs = self.build_attrs(attrs, name=name)
97 attrs = attrs or attrs[:]
98 attrs["name"] = name
99 attrs["value"] = value
100 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>
101 <script>
102 $(function() {
103 $("#%(id)s_slider").slider({
104 value: %(value)s,
105 slide: function(event, ui) { $("#%(id)s").val( ui.value ); $("#%(id)s_label").html(ui.value); },
106 });
107 });
108 </script>
109 <input type="hidden" id="%(id)s" name="%(name)s" value="%(value)s"></input>
110 """ % attrs
111 html = html.replace("{","{{").replace("}","}}")
112 return format_html(html,
113 flatatt(final_attrs),
114 force_text(value))
115
116
Scott Baker36f50872014-08-21 13:01:25 -0700117class PlainTextWidget(forms.HiddenInput):
118 input_type = 'hidden'
119
120 def render(self, name, value, attrs=None):
121 if value is None:
122 value = ''
123 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
124
Scott Bakera9412c32015-02-27 12:21:22 -0800125class XOSAdminMixin(object):
Scott Bakercbfb6002014-10-03 00:32:37 -0700126 # call save_by_user and delete_by_user instead of save and delete
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500127
128 def has_add_permission(self, request, obj=None):
129 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -0700130
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500131 def has_delete_permission(self, request, obj=None):
132 return (not self.__user_is_readonly(request))
133
134 def save_model(self, request, obj, form, change):
135 if self.__user_is_readonly(request):
Scott Bakercbfb6002014-10-03 00:32:37 -0700136 # this 'if' might be redundant if save_by_user is implemented right
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500137 raise PermissionDenied
Scott Bakercbfb6002014-10-03 00:32:37 -0700138
Scott Bakerbf08f2f2015-12-01 10:24:01 -0800139 # reset exponential backoff
140 if hasattr(obj, "backend_register"):
141 obj.backend_register = "{}"
142
Scott Bakercbfb6002014-10-03 00:32:37 -0700143 obj.caller = request.user
144 # update openstack connection to use this site/tenant
145 obj.save_by_user(request.user)
146
147 def delete_model(self, request, obj):
148 obj.delete_by_user(request.user)
149
150 def save_formset(self, request, form, formset, change):
151 instances = formset.save(commit=False)
152 for instance in instances:
Scott Bakercf9cbad2015-07-08 18:23:17 -0700153 instance.caller = request.user
Scott Bakercbfb6002014-10-03 00:32:37 -0700154 instance.save_by_user(request.user)
155
156 # BUG in django 1.7? Objects are not deleted by formset.save if
157 # commit is False. So let's delete them ourselves.
158 #
159 # code from forms/models.py save_existing_objects()
160 try:
S.Çağlar Onur0e591832015-02-24 17:28:09 -0500161 forms_to_delete = formset.deleted_forms
162 except AttributeError:
Scott Bakercbfb6002014-10-03 00:32:37 -0700163 forms_to_delete = []
164 if formset.initial_forms:
165 for form in formset.initial_forms:
166 obj = form.instance
167 if form in forms_to_delete:
168 if obj.pk is None:
169 continue
170 formset.deleted_objects.append(obj)
171 obj.delete()
172
173 formset.save_m2m()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500174
175 def get_actions(self,request):
Scott Bakera9412c32015-02-27 12:21:22 -0800176 actions = super(XOSAdminMixin,self).get_actions(request)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500177
178 if self.__user_is_readonly(request):
179 if 'delete_selected' in actions:
180 del actions['delete_selected']
181
182 return actions
183
Scott Bakerff3c2d22015-04-03 17:44:31 -0700184 def url_for_model_changelist(self, request, model):
185 # used in add_extra_context
186 return reverse('admin:%s_%s_changelist' % (model._meta.app_label, model._meta.model_name), current_app=model._meta.app_label)
187
Scott Bakerba534e72015-04-02 22:32:40 -0700188 def add_extra_context(self, request, extra_context):
Scott Bakerc481b322015-02-27 12:12:14 -0800189 # allow custom application breadcrumb url and name
190 extra_context["custom_app_breadcrumb_url"] = getattr(self, "custom_app_breadcrumb_url", None)
191 extra_context["custom_app_breadcrumb_name"] = getattr(self, "custom_app_breadcrumb_name", None)
Scott Baker0998bcc2015-04-02 22:07:18 -0700192 extra_context["custom_changelist_breadcrumb_url"] = getattr(self, "custom_changelist_breadcrumb_url", None)
Scott Bakerc481b322015-02-27 12:12:14 -0800193
194 # for Service admins to render their Administration page
195 if getattr(self, "extracontext_registered_admins", False):
196 admins=[]
Sapan Bhatia67ea0d72016-01-14 11:41:38 -0500197 for model, model_admin in admin.site._registry.items():
198 if model == self.model:
199 continue
200 if model._meta.app_label == self.model._meta.app_label:
201 info = {"app": model._meta.app_label,
202 "model": model._meta.model_name,
203 "name": capfirst(model._meta.verbose_name_plural),
204 "url": self.url_for_model_changelist(request,model) }
205 admins.append(info)
Scott Bakerc481b322015-02-27 12:12:14 -0800206 extra_context["registered_admins"] = admins
207
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500208 def change_view(self,request,object_id, extra_context=None):
Scott Bakerc481b322015-02-27 12:12:14 -0800209 extra_context = extra_context or {}
210
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500211 if self.__user_is_readonly(request):
S.Çağlar Onur0e591832015-02-24 17:28:09 -0500212 if not hasattr(self, "readonly_save"):
213 # save the original readonly fields
214 self.readonly_save = self.readonly_fields
215 self.inlines_save = self.inlines
216 if hasattr(self, "user_readonly_fields"):
217 self.readonly_fields=self.user_readonly_fields
218 if hasattr(self, "user_readonly_inlines"):
219 self.inlines = self.user_readonly_inlines
220 else:
221 if hasattr(self, "readonly_save"):
222 # restore the original readonly fields
223 self.readonly_fields = self.readonly_save
224 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -0700225 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500226
Scott Bakerba534e72015-04-02 22:32:40 -0700227 self.add_extra_context(request, extra_context)
Scott Bakerc481b322015-02-27 12:12:14 -0800228
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500229 try:
Scott Bakera9412c32015-02-27 12:21:22 -0800230 return super(XOSAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500231 except PermissionDenied:
232 pass
Scott Baker2d281a92015-07-09 19:06:08 -0700233 except ValidationError as e:
234 if (e.params is None):
235 # Validation errors that don't reference a specific field will
236 # often throw a non-descriptive 500 page to the user. The code
237 # below will cause an error message to be printed and the
238 # page refreshed instead.
239 # As a side-effect it turns the request back into a 'GET' which
240 # may wipe anything the user had changed on the page. But, at
241 # least the user gets a real error message.
242 # TODO: revisit this and display some kind of error view
243 request.method = 'GET'
244 messages.error(request, e.message)
245 return super(XOSAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
246 else:
247 raise
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500248 if request.method == 'POST':
249 raise PermissionDenied
250 request.readonly = True
Scott Bakera9412c32015-02-27 12:21:22 -0800251 return super(XOSAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500252
Scott Bakerc481b322015-02-27 12:12:14 -0800253 def changelist_view(self, request, extra_context = None):
254 extra_context = extra_context or {}
255
Scott Bakerba534e72015-04-02 22:32:40 -0700256 self.add_extra_context(request, extra_context)
Scott Bakerc481b322015-02-27 12:12:14 -0800257
Scott Bakera9412c32015-02-27 12:21:22 -0800258 return super(XOSAdminMixin, self).changelist_view(request, extra_context=extra_context)
Scott Bakerc481b322015-02-27 12:12:14 -0800259
Scott Baker2aea0362015-04-14 17:01:18 -0700260 def add_view(self, request, form_url='', extra_context = None):
Scott Bakerff3c2d22015-04-03 17:44:31 -0700261 extra_context = extra_context or {}
262
263 self.add_extra_context(request, extra_context)
264
Scott Baker2aea0362015-04-14 17:01:18 -0700265 return super(XOSAdminMixin, self).add_view(request, form_url, extra_context=extra_context)
Scott Bakerff3c2d22015-04-03 17:44:31 -0700266
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500267 def __user_is_readonly(self, request):
268 return request.user.isReadOnlyUser()
269
Scott Baker40c00762014-08-21 16:55:59 -0700270 def backend_status_text(self, obj):
271 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -0700272
Scott Baker63d1a552014-08-21 15:19:07 -0700273 def backend_status_icon(self, obj):
Scott Baker40c00762014-08-21 16:55:59 -0700274 return mark_safe(backend_icon(obj))
Scott Baker63d1a552014-08-21 15:19:07 -0700275 backend_status_icon.short_description = ""
276
Scott Baker24ded6a2014-11-05 09:05:38 -0800277 def get_form(self, request, obj=None, **kwargs):
Scott Baker5c432692014-10-16 00:57:55 -0700278 # Save obj and request in thread-local storage, so suit_form_tabs can
279 # use it to determine whether we're in edit or add mode, and can
280 # determine whether the user is an admin.
281 _thread_locals.request = request
282 _thread_locals.obj = obj
Scott Bakera9412c32015-02-27 12:21:22 -0800283 return super(XOSAdminMixin, self).get_form(request, obj, **kwargs)
Scott Baker5c432692014-10-16 00:57:55 -0700284
285 def get_inline_instances(self, request, obj=None):
Scott Bakera9412c32015-02-27 12:21:22 -0800286 inlines = super(XOSAdminMixin, self).get_inline_instances(request, obj)
Scott Baker5c432692014-10-16 00:57:55 -0700287
288 # inlines that should only be shown to an admin user
289 if request.user.is_admin:
290 for inline_class in getattr(self, "admin_inlines", []):
291 inlines.append(inline_class(self.model, self.admin_site))
292
293 return inlines
294
Scott Bakera9412c32015-02-27 12:21:22 -0800295class ReadOnlyAwareAdmin(XOSAdminMixin, admin.ModelAdmin):
296 # Note: Make sure XOSAdminMixin is listed before
Scott Baker86c83ab2014-10-03 13:10:47 -0700297 # admin.ModelAdmin in the class declaration.
298
Scott Bakercbfb6002014-10-03 00:32:37 -0700299 pass
300
Scott Baker022cdcd2015-02-18 15:50:11 -0800301class XOSBaseAdmin(ReadOnlyAwareAdmin):
Scott Bakercbfb6002014-10-03 00:32:37 -0700302 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700303
Scott Bakere8859f92014-05-23 12:42:40 -0700304class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400305 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700306 if not super(SingletonAdmin, self).has_add_permission(request):
307 return False
308
Siobhan Tullyce652d02013-10-08 21:52:35 -0400309 num_objects = self.model.objects.count()
310 if num_objects >= 1:
311 return False
312 else:
313 return True
314
Scott Baker7a056af2015-02-26 20:42:11 -0800315class ServiceAppAdmin (SingletonAdmin):
Scott Bakerc481b322015-02-27 12:12:14 -0800316 extracontext_registered_admins = True
Scott Baker7a056af2015-02-26 20:42:11 -0800317
Scott Baker022cdcd2015-02-18 15:50:11 -0800318class XOSTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800319 def __init__(self, *args, **kwargs):
Scott Baker022cdcd2015-02-18 15:50:11 -0800320 super(XOSTabularInline, self).__init__(*args, **kwargs)
Scott Baker86568322014-01-12 16:53:31 -0800321
322 # InlineModelAdmin as no get_fields() method, so in order to add
323 # the selflink field, we override __init__ to modify self.fields and
324 # self.readonly_fields.
325
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800326 self.setup_selflink()
327
Scott Bakerd5df9502015-07-24 09:32:14 -0700328 @property
329 def selflink_model(self):
330 if hasattr(self, "selflink_fieldname"):
331 """ self.selflink_model can be defined to punch through a relation
332 to its target object. For example, in SliceNetworkInline, set
333 selflink_model = "network", and the URL will lead to the Network
334 object instead of trying to bring up a change view of the
335 SliceNetwork object.
336 """
337 return getattr(self.model,self.selflink_fieldname).field.rel.to
338 else:
339 return self.model
340
341 @property
342 def selflink_reverse_path(self):
343 return "admin:%s_change" % (self.selflink_model._meta.db_table)
344
345 def get_change_url(self, id):
Scott Baker874936e2014-01-13 18:15:34 -0800346 """ Get the URL to a change form in the admin for this model """
Scott Baker29786782015-07-27 08:53:05 -0700347 reverse_path = self.selflink_reverse_path # "admin:%s_change" % (self.selflink_model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800348 try:
Scott Baker874936e2014-01-13 18:15:34 -0800349 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800350 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800351 return None
352
353 return url
354
355 def setup_selflink(self):
Scott Bakerd5df9502015-07-24 09:32:14 -0700356 url = self.get_change_url(0)
Scott Baker874936e2014-01-13 18:15:34 -0800357
358 # We don't have an admin for this object, so don't create the
359 # selflink.
360 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800361 return
362
Scott Baker874936e2014-01-13 18:15:34 -0800363 # Since we need to add "selflink" to the field list, we need to create
364 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800365 if (self.fields is None):
366 self.fields = []
367 for f in self.model._meta.fields:
368 if f.editable and f.name != "id":
369 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800370
Scott Baker874936e2014-01-13 18:15:34 -0800371 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800372
Scott Baker874936e2014-01-13 18:15:34 -0800373 if self.readonly_fields is None:
374 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800375
Scott Baker874936e2014-01-13 18:15:34 -0800376 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800377
378 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800379 if hasattr(self, "selflink_fieldname"):
380 obj = getattr(obj, self.selflink_fieldname)
381
Scott Baker86568322014-01-12 16:53:31 -0800382 if obj.id:
Scott Bakerd5df9502015-07-24 09:32:14 -0700383 url = self.get_change_url(obj.id)
Scott Baker874936e2014-01-13 18:15:34 -0800384 return "<a href='%s'>Details</a>" % str(url)
S.Çağlar Onur0e591832015-02-24 17:28:09 -0500385 else:
386 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800387
388 selflink.allow_tags = True
389 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400390
Scott Bakerb27b62c2014-08-15 16:29:16 -0700391 def has_add_permission(self, request):
392 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500393
394 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700395 readonly_fields = list(self.readonly_fields)[:]
396 if request.user.isReadOnlyUser():
397 for field in self.fields:
398 if not field in readonly_fields:
399 readonly_fields.append(field)
400 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500401
Scott Baker40c00762014-08-21 16:55:59 -0700402 def backend_status_icon(self, obj):
403 return mark_safe(backend_icon(obj))
404 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700405
Scott Bakerb27b62c2014-08-15 16:29:16 -0700406class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500407 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700408 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500409
Scott Bakerb27b62c2014-08-15 16:29:16 -0700410 def get_readonly_fields(self, request, obj=None):
411 readonly_fields = list(self.readonly_fields)[:]
412 if request.user.isReadOnlyUser():
413 for field in self.fields:
414 if not field in readonly_fields:
415 readonly_fields.append(field)
416 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500417
Scott Baker40c00762014-08-21 16:55:59 -0700418 def backend_status_icon(self, obj):
419 return mark_safe(backend_icon(obj))
420 backend_status_icon.short_description = ""
421
Scott Baker022cdcd2015-02-18 15:50:11 -0800422class ReservationInline(XOSTabularInline):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400423 model = Reservation
424 extra = 0
425 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700426
Tony Mack5b061472014-02-04 07:57:10 -0500427 def queryset(self, request):
428 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400429
Scott Bakerb27b62c2014-08-15 16:29:16 -0700430class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400431 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400432 extra = 0
433 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500434 fields = ['service', 'name', 'value']
435
436 def queryset(self, request):
437 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400438
Tony Mack3de59e32015-08-19 11:58:18 -0400439class InstanceInline(XOSTabularInline):
440 model = Instance
Scott Baker4103eb32015-09-21 14:52:15 -0700441 fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400442 extra = 0
Scott Baker4103eb32015-09-21 14:52:15 -0700443 max_num = 0
Scott Baker0befcd72015-09-21 15:10:18 -0700444 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node']
Tony Mack3de59e32015-08-19 11:58:18 -0400445 suit_classes = 'suit-tab suit-tab-instances'
Scott Baker74d8e622013-07-29 16:04:22 -0700446
Tony Mack5b061472014-02-04 07:57:10 -0500447 def queryset(self, request):
Tony Mack3de59e32015-08-19 11:58:18 -0400448 return Instance.select_by_user(request.user)
Tony Mack5b061472014-02-04 07:57:10 -0500449
Scott Bakerb24cc932014-06-09 10:51:16 -0700450 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Tony Mackbf6aa302014-12-26 13:38:02 -0500451 if db_field.name == 'deployment':
Tony Mackbd908ae2015-02-24 15:41:49 -0500452 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
Tony Mack3de59e32015-08-19 11:58:18 -0400453 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_deployment_changed(this);"})
Tony Mackbf6aa302014-12-26 13:38:02 -0500454 if db_field.name == 'flavor':
Tony Mack3de59e32015-08-19 11:58:18 -0400455 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700456
Tony Mack3de59e32015-08-19 11:58:18 -0400457 field = super(InstanceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700458
459 return field
460
Tony Mack3de59e32015-08-19 11:58:18 -0400461class CordInstanceInline(XOSTabularInline):
462 model = Instance
Scott Baker0e289fd2015-06-12 10:40:15 -0700463 fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'flavor', 'image', 'node']
464 extra = 0
465 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name']
Tony Mack3de59e32015-08-19 11:58:18 -0400466 suit_classes = 'suit-tab suit-tab-instances'
Scott Baker0e289fd2015-06-12 10:40:15 -0700467
468 def queryset(self, request):
Tony Mack3de59e32015-08-19 11:58:18 -0400469 return Instance.select_by_user(request.user)
Scott Baker0e289fd2015-06-12 10:40:15 -0700470
471 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
472 if db_field.name == 'deployment':
473
474 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
Tony Mack3de59e32015-08-19 11:58:18 -0400475 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_deployment_changed(this);"})
Scott Baker0e289fd2015-06-12 10:40:15 -0700476 if db_field.name == 'flavor':
Tony Mack3de59e32015-08-19 11:58:18 -0400477 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_flavor_changed(this);"})
Scott Baker0e289fd2015-06-12 10:40:15 -0700478
Tony Mack3de59e32015-08-19 11:58:18 -0400479 field = super(CordInstanceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Baker0e289fd2015-06-12 10:40:15 -0700480
481 return field
482
Scott Baker022cdcd2015-02-18 15:50:11 -0800483class SiteInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400484 model = Site
485 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400486 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400487
Tony Mack5b061472014-02-04 07:57:10 -0500488 def queryset(self, request):
489 return Site.select_by_user(request.user)
490
Tony Mack15136b52015-08-04 17:53:23 -0400491class SiteHostsNodesInline(SiteInline):
492 def queryset(self, request):
493 return Site.select_by_user(request.user).filter(hosts_nodes=True)
494
495class SiteHostsUsersInline(SiteInline):
496 def queryset(self, request):
497 return Site.select_by_user(request.user).filter(hosts_users=True)
498
Scott Baker022cdcd2015-02-18 15:50:11 -0800499class UserInline(XOSTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400500 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700501 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
502 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400503 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400504 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400505
Tony Mack5b061472014-02-04 07:57:10 -0500506 def queryset(self, request):
507 return User.select_by_user(request.user)
508
Scott Baker022cdcd2015-02-18 15:50:11 -0800509class SliceInline(XOSTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400510 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700511 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
512 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400513 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400514 suit_classes = 'suit-tab suit-tab-slices'
515
Tony Mack5b061472014-02-04 07:57:10 -0500516 def queryset(self, request):
517 return Slice.select_by_user(request.user)
518
Scott Baker022cdcd2015-02-18 15:50:11 -0800519class NodeInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400520 model = Node
521 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400522 suit_classes = 'suit-tab suit-tab-nodes'
Tony Mack93d1b032014-12-08 16:43:02 -0500523 fields = ['backend_status_icon', 'name', 'site_deployment']
Scott Baker40c00762014-08-21 16:55:59 -0700524 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400525
Scott Baker022cdcd2015-02-18 15:50:11 -0800526class DeploymentPrivilegeInline(XOSTabularInline):
Tony Mack93d1b032014-12-08 16:43:02 -0500527 model = DeploymentPrivilege
528 extra = 0
Tony Mack4ce14c42015-02-09 21:41:57 -0500529 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Tony Mack93d1b032014-12-08 16:43:02 -0500530 fields = ['backend_status_icon', 'user','role','deployment']
531 readonly_fields = ('backend_status_icon', )
532
533 def queryset(self, request):
534 return DeploymentPrivilege.select_by_user(request.user)
535
Scott Baker022cdcd2015-02-18 15:50:11 -0800536class ControllerSiteInline(XOSTabularInline):
Tony Macka7dbd422015-01-05 22:48:11 -0500537 model = ControllerSite
538 extra = 0
539 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack8f30ebe2015-01-06 15:08:20 -0500540 fields = ['controller', 'site', 'tenant_id']
Tony Macka7dbd422015-01-05 22:48:11 -0500541
542
Scott Baker022cdcd2015-02-18 15:50:11 -0800543class SitePrivilegeInline(XOSTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400544 model = SitePrivilege
545 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400546 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700547 fields = ['backend_status_icon', 'user','site', 'role']
548 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400549
Tony Mackc2835a92013-05-28 09:18:49 -0400550 def formfield_for_foreignkey(self, db_field, request, **kwargs):
551 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500552 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400553
554 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500555 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400556 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
557
Tony Mack5b061472014-02-04 07:57:10 -0500558 def queryset(self, request):
559 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400560
Tony Mack8d108e22015-05-11 20:39:32 -0400561
562class ServicePrivilegeInline(XOSTabularInline):
563 model = ServicePrivilege
564 extra = 0
565 suit_classes = 'suit-tab suit-tab-serviceprivileges'
566 fields = ['backend_status_icon', 'user','service', 'role']
567 readonly_fields = ('backend_status_icon', )
568
569 def formfield_for_foreignkey(self, db_field, request, **kwargs):
570 if db_field.name == 'service':
571 kwargs['queryset'] = Service.select_by_user(request.user)
Tony Mackcf88f8c2015-05-15 06:33:45 -0400572 if db_field.name == 'user':
573 kwargs['queryset'] = User.select_by_user(request.user)
574 return super(ServicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mack8d108e22015-05-11 20:39:32 -0400575
576 def queryset(self, request):
577 return ServicePrivilege.select_by_user(request.user)
578
Scott Baker022cdcd2015-02-18 15:50:11 -0800579class SiteDeploymentInline(XOSTabularInline):
Tony Macka7dbd422015-01-05 22:48:11 -0500580 model = SiteDeployment
Tony Macke4be32f2014-03-11 20:45:25 -0400581 extra = 0
Tony Mack10812252015-01-30 10:58:29 -0500582 suit_classes = 'suit-tab suit-tab-sitedeployments'
Tony Mack528d4222014-12-05 17:13:08 -0500583 fields = ['backend_status_icon', 'deployment','site', 'controller']
Scott Baker40c00762014-08-21 16:55:59 -0700584 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400585
586 def formfield_for_foreignkey(self, db_field, request, **kwargs):
587 if db_field.name == 'site':
588 kwargs['queryset'] = Site.select_by_user(request.user)
589
590 if db_field.name == 'deployment':
591 kwargs['queryset'] = Deployment.select_by_user(request.user)
Tony Mack528d4222014-12-05 17:13:08 -0500592
593 if db_field.name == 'controller':
Scott Baker4103eb32015-09-21 14:52:15 -0700594 if len(resolve(request.path).args) > 0:
595 kwargs['queryset'] = Controller.select_by_user(request.user).filter(deployment__id=int(resolve(request.path).args[0]))
Tony Mack528d4222014-12-05 17:13:08 -0500596
Tony Macka7dbd422015-01-05 22:48:11 -0500597 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Macke4be32f2014-03-11 20:45:25 -0400598
599 def queryset(self, request):
Tony Macka7dbd422015-01-05 22:48:11 -0500600 return SiteDeployment.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400601
602
Scott Baker022cdcd2015-02-18 15:50:11 -0800603class SlicePrivilegeInline(XOSTabularInline):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400604 model = SlicePrivilege
605 suit_classes = 'suit-tab suit-tab-sliceprivileges'
606 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700607 fields = ('backend_status_icon', 'user', 'slice', 'role')
608 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400609
Tony Mackc2835a92013-05-28 09:18:49 -0400610 def formfield_for_foreignkey(self, db_field, request, **kwargs):
611 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700612 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400613 if db_field.name == 'user':
Scott Baker521abcc2015-07-16 12:40:07 -0700614 # all users are available to be granted SlicePrivilege
615 kwargs['queryset'] = User.objects.all()
Tony Mackc2835a92013-05-28 09:18:49 -0400616
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400617 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400618
Tony Mack5b061472014-02-04 07:57:10 -0500619 def queryset(self, request):
620 return SlicePrivilege.select_by_user(request.user)
621
Scott Baker022cdcd2015-02-18 15:50:11 -0800622class SliceNetworkInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700623 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800624 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700625 extra = 0
626 verbose_name = "Network Connection"
627 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400628 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700629 fields = ['backend_status_icon', 'network']
630 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700631
Scott Baker022cdcd2015-02-18 15:50:11 -0800632class ImageDeploymentsInline(XOSTabularInline):
Sapan Bhatiae9f96f62014-11-19 15:10:16 -0500633 model = ImageDeployments
Scott Baker2170b972014-06-03 12:14:07 -0700634 extra = 0
635 verbose_name = "Image Deployments"
636 verbose_name_plural = "Image Deployments"
637 suit_classes = 'suit-tab suit-tab-imagedeployments'
Tony Mack336e0f92014-11-30 15:53:08 -0500638 fields = ['backend_status_icon', 'image', 'deployment']
639 readonly_fields = ['backend_status_icon']
640
Scott Baker022cdcd2015-02-18 15:50:11 -0800641class ControllerImagesInline(XOSTabularInline):
Tony Mack336e0f92014-11-30 15:53:08 -0500642 model = ControllerImages
643 extra = 0
644 verbose_name = "Controller Images"
645 verbose_name_plural = "Controller Images"
646 suit_classes = 'suit-tab suit-tab-admin-only'
647 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
Scott Baker40c00762014-08-21 16:55:59 -0700648 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700649
Scott Baker022cdcd2015-02-18 15:50:11 -0800650class SliceRoleAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400651 model = SliceRole
652 pass
653
Scott Baker022cdcd2015-02-18 15:50:11 -0800654class SiteRoleAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400655 model = SiteRole
656 pass
657
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400658class DeploymentAdminForm(forms.ModelForm):
Scott Bakerde0f4412014-06-11 15:40:26 -0700659 images = forms.ModelMultipleChoiceField(
660 queryset=Image.objects.all(),
661 required=False,
662 help_text="Select which images should be deployed on this deployment",
663 widget=FilteredSelectMultiple(
664 verbose_name=('Images'), is_stacked=False
665 )
666 )
Scott Baker37b47902014-09-02 14:37:41 -0700667 flavors = forms.ModelMultipleChoiceField(
668 queryset=Flavor.objects.all(),
669 required=False,
670 help_text="Select which flavors should be usable on this deployment",
671 widget=FilteredSelectMultiple(
672 verbose_name=('Flavors'), is_stacked=False
673 )
674 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400675 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400676 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700677 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400678
Siobhan Tully320b4622014-01-17 15:11:14 -0500679 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700680 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500681 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
682
Scott Baker5380c522014-06-06 14:49:43 -0700683 self.fields['accessControl'].initial = "allow site " + request.user.site.name
684
Siobhan Tully320b4622014-01-17 15:11:14 -0500685 if self.instance and self.instance.pk:
Scott Baker9f6b8ed2014-11-17 23:44:03 -0800686 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700687 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700688
689 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
690 """ helper function for handling m2m relations from the MultipleChoiceField
691
692 this_obj: the source object we want to link from
693
694 selected_objs: a list of destination objects we want to link to
695
696 all_relations: the full set of relations involving this_obj, including ones we don't want
697
698 relation_class: the class that implements the relation from source to dest
699
700 local_attrname: field name representing this_obj in relation_class
701
702 foreign_attrname: field name representing selected_objs in relation_class
703
704 This function will remove all newobjclass relations from this_obj
705 that are not contained in selected_objs, and add any relations that
706 are in selected_objs but don't exist in the data model yet.
707 """
708
709 existing_dest_objs = []
710 for relation in list(all_relations):
711 if getattr(relation, foreign_attrname) not in selected_objs:
712 #print "deleting site", sdp.site
713 relation.delete()
714 else:
715 existing_dest_objs.append(getattr(relation, foreign_attrname))
716
717 for dest_obj in selected_objs:
718 if dest_obj not in existing_dest_objs:
719 #print "adding site", site
720 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
721 relation = relation_class(**kwargs)
722 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500723
724 def save(self, commit=True):
725 deployment = super(DeploymentAdminForm, self).save(commit=False)
726
727 if commit:
728 deployment.save()
Scott Baker61b6aec2014-10-06 17:17:40 -0700729 # this has to be done after save() if/when a deployment is first created
730 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500731
732 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700733 # save_m2m() doesn't seem to work with 'through' relations. So we
734 # create/destroy the through models ourselves. There has to be
735 # a better way...
736
Tony Mack592aa952014-12-15 11:45:02 -0500737 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ImageDeployments, "deployment", "image")
738 # manipulate_m2m_objs doesn't work for Flavor/Deployment relationship
739 # so well handle that manually here
740 for flavor in deployment.flavors.all():
741 if getattr(flavor, 'name') not in self.cleaned_data['flavors']:
Tony Mack11f4d202014-12-15 12:37:59 -0500742 deployment.flavors.remove(flavor)
Tony Mack592aa952014-12-15 11:45:02 -0500743 for flavor in self.cleaned_data['flavors']:
744 if flavor not in deployment.flavors.all():
745 flavor.deployments.add(deployment)
Scott Bakerc9b14f72014-05-22 13:44:20 -0700746
Scott Baker37b47902014-09-02 14:37:41 -0700747 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500748
749 return deployment
750
Scott Bakerff5e0f32014-05-22 14:40:27 -0700751class DeploymentAdminROForm(DeploymentAdminForm):
752 def save(self, commit=True):
753 raise PermissionDenied
754
Scott Baker022cdcd2015-02-18 15:50:11 -0800755class SiteAssocInline(XOSTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500756 model = Site.deployments.through
757 extra = 0
758 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400759
Scott Baker022cdcd2015-02-18 15:50:11 -0800760class DeploymentAdmin(XOSBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500761 model = Deployment
Scott Bakerae233f42015-02-10 08:40:34 -0800762 fieldList = ['backend_status_text', 'name', 'images', 'flavors', 'accessControl']
763 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack93d1b032014-12-08 16:43:02 -0500764 # node no longer directly connected to deployment
765 #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
Tony Mack10812252015-01-30 10:58:29 -0500766 inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline,SiteDeploymentInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700767 list_display = ['backend_status_icon', 'name']
768 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700769 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500770
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500771 user_readonly_fields = ['name']
772
Tony Mack93d1b032014-12-08 16:43:02 -0500773 # nodes no longer direclty connected to deployments
Scott Bakerae233f42015-02-10 08:40:34 -0800774 suit_form_tabs =(('general','Deployment Details'),('deploymentprivileges','Privileges'), ('sitedeployments', 'Sites'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500775
Scott Bakerff5e0f32014-05-22 14:40:27 -0700776 def get_form(self, request, obj=None, **kwargs):
Tony Mackd893dfb2015-02-05 06:13:04 -0500777 if request.user.isReadOnlyUser() or not request.user.is_admin:
Scott Bakerff5e0f32014-05-22 14:40:27 -0700778 kwargs["form"] = DeploymentAdminROForm
779 else:
780 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700781 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
782
783 # from stackexchange: pass the request object into the form
784
785 class AdminFormMetaClass(adminForm):
786 def __new__(cls, *args, **kwargs):
787 kwargs['request'] = request
788 return adminForm(*args, **kwargs)
789
790 return AdminFormMetaClass
791
Scott Bakere497b3c2016-01-29 12:18:19 -0800792class ControllerAdminForm(forms.ModelForm):
793 backend_disabled = forms.BooleanField(required=False)
794 class Meta:
795 model = Controller
796
797 def __init__(self, *args, **kwargs):
798 request = kwargs.pop('request', None)
799 super(ControllerAdminForm, self).__init__(*args, **kwargs)
800
801 if self.instance and self.instance.pk:
802 self.fields['backend_disabled'].initial = self.instance.get_backend_register('disabled', False)
803 else:
804 # defaults when adding new controller
805 self.fields['backend_disabled'].initial = False
806
807 def save(self, commit=True):
808 self.instance.set_backend_register("disabled", self.cleaned_data["backend_disabled"])
809 return super(ControllerAdminForm, self).save(commit=commit)
810
Scott Baker022cdcd2015-02-18 15:50:11 -0800811class ControllerAdmin(XOSBaseAdmin):
Scott Bakerae233f42015-02-10 08:40:34 -0800812 model = Controller
Scott Bakere497b3c2016-01-29 12:18:19 -0800813 fieldList = ['deployment', 'name', 'backend_type', 'backend_disabled', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password', 'domain', 'rabbit_host', 'rabbit_user', 'rabbit_password']
Scott Bakerae233f42015-02-10 08:40:34 -0800814 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Macka7dbd422015-01-05 22:48:11 -0500815 inlines = [ControllerSiteInline] # ,ControllerImagesInline]
Tony Mack528d4222014-12-05 17:13:08 -0500816 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
817 list_display_links = ('backend_status_icon', 'name', )
818 readonly_fields = ('backend_status_text',)
Scott Bakere497b3c2016-01-29 12:18:19 -0800819 form = ControllerAdminForm
Tony Mack528d4222014-12-05 17:13:08 -0500820
821 user_readonly_fields = []
822
Tony Mackc36cafb2015-01-13 17:33:08 -0500823 def save_model(self, request, obj, form, change):
824 # update openstack connection to use this site/tenant
825 obj.save_by_user(request.user)
826
827 def delete_model(self, request, obj):
Scott Bakerae233f42015-02-10 08:40:34 -0800828 obj.delete_by_user(request.user)
829
Tony Mack79e2e662015-02-18 11:41:36 -0500830 def queryset(self, request):
831 return Controller.select_by_user(request.user)
832
Scott Bakerae233f42015-02-10 08:40:34 -0800833 @property
834 def suit_form_tabs(self):
835 tabs = [('general', 'Controller Details'),
836 ]
837
838 request=getattr(_thread_locals, "request", None)
839 if request and request.user.is_admin:
840 tabs.append( ('admin-only', 'Admin-Only') )
841
842 return tabs
Tony Mackc36cafb2015-01-13 17:33:08 -0500843
Scott Baker462a1d92015-10-15 15:59:19 -0700844class TenantAttributeAdmin(XOSBaseAdmin):
845 model = TenantAttribute
846 list_display = ('backend_status_icon', 'tenant', 'name', 'value')
847 list_display_links = ('backend_status_icon', 'name')
848 fieldList = ('backend_status_text', 'tenant', 'name', 'value', )
849 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
850 readonly_fields = ('backend_status_text', )
851
852 suit_form_tabs =(('general', 'Tenant Root Details'),
853 )
854
855class TenantAttrAsTabInline(XOSTabularInline):
856 model = TenantAttribute
857 fields = ['name','value']
858 extra = 0
859 suit_classes = 'suit-tab suit-tab-tenantattrs'
860
Scott Baker1b06b6c2015-07-06 14:40:20 -0700861class TenantRootRoleAdmin(XOSBaseAdmin):
862 model = TenantRootRole
863 fields = ('role',)
864
865class TenantRootTenantInline(XOSTabularInline):
866 model = Tenant
867 fields = ['provider_service', 'subscriber_root']
868 extra = 0
869 suit_classes = 'suit-tab suit-tab-tenantroots'
870 fk_name = 'subscriber_root'
871 verbose_name = 'subscribed tenant'
872 verbose_name_plural = 'subscribed tenants'
873
874 #def queryset(self, request):
875 # qs = super(TenantRootTenantInline, self).queryset(request)
876 # return qs.filter(kind="coarse")
877
878class TenantRootPrivilegeInline(XOSTabularInline):
879 model = TenantRootPrivilege
880 extra = 0
881 suit_classes = 'suit-tab suit-tab-tenantrootprivileges'
882 fields = ['backend_status_icon', 'user', 'role', 'tenant_root']
883 readonly_fields = ('backend_status_icon', )
884
885 def queryset(self, request):
886 return TenantRootPrivilege.select_by_user(request.user)
887
888class TenantRootAdmin(XOSBaseAdmin):
889 model = TenantRoot
890 list_display = ('backend_status_icon', 'name', 'kind')
891 list_display_links = ('backend_status_icon', 'name')
892 fieldList = ('backend_status_text', 'name', 'kind', )
893 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
894 inlines = (TenantRootTenantInline, TenantRootPrivilegeInline)
895 readonly_fields = ('backend_status_text', )
896
897 suit_form_tabs =(('general', 'Tenant Root Details'),
898 ('tenantroots','Tenancy'),
899 ('tenantrootprivileges','Privileges')
900 )
901
Scott Baker925a8fa2015-04-26 20:30:40 -0700902class ProviderTenantInline(XOSTabularInline):
903 model = CoarseTenant
904 fields = ['provider_service', 'subscriber_service', 'connect_method']
905 extra = 0
906 suit_classes = 'suit-tab suit-tab-servicetenants'
907 fk_name = 'provider_service'
908 verbose_name = 'provided tenant'
909 verbose_name_plural = 'provided tenants'
910
911 def queryset(self, request):
912 qs = super(ProviderTenantInline, self).queryset(request)
913 return qs.filter(kind="coarse")
914
915class SubscriberTenantInline(XOSTabularInline):
916 model = CoarseTenant
917 fields = ['provider_service', 'subscriber_service', 'connect_method']
918 extra = 0
919 suit_classes = 'suit-tab suit-tab-servicetenants'
920 fk_name = 'subscriber_service'
921 verbose_name = 'subscribed tenant'
922 verbose_name_plural = 'subscribed tenants'
923
924 def queryset(self, request):
925 qs = super(SubscriberTenantInline, self).queryset(request)
926 return qs.filter(kind="coarse")
927
Scott Baker022cdcd2015-02-18 15:50:11 -0800928class ServiceAttrAsTabInline(XOSTabularInline):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400929 model = ServiceAttribute
930 fields = ['name','value']
931 extra = 0
932 suit_classes = 'suit-tab suit-tab-serviceattrs'
933
Scott Baker022cdcd2015-02-18 15:50:11 -0800934class ServiceAdmin(XOSBaseAdmin):
Scott Baker008a9962015-04-15 20:58:20 -0700935 list_display = ("backend_status_icon","name","kind","versionNumber","enabled","published")
Scott Baker63d1a552014-08-21 15:19:07 -0700936 list_display_links = ('backend_status_icon', 'name', )
Scott Bakerf60c0102015-11-12 16:22:52 -0800937 fieldList = ["backend_status_text","name","kind","description","versionNumber","enabled","published","view_url","icon_url","public_key","private_key_fn","service_specific_attribute","service_specific_id"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500938 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack8d108e22015-05-11 20:39:32 -0400939 inlines = [ServiceAttrAsTabInline,SliceInline,ProviderTenantInline,SubscriberTenantInline,ServicePrivilegeInline]
Scott Baker40c00762014-08-21 16:55:59 -0700940 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500941
942 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500943
944 suit_form_tabs =(('general', 'Service Details'),
945 ('slices','Slices'),
946 ('serviceattrs','Additional Attributes'),
Scott Baker925a8fa2015-04-26 20:30:40 -0700947 ('servicetenants','Tenancy'),
Scott Baker1b06b6c2015-07-06 14:40:20 -0700948 ('serviceprivileges','Privileges')
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500949 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400950
Scott Baker022cdcd2015-02-18 15:50:11 -0800951class SiteNodeInline(XOSTabularInline):
Tony Mack82df1d02015-01-14 20:58:38 -0500952 model = Node
953 fields = ['name', 'site_deployment']
954 extra = 0
955 suit_classes = 'suit-tab suit-tab-nodes'
956
Tony Mack26b59532015-02-25 11:39:34 -0500957 def formfield_for_foreignkey(self, db_field, request, **kwargs):
958 # only display site deployments associated with this site
959 if db_field.name == 'site_deployment':
960 kwargs['queryset'] = SiteDeployment.objects.filter(site__id=int(request.path.split('/')[-2]))
961
962 return super(SiteNodeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
963
Scott Baker022cdcd2015-02-18 15:50:11 -0800964class SiteAdmin(XOSBaseAdmin):
Tony Mack598eaf22015-01-25 12:35:29 -0500965 #fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Tony Mack2862dca2015-08-04 17:21:55 -0400966 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'login_base', 'location', 'is_public', 'hosts_nodes', 'hosts_users']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400967 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500968 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400969 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400970 ]
Tony Mack598eaf22015-01-25 12:35:29 -0500971 #readonly_fields = ['backend_status_text', 'accountLink']
972 readonly_fields = ['backend_status_text']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500973
Tony Mack598eaf22015-01-25 12:35:29 -0500974 #user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Tony Mack2862dca2015-08-04 17:21:55 -0400975 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'hosts_nodes', 'hosts_users']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500976
Scott Baker63d1a552014-08-21 15:19:07 -0700977 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
978 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400979 filter_horizontal = ('deployments',)
Tony Mack10812252015-01-30 10:58:29 -0500980 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteNodeInline]
Tony Mack10328a12015-01-14 12:11:05 -0500981 admin_inlines = [ControllerSiteInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400982 search_fields = ['name']
983
Tony Mack3c01ff92015-01-10 23:08:10 -0500984 @property
985 def suit_form_tabs(self):
986 tabs = [('general', 'Site Details'),
987 ('users','Users'),
988 ('siteprivileges','Privileges'),
Tony Mack3c01ff92015-01-10 23:08:10 -0500989 ('slices','Slices'),
Tony Mack82df1d02015-01-14 20:58:38 -0500990 ('nodes','Nodes'),
Tony Mack3c01ff92015-01-10 23:08:10 -0500991 ]
992
993 request=getattr(_thread_locals, "request", None)
994 if request and request.user.is_admin:
995 tabs.append( ('admin-only', 'Admin-Only') )
996
997 return tabs
998
Tony Mack04062832013-05-10 08:22:44 -0400999 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -05001000 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -04001001
Tony Mack5cd13202013-05-01 21:48:38 -04001002 def get_formsets(self, request, obj=None):
1003 for inline in self.get_inline_instances(request, obj):
1004 # hide MyInline in the add view
1005 if obj is None:
1006 continue
Tony Mack3de59e32015-08-19 11:58:18 -04001007 if isinstance(inline, InstanceInline):
Tony Mack2bd5b412013-06-11 21:05:06 -04001008 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -04001009 yield inline.get_formset(request, obj)
1010
Scott Baker545db2a2013-12-09 18:44:43 -08001011 def accountLink(self, obj):
1012 link_obj = obj.accounts.all()
1013 if link_obj:
1014 reverse_path = "admin:core_account_change"
1015 url = reverse(reverse_path, args =(link_obj[0].id,))
1016 return "<a href='%s'>%s</a>" % (url, "view billing details")
1017 else:
1018 return "no billing data for this site"
1019 accountLink.allow_tags = True
1020 accountLink.short_description = "Billing"
1021
Tony Mack332ee1d2014-02-04 15:33:45 -05001022 def save_model(self, request, obj, form, change):
1023 # update openstack connection to use this site/tenant
1024 obj.save_by_user(request.user)
1025
1026 def delete_model(self, request, obj):
1027 obj.delete_by_user(request.user)
1028
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001029
Scott Baker022cdcd2015-02-18 15:50:11 -08001030class SitePrivilegeAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001031 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -04001032 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001033 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -04001034 ]
Scott Baker40c00762014-08-21 16:55:59 -07001035 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -07001036 list_display = ('backend_status_icon', 'user', 'site', 'role')
1037 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001038 user_readonly_fields = fieldList
1039 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -04001040
Tony Mackc2835a92013-05-28 09:18:49 -04001041 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1042 if db_field.name == 'site':
1043 if not request.user.is_admin:
1044 # only show sites where user is an admin or pi
1045 sites = set()
1046 for site_privilege in SitePrivilege.objects.filer(user=request.user):
1047 if site_privilege.role.role_type in ['admin', 'pi']:
1048 sites.add(site_privilege.site)
1049 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
1050
1051 if db_field.name == 'user':
1052 if not request.user.is_admin:
1053 # only show users from sites where caller has admin or pi role
1054 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
1055 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
1056 sites = [site_privilege.site for site_privilege in site_privileges]
1057 site_privileges = SitePrivilege.objects.filter(site__in=sites)
1058 emails = [site_privilege.user.email for site_privilege in site_privileges]
1059 users = User.objects.filter(email__in=emails)
1060 kwargs['queryset'] = users
1061
1062 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1063
Tony Mack04062832013-05-10 08:22:44 -04001064 def queryset(self, request):
1065 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -04001066 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -04001067 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -05001068 #if not request.user.is_admin:
1069 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
1070 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
1071 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
1072 # sites = Site.objects.filter(login_base__in=login_bases)
1073 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -04001074 return qs
1075
Siobhan Tullyce652d02013-10-08 21:52:35 -04001076class SliceForm(forms.ModelForm):
1077 class Meta:
1078 model = Slice
1079 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -07001080 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -04001081 }
1082
Tony Mack2cbd3802014-09-29 16:10:52 -04001083 def clean(self):
1084 cleaned_data = super(SliceForm, self).clean()
1085 name = cleaned_data.get('name')
Scott Baker6efad462014-10-06 23:09:59 -07001086 site = cleaned_data.get('site')
Tony Mack585cb192014-10-22 12:54:19 -04001087 slice_id = self.instance.id
1088 if not site and slice_id:
1089 site = Slice.objects.get(id=slice_id).site
Scott Baker6efad462014-10-06 23:09:59 -07001090 if (not isinstance(site,Site)):
1091 # previous code indicates 'site' could be a site_id and not a site?
1092 site = Slice.objects.get(id=site.id)
Tony Mack2cbd3802014-09-29 16:10:52 -04001093 if not name.startswith(site.login_base):
1094 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
1095 return cleaned_data
1096
Scott Baker022cdcd2015-02-18 15:50:11 -08001097class ControllerSliceInline(XOSTabularInline):
Tony Macka7dbd422015-01-05 22:48:11 -05001098 model = ControllerSlice
Scott Bakerc4efdc72014-10-15 16:54:04 -07001099 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001100 verbose_name = "Controller Slices"
1101 verbose_name_plural = "Controller Slices"
Scott Bakerc4efdc72014-10-15 16:54:04 -07001102 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001103 fields = ['backend_status_icon', 'controller', 'tenant_id']
Tony Mack3c01ff92015-01-10 23:08:10 -05001104 readonly_fields = ('backend_status_icon', 'controller' )
Scott Bakerc4efdc72014-10-15 16:54:04 -07001105
Scott Baker022cdcd2015-02-18 15:50:11 -08001106class SliceAdmin(XOSBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -04001107 form = SliceForm
Scott Bakerde088532015-12-14 19:37:55 -08001108 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_instances', "default_isolation", "network"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001109 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -07001110 readonly_fields = ('backend_status_text', )
Tony Mack3de59e32015-08-19 11:58:18 -04001111 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_instances')
Tony Mack7d459902014-09-03 13:18:57 -04001112 list_display_links = ('backend_status_icon', 'name', )
Tony Mack3de59e32015-08-19 11:58:18 -04001113 normal_inlines = [SlicePrivilegeInline, InstanceInline, TagInline, ReservationInline, SliceNetworkInline]
Scott Baker0e289fd2015-06-12 10:40:15 -07001114 inlines = normal_inlines
Tony Macka7dbd422015-01-05 22:48:11 -05001115 admin_inlines = [ControllerSliceInline]
Scott Baker591fb062015-09-15 15:21:50 -07001116 suit_form_includes = (('slice_instance_tab.html', 'bottom', 'instances'),)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001117
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001118 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001119
Scott Bakerc4efdc72014-10-15 16:54:04 -07001120 @property
1121 def suit_form_tabs(self):
1122 tabs =[('general', 'Slice Details'),
1123 ('slicenetworks','Networks'),
1124 ('sliceprivileges','Privileges'),
Tony Mack3de59e32015-08-19 11:58:18 -04001125 ('instances','Instances'),
Tony Mack598eaf22015-01-25 12:35:29 -05001126 #('reservations','Reservations'),
Tony Mackb34553e2015-01-15 14:44:06 -05001127 ('tags','Tags'),
Scott Bakerc4efdc72014-10-15 16:54:04 -07001128 ]
1129
1130 request=getattr(_thread_locals, "request", None)
1131 if request and request.user.is_admin:
1132 tabs.append( ('admin-only', 'Admin-Only') )
1133
1134 return tabs
Tony Mack7b8505a2014-10-22 11:54:29 -04001135
1136 def add_view(self, request, form_url='', extra_context=None):
Scott Baker0e289fd2015-06-12 10:40:15 -07001137 # Ugly hack for CORD
1138 self.inlines = self.normal_inlines
Tony Mack7b8505a2014-10-22 11:54:29 -04001139 # revert to default read-only fields
1140 self.readonly_fields = ('backend_status_text',)
1141 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
1142
1143 def change_view(self, request, object_id, form_url='', extra_context=None):
Tony Mack7b8505a2014-10-22 11:54:29 -04001144 # cannot change the site of an existing slice so make the site field read only
1145 if object_id:
1146 self.readonly_fields = ('backend_status_text','site')
Scott Baker0e289fd2015-06-12 10:40:15 -07001147
Tony Mack7b8505a2014-10-22 11:54:29 -04001148 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001149
Scott Baker510fdbb2014-08-05 17:19:24 -07001150 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -07001151 deployment_nodes = []
1152 for node in Node.objects.all():
Scott Baker39293d72015-01-21 16:24:07 -08001153 deployment_nodes.append( (node.site_deployment.deployment.id, node.id, node.name) )
Scott Baker510fdbb2014-08-05 17:19:24 -07001154
Scott Baker7a61dc42014-09-02 17:08:20 -07001155 deployment_flavors = []
1156 for flavor in Flavor.objects.all():
1157 for deployment in flavor.deployments.all():
1158 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
1159
Tony Mack93d1b032014-12-08 16:43:02 -05001160 deployment_images = []
Scott Baker93e80cd2014-09-09 09:58:49 -07001161 for image in Image.objects.all():
Tony Mack93d1b032014-12-08 16:43:02 -05001162 for deployment_image in image.imagedeployments.all():
Scott Bakera6a0c772014-12-22 17:35:34 -08001163 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
Scott Baker93e80cd2014-09-09 09:58:49 -07001164
Tony Mackec23b992014-09-02 21:18:45 -04001165 site_login_bases = []
1166 for site in Site.objects.all():
Scott Baker93e80cd2014-09-09 09:58:49 -07001167 site_login_bases.append((site.id, site.login_base))
1168
Scott Baker510fdbb2014-08-05 17:19:24 -07001169 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -07001170 context["deployment_flavors"] = deployment_flavors
Scott Baker93e80cd2014-09-09 09:58:49 -07001171 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -04001172 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -07001173 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
1174
Tony Mackc2835a92013-05-28 09:18:49 -04001175 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1176 if db_field.name == 'site':
Tony Mack15136b52015-08-04 17:53:23 -04001177 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_users=True)
Tony Mackec23b992014-09-02 21:18:45 -04001178 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 -07001179
Tony Mackc2835a92013-05-28 09:18:49 -04001180 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1181
Tony Mack04062832013-05-10 08:22:44 -04001182 def queryset(self, request):
1183 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001184 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -04001185
Tony Mack79748612013-05-01 14:52:03 -04001186 def get_formsets(self, request, obj=None):
1187 for inline in self.get_inline_instances(request, obj):
1188 # hide MyInline in the add view
1189 if obj is None:
1190 continue
Tony Mack3de59e32015-08-19 11:58:18 -04001191 if isinstance(inline, InstanceInline):
Tony Mack2bd5b412013-06-11 21:05:06 -04001192 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -04001193 yield inline.get_formset(request, obj)
1194
Scott Baker03394b32015-09-15 17:48:57 -07001195 def add_extra_context(self, request, extra_context):
1196 super(SliceAdmin, self).add_extra_context(request, extra_context)
1197 # set context["slice_id"] to the PK passed in the URL to this view
1198 if len(request.resolver_match.args)>0:
1199 extra_context["slice_id"] = request.resolver_match.args[0]
1200
Scott Baker0e289fd2015-06-12 10:40:15 -07001201 def UNUSED_get_inline_instances(self, request, obj=None):
1202 # HACK for CORD to do something special on vcpe slice page
1203 # this was a good idea, but failed miserably, as something still
1204 # expects there to be a deployment field.
1205 # XXX this approach is better than clobbering self.inlines, so
1206 # try to make this work post-demo.
1207 if (obj is not None) and (obj.name == "mysite_vcpe"):
Tony Mack3de59e32015-08-19 11:58:18 -04001208 cord_vcpe_inlines = [ SlicePrivilegeInline, CordInstanceInline, TagInline, ReservationInline,SliceNetworkInline]
Scott Baker0e289fd2015-06-12 10:40:15 -07001209
1210 inlines=[]
1211 for inline_class in cord_vcpe_inlines:
1212 inlines.append(inline_class(self.model, self.admin_site))
1213 else:
1214 inlines = super(SliceAdmin, self).get_inline_instances(request, obj)
1215
1216 return inlines
1217
Scott Baker022cdcd2015-02-18 15:50:11 -08001218class SlicePrivilegeAdmin(XOSBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -04001219 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -07001220 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -04001221 ]
Scott Baker40c00762014-08-21 16:55:59 -07001222 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -07001223 list_display = ('backend_status_icon', 'user', 'slice', 'role')
1224 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -04001225
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001226 user_readonly_fields = ['user', 'slice', 'role']
1227 user_readonly_inlines = []
1228
Tony Mackc2835a92013-05-28 09:18:49 -04001229 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1230 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001231 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001232
1233 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -05001234 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001235
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001236 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001237
Tony Mack04062832013-05-10 08:22:44 -04001238 def queryset(self, request):
1239 # admins can see all memberships. Users can only see memberships of
1240 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -05001241 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001242
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001243 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -04001244 # update openstack connection to use this site/tenant
1245 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001246 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001247 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001248 obj.save()
1249
1250 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -04001251 # update openstack connection to use this site/tenant
1252 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001253 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001254 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001255 obj.delete()
1256
Scott Baker022cdcd2015-02-18 15:50:11 -08001257class ImageAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001258
Scott Baker36f50872014-08-21 13:01:25 -07001259 fieldsets = [('Image Details',
Scott Baker14266232015-12-14 10:11:56 -08001260 {'fields': ['backend_status_text', 'name', 'kind', 'disk_format', 'container_format', 'tag', 'path'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001261 'classes': ['suit-tab suit-tab-general']})
1262 ]
Scott Baker40c00762014-08-21 16:55:59 -07001263 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001264
Tony Mack3de59e32015-08-19 11:58:18 -04001265 suit_form_tabs =(('general','Image Details'),('instances','Instances'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001266
Tony Mack3de59e32015-08-19 11:58:18 -04001267 inlines = [InstanceInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -07001268
Scott Baker14266232015-12-14 10:11:56 -08001269 user_readonly_fields = ['name', 'disk_format', 'container_format', 'tag', 'path']
Scott Bakerb27b62c2014-08-15 16:29:16 -07001270
Scott Baker0cefa532015-11-09 16:17:11 -08001271 list_display = ['backend_status_icon', 'name', 'kind']
Scott Baker63d1a552014-08-21 15:19:07 -07001272 list_display_links = ('backend_status_icon', 'name', )
1273
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001274class NodeForm(forms.ModelForm):
1275 class Meta:
Scott Baker2a11f852015-09-21 15:06:38 -07001276 model = Node
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001277 widgets = {
1278 'site': LinkedSelect,
1279 'deployment': LinkedSelect
1280 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001281
Scott Baker022cdcd2015-02-18 15:50:11 -08001282class NodeAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001283 form = NodeForm
Tony Mack93d1b032014-12-08 16:43:02 -05001284 list_display = ('backend_status_icon', 'name', 'site_deployment')
Scott Baker63d1a552014-08-21 15:19:07 -07001285 list_display_links = ('backend_status_icon', 'name', )
Tony Mack93d1b032014-12-08 16:43:02 -05001286 list_filter = ('site_deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001287
Tony Mack3de59e32015-08-19 11:58:18 -04001288 inlines = [TagInline,InstanceInline]
Scott Baker2a11f852015-09-21 15:06:38 -07001289 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name', 'site_deployment'], 'classes':['suit-tab suit-tab-details']})]
Scott Baker40c00762014-08-21 16:55:59 -07001290 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001291
Tony Mack93d1b032014-12-08 16:43:02 -05001292 user_readonly_fields = ['name','site_deployment']
Tony Mack3de59e32015-08-19 11:58:18 -04001293 user_readonly_inlines = [TagInline,InstanceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001294
Tony Mack3de59e32015-08-19 11:58:18 -04001295 suit_form_tabs =(('details','Node Details'),('instances','Instances'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001296
Tony Mack17b97b82015-08-04 17:32:32 -04001297 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1298 if db_field.name == 'site':
1299 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_nodes=True)
Siobhan Tully567e3e62013-06-21 18:03:16 -04001300
Scott Baker2a11f852015-09-21 15:06:38 -07001301 field = super(NodeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1302
1303 return field
1304
Tony Mack3de59e32015-08-19 11:58:18 -04001305class InstanceForm(forms.ModelForm):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001306 class Meta:
Tony Mack3de59e32015-08-19 11:58:18 -04001307 model = Instance
Tony Mackd90cdbf2013-04-16 22:48:40 -04001308 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001309 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001310 widgets = {
1311 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001312 'instance_name': PlainTextWidget(),
Scott Baker887d4a82015-01-19 11:32:20 -08001313 'instance_id': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001314 'slice': LinkedSelect,
Tony Mackbf6aa302014-12-26 13:38:02 -05001315 'deployment': LinkedSelect,
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001316 'node': LinkedSelect,
1317 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001318 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001319
Scott Baker022cdcd2015-02-18 15:50:11 -08001320class TagAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001321 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1322 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001323 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1324 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001325
Tony Mack8bc36b92015-09-01 16:06:52 +00001326class InstancePortInline(XOSTabularInline):
Tony Mackea30da82015-09-10 21:58:15 +00001327 fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
Scott Baker0672e982015-09-08 18:22:15 -07001328 readonly_fields = ("backend_status_icon", "ip", "mac")
Scott Baker43facad2015-08-26 09:43:33 -07001329 model = Port
Scott Baker29514bc2015-11-16 16:21:47 -08001330 #selflink_fieldname = "network"
Scott Baker0bdb6a52015-08-25 18:00:15 -07001331 extra = 0
1332 verbose_name_plural = "Ports"
1333 verbose_name = "Port"
1334 suit_classes = 'suit-tab suit-tab-ports'
1335
Tony Mack3de59e32015-08-19 11:58:18 -04001336class InstanceAdmin(XOSBaseAdmin):
1337 form = InstanceForm
Tony Mackcdec0902013-04-15 00:38:49 -04001338 fieldsets = [
Scott Baker04a356a2015-11-12 17:25:53 -08001339 ('Instance Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'isolation', 'flavor', 'image', 'node', 'parent', 'all_ips_string', 'instance_id', 'instance_name', 'ssh_command', ], 'classes': ['suit-tab suit-tab-general'], }),
Scott Bakere1a6e0b2015-11-10 17:07:23 -08001340 ('Container Settings', {'fields': ['volumes'], 'classes': ['suit-tab suit-tab-container'], }),
Tony Mackcdec0902013-04-15 00:38:49 -04001341 ]
Tony Mack7d61efb2015-01-30 17:20:46 -05001342 readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Scott Baker0cefa532015-11-09 16:17:11 -08001343 list_display = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'isolation', 'slice', 'flavor', 'image', 'node', 'deployment']
Scott Bakerf0b403f2015-02-13 14:38:21 -08001344 list_display_links = ('backend_status_icon', 'all_ips_string', 'instance_id', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001345
Scott Baker3cf51d62016-02-02 16:54:38 -08001346 suit_form_tabs =(('general', 'Instance Details'), ('ports', 'Ports'), ('container', 'Container Settings'), ('tags', 'Tags'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001347
Tony Mack6a782f92015-09-13 22:50:39 +00001348 inlines = [TagInline, InstancePortInline]
Tony Mack53106f32013-04-27 16:43:01 -04001349
Tony Mackbf6aa302014-12-26 13:38:02 -05001350 user_readonly_fields = ['slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001351
Scott Baker970314b2015-01-25 22:16:13 -08001352 def ssh_command(self, obj):
1353 ssh_command = obj.get_ssh_command()
1354 if ssh_command:
1355 return ssh_command
1356 else:
1357 return "(not available)"
1358
Tony Mackc2835a92013-05-28 09:18:49 -04001359 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1360 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001361 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001362
Tony Mack3de59e32015-08-19 11:58:18 -04001363 return super(InstanceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001364
Tony Mack04062832013-05-10 08:22:44 -04001365 def queryset(self, request):
Tony Mack3de59e32015-08-19 11:58:18 -04001366 # admins can see all instances. Users can only see instances of
Tony Mack04062832013-05-10 08:22:44 -04001367 # the slices they belong to.
Tony Mack3de59e32015-08-19 11:58:18 -04001368 return Instance.select_by_user(request.user)
Tony Mack5b061472014-02-04 07:57:10 -05001369
Scott Bakerb84392d2015-09-21 21:42:41 -07001370 def add_view(self, request, form_url='', extra_context = None):
1371 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
1372 return super(InstanceAdmin,self).add_view(request, form_url, extra_context)
Tony Mack04062832013-05-10 08:22:44 -04001373
Scott Bakerb84392d2015-09-21 21:42:41 -07001374 def change_view(self, request, object_id, extra_context=None):
1375 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string', 'deployment', 'slice', 'flavor', 'image', 'node')
1376 self.readonly_save = self.readonly_fields # for XOSAdminMixin.change_view's user_readonly_fields switching code
1377 return super(InstanceAdmin,self).change_view(request, object_id, extra_context)
Tony Mack53106f32013-04-27 16:43:01 -04001378
Scott Baker530e4de2015-09-21 16:02:54 -07001379 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
1380 deployment_nodes = []
1381 for node in Node.objects.all():
1382 deployment_nodes.append( (node.site_deployment.deployment.id, node.id, node.name) )
1383
1384 deployment_flavors = []
1385 for flavor in Flavor.objects.all():
1386 for deployment in flavor.deployments.all():
1387 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
1388
1389 deployment_images = []
1390 for image in Image.objects.all():
1391 for deployment_image in image.imagedeployments.all():
1392 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
1393
1394 site_login_bases = []
1395 for site in Site.objects.all():
1396 site_login_bases.append((site.id, site.login_base))
1397
1398 context["deployment_nodes"] = deployment_nodes
1399 context["deployment_flavors"] = deployment_flavors
1400 context["deployment_images"] = deployment_images
1401 context["site_login_bases"] = site_login_bases
1402 return super(InstanceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
1403
1404 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1405 if db_field.name == 'deployment':
1406 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
1407 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_deployment_changed(this);"})
1408 if db_field.name == 'flavor':
1409 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_flavor_changed(this);"})
1410
1411 field = super(InstanceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1412
1413 return field
1414
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001415 #def save_model(self, request, obj, form, change):
1416 # # update openstack connection to use this site/tenant
1417 # auth = request.session.get('auth', {})
1418 # auth['tenant'] = obj.slice.name
1419 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1420 # obj.creator = request.user
1421 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001422
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001423 #def delete_model(self, request, obj):
1424 # # update openstack connection to use this site/tenant
1425 # auth = request.session.get('auth', {})
1426 # auth['tenant'] = obj.slice.name
1427 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1428 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001429
Scott Baker0cefa532015-11-09 16:17:11 -08001430#class ContainerPortInline(XOSTabularInline):
1431# fields = ['backend_status_icon', 'network', 'container', 'ip', 'mac', 'segmentation_id']
1432# readonly_fields = ("backend_status_icon", "ip", "mac", "segmentation_id")
1433# model = Port
1434# selflink_fieldname = "network"
1435# extra = 0
1436# verbose_name_plural = "Ports"
1437# verbose_name = "Port"
1438# suit_classes = 'suit-tab suit-tab-ports'
Scott Baker1de8b0d2015-10-26 19:52:10 -07001439
Scott Baker0cefa532015-11-09 16:17:11 -08001440#class ContainerAdmin(XOSBaseAdmin):
1441# fieldsets = [
1442# ('Container Details', {'fields': ['backend_status_text', 'slice', 'node', 'docker_image', 'volumes', 'no_sync'], 'classes': ['suit-tab suit-tab-general'], })
1443# ]
1444# readonly_fields = ('backend_status_text', )
1445# list_display = ['backend_status_icon', 'id']
1446# list_display_links = ('backend_status_icon', 'id', )
1447#
1448# suit_form_tabs =(('general', 'Container Details'), ('ports', 'Ports'))
1449#
1450# inlines = [TagInline, ContainerPortInline]
1451#
1452# def formfield_for_foreignkey(self, db_field, request, **kwargs):
1453# if db_field.name == 'slice':
1454# kwargs['queryset'] = Slice.select_by_user(request.user)
1455#
1456# return super(ContainerAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1457#
1458# def queryset(self, request):
1459# # admins can see all instances. Users can only see instances of
1460# # the slices they belong to.
1461# return Container.select_by_user(request.user)
Scott Bakerda513652015-10-26 15:12:13 -07001462
Siobhan Tully53437282013-04-26 19:30:27 -04001463class UserCreationForm(forms.ModelForm):
1464 """A form for creating new users. Includes all the required
1465 fields, plus a repeated password."""
1466 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1467 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1468
1469 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001470 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001471 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001472
1473 def clean_password2(self):
1474 # Check that the two password entries match
1475 password1 = self.cleaned_data.get("password1")
1476 password2 = self.cleaned_data.get("password2")
1477 if password1 and password2 and password1 != password2:
1478 raise forms.ValidationError("Passwords don't match")
1479 return password2
1480
1481 def save(self, commit=True):
1482 # Save the provided password in hashed format
1483 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001484 user.password = self.cleaned_data["password1"]
1485 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001486 if commit:
1487 user.save()
1488 return user
1489
Siobhan Tully567e3e62013-06-21 18:03:16 -04001490
Siobhan Tully53437282013-04-26 19:30:27 -04001491class UserChangeForm(forms.ModelForm):
1492 """A form for updating users. Includes all the fields on
1493 the user, but replaces the password field with admin's
1494 password hash display field.
1495 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001496 password = ReadOnlyPasswordHashField(label='Password',
1497 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001498
Scott Baker811d4472015-05-19 16:39:48 -07001499 PROFILE_CHOICES = ((None, '------'), ('regular', 'Regular user'), ('cp', 'Content Provider'))
1500 profile = forms.ChoiceField(choices=PROFILE_CHOICES, required=False, label="Quick Profile")
1501
Siobhan Tully53437282013-04-26 19:30:27 -04001502 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001503 model = User
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001504 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001505
1506 def clean_password(self):
1507 # Regardless of what the user provides, return the initial value.
1508 # This is done here, rather than on the field, because the
1509 # field does not have access to the initial value
1510 return self.initial["password"]
1511
Scott Baker811d4472015-05-19 16:39:48 -07001512 def save(self, *args, **kwargs):
1513 if self.cleaned_data['profile']:
1514 self.instance.apply_profile(self.cleaned_data['profile'])
1515
1516 return super(UserChangeForm, self).save(*args, **kwargs)
1517
Scott Baker022cdcd2015-02-18 15:50:11 -08001518class UserDashboardViewInline(XOSTabularInline):
Scott Baker2c3cb642014-05-19 17:55:56 -07001519 model = UserDashboardView
1520 extra = 0
1521 suit_classes = 'suit-tab suit-tab-dashboards'
1522 fields = ['user', 'dashboardView', 'order']
1523
Scott Baker022cdcd2015-02-18 15:50:11 -08001524class ControllerUserInline(XOSTabularInline):
Tony Mack3c01ff92015-01-10 23:08:10 -05001525 model = ControllerUser
1526 extra = 0
1527 suit_classes = 'suit-tab suit-tab-admin-only'
1528 fields = ['controller', 'user', 'kuser_id']
1529 readonly_fields=['controller']
1530
1531
Scott Bakera9412c32015-02-27 12:21:22 -08001532class UserAdmin(XOSAdminMixin, UserAdmin):
1533 # Note: Make sure XOSAdminMixin is listed before
Scott Baker86c83ab2014-10-03 13:10:47 -07001534 # admin.ModelAdmin in the class declaration.
1535
Siobhan Tully53437282013-04-26 19:30:27 -04001536 class Meta:
1537 app_label = "core"
1538
1539 # The forms to add and change user instances
1540 form = UserChangeForm
1541 add_form = UserCreationForm
1542
1543 # The fields to be used in displaying the User model.
1544 # These override the definitions on the base UserAdmin
1545 # that reference specific fields on auth.User.
Scott Bakerf587f442015-01-24 13:33:26 -08001546 list_display = ('backend_status_icon', 'email', 'firstname', 'lastname', 'site', 'last_login')
1547 list_display_links = ("email",)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001548 list_filter = ('site',)
Scott Baker6e9027f2015-01-29 10:55:53 -08001549 inlines = [SlicePrivilegeInline,SitePrivilegeInline]
Tony Mack3c01ff92015-01-10 23:08:10 -05001550 admin_inlines = [ControllerUserInline]
Scott Bakere61e3a02015-06-10 16:14:58 -07001551 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 -05001552 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1553
Siobhan Tully53437282013-04-26 19:30:27 -04001554 fieldsets = (
Scott Baker74ebc7a2015-05-15 09:19:36 -07001555 ('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 -04001556 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001557 #('Important dates', {'fields': ('last_login',)}),
1558 )
1559 add_fieldsets = (
1560 (None, {
1561 'classes': ('wide',),
Scott Baker74ebc7a2015-05-15 09:19:36 -07001562 'fields': ('site', 'email', 'firstname', 'lastname', 'is_admin', 'is_readonly', 'is_appuser', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001563 ),
1564 )
Scott Baker40c00762014-08-21 16:55:59 -07001565 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001566 search_fields = ('email',)
1567 ordering = ('email',)
1568 filter_horizontal = ()
1569
Scott Baker3ca51f62014-05-23 12:05:11 -07001570 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001571
Scott Baker0a5633b2014-10-06 17:51:20 -07001572 @property
1573 def suit_form_tabs(self):
1574 if getattr(_thread_locals, "obj", None) is None:
1575 return []
1576 else:
Tony Mack3c01ff92015-01-10 23:08:10 -05001577 tabs = [('general','Login Details'),
Scott Baker0a5633b2014-10-06 17:51:20 -07001578 ('contact','Contact Information'),
1579 ('sliceprivileges','Slice Privileges'),
Scott Baker6e9027f2015-01-29 10:55:53 -08001580 ('siteprivileges','Site Privileges')]
Tony Mack3c01ff92015-01-10 23:08:10 -05001581
1582 request=getattr(_thread_locals, "request", None)
1583 if request and request.user.is_admin:
1584 tabs.append( ('admin-only', 'Admin-Only') )
1585
1586 return tabs
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001587
Tony Mackc2835a92013-05-28 09:18:49 -04001588 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1589 if db_field.name == 'site':
Tony Mack17b97b82015-08-04 17:32:32 -04001590 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_users=True)
Tony Mackc2835a92013-05-28 09:18:49 -04001591
1592 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1593
Tony Mack5b061472014-02-04 07:57:10 -05001594 def queryset(self, request):
1595 return User.select_by_user(request.user)
1596
Tony Mack6235fc82015-01-25 21:58:30 -05001597 def get_form(self, request, obj=None, **kwargs):
Tony Mackb2c407f2015-01-28 12:37:12 -05001598 # copy login details list
1599 login_details_fields = list(self.fieldListLoginDetails)
Tony Mack92b12052015-01-28 12:49:58 -05001600 if not request.user.is_admin:
Scott Baker6e9027f2015-01-29 10:55:53 -08001601 # only admins can see 'is_admin' and 'is_readonly' fields
Tony Mackb2c407f2015-01-28 12:37:12 -05001602 if 'is_admin' in login_details_fields:
1603 login_details_fields.remove('is_admin')
1604 if 'is_readonly' in login_details_fields:
Scott Baker811d4472015-05-19 16:39:48 -07001605 login_details_fields.remove('is_readonly')
1606 if 'is_appuser' in login_details_fields:
1607 login_details_fields.remove('is_admin')
1608 if 'profile' in login_details_fields:
1609 login_details_fields.remove('profile')
Tony Mack92b12052015-01-28 12:49:58 -05001610 #if len(request.user.siteprivileges.filter(role__role = 'pi')) > 0:
Scott Baker811d4472015-05-19 16:39:48 -07001611 # only admins and pis can change a user's site
Tony Mack92b12052015-01-28 12:49:58 -05001612 # self.readonly_fields = ('backend_status_text', 'site')
Tony Mackb2c407f2015-01-28 12:37:12 -05001613 self.fieldsets = (
1614 ('Login Details', {'fields': login_details_fields, 'classes':['suit-tab suit-tab-general']}),
1615 ('Contact Information', {'fields': self.fieldListContactInfo, 'classes':['suit-tab suit-tab-contact']}),
1616 )
Tony Mack6235fc82015-01-25 21:58:30 -05001617 return super(UserAdmin, self).get_form(request, obj, **kwargs)
1618
Scott Baker022cdcd2015-02-18 15:50:11 -08001619class ControllerDashboardViewInline(XOSTabularInline):
Scott Bakera6a0c772014-12-22 17:35:34 -08001620 model = ControllerDashboardView
Scott Bakereef5a6b2014-12-19 16:41:12 -08001621 extra = 0
1622 fields = ["controller", "url"]
1623 suit_classes = 'suit-tab suit-tab-controllers'
1624
Scott Baker022cdcd2015-02-18 15:50:11 -08001625class DashboardViewAdmin(XOSBaseAdmin):
Scott Baker2c3cb642014-05-19 17:55:56 -07001626 fieldsets = [('Dashboard View Details',
Scott Bakerecc55ac2015-02-17 13:34:32 -08001627 {'fields': ['backend_status_text', 'name', 'url', 'enabled', 'deployments'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001628 'classes': ['suit-tab suit-tab-general']})
1629 ]
Scott Baker9daf19c2015-01-18 16:46:26 -08001630 list_display = ["name", "enabled", "url"]
Scott Baker40c00762014-08-21 16:55:59 -07001631 readonly_fields = ('backend_status_text', )
Scott Bakera6a0c772014-12-22 17:35:34 -08001632 inlines = [ControllerDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001633
Scott Bakereef5a6b2014-12-19 16:41:12 -08001634 suit_form_tabs =(('general','Dashboard View Details'),
1635 ('controllers', 'Per-controller Dashboard Details'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001636
Scott Baker022cdcd2015-02-18 15:50:11 -08001637class ServiceResourceInline(XOSTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001638 model = ServiceResource
1639 extra = 0
1640
Scott Baker022cdcd2015-02-18 15:50:11 -08001641class ServiceClassAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001642 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1643 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001644 inlines = [ServiceResourceInline]
1645
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001646 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1647 user_readonly_inlines = []
1648
Scott Baker022cdcd2015-02-18 15:50:11 -08001649class ReservedResourceInline(XOSTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001650 model = ReservedResource
1651 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001652 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001653
1654 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1655 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1656
1657 if db_field.name == 'resource':
1658 # restrict resources to those that the slice's service class allows
1659 if request._slice is not None:
1660 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1661 if len(field.queryset) > 0:
1662 field.initial = field.queryset.all()[0]
S.Çağlar Onur0e591832015-02-24 17:28:09 -05001663 else:
1664 field.queryset = field.queryset.none()
Tony Mack3de59e32015-08-19 11:58:18 -04001665 elif db_field.name == 'instance':
1666 # restrict instances to those that belong to the slice
S.Çağlar Onur0e591832015-02-24 17:28:09 -05001667 if request._slice is not None:
Scott Baker133c9212013-05-17 09:09:11 -07001668 field.queryset = field.queryset.filter(slice = request._slice)
1669 else:
S.Çağlar Onur0e591832015-02-24 17:28:09 -05001670 field.queryset = field.queryset.none()
1671
Scott Baker133c9212013-05-17 09:09:11 -07001672 return field
1673
Tony Mack5b061472014-02-04 07:57:10 -05001674 def queryset(self, request):
1675 return ReservedResource.select_by_user(request.user)
1676
Scott Baker133c9212013-05-17 09:09:11 -07001677class ReservationChangeForm(forms.ModelForm):
1678 class Meta:
1679 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001680 widgets = {
1681 'slice' : LinkedSelect
1682 }
Scott Baker133c9212013-05-17 09:09:11 -07001683
1684class ReservationAddForm(forms.ModelForm):
1685 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1686 refresh = forms.CharField(widget=forms.HiddenInput())
1687
1688 class Media:
Scott Baker97468b72015-02-18 15:15:58 -08001689 css = {'all': ('xos.css',)} # .field-refresh { display: none; }
Scott Baker133c9212013-05-17 09:09:11 -07001690
1691 def clean_slice(self):
1692 slice = self.cleaned_data.get("slice")
1693 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1694 if len(x) == 0:
1695 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1696 return slice
1697
1698 class Meta:
1699 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001700 widgets = {
1701 'slice' : LinkedSelect
1702 }
1703
Scott Baker133c9212013-05-17 09:09:11 -07001704
1705class ReservationAddRefreshForm(ReservationAddForm):
1706 """ This form is displayed when the Reservation Form receives an update
1707 from the Slice dropdown onChange handler. It doesn't validate the
1708 data and doesn't save the data. This will cause the form to be
1709 redrawn.
1710 """
1711
Scott Baker8737e5f2013-05-17 09:35:32 -07001712 """ don't validate anything other than slice """
1713 dont_validate_fields = ("startTime", "duration")
1714
Scott Baker133c9212013-05-17 09:09:11 -07001715 def full_clean(self):
1716 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001717
1718 for fieldname in self.dont_validate_fields:
1719 if fieldname in self._errors:
1720 del self._errors[fieldname]
1721
Scott Baker133c9212013-05-17 09:09:11 -07001722 return result
1723
1724 """ don't save anything """
1725 def is_valid(self):
1726 return False
1727
Scott Baker022cdcd2015-02-18 15:50:11 -08001728class ReservationAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001729 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001730 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001731 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001732 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001733 form = ReservationAddForm
1734
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001735 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1736
1737 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001738 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001739
Scott Baker133c9212013-05-17 09:09:11 -07001740 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001741 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001742 request._refresh = False
1743 request._slice = None
1744 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001745 # "refresh" will be set to "1" if the form was submitted due to
1746 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001747 if request.POST.get("refresh","1") == "1":
1748 request._refresh = True
1749 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001750
1751 # Keep track of the slice that was selected, so the
1752 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001753 request._slice = request.POST.get("slice",None)
1754 if (request._slice is not None):
1755 request._slice = Slice.objects.get(id=request._slice)
1756
1757 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1758 return result
1759
Scott Bakeracd45142013-05-19 16:19:16 -07001760 def changelist_view(self, request, extra_context = None):
1761 timezone.activate(request.user.timezone)
1762 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1763
Scott Baker133c9212013-05-17 09:09:11 -07001764 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001765 request._obj_ = obj
1766 if obj is not None:
1767 # For changes, set request._slice to the slice already set in the
1768 # object.
1769 request._slice = obj.slice
1770 self.form = ReservationChangeForm
1771 else:
1772 if getattr(request, "_refresh", False):
1773 self.form = ReservationAddRefreshForm
1774 else:
1775 self.form = ReservationAddForm
1776 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1777
Scott Baker133c9212013-05-17 09:09:11 -07001778 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001779 if (obj is not None):
1780 # Prevent slice from being changed after the reservation has been
1781 # created.
1782 return ['slice']
1783 else:
Scott Baker133c9212013-05-17 09:09:11 -07001784 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001785
Tony Mack5b061472014-02-04 07:57:10 -05001786 def queryset(self, request):
1787 return Reservation.select_by_user(request.user)
1788
Scott Baker022cdcd2015-02-18 15:50:11 -08001789class NetworkParameterTypeAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001790 list_display = ("backend_status_icon", "name", )
1791 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001792 user_readonly_fields = ['name']
1793 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001794
Scott Baker022cdcd2015-02-18 15:50:11 -08001795class RouterAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001796 list_display = ("backend_status_icon", "name", )
1797 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001798 user_readonly_fields = ['name']
1799 user_readonly_inlines = []
1800
Scott Baker022cdcd2015-02-18 15:50:11 -08001801class RouterInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001802 model = Router.networks.through
1803 extra = 0
1804 verbose_name_plural = "Routers"
1805 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001806 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001807
Scott Bakerb27b62c2014-08-15 16:29:16 -07001808class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001809 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001810 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001811 verbose_name_plural = "Parameters"
1812 verbose_name = "Parameter"
1813 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001814 fields = ['backend_status_icon', 'parameter', 'value']
1815 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001816
Scott Baker0bdb6a52015-08-25 18:00:15 -07001817class NetworkPortInline(XOSTabularInline):
Scott Baker0cefa532015-11-09 16:17:11 -08001818 fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
Scott Baker0672e982015-09-08 18:22:15 -07001819 readonly_fields = ("backend_status_icon", "ip", "mac")
Scott Baker43facad2015-08-26 09:43:33 -07001820 model = Port
Scott Bakerf3e905d2015-11-16 13:43:38 -08001821 #selflink_fieldname = "instance"
Scott Baker74d8e622013-07-29 16:04:22 -07001822 extra = 0
Scott Bakera68e6e32015-08-25 17:11:30 -07001823 verbose_name_plural = "Ports"
1824 verbose_name = "Port"
1825 suit_classes = 'suit-tab suit-tab-ports'
Scott Baker74d8e622013-07-29 16:04:22 -07001826
Scott Baker022cdcd2015-02-18 15:50:11 -08001827class NetworkSlicesInline(XOSTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001828 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001829 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001830 extra = 0
1831 verbose_name_plural = "Slices"
1832 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001833 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001834 fields = ['backend_status_icon', 'network','slice']
1835 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001836
Scott Baker022cdcd2015-02-18 15:50:11 -08001837class ControllerNetworkInline(XOSTabularInline):
Tony Macka7dbd422015-01-05 22:48:11 -05001838 model = ControllerNetwork
Scott Bakerfbb45862014-10-17 16:27:23 -07001839 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001840 verbose_name_plural = "Controller Networks"
1841 verbose_name = "Controller Network"
Scott Bakerfbb45862014-10-17 16:27:23 -07001842 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001843 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Bakerfbb45862014-10-17 16:27:23 -07001844 readonly_fields = ('backend_status_icon', )
1845
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001846class NetworkForm(forms.ModelForm):
1847 class Meta:
1848 model = Network
1849 widgets = {
1850 'topologyParameters': UploadTextareaWidget,
1851 'controllerParameters': UploadTextareaWidget,
1852 }
1853
Scott Baker022cdcd2015-02-18 15:50:11 -08001854class NetworkAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001855 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1856 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001857 readonly_fields = ("subnet", )
Scott Baker0bdb6a52015-08-25 18:00:15 -07001858 inlines = [NetworkParameterInline, NetworkPortInline, NetworkSlicesInline, RouterInline]
Tony Macka7dbd422015-01-05 22:48:11 -05001859 admin_inlines = [ControllerNetworkInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001860
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001861 form=NetworkForm
1862
Siobhan Tully2d95e482013-09-06 10:56:06 -04001863 fieldsets = [
Scott Bakera4c11bd2015-08-21 16:40:53 -07001864 (None, {'fields': ['backend_status_text', 'name','template','ports','labels',
1865 'owner','guaranteed_bandwidth', 'permit_all_slices',
1866 'permitted_slices','network_id','router_id','subnet_id',
1867 'subnet', 'autoconnect'],
Scott Baker40248712014-11-17 16:04:45 -08001868 'classes':['suit-tab suit-tab-general']}),
Scott Baker0451fb62015-01-03 12:29:29 -08001869 (None, {'fields': ['topology_parameters', 'controller_url', 'controller_parameters'],
Scott Baker40248712014-11-17 16:04:45 -08001870 'classes':['suit-tab suit-tab-sdn']}),
1871 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001872
Scott Baker40c00762014-08-21 16:55:59 -07001873 readonly_fields = ('backend_status_text', )
Scott Bakera4c11bd2015-08-21 16:40:53 -07001874 user_readonly_fields = ['name','template','ports','labels','owner','guaranteed_bandwidth',
1875 'permit_all_slices','permitted_slices','network_id','router_id',
1876 'subnet_id','subnet','autoconnect']
Siobhan Tully2d95e482013-09-06 10:56:06 -04001877
Scott Bakerfbb45862014-10-17 16:27:23 -07001878 @property
1879 def suit_form_tabs(self):
1880 tabs=[('general','Network Details'),
Scott Baker40248712014-11-17 16:04:45 -08001881 ('sdn', 'SDN Configuration'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001882 ('netparams', 'Parameters'),
Scott Bakera68e6e32015-08-25 17:11:30 -07001883 ('ports','Ports'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001884 ('networkslices','Slices'),
1885 ('routers','Routers'),
1886 ]
1887
1888 request=getattr(_thread_locals, "request", None)
1889 if request and request.user.is_admin:
1890 tabs.append( ('admin-only', 'Admin-Only') )
1891
1892 return tabs
1893
1894
Scott Baker022cdcd2015-02-18 15:50:11 -08001895class NetworkTemplateAdmin(XOSBaseAdmin):
Scott Baker81fa17f2015-01-03 12:03:38 -08001896 list_display = ("backend_status_icon", "name", "guaranteed_bandwidth", "visibility")
Scott Baker63d1a552014-08-21 15:19:07 -07001897 list_display_links = ('backend_status_icon', 'name', )
Scott Baker81fa17f2015-01-03 12:03:38 -08001898 user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001899 user_readonly_inlines = []
Scott Baker29514bc2015-11-16 16:21:47 -08001900 inlines = [NetworkParameterInline,]
Scott Baker40248712014-11-17 16:04:45 -08001901 fieldsets = [
Scott Baker9d5bf3b2015-12-09 15:44:55 -08001902 (None, {'fields': ['name', 'description', 'guaranteed_bandwidth', 'visibility', 'translation', 'access', 'shared_network_name', 'shared_network_id', 'topology_kind', 'controller_kind'],
Scott Baker40248712014-11-17 16:04:45 -08001903 'classes':['suit-tab suit-tab-general']}),]
Scott Baker29514bc2015-11-16 16:21:47 -08001904 suit_form_tabs = (('general','Network Template Details'), ('netparams', 'Parameters') )
Scott Baker74d8e622013-07-29 16:04:22 -07001905
Scott Bakerf3e905d2015-11-16 13:43:38 -08001906class PortAdmin(XOSBaseAdmin):
1907 list_display = ("backend_status_icon", "name", "id", "ip")
1908 list_display_links = ('backend_status_icon', 'id')
1909 readonly_fields = ("subnet", )
1910 inlines = [NetworkParameterInline]
1911
1912 fieldsets = [
1913 (None, {'fields': ['backend_status_text', 'network', 'instance', 'ip', 'port_id', 'mac'],
1914 'classes':['suit-tab suit-tab-general']}),
1915 ]
1916
1917 readonly_fields = ('backend_status_text', )
1918 suit_form_tabs = (('general', 'Port Details'), ('netparams', 'Parameters'))
1919
Scott Baker022cdcd2015-02-18 15:50:11 -08001920class FlavorAdmin(XOSBaseAdmin):
Scott Baker37b47902014-09-02 14:37:41 -07001921 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1922 list_display_links = ("backend_status_icon", "name")
1923 user_readonly_fields = ("name", "flavor")
1924 fields = ("name", "description", "flavor", "order", "default")
1925
Tony Mack31c2b8f2013-04-26 20:01:42 -04001926# register a signal that caches the user's credentials when they log in
1927def cache_credentials(sender, user, request, **kwds):
1928 auth = {'username': request.POST['username'],
1929 'password': request.POST['password']}
1930 request.session['auth'] = auth
1931user_logged_in.connect(cache_credentials)
1932
Scott Baker15cddfa2013-12-09 13:45:19 -08001933def dollar_field(fieldName, short_description):
1934 def newFunc(self, obj):
1935 try:
1936 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1937 except:
1938 x=getattr(obj, fieldName, 0.0)
1939 return x
1940 newFunc.short_description = short_description
1941 return newFunc
1942
1943def right_dollar_field(fieldName, short_description):
1944 def newFunc(self, obj):
1945 try:
1946 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1947 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1948 except:
1949 x=getattr(obj, fieldName, 0.0)
1950 return x
1951 newFunc.short_description = short_description
1952 newFunc.allow_tags = True
1953 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001954
Scott Baker022cdcd2015-02-18 15:50:11 -08001955class InvoiceChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001956 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001957 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001958 verbose_name_plural = "Charges"
1959 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001960 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001961 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1962 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1963 can_delete = False
1964 max_num = 0
1965
1966 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001967
1968class InvoiceAdmin(admin.ModelAdmin):
1969 list_display = ("date", "account")
1970
1971 inlines = [InvoiceChargeInline]
1972
Scott Baker9cb88a22013-12-09 18:56:00 -08001973 fields = ["date", "account", "dollar_amount"]
1974 readonly_fields = ["date", "account", "dollar_amount"]
1975
1976 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001977
Scott Baker022cdcd2015-02-18 15:50:11 -08001978class InvoiceInline(XOSTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001979 model = Invoice
1980 extra = 0
1981 verbose_name_plural = "Invoices"
1982 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001983 fields = ["date", "dollar_amount"]
1984 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001985 suit_classes = 'suit-tab suit-tab-accountinvoice'
1986 can_delete=False
1987 max_num=0
1988
1989 dollar_amount = right_dollar_field("amount", "Amount")
1990
Scott Baker022cdcd2015-02-18 15:50:11 -08001991class PendingChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001992 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001993 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001994 verbose_name_plural = "Charges"
1995 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001996 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001997 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1998 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001999 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08002000 can_delete=False
2001 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08002002
2003 def queryset(self, request):
2004 qs = super(PendingChargeInline, self).queryset(request)
2005 qs = qs.filter(state="pending")
2006 return qs
2007
Scott Baker15cddfa2013-12-09 13:45:19 -08002008 dollar_amount = right_dollar_field("amount", "Amount")
2009
Scott Baker022cdcd2015-02-18 15:50:11 -08002010class PaymentInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08002011 model=Payment
2012 extra = 1
2013 verbose_name_plural = "Payments"
2014 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08002015 fields = ["date", "dollar_amount"]
2016 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08002017 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08002018 can_delete=False
2019 max_num=0
2020
2021 dollar_amount = right_dollar_field("amount", "Amount")
2022
Scott Baker43105042013-12-06 23:23:36 -08002023class AccountAdmin(admin.ModelAdmin):
2024 list_display = ("site", "balance_due")
2025
2026 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
2027
2028 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05002029 (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 -08002030
Scott Baker15cddfa2013-12-09 13:45:19 -08002031 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08002032
2033 suit_form_tabs =(
2034 ('general','Account Details'),
2035 ('accountinvoice', 'Invoices'),
2036 ('accountpayments', 'Payments'),
2037 ('accountpendingcharges','Pending Charges'),
2038 )
2039
Scott Baker15cddfa2013-12-09 13:45:19 -08002040 dollar_balance_due = dollar_field("balance_due", "Balance Due")
2041 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
2042 dollar_total_payments = dollar_field("total_payments", "Total Payments")
2043
Scott Baker2461bec2015-08-14 09:10:11 -07002044class ProgramForm(forms.ModelForm):
2045 class Meta:
2046 model = Program
2047 widgets = {
2048 'contents': UploadTextareaWidget(attrs={'rows': 20, 'cols': 80, 'class': "input-xxlarge"}),
2049 'description': forms.Textarea(attrs={'rows': 3, 'cols': 80, 'class': 'input-xxlarge'}),
2050 'messages': forms.Textarea(attrs={'rows': 20, 'cols': 80, 'class': 'input-xxlarge'}),
2051 'output': forms.Textarea(attrs={'rows': 3, 'cols': 80, 'class': 'input-xxlarge'})
2052 }
2053
2054class ProgramAdmin(XOSBaseAdmin):
2055 list_display = ("name", "status")
2056 list_display_links = ('name', "status")
2057
2058 form=ProgramForm
2059
2060 fieldsets = [
2061 (None, {'fields': ['name', 'command', 'kind', 'description', 'output', 'status'],
2062 'classes':['suit-tab suit-tab-general']}),
2063 (None, {'fields': ['contents'],
2064 'classes':['suit-tab suit-tab-contents']}),
2065 (None, {'fields': ['messages'],
2066 'classes':['suit-tab suit-tab-messages']}),
2067 ]
2068
2069 readonly_fields = ("status",)
2070
2071 @property
2072 def suit_form_tabs(self):
2073 tabs=[('general','Program Details'),
2074 ('contents','Program Source'),
2075 ('messages','Messages'),
2076 ]
2077
2078 request=getattr(_thread_locals, "request", None)
2079 if request and request.user.is_admin:
2080 tabs.append( ('admin-only', 'Admin-Only') )
2081
2082 return tabs
2083
Siobhan Tully53437282013-04-26 19:30:27 -04002084# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04002085admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04002086# ... and, since we're not using Django's builtin permissions,
2087# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04002088#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04002089
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002090# When debugging it is often easier to see all the classes, but for regular use
2091# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05002092showAll = False
Scott Baker43105042013-12-06 23:23:36 -08002093
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002094admin.site.register(Deployment, DeploymentAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05002095admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04002096admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04002097admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04002098admin.site.register(Service, ServiceAdmin)
Tony Mack598eaf22015-01-25 12:35:29 -05002099#admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07002100admin.site.register(Network, NetworkAdmin)
Scott Bakerf3e905d2015-11-16 13:43:38 -08002101admin.site.register(Port, PortAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07002102admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07002103admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Scott Baker2461bec2015-08-14 09:10:11 -07002104admin.site.register(Program, ProgramAdmin)
Tony Mack598eaf22015-01-25 12:35:29 -05002105#admin.site.register(Account, AccountAdmin)
2106#admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002107
Siobhan Tullycf04fb62014-01-11 11:25:57 -05002108if True:
2109 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
2110 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullyd3515752013-06-21 16:34:53 -04002111 admin.site.register(Tag, TagAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05002112 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04002113 admin.site.register(SiteRole)
2114 admin.site.register(SliceRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002115 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04002116 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
2117 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Tony Mack3de59e32015-08-19 11:58:18 -04002118 admin.site.register(Instance, InstanceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002119 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07002120 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07002121 admin.site.register(Flavor, FlavorAdmin)
Scott Baker1b06b6c2015-07-06 14:40:20 -07002122 admin.site.register(TenantRoot, TenantRootAdmin)
2123 admin.site.register(TenantRootRole, TenantRootRoleAdmin)
Scott Baker462a1d92015-10-15 15:59:19 -07002124 admin.site.register(TenantAttribute, TenantAttributeAdmin)
Scott Baker0cefa532015-11-09 16:17:11 -08002125# admin.site.register(Container, ContainerAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04002126