blob: 675b8c2c466905022f44e03ed166c483aad2379d [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=[]
197 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),
Scott Bakerff3c2d22015-04-03 17:44:31 -0700204 "url": self.url_for_model_changelist(request,model) }
Scott Bakerc481b322015-02-27 12:12:14 -0800205 admins.append(info)
206 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 Baker022cdcd2015-02-18 15:50:11 -0800792class ControllerAdmin(XOSBaseAdmin):
Scott Bakerae233f42015-02-10 08:40:34 -0800793 model = Controller
svavilapfa144d82015-10-13 23:50:03 -0400794 fieldList = ['deployment', 'name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password', 'domain', 'rabbit_host', 'rabbit_user', 'rabbit_password']
Scott Bakerae233f42015-02-10 08:40:34 -0800795 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Macka7dbd422015-01-05 22:48:11 -0500796 inlines = [ControllerSiteInline] # ,ControllerImagesInline]
Tony Mack528d4222014-12-05 17:13:08 -0500797 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
798 list_display_links = ('backend_status_icon', 'name', )
799 readonly_fields = ('backend_status_text',)
800
801 user_readonly_fields = []
802
Tony Mackc36cafb2015-01-13 17:33:08 -0500803 def save_model(self, request, obj, form, change):
804 # update openstack connection to use this site/tenant
805 obj.save_by_user(request.user)
806
807 def delete_model(self, request, obj):
Scott Bakerae233f42015-02-10 08:40:34 -0800808 obj.delete_by_user(request.user)
809
Tony Mack79e2e662015-02-18 11:41:36 -0500810 def queryset(self, request):
811 return Controller.select_by_user(request.user)
812
Scott Bakerae233f42015-02-10 08:40:34 -0800813 @property
814 def suit_form_tabs(self):
815 tabs = [('general', 'Controller Details'),
816 ]
817
818 request=getattr(_thread_locals, "request", None)
819 if request and request.user.is_admin:
820 tabs.append( ('admin-only', 'Admin-Only') )
821
822 return tabs
Tony Mackc36cafb2015-01-13 17:33:08 -0500823
Scott Baker462a1d92015-10-15 15:59:19 -0700824class TenantAttributeAdmin(XOSBaseAdmin):
825 model = TenantAttribute
826 list_display = ('backend_status_icon', 'tenant', 'name', 'value')
827 list_display_links = ('backend_status_icon', 'name')
828 fieldList = ('backend_status_text', 'tenant', 'name', 'value', )
829 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
830 readonly_fields = ('backend_status_text', )
831
832 suit_form_tabs =(('general', 'Tenant Root Details'),
833 )
834
835class TenantAttrAsTabInline(XOSTabularInline):
836 model = TenantAttribute
837 fields = ['name','value']
838 extra = 0
839 suit_classes = 'suit-tab suit-tab-tenantattrs'
840
Scott Baker1b06b6c2015-07-06 14:40:20 -0700841class TenantRootRoleAdmin(XOSBaseAdmin):
842 model = TenantRootRole
843 fields = ('role',)
844
845class TenantRootTenantInline(XOSTabularInline):
846 model = Tenant
847 fields = ['provider_service', 'subscriber_root']
848 extra = 0
849 suit_classes = 'suit-tab suit-tab-tenantroots'
850 fk_name = 'subscriber_root'
851 verbose_name = 'subscribed tenant'
852 verbose_name_plural = 'subscribed tenants'
853
854 #def queryset(self, request):
855 # qs = super(TenantRootTenantInline, self).queryset(request)
856 # return qs.filter(kind="coarse")
857
858class TenantRootPrivilegeInline(XOSTabularInline):
859 model = TenantRootPrivilege
860 extra = 0
861 suit_classes = 'suit-tab suit-tab-tenantrootprivileges'
862 fields = ['backend_status_icon', 'user', 'role', 'tenant_root']
863 readonly_fields = ('backend_status_icon', )
864
865 def queryset(self, request):
866 return TenantRootPrivilege.select_by_user(request.user)
867
868class TenantRootAdmin(XOSBaseAdmin):
869 model = TenantRoot
870 list_display = ('backend_status_icon', 'name', 'kind')
871 list_display_links = ('backend_status_icon', 'name')
872 fieldList = ('backend_status_text', 'name', 'kind', )
873 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
874 inlines = (TenantRootTenantInline, TenantRootPrivilegeInline)
875 readonly_fields = ('backend_status_text', )
876
877 suit_form_tabs =(('general', 'Tenant Root Details'),
878 ('tenantroots','Tenancy'),
879 ('tenantrootprivileges','Privileges')
880 )
881
Scott Baker925a8fa2015-04-26 20:30:40 -0700882class ProviderTenantInline(XOSTabularInline):
883 model = CoarseTenant
884 fields = ['provider_service', 'subscriber_service', 'connect_method']
885 extra = 0
886 suit_classes = 'suit-tab suit-tab-servicetenants'
887 fk_name = 'provider_service'
888 verbose_name = 'provided tenant'
889 verbose_name_plural = 'provided tenants'
890
891 def queryset(self, request):
892 qs = super(ProviderTenantInline, self).queryset(request)
893 return qs.filter(kind="coarse")
894
895class SubscriberTenantInline(XOSTabularInline):
896 model = CoarseTenant
897 fields = ['provider_service', 'subscriber_service', 'connect_method']
898 extra = 0
899 suit_classes = 'suit-tab suit-tab-servicetenants'
900 fk_name = 'subscriber_service'
901 verbose_name = 'subscribed tenant'
902 verbose_name_plural = 'subscribed tenants'
903
904 def queryset(self, request):
905 qs = super(SubscriberTenantInline, self).queryset(request)
906 return qs.filter(kind="coarse")
907
Scott Baker022cdcd2015-02-18 15:50:11 -0800908class ServiceAttrAsTabInline(XOSTabularInline):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400909 model = ServiceAttribute
910 fields = ['name','value']
911 extra = 0
912 suit_classes = 'suit-tab suit-tab-serviceattrs'
913
Scott Baker022cdcd2015-02-18 15:50:11 -0800914class ServiceAdmin(XOSBaseAdmin):
Scott Baker008a9962015-04-15 20:58:20 -0700915 list_display = ("backend_status_icon","name","kind","versionNumber","enabled","published")
Scott Baker63d1a552014-08-21 15:19:07 -0700916 list_display_links = ('backend_status_icon', 'name', )
Scott Bakerf60c0102015-11-12 16:22:52 -0800917 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 -0500918 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack8d108e22015-05-11 20:39:32 -0400919 inlines = [ServiceAttrAsTabInline,SliceInline,ProviderTenantInline,SubscriberTenantInline,ServicePrivilegeInline]
Scott Baker40c00762014-08-21 16:55:59 -0700920 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500921
922 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500923
924 suit_form_tabs =(('general', 'Service Details'),
925 ('slices','Slices'),
926 ('serviceattrs','Additional Attributes'),
Scott Baker925a8fa2015-04-26 20:30:40 -0700927 ('servicetenants','Tenancy'),
Scott Baker1b06b6c2015-07-06 14:40:20 -0700928 ('serviceprivileges','Privileges')
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500929 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400930
Scott Baker022cdcd2015-02-18 15:50:11 -0800931class SiteNodeInline(XOSTabularInline):
Tony Mack82df1d02015-01-14 20:58:38 -0500932 model = Node
933 fields = ['name', 'site_deployment']
934 extra = 0
935 suit_classes = 'suit-tab suit-tab-nodes'
936
Tony Mack26b59532015-02-25 11:39:34 -0500937 def formfield_for_foreignkey(self, db_field, request, **kwargs):
938 # only display site deployments associated with this site
939 if db_field.name == 'site_deployment':
940 kwargs['queryset'] = SiteDeployment.objects.filter(site__id=int(request.path.split('/')[-2]))
941
942 return super(SiteNodeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
943
Scott Baker022cdcd2015-02-18 15:50:11 -0800944class SiteAdmin(XOSBaseAdmin):
Tony Mack598eaf22015-01-25 12:35:29 -0500945 #fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Tony Mack2862dca2015-08-04 17:21:55 -0400946 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'login_base', 'location', 'is_public', 'hosts_nodes', 'hosts_users']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400947 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500948 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400949 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400950 ]
Tony Mack598eaf22015-01-25 12:35:29 -0500951 #readonly_fields = ['backend_status_text', 'accountLink']
952 readonly_fields = ['backend_status_text']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500953
Tony Mack598eaf22015-01-25 12:35:29 -0500954 #user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Tony Mack2862dca2015-08-04 17:21:55 -0400955 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'hosts_nodes', 'hosts_users']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500956
Scott Baker63d1a552014-08-21 15:19:07 -0700957 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
958 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400959 filter_horizontal = ('deployments',)
Tony Mack10812252015-01-30 10:58:29 -0500960 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteNodeInline]
Tony Mack10328a12015-01-14 12:11:05 -0500961 admin_inlines = [ControllerSiteInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400962 search_fields = ['name']
963
Tony Mack3c01ff92015-01-10 23:08:10 -0500964 @property
965 def suit_form_tabs(self):
966 tabs = [('general', 'Site Details'),
967 ('users','Users'),
968 ('siteprivileges','Privileges'),
Tony Mack3c01ff92015-01-10 23:08:10 -0500969 ('slices','Slices'),
Tony Mack82df1d02015-01-14 20:58:38 -0500970 ('nodes','Nodes'),
Tony Mack3c01ff92015-01-10 23:08:10 -0500971 ]
972
973 request=getattr(_thread_locals, "request", None)
974 if request and request.user.is_admin:
975 tabs.append( ('admin-only', 'Admin-Only') )
976
977 return tabs
978
Tony Mack04062832013-05-10 08:22:44 -0400979 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500980 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400981
Tony Mack5cd13202013-05-01 21:48:38 -0400982 def get_formsets(self, request, obj=None):
983 for inline in self.get_inline_instances(request, obj):
984 # hide MyInline in the add view
985 if obj is None:
986 continue
Tony Mack3de59e32015-08-19 11:58:18 -0400987 if isinstance(inline, InstanceInline):
Tony Mack2bd5b412013-06-11 21:05:06 -0400988 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400989 yield inline.get_formset(request, obj)
990
Scott Baker545db2a2013-12-09 18:44:43 -0800991 def accountLink(self, obj):
992 link_obj = obj.accounts.all()
993 if link_obj:
994 reverse_path = "admin:core_account_change"
995 url = reverse(reverse_path, args =(link_obj[0].id,))
996 return "<a href='%s'>%s</a>" % (url, "view billing details")
997 else:
998 return "no billing data for this site"
999 accountLink.allow_tags = True
1000 accountLink.short_description = "Billing"
1001
Tony Mack332ee1d2014-02-04 15:33:45 -05001002 def save_model(self, request, obj, form, change):
1003 # update openstack connection to use this site/tenant
1004 obj.save_by_user(request.user)
1005
1006 def delete_model(self, request, obj):
1007 obj.delete_by_user(request.user)
1008
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001009
Scott Baker022cdcd2015-02-18 15:50:11 -08001010class SitePrivilegeAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001011 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -04001012 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001013 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -04001014 ]
Scott Baker40c00762014-08-21 16:55:59 -07001015 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -07001016 list_display = ('backend_status_icon', 'user', 'site', 'role')
1017 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001018 user_readonly_fields = fieldList
1019 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -04001020
Tony Mackc2835a92013-05-28 09:18:49 -04001021 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1022 if db_field.name == 'site':
1023 if not request.user.is_admin:
1024 # only show sites where user is an admin or pi
1025 sites = set()
1026 for site_privilege in SitePrivilege.objects.filer(user=request.user):
1027 if site_privilege.role.role_type in ['admin', 'pi']:
1028 sites.add(site_privilege.site)
1029 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
1030
1031 if db_field.name == 'user':
1032 if not request.user.is_admin:
1033 # only show users from sites where caller has admin or pi role
1034 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
1035 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
1036 sites = [site_privilege.site for site_privilege in site_privileges]
1037 site_privileges = SitePrivilege.objects.filter(site__in=sites)
1038 emails = [site_privilege.user.email for site_privilege in site_privileges]
1039 users = User.objects.filter(email__in=emails)
1040 kwargs['queryset'] = users
1041
1042 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1043
Tony Mack04062832013-05-10 08:22:44 -04001044 def queryset(self, request):
1045 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -04001046 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -04001047 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -05001048 #if not request.user.is_admin:
1049 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
1050 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
1051 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
1052 # sites = Site.objects.filter(login_base__in=login_bases)
1053 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -04001054 return qs
1055
Siobhan Tullyce652d02013-10-08 21:52:35 -04001056class SliceForm(forms.ModelForm):
1057 class Meta:
1058 model = Slice
1059 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -07001060 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -04001061 }
1062
Tony Mack2cbd3802014-09-29 16:10:52 -04001063 def clean(self):
1064 cleaned_data = super(SliceForm, self).clean()
1065 name = cleaned_data.get('name')
Scott Baker6efad462014-10-06 23:09:59 -07001066 site = cleaned_data.get('site')
Tony Mack585cb192014-10-22 12:54:19 -04001067 slice_id = self.instance.id
1068 if not site and slice_id:
1069 site = Slice.objects.get(id=slice_id).site
Scott Baker6efad462014-10-06 23:09:59 -07001070 if (not isinstance(site,Site)):
1071 # previous code indicates 'site' could be a site_id and not a site?
1072 site = Slice.objects.get(id=site.id)
Tony Mack2cbd3802014-09-29 16:10:52 -04001073 if not name.startswith(site.login_base):
1074 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
1075 return cleaned_data
1076
Scott Baker022cdcd2015-02-18 15:50:11 -08001077class ControllerSliceInline(XOSTabularInline):
Tony Macka7dbd422015-01-05 22:48:11 -05001078 model = ControllerSlice
Scott Bakerc4efdc72014-10-15 16:54:04 -07001079 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001080 verbose_name = "Controller Slices"
1081 verbose_name_plural = "Controller Slices"
Scott Bakerc4efdc72014-10-15 16:54:04 -07001082 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001083 fields = ['backend_status_icon', 'controller', 'tenant_id']
Tony Mack3c01ff92015-01-10 23:08:10 -05001084 readonly_fields = ('backend_status_icon', 'controller' )
Scott Bakerc4efdc72014-10-15 16:54:04 -07001085
Scott Baker022cdcd2015-02-18 15:50:11 -08001086class SliceAdmin(XOSBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -04001087 form = SliceForm
Scott Bakerde088532015-12-14 19:37:55 -08001088 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_instances', "default_isolation", "network"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001089 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -07001090 readonly_fields = ('backend_status_text', )
Tony Mack3de59e32015-08-19 11:58:18 -04001091 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_instances')
Tony Mack7d459902014-09-03 13:18:57 -04001092 list_display_links = ('backend_status_icon', 'name', )
Tony Mack3de59e32015-08-19 11:58:18 -04001093 normal_inlines = [SlicePrivilegeInline, InstanceInline, TagInline, ReservationInline, SliceNetworkInline]
Scott Baker0e289fd2015-06-12 10:40:15 -07001094 inlines = normal_inlines
Tony Macka7dbd422015-01-05 22:48:11 -05001095 admin_inlines = [ControllerSliceInline]
Scott Baker591fb062015-09-15 15:21:50 -07001096 suit_form_includes = (('slice_instance_tab.html', 'bottom', 'instances'),)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001097
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001098 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001099
Scott Bakerc4efdc72014-10-15 16:54:04 -07001100 @property
1101 def suit_form_tabs(self):
1102 tabs =[('general', 'Slice Details'),
1103 ('slicenetworks','Networks'),
1104 ('sliceprivileges','Privileges'),
Tony Mack3de59e32015-08-19 11:58:18 -04001105 ('instances','Instances'),
Tony Mack598eaf22015-01-25 12:35:29 -05001106 #('reservations','Reservations'),
Tony Mackb34553e2015-01-15 14:44:06 -05001107 ('tags','Tags'),
Scott Bakerc4efdc72014-10-15 16:54:04 -07001108 ]
1109
1110 request=getattr(_thread_locals, "request", None)
1111 if request and request.user.is_admin:
1112 tabs.append( ('admin-only', 'Admin-Only') )
1113
1114 return tabs
Tony Mack7b8505a2014-10-22 11:54:29 -04001115
1116 def add_view(self, request, form_url='', extra_context=None):
Scott Baker0e289fd2015-06-12 10:40:15 -07001117 # Ugly hack for CORD
1118 self.inlines = self.normal_inlines
Tony Mack7b8505a2014-10-22 11:54:29 -04001119 # revert to default read-only fields
1120 self.readonly_fields = ('backend_status_text',)
1121 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
1122
1123 def change_view(self, request, object_id, form_url='', extra_context=None):
Tony Mack7b8505a2014-10-22 11:54:29 -04001124 # cannot change the site of an existing slice so make the site field read only
1125 if object_id:
1126 self.readonly_fields = ('backend_status_text','site')
Scott Baker0e289fd2015-06-12 10:40:15 -07001127
Tony Mack7b8505a2014-10-22 11:54:29 -04001128 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001129
Scott Baker510fdbb2014-08-05 17:19:24 -07001130 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -07001131 deployment_nodes = []
1132 for node in Node.objects.all():
Scott Baker39293d72015-01-21 16:24:07 -08001133 deployment_nodes.append( (node.site_deployment.deployment.id, node.id, node.name) )
Scott Baker510fdbb2014-08-05 17:19:24 -07001134
Scott Baker7a61dc42014-09-02 17:08:20 -07001135 deployment_flavors = []
1136 for flavor in Flavor.objects.all():
1137 for deployment in flavor.deployments.all():
1138 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
1139
Tony Mack93d1b032014-12-08 16:43:02 -05001140 deployment_images = []
Scott Baker93e80cd2014-09-09 09:58:49 -07001141 for image in Image.objects.all():
Tony Mack93d1b032014-12-08 16:43:02 -05001142 for deployment_image in image.imagedeployments.all():
Scott Bakera6a0c772014-12-22 17:35:34 -08001143 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
Scott Baker93e80cd2014-09-09 09:58:49 -07001144
Tony Mackec23b992014-09-02 21:18:45 -04001145 site_login_bases = []
1146 for site in Site.objects.all():
Scott Baker93e80cd2014-09-09 09:58:49 -07001147 site_login_bases.append((site.id, site.login_base))
1148
Scott Baker510fdbb2014-08-05 17:19:24 -07001149 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -07001150 context["deployment_flavors"] = deployment_flavors
Scott Baker93e80cd2014-09-09 09:58:49 -07001151 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -04001152 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -07001153 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
1154
Tony Mackc2835a92013-05-28 09:18:49 -04001155 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1156 if db_field.name == 'site':
Tony Mack15136b52015-08-04 17:53:23 -04001157 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_users=True)
Tony Mackec23b992014-09-02 21:18:45 -04001158 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 -07001159
Tony Mackc2835a92013-05-28 09:18:49 -04001160 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1161
Tony Mack04062832013-05-10 08:22:44 -04001162 def queryset(self, request):
1163 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001164 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -04001165
Tony Mack79748612013-05-01 14:52:03 -04001166 def get_formsets(self, request, obj=None):
1167 for inline in self.get_inline_instances(request, obj):
1168 # hide MyInline in the add view
1169 if obj is None:
1170 continue
Tony Mack3de59e32015-08-19 11:58:18 -04001171 if isinstance(inline, InstanceInline):
Tony Mack2bd5b412013-06-11 21:05:06 -04001172 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -04001173 yield inline.get_formset(request, obj)
1174
Scott Baker03394b32015-09-15 17:48:57 -07001175 def add_extra_context(self, request, extra_context):
1176 super(SliceAdmin, self).add_extra_context(request, extra_context)
1177 # set context["slice_id"] to the PK passed in the URL to this view
1178 if len(request.resolver_match.args)>0:
1179 extra_context["slice_id"] = request.resolver_match.args[0]
1180
Scott Baker0e289fd2015-06-12 10:40:15 -07001181 def UNUSED_get_inline_instances(self, request, obj=None):
1182 # HACK for CORD to do something special on vcpe slice page
1183 # this was a good idea, but failed miserably, as something still
1184 # expects there to be a deployment field.
1185 # XXX this approach is better than clobbering self.inlines, so
1186 # try to make this work post-demo.
1187 if (obj is not None) and (obj.name == "mysite_vcpe"):
Tony Mack3de59e32015-08-19 11:58:18 -04001188 cord_vcpe_inlines = [ SlicePrivilegeInline, CordInstanceInline, TagInline, ReservationInline,SliceNetworkInline]
Scott Baker0e289fd2015-06-12 10:40:15 -07001189
1190 inlines=[]
1191 for inline_class in cord_vcpe_inlines:
1192 inlines.append(inline_class(self.model, self.admin_site))
1193 else:
1194 inlines = super(SliceAdmin, self).get_inline_instances(request, obj)
1195
1196 return inlines
1197
Scott Baker022cdcd2015-02-18 15:50:11 -08001198class SlicePrivilegeAdmin(XOSBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -04001199 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -07001200 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -04001201 ]
Scott Baker40c00762014-08-21 16:55:59 -07001202 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -07001203 list_display = ('backend_status_icon', 'user', 'slice', 'role')
1204 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -04001205
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001206 user_readonly_fields = ['user', 'slice', 'role']
1207 user_readonly_inlines = []
1208
Tony Mackc2835a92013-05-28 09:18:49 -04001209 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1210 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001211 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001212
1213 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -05001214 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001215
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001216 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001217
Tony Mack04062832013-05-10 08:22:44 -04001218 def queryset(self, request):
1219 # admins can see all memberships. Users can only see memberships of
1220 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -05001221 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001222
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001223 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -04001224 # update openstack connection to use this site/tenant
1225 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001226 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001227 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001228 obj.save()
1229
1230 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -04001231 # update openstack connection to use this site/tenant
1232 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001233 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001234 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001235 obj.delete()
1236
Scott Baker022cdcd2015-02-18 15:50:11 -08001237class ImageAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001238
Scott Baker36f50872014-08-21 13:01:25 -07001239 fieldsets = [('Image Details',
Scott Baker14266232015-12-14 10:11:56 -08001240 {'fields': ['backend_status_text', 'name', 'kind', 'disk_format', 'container_format', 'tag', 'path'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001241 'classes': ['suit-tab suit-tab-general']})
1242 ]
Scott Baker40c00762014-08-21 16:55:59 -07001243 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001244
Tony Mack3de59e32015-08-19 11:58:18 -04001245 suit_form_tabs =(('general','Image Details'),('instances','Instances'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001246
Tony Mack3de59e32015-08-19 11:58:18 -04001247 inlines = [InstanceInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -07001248
Scott Baker14266232015-12-14 10:11:56 -08001249 user_readonly_fields = ['name', 'disk_format', 'container_format', 'tag', 'path']
Scott Bakerb27b62c2014-08-15 16:29:16 -07001250
Scott Baker0cefa532015-11-09 16:17:11 -08001251 list_display = ['backend_status_icon', 'name', 'kind']
Scott Baker63d1a552014-08-21 15:19:07 -07001252 list_display_links = ('backend_status_icon', 'name', )
1253
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001254class NodeForm(forms.ModelForm):
1255 class Meta:
Scott Baker2a11f852015-09-21 15:06:38 -07001256 model = Node
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001257 widgets = {
1258 'site': LinkedSelect,
1259 'deployment': LinkedSelect
1260 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001261
Scott Baker022cdcd2015-02-18 15:50:11 -08001262class NodeAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001263 form = NodeForm
Tony Mack93d1b032014-12-08 16:43:02 -05001264 list_display = ('backend_status_icon', 'name', 'site_deployment')
Scott Baker63d1a552014-08-21 15:19:07 -07001265 list_display_links = ('backend_status_icon', 'name', )
Tony Mack93d1b032014-12-08 16:43:02 -05001266 list_filter = ('site_deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001267
Tony Mack3de59e32015-08-19 11:58:18 -04001268 inlines = [TagInline,InstanceInline]
Scott Baker2a11f852015-09-21 15:06:38 -07001269 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name', 'site_deployment'], 'classes':['suit-tab suit-tab-details']})]
Scott Baker40c00762014-08-21 16:55:59 -07001270 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001271
Tony Mack93d1b032014-12-08 16:43:02 -05001272 user_readonly_fields = ['name','site_deployment']
Tony Mack3de59e32015-08-19 11:58:18 -04001273 user_readonly_inlines = [TagInline,InstanceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001274
Tony Mack3de59e32015-08-19 11:58:18 -04001275 suit_form_tabs =(('details','Node Details'),('instances','Instances'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001276
Tony Mack17b97b82015-08-04 17:32:32 -04001277 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1278 if db_field.name == 'site':
1279 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_nodes=True)
Siobhan Tully567e3e62013-06-21 18:03:16 -04001280
Scott Baker2a11f852015-09-21 15:06:38 -07001281 field = super(NodeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1282
1283 return field
1284
Tony Mack3de59e32015-08-19 11:58:18 -04001285class InstanceForm(forms.ModelForm):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001286 class Meta:
Tony Mack3de59e32015-08-19 11:58:18 -04001287 model = Instance
Tony Mackd90cdbf2013-04-16 22:48:40 -04001288 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001289 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001290 widgets = {
1291 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001292 'instance_name': PlainTextWidget(),
Scott Baker887d4a82015-01-19 11:32:20 -08001293 'instance_id': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001294 'slice': LinkedSelect,
Tony Mackbf6aa302014-12-26 13:38:02 -05001295 'deployment': LinkedSelect,
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001296 'node': LinkedSelect,
1297 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001298 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001299
Scott Baker022cdcd2015-02-18 15:50:11 -08001300class TagAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001301 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1302 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001303 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1304 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001305
Tony Mack8bc36b92015-09-01 16:06:52 +00001306class InstancePortInline(XOSTabularInline):
Tony Mackea30da82015-09-10 21:58:15 +00001307 fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
Scott Baker0672e982015-09-08 18:22:15 -07001308 readonly_fields = ("backend_status_icon", "ip", "mac")
Scott Baker43facad2015-08-26 09:43:33 -07001309 model = Port
Scott Baker29514bc2015-11-16 16:21:47 -08001310 #selflink_fieldname = "network"
Scott Baker0bdb6a52015-08-25 18:00:15 -07001311 extra = 0
1312 verbose_name_plural = "Ports"
1313 verbose_name = "Port"
1314 suit_classes = 'suit-tab suit-tab-ports'
1315
Tony Mack3de59e32015-08-19 11:58:18 -04001316class InstanceAdmin(XOSBaseAdmin):
1317 form = InstanceForm
Tony Mackcdec0902013-04-15 00:38:49 -04001318 fieldsets = [
Scott Baker04a356a2015-11-12 17:25:53 -08001319 ('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 -08001320 ('Container Settings', {'fields': ['volumes'], 'classes': ['suit-tab suit-tab-container'], }),
Tony Mackcdec0902013-04-15 00:38:49 -04001321 ]
Tony Mack7d61efb2015-01-30 17:20:46 -05001322 readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Scott Baker0cefa532015-11-09 16:17:11 -08001323 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 -08001324 list_display_links = ('backend_status_icon', 'all_ips_string', 'instance_id', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001325
Scott Bakere1a6e0b2015-11-10 17:07:23 -08001326 suit_form_tabs =(('general', 'Instance Details'), ('ports', 'Ports'), ('container', 'Container Settings'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001327
Tony Mack6a782f92015-09-13 22:50:39 +00001328 inlines = [TagInline, InstancePortInline]
Tony Mack53106f32013-04-27 16:43:01 -04001329
Tony Mackbf6aa302014-12-26 13:38:02 -05001330 user_readonly_fields = ['slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001331
Scott Baker970314b2015-01-25 22:16:13 -08001332 def ssh_command(self, obj):
1333 ssh_command = obj.get_ssh_command()
1334 if ssh_command:
1335 return ssh_command
1336 else:
1337 return "(not available)"
1338
Tony Mackc2835a92013-05-28 09:18:49 -04001339 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1340 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001341 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001342
Tony Mack3de59e32015-08-19 11:58:18 -04001343 return super(InstanceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001344
Tony Mack04062832013-05-10 08:22:44 -04001345 def queryset(self, request):
Tony Mack3de59e32015-08-19 11:58:18 -04001346 # admins can see all instances. Users can only see instances of
Tony Mack04062832013-05-10 08:22:44 -04001347 # the slices they belong to.
Tony Mack3de59e32015-08-19 11:58:18 -04001348 return Instance.select_by_user(request.user)
Tony Mack5b061472014-02-04 07:57:10 -05001349
Scott Bakerb84392d2015-09-21 21:42:41 -07001350 def add_view(self, request, form_url='', extra_context = None):
1351 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
1352 return super(InstanceAdmin,self).add_view(request, form_url, extra_context)
Tony Mack04062832013-05-10 08:22:44 -04001353
Scott Bakerb84392d2015-09-21 21:42:41 -07001354 def change_view(self, request, object_id, extra_context=None):
1355 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string', 'deployment', 'slice', 'flavor', 'image', 'node')
1356 self.readonly_save = self.readonly_fields # for XOSAdminMixin.change_view's user_readonly_fields switching code
1357 return super(InstanceAdmin,self).change_view(request, object_id, extra_context)
Tony Mack53106f32013-04-27 16:43:01 -04001358
Scott Baker530e4de2015-09-21 16:02:54 -07001359 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
1360 deployment_nodes = []
1361 for node in Node.objects.all():
1362 deployment_nodes.append( (node.site_deployment.deployment.id, node.id, node.name) )
1363
1364 deployment_flavors = []
1365 for flavor in Flavor.objects.all():
1366 for deployment in flavor.deployments.all():
1367 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
1368
1369 deployment_images = []
1370 for image in Image.objects.all():
1371 for deployment_image in image.imagedeployments.all():
1372 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
1373
1374 site_login_bases = []
1375 for site in Site.objects.all():
1376 site_login_bases.append((site.id, site.login_base))
1377
1378 context["deployment_nodes"] = deployment_nodes
1379 context["deployment_flavors"] = deployment_flavors
1380 context["deployment_images"] = deployment_images
1381 context["site_login_bases"] = site_login_bases
1382 return super(InstanceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
1383
1384 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1385 if db_field.name == 'deployment':
1386 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
1387 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_deployment_changed(this);"})
1388 if db_field.name == 'flavor':
1389 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_flavor_changed(this);"})
1390
1391 field = super(InstanceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1392
1393 return field
1394
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001395 #def save_model(self, request, obj, form, change):
1396 # # update openstack connection to use this site/tenant
1397 # auth = request.session.get('auth', {})
1398 # auth['tenant'] = obj.slice.name
1399 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1400 # obj.creator = request.user
1401 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001402
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001403 #def delete_model(self, request, obj):
1404 # # update openstack connection to use this site/tenant
1405 # auth = request.session.get('auth', {})
1406 # auth['tenant'] = obj.slice.name
1407 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1408 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001409
Scott Baker0cefa532015-11-09 16:17:11 -08001410#class ContainerPortInline(XOSTabularInline):
1411# fields = ['backend_status_icon', 'network', 'container', 'ip', 'mac', 'segmentation_id']
1412# readonly_fields = ("backend_status_icon", "ip", "mac", "segmentation_id")
1413# model = Port
1414# selflink_fieldname = "network"
1415# extra = 0
1416# verbose_name_plural = "Ports"
1417# verbose_name = "Port"
1418# suit_classes = 'suit-tab suit-tab-ports'
Scott Baker1de8b0d2015-10-26 19:52:10 -07001419
Scott Baker0cefa532015-11-09 16:17:11 -08001420#class ContainerAdmin(XOSBaseAdmin):
1421# fieldsets = [
1422# ('Container Details', {'fields': ['backend_status_text', 'slice', 'node', 'docker_image', 'volumes', 'no_sync'], 'classes': ['suit-tab suit-tab-general'], })
1423# ]
1424# readonly_fields = ('backend_status_text', )
1425# list_display = ['backend_status_icon', 'id']
1426# list_display_links = ('backend_status_icon', 'id', )
1427#
1428# suit_form_tabs =(('general', 'Container Details'), ('ports', 'Ports'))
1429#
1430# inlines = [TagInline, ContainerPortInline]
1431#
1432# def formfield_for_foreignkey(self, db_field, request, **kwargs):
1433# if db_field.name == 'slice':
1434# kwargs['queryset'] = Slice.select_by_user(request.user)
1435#
1436# return super(ContainerAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1437#
1438# def queryset(self, request):
1439# # admins can see all instances. Users can only see instances of
1440# # the slices they belong to.
1441# return Container.select_by_user(request.user)
Scott Bakerda513652015-10-26 15:12:13 -07001442
Siobhan Tully53437282013-04-26 19:30:27 -04001443class UserCreationForm(forms.ModelForm):
1444 """A form for creating new users. Includes all the required
1445 fields, plus a repeated password."""
1446 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1447 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1448
1449 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001450 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001451 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001452
1453 def clean_password2(self):
1454 # Check that the two password entries match
1455 password1 = self.cleaned_data.get("password1")
1456 password2 = self.cleaned_data.get("password2")
1457 if password1 and password2 and password1 != password2:
1458 raise forms.ValidationError("Passwords don't match")
1459 return password2
1460
1461 def save(self, commit=True):
1462 # Save the provided password in hashed format
1463 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001464 user.password = self.cleaned_data["password1"]
1465 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001466 if commit:
1467 user.save()
1468 return user
1469
Siobhan Tully567e3e62013-06-21 18:03:16 -04001470
Siobhan Tully53437282013-04-26 19:30:27 -04001471class UserChangeForm(forms.ModelForm):
1472 """A form for updating users. Includes all the fields on
1473 the user, but replaces the password field with admin's
1474 password hash display field.
1475 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001476 password = ReadOnlyPasswordHashField(label='Password',
1477 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001478
Scott Baker811d4472015-05-19 16:39:48 -07001479 PROFILE_CHOICES = ((None, '------'), ('regular', 'Regular user'), ('cp', 'Content Provider'))
1480 profile = forms.ChoiceField(choices=PROFILE_CHOICES, required=False, label="Quick Profile")
1481
Siobhan Tully53437282013-04-26 19:30:27 -04001482 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001483 model = User
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001484 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001485
1486 def clean_password(self):
1487 # Regardless of what the user provides, return the initial value.
1488 # This is done here, rather than on the field, because the
1489 # field does not have access to the initial value
1490 return self.initial["password"]
1491
Scott Baker811d4472015-05-19 16:39:48 -07001492 def save(self, *args, **kwargs):
1493 if self.cleaned_data['profile']:
1494 self.instance.apply_profile(self.cleaned_data['profile'])
1495
1496 return super(UserChangeForm, self).save(*args, **kwargs)
1497
Scott Baker022cdcd2015-02-18 15:50:11 -08001498class UserDashboardViewInline(XOSTabularInline):
Scott Baker2c3cb642014-05-19 17:55:56 -07001499 model = UserDashboardView
1500 extra = 0
1501 suit_classes = 'suit-tab suit-tab-dashboards'
1502 fields = ['user', 'dashboardView', 'order']
1503
Scott Baker022cdcd2015-02-18 15:50:11 -08001504class ControllerUserInline(XOSTabularInline):
Tony Mack3c01ff92015-01-10 23:08:10 -05001505 model = ControllerUser
1506 extra = 0
1507 suit_classes = 'suit-tab suit-tab-admin-only'
1508 fields = ['controller', 'user', 'kuser_id']
1509 readonly_fields=['controller']
1510
1511
Scott Bakera9412c32015-02-27 12:21:22 -08001512class UserAdmin(XOSAdminMixin, UserAdmin):
1513 # Note: Make sure XOSAdminMixin is listed before
Scott Baker86c83ab2014-10-03 13:10:47 -07001514 # admin.ModelAdmin in the class declaration.
1515
Siobhan Tully53437282013-04-26 19:30:27 -04001516 class Meta:
1517 app_label = "core"
1518
1519 # The forms to add and change user instances
1520 form = UserChangeForm
1521 add_form = UserCreationForm
1522
1523 # The fields to be used in displaying the User model.
1524 # These override the definitions on the base UserAdmin
1525 # that reference specific fields on auth.User.
Scott Bakerf587f442015-01-24 13:33:26 -08001526 list_display = ('backend_status_icon', 'email', 'firstname', 'lastname', 'site', 'last_login')
1527 list_display_links = ("email",)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001528 list_filter = ('site',)
Scott Baker6e9027f2015-01-29 10:55:53 -08001529 inlines = [SlicePrivilegeInline,SitePrivilegeInline]
Tony Mack3c01ff92015-01-10 23:08:10 -05001530 admin_inlines = [ControllerUserInline]
Scott Bakere61e3a02015-06-10 16:14:58 -07001531 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 -05001532 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1533
Siobhan Tully53437282013-04-26 19:30:27 -04001534 fieldsets = (
Scott Baker74ebc7a2015-05-15 09:19:36 -07001535 ('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 -04001536 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001537 #('Important dates', {'fields': ('last_login',)}),
1538 )
1539 add_fieldsets = (
1540 (None, {
1541 'classes': ('wide',),
Scott Baker74ebc7a2015-05-15 09:19:36 -07001542 'fields': ('site', 'email', 'firstname', 'lastname', 'is_admin', 'is_readonly', 'is_appuser', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001543 ),
1544 )
Scott Baker40c00762014-08-21 16:55:59 -07001545 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001546 search_fields = ('email',)
1547 ordering = ('email',)
1548 filter_horizontal = ()
1549
Scott Baker3ca51f62014-05-23 12:05:11 -07001550 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001551
Scott Baker0a5633b2014-10-06 17:51:20 -07001552 @property
1553 def suit_form_tabs(self):
1554 if getattr(_thread_locals, "obj", None) is None:
1555 return []
1556 else:
Tony Mack3c01ff92015-01-10 23:08:10 -05001557 tabs = [('general','Login Details'),
Scott Baker0a5633b2014-10-06 17:51:20 -07001558 ('contact','Contact Information'),
1559 ('sliceprivileges','Slice Privileges'),
Scott Baker6e9027f2015-01-29 10:55:53 -08001560 ('siteprivileges','Site Privileges')]
Tony Mack3c01ff92015-01-10 23:08:10 -05001561
1562 request=getattr(_thread_locals, "request", None)
1563 if request and request.user.is_admin:
1564 tabs.append( ('admin-only', 'Admin-Only') )
1565
1566 return tabs
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001567
Tony Mackc2835a92013-05-28 09:18:49 -04001568 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1569 if db_field.name == 'site':
Tony Mack17b97b82015-08-04 17:32:32 -04001570 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_users=True)
Tony Mackc2835a92013-05-28 09:18:49 -04001571
1572 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1573
Tony Mack5b061472014-02-04 07:57:10 -05001574 def queryset(self, request):
1575 return User.select_by_user(request.user)
1576
Tony Mack6235fc82015-01-25 21:58:30 -05001577 def get_form(self, request, obj=None, **kwargs):
Tony Mackb2c407f2015-01-28 12:37:12 -05001578 # copy login details list
1579 login_details_fields = list(self.fieldListLoginDetails)
Tony Mack92b12052015-01-28 12:49:58 -05001580 if not request.user.is_admin:
Scott Baker6e9027f2015-01-29 10:55:53 -08001581 # only admins can see 'is_admin' and 'is_readonly' fields
Tony Mackb2c407f2015-01-28 12:37:12 -05001582 if 'is_admin' in login_details_fields:
1583 login_details_fields.remove('is_admin')
1584 if 'is_readonly' in login_details_fields:
Scott Baker811d4472015-05-19 16:39:48 -07001585 login_details_fields.remove('is_readonly')
1586 if 'is_appuser' in login_details_fields:
1587 login_details_fields.remove('is_admin')
1588 if 'profile' in login_details_fields:
1589 login_details_fields.remove('profile')
Tony Mack92b12052015-01-28 12:49:58 -05001590 #if len(request.user.siteprivileges.filter(role__role = 'pi')) > 0:
Scott Baker811d4472015-05-19 16:39:48 -07001591 # only admins and pis can change a user's site
Tony Mack92b12052015-01-28 12:49:58 -05001592 # self.readonly_fields = ('backend_status_text', 'site')
Tony Mackb2c407f2015-01-28 12:37:12 -05001593 self.fieldsets = (
1594 ('Login Details', {'fields': login_details_fields, 'classes':['suit-tab suit-tab-general']}),
1595 ('Contact Information', {'fields': self.fieldListContactInfo, 'classes':['suit-tab suit-tab-contact']}),
1596 )
Tony Mack6235fc82015-01-25 21:58:30 -05001597 return super(UserAdmin, self).get_form(request, obj, **kwargs)
1598
Scott Baker022cdcd2015-02-18 15:50:11 -08001599class ControllerDashboardViewInline(XOSTabularInline):
Scott Bakera6a0c772014-12-22 17:35:34 -08001600 model = ControllerDashboardView
Scott Bakereef5a6b2014-12-19 16:41:12 -08001601 extra = 0
1602 fields = ["controller", "url"]
1603 suit_classes = 'suit-tab suit-tab-controllers'
1604
Scott Baker022cdcd2015-02-18 15:50:11 -08001605class DashboardViewAdmin(XOSBaseAdmin):
Scott Baker2c3cb642014-05-19 17:55:56 -07001606 fieldsets = [('Dashboard View Details',
Scott Bakerecc55ac2015-02-17 13:34:32 -08001607 {'fields': ['backend_status_text', 'name', 'url', 'enabled', 'deployments'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001608 'classes': ['suit-tab suit-tab-general']})
1609 ]
Scott Baker9daf19c2015-01-18 16:46:26 -08001610 list_display = ["name", "enabled", "url"]
Scott Baker40c00762014-08-21 16:55:59 -07001611 readonly_fields = ('backend_status_text', )
Scott Bakera6a0c772014-12-22 17:35:34 -08001612 inlines = [ControllerDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001613
Scott Bakereef5a6b2014-12-19 16:41:12 -08001614 suit_form_tabs =(('general','Dashboard View Details'),
1615 ('controllers', 'Per-controller Dashboard Details'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001616
Scott Baker022cdcd2015-02-18 15:50:11 -08001617class ServiceResourceInline(XOSTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001618 model = ServiceResource
1619 extra = 0
1620
Scott Baker022cdcd2015-02-18 15:50:11 -08001621class ServiceClassAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001622 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1623 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001624 inlines = [ServiceResourceInline]
1625
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001626 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1627 user_readonly_inlines = []
1628
Scott Baker022cdcd2015-02-18 15:50:11 -08001629class ReservedResourceInline(XOSTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001630 model = ReservedResource
1631 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001632 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001633
1634 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1635 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1636
1637 if db_field.name == 'resource':
1638 # restrict resources to those that the slice's service class allows
1639 if request._slice is not None:
1640 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1641 if len(field.queryset) > 0:
1642 field.initial = field.queryset.all()[0]
S.Çağlar Onur0e591832015-02-24 17:28:09 -05001643 else:
1644 field.queryset = field.queryset.none()
Tony Mack3de59e32015-08-19 11:58:18 -04001645 elif db_field.name == 'instance':
1646 # restrict instances to those that belong to the slice
S.Çağlar Onur0e591832015-02-24 17:28:09 -05001647 if request._slice is not None:
Scott Baker133c9212013-05-17 09:09:11 -07001648 field.queryset = field.queryset.filter(slice = request._slice)
1649 else:
S.Çağlar Onur0e591832015-02-24 17:28:09 -05001650 field.queryset = field.queryset.none()
1651
Scott Baker133c9212013-05-17 09:09:11 -07001652 return field
1653
Tony Mack5b061472014-02-04 07:57:10 -05001654 def queryset(self, request):
1655 return ReservedResource.select_by_user(request.user)
1656
Scott Baker133c9212013-05-17 09:09:11 -07001657class ReservationChangeForm(forms.ModelForm):
1658 class Meta:
1659 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001660 widgets = {
1661 'slice' : LinkedSelect
1662 }
Scott Baker133c9212013-05-17 09:09:11 -07001663
1664class ReservationAddForm(forms.ModelForm):
1665 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1666 refresh = forms.CharField(widget=forms.HiddenInput())
1667
1668 class Media:
Scott Baker97468b72015-02-18 15:15:58 -08001669 css = {'all': ('xos.css',)} # .field-refresh { display: none; }
Scott Baker133c9212013-05-17 09:09:11 -07001670
1671 def clean_slice(self):
1672 slice = self.cleaned_data.get("slice")
1673 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1674 if len(x) == 0:
1675 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1676 return slice
1677
1678 class Meta:
1679 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001680 widgets = {
1681 'slice' : LinkedSelect
1682 }
1683
Scott Baker133c9212013-05-17 09:09:11 -07001684
1685class ReservationAddRefreshForm(ReservationAddForm):
1686 """ This form is displayed when the Reservation Form receives an update
1687 from the Slice dropdown onChange handler. It doesn't validate the
1688 data and doesn't save the data. This will cause the form to be
1689 redrawn.
1690 """
1691
Scott Baker8737e5f2013-05-17 09:35:32 -07001692 """ don't validate anything other than slice """
1693 dont_validate_fields = ("startTime", "duration")
1694
Scott Baker133c9212013-05-17 09:09:11 -07001695 def full_clean(self):
1696 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001697
1698 for fieldname in self.dont_validate_fields:
1699 if fieldname in self._errors:
1700 del self._errors[fieldname]
1701
Scott Baker133c9212013-05-17 09:09:11 -07001702 return result
1703
1704 """ don't save anything """
1705 def is_valid(self):
1706 return False
1707
Scott Baker022cdcd2015-02-18 15:50:11 -08001708class ReservationAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001709 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001710 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001711 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001712 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001713 form = ReservationAddForm
1714
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001715 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1716
1717 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001718 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001719
Scott Baker133c9212013-05-17 09:09:11 -07001720 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001721 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001722 request._refresh = False
1723 request._slice = None
1724 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001725 # "refresh" will be set to "1" if the form was submitted due to
1726 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001727 if request.POST.get("refresh","1") == "1":
1728 request._refresh = True
1729 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001730
1731 # Keep track of the slice that was selected, so the
1732 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001733 request._slice = request.POST.get("slice",None)
1734 if (request._slice is not None):
1735 request._slice = Slice.objects.get(id=request._slice)
1736
1737 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1738 return result
1739
Scott Bakeracd45142013-05-19 16:19:16 -07001740 def changelist_view(self, request, extra_context = None):
1741 timezone.activate(request.user.timezone)
1742 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1743
Scott Baker133c9212013-05-17 09:09:11 -07001744 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001745 request._obj_ = obj
1746 if obj is not None:
1747 # For changes, set request._slice to the slice already set in the
1748 # object.
1749 request._slice = obj.slice
1750 self.form = ReservationChangeForm
1751 else:
1752 if getattr(request, "_refresh", False):
1753 self.form = ReservationAddRefreshForm
1754 else:
1755 self.form = ReservationAddForm
1756 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1757
Scott Baker133c9212013-05-17 09:09:11 -07001758 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001759 if (obj is not None):
1760 # Prevent slice from being changed after the reservation has been
1761 # created.
1762 return ['slice']
1763 else:
Scott Baker133c9212013-05-17 09:09:11 -07001764 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001765
Tony Mack5b061472014-02-04 07:57:10 -05001766 def queryset(self, request):
1767 return Reservation.select_by_user(request.user)
1768
Scott Baker022cdcd2015-02-18 15:50:11 -08001769class NetworkParameterTypeAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001770 list_display = ("backend_status_icon", "name", )
1771 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001772 user_readonly_fields = ['name']
1773 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001774
Scott Baker022cdcd2015-02-18 15:50:11 -08001775class RouterAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001776 list_display = ("backend_status_icon", "name", )
1777 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001778 user_readonly_fields = ['name']
1779 user_readonly_inlines = []
1780
Scott Baker022cdcd2015-02-18 15:50:11 -08001781class RouterInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001782 model = Router.networks.through
1783 extra = 0
1784 verbose_name_plural = "Routers"
1785 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001786 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001787
Scott Bakerb27b62c2014-08-15 16:29:16 -07001788class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001789 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001790 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001791 verbose_name_plural = "Parameters"
1792 verbose_name = "Parameter"
1793 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001794 fields = ['backend_status_icon', 'parameter', 'value']
1795 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001796
Scott Baker0bdb6a52015-08-25 18:00:15 -07001797class NetworkPortInline(XOSTabularInline):
Scott Baker0cefa532015-11-09 16:17:11 -08001798 fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
Scott Baker0672e982015-09-08 18:22:15 -07001799 readonly_fields = ("backend_status_icon", "ip", "mac")
Scott Baker43facad2015-08-26 09:43:33 -07001800 model = Port
Scott Bakerf3e905d2015-11-16 13:43:38 -08001801 #selflink_fieldname = "instance"
Scott Baker74d8e622013-07-29 16:04:22 -07001802 extra = 0
Scott Bakera68e6e32015-08-25 17:11:30 -07001803 verbose_name_plural = "Ports"
1804 verbose_name = "Port"
1805 suit_classes = 'suit-tab suit-tab-ports'
Scott Baker74d8e622013-07-29 16:04:22 -07001806
Scott Baker022cdcd2015-02-18 15:50:11 -08001807class NetworkSlicesInline(XOSTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001808 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001809 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001810 extra = 0
1811 verbose_name_plural = "Slices"
1812 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001813 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001814 fields = ['backend_status_icon', 'network','slice']
1815 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001816
Scott Baker022cdcd2015-02-18 15:50:11 -08001817class ControllerNetworkInline(XOSTabularInline):
Tony Macka7dbd422015-01-05 22:48:11 -05001818 model = ControllerNetwork
Scott Bakerfbb45862014-10-17 16:27:23 -07001819 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001820 verbose_name_plural = "Controller Networks"
1821 verbose_name = "Controller Network"
Scott Bakerfbb45862014-10-17 16:27:23 -07001822 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001823 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Bakerfbb45862014-10-17 16:27:23 -07001824 readonly_fields = ('backend_status_icon', )
1825
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001826class NetworkForm(forms.ModelForm):
1827 class Meta:
1828 model = Network
1829 widgets = {
1830 'topologyParameters': UploadTextareaWidget,
1831 'controllerParameters': UploadTextareaWidget,
1832 }
1833
Scott Baker022cdcd2015-02-18 15:50:11 -08001834class NetworkAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001835 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1836 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001837 readonly_fields = ("subnet", )
Scott Baker0bdb6a52015-08-25 18:00:15 -07001838 inlines = [NetworkParameterInline, NetworkPortInline, NetworkSlicesInline, RouterInline]
Tony Macka7dbd422015-01-05 22:48:11 -05001839 admin_inlines = [ControllerNetworkInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001840
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001841 form=NetworkForm
1842
Siobhan Tully2d95e482013-09-06 10:56:06 -04001843 fieldsets = [
Scott Bakera4c11bd2015-08-21 16:40:53 -07001844 (None, {'fields': ['backend_status_text', 'name','template','ports','labels',
1845 'owner','guaranteed_bandwidth', 'permit_all_slices',
1846 'permitted_slices','network_id','router_id','subnet_id',
1847 'subnet', 'autoconnect'],
Scott Baker40248712014-11-17 16:04:45 -08001848 'classes':['suit-tab suit-tab-general']}),
Scott Baker0451fb62015-01-03 12:29:29 -08001849 (None, {'fields': ['topology_parameters', 'controller_url', 'controller_parameters'],
Scott Baker40248712014-11-17 16:04:45 -08001850 'classes':['suit-tab suit-tab-sdn']}),
1851 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001852
Scott Baker40c00762014-08-21 16:55:59 -07001853 readonly_fields = ('backend_status_text', )
Scott Bakera4c11bd2015-08-21 16:40:53 -07001854 user_readonly_fields = ['name','template','ports','labels','owner','guaranteed_bandwidth',
1855 'permit_all_slices','permitted_slices','network_id','router_id',
1856 'subnet_id','subnet','autoconnect']
Siobhan Tully2d95e482013-09-06 10:56:06 -04001857
Scott Bakerfbb45862014-10-17 16:27:23 -07001858 @property
1859 def suit_form_tabs(self):
1860 tabs=[('general','Network Details'),
Scott Baker40248712014-11-17 16:04:45 -08001861 ('sdn', 'SDN Configuration'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001862 ('netparams', 'Parameters'),
Scott Bakera68e6e32015-08-25 17:11:30 -07001863 ('ports','Ports'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001864 ('networkslices','Slices'),
1865 ('routers','Routers'),
1866 ]
1867
1868 request=getattr(_thread_locals, "request", None)
1869 if request and request.user.is_admin:
1870 tabs.append( ('admin-only', 'Admin-Only') )
1871
1872 return tabs
1873
1874
Scott Baker022cdcd2015-02-18 15:50:11 -08001875class NetworkTemplateAdmin(XOSBaseAdmin):
Scott Baker81fa17f2015-01-03 12:03:38 -08001876 list_display = ("backend_status_icon", "name", "guaranteed_bandwidth", "visibility")
Scott Baker63d1a552014-08-21 15:19:07 -07001877 list_display_links = ('backend_status_icon', 'name', )
Scott Baker81fa17f2015-01-03 12:03:38 -08001878 user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001879 user_readonly_inlines = []
Scott Baker29514bc2015-11-16 16:21:47 -08001880 inlines = [NetworkParameterInline,]
Scott Baker40248712014-11-17 16:04:45 -08001881 fieldsets = [
Scott Baker9d5bf3b2015-12-09 15:44:55 -08001882 (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 -08001883 'classes':['suit-tab suit-tab-general']}),]
Scott Baker29514bc2015-11-16 16:21:47 -08001884 suit_form_tabs = (('general','Network Template Details'), ('netparams', 'Parameters') )
Scott Baker74d8e622013-07-29 16:04:22 -07001885
Scott Bakerf3e905d2015-11-16 13:43:38 -08001886class PortAdmin(XOSBaseAdmin):
1887 list_display = ("backend_status_icon", "name", "id", "ip")
1888 list_display_links = ('backend_status_icon', 'id')
1889 readonly_fields = ("subnet", )
1890 inlines = [NetworkParameterInline]
1891
1892 fieldsets = [
1893 (None, {'fields': ['backend_status_text', 'network', 'instance', 'ip', 'port_id', 'mac'],
1894 'classes':['suit-tab suit-tab-general']}),
1895 ]
1896
1897 readonly_fields = ('backend_status_text', )
1898 suit_form_tabs = (('general', 'Port Details'), ('netparams', 'Parameters'))
1899
Scott Baker022cdcd2015-02-18 15:50:11 -08001900class FlavorAdmin(XOSBaseAdmin):
Scott Baker37b47902014-09-02 14:37:41 -07001901 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1902 list_display_links = ("backend_status_icon", "name")
1903 user_readonly_fields = ("name", "flavor")
1904 fields = ("name", "description", "flavor", "order", "default")
1905
Tony Mack31c2b8f2013-04-26 20:01:42 -04001906# register a signal that caches the user's credentials when they log in
1907def cache_credentials(sender, user, request, **kwds):
1908 auth = {'username': request.POST['username'],
1909 'password': request.POST['password']}
1910 request.session['auth'] = auth
1911user_logged_in.connect(cache_credentials)
1912
Scott Baker15cddfa2013-12-09 13:45:19 -08001913def dollar_field(fieldName, short_description):
1914 def newFunc(self, obj):
1915 try:
1916 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1917 except:
1918 x=getattr(obj, fieldName, 0.0)
1919 return x
1920 newFunc.short_description = short_description
1921 return newFunc
1922
1923def right_dollar_field(fieldName, short_description):
1924 def newFunc(self, obj):
1925 try:
1926 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1927 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1928 except:
1929 x=getattr(obj, fieldName, 0.0)
1930 return x
1931 newFunc.short_description = short_description
1932 newFunc.allow_tags = True
1933 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001934
Scott Baker022cdcd2015-02-18 15:50:11 -08001935class InvoiceChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001936 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001937 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001938 verbose_name_plural = "Charges"
1939 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001940 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001941 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1942 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1943 can_delete = False
1944 max_num = 0
1945
1946 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001947
1948class InvoiceAdmin(admin.ModelAdmin):
1949 list_display = ("date", "account")
1950
1951 inlines = [InvoiceChargeInline]
1952
Scott Baker9cb88a22013-12-09 18:56:00 -08001953 fields = ["date", "account", "dollar_amount"]
1954 readonly_fields = ["date", "account", "dollar_amount"]
1955
1956 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001957
Scott Baker022cdcd2015-02-18 15:50:11 -08001958class InvoiceInline(XOSTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001959 model = Invoice
1960 extra = 0
1961 verbose_name_plural = "Invoices"
1962 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001963 fields = ["date", "dollar_amount"]
1964 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001965 suit_classes = 'suit-tab suit-tab-accountinvoice'
1966 can_delete=False
1967 max_num=0
1968
1969 dollar_amount = right_dollar_field("amount", "Amount")
1970
Scott Baker022cdcd2015-02-18 15:50:11 -08001971class PendingChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001972 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001973 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001974 verbose_name_plural = "Charges"
1975 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001976 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001977 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1978 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001979 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001980 can_delete=False
1981 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001982
1983 def queryset(self, request):
1984 qs = super(PendingChargeInline, self).queryset(request)
1985 qs = qs.filter(state="pending")
1986 return qs
1987
Scott Baker15cddfa2013-12-09 13:45:19 -08001988 dollar_amount = right_dollar_field("amount", "Amount")
1989
Scott Baker022cdcd2015-02-18 15:50:11 -08001990class PaymentInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001991 model=Payment
1992 extra = 1
1993 verbose_name_plural = "Payments"
1994 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001995 fields = ["date", "dollar_amount"]
1996 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001997 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001998 can_delete=False
1999 max_num=0
2000
2001 dollar_amount = right_dollar_field("amount", "Amount")
2002
Scott Baker43105042013-12-06 23:23:36 -08002003class AccountAdmin(admin.ModelAdmin):
2004 list_display = ("site", "balance_due")
2005
2006 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
2007
2008 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05002009 (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 -08002010
Scott Baker15cddfa2013-12-09 13:45:19 -08002011 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08002012
2013 suit_form_tabs =(
2014 ('general','Account Details'),
2015 ('accountinvoice', 'Invoices'),
2016 ('accountpayments', 'Payments'),
2017 ('accountpendingcharges','Pending Charges'),
2018 )
2019
Scott Baker15cddfa2013-12-09 13:45:19 -08002020 dollar_balance_due = dollar_field("balance_due", "Balance Due")
2021 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
2022 dollar_total_payments = dollar_field("total_payments", "Total Payments")
2023
Scott Baker2461bec2015-08-14 09:10:11 -07002024class ProgramForm(forms.ModelForm):
2025 class Meta:
2026 model = Program
2027 widgets = {
2028 'contents': UploadTextareaWidget(attrs={'rows': 20, 'cols': 80, 'class': "input-xxlarge"}),
2029 'description': forms.Textarea(attrs={'rows': 3, 'cols': 80, 'class': 'input-xxlarge'}),
2030 'messages': forms.Textarea(attrs={'rows': 20, 'cols': 80, 'class': 'input-xxlarge'}),
2031 'output': forms.Textarea(attrs={'rows': 3, 'cols': 80, 'class': 'input-xxlarge'})
2032 }
2033
2034class ProgramAdmin(XOSBaseAdmin):
2035 list_display = ("name", "status")
2036 list_display_links = ('name', "status")
2037
2038 form=ProgramForm
2039
2040 fieldsets = [
2041 (None, {'fields': ['name', 'command', 'kind', 'description', 'output', 'status'],
2042 'classes':['suit-tab suit-tab-general']}),
2043 (None, {'fields': ['contents'],
2044 'classes':['suit-tab suit-tab-contents']}),
2045 (None, {'fields': ['messages'],
2046 'classes':['suit-tab suit-tab-messages']}),
2047 ]
2048
2049 readonly_fields = ("status",)
2050
2051 @property
2052 def suit_form_tabs(self):
2053 tabs=[('general','Program Details'),
2054 ('contents','Program Source'),
2055 ('messages','Messages'),
2056 ]
2057
2058 request=getattr(_thread_locals, "request", None)
2059 if request and request.user.is_admin:
2060 tabs.append( ('admin-only', 'Admin-Only') )
2061
2062 return tabs
2063
Siobhan Tully53437282013-04-26 19:30:27 -04002064# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04002065admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04002066# ... and, since we're not using Django's builtin permissions,
2067# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04002068#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04002069
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002070# When debugging it is often easier to see all the classes, but for regular use
2071# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05002072showAll = False
Scott Baker43105042013-12-06 23:23:36 -08002073
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002074admin.site.register(Deployment, DeploymentAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05002075admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04002076admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04002077admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04002078admin.site.register(Service, ServiceAdmin)
Tony Mack598eaf22015-01-25 12:35:29 -05002079#admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07002080admin.site.register(Network, NetworkAdmin)
Scott Bakerf3e905d2015-11-16 13:43:38 -08002081admin.site.register(Port, PortAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07002082admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07002083admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Scott Baker2461bec2015-08-14 09:10:11 -07002084admin.site.register(Program, ProgramAdmin)
Tony Mack598eaf22015-01-25 12:35:29 -05002085#admin.site.register(Account, AccountAdmin)
2086#admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002087
Siobhan Tullycf04fb62014-01-11 11:25:57 -05002088if True:
2089 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
2090 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullyd3515752013-06-21 16:34:53 -04002091 admin.site.register(Tag, TagAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05002092 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04002093 admin.site.register(SiteRole)
2094 admin.site.register(SliceRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002095 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04002096 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
2097 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Tony Mack3de59e32015-08-19 11:58:18 -04002098 admin.site.register(Instance, InstanceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002099 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07002100 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07002101 admin.site.register(Flavor, FlavorAdmin)
Scott Baker1b06b6c2015-07-06 14:40:20 -07002102 admin.site.register(TenantRoot, TenantRootAdmin)
2103 admin.site.register(TenantRootRole, TenantRootRoleAdmin)
Scott Baker462a1d92015-10-15 15:59:19 -07002104 admin.site.register(TenantAttribute, TenantAttributeAdmin)
Scott Baker0cefa532015-11-09 16:17:11 -08002105# admin.site.register(Container, ContainerAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04002106