blob: 3c35768f83b44bdeccde52e9bb5e79b17e38ee48 [file] [log] [blame]
Siobhan Tully30fd4292013-05-10 08:59:56 -04001from core.models import Site
2from core.models import *
3from openstack.manager import OpenStackManager
Tony Macke59a7c82013-04-27 11:08:10 -04004
Tony Mack7130ac32013-03-22 21:58:00 -04005from django.contrib import admin
Siobhan Tully53437282013-04-26 19:30:27 -04006from django.contrib.auth.models import Group
Siobhan Tully4bc09f22013-04-10 21:15:21 -04007from django import forms
Tony Mackd90cdbf2013-04-16 22:48:40 -04008from django.utils.safestring import mark_safe
Tony Mack7130ac32013-03-22 21:58:00 -04009from django.contrib.auth.admin import UserAdmin
Scott Baker69e045d2014-11-17 23:44:03 -080010from django.contrib.admin.widgets import FilteredSelectMultiple, AdminTextareaWidget
Scott Baker1a6a3902014-10-03 00:32:37 -070011from django.contrib.auth.forms import ReadOnlyPasswordHashField, AdminPasswordChangeForm
Scott Bakeracd45142013-05-19 16:19:16 -070012from django.contrib.auth.signals import user_logged_in
13from django.utils import timezone
Siobhan Tullyde5450d2013-06-21 11:35:33 -040014from django.contrib.contenttypes import generic
Siobhan Tullybfd11dc2013-09-03 12:59:24 -040015from suit.widgets import LinkedSelect
Siobhan Tullycf04fb62014-01-11 11:25:57 -050016from django.core.exceptions import PermissionDenied
Tony Mack5817cb42015-02-16 19:54:24 -050017from django.core.urlresolvers import reverse, resolve, NoReverseMatch
Scott Baker69e045d2014-11-17 23:44:03 -080018from django.utils.encoding import force_text, python_2_unicode_compatible
19from django.utils.html import conditional_escape, format_html
Scott Bakera9b8f612015-02-26 20:42:11 -080020from django.utils.text import capfirst
Scott Baker69e045d2014-11-17 23:44:03 -080021from django.forms.utils import flatatt, to_current_timezone
Scott Bakerf41fe2c2015-07-09 19:06:08 -070022from django.core.exceptions import PermissionDenied, ValidationError
Scott Baker92d22172014-10-21 21:03:08 -070023from cgi import escape as html_escape
Scott Bakerf41fe2c2015-07-09 19:06:08 -070024from django.contrib import messages
Tony Mack7130ac32013-03-22 21:58:00 -040025
Scott Baker36f50872014-08-21 13:01:25 -070026import django_evolution
Scott Baker6a995352014-10-06 17:51:20 -070027import threading
28
29# thread locals necessary to work around a django-suit issue
30_thread_locals = threading.local()
Scott Baker36f50872014-08-21 13:01:25 -070031
Scott Bakere5f9d7d2015-02-10 18:24:20 -080032ICON_URLS = {"success": "/static/admin/img/icon_success.gif",
33 "clock": "/static/admin/img/icon_clock.gif",
34 "error": "/static/admin/img/icon_error.gif"}
35
36def backend_icon(obj):
37 (icon, tooltip) = obj.get_backend_icon()
38 icon_url = ICON_URLS.get(icon, "unknown")
39
40 if tooltip:
41 return '<span style="min-width:16px;" title="%s"><img src="%s"></span>' % (tooltip, icon_url)
Scott Baker40c00762014-08-21 16:55:59 -070042 else:
Scott Bakere5f9d7d2015-02-10 18:24:20 -080043 return '<span style="min-width:16px;"><img src="%s"></span>' % icon_url
Scott Baker40c00762014-08-21 16:55:59 -070044
45def backend_text(obj):
Scott Bakere5f9d7d2015-02-10 18:24:20 -080046 (icon, tooltip) = obj.get_backend_icon()
47 icon_url = ICON_URLS.get(icon, "unknown")
48
49 return '<img src="%s"> %s' % (icon_url, tooltip)
Scott Baker63d1a552014-08-21 15:19:07 -070050
Scott Baker69e045d2014-11-17 23:44:03 -080051class UploadTextareaWidget(AdminTextareaWidget):
52 def render(self, name, value, attrs=None):
53 if value is None:
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -050054 value = ''
55 final_attrs = self.build_attrs(attrs, name=name)
56 return format_html('<input type="file" style="width: 0; height: 0" id="btn_upload_%s" onChange="uploadTextarea(event,\'%s\');">' \
57 '<button onClick="$(\'#btn_upload_%s\').click(); return false;">Upload</button>' \
58 '<br><textarea{0}>\r\n{1}</textarea>' % (attrs["id"], attrs["id"], attrs["id"]),
59 flatatt(final_attrs),
Scott Baker69e045d2014-11-17 23:44:03 -080060 force_text(value))
61
Scott Baker50ac4192015-05-11 16:36:58 -070062class SliderWidget(forms.HiddenInput):
63 def render(self, name, value, attrs=None):
64 if value is None:
65 value = '0'
66 final_attrs = self.build_attrs(attrs, name=name)
67 attrs = attrs or attrs[:]
68 attrs["name"] = name
69 attrs["value"] = value
70 html = """<div style="width:640px"><span id="%(id)s_label">%(value)s</span><div id="%(id)s_slider" style="float:right;width:610px;margin-top:5px"></div></div>
71 <script>
72 $(function() {
73 $("#%(id)s_slider").slider({
74 value: %(value)s,
75 slide: function(event, ui) { $("#%(id)s").val( ui.value ); $("#%(id)s_label").html(ui.value); },
76 });
77 });
78 </script>
79 <input type="hidden" id="%(id)s" name="%(name)s" value="%(value)s"></input>
80 """ % attrs
81 html = html.replace("{","{{").replace("}","}}")
82 return format_html(html,
83 flatatt(final_attrs),
84 force_text(value))
85
86
Scott Baker36f50872014-08-21 13:01:25 -070087class PlainTextWidget(forms.HiddenInput):
88 input_type = 'hidden'
89
90 def render(self, name, value, attrs=None):
91 if value is None:
92 value = ''
93 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
94
Scott Baker3a8aed62015-02-27 12:21:22 -080095class XOSAdminMixin(object):
Scott Baker1a6a3902014-10-03 00:32:37 -070096 # call save_by_user and delete_by_user instead of save and delete
Siobhan Tullycf04fb62014-01-11 11:25:57 -050097
98 def has_add_permission(self, request, obj=None):
99 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -0700100
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500101 def has_delete_permission(self, request, obj=None):
102 return (not self.__user_is_readonly(request))
103
104 def save_model(self, request, obj, form, change):
105 if self.__user_is_readonly(request):
Scott Baker1a6a3902014-10-03 00:32:37 -0700106 # this 'if' might be redundant if save_by_user is implemented right
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500107 raise PermissionDenied
Scott Baker1a6a3902014-10-03 00:32:37 -0700108
109 obj.caller = request.user
110 # update openstack connection to use this site/tenant
111 obj.save_by_user(request.user)
112
113 def delete_model(self, request, obj):
114 obj.delete_by_user(request.user)
115
116 def save_formset(self, request, form, formset, change):
117 instances = formset.save(commit=False)
118 for instance in instances:
Scott Bakerf8cbac72015-07-08 18:23:17 -0700119 instance.caller = request.user
Scott Baker1a6a3902014-10-03 00:32:37 -0700120 instance.save_by_user(request.user)
121
122 # BUG in django 1.7? Objects are not deleted by formset.save if
123 # commit is False. So let's delete them ourselves.
124 #
125 # code from forms/models.py save_existing_objects()
126 try:
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -0500127 forms_to_delete = formset.deleted_forms
128 except AttributeError:
Scott Baker1a6a3902014-10-03 00:32:37 -0700129 forms_to_delete = []
130 if formset.initial_forms:
131 for form in formset.initial_forms:
132 obj = form.instance
133 if form in forms_to_delete:
134 if obj.pk is None:
135 continue
136 formset.deleted_objects.append(obj)
137 obj.delete()
138
139 formset.save_m2m()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500140
141 def get_actions(self,request):
Scott Baker3a8aed62015-02-27 12:21:22 -0800142 actions = super(XOSAdminMixin,self).get_actions(request)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500143
144 if self.__user_is_readonly(request):
145 if 'delete_selected' in actions:
146 del actions['delete_selected']
147
148 return actions
149
Scott Bakerfbe0f652015-04-03 17:44:31 -0700150 def url_for_model_changelist(self, request, model):
151 # used in add_extra_context
152 return reverse('admin:%s_%s_changelist' % (model._meta.app_label, model._meta.model_name), current_app=model._meta.app_label)
153
Scott Bakera8ef2742015-04-02 22:32:40 -0700154 def add_extra_context(self, request, extra_context):
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800155 # allow custom application breadcrumb url and name
156 extra_context["custom_app_breadcrumb_url"] = getattr(self, "custom_app_breadcrumb_url", None)
157 extra_context["custom_app_breadcrumb_name"] = getattr(self, "custom_app_breadcrumb_name", None)
Scott Bakerf85c0092015-04-02 22:07:18 -0700158 extra_context["custom_changelist_breadcrumb_url"] = getattr(self, "custom_changelist_breadcrumb_url", None)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800159
160 # for Service admins to render their Administration page
161 if getattr(self, "extracontext_registered_admins", False):
162 admins=[]
163 for model, model_admin in admin.site._registry.items():
164 if model == self.model:
165 continue
166 if model._meta.app_label == self.model._meta.app_label:
167 info = {"app": model._meta.app_label,
168 "model": model._meta.model_name,
169 "name": capfirst(model._meta.verbose_name_plural),
Scott Bakerfbe0f652015-04-03 17:44:31 -0700170 "url": self.url_for_model_changelist(request,model) }
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800171 admins.append(info)
172 extra_context["registered_admins"] = admins
173
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500174 def change_view(self,request,object_id, extra_context=None):
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800175 extra_context = extra_context or {}
176
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500177 if self.__user_is_readonly(request):
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -0500178 if not hasattr(self, "readonly_save"):
179 # save the original readonly fields
180 self.readonly_save = self.readonly_fields
181 self.inlines_save = self.inlines
182 if hasattr(self, "user_readonly_fields"):
183 self.readonly_fields=self.user_readonly_fields
184 if hasattr(self, "user_readonly_inlines"):
185 self.inlines = self.user_readonly_inlines
186 else:
187 if hasattr(self, "readonly_save"):
188 # restore the original readonly fields
189 self.readonly_fields = self.readonly_save
190 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -0700191 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500192
Scott Bakera8ef2742015-04-02 22:32:40 -0700193 self.add_extra_context(request, extra_context)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800194
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500195 try:
Scott Baker3a8aed62015-02-27 12:21:22 -0800196 return super(XOSAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500197 except PermissionDenied:
198 pass
Scott Bakerf41fe2c2015-07-09 19:06:08 -0700199 except ValidationError as e:
200 if (e.params is None):
201 # Validation errors that don't reference a specific field will
202 # often throw a non-descriptive 500 page to the user. The code
203 # below will cause an error message to be printed and the
204 # page refreshed instead.
205 # As a side-effect it turns the request back into a 'GET' which
206 # may wipe anything the user had changed on the page. But, at
207 # least the user gets a real error message.
208 # TODO: revisit this and display some kind of error view
209 request.method = 'GET'
210 messages.error(request, e.message)
211 return super(XOSAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
212 else:
213 raise
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500214 if request.method == 'POST':
215 raise PermissionDenied
216 request.readonly = True
Scott Baker3a8aed62015-02-27 12:21:22 -0800217 return super(XOSAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500218
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800219 def changelist_view(self, request, extra_context = None):
220 extra_context = extra_context or {}
221
Scott Bakera8ef2742015-04-02 22:32:40 -0700222 self.add_extra_context(request, extra_context)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800223
Scott Baker3a8aed62015-02-27 12:21:22 -0800224 return super(XOSAdminMixin, self).changelist_view(request, extra_context=extra_context)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800225
Scott Baker88ac9d62015-04-14 17:01:18 -0700226 def add_view(self, request, form_url='', extra_context = None):
Scott Bakerfbe0f652015-04-03 17:44:31 -0700227 extra_context = extra_context or {}
228
229 self.add_extra_context(request, extra_context)
230
Scott Baker88ac9d62015-04-14 17:01:18 -0700231 return super(XOSAdminMixin, self).add_view(request, form_url, extra_context=extra_context)
Scott Bakerfbe0f652015-04-03 17:44:31 -0700232
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500233 def __user_is_readonly(self, request):
234 return request.user.isReadOnlyUser()
235
Scott Baker40c00762014-08-21 16:55:59 -0700236 def backend_status_text(self, obj):
237 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -0700238
Scott Baker63d1a552014-08-21 15:19:07 -0700239 def backend_status_icon(self, obj):
Scott Baker40c00762014-08-21 16:55:59 -0700240 return mark_safe(backend_icon(obj))
Scott Baker63d1a552014-08-21 15:19:07 -0700241 backend_status_icon.short_description = ""
242
Scott Bakerdc4724c2014-11-05 09:05:38 -0800243 def get_form(self, request, obj=None, **kwargs):
Scott Baker9b3c1af2014-10-16 00:57:55 -0700244 # Save obj and request in thread-local storage, so suit_form_tabs can
245 # use it to determine whether we're in edit or add mode, and can
246 # determine whether the user is an admin.
247 _thread_locals.request = request
248 _thread_locals.obj = obj
Scott Baker3a8aed62015-02-27 12:21:22 -0800249 return super(XOSAdminMixin, self).get_form(request, obj, **kwargs)
Scott Baker9b3c1af2014-10-16 00:57:55 -0700250
251 def get_inline_instances(self, request, obj=None):
Scott Baker3a8aed62015-02-27 12:21:22 -0800252 inlines = super(XOSAdminMixin, self).get_inline_instances(request, obj)
Scott Baker9b3c1af2014-10-16 00:57:55 -0700253
254 # inlines that should only be shown to an admin user
255 if request.user.is_admin:
256 for inline_class in getattr(self, "admin_inlines", []):
257 inlines.append(inline_class(self.model, self.admin_site))
258
259 return inlines
260
Scott Baker3a8aed62015-02-27 12:21:22 -0800261class ReadOnlyAwareAdmin(XOSAdminMixin, admin.ModelAdmin):
262 # Note: Make sure XOSAdminMixin is listed before
Scott Bakerf4aeedc2014-10-03 13:10:47 -0700263 # admin.ModelAdmin in the class declaration.
264
Scott Baker1a6a3902014-10-03 00:32:37 -0700265 pass
266
Scott Baker67db95f2015-02-18 15:50:11 -0800267class XOSBaseAdmin(ReadOnlyAwareAdmin):
Scott Baker1a6a3902014-10-03 00:32:37 -0700268 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700269
Scott Bakere8859f92014-05-23 12:42:40 -0700270class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400271 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700272 if not super(SingletonAdmin, self).has_add_permission(request):
273 return False
274
Siobhan Tullyce652d02013-10-08 21:52:35 -0400275 num_objects = self.model.objects.count()
276 if num_objects >= 1:
277 return False
278 else:
279 return True
280
Scott Bakera9b8f612015-02-26 20:42:11 -0800281class ServiceAppAdmin (SingletonAdmin):
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800282 extracontext_registered_admins = True
Scott Bakera9b8f612015-02-26 20:42:11 -0800283
Scott Baker67db95f2015-02-18 15:50:11 -0800284class XOSTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800285 def __init__(self, *args, **kwargs):
Scott Baker67db95f2015-02-18 15:50:11 -0800286 super(XOSTabularInline, self).__init__(*args, **kwargs)
Scott Baker86568322014-01-12 16:53:31 -0800287
288 # InlineModelAdmin as no get_fields() method, so in order to add
289 # the selflink field, we override __init__ to modify self.fields and
290 # self.readonly_fields.
291
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800292 self.setup_selflink()
293
Scott Baker54c9b9b2015-07-24 09:32:14 -0700294 @property
295 def selflink_model(self):
296 if hasattr(self, "selflink_fieldname"):
297 """ self.selflink_model can be defined to punch through a relation
298 to its target object. For example, in SliceNetworkInline, set
299 selflink_model = "network", and the URL will lead to the Network
300 object instead of trying to bring up a change view of the
301 SliceNetwork object.
302 """
303 return getattr(self.model,self.selflink_fieldname).field.rel.to
304 else:
305 return self.model
306
307 @property
308 def selflink_reverse_path(self):
309 return "admin:%s_change" % (self.selflink_model._meta.db_table)
310
311 def get_change_url(self, id):
Scott Baker874936e2014-01-13 18:15:34 -0800312 """ Get the URL to a change form in the admin for this model """
Scott Bakerb581a462015-07-27 08:53:05 -0700313 reverse_path = self.selflink_reverse_path # "admin:%s_change" % (self.selflink_model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800314 try:
Scott Baker874936e2014-01-13 18:15:34 -0800315 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800316 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800317 return None
318
319 return url
320
321 def setup_selflink(self):
Scott Baker54c9b9b2015-07-24 09:32:14 -0700322 url = self.get_change_url(0)
Scott Baker874936e2014-01-13 18:15:34 -0800323
324 # We don't have an admin for this object, so don't create the
325 # selflink.
326 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800327 return
328
Scott Baker874936e2014-01-13 18:15:34 -0800329 # Since we need to add "selflink" to the field list, we need to create
330 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800331 if (self.fields is None):
332 self.fields = []
333 for f in self.model._meta.fields:
334 if f.editable and f.name != "id":
335 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800336
Scott Baker874936e2014-01-13 18:15:34 -0800337 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800338
Scott Baker874936e2014-01-13 18:15:34 -0800339 if self.readonly_fields is None:
340 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800341
Scott Baker874936e2014-01-13 18:15:34 -0800342 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800343
344 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800345 if hasattr(self, "selflink_fieldname"):
346 obj = getattr(obj, self.selflink_fieldname)
347
Scott Baker86568322014-01-12 16:53:31 -0800348 if obj.id:
Scott Baker54c9b9b2015-07-24 09:32:14 -0700349 url = self.get_change_url(obj.id)
Scott Baker874936e2014-01-13 18:15:34 -0800350 return "<a href='%s'>Details</a>" % str(url)
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -0500351 else:
352 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800353
354 selflink.allow_tags = True
355 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400356
Scott Bakerb27b62c2014-08-15 16:29:16 -0700357 def has_add_permission(self, request):
358 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500359
360 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700361 readonly_fields = list(self.readonly_fields)[:]
362 if request.user.isReadOnlyUser():
363 for field in self.fields:
364 if not field in readonly_fields:
365 readonly_fields.append(field)
366 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500367
Scott Baker40c00762014-08-21 16:55:59 -0700368 def backend_status_icon(self, obj):
369 return mark_safe(backend_icon(obj))
370 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700371
Scott Bakerb27b62c2014-08-15 16:29:16 -0700372class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500373 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700374 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500375
Scott Bakerb27b62c2014-08-15 16:29:16 -0700376 def get_readonly_fields(self, request, obj=None):
377 readonly_fields = list(self.readonly_fields)[:]
378 if request.user.isReadOnlyUser():
379 for field in self.fields:
380 if not field in readonly_fields:
381 readonly_fields.append(field)
382 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500383
Scott Baker40c00762014-08-21 16:55:59 -0700384 def backend_status_icon(self, obj):
385 return mark_safe(backend_icon(obj))
386 backend_status_icon.short_description = ""
387
Scott Baker67db95f2015-02-18 15:50:11 -0800388class ReservationInline(XOSTabularInline):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400389 model = Reservation
390 extra = 0
391 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700392
Tony Mack5b061472014-02-04 07:57:10 -0500393 def queryset(self, request):
394 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400395
Scott Bakerb27b62c2014-08-15 16:29:16 -0700396class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400397 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400398 extra = 0
399 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500400 fields = ['service', 'name', 'value']
401
402 def queryset(self, request):
403 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400404
Tony Mackd8515472015-08-19 11:58:18 -0400405class InstanceInline(XOSTabularInline):
406 model = Instance
Scott Bakerebe89232015-09-21 14:52:15 -0700407 fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400408 extra = 0
Scott Bakerebe89232015-09-21 14:52:15 -0700409 max_num = 0
Scott Baker4fcc6c52015-09-21 15:10:18 -0700410 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node']
Tony Mackd8515472015-08-19 11:58:18 -0400411 suit_classes = 'suit-tab suit-tab-instances'
Scott Baker74d8e622013-07-29 16:04:22 -0700412
Tony Mack5b061472014-02-04 07:57:10 -0500413 def queryset(self, request):
Tony Mackd8515472015-08-19 11:58:18 -0400414 return Instance.select_by_user(request.user)
Tony Mack5b061472014-02-04 07:57:10 -0500415
Scott Bakerb24cc932014-06-09 10:51:16 -0700416 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Tony Mackb2dba4b2014-12-26 13:38:02 -0500417 if db_field.name == 'deployment':
Tony Mack1b8975c2015-02-24 15:41:49 -0500418 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
Tony Mackd8515472015-08-19 11:58:18 -0400419 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_deployment_changed(this);"})
Tony Mackb2dba4b2014-12-26 13:38:02 -0500420 if db_field.name == 'flavor':
Tony Mackd8515472015-08-19 11:58:18 -0400421 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700422
Tony Mackd8515472015-08-19 11:58:18 -0400423 field = super(InstanceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700424
425 return field
426
Tony Mackd8515472015-08-19 11:58:18 -0400427class CordInstanceInline(XOSTabularInline):
428 model = Instance
Scott Baker25881992015-06-12 10:40:15 -0700429 fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'flavor', 'image', 'node']
430 extra = 0
431 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name']
Tony Mackd8515472015-08-19 11:58:18 -0400432 suit_classes = 'suit-tab suit-tab-instances'
Scott Baker25881992015-06-12 10:40:15 -0700433
434 def queryset(self, request):
Tony Mackd8515472015-08-19 11:58:18 -0400435 return Instance.select_by_user(request.user)
Scott Baker25881992015-06-12 10:40:15 -0700436
437 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
438 if db_field.name == 'deployment':
439
440 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
Tony Mackd8515472015-08-19 11:58:18 -0400441 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_deployment_changed(this);"})
Scott Baker25881992015-06-12 10:40:15 -0700442 if db_field.name == 'flavor':
Tony Mackd8515472015-08-19 11:58:18 -0400443 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_flavor_changed(this);"})
Scott Baker25881992015-06-12 10:40:15 -0700444
Tony Mackd8515472015-08-19 11:58:18 -0400445 field = super(CordInstanceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Baker25881992015-06-12 10:40:15 -0700446
447 return field
448
Scott Baker67db95f2015-02-18 15:50:11 -0800449class SiteInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400450 model = Site
451 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400452 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400453
Tony Mack5b061472014-02-04 07:57:10 -0500454 def queryset(self, request):
455 return Site.select_by_user(request.user)
456
Tony Mack8d60ba32015-08-04 17:53:23 -0400457class SiteHostsNodesInline(SiteInline):
458 def queryset(self, request):
459 return Site.select_by_user(request.user).filter(hosts_nodes=True)
460
461class SiteHostsUsersInline(SiteInline):
462 def queryset(self, request):
463 return Site.select_by_user(request.user).filter(hosts_users=True)
464
Scott Baker67db95f2015-02-18 15:50:11 -0800465class UserInline(XOSTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400466 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700467 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
468 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400469 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400470 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400471
Tony Mack5b061472014-02-04 07:57:10 -0500472 def queryset(self, request):
473 return User.select_by_user(request.user)
474
Scott Baker67db95f2015-02-18 15:50:11 -0800475class SliceInline(XOSTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400476 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700477 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
478 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400479 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400480 suit_classes = 'suit-tab suit-tab-slices'
481
Tony Mack5b061472014-02-04 07:57:10 -0500482 def queryset(self, request):
483 return Slice.select_by_user(request.user)
484
Scott Baker67db95f2015-02-18 15:50:11 -0800485class NodeInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400486 model = Node
487 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400488 suit_classes = 'suit-tab suit-tab-nodes'
Tony Mack68a1e422014-12-08 16:43:02 -0500489 fields = ['backend_status_icon', 'name', 'site_deployment']
Scott Baker40c00762014-08-21 16:55:59 -0700490 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400491
Scott Baker67db95f2015-02-18 15:50:11 -0800492class DeploymentPrivilegeInline(XOSTabularInline):
Tony Mack68a1e422014-12-08 16:43:02 -0500493 model = DeploymentPrivilege
494 extra = 0
Tony Mack88c89902015-02-09 21:41:57 -0500495 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Tony Mack68a1e422014-12-08 16:43:02 -0500496 fields = ['backend_status_icon', 'user','role','deployment']
497 readonly_fields = ('backend_status_icon', )
498
499 def queryset(self, request):
500 return DeploymentPrivilege.select_by_user(request.user)
501
Scott Baker67db95f2015-02-18 15:50:11 -0800502class ControllerSiteInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -0500503 model = ControllerSite
504 extra = 0
505 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Macke2363c12015-01-06 15:08:20 -0500506 fields = ['controller', 'site', 'tenant_id']
Tony Mack3066a952015-01-05 22:48:11 -0500507
508
Scott Baker67db95f2015-02-18 15:50:11 -0800509class SitePrivilegeInline(XOSTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400510 model = SitePrivilege
511 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400512 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700513 fields = ['backend_status_icon', 'user','site', 'role']
514 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400515
Tony Mackc2835a92013-05-28 09:18:49 -0400516 def formfield_for_foreignkey(self, db_field, request, **kwargs):
517 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500518 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400519
520 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500521 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400522 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
523
Tony Mack5b061472014-02-04 07:57:10 -0500524 def queryset(self, request):
525 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400526
Tony Mack60789ac2015-05-11 20:39:32 -0400527
528class ServicePrivilegeInline(XOSTabularInline):
529 model = ServicePrivilege
530 extra = 0
531 suit_classes = 'suit-tab suit-tab-serviceprivileges'
532 fields = ['backend_status_icon', 'user','service', 'role']
533 readonly_fields = ('backend_status_icon', )
534
535 def formfield_for_foreignkey(self, db_field, request, **kwargs):
536 if db_field.name == 'service':
537 kwargs['queryset'] = Service.select_by_user(request.user)
Tony Mack5fa0f402015-05-15 06:33:45 -0400538 if db_field.name == 'user':
539 kwargs['queryset'] = User.select_by_user(request.user)
540 return super(ServicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mack60789ac2015-05-11 20:39:32 -0400541
542 def queryset(self, request):
543 return ServicePrivilege.select_by_user(request.user)
544
Scott Baker67db95f2015-02-18 15:50:11 -0800545class SiteDeploymentInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -0500546 model = SiteDeployment
Tony Macke4be32f2014-03-11 20:45:25 -0400547 extra = 0
Tony Mackb81d5e42015-01-30 10:58:29 -0500548 suit_classes = 'suit-tab suit-tab-sitedeployments'
Tony Mackd14d48f2014-12-05 17:13:08 -0500549 fields = ['backend_status_icon', 'deployment','site', 'controller']
Scott Baker40c00762014-08-21 16:55:59 -0700550 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400551
552 def formfield_for_foreignkey(self, db_field, request, **kwargs):
553 if db_field.name == 'site':
554 kwargs['queryset'] = Site.select_by_user(request.user)
555
556 if db_field.name == 'deployment':
557 kwargs['queryset'] = Deployment.select_by_user(request.user)
Tony Mackd14d48f2014-12-05 17:13:08 -0500558
559 if db_field.name == 'controller':
Scott Bakerebe89232015-09-21 14:52:15 -0700560 if len(resolve(request.path).args) > 0:
561 kwargs['queryset'] = Controller.select_by_user(request.user).filter(deployment__id=int(resolve(request.path).args[0]))
Tony Mackd14d48f2014-12-05 17:13:08 -0500562
Tony Mack3066a952015-01-05 22:48:11 -0500563 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Macke4be32f2014-03-11 20:45:25 -0400564
565 def queryset(self, request):
Tony Mack3066a952015-01-05 22:48:11 -0500566 return SiteDeployment.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400567
568
Scott Baker67db95f2015-02-18 15:50:11 -0800569class SlicePrivilegeInline(XOSTabularInline):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400570 model = SlicePrivilege
571 suit_classes = 'suit-tab suit-tab-sliceprivileges'
572 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700573 fields = ('backend_status_icon', 'user', 'slice', 'role')
574 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400575
Tony Mackc2835a92013-05-28 09:18:49 -0400576 def formfield_for_foreignkey(self, db_field, request, **kwargs):
577 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700578 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400579 if db_field.name == 'user':
Scott Baker31ba9ca2015-07-16 12:40:07 -0700580 # all users are available to be granted SlicePrivilege
581 kwargs['queryset'] = User.objects.all()
Tony Mackc2835a92013-05-28 09:18:49 -0400582
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400583 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400584
Tony Mack5b061472014-02-04 07:57:10 -0500585 def queryset(self, request):
586 return SlicePrivilege.select_by_user(request.user)
587
Scott Baker67db95f2015-02-18 15:50:11 -0800588class SliceNetworkInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700589 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800590 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700591 extra = 0
592 verbose_name = "Network Connection"
593 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400594 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700595 fields = ['backend_status_icon', 'network']
596 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700597
Scott Baker67db95f2015-02-18 15:50:11 -0800598class ImageDeploymentsInline(XOSTabularInline):
Sapan Bhatia1b6bba22014-11-19 15:10:16 -0500599 model = ImageDeployments
Scott Baker2170b972014-06-03 12:14:07 -0700600 extra = 0
601 verbose_name = "Image Deployments"
602 verbose_name_plural = "Image Deployments"
603 suit_classes = 'suit-tab suit-tab-imagedeployments'
Tony Mack06c8e472014-11-30 15:53:08 -0500604 fields = ['backend_status_icon', 'image', 'deployment']
605 readonly_fields = ['backend_status_icon']
606
Scott Baker67db95f2015-02-18 15:50:11 -0800607class ControllerImagesInline(XOSTabularInline):
Tony Mack06c8e472014-11-30 15:53:08 -0500608 model = ControllerImages
609 extra = 0
610 verbose_name = "Controller Images"
611 verbose_name_plural = "Controller Images"
612 suit_classes = 'suit-tab suit-tab-admin-only'
613 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
Scott Baker40c00762014-08-21 16:55:59 -0700614 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700615
Scott Baker67db95f2015-02-18 15:50:11 -0800616class SliceRoleAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400617 model = SliceRole
618 pass
619
Scott Baker67db95f2015-02-18 15:50:11 -0800620class SiteRoleAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400621 model = SiteRole
622 pass
623
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400624class DeploymentAdminForm(forms.ModelForm):
Scott Bakerde0f4412014-06-11 15:40:26 -0700625 images = forms.ModelMultipleChoiceField(
626 queryset=Image.objects.all(),
627 required=False,
628 help_text="Select which images should be deployed on this deployment",
629 widget=FilteredSelectMultiple(
630 verbose_name=('Images'), is_stacked=False
631 )
632 )
Scott Baker37b47902014-09-02 14:37:41 -0700633 flavors = forms.ModelMultipleChoiceField(
634 queryset=Flavor.objects.all(),
635 required=False,
636 help_text="Select which flavors should be usable on this deployment",
637 widget=FilteredSelectMultiple(
638 verbose_name=('Flavors'), is_stacked=False
639 )
640 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400641 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400642 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700643 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400644
Siobhan Tully320b4622014-01-17 15:11:14 -0500645 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700646 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500647 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
648
Scott Baker5380c522014-06-06 14:49:43 -0700649 self.fields['accessControl'].initial = "allow site " + request.user.site.name
650
Siobhan Tully320b4622014-01-17 15:11:14 -0500651 if self.instance and self.instance.pk:
Scott Baker69e045d2014-11-17 23:44:03 -0800652 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700653 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700654
655 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
656 """ helper function for handling m2m relations from the MultipleChoiceField
657
658 this_obj: the source object we want to link from
659
660 selected_objs: a list of destination objects we want to link to
661
662 all_relations: the full set of relations involving this_obj, including ones we don't want
663
664 relation_class: the class that implements the relation from source to dest
665
666 local_attrname: field name representing this_obj in relation_class
667
668 foreign_attrname: field name representing selected_objs in relation_class
669
670 This function will remove all newobjclass relations from this_obj
671 that are not contained in selected_objs, and add any relations that
672 are in selected_objs but don't exist in the data model yet.
673 """
674
675 existing_dest_objs = []
676 for relation in list(all_relations):
677 if getattr(relation, foreign_attrname) not in selected_objs:
678 #print "deleting site", sdp.site
679 relation.delete()
680 else:
681 existing_dest_objs.append(getattr(relation, foreign_attrname))
682
683 for dest_obj in selected_objs:
684 if dest_obj not in existing_dest_objs:
685 #print "adding site", site
686 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
687 relation = relation_class(**kwargs)
688 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500689
690 def save(self, commit=True):
691 deployment = super(DeploymentAdminForm, self).save(commit=False)
692
693 if commit:
694 deployment.save()
Scott Baker0057d052014-10-06 17:17:40 -0700695 # this has to be done after save() if/when a deployment is first created
696 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500697
698 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700699 # save_m2m() doesn't seem to work with 'through' relations. So we
700 # create/destroy the through models ourselves. There has to be
701 # a better way...
702
Tony Mackb2fde612014-12-15 11:45:02 -0500703 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ImageDeployments, "deployment", "image")
704 # manipulate_m2m_objs doesn't work for Flavor/Deployment relationship
705 # so well handle that manually here
706 for flavor in deployment.flavors.all():
707 if getattr(flavor, 'name') not in self.cleaned_data['flavors']:
Tony Mackd4ab7822014-12-15 12:37:59 -0500708 deployment.flavors.remove(flavor)
Tony Mackb2fde612014-12-15 11:45:02 -0500709 for flavor in self.cleaned_data['flavors']:
710 if flavor not in deployment.flavors.all():
711 flavor.deployments.add(deployment)
Scott Bakerc9b14f72014-05-22 13:44:20 -0700712
Scott Baker37b47902014-09-02 14:37:41 -0700713 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500714
715 return deployment
716
Scott Bakerff5e0f32014-05-22 14:40:27 -0700717class DeploymentAdminROForm(DeploymentAdminForm):
718 def save(self, commit=True):
719 raise PermissionDenied
720
Scott Baker67db95f2015-02-18 15:50:11 -0800721class SiteAssocInline(XOSTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500722 model = Site.deployments.through
723 extra = 0
724 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400725
Scott Baker67db95f2015-02-18 15:50:11 -0800726class DeploymentAdmin(XOSBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500727 model = Deployment
Scott Baker622bcf02015-02-10 08:40:34 -0800728 fieldList = ['backend_status_text', 'name', 'images', 'flavors', 'accessControl']
729 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack68a1e422014-12-08 16:43:02 -0500730 # node no longer directly connected to deployment
731 #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
Tony Mackb81d5e42015-01-30 10:58:29 -0500732 inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline,SiteDeploymentInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700733 list_display = ['backend_status_icon', 'name']
734 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700735 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500736
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500737 user_readonly_fields = ['name']
738
Tony Mack68a1e422014-12-08 16:43:02 -0500739 # nodes no longer direclty connected to deployments
Scott Baker622bcf02015-02-10 08:40:34 -0800740 suit_form_tabs =(('general','Deployment Details'),('deploymentprivileges','Privileges'), ('sitedeployments', 'Sites'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500741
Scott Bakerff5e0f32014-05-22 14:40:27 -0700742 def get_form(self, request, obj=None, **kwargs):
Tony Mackcf29cfa2015-02-05 06:13:04 -0500743 if request.user.isReadOnlyUser() or not request.user.is_admin:
Scott Bakerff5e0f32014-05-22 14:40:27 -0700744 kwargs["form"] = DeploymentAdminROForm
745 else:
746 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700747 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
748
749 # from stackexchange: pass the request object into the form
750
751 class AdminFormMetaClass(adminForm):
752 def __new__(cls, *args, **kwargs):
753 kwargs['request'] = request
754 return adminForm(*args, **kwargs)
755
756 return AdminFormMetaClass
757
Scott Baker67db95f2015-02-18 15:50:11 -0800758class ControllerAdmin(XOSBaseAdmin):
Scott Baker622bcf02015-02-10 08:40:34 -0800759 model = Controller
svavilapaf911082015-10-13 23:50:03 -0400760 fieldList = ['deployment', 'name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password', 'domain', 'rabbit_host', 'rabbit_user', 'rabbit_password']
Scott Baker622bcf02015-02-10 08:40:34 -0800761 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack3066a952015-01-05 22:48:11 -0500762 inlines = [ControllerSiteInline] # ,ControllerImagesInline]
Tony Mackd14d48f2014-12-05 17:13:08 -0500763 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
764 list_display_links = ('backend_status_icon', 'name', )
765 readonly_fields = ('backend_status_text',)
766
767 user_readonly_fields = []
768
Tony Mack2e897fa2015-01-13 17:33:08 -0500769 def save_model(self, request, obj, form, change):
770 # update openstack connection to use this site/tenant
771 obj.save_by_user(request.user)
772
773 def delete_model(self, request, obj):
Scott Baker622bcf02015-02-10 08:40:34 -0800774 obj.delete_by_user(request.user)
775
Tony Mack78fc1362015-02-18 11:41:36 -0500776 def queryset(self, request):
777 return Controller.select_by_user(request.user)
778
Scott Baker622bcf02015-02-10 08:40:34 -0800779 @property
780 def suit_form_tabs(self):
781 tabs = [('general', 'Controller Details'),
782 ]
783
784 request=getattr(_thread_locals, "request", None)
785 if request and request.user.is_admin:
786 tabs.append( ('admin-only', 'Admin-Only') )
787
788 return tabs
Tony Mack2e897fa2015-01-13 17:33:08 -0500789
Scott Baker1e7e3482015-10-15 15:59:19 -0700790class TenantAttributeAdmin(XOSBaseAdmin):
791 model = TenantAttribute
792 list_display = ('backend_status_icon', 'tenant', 'name', 'value')
793 list_display_links = ('backend_status_icon', 'name')
794 fieldList = ('backend_status_text', 'tenant', 'name', 'value', )
795 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
796 readonly_fields = ('backend_status_text', )
797
798 suit_form_tabs =(('general', 'Tenant Root Details'),
799 )
800
801class TenantAttrAsTabInline(XOSTabularInline):
802 model = TenantAttribute
803 fields = ['name','value']
804 extra = 0
805 suit_classes = 'suit-tab suit-tab-tenantattrs'
806
Scott Bakerb3cf9212015-07-06 14:40:20 -0700807class TenantRootRoleAdmin(XOSBaseAdmin):
808 model = TenantRootRole
809 fields = ('role',)
810
811class TenantRootTenantInline(XOSTabularInline):
812 model = Tenant
813 fields = ['provider_service', 'subscriber_root']
814 extra = 0
815 suit_classes = 'suit-tab suit-tab-tenantroots'
816 fk_name = 'subscriber_root'
817 verbose_name = 'subscribed tenant'
818 verbose_name_plural = 'subscribed tenants'
819
820 #def queryset(self, request):
821 # qs = super(TenantRootTenantInline, self).queryset(request)
822 # return qs.filter(kind="coarse")
823
824class TenantRootPrivilegeInline(XOSTabularInline):
825 model = TenantRootPrivilege
826 extra = 0
827 suit_classes = 'suit-tab suit-tab-tenantrootprivileges'
828 fields = ['backend_status_icon', 'user', 'role', 'tenant_root']
829 readonly_fields = ('backend_status_icon', )
830
831 def queryset(self, request):
832 return TenantRootPrivilege.select_by_user(request.user)
833
834class TenantRootAdmin(XOSBaseAdmin):
835 model = TenantRoot
836 list_display = ('backend_status_icon', 'name', 'kind')
837 list_display_links = ('backend_status_icon', 'name')
838 fieldList = ('backend_status_text', 'name', 'kind', )
839 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
840 inlines = (TenantRootTenantInline, TenantRootPrivilegeInline)
841 readonly_fields = ('backend_status_text', )
842
843 suit_form_tabs =(('general', 'Tenant Root Details'),
844 ('tenantroots','Tenancy'),
845 ('tenantrootprivileges','Privileges')
846 )
847
Scott Bakeref58a842015-04-26 20:30:40 -0700848class ProviderTenantInline(XOSTabularInline):
849 model = CoarseTenant
850 fields = ['provider_service', 'subscriber_service', 'connect_method']
851 extra = 0
852 suit_classes = 'suit-tab suit-tab-servicetenants'
853 fk_name = 'provider_service'
854 verbose_name = 'provided tenant'
855 verbose_name_plural = 'provided tenants'
856
857 def queryset(self, request):
858 qs = super(ProviderTenantInline, self).queryset(request)
859 return qs.filter(kind="coarse")
860
861class SubscriberTenantInline(XOSTabularInline):
862 model = CoarseTenant
863 fields = ['provider_service', 'subscriber_service', 'connect_method']
864 extra = 0
865 suit_classes = 'suit-tab suit-tab-servicetenants'
866 fk_name = 'subscriber_service'
867 verbose_name = 'subscribed tenant'
868 verbose_name_plural = 'subscribed tenants'
869
870 def queryset(self, request):
871 qs = super(SubscriberTenantInline, self).queryset(request)
872 return qs.filter(kind="coarse")
873
Scott Baker67db95f2015-02-18 15:50:11 -0800874class ServiceAttrAsTabInline(XOSTabularInline):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400875 model = ServiceAttribute
876 fields = ['name','value']
877 extra = 0
878 suit_classes = 'suit-tab suit-tab-serviceattrs'
879
Scott Baker67db95f2015-02-18 15:50:11 -0800880class ServiceAdmin(XOSBaseAdmin):
Scott Baker0d306722015-04-15 20:58:20 -0700881 list_display = ("backend_status_icon","name","kind","versionNumber","enabled","published")
Scott Baker63d1a552014-08-21 15:19:07 -0700882 list_display_links = ('backend_status_icon', 'name', )
Scott Bakerdc63fb32015-11-12 16:22:52 -0800883 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 -0500884 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack60789ac2015-05-11 20:39:32 -0400885 inlines = [ServiceAttrAsTabInline,SliceInline,ProviderTenantInline,SubscriberTenantInline,ServicePrivilegeInline]
Scott Baker40c00762014-08-21 16:55:59 -0700886 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500887
888 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500889
890 suit_form_tabs =(('general', 'Service Details'),
891 ('slices','Slices'),
892 ('serviceattrs','Additional Attributes'),
Scott Bakeref58a842015-04-26 20:30:40 -0700893 ('servicetenants','Tenancy'),
Scott Bakerb3cf9212015-07-06 14:40:20 -0700894 ('serviceprivileges','Privileges')
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500895 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400896
Scott Baker67db95f2015-02-18 15:50:11 -0800897class SiteNodeInline(XOSTabularInline):
Tony Mack4f134e62015-01-14 20:58:38 -0500898 model = Node
899 fields = ['name', 'site_deployment']
900 extra = 0
901 suit_classes = 'suit-tab suit-tab-nodes'
902
Tony Mackc2a0d312015-02-25 11:39:34 -0500903 def formfield_for_foreignkey(self, db_field, request, **kwargs):
904 # only display site deployments associated with this site
905 if db_field.name == 'site_deployment':
906 kwargs['queryset'] = SiteDeployment.objects.filter(site__id=int(request.path.split('/')[-2]))
907
908 return super(SiteNodeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
909
Scott Baker67db95f2015-02-18 15:50:11 -0800910class SiteAdmin(XOSBaseAdmin):
Tony Mack450b6e02015-01-25 12:35:29 -0500911 #fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Tony Mack6a388472015-08-04 17:21:55 -0400912 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'login_base', 'location', 'is_public', 'hosts_nodes', 'hosts_users']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400913 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500914 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400915 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400916 ]
Tony Mack450b6e02015-01-25 12:35:29 -0500917 #readonly_fields = ['backend_status_text', 'accountLink']
918 readonly_fields = ['backend_status_text']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500919
Tony Mack450b6e02015-01-25 12:35:29 -0500920 #user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Tony Mack6a388472015-08-04 17:21:55 -0400921 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'hosts_nodes', 'hosts_users']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500922
Scott Baker63d1a552014-08-21 15:19:07 -0700923 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
924 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400925 filter_horizontal = ('deployments',)
Tony Mackb81d5e42015-01-30 10:58:29 -0500926 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteNodeInline]
Tony Mackde100182015-01-14 12:11:05 -0500927 admin_inlines = [ControllerSiteInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400928 search_fields = ['name']
929
Tony Mack30dfcd72015-01-10 23:08:10 -0500930 @property
931 def suit_form_tabs(self):
932 tabs = [('general', 'Site Details'),
933 ('users','Users'),
934 ('siteprivileges','Privileges'),
Tony Mack30dfcd72015-01-10 23:08:10 -0500935 ('slices','Slices'),
Tony Mack4f134e62015-01-14 20:58:38 -0500936 ('nodes','Nodes'),
Tony Mack30dfcd72015-01-10 23:08:10 -0500937 ]
938
939 request=getattr(_thread_locals, "request", None)
940 if request and request.user.is_admin:
941 tabs.append( ('admin-only', 'Admin-Only') )
942
943 return tabs
944
Tony Mack04062832013-05-10 08:22:44 -0400945 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500946 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400947
Tony Mack5cd13202013-05-01 21:48:38 -0400948 def get_formsets(self, request, obj=None):
949 for inline in self.get_inline_instances(request, obj):
950 # hide MyInline in the add view
951 if obj is None:
952 continue
Tony Mackd8515472015-08-19 11:58:18 -0400953 if isinstance(inline, InstanceInline):
Tony Mack2bd5b412013-06-11 21:05:06 -0400954 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400955 yield inline.get_formset(request, obj)
956
Scott Baker545db2a2013-12-09 18:44:43 -0800957 def accountLink(self, obj):
958 link_obj = obj.accounts.all()
959 if link_obj:
960 reverse_path = "admin:core_account_change"
961 url = reverse(reverse_path, args =(link_obj[0].id,))
962 return "<a href='%s'>%s</a>" % (url, "view billing details")
963 else:
964 return "no billing data for this site"
965 accountLink.allow_tags = True
966 accountLink.short_description = "Billing"
967
Tony Mack332ee1d2014-02-04 15:33:45 -0500968 def save_model(self, request, obj, form, change):
969 # update openstack connection to use this site/tenant
970 obj.save_by_user(request.user)
971
972 def delete_model(self, request, obj):
973 obj.delete_by_user(request.user)
974
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500975
Scott Baker67db95f2015-02-18 15:50:11 -0800976class SitePrivilegeAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700977 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400978 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500979 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400980 ]
Scott Baker40c00762014-08-21 16:55:59 -0700981 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700982 list_display = ('backend_status_icon', 'user', 'site', 'role')
983 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500984 user_readonly_fields = fieldList
985 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400986
Tony Mackc2835a92013-05-28 09:18:49 -0400987 def formfield_for_foreignkey(self, db_field, request, **kwargs):
988 if db_field.name == 'site':
989 if not request.user.is_admin:
990 # only show sites where user is an admin or pi
991 sites = set()
992 for site_privilege in SitePrivilege.objects.filer(user=request.user):
993 if site_privilege.role.role_type in ['admin', 'pi']:
994 sites.add(site_privilege.site)
995 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
996
997 if db_field.name == 'user':
998 if not request.user.is_admin:
999 # only show users from sites where caller has admin or pi role
1000 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
1001 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
1002 sites = [site_privilege.site for site_privilege in site_privileges]
1003 site_privileges = SitePrivilege.objects.filter(site__in=sites)
1004 emails = [site_privilege.user.email for site_privilege in site_privileges]
1005 users = User.objects.filter(email__in=emails)
1006 kwargs['queryset'] = users
1007
1008 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1009
Tony Mack04062832013-05-10 08:22:44 -04001010 def queryset(self, request):
1011 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -04001012 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -04001013 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -05001014 #if not request.user.is_admin:
1015 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
1016 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
1017 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
1018 # sites = Site.objects.filter(login_base__in=login_bases)
1019 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -04001020 return qs
1021
Siobhan Tullyce652d02013-10-08 21:52:35 -04001022class SliceForm(forms.ModelForm):
1023 class Meta:
1024 model = Slice
1025 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -07001026 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -04001027 }
1028
Tony Macke75841e2014-09-29 16:10:52 -04001029 def clean(self):
1030 cleaned_data = super(SliceForm, self).clean()
1031 name = cleaned_data.get('name')
Scott Baker3cb382c2014-10-06 23:09:59 -07001032 site = cleaned_data.get('site')
Tony Mackcc9e2592014-10-22 12:54:19 -04001033 slice_id = self.instance.id
1034 if not site and slice_id:
1035 site = Slice.objects.get(id=slice_id).site
Scott Baker3cb382c2014-10-06 23:09:59 -07001036 if (not isinstance(site,Site)):
1037 # previous code indicates 'site' could be a site_id and not a site?
1038 site = Slice.objects.get(id=site.id)
Tony Macke75841e2014-09-29 16:10:52 -04001039 if not name.startswith(site.login_base):
1040 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
1041 return cleaned_data
1042
Scott Baker67db95f2015-02-18 15:50:11 -08001043class ControllerSliceInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -05001044 model = ControllerSlice
Scott Bakerf9f1ef42014-10-15 16:54:04 -07001045 extra = 0
Tony Mack06c8e472014-11-30 15:53:08 -05001046 verbose_name = "Controller Slices"
1047 verbose_name_plural = "Controller Slices"
Scott Bakerf9f1ef42014-10-15 16:54:04 -07001048 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack06c8e472014-11-30 15:53:08 -05001049 fields = ['backend_status_icon', 'controller', 'tenant_id']
Tony Mack30dfcd72015-01-10 23:08:10 -05001050 readonly_fields = ('backend_status_icon', 'controller' )
Scott Bakerf9f1ef42014-10-15 16:54:04 -07001051
Scott Baker67db95f2015-02-18 15:50:11 -08001052class SliceAdmin(XOSBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -04001053 form = SliceForm
Tony Mackd8515472015-08-19 11:58:18 -04001054 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_instances']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001055 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -07001056 readonly_fields = ('backend_status_text', )
Tony Mackd8515472015-08-19 11:58:18 -04001057 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_instances')
Tony Mack7d459902014-09-03 13:18:57 -04001058 list_display_links = ('backend_status_icon', 'name', )
Tony Mackd8515472015-08-19 11:58:18 -04001059 normal_inlines = [SlicePrivilegeInline, InstanceInline, TagInline, ReservationInline, SliceNetworkInline]
Scott Baker25881992015-06-12 10:40:15 -07001060 inlines = normal_inlines
Tony Mack3066a952015-01-05 22:48:11 -05001061 admin_inlines = [ControllerSliceInline]
Scott Baker22beb6a2015-09-15 15:21:50 -07001062 suit_form_includes = (('slice_instance_tab.html', 'bottom', 'instances'),)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001063
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001064 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001065
Scott Bakerf9f1ef42014-10-15 16:54:04 -07001066 @property
1067 def suit_form_tabs(self):
1068 tabs =[('general', 'Slice Details'),
1069 ('slicenetworks','Networks'),
1070 ('sliceprivileges','Privileges'),
Tony Mackd8515472015-08-19 11:58:18 -04001071 ('instances','Instances'),
Tony Mack450b6e02015-01-25 12:35:29 -05001072 #('reservations','Reservations'),
Tony Mackd2433382015-01-15 14:44:06 -05001073 ('tags','Tags'),
Scott Bakerf9f1ef42014-10-15 16:54:04 -07001074 ]
1075
1076 request=getattr(_thread_locals, "request", None)
1077 if request and request.user.is_admin:
1078 tabs.append( ('admin-only', 'Admin-Only') )
1079
1080 return tabs
Tony Mack0aa732a2014-10-22 11:54:29 -04001081
1082 def add_view(self, request, form_url='', extra_context=None):
Scott Baker25881992015-06-12 10:40:15 -07001083 # Ugly hack for CORD
1084 self.inlines = self.normal_inlines
Tony Mack0aa732a2014-10-22 11:54:29 -04001085 # revert to default read-only fields
1086 self.readonly_fields = ('backend_status_text',)
1087 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
1088
1089 def change_view(self, request, object_id, form_url='', extra_context=None):
Tony Mack0aa732a2014-10-22 11:54:29 -04001090 # cannot change the site of an existing slice so make the site field read only
1091 if object_id:
1092 self.readonly_fields = ('backend_status_text','site')
Scott Baker25881992015-06-12 10:40:15 -07001093
Tony Mack0aa732a2014-10-22 11:54:29 -04001094 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001095
Scott Baker510fdbb2014-08-05 17:19:24 -07001096 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -07001097 deployment_nodes = []
1098 for node in Node.objects.all():
Scott Baker66b11e22015-01-21 16:24:07 -08001099 deployment_nodes.append( (node.site_deployment.deployment.id, node.id, node.name) )
Scott Baker510fdbb2014-08-05 17:19:24 -07001100
Scott Baker7a61dc42014-09-02 17:08:20 -07001101 deployment_flavors = []
1102 for flavor in Flavor.objects.all():
1103 for deployment in flavor.deployments.all():
1104 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
1105
Tony Mack68a1e422014-12-08 16:43:02 -05001106 deployment_images = []
Scott Bakeraf36c4d2014-09-09 09:58:49 -07001107 for image in Image.objects.all():
Tony Mack68a1e422014-12-08 16:43:02 -05001108 for deployment_image in image.imagedeployments.all():
Scott Bakerf2c0c512014-12-22 17:35:34 -08001109 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
Scott Bakeraf36c4d2014-09-09 09:58:49 -07001110
Tony Mackec23b992014-09-02 21:18:45 -04001111 site_login_bases = []
1112 for site in Site.objects.all():
Scott Bakeraf36c4d2014-09-09 09:58:49 -07001113 site_login_bases.append((site.id, site.login_base))
1114
Scott Baker510fdbb2014-08-05 17:19:24 -07001115 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -07001116 context["deployment_flavors"] = deployment_flavors
Scott Bakeraf36c4d2014-09-09 09:58:49 -07001117 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -04001118 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -07001119 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
1120
Tony Mackc2835a92013-05-28 09:18:49 -04001121 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1122 if db_field.name == 'site':
Tony Mack8d60ba32015-08-04 17:53:23 -04001123 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_users=True)
Tony Mackec23b992014-09-02 21:18:45 -04001124 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 -07001125
Tony Mackc2835a92013-05-28 09:18:49 -04001126 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1127
Tony Mack04062832013-05-10 08:22:44 -04001128 def queryset(self, request):
1129 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001130 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -04001131
Tony Mack79748612013-05-01 14:52:03 -04001132 def get_formsets(self, request, obj=None):
1133 for inline in self.get_inline_instances(request, obj):
1134 # hide MyInline in the add view
1135 if obj is None:
1136 continue
Tony Mackd8515472015-08-19 11:58:18 -04001137 if isinstance(inline, InstanceInline):
Tony Mack2bd5b412013-06-11 21:05:06 -04001138 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -04001139 yield inline.get_formset(request, obj)
1140
Scott Baker533c2152015-09-15 17:48:57 -07001141 def add_extra_context(self, request, extra_context):
1142 super(SliceAdmin, self).add_extra_context(request, extra_context)
1143 # set context["slice_id"] to the PK passed in the URL to this view
1144 if len(request.resolver_match.args)>0:
1145 extra_context["slice_id"] = request.resolver_match.args[0]
1146
Scott Baker25881992015-06-12 10:40:15 -07001147 def UNUSED_get_inline_instances(self, request, obj=None):
1148 # HACK for CORD to do something special on vcpe slice page
1149 # this was a good idea, but failed miserably, as something still
1150 # expects there to be a deployment field.
1151 # XXX this approach is better than clobbering self.inlines, so
1152 # try to make this work post-demo.
1153 if (obj is not None) and (obj.name == "mysite_vcpe"):
Tony Mackd8515472015-08-19 11:58:18 -04001154 cord_vcpe_inlines = [ SlicePrivilegeInline, CordInstanceInline, TagInline, ReservationInline,SliceNetworkInline]
Scott Baker25881992015-06-12 10:40:15 -07001155
1156 inlines=[]
1157 for inline_class in cord_vcpe_inlines:
1158 inlines.append(inline_class(self.model, self.admin_site))
1159 else:
1160 inlines = super(SliceAdmin, self).get_inline_instances(request, obj)
1161
1162 return inlines
1163
Scott Baker67db95f2015-02-18 15:50:11 -08001164class SlicePrivilegeAdmin(XOSBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -04001165 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -07001166 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -04001167 ]
Scott Baker40c00762014-08-21 16:55:59 -07001168 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -07001169 list_display = ('backend_status_icon', 'user', 'slice', 'role')
1170 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -04001171
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001172 user_readonly_fields = ['user', 'slice', 'role']
1173 user_readonly_inlines = []
1174
Tony Mackc2835a92013-05-28 09:18:49 -04001175 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1176 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001177 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001178
1179 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -05001180 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001181
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001182 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001183
Tony Mack04062832013-05-10 08:22:44 -04001184 def queryset(self, request):
1185 # admins can see all memberships. Users can only see memberships of
1186 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -05001187 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001188
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001189 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -04001190 # update openstack connection to use this site/tenant
1191 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001192 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001193 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001194 obj.save()
1195
1196 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -04001197 # update openstack connection to use this site/tenant
1198 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001199 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001200 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001201 obj.delete()
1202
Scott Baker67db95f2015-02-18 15:50:11 -08001203class ImageAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001204
Scott Baker36f50872014-08-21 13:01:25 -07001205 fieldsets = [('Image Details',
Scott Bakerdcf9e0d2015-11-09 16:17:11 -08001206 {'fields': ['backend_status_text', 'name', 'kind', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001207 'classes': ['suit-tab suit-tab-general']})
1208 ]
Scott Baker40c00762014-08-21 16:55:59 -07001209 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001210
Tony Mackd8515472015-08-19 11:58:18 -04001211 suit_form_tabs =(('general','Image Details'),('instances','Instances'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001212
Tony Mackd8515472015-08-19 11:58:18 -04001213 inlines = [InstanceInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -07001214
Tony Mack32e1ce32014-05-07 13:29:41 -04001215 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -07001216
Scott Bakerdcf9e0d2015-11-09 16:17:11 -08001217 list_display = ['backend_status_icon', 'name', 'kind']
Scott Baker63d1a552014-08-21 15:19:07 -07001218 list_display_links = ('backend_status_icon', 'name', )
1219
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001220class NodeForm(forms.ModelForm):
1221 class Meta:
Scott Bakerdc5db282015-09-21 15:06:38 -07001222 model = Node
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001223 widgets = {
1224 'site': LinkedSelect,
1225 'deployment': LinkedSelect
1226 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001227
Scott Baker67db95f2015-02-18 15:50:11 -08001228class NodeAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001229 form = NodeForm
Tony Mack68a1e422014-12-08 16:43:02 -05001230 list_display = ('backend_status_icon', 'name', 'site_deployment')
Scott Baker63d1a552014-08-21 15:19:07 -07001231 list_display_links = ('backend_status_icon', 'name', )
Tony Mack68a1e422014-12-08 16:43:02 -05001232 list_filter = ('site_deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001233
Tony Mackd8515472015-08-19 11:58:18 -04001234 inlines = [TagInline,InstanceInline]
Scott Bakerdc5db282015-09-21 15:06:38 -07001235 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name', 'site_deployment'], 'classes':['suit-tab suit-tab-details']})]
Scott Baker40c00762014-08-21 16:55:59 -07001236 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001237
Tony Mack68a1e422014-12-08 16:43:02 -05001238 user_readonly_fields = ['name','site_deployment']
Tony Mackd8515472015-08-19 11:58:18 -04001239 user_readonly_inlines = [TagInline,InstanceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001240
Tony Mackd8515472015-08-19 11:58:18 -04001241 suit_form_tabs =(('details','Node Details'),('instances','Instances'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001242
Tony Mack02b8f142015-08-04 17:32:32 -04001243 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1244 if db_field.name == 'site':
1245 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_nodes=True)
Siobhan Tully567e3e62013-06-21 18:03:16 -04001246
Scott Bakerdc5db282015-09-21 15:06:38 -07001247 field = super(NodeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1248
1249 return field
1250
Tony Mackd8515472015-08-19 11:58:18 -04001251class InstanceForm(forms.ModelForm):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001252 class Meta:
Tony Mackd8515472015-08-19 11:58:18 -04001253 model = Instance
Tony Mackd90cdbf2013-04-16 22:48:40 -04001254 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001255 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001256 widgets = {
1257 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001258 'instance_name': PlainTextWidget(),
Scott Baker9d856052015-01-19 11:32:20 -08001259 'instance_id': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001260 'slice': LinkedSelect,
Tony Mackb2dba4b2014-12-26 13:38:02 -05001261 'deployment': LinkedSelect,
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001262 'node': LinkedSelect,
1263 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001264 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001265
Scott Baker67db95f2015-02-18 15:50:11 -08001266class TagAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001267 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1268 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001269 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1270 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001271
Tony Mack26ea0eb2015-09-01 16:06:52 +00001272class InstancePortInline(XOSTabularInline):
Tony Mackb956a5d2015-09-10 21:58:15 +00001273 fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
Scott Bakere553b472015-09-08 18:22:15 -07001274 readonly_fields = ("backend_status_icon", "ip", "mac")
Scott Baker5a7d9312015-08-26 09:43:33 -07001275 model = Port
Scott Bakerfbade882015-08-25 18:00:15 -07001276 selflink_fieldname = "network"
1277 extra = 0
1278 verbose_name_plural = "Ports"
1279 verbose_name = "Port"
1280 suit_classes = 'suit-tab suit-tab-ports'
1281
Tony Mackd8515472015-08-19 11:58:18 -04001282class InstanceAdmin(XOSBaseAdmin):
1283 form = InstanceForm
Tony Mackcdec0902013-04-15 00:38:49 -04001284 fieldsets = [
Scott Baker1f454e32015-11-12 17:25:53 -08001285 ('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 Bakere317a992015-11-10 17:07:23 -08001286 ('Container Settings', {'fields': ['volumes'], 'classes': ['suit-tab suit-tab-container'], }),
Tony Mackcdec0902013-04-15 00:38:49 -04001287 ]
Tony Mackdb8580b2015-01-30 17:20:46 -05001288 readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Scott Bakerdcf9e0d2015-11-09 16:17:11 -08001289 list_display = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'isolation', 'slice', 'flavor', 'image', 'node', 'deployment']
Scott Baker2f295402015-02-13 14:38:21 -08001290 list_display_links = ('backend_status_icon', 'all_ips_string', 'instance_id', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001291
Scott Bakere317a992015-11-10 17:07:23 -08001292 suit_form_tabs =(('general', 'Instance Details'), ('ports', 'Ports'), ('container', 'Container Settings'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001293
Tony Mack32010062015-09-13 22:50:39 +00001294 inlines = [TagInline, InstancePortInline]
Tony Mack53106f32013-04-27 16:43:01 -04001295
Tony Mackb2dba4b2014-12-26 13:38:02 -05001296 user_readonly_fields = ['slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001297
Scott Baker7ccc6ad2015-01-25 22:16:13 -08001298 def ssh_command(self, obj):
1299 ssh_command = obj.get_ssh_command()
1300 if ssh_command:
1301 return ssh_command
1302 else:
1303 return "(not available)"
1304
Tony Mackc2835a92013-05-28 09:18:49 -04001305 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1306 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001307 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001308
Tony Mackd8515472015-08-19 11:58:18 -04001309 return super(InstanceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001310
Tony Mack04062832013-05-10 08:22:44 -04001311 def queryset(self, request):
Tony Mackd8515472015-08-19 11:58:18 -04001312 # admins can see all instances. Users can only see instances of
Tony Mack04062832013-05-10 08:22:44 -04001313 # the slices they belong to.
Tony Mackd8515472015-08-19 11:58:18 -04001314 return Instance.select_by_user(request.user)
Tony Mack5b061472014-02-04 07:57:10 -05001315
Scott Baker17ee7f82015-09-21 21:42:41 -07001316 def add_view(self, request, form_url='', extra_context = None):
1317 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
1318 return super(InstanceAdmin,self).add_view(request, form_url, extra_context)
Tony Mack04062832013-05-10 08:22:44 -04001319
Scott Baker17ee7f82015-09-21 21:42:41 -07001320 def change_view(self, request, object_id, extra_context=None):
1321 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string', 'deployment', 'slice', 'flavor', 'image', 'node')
1322 self.readonly_save = self.readonly_fields # for XOSAdminMixin.change_view's user_readonly_fields switching code
1323 return super(InstanceAdmin,self).change_view(request, object_id, extra_context)
Tony Mack53106f32013-04-27 16:43:01 -04001324
Scott Baker878576f2015-09-21 16:02:54 -07001325 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
1326 deployment_nodes = []
1327 for node in Node.objects.all():
1328 deployment_nodes.append( (node.site_deployment.deployment.id, node.id, node.name) )
1329
1330 deployment_flavors = []
1331 for flavor in Flavor.objects.all():
1332 for deployment in flavor.deployments.all():
1333 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
1334
1335 deployment_images = []
1336 for image in Image.objects.all():
1337 for deployment_image in image.imagedeployments.all():
1338 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
1339
1340 site_login_bases = []
1341 for site in Site.objects.all():
1342 site_login_bases.append((site.id, site.login_base))
1343
1344 context["deployment_nodes"] = deployment_nodes
1345 context["deployment_flavors"] = deployment_flavors
1346 context["deployment_images"] = deployment_images
1347 context["site_login_bases"] = site_login_bases
1348 return super(InstanceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
1349
1350 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1351 if db_field.name == 'deployment':
1352 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
1353 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_deployment_changed(this);"})
1354 if db_field.name == 'flavor':
1355 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_flavor_changed(this);"})
1356
1357 field = super(InstanceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1358
1359 return field
1360
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001361 #def save_model(self, request, obj, form, change):
1362 # # update openstack connection to use this site/tenant
1363 # auth = request.session.get('auth', {})
1364 # auth['tenant'] = obj.slice.name
1365 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1366 # obj.creator = request.user
1367 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001368
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001369 #def delete_model(self, request, obj):
1370 # # update openstack connection to use this site/tenant
1371 # auth = request.session.get('auth', {})
1372 # auth['tenant'] = obj.slice.name
1373 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1374 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001375
Scott Bakerdcf9e0d2015-11-09 16:17:11 -08001376#class ContainerPortInline(XOSTabularInline):
1377# fields = ['backend_status_icon', 'network', 'container', 'ip', 'mac', 'segmentation_id']
1378# readonly_fields = ("backend_status_icon", "ip", "mac", "segmentation_id")
1379# model = Port
1380# selflink_fieldname = "network"
1381# extra = 0
1382# verbose_name_plural = "Ports"
1383# verbose_name = "Port"
1384# suit_classes = 'suit-tab suit-tab-ports'
Scott Baker9f457d92015-10-26 19:52:10 -07001385
Scott Bakerdcf9e0d2015-11-09 16:17:11 -08001386#class ContainerAdmin(XOSBaseAdmin):
1387# fieldsets = [
1388# ('Container Details', {'fields': ['backend_status_text', 'slice', 'node', 'docker_image', 'volumes', 'no_sync'], 'classes': ['suit-tab suit-tab-general'], })
1389# ]
1390# readonly_fields = ('backend_status_text', )
1391# list_display = ['backend_status_icon', 'id']
1392# list_display_links = ('backend_status_icon', 'id', )
1393#
1394# suit_form_tabs =(('general', 'Container Details'), ('ports', 'Ports'))
1395#
1396# inlines = [TagInline, ContainerPortInline]
1397#
1398# def formfield_for_foreignkey(self, db_field, request, **kwargs):
1399# if db_field.name == 'slice':
1400# kwargs['queryset'] = Slice.select_by_user(request.user)
1401#
1402# return super(ContainerAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1403#
1404# def queryset(self, request):
1405# # admins can see all instances. Users can only see instances of
1406# # the slices they belong to.
1407# return Container.select_by_user(request.user)
Scott Bakere4ea8952015-10-26 15:12:13 -07001408
Siobhan Tully53437282013-04-26 19:30:27 -04001409class UserCreationForm(forms.ModelForm):
1410 """A form for creating new users. Includes all the required
1411 fields, plus a repeated password."""
1412 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1413 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1414
1415 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001416 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001417 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001418
1419 def clean_password2(self):
1420 # Check that the two password entries match
1421 password1 = self.cleaned_data.get("password1")
1422 password2 = self.cleaned_data.get("password2")
1423 if password1 and password2 and password1 != password2:
1424 raise forms.ValidationError("Passwords don't match")
1425 return password2
1426
1427 def save(self, commit=True):
1428 # Save the provided password in hashed format
1429 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001430 user.password = self.cleaned_data["password1"]
1431 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001432 if commit:
1433 user.save()
1434 return user
1435
Siobhan Tully567e3e62013-06-21 18:03:16 -04001436
Siobhan Tully53437282013-04-26 19:30:27 -04001437class UserChangeForm(forms.ModelForm):
1438 """A form for updating users. Includes all the fields on
1439 the user, but replaces the password field with admin's
1440 password hash display field.
1441 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001442 password = ReadOnlyPasswordHashField(label='Password',
1443 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001444
Scott Bakerb6043c22015-05-19 16:39:48 -07001445 PROFILE_CHOICES = ((None, '------'), ('regular', 'Regular user'), ('cp', 'Content Provider'))
1446 profile = forms.ChoiceField(choices=PROFILE_CHOICES, required=False, label="Quick Profile")
1447
Siobhan Tully53437282013-04-26 19:30:27 -04001448 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001449 model = User
Scott Baker69e045d2014-11-17 23:44:03 -08001450 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001451
1452 def clean_password(self):
1453 # Regardless of what the user provides, return the initial value.
1454 # This is done here, rather than on the field, because the
1455 # field does not have access to the initial value
1456 return self.initial["password"]
1457
Scott Bakerb6043c22015-05-19 16:39:48 -07001458 def save(self, *args, **kwargs):
1459 if self.cleaned_data['profile']:
1460 self.instance.apply_profile(self.cleaned_data['profile'])
1461
1462 return super(UserChangeForm, self).save(*args, **kwargs)
1463
Scott Baker67db95f2015-02-18 15:50:11 -08001464class UserDashboardViewInline(XOSTabularInline):
Scott Baker2c3cb642014-05-19 17:55:56 -07001465 model = UserDashboardView
1466 extra = 0
1467 suit_classes = 'suit-tab suit-tab-dashboards'
1468 fields = ['user', 'dashboardView', 'order']
1469
Scott Baker67db95f2015-02-18 15:50:11 -08001470class ControllerUserInline(XOSTabularInline):
Tony Mack30dfcd72015-01-10 23:08:10 -05001471 model = ControllerUser
1472 extra = 0
1473 suit_classes = 'suit-tab suit-tab-admin-only'
1474 fields = ['controller', 'user', 'kuser_id']
1475 readonly_fields=['controller']
1476
1477
Scott Baker3a8aed62015-02-27 12:21:22 -08001478class UserAdmin(XOSAdminMixin, UserAdmin):
1479 # Note: Make sure XOSAdminMixin is listed before
Scott Bakerf4aeedc2014-10-03 13:10:47 -07001480 # admin.ModelAdmin in the class declaration.
1481
Siobhan Tully53437282013-04-26 19:30:27 -04001482 class Meta:
1483 app_label = "core"
1484
1485 # The forms to add and change user instances
1486 form = UserChangeForm
1487 add_form = UserCreationForm
1488
1489 # The fields to be used in displaying the User model.
1490 # These override the definitions on the base UserAdmin
1491 # that reference specific fields on auth.User.
Scott Bakera111f442015-01-24 13:33:26 -08001492 list_display = ('backend_status_icon', 'email', 'firstname', 'lastname', 'site', 'last_login')
1493 list_display_links = ("email",)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001494 list_filter = ('site',)
Scott Baker90472612015-01-29 10:55:53 -08001495 inlines = [SlicePrivilegeInline,SitePrivilegeInline]
Tony Mack30dfcd72015-01-10 23:08:10 -05001496 admin_inlines = [ControllerUserInline]
Scott Baker6da26a52015-06-10 16:14:58 -07001497 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 -05001498 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1499
Siobhan Tully53437282013-04-26 19:30:27 -04001500 fieldsets = (
Scott Baker034232d2015-05-15 09:19:36 -07001501 ('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 -04001502 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001503 #('Important dates', {'fields': ('last_login',)}),
1504 )
1505 add_fieldsets = (
1506 (None, {
1507 'classes': ('wide',),
Scott Baker034232d2015-05-15 09:19:36 -07001508 'fields': ('site', 'email', 'firstname', 'lastname', 'is_admin', 'is_readonly', 'is_appuser', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001509 ),
1510 )
Scott Baker40c00762014-08-21 16:55:59 -07001511 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001512 search_fields = ('email',)
1513 ordering = ('email',)
1514 filter_horizontal = ()
1515
Scott Baker3ca51f62014-05-23 12:05:11 -07001516 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001517
Scott Baker6a995352014-10-06 17:51:20 -07001518 @property
1519 def suit_form_tabs(self):
1520 if getattr(_thread_locals, "obj", None) is None:
1521 return []
1522 else:
Tony Mack30dfcd72015-01-10 23:08:10 -05001523 tabs = [('general','Login Details'),
Scott Baker6a995352014-10-06 17:51:20 -07001524 ('contact','Contact Information'),
1525 ('sliceprivileges','Slice Privileges'),
Scott Baker90472612015-01-29 10:55:53 -08001526 ('siteprivileges','Site Privileges')]
Tony Mack30dfcd72015-01-10 23:08:10 -05001527
1528 request=getattr(_thread_locals, "request", None)
1529 if request and request.user.is_admin:
1530 tabs.append( ('admin-only', 'Admin-Only') )
1531
1532 return tabs
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001533
Tony Mackc2835a92013-05-28 09:18:49 -04001534 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1535 if db_field.name == 'site':
Tony Mack02b8f142015-08-04 17:32:32 -04001536 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_users=True)
Tony Mackc2835a92013-05-28 09:18:49 -04001537
1538 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1539
Tony Mack5b061472014-02-04 07:57:10 -05001540 def queryset(self, request):
1541 return User.select_by_user(request.user)
1542
Tony Mackc8f443d2015-01-25 21:58:30 -05001543 def get_form(self, request, obj=None, **kwargs):
Tony Mack03b92292015-01-28 12:37:12 -05001544 # copy login details list
1545 login_details_fields = list(self.fieldListLoginDetails)
Tony Mack933b2912015-01-28 12:49:58 -05001546 if not request.user.is_admin:
Scott Baker90472612015-01-29 10:55:53 -08001547 # only admins can see 'is_admin' and 'is_readonly' fields
Tony Mack03b92292015-01-28 12:37:12 -05001548 if 'is_admin' in login_details_fields:
1549 login_details_fields.remove('is_admin')
1550 if 'is_readonly' in login_details_fields:
Scott Bakerb6043c22015-05-19 16:39:48 -07001551 login_details_fields.remove('is_readonly')
1552 if 'is_appuser' in login_details_fields:
1553 login_details_fields.remove('is_admin')
1554 if 'profile' in login_details_fields:
1555 login_details_fields.remove('profile')
Tony Mack933b2912015-01-28 12:49:58 -05001556 #if len(request.user.siteprivileges.filter(role__role = 'pi')) > 0:
Scott Bakerb6043c22015-05-19 16:39:48 -07001557 # only admins and pis can change a user's site
Tony Mack933b2912015-01-28 12:49:58 -05001558 # self.readonly_fields = ('backend_status_text', 'site')
Tony Mack03b92292015-01-28 12:37:12 -05001559 self.fieldsets = (
1560 ('Login Details', {'fields': login_details_fields, 'classes':['suit-tab suit-tab-general']}),
1561 ('Contact Information', {'fields': self.fieldListContactInfo, 'classes':['suit-tab suit-tab-contact']}),
1562 )
Tony Mackc8f443d2015-01-25 21:58:30 -05001563 return super(UserAdmin, self).get_form(request, obj, **kwargs)
1564
Scott Baker67db95f2015-02-18 15:50:11 -08001565class ControllerDashboardViewInline(XOSTabularInline):
Scott Bakerf2c0c512014-12-22 17:35:34 -08001566 model = ControllerDashboardView
Scott Baker786a9c12014-12-19 16:41:12 -08001567 extra = 0
1568 fields = ["controller", "url"]
1569 suit_classes = 'suit-tab suit-tab-controllers'
1570
Scott Baker67db95f2015-02-18 15:50:11 -08001571class DashboardViewAdmin(XOSBaseAdmin):
Scott Baker2c3cb642014-05-19 17:55:56 -07001572 fieldsets = [('Dashboard View Details',
Scott Baker59248182015-02-17 13:34:32 -08001573 {'fields': ['backend_status_text', 'name', 'url', 'enabled', 'deployments'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001574 'classes': ['suit-tab suit-tab-general']})
1575 ]
Scott Baker2c44e6e2015-01-18 16:46:26 -08001576 list_display = ["name", "enabled", "url"]
Scott Baker40c00762014-08-21 16:55:59 -07001577 readonly_fields = ('backend_status_text', )
Scott Bakerf2c0c512014-12-22 17:35:34 -08001578 inlines = [ControllerDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001579
Scott Baker786a9c12014-12-19 16:41:12 -08001580 suit_form_tabs =(('general','Dashboard View Details'),
1581 ('controllers', 'Per-controller Dashboard Details'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001582
Scott Baker67db95f2015-02-18 15:50:11 -08001583class ServiceResourceInline(XOSTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001584 model = ServiceResource
1585 extra = 0
1586
Scott Baker67db95f2015-02-18 15:50:11 -08001587class ServiceClassAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001588 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1589 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001590 inlines = [ServiceResourceInline]
1591
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001592 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1593 user_readonly_inlines = []
1594
Scott Baker67db95f2015-02-18 15:50:11 -08001595class ReservedResourceInline(XOSTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001596 model = ReservedResource
1597 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001598 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001599
1600 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1601 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1602
1603 if db_field.name == 'resource':
1604 # restrict resources to those that the slice's service class allows
1605 if request._slice is not None:
1606 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1607 if len(field.queryset) > 0:
1608 field.initial = field.queryset.all()[0]
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -05001609 else:
1610 field.queryset = field.queryset.none()
Tony Mackd8515472015-08-19 11:58:18 -04001611 elif db_field.name == 'instance':
1612 # restrict instances to those that belong to the slice
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -05001613 if request._slice is not None:
Scott Baker133c9212013-05-17 09:09:11 -07001614 field.queryset = field.queryset.filter(slice = request._slice)
1615 else:
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -05001616 field.queryset = field.queryset.none()
1617
Scott Baker133c9212013-05-17 09:09:11 -07001618 return field
1619
Tony Mack5b061472014-02-04 07:57:10 -05001620 def queryset(self, request):
1621 return ReservedResource.select_by_user(request.user)
1622
Scott Baker133c9212013-05-17 09:09:11 -07001623class ReservationChangeForm(forms.ModelForm):
1624 class Meta:
1625 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001626 widgets = {
1627 'slice' : LinkedSelect
1628 }
Scott Baker133c9212013-05-17 09:09:11 -07001629
1630class ReservationAddForm(forms.ModelForm):
1631 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1632 refresh = forms.CharField(widget=forms.HiddenInput())
1633
1634 class Media:
Scott Baker06868952015-02-18 15:15:58 -08001635 css = {'all': ('xos.css',)} # .field-refresh { display: none; }
Scott Baker133c9212013-05-17 09:09:11 -07001636
1637 def clean_slice(self):
1638 slice = self.cleaned_data.get("slice")
1639 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1640 if len(x) == 0:
1641 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1642 return slice
1643
1644 class Meta:
1645 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001646 widgets = {
1647 'slice' : LinkedSelect
1648 }
1649
Scott Baker133c9212013-05-17 09:09:11 -07001650
1651class ReservationAddRefreshForm(ReservationAddForm):
1652 """ This form is displayed when the Reservation Form receives an update
1653 from the Slice dropdown onChange handler. It doesn't validate the
1654 data and doesn't save the data. This will cause the form to be
1655 redrawn.
1656 """
1657
Scott Baker8737e5f2013-05-17 09:35:32 -07001658 """ don't validate anything other than slice """
1659 dont_validate_fields = ("startTime", "duration")
1660
Scott Baker133c9212013-05-17 09:09:11 -07001661 def full_clean(self):
1662 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001663
1664 for fieldname in self.dont_validate_fields:
1665 if fieldname in self._errors:
1666 del self._errors[fieldname]
1667
Scott Baker133c9212013-05-17 09:09:11 -07001668 return result
1669
1670 """ don't save anything """
1671 def is_valid(self):
1672 return False
1673
Scott Baker67db95f2015-02-18 15:50:11 -08001674class ReservationAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001675 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001676 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001677 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001678 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001679 form = ReservationAddForm
1680
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001681 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1682
1683 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001684 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001685
Scott Baker133c9212013-05-17 09:09:11 -07001686 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001687 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001688 request._refresh = False
1689 request._slice = None
1690 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001691 # "refresh" will be set to "1" if the form was submitted due to
1692 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001693 if request.POST.get("refresh","1") == "1":
1694 request._refresh = True
1695 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001696
1697 # Keep track of the slice that was selected, so the
1698 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001699 request._slice = request.POST.get("slice",None)
1700 if (request._slice is not None):
1701 request._slice = Slice.objects.get(id=request._slice)
1702
1703 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1704 return result
1705
Scott Bakeracd45142013-05-19 16:19:16 -07001706 def changelist_view(self, request, extra_context = None):
1707 timezone.activate(request.user.timezone)
1708 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1709
Scott Baker133c9212013-05-17 09:09:11 -07001710 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001711 request._obj_ = obj
1712 if obj is not None:
1713 # For changes, set request._slice to the slice already set in the
1714 # object.
1715 request._slice = obj.slice
1716 self.form = ReservationChangeForm
1717 else:
1718 if getattr(request, "_refresh", False):
1719 self.form = ReservationAddRefreshForm
1720 else:
1721 self.form = ReservationAddForm
1722 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1723
Scott Baker133c9212013-05-17 09:09:11 -07001724 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001725 if (obj is not None):
1726 # Prevent slice from being changed after the reservation has been
1727 # created.
1728 return ['slice']
1729 else:
Scott Baker133c9212013-05-17 09:09:11 -07001730 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001731
Tony Mack5b061472014-02-04 07:57:10 -05001732 def queryset(self, request):
1733 return Reservation.select_by_user(request.user)
1734
Scott Baker67db95f2015-02-18 15:50:11 -08001735class NetworkParameterTypeAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001736 list_display = ("backend_status_icon", "name", )
1737 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001738 user_readonly_fields = ['name']
1739 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001740
Scott Baker67db95f2015-02-18 15:50:11 -08001741class RouterAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001742 list_display = ("backend_status_icon", "name", )
1743 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001744 user_readonly_fields = ['name']
1745 user_readonly_inlines = []
1746
Scott Baker67db95f2015-02-18 15:50:11 -08001747class RouterInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001748 model = Router.networks.through
1749 extra = 0
1750 verbose_name_plural = "Routers"
1751 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001752 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001753
Scott Bakerb27b62c2014-08-15 16:29:16 -07001754class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001755 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001756 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001757 verbose_name_plural = "Parameters"
1758 verbose_name = "Parameter"
1759 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001760 fields = ['backend_status_icon', 'parameter', 'value']
1761 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001762
Scott Bakerfbade882015-08-25 18:00:15 -07001763class NetworkPortInline(XOSTabularInline):
Scott Bakerdcf9e0d2015-11-09 16:17:11 -08001764 fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
Scott Bakere553b472015-09-08 18:22:15 -07001765 readonly_fields = ("backend_status_icon", "ip", "mac")
Scott Baker5a7d9312015-08-26 09:43:33 -07001766 model = Port
Tony Mackd8515472015-08-19 11:58:18 -04001767 selflink_fieldname = "instance"
Scott Baker74d8e622013-07-29 16:04:22 -07001768 extra = 0
Scott Baker5f1068a2015-08-25 17:11:30 -07001769 verbose_name_plural = "Ports"
1770 verbose_name = "Port"
1771 suit_classes = 'suit-tab suit-tab-ports'
Scott Baker74d8e622013-07-29 16:04:22 -07001772
Scott Baker67db95f2015-02-18 15:50:11 -08001773class NetworkSlicesInline(XOSTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001774 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001775 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001776 extra = 0
1777 verbose_name_plural = "Slices"
1778 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001779 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001780 fields = ['backend_status_icon', 'network','slice']
1781 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001782
Scott Baker67db95f2015-02-18 15:50:11 -08001783class ControllerNetworkInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -05001784 model = ControllerNetwork
Scott Baker8806cdf2014-10-17 16:27:23 -07001785 extra = 0
Tony Mack06c8e472014-11-30 15:53:08 -05001786 verbose_name_plural = "Controller Networks"
1787 verbose_name = "Controller Network"
Scott Baker8806cdf2014-10-17 16:27:23 -07001788 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack06c8e472014-11-30 15:53:08 -05001789 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Baker8806cdf2014-10-17 16:27:23 -07001790 readonly_fields = ('backend_status_icon', )
1791
Scott Baker69e045d2014-11-17 23:44:03 -08001792class NetworkForm(forms.ModelForm):
1793 class Meta:
1794 model = Network
1795 widgets = {
1796 'topologyParameters': UploadTextareaWidget,
1797 'controllerParameters': UploadTextareaWidget,
1798 }
1799
Scott Baker67db95f2015-02-18 15:50:11 -08001800class NetworkAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001801 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1802 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001803 readonly_fields = ("subnet", )
Scott Bakerfbade882015-08-25 18:00:15 -07001804 inlines = [NetworkParameterInline, NetworkPortInline, NetworkSlicesInline, RouterInline]
Tony Mack3066a952015-01-05 22:48:11 -05001805 admin_inlines = [ControllerNetworkInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001806
Scott Baker69e045d2014-11-17 23:44:03 -08001807 form=NetworkForm
1808
Siobhan Tully2d95e482013-09-06 10:56:06 -04001809 fieldsets = [
Scott Baker3789cb22015-08-21 16:40:53 -07001810 (None, {'fields': ['backend_status_text', 'name','template','ports','labels',
1811 'owner','guaranteed_bandwidth', 'permit_all_slices',
1812 'permitted_slices','network_id','router_id','subnet_id',
1813 'subnet', 'autoconnect'],
Scott Baker3e28dd72014-11-17 16:04:45 -08001814 'classes':['suit-tab suit-tab-general']}),
Scott Baker549aa252015-01-03 12:29:29 -08001815 (None, {'fields': ['topology_parameters', 'controller_url', 'controller_parameters'],
Scott Baker3e28dd72014-11-17 16:04:45 -08001816 'classes':['suit-tab suit-tab-sdn']}),
1817 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001818
Scott Baker40c00762014-08-21 16:55:59 -07001819 readonly_fields = ('backend_status_text', )
Scott Baker3789cb22015-08-21 16:40:53 -07001820 user_readonly_fields = ['name','template','ports','labels','owner','guaranteed_bandwidth',
1821 'permit_all_slices','permitted_slices','network_id','router_id',
1822 'subnet_id','subnet','autoconnect']
Siobhan Tully2d95e482013-09-06 10:56:06 -04001823
Scott Baker8806cdf2014-10-17 16:27:23 -07001824 @property
1825 def suit_form_tabs(self):
1826 tabs=[('general','Network Details'),
Scott Baker3e28dd72014-11-17 16:04:45 -08001827 ('sdn', 'SDN Configuration'),
Scott Baker8806cdf2014-10-17 16:27:23 -07001828 ('netparams', 'Parameters'),
Scott Baker5f1068a2015-08-25 17:11:30 -07001829 ('ports','Ports'),
Scott Baker8806cdf2014-10-17 16:27:23 -07001830 ('networkslices','Slices'),
1831 ('routers','Routers'),
1832 ]
1833
1834 request=getattr(_thread_locals, "request", None)
1835 if request and request.user.is_admin:
1836 tabs.append( ('admin-only', 'Admin-Only') )
1837
1838 return tabs
1839
1840
Scott Baker67db95f2015-02-18 15:50:11 -08001841class NetworkTemplateAdmin(XOSBaseAdmin):
Scott Baker369f9b92015-01-03 12:03:38 -08001842 list_display = ("backend_status_icon", "name", "guaranteed_bandwidth", "visibility")
Scott Baker63d1a552014-08-21 15:19:07 -07001843 list_display_links = ('backend_status_icon', 'name', )
Scott Baker369f9b92015-01-03 12:03:38 -08001844 user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001845 user_readonly_inlines = []
Scott Baker3e28dd72014-11-17 16:04:45 -08001846 fieldsets = [
Scott Baker369f9b92015-01-03 12:03:38 -08001847 (None, {'fields': ['name', 'description', 'guaranteed_bandwidth', 'visibility', 'translation', 'shared_network_name', 'shared_network_id', 'topology_kind', 'controller_kind'],
Scott Baker3e28dd72014-11-17 16:04:45 -08001848 'classes':['suit-tab suit-tab-general']}),]
1849 suit_form_tabs = (('general','Network Template Details'), )
Scott Baker74d8e622013-07-29 16:04:22 -07001850
Scott Baker67db95f2015-02-18 15:50:11 -08001851class FlavorAdmin(XOSBaseAdmin):
Scott Baker37b47902014-09-02 14:37:41 -07001852 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1853 list_display_links = ("backend_status_icon", "name")
1854 user_readonly_fields = ("name", "flavor")
1855 fields = ("name", "description", "flavor", "order", "default")
1856
Tony Mack31c2b8f2013-04-26 20:01:42 -04001857# register a signal that caches the user's credentials when they log in
1858def cache_credentials(sender, user, request, **kwds):
1859 auth = {'username': request.POST['username'],
1860 'password': request.POST['password']}
1861 request.session['auth'] = auth
1862user_logged_in.connect(cache_credentials)
1863
Scott Baker15cddfa2013-12-09 13:45:19 -08001864def dollar_field(fieldName, short_description):
1865 def newFunc(self, obj):
1866 try:
1867 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1868 except:
1869 x=getattr(obj, fieldName, 0.0)
1870 return x
1871 newFunc.short_description = short_description
1872 return newFunc
1873
1874def right_dollar_field(fieldName, short_description):
1875 def newFunc(self, obj):
1876 try:
1877 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1878 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1879 except:
1880 x=getattr(obj, fieldName, 0.0)
1881 return x
1882 newFunc.short_description = short_description
1883 newFunc.allow_tags = True
1884 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001885
Scott Baker67db95f2015-02-18 15:50:11 -08001886class InvoiceChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001887 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001888 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001889 verbose_name_plural = "Charges"
1890 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001891 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001892 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1893 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1894 can_delete = False
1895 max_num = 0
1896
1897 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001898
1899class InvoiceAdmin(admin.ModelAdmin):
1900 list_display = ("date", "account")
1901
1902 inlines = [InvoiceChargeInline]
1903
Scott Baker9cb88a22013-12-09 18:56:00 -08001904 fields = ["date", "account", "dollar_amount"]
1905 readonly_fields = ["date", "account", "dollar_amount"]
1906
1907 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001908
Scott Baker67db95f2015-02-18 15:50:11 -08001909class InvoiceInline(XOSTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001910 model = Invoice
1911 extra = 0
1912 verbose_name_plural = "Invoices"
1913 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001914 fields = ["date", "dollar_amount"]
1915 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001916 suit_classes = 'suit-tab suit-tab-accountinvoice'
1917 can_delete=False
1918 max_num=0
1919
1920 dollar_amount = right_dollar_field("amount", "Amount")
1921
Scott Baker67db95f2015-02-18 15:50:11 -08001922class PendingChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001923 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001924 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001925 verbose_name_plural = "Charges"
1926 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001927 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001928 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1929 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001930 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001931 can_delete=False
1932 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001933
1934 def queryset(self, request):
1935 qs = super(PendingChargeInline, self).queryset(request)
1936 qs = qs.filter(state="pending")
1937 return qs
1938
Scott Baker15cddfa2013-12-09 13:45:19 -08001939 dollar_amount = right_dollar_field("amount", "Amount")
1940
Scott Baker67db95f2015-02-18 15:50:11 -08001941class PaymentInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001942 model=Payment
1943 extra = 1
1944 verbose_name_plural = "Payments"
1945 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001946 fields = ["date", "dollar_amount"]
1947 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001948 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001949 can_delete=False
1950 max_num=0
1951
1952 dollar_amount = right_dollar_field("amount", "Amount")
1953
Scott Baker43105042013-12-06 23:23:36 -08001954class AccountAdmin(admin.ModelAdmin):
1955 list_display = ("site", "balance_due")
1956
1957 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1958
1959 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001960 (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 -08001961
Scott Baker15cddfa2013-12-09 13:45:19 -08001962 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001963
1964 suit_form_tabs =(
1965 ('general','Account Details'),
1966 ('accountinvoice', 'Invoices'),
1967 ('accountpayments', 'Payments'),
1968 ('accountpendingcharges','Pending Charges'),
1969 )
1970
Scott Baker15cddfa2013-12-09 13:45:19 -08001971 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1972 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1973 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1974
Scott Bakerc24f86d2015-08-14 09:10:11 -07001975class ProgramForm(forms.ModelForm):
1976 class Meta:
1977 model = Program
1978 widgets = {
1979 'contents': UploadTextareaWidget(attrs={'rows': 20, 'cols': 80, 'class': "input-xxlarge"}),
1980 'description': forms.Textarea(attrs={'rows': 3, 'cols': 80, 'class': 'input-xxlarge'}),
1981 'messages': forms.Textarea(attrs={'rows': 20, 'cols': 80, 'class': 'input-xxlarge'}),
1982 'output': forms.Textarea(attrs={'rows': 3, 'cols': 80, 'class': 'input-xxlarge'})
1983 }
1984
1985class ProgramAdmin(XOSBaseAdmin):
1986 list_display = ("name", "status")
1987 list_display_links = ('name', "status")
1988
1989 form=ProgramForm
1990
1991 fieldsets = [
1992 (None, {'fields': ['name', 'command', 'kind', 'description', 'output', 'status'],
1993 'classes':['suit-tab suit-tab-general']}),
1994 (None, {'fields': ['contents'],
1995 'classes':['suit-tab suit-tab-contents']}),
1996 (None, {'fields': ['messages'],
1997 'classes':['suit-tab suit-tab-messages']}),
1998 ]
1999
2000 readonly_fields = ("status",)
2001
2002 @property
2003 def suit_form_tabs(self):
2004 tabs=[('general','Program Details'),
2005 ('contents','Program Source'),
2006 ('messages','Messages'),
2007 ]
2008
2009 request=getattr(_thread_locals, "request", None)
2010 if request and request.user.is_admin:
2011 tabs.append( ('admin-only', 'Admin-Only') )
2012
2013 return tabs
2014
Siobhan Tully53437282013-04-26 19:30:27 -04002015# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04002016admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04002017# ... and, since we're not using Django's builtin permissions,
2018# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04002019#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04002020
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002021#Do not show django evolution in the admin interface
2022from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05002023#admin.site.unregister(Version)
2024#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002025
2026
2027# When debugging it is often easier to see all the classes, but for regular use
2028# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05002029showAll = False
Scott Baker43105042013-12-06 23:23:36 -08002030
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002031admin.site.register(Deployment, DeploymentAdmin)
Tony Mack06c8e472014-11-30 15:53:08 -05002032admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04002033admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04002034admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04002035admin.site.register(Service, ServiceAdmin)
Tony Mack450b6e02015-01-25 12:35:29 -05002036#admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07002037admin.site.register(Network, NetworkAdmin)
2038admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07002039admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Scott Bakerc24f86d2015-08-14 09:10:11 -07002040admin.site.register(Program, ProgramAdmin)
Tony Mack450b6e02015-01-25 12:35:29 -05002041#admin.site.register(Account, AccountAdmin)
2042#admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002043
Siobhan Tullycf04fb62014-01-11 11:25:57 -05002044if True:
2045 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
2046 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullyd3515752013-06-21 16:34:53 -04002047 admin.site.register(Tag, TagAdmin)
Tony Mack06c8e472014-11-30 15:53:08 -05002048 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04002049 admin.site.register(SiteRole)
2050 admin.site.register(SliceRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002051 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04002052 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
2053 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Tony Mackd8515472015-08-19 11:58:18 -04002054 admin.site.register(Instance, InstanceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04002055 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07002056 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07002057 admin.site.register(Flavor, FlavorAdmin)
Scott Bakerb3cf9212015-07-06 14:40:20 -07002058 admin.site.register(TenantRoot, TenantRootAdmin)
2059 admin.site.register(TenantRootRole, TenantRootRoleAdmin)
Scott Baker1e7e3482015-10-15 15:59:19 -07002060 admin.site.register(TenantAttribute, TenantAttributeAdmin)
Scott Bakerdcf9e0d2015-11-09 16:17:11 -08002061# admin.site.register(Container, ContainerAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04002062