blob: afaf0576699e0761ce2d067d876754214133e4fe [file] [log] [blame]
Siobhan Tully30fd4292013-05-10 08:59:56 -04001from core.models import Site
2from core.models import *
3from openstack.manager import OpenStackManager
Tony Macke59a7c82013-04-27 11:08:10 -04004
Tony Mack7130ac32013-03-22 21:58:00 -04005from django.contrib import admin
Siobhan Tully53437282013-04-26 19:30:27 -04006from django.contrib.auth.models import Group
Siobhan Tully4bc09f22013-04-10 21:15:21 -04007from django import forms
Tony Mackd90cdbf2013-04-16 22:48:40 -04008from django.utils.safestring import mark_safe
Tony Mack7130ac32013-03-22 21:58:00 -04009from django.contrib.auth.admin import UserAdmin
Scott Baker9f6b8ed2014-11-17 23:44:03 -080010from django.contrib.admin.widgets import FilteredSelectMultiple, AdminTextareaWidget
Scott Bakercbfb6002014-10-03 00:32:37 -070011from django.contrib.auth.forms import ReadOnlyPasswordHashField, AdminPasswordChangeForm
Scott Bakeracd45142013-05-19 16:19:16 -070012from django.contrib.auth.signals import user_logged_in
13from django.utils import timezone
Siobhan Tullyde5450d2013-06-21 11:35:33 -040014from django.contrib.contenttypes import generic
Siobhan Tullybfd11dc2013-09-03 12:59:24 -040015from suit.widgets import LinkedSelect
Siobhan Tullycf04fb62014-01-11 11:25:57 -050016from django.core.exceptions import PermissionDenied
Tony Mack7b6400d2015-02-16 19:54:24 -050017from django.core.urlresolvers import reverse, resolve, NoReverseMatch
Scott Baker9f6b8ed2014-11-17 23:44:03 -080018from django.utils.encoding import force_text, python_2_unicode_compatible
19from django.utils.html import conditional_escape, format_html
Scott Baker7a056af2015-02-26 20:42:11 -080020from django.utils.text import capfirst
Scott Baker9f6b8ed2014-11-17 23:44:03 -080021from django.forms.utils import flatatt, to_current_timezone
Scott Baker2d281a92015-07-09 19:06:08 -070022from django.core.exceptions import PermissionDenied, ValidationError
Scott Baker3cde7372014-10-21 21:03:08 -070023from cgi import escape as html_escape
Scott Baker2d281a92015-07-09 19:06:08 -070024from django.contrib import messages
Tony Mack7130ac32013-03-22 21:58:00 -040025
Scott Baker36f50872014-08-21 13:01:25 -070026import django_evolution
Scott Baker0a5633b2014-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 Bakerb6b474d2015-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 Bakerb6b474d2015-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 Bakerb6b474d2015-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 Baker9f6b8ed2014-11-17 23:44:03 -080051class UploadTextareaWidget(AdminTextareaWidget):
52 def render(self, name, value, attrs=None):
53 if value is None:
S.Çağlar Onur0e591832015-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 Baker9f6b8ed2014-11-17 23:44:03 -080060 force_text(value))
61
Scott Bakerb92b5c72015-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 Bakera9412c32015-02-27 12:21:22 -080095class XOSAdminMixin(object):
Scott Bakercbfb6002014-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 Bakercbfb6002014-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 Bakercbfb6002014-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 Bakercf9cbad2015-07-08 18:23:17 -0700119 instance.caller = request.user
Scott Bakercbfb6002014-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 Onur0e591832015-02-24 17:28:09 -0500127 forms_to_delete = formset.deleted_forms
128 except AttributeError:
Scott Bakercbfb6002014-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 Bakera9412c32015-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 Bakerff3c2d22015-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 Bakerba534e72015-04-02 22:32:40 -0700154 def add_extra_context(self, request, extra_context):
Scott Bakerc481b322015-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 Baker0998bcc2015-04-02 22:07:18 -0700158 extra_context["custom_changelist_breadcrumb_url"] = getattr(self, "custom_changelist_breadcrumb_url", None)
Scott Bakerc481b322015-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 Bakerff3c2d22015-04-03 17:44:31 -0700170 "url": self.url_for_model_changelist(request,model) }
Scott Bakerc481b322015-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 Bakerc481b322015-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 Onur0e591832015-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 Bakerba534e72015-04-02 22:32:40 -0700193 self.add_extra_context(request, extra_context)
Scott Bakerc481b322015-02-27 12:12:14 -0800194
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500195 try:
Scott Bakera9412c32015-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 Baker2d281a92015-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 Bakera9412c32015-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 Bakerc481b322015-02-27 12:12:14 -0800219 def changelist_view(self, request, extra_context = None):
220 extra_context = extra_context or {}
221
Scott Bakerba534e72015-04-02 22:32:40 -0700222 self.add_extra_context(request, extra_context)
Scott Bakerc481b322015-02-27 12:12:14 -0800223
Scott Bakera9412c32015-02-27 12:21:22 -0800224 return super(XOSAdminMixin, self).changelist_view(request, extra_context=extra_context)
Scott Bakerc481b322015-02-27 12:12:14 -0800225
Scott Baker2aea0362015-04-14 17:01:18 -0700226 def add_view(self, request, form_url='', extra_context = None):
Scott Bakerff3c2d22015-04-03 17:44:31 -0700227 extra_context = extra_context or {}
228
229 self.add_extra_context(request, extra_context)
230
Scott Baker2aea0362015-04-14 17:01:18 -0700231 return super(XOSAdminMixin, self).add_view(request, form_url, extra_context=extra_context)
Scott Bakerff3c2d22015-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 Baker24ded6a2014-11-05 09:05:38 -0800243 def get_form(self, request, obj=None, **kwargs):
Scott Baker5c432692014-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 Bakera9412c32015-02-27 12:21:22 -0800249 return super(XOSAdminMixin, self).get_form(request, obj, **kwargs)
Scott Baker5c432692014-10-16 00:57:55 -0700250
251 def get_inline_instances(self, request, obj=None):
Scott Bakera9412c32015-02-27 12:21:22 -0800252 inlines = super(XOSAdminMixin, self).get_inline_instances(request, obj)
Scott Baker5c432692014-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 Bakera9412c32015-02-27 12:21:22 -0800261class ReadOnlyAwareAdmin(XOSAdminMixin, admin.ModelAdmin):
262 # Note: Make sure XOSAdminMixin is listed before
Scott Baker86c83ab2014-10-03 13:10:47 -0700263 # admin.ModelAdmin in the class declaration.
264
Scott Bakercbfb6002014-10-03 00:32:37 -0700265 pass
266
Scott Baker022cdcd2015-02-18 15:50:11 -0800267class XOSBaseAdmin(ReadOnlyAwareAdmin):
Scott Bakercbfb6002014-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 Baker7a056af2015-02-26 20:42:11 -0800281class ServiceAppAdmin (SingletonAdmin):
Scott Bakerc481b322015-02-27 12:12:14 -0800282 extracontext_registered_admins = True
Scott Baker7a056af2015-02-26 20:42:11 -0800283
Scott Baker022cdcd2015-02-18 15:50:11 -0800284class XOSTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800285 def __init__(self, *args, **kwargs):
Scott Baker022cdcd2015-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 Bakerd5df9502015-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 Baker29786782015-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 Bakerd5df9502015-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 Bakerd5df9502015-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 Onur0e591832015-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 Baker022cdcd2015-02-18 15:50:11 -0800388class ReservationInline(XOSTabularInline):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400389 model = Reservation
390 extra = 0
391 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700392
Tony Mack5b061472014-02-04 07:57:10 -0500393 def queryset(self, request):
394 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400395
Scott Bakerb27b62c2014-08-15 16:29:16 -0700396class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400397 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400398 extra = 0
399 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500400 fields = ['service', 'name', 'value']
401
402 def queryset(self, request):
403 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400404
Scott Baker74d8e622013-07-29 16:04:22 -0700405class NetworkLookerUpper:
Tony Mack3de59e32015-08-19 11:58:18 -0400406 """ This is a callable that looks up a network name in a instance and returns
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400407 the ip address for that network.
408 """
409
Scott Baker434ca7e2014-08-15 12:29:20 -0700410 byNetworkName = {} # class variable
411
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400412 def __init__(self, name):
413 self.short_description = name
414 self.__name__ = name
415 self.network_name = name
416
417 def __call__(self, obj):
418 if obj is not None:
Tony Mack3de59e32015-08-19 11:58:18 -0400419 for nbs in obj.networkinstance_set.all():
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400420 if (nbs.network.name == self.network_name):
421 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700422 return ""
423
424 def __str__(self):
425 return self.network_name
426
Scott Baker434ca7e2014-08-15 12:29:20 -0700427 @staticmethod
428 def get(network_name):
429 """ We want to make sure we alwars return the same NetworkLookerUpper
430 because sometimes django will cause them to be instantiated multiple
431 times (and we don't want different ones in form.fields vs
Tony Mack3de59e32015-08-19 11:58:18 -0400432 InstanceInline.readonly_fields).
Scott Baker434ca7e2014-08-15 12:29:20 -0700433 """
434 if network_name not in NetworkLookerUpper.byNetworkName:
435 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
436 return NetworkLookerUpper.byNetworkName[network_name]
437
Tony Mack3de59e32015-08-19 11:58:18 -0400438class InstanceInline(XOSTabularInline):
439 model = Instance
Scott Baker887d4a82015-01-19 11:32:20 -0800440 fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400441 extra = 0
Scott Baker887d4a82015-01-19 11:32:20 -0800442 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name']
Tony Mack3de59e32015-08-19 11:58:18 -0400443 suit_classes = 'suit-tab suit-tab-instances'
Scott Baker74d8e622013-07-29 16:04:22 -0700444
Tony Mack5b061472014-02-04 07:57:10 -0500445 def queryset(self, request):
Tony Mack3de59e32015-08-19 11:58:18 -0400446 return Instance.select_by_user(request.user)
Tony Mack5b061472014-02-04 07:57:10 -0500447
Scott Bakerb24cc932014-06-09 10:51:16 -0700448 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Tony Mackbf6aa302014-12-26 13:38:02 -0500449 if db_field.name == 'deployment':
Tony Mackbd908ae2015-02-24 15:41:49 -0500450
451 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
Tony Mack3de59e32015-08-19 11:58:18 -0400452 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_deployment_changed(this);"})
Tony Mackbf6aa302014-12-26 13:38:02 -0500453 if db_field.name == 'flavor':
Tony Mack3de59e32015-08-19 11:58:18 -0400454 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700455
Tony Mack3de59e32015-08-19 11:58:18 -0400456 field = super(InstanceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700457
458 return field
459
Tony Mack3de59e32015-08-19 11:58:18 -0400460class CordInstanceInline(XOSTabularInline):
461 model = Instance
Scott Baker0e289fd2015-06-12 10:40:15 -0700462 fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'flavor', 'image', 'node']
463 extra = 0
464 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name']
Tony Mack3de59e32015-08-19 11:58:18 -0400465 suit_classes = 'suit-tab suit-tab-instances'
Scott Baker0e289fd2015-06-12 10:40:15 -0700466
467 def queryset(self, request):
Tony Mack3de59e32015-08-19 11:58:18 -0400468 return Instance.select_by_user(request.user)
Scott Baker0e289fd2015-06-12 10:40:15 -0700469
470 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
471 if db_field.name == 'deployment':
472
473 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
Tony Mack3de59e32015-08-19 11:58:18 -0400474 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_deployment_changed(this);"})
Scott Baker0e289fd2015-06-12 10:40:15 -0700475 if db_field.name == 'flavor':
Tony Mack3de59e32015-08-19 11:58:18 -0400476 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_flavor_changed(this);"})
Scott Baker0e289fd2015-06-12 10:40:15 -0700477
Tony Mack3de59e32015-08-19 11:58:18 -0400478 field = super(CordInstanceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Baker0e289fd2015-06-12 10:40:15 -0700479
480 return field
481
Scott Baker022cdcd2015-02-18 15:50:11 -0800482class SiteInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400483 model = Site
484 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400485 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400486
Tony Mack5b061472014-02-04 07:57:10 -0500487 def queryset(self, request):
488 return Site.select_by_user(request.user)
489
Tony Mack15136b52015-08-04 17:53:23 -0400490class SiteHostsNodesInline(SiteInline):
491 def queryset(self, request):
492 return Site.select_by_user(request.user).filter(hosts_nodes=True)
493
494class SiteHostsUsersInline(SiteInline):
495 def queryset(self, request):
496 return Site.select_by_user(request.user).filter(hosts_users=True)
497
Scott Baker022cdcd2015-02-18 15:50:11 -0800498class UserInline(XOSTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400499 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700500 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
501 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400502 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400503 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400504
Tony Mack5b061472014-02-04 07:57:10 -0500505 def queryset(self, request):
506 return User.select_by_user(request.user)
507
Scott Baker022cdcd2015-02-18 15:50:11 -0800508class SliceInline(XOSTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400509 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700510 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
511 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400512 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400513 suit_classes = 'suit-tab suit-tab-slices'
514
Tony Mack5b061472014-02-04 07:57:10 -0500515 def queryset(self, request):
516 return Slice.select_by_user(request.user)
517
Scott Baker022cdcd2015-02-18 15:50:11 -0800518class NodeInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400519 model = Node
520 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400521 suit_classes = 'suit-tab suit-tab-nodes'
Tony Mack93d1b032014-12-08 16:43:02 -0500522 fields = ['backend_status_icon', 'name', 'site_deployment']
Scott Baker40c00762014-08-21 16:55:59 -0700523 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400524
Scott Baker022cdcd2015-02-18 15:50:11 -0800525class DeploymentPrivilegeInline(XOSTabularInline):
Tony Mack93d1b032014-12-08 16:43:02 -0500526 model = DeploymentPrivilege
527 extra = 0
Tony Mack4ce14c42015-02-09 21:41:57 -0500528 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Tony Mack93d1b032014-12-08 16:43:02 -0500529 fields = ['backend_status_icon', 'user','role','deployment']
530 readonly_fields = ('backend_status_icon', )
531
532 def queryset(self, request):
533 return DeploymentPrivilege.select_by_user(request.user)
534
Scott Baker022cdcd2015-02-18 15:50:11 -0800535class ControllerSiteInline(XOSTabularInline):
Tony Macka7dbd422015-01-05 22:48:11 -0500536 model = ControllerSite
537 extra = 0
538 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack8f30ebe2015-01-06 15:08:20 -0500539 fields = ['controller', 'site', 'tenant_id']
Tony Macka7dbd422015-01-05 22:48:11 -0500540
541
Scott Baker022cdcd2015-02-18 15:50:11 -0800542class SitePrivilegeInline(XOSTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400543 model = SitePrivilege
544 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400545 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700546 fields = ['backend_status_icon', 'user','site', 'role']
547 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400548
Tony Mackc2835a92013-05-28 09:18:49 -0400549 def formfield_for_foreignkey(self, db_field, request, **kwargs):
550 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500551 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400552
553 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500554 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400555 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
556
Tony Mack5b061472014-02-04 07:57:10 -0500557 def queryset(self, request):
558 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400559
Tony Mack8d108e22015-05-11 20:39:32 -0400560
561class ServicePrivilegeInline(XOSTabularInline):
562 model = ServicePrivilege
563 extra = 0
564 suit_classes = 'suit-tab suit-tab-serviceprivileges'
565 fields = ['backend_status_icon', 'user','service', 'role']
566 readonly_fields = ('backend_status_icon', )
567
568 def formfield_for_foreignkey(self, db_field, request, **kwargs):
569 if db_field.name == 'service':
570 kwargs['queryset'] = Service.select_by_user(request.user)
Tony Mackcf88f8c2015-05-15 06:33:45 -0400571 if db_field.name == 'user':
572 kwargs['queryset'] = User.select_by_user(request.user)
573 return super(ServicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mack8d108e22015-05-11 20:39:32 -0400574
575 def queryset(self, request):
576 return ServicePrivilege.select_by_user(request.user)
577
Scott Baker022cdcd2015-02-18 15:50:11 -0800578class SiteDeploymentInline(XOSTabularInline):
Tony Macka7dbd422015-01-05 22:48:11 -0500579 model = SiteDeployment
Tony Macke4be32f2014-03-11 20:45:25 -0400580 extra = 0
Tony Mack10812252015-01-30 10:58:29 -0500581 suit_classes = 'suit-tab suit-tab-sitedeployments'
Tony Mack528d4222014-12-05 17:13:08 -0500582 fields = ['backend_status_icon', 'deployment','site', 'controller']
Scott Baker40c00762014-08-21 16:55:59 -0700583 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400584
585 def formfield_for_foreignkey(self, db_field, request, **kwargs):
586 if db_field.name == 'site':
587 kwargs['queryset'] = Site.select_by_user(request.user)
588
589 if db_field.name == 'deployment':
590 kwargs['queryset'] = Deployment.select_by_user(request.user)
Tony Mack528d4222014-12-05 17:13:08 -0500591
592 if db_field.name == 'controller':
Tony Mack7b6400d2015-02-16 19:54:24 -0500593 kwargs['queryset'] = Controller.select_by_user(request.user).filter(deployment__id=int(resolve(request.path).args[0]))
Tony Mack528d4222014-12-05 17:13:08 -0500594
Tony Macka7dbd422015-01-05 22:48:11 -0500595 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Macke4be32f2014-03-11 20:45:25 -0400596
597 def queryset(self, request):
Tony Macka7dbd422015-01-05 22:48:11 -0500598 return SiteDeployment.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400599
600
Scott Baker022cdcd2015-02-18 15:50:11 -0800601class SlicePrivilegeInline(XOSTabularInline):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400602 model = SlicePrivilege
603 suit_classes = 'suit-tab suit-tab-sliceprivileges'
604 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700605 fields = ('backend_status_icon', 'user', 'slice', 'role')
606 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400607
Tony Mackc2835a92013-05-28 09:18:49 -0400608 def formfield_for_foreignkey(self, db_field, request, **kwargs):
609 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700610 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400611 if db_field.name == 'user':
Scott Baker521abcc2015-07-16 12:40:07 -0700612 # all users are available to be granted SlicePrivilege
613 kwargs['queryset'] = User.objects.all()
Tony Mackc2835a92013-05-28 09:18:49 -0400614
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400615 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400616
Tony Mack5b061472014-02-04 07:57:10 -0500617 def queryset(self, request):
618 return SlicePrivilege.select_by_user(request.user)
619
Scott Baker022cdcd2015-02-18 15:50:11 -0800620class SliceNetworkInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700621 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800622 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700623 extra = 0
624 verbose_name = "Network Connection"
625 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400626 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700627 fields = ['backend_status_icon', 'network']
628 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700629
Scott Baker022cdcd2015-02-18 15:50:11 -0800630class ImageDeploymentsInline(XOSTabularInline):
Sapan Bhatiae9f96f62014-11-19 15:10:16 -0500631 model = ImageDeployments
Scott Baker2170b972014-06-03 12:14:07 -0700632 extra = 0
633 verbose_name = "Image Deployments"
634 verbose_name_plural = "Image Deployments"
635 suit_classes = 'suit-tab suit-tab-imagedeployments'
Tony Mack336e0f92014-11-30 15:53:08 -0500636 fields = ['backend_status_icon', 'image', 'deployment']
637 readonly_fields = ['backend_status_icon']
638
Scott Baker022cdcd2015-02-18 15:50:11 -0800639class ControllerImagesInline(XOSTabularInline):
Tony Mack336e0f92014-11-30 15:53:08 -0500640 model = ControllerImages
641 extra = 0
642 verbose_name = "Controller Images"
643 verbose_name_plural = "Controller Images"
644 suit_classes = 'suit-tab suit-tab-admin-only'
645 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
Scott Baker40c00762014-08-21 16:55:59 -0700646 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700647
Scott Baker022cdcd2015-02-18 15:50:11 -0800648class SliceRoleAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400649 model = SliceRole
650 pass
651
Scott Baker022cdcd2015-02-18 15:50:11 -0800652class SiteRoleAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400653 model = SiteRole
654 pass
655
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400656class DeploymentAdminForm(forms.ModelForm):
Scott Bakerde0f4412014-06-11 15:40:26 -0700657 images = forms.ModelMultipleChoiceField(
658 queryset=Image.objects.all(),
659 required=False,
660 help_text="Select which images should be deployed on this deployment",
661 widget=FilteredSelectMultiple(
662 verbose_name=('Images'), is_stacked=False
663 )
664 )
Scott Baker37b47902014-09-02 14:37:41 -0700665 flavors = forms.ModelMultipleChoiceField(
666 queryset=Flavor.objects.all(),
667 required=False,
668 help_text="Select which flavors should be usable on this deployment",
669 widget=FilteredSelectMultiple(
670 verbose_name=('Flavors'), is_stacked=False
671 )
672 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400673 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400674 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700675 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400676
Siobhan Tully320b4622014-01-17 15:11:14 -0500677 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700678 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500679 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
680
Scott Baker5380c522014-06-06 14:49:43 -0700681 self.fields['accessControl'].initial = "allow site " + request.user.site.name
682
Siobhan Tully320b4622014-01-17 15:11:14 -0500683 if self.instance and self.instance.pk:
Scott Baker9f6b8ed2014-11-17 23:44:03 -0800684 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700685 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700686
687 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
688 """ helper function for handling m2m relations from the MultipleChoiceField
689
690 this_obj: the source object we want to link from
691
692 selected_objs: a list of destination objects we want to link to
693
694 all_relations: the full set of relations involving this_obj, including ones we don't want
695
696 relation_class: the class that implements the relation from source to dest
697
698 local_attrname: field name representing this_obj in relation_class
699
700 foreign_attrname: field name representing selected_objs in relation_class
701
702 This function will remove all newobjclass relations from this_obj
703 that are not contained in selected_objs, and add any relations that
704 are in selected_objs but don't exist in the data model yet.
705 """
706
707 existing_dest_objs = []
708 for relation in list(all_relations):
709 if getattr(relation, foreign_attrname) not in selected_objs:
710 #print "deleting site", sdp.site
711 relation.delete()
712 else:
713 existing_dest_objs.append(getattr(relation, foreign_attrname))
714
715 for dest_obj in selected_objs:
716 if dest_obj not in existing_dest_objs:
717 #print "adding site", site
718 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
719 relation = relation_class(**kwargs)
720 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500721
722 def save(self, commit=True):
723 deployment = super(DeploymentAdminForm, self).save(commit=False)
724
725 if commit:
726 deployment.save()
Scott Baker61b6aec2014-10-06 17:17:40 -0700727 # this has to be done after save() if/when a deployment is first created
728 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500729
730 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700731 # save_m2m() doesn't seem to work with 'through' relations. So we
732 # create/destroy the through models ourselves. There has to be
733 # a better way...
734
Tony Mack592aa952014-12-15 11:45:02 -0500735 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ImageDeployments, "deployment", "image")
736 # manipulate_m2m_objs doesn't work for Flavor/Deployment relationship
737 # so well handle that manually here
738 for flavor in deployment.flavors.all():
739 if getattr(flavor, 'name') not in self.cleaned_data['flavors']:
Tony Mack11f4d202014-12-15 12:37:59 -0500740 deployment.flavors.remove(flavor)
Tony Mack592aa952014-12-15 11:45:02 -0500741 for flavor in self.cleaned_data['flavors']:
742 if flavor not in deployment.flavors.all():
743 flavor.deployments.add(deployment)
Scott Bakerc9b14f72014-05-22 13:44:20 -0700744
Scott Baker37b47902014-09-02 14:37:41 -0700745 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500746
747 return deployment
748
Scott Bakerff5e0f32014-05-22 14:40:27 -0700749class DeploymentAdminROForm(DeploymentAdminForm):
750 def save(self, commit=True):
751 raise PermissionDenied
752
Scott Baker022cdcd2015-02-18 15:50:11 -0800753class SiteAssocInline(XOSTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500754 model = Site.deployments.through
755 extra = 0
756 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400757
Scott Baker022cdcd2015-02-18 15:50:11 -0800758class DeploymentAdmin(XOSBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500759 model = Deployment
Scott Bakerae233f42015-02-10 08:40:34 -0800760 fieldList = ['backend_status_text', 'name', 'images', 'flavors', 'accessControl']
761 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack93d1b032014-12-08 16:43:02 -0500762 # node no longer directly connected to deployment
763 #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
Tony Mack10812252015-01-30 10:58:29 -0500764 inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline,SiteDeploymentInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700765 list_display = ['backend_status_icon', 'name']
766 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700767 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500768
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500769 user_readonly_fields = ['name']
770
Tony Mack93d1b032014-12-08 16:43:02 -0500771 # nodes no longer direclty connected to deployments
Scott Bakerae233f42015-02-10 08:40:34 -0800772 suit_form_tabs =(('general','Deployment Details'),('deploymentprivileges','Privileges'), ('sitedeployments', 'Sites'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500773
Scott Bakerff5e0f32014-05-22 14:40:27 -0700774 def get_form(self, request, obj=None, **kwargs):
Tony Mackd893dfb2015-02-05 06:13:04 -0500775 if request.user.isReadOnlyUser() or not request.user.is_admin:
Scott Bakerff5e0f32014-05-22 14:40:27 -0700776 kwargs["form"] = DeploymentAdminROForm
777 else:
778 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700779 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
780
781 # from stackexchange: pass the request object into the form
782
783 class AdminFormMetaClass(adminForm):
784 def __new__(cls, *args, **kwargs):
785 kwargs['request'] = request
786 return adminForm(*args, **kwargs)
787
788 return AdminFormMetaClass
789
Scott Baker022cdcd2015-02-18 15:50:11 -0800790class ControllerAdmin(XOSBaseAdmin):
Scott Bakerae233f42015-02-10 08:40:34 -0800791 model = Controller
Scott Baker00eae8c2015-02-16 11:55:09 -0800792 fieldList = ['deployment', 'name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password', 'domain']
Scott Bakerae233f42015-02-10 08:40:34 -0800793 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Macka7dbd422015-01-05 22:48:11 -0500794 inlines = [ControllerSiteInline] # ,ControllerImagesInline]
Tony Mack528d4222014-12-05 17:13:08 -0500795 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
796 list_display_links = ('backend_status_icon', 'name', )
797 readonly_fields = ('backend_status_text',)
798
799 user_readonly_fields = []
800
Tony Mackc36cafb2015-01-13 17:33:08 -0500801 def save_model(self, request, obj, form, change):
802 # update openstack connection to use this site/tenant
803 obj.save_by_user(request.user)
804
805 def delete_model(self, request, obj):
Scott Bakerae233f42015-02-10 08:40:34 -0800806 obj.delete_by_user(request.user)
807
Tony Mack79e2e662015-02-18 11:41:36 -0500808 def queryset(self, request):
809 return Controller.select_by_user(request.user)
810
Scott Bakerae233f42015-02-10 08:40:34 -0800811 @property
812 def suit_form_tabs(self):
813 tabs = [('general', 'Controller Details'),
814 ]
815
816 request=getattr(_thread_locals, "request", None)
817 if request and request.user.is_admin:
818 tabs.append( ('admin-only', 'Admin-Only') )
819
820 return tabs
Tony Mackc36cafb2015-01-13 17:33:08 -0500821
Scott Baker1b06b6c2015-07-06 14:40:20 -0700822class TenantRootRoleAdmin(XOSBaseAdmin):
823 model = TenantRootRole
824 fields = ('role',)
825
826class TenantRootTenantInline(XOSTabularInline):
827 model = Tenant
828 fields = ['provider_service', 'subscriber_root']
829 extra = 0
830 suit_classes = 'suit-tab suit-tab-tenantroots'
831 fk_name = 'subscriber_root'
832 verbose_name = 'subscribed tenant'
833 verbose_name_plural = 'subscribed tenants'
834
835 #def queryset(self, request):
836 # qs = super(TenantRootTenantInline, self).queryset(request)
837 # return qs.filter(kind="coarse")
838
839class TenantRootPrivilegeInline(XOSTabularInline):
840 model = TenantRootPrivilege
841 extra = 0
842 suit_classes = 'suit-tab suit-tab-tenantrootprivileges'
843 fields = ['backend_status_icon', 'user', 'role', 'tenant_root']
844 readonly_fields = ('backend_status_icon', )
845
846 def queryset(self, request):
847 return TenantRootPrivilege.select_by_user(request.user)
848
849class TenantRootAdmin(XOSBaseAdmin):
850 model = TenantRoot
851 list_display = ('backend_status_icon', 'name', 'kind')
852 list_display_links = ('backend_status_icon', 'name')
853 fieldList = ('backend_status_text', 'name', 'kind', )
854 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
855 inlines = (TenantRootTenantInline, TenantRootPrivilegeInline)
856 readonly_fields = ('backend_status_text', )
857
858 suit_form_tabs =(('general', 'Tenant Root Details'),
859 ('tenantroots','Tenancy'),
860 ('tenantrootprivileges','Privileges')
861 )
862
Scott Baker925a8fa2015-04-26 20:30:40 -0700863class ProviderTenantInline(XOSTabularInline):
864 model = CoarseTenant
865 fields = ['provider_service', 'subscriber_service', 'connect_method']
866 extra = 0
867 suit_classes = 'suit-tab suit-tab-servicetenants'
868 fk_name = 'provider_service'
869 verbose_name = 'provided tenant'
870 verbose_name_plural = 'provided tenants'
871
872 def queryset(self, request):
873 qs = super(ProviderTenantInline, self).queryset(request)
874 return qs.filter(kind="coarse")
875
876class SubscriberTenantInline(XOSTabularInline):
877 model = CoarseTenant
878 fields = ['provider_service', 'subscriber_service', 'connect_method']
879 extra = 0
880 suit_classes = 'suit-tab suit-tab-servicetenants'
881 fk_name = 'subscriber_service'
882 verbose_name = 'subscribed tenant'
883 verbose_name_plural = 'subscribed tenants'
884
885 def queryset(self, request):
886 qs = super(SubscriberTenantInline, self).queryset(request)
887 return qs.filter(kind="coarse")
888
Scott Baker022cdcd2015-02-18 15:50:11 -0800889class ServiceAttrAsTabInline(XOSTabularInline):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400890 model = ServiceAttribute
891 fields = ['name','value']
892 extra = 0
893 suit_classes = 'suit-tab suit-tab-serviceattrs'
894
Scott Baker022cdcd2015-02-18 15:50:11 -0800895class ServiceAdmin(XOSBaseAdmin):
Scott Baker008a9962015-04-15 20:58:20 -0700896 list_display = ("backend_status_icon","name","kind","versionNumber","enabled","published")
Scott Baker63d1a552014-08-21 15:19:07 -0700897 list_display_links = ('backend_status_icon', 'name', )
Scott Baker2f0828e2015-07-13 12:33:28 -0700898 fieldList = ["backend_status_text","name","kind","description","versionNumber","enabled","published","view_url","icon_url","public_key","service_specific_attribute","service_specific_id"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500899 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack8d108e22015-05-11 20:39:32 -0400900 inlines = [ServiceAttrAsTabInline,SliceInline,ProviderTenantInline,SubscriberTenantInline,ServicePrivilegeInline]
Scott Baker40c00762014-08-21 16:55:59 -0700901 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500902
903 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500904
905 suit_form_tabs =(('general', 'Service Details'),
906 ('slices','Slices'),
907 ('serviceattrs','Additional Attributes'),
Scott Baker925a8fa2015-04-26 20:30:40 -0700908 ('servicetenants','Tenancy'),
Scott Baker1b06b6c2015-07-06 14:40:20 -0700909 ('serviceprivileges','Privileges')
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500910 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400911
Scott Baker022cdcd2015-02-18 15:50:11 -0800912class SiteNodeInline(XOSTabularInline):
Tony Mack82df1d02015-01-14 20:58:38 -0500913 model = Node
914 fields = ['name', 'site_deployment']
915 extra = 0
916 suit_classes = 'suit-tab suit-tab-nodes'
917
Tony Mack26b59532015-02-25 11:39:34 -0500918 def formfield_for_foreignkey(self, db_field, request, **kwargs):
919 # only display site deployments associated with this site
920 if db_field.name == 'site_deployment':
921 kwargs['queryset'] = SiteDeployment.objects.filter(site__id=int(request.path.split('/')[-2]))
922
923 return super(SiteNodeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
924
Scott Baker022cdcd2015-02-18 15:50:11 -0800925class SiteAdmin(XOSBaseAdmin):
Tony Mack598eaf22015-01-25 12:35:29 -0500926 #fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Tony Mack2862dca2015-08-04 17:21:55 -0400927 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'login_base', 'location', 'is_public', 'hosts_nodes', 'hosts_users']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400928 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500929 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400930 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400931 ]
Tony Mack598eaf22015-01-25 12:35:29 -0500932 #readonly_fields = ['backend_status_text', 'accountLink']
933 readonly_fields = ['backend_status_text']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500934
Tony Mack598eaf22015-01-25 12:35:29 -0500935 #user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Tony Mack2862dca2015-08-04 17:21:55 -0400936 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'hosts_nodes', 'hosts_users']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500937
Scott Baker63d1a552014-08-21 15:19:07 -0700938 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
939 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400940 filter_horizontal = ('deployments',)
Tony Mack10812252015-01-30 10:58:29 -0500941 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteNodeInline]
Tony Mack10328a12015-01-14 12:11:05 -0500942 admin_inlines = [ControllerSiteInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400943 search_fields = ['name']
944
Tony Mack3c01ff92015-01-10 23:08:10 -0500945 @property
946 def suit_form_tabs(self):
947 tabs = [('general', 'Site Details'),
948 ('users','Users'),
949 ('siteprivileges','Privileges'),
Tony Mack3c01ff92015-01-10 23:08:10 -0500950 ('slices','Slices'),
Tony Mack82df1d02015-01-14 20:58:38 -0500951 ('nodes','Nodes'),
Tony Mack3c01ff92015-01-10 23:08:10 -0500952 ]
953
954 request=getattr(_thread_locals, "request", None)
955 if request and request.user.is_admin:
956 tabs.append( ('admin-only', 'Admin-Only') )
957
958 return tabs
959
Tony Mack04062832013-05-10 08:22:44 -0400960 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500961 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400962
Tony Mack5cd13202013-05-01 21:48:38 -0400963 def get_formsets(self, request, obj=None):
964 for inline in self.get_inline_instances(request, obj):
965 # hide MyInline in the add view
966 if obj is None:
967 continue
Tony Mack3de59e32015-08-19 11:58:18 -0400968 if isinstance(inline, InstanceInline):
Tony Mack2bd5b412013-06-11 21:05:06 -0400969 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400970 yield inline.get_formset(request, obj)
971
Scott Baker545db2a2013-12-09 18:44:43 -0800972 def accountLink(self, obj):
973 link_obj = obj.accounts.all()
974 if link_obj:
975 reverse_path = "admin:core_account_change"
976 url = reverse(reverse_path, args =(link_obj[0].id,))
977 return "<a href='%s'>%s</a>" % (url, "view billing details")
978 else:
979 return "no billing data for this site"
980 accountLink.allow_tags = True
981 accountLink.short_description = "Billing"
982
Tony Mack332ee1d2014-02-04 15:33:45 -0500983 def save_model(self, request, obj, form, change):
984 # update openstack connection to use this site/tenant
985 obj.save_by_user(request.user)
986
987 def delete_model(self, request, obj):
988 obj.delete_by_user(request.user)
989
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500990
Scott Baker022cdcd2015-02-18 15:50:11 -0800991class SitePrivilegeAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700992 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400993 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500994 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400995 ]
Scott Baker40c00762014-08-21 16:55:59 -0700996 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700997 list_display = ('backend_status_icon', 'user', 'site', 'role')
998 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500999 user_readonly_fields = fieldList
1000 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -04001001
Tony Mackc2835a92013-05-28 09:18:49 -04001002 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1003 if db_field.name == 'site':
1004 if not request.user.is_admin:
1005 # only show sites where user is an admin or pi
1006 sites = set()
1007 for site_privilege in SitePrivilege.objects.filer(user=request.user):
1008 if site_privilege.role.role_type in ['admin', 'pi']:
1009 sites.add(site_privilege.site)
1010 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
1011
1012 if db_field.name == 'user':
1013 if not request.user.is_admin:
1014 # only show users from sites where caller has admin or pi role
1015 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
1016 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
1017 sites = [site_privilege.site for site_privilege in site_privileges]
1018 site_privileges = SitePrivilege.objects.filter(site__in=sites)
1019 emails = [site_privilege.user.email for site_privilege in site_privileges]
1020 users = User.objects.filter(email__in=emails)
1021 kwargs['queryset'] = users
1022
1023 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1024
Tony Mack04062832013-05-10 08:22:44 -04001025 def queryset(self, request):
1026 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -04001027 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -04001028 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -05001029 #if not request.user.is_admin:
1030 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
1031 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
1032 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
1033 # sites = Site.objects.filter(login_base__in=login_bases)
1034 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -04001035 return qs
1036
Siobhan Tullyce652d02013-10-08 21:52:35 -04001037class SliceForm(forms.ModelForm):
1038 class Meta:
1039 model = Slice
1040 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -07001041 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -04001042 }
1043
Tony Mack2cbd3802014-09-29 16:10:52 -04001044 def clean(self):
1045 cleaned_data = super(SliceForm, self).clean()
1046 name = cleaned_data.get('name')
Scott Baker6efad462014-10-06 23:09:59 -07001047 site = cleaned_data.get('site')
Tony Mack585cb192014-10-22 12:54:19 -04001048 slice_id = self.instance.id
1049 if not site and slice_id:
1050 site = Slice.objects.get(id=slice_id).site
Scott Baker6efad462014-10-06 23:09:59 -07001051 if (not isinstance(site,Site)):
1052 # previous code indicates 'site' could be a site_id and not a site?
1053 site = Slice.objects.get(id=site.id)
Tony Mack2cbd3802014-09-29 16:10:52 -04001054 if not name.startswith(site.login_base):
1055 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
1056 return cleaned_data
1057
Scott Baker022cdcd2015-02-18 15:50:11 -08001058class ControllerSliceInline(XOSTabularInline):
Tony Macka7dbd422015-01-05 22:48:11 -05001059 model = ControllerSlice
Scott Bakerc4efdc72014-10-15 16:54:04 -07001060 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001061 verbose_name = "Controller Slices"
1062 verbose_name_plural = "Controller Slices"
Scott Bakerc4efdc72014-10-15 16:54:04 -07001063 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001064 fields = ['backend_status_icon', 'controller', 'tenant_id']
Tony Mack3c01ff92015-01-10 23:08:10 -05001065 readonly_fields = ('backend_status_icon', 'controller' )
Scott Bakerc4efdc72014-10-15 16:54:04 -07001066
Scott Baker022cdcd2015-02-18 15:50:11 -08001067class SliceAdmin(XOSBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -04001068 form = SliceForm
Tony Mack3de59e32015-08-19 11:58:18 -04001069 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_instances']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001070 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -07001071 readonly_fields = ('backend_status_text', )
Tony Mack3de59e32015-08-19 11:58:18 -04001072 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_instances')
Tony Mack7d459902014-09-03 13:18:57 -04001073 list_display_links = ('backend_status_icon', 'name', )
Tony Mack3de59e32015-08-19 11:58:18 -04001074 normal_inlines = [SlicePrivilegeInline, InstanceInline, TagInline, ReservationInline, SliceNetworkInline]
Scott Baker0e289fd2015-06-12 10:40:15 -07001075 inlines = normal_inlines
Tony Macka7dbd422015-01-05 22:48:11 -05001076 admin_inlines = [ControllerSliceInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001077
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001078 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001079
Scott Bakerc4efdc72014-10-15 16:54:04 -07001080 @property
1081 def suit_form_tabs(self):
1082 tabs =[('general', 'Slice Details'),
1083 ('slicenetworks','Networks'),
1084 ('sliceprivileges','Privileges'),
Tony Mack3de59e32015-08-19 11:58:18 -04001085 ('instances','Instances'),
Tony Mack598eaf22015-01-25 12:35:29 -05001086 #('reservations','Reservations'),
Tony Mackb34553e2015-01-15 14:44:06 -05001087 ('tags','Tags'),
Scott Bakerc4efdc72014-10-15 16:54:04 -07001088 ]
1089
1090 request=getattr(_thread_locals, "request", None)
1091 if request and request.user.is_admin:
1092 tabs.append( ('admin-only', 'Admin-Only') )
1093
1094 return tabs
Tony Mack7b8505a2014-10-22 11:54:29 -04001095
1096 def add_view(self, request, form_url='', extra_context=None):
Scott Baker0e289fd2015-06-12 10:40:15 -07001097 # Ugly hack for CORD
1098 self.inlines = self.normal_inlines
Tony Mack7b8505a2014-10-22 11:54:29 -04001099 # revert to default read-only fields
1100 self.readonly_fields = ('backend_status_text',)
1101 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
1102
1103 def change_view(self, request, object_id, form_url='', extra_context=None):
Tony Mack7b8505a2014-10-22 11:54:29 -04001104 # cannot change the site of an existing slice so make the site field read only
1105 if object_id:
1106 self.readonly_fields = ('backend_status_text','site')
Scott Baker0e289fd2015-06-12 10:40:15 -07001107
Tony Mack7b8505a2014-10-22 11:54:29 -04001108 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001109
Scott Baker510fdbb2014-08-05 17:19:24 -07001110 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -07001111 deployment_nodes = []
1112 for node in Node.objects.all():
Scott Baker39293d72015-01-21 16:24:07 -08001113 deployment_nodes.append( (node.site_deployment.deployment.id, node.id, node.name) )
Scott Baker510fdbb2014-08-05 17:19:24 -07001114
Scott Baker7a61dc42014-09-02 17:08:20 -07001115 deployment_flavors = []
1116 for flavor in Flavor.objects.all():
1117 for deployment in flavor.deployments.all():
1118 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
1119
Tony Mack93d1b032014-12-08 16:43:02 -05001120 deployment_images = []
Scott Baker93e80cd2014-09-09 09:58:49 -07001121 for image in Image.objects.all():
Tony Mack93d1b032014-12-08 16:43:02 -05001122 for deployment_image in image.imagedeployments.all():
Scott Bakera6a0c772014-12-22 17:35:34 -08001123 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
Scott Baker93e80cd2014-09-09 09:58:49 -07001124
Tony Mackec23b992014-09-02 21:18:45 -04001125 site_login_bases = []
1126 for site in Site.objects.all():
Scott Baker93e80cd2014-09-09 09:58:49 -07001127 site_login_bases.append((site.id, site.login_base))
1128
Scott Baker510fdbb2014-08-05 17:19:24 -07001129 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -07001130 context["deployment_flavors"] = deployment_flavors
Scott Baker93e80cd2014-09-09 09:58:49 -07001131 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -04001132 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -07001133 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
1134
Tony Mackc2835a92013-05-28 09:18:49 -04001135 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1136 if db_field.name == 'site':
Tony Mack15136b52015-08-04 17:53:23 -04001137 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_users=True)
Tony Mackec23b992014-09-02 21:18:45 -04001138 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 -07001139
Tony Mackc2835a92013-05-28 09:18:49 -04001140 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1141
Tony Mack04062832013-05-10 08:22:44 -04001142 def queryset(self, request):
1143 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001144 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -04001145
Tony Mack79748612013-05-01 14:52:03 -04001146 def get_formsets(self, request, obj=None):
1147 for inline in self.get_inline_instances(request, obj):
1148 # hide MyInline in the add view
1149 if obj is None:
1150 continue
Tony Mack3de59e32015-08-19 11:58:18 -04001151 if isinstance(inline, InstanceInline):
Tony Mack2bd5b412013-06-11 21:05:06 -04001152 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -04001153 yield inline.get_formset(request, obj)
1154
Scott Baker0e289fd2015-06-12 10:40:15 -07001155 def UNUSED_get_inline_instances(self, request, obj=None):
1156 # HACK for CORD to do something special on vcpe slice page
1157 # this was a good idea, but failed miserably, as something still
1158 # expects there to be a deployment field.
1159 # XXX this approach is better than clobbering self.inlines, so
1160 # try to make this work post-demo.
1161 if (obj is not None) and (obj.name == "mysite_vcpe"):
Tony Mack3de59e32015-08-19 11:58:18 -04001162 cord_vcpe_inlines = [ SlicePrivilegeInline, CordInstanceInline, TagInline, ReservationInline,SliceNetworkInline]
Scott Baker0e289fd2015-06-12 10:40:15 -07001163
1164 inlines=[]
1165 for inline_class in cord_vcpe_inlines:
1166 inlines.append(inline_class(self.model, self.admin_site))
1167 else:
1168 inlines = super(SliceAdmin, self).get_inline_instances(request, obj)
1169
1170 return inlines
1171
Scott Baker022cdcd2015-02-18 15:50:11 -08001172class SlicePrivilegeAdmin(XOSBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -04001173 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -07001174 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -04001175 ]
Scott Baker40c00762014-08-21 16:55:59 -07001176 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -07001177 list_display = ('backend_status_icon', 'user', 'slice', 'role')
1178 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -04001179
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001180 user_readonly_fields = ['user', 'slice', 'role']
1181 user_readonly_inlines = []
1182
Tony Mackc2835a92013-05-28 09:18:49 -04001183 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1184 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001185 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001186
1187 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -05001188 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001189
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001190 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001191
Tony Mack04062832013-05-10 08:22:44 -04001192 def queryset(self, request):
1193 # admins can see all memberships. Users can only see memberships of
1194 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -05001195 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001196
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001197 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -04001198 # update openstack connection to use this site/tenant
1199 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001200 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001201 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001202 obj.save()
1203
1204 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -04001205 # update openstack connection to use this site/tenant
1206 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001207 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001208 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001209 obj.delete()
1210
Scott Baker022cdcd2015-02-18 15:50:11 -08001211class ImageAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001212
Scott Baker36f50872014-08-21 13:01:25 -07001213 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -07001214 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001215 'classes': ['suit-tab suit-tab-general']})
1216 ]
Scott Baker40c00762014-08-21 16:55:59 -07001217 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001218
Tony Mack3de59e32015-08-19 11:58:18 -04001219 suit_form_tabs =(('general','Image Details'),('instances','Instances'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001220
Tony Mack3de59e32015-08-19 11:58:18 -04001221 inlines = [InstanceInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -07001222
Tony Mack32e1ce32014-05-07 13:29:41 -04001223 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -07001224
Scott Baker63d1a552014-08-21 15:19:07 -07001225 list_display = ['backend_status_icon', 'name']
1226 list_display_links = ('backend_status_icon', 'name', )
1227
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001228class NodeForm(forms.ModelForm):
1229 class Meta:
1230 widgets = {
1231 'site': LinkedSelect,
1232 'deployment': LinkedSelect
1233 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001234
Scott Baker022cdcd2015-02-18 15:50:11 -08001235class NodeAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001236 form = NodeForm
Tony Mack93d1b032014-12-08 16:43:02 -05001237 list_display = ('backend_status_icon', 'name', 'site_deployment')
Scott Baker63d1a552014-08-21 15:19:07 -07001238 list_display_links = ('backend_status_icon', 'name', )
Tony Mack93d1b032014-12-08 16:43:02 -05001239 list_filter = ('site_deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001240
Tony Mack3de59e32015-08-19 11:58:18 -04001241 inlines = [TagInline,InstanceInline]
Tony Mack93d1b032014-12-08 16:43:02 -05001242 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site_deployment'], 'classes':['suit-tab suit-tab-details']})]
Scott Baker40c00762014-08-21 16:55:59 -07001243 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001244
Tony Mack93d1b032014-12-08 16:43:02 -05001245 user_readonly_fields = ['name','site_deployment']
Tony Mack3de59e32015-08-19 11:58:18 -04001246 user_readonly_inlines = [TagInline,InstanceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001247
Tony Mack3de59e32015-08-19 11:58:18 -04001248 suit_form_tabs =(('details','Node Details'),('instances','Instances'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001249
Tony Mack17b97b82015-08-04 17:32:32 -04001250 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1251 if db_field.name == 'site':
1252 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_nodes=True)
Siobhan Tully567e3e62013-06-21 18:03:16 -04001253
Tony Mack3de59e32015-08-19 11:58:18 -04001254class InstanceForm(forms.ModelForm):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001255 class Meta:
Tony Mack3de59e32015-08-19 11:58:18 -04001256 model = Instance
Tony Mackd90cdbf2013-04-16 22:48:40 -04001257 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001258 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001259 widgets = {
1260 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001261 'instance_name': PlainTextWidget(),
Scott Baker887d4a82015-01-19 11:32:20 -08001262 'instance_id': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001263 'slice': LinkedSelect,
Tony Mackbf6aa302014-12-26 13:38:02 -05001264 'deployment': LinkedSelect,
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001265 'node': LinkedSelect,
1266 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001267 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001268
Scott Baker022cdcd2015-02-18 15:50:11 -08001269class TagAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001270 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1271 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001272 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1273 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001274
Tony Mack3de59e32015-08-19 11:58:18 -04001275class InstanceAdmin(XOSBaseAdmin):
1276 form = InstanceForm
Tony Mackcdec0902013-04-15 00:38:49 -04001277 fieldsets = [
Tony Mack3de59e32015-08-19 11:58:18 -04001278 ('Instance Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'node', 'all_ips_string', 'instance_id', 'instance_name', 'flavor', 'image', 'ssh_command'], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -04001279 ]
Tony Mack7d61efb2015-01-30 17:20:46 -05001280 readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Tony Mackd8336582015-01-30 12:52:46 -05001281 list_display = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deployment']
Scott Bakerf0b403f2015-02-13 14:38:21 -08001282 list_display_links = ('backend_status_icon', 'all_ips_string', 'instance_id', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001283
Tony Mack3de59e32015-08-19 11:58:18 -04001284 suit_form_tabs =(('general', 'Instance Details'),)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001285
Siobhan Tullyde5450d2013-06-21 11:35:33 -04001286 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -04001287
Tony Mackbf6aa302014-12-26 13:38:02 -05001288 user_readonly_fields = ['slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001289
Scott Baker970314b2015-01-25 22:16:13 -08001290 def ssh_command(self, obj):
1291 ssh_command = obj.get_ssh_command()
1292 if ssh_command:
1293 return ssh_command
1294 else:
1295 return "(not available)"
1296
Tony Mackc2835a92013-05-28 09:18:49 -04001297 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1298 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001299 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001300
Tony Mack3de59e32015-08-19 11:58:18 -04001301 return super(InstanceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001302
Tony Mack04062832013-05-10 08:22:44 -04001303 def queryset(self, request):
Tony Mack3de59e32015-08-19 11:58:18 -04001304 # admins can see all instances. Users can only see instances of
Tony Mack04062832013-05-10 08:22:44 -04001305 # the slices they belong to.
Tony Mack3de59e32015-08-19 11:58:18 -04001306 return Instance.select_by_user(request.user)
Tony Mack5b061472014-02-04 07:57:10 -05001307
Tony Mack04062832013-05-10 08:22:44 -04001308
Tony Mack1d6b85f2013-05-07 18:49:14 -04001309 def get_formsets(self, request, obj=None):
1310 # make some fields read only if we are updating an existing record
1311 if obj == None:
Tony Mack26dbd8d2015-01-30 17:42:10 -05001312 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001313 else:
Tony Mack26dbd8d2015-01-30 17:42:10 -05001314 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string', 'slice', 'flavor', 'image', 'node')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001315
1316 for inline in self.get_inline_instances(request, obj):
1317 # hide MyInline in the add view
1318 if obj is None:
1319 continue
Tony Mack3de59e32015-08-19 11:58:18 -04001320 if isinstance(inline, InstanceInline):
Scott Baker526b71e2014-05-13 13:18:01 -07001321 inline.model.caller = request.user
1322 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -04001323
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001324 #def save_model(self, request, obj, form, change):
1325 # # update openstack connection to use this site/tenant
1326 # auth = request.session.get('auth', {})
1327 # auth['tenant'] = obj.slice.name
1328 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1329 # obj.creator = request.user
1330 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001331
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001332 #def delete_model(self, request, obj):
1333 # # update openstack connection to use this site/tenant
1334 # auth = request.session.get('auth', {})
1335 # auth['tenant'] = obj.slice.name
1336 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1337 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001338
Siobhan Tully53437282013-04-26 19:30:27 -04001339class UserCreationForm(forms.ModelForm):
1340 """A form for creating new users. Includes all the required
1341 fields, plus a repeated password."""
1342 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1343 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1344
1345 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001346 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001347 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001348
1349 def clean_password2(self):
1350 # Check that the two password entries match
1351 password1 = self.cleaned_data.get("password1")
1352 password2 = self.cleaned_data.get("password2")
1353 if password1 and password2 and password1 != password2:
1354 raise forms.ValidationError("Passwords don't match")
1355 return password2
1356
1357 def save(self, commit=True):
1358 # Save the provided password in hashed format
1359 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001360 user.password = self.cleaned_data["password1"]
1361 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001362 if commit:
1363 user.save()
1364 return user
1365
Siobhan Tully567e3e62013-06-21 18:03:16 -04001366
Siobhan Tully53437282013-04-26 19:30:27 -04001367class UserChangeForm(forms.ModelForm):
1368 """A form for updating users. Includes all the fields on
1369 the user, but replaces the password field with admin's
1370 password hash display field.
1371 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001372 password = ReadOnlyPasswordHashField(label='Password',
1373 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001374
Scott Baker811d4472015-05-19 16:39:48 -07001375 PROFILE_CHOICES = ((None, '------'), ('regular', 'Regular user'), ('cp', 'Content Provider'))
1376 profile = forms.ChoiceField(choices=PROFILE_CHOICES, required=False, label="Quick Profile")
1377
Siobhan Tully53437282013-04-26 19:30:27 -04001378 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001379 model = User
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001380 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001381
1382 def clean_password(self):
1383 # Regardless of what the user provides, return the initial value.
1384 # This is done here, rather than on the field, because the
1385 # field does not have access to the initial value
1386 return self.initial["password"]
1387
Scott Baker811d4472015-05-19 16:39:48 -07001388 def save(self, *args, **kwargs):
1389 if self.cleaned_data['profile']:
1390 self.instance.apply_profile(self.cleaned_data['profile'])
1391
1392 return super(UserChangeForm, self).save(*args, **kwargs)
1393
Scott Baker022cdcd2015-02-18 15:50:11 -08001394class UserDashboardViewInline(XOSTabularInline):
Scott Baker2c3cb642014-05-19 17:55:56 -07001395 model = UserDashboardView
1396 extra = 0
1397 suit_classes = 'suit-tab suit-tab-dashboards'
1398 fields = ['user', 'dashboardView', 'order']
1399
Scott Baker022cdcd2015-02-18 15:50:11 -08001400class ControllerUserInline(XOSTabularInline):
Tony Mack3c01ff92015-01-10 23:08:10 -05001401 model = ControllerUser
1402 extra = 0
1403 suit_classes = 'suit-tab suit-tab-admin-only'
1404 fields = ['controller', 'user', 'kuser_id']
1405 readonly_fields=['controller']
1406
1407
Scott Bakera9412c32015-02-27 12:21:22 -08001408class UserAdmin(XOSAdminMixin, UserAdmin):
1409 # Note: Make sure XOSAdminMixin is listed before
Scott Baker86c83ab2014-10-03 13:10:47 -07001410 # admin.ModelAdmin in the class declaration.
1411
Siobhan Tully53437282013-04-26 19:30:27 -04001412 class Meta:
1413 app_label = "core"
1414
1415 # The forms to add and change user instances
1416 form = UserChangeForm
1417 add_form = UserCreationForm
1418
1419 # The fields to be used in displaying the User model.
1420 # These override the definitions on the base UserAdmin
1421 # that reference specific fields on auth.User.
Scott Bakerf587f442015-01-24 13:33:26 -08001422 list_display = ('backend_status_icon', 'email', 'firstname', 'lastname', 'site', 'last_login')
1423 list_display_links = ("email",)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001424 list_filter = ('site',)
Scott Baker6e9027f2015-01-29 10:55:53 -08001425 inlines = [SlicePrivilegeInline,SitePrivilegeInline]
Tony Mack3c01ff92015-01-10 23:08:10 -05001426 admin_inlines = [ControllerUserInline]
Scott Bakere61e3a02015-06-10 16:14:58 -07001427 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 -05001428 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1429
Siobhan Tully53437282013-04-26 19:30:27 -04001430 fieldsets = (
Scott Baker74ebc7a2015-05-15 09:19:36 -07001431 ('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 -04001432 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001433 #('Important dates', {'fields': ('last_login',)}),
1434 )
1435 add_fieldsets = (
1436 (None, {
1437 'classes': ('wide',),
Scott Baker74ebc7a2015-05-15 09:19:36 -07001438 'fields': ('site', 'email', 'firstname', 'lastname', 'is_admin', 'is_readonly', 'is_appuser', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001439 ),
1440 )
Scott Baker40c00762014-08-21 16:55:59 -07001441 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001442 search_fields = ('email',)
1443 ordering = ('email',)
1444 filter_horizontal = ()
1445
Scott Baker3ca51f62014-05-23 12:05:11 -07001446 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001447
Scott Baker0a5633b2014-10-06 17:51:20 -07001448 @property
1449 def suit_form_tabs(self):
1450 if getattr(_thread_locals, "obj", None) is None:
1451 return []
1452 else:
Tony Mack3c01ff92015-01-10 23:08:10 -05001453 tabs = [('general','Login Details'),
Scott Baker0a5633b2014-10-06 17:51:20 -07001454 ('contact','Contact Information'),
1455 ('sliceprivileges','Slice Privileges'),
Scott Baker6e9027f2015-01-29 10:55:53 -08001456 ('siteprivileges','Site Privileges')]
Tony Mack3c01ff92015-01-10 23:08:10 -05001457
1458 request=getattr(_thread_locals, "request", None)
1459 if request and request.user.is_admin:
1460 tabs.append( ('admin-only', 'Admin-Only') )
1461
1462 return tabs
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001463
Tony Mackc2835a92013-05-28 09:18:49 -04001464 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1465 if db_field.name == 'site':
Tony Mack17b97b82015-08-04 17:32:32 -04001466 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_users=True)
Tony Mackc2835a92013-05-28 09:18:49 -04001467
1468 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1469
Tony Mack5b061472014-02-04 07:57:10 -05001470 def queryset(self, request):
1471 return User.select_by_user(request.user)
1472
Tony Mack6235fc82015-01-25 21:58:30 -05001473 def get_form(self, request, obj=None, **kwargs):
Tony Mackb2c407f2015-01-28 12:37:12 -05001474 # copy login details list
1475 login_details_fields = list(self.fieldListLoginDetails)
Tony Mack92b12052015-01-28 12:49:58 -05001476 if not request.user.is_admin:
Scott Baker6e9027f2015-01-29 10:55:53 -08001477 # only admins can see 'is_admin' and 'is_readonly' fields
Tony Mackb2c407f2015-01-28 12:37:12 -05001478 if 'is_admin' in login_details_fields:
1479 login_details_fields.remove('is_admin')
1480 if 'is_readonly' in login_details_fields:
Scott Baker811d4472015-05-19 16:39:48 -07001481 login_details_fields.remove('is_readonly')
1482 if 'is_appuser' in login_details_fields:
1483 login_details_fields.remove('is_admin')
1484 if 'profile' in login_details_fields:
1485 login_details_fields.remove('profile')
Tony Mack92b12052015-01-28 12:49:58 -05001486 #if len(request.user.siteprivileges.filter(role__role = 'pi')) > 0:
Scott Baker811d4472015-05-19 16:39:48 -07001487 # only admins and pis can change a user's site
Tony Mack92b12052015-01-28 12:49:58 -05001488 # self.readonly_fields = ('backend_status_text', 'site')
Tony Mackb2c407f2015-01-28 12:37:12 -05001489 self.fieldsets = (
1490 ('Login Details', {'fields': login_details_fields, 'classes':['suit-tab suit-tab-general']}),
1491 ('Contact Information', {'fields': self.fieldListContactInfo, 'classes':['suit-tab suit-tab-contact']}),
1492 )
Tony Mack6235fc82015-01-25 21:58:30 -05001493 return super(UserAdmin, self).get_form(request, obj, **kwargs)
1494
Scott Baker022cdcd2015-02-18 15:50:11 -08001495class ControllerDashboardViewInline(XOSTabularInline):
Scott Bakera6a0c772014-12-22 17:35:34 -08001496 model = ControllerDashboardView
Scott Bakereef5a6b2014-12-19 16:41:12 -08001497 extra = 0
1498 fields = ["controller", "url"]
1499 suit_classes = 'suit-tab suit-tab-controllers'
1500
Scott Baker022cdcd2015-02-18 15:50:11 -08001501class DashboardViewAdmin(XOSBaseAdmin):
Scott Baker2c3cb642014-05-19 17:55:56 -07001502 fieldsets = [('Dashboard View Details',
Scott Bakerecc55ac2015-02-17 13:34:32 -08001503 {'fields': ['backend_status_text', 'name', 'url', 'enabled', 'deployments'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001504 'classes': ['suit-tab suit-tab-general']})
1505 ]
Scott Baker9daf19c2015-01-18 16:46:26 -08001506 list_display = ["name", "enabled", "url"]
Scott Baker40c00762014-08-21 16:55:59 -07001507 readonly_fields = ('backend_status_text', )
Scott Bakera6a0c772014-12-22 17:35:34 -08001508 inlines = [ControllerDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001509
Scott Bakereef5a6b2014-12-19 16:41:12 -08001510 suit_form_tabs =(('general','Dashboard View Details'),
1511 ('controllers', 'Per-controller Dashboard Details'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001512
Scott Baker022cdcd2015-02-18 15:50:11 -08001513class ServiceResourceInline(XOSTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001514 model = ServiceResource
1515 extra = 0
1516
Scott Baker022cdcd2015-02-18 15:50:11 -08001517class ServiceClassAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001518 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1519 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001520 inlines = [ServiceResourceInline]
1521
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001522 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1523 user_readonly_inlines = []
1524
Scott Baker022cdcd2015-02-18 15:50:11 -08001525class ReservedResourceInline(XOSTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001526 model = ReservedResource
1527 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001528 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001529
1530 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1531 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1532
1533 if db_field.name == 'resource':
1534 # restrict resources to those that the slice's service class allows
1535 if request._slice is not None:
1536 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1537 if len(field.queryset) > 0:
1538 field.initial = field.queryset.all()[0]
S.Çağlar Onur0e591832015-02-24 17:28:09 -05001539 else:
1540 field.queryset = field.queryset.none()
Tony Mack3de59e32015-08-19 11:58:18 -04001541 elif db_field.name == 'instance':
1542 # restrict instances to those that belong to the slice
S.Çağlar Onur0e591832015-02-24 17:28:09 -05001543 if request._slice is not None:
Scott Baker133c9212013-05-17 09:09:11 -07001544 field.queryset = field.queryset.filter(slice = request._slice)
1545 else:
S.Çağlar Onur0e591832015-02-24 17:28:09 -05001546 field.queryset = field.queryset.none()
1547
Scott Baker133c9212013-05-17 09:09:11 -07001548 return field
1549
Tony Mack5b061472014-02-04 07:57:10 -05001550 def queryset(self, request):
1551 return ReservedResource.select_by_user(request.user)
1552
Scott Baker133c9212013-05-17 09:09:11 -07001553class ReservationChangeForm(forms.ModelForm):
1554 class Meta:
1555 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001556 widgets = {
1557 'slice' : LinkedSelect
1558 }
Scott Baker133c9212013-05-17 09:09:11 -07001559
1560class ReservationAddForm(forms.ModelForm):
1561 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1562 refresh = forms.CharField(widget=forms.HiddenInput())
1563
1564 class Media:
Scott Baker97468b72015-02-18 15:15:58 -08001565 css = {'all': ('xos.css',)} # .field-refresh { display: none; }
Scott Baker133c9212013-05-17 09:09:11 -07001566
1567 def clean_slice(self):
1568 slice = self.cleaned_data.get("slice")
1569 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1570 if len(x) == 0:
1571 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1572 return slice
1573
1574 class Meta:
1575 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001576 widgets = {
1577 'slice' : LinkedSelect
1578 }
1579
Scott Baker133c9212013-05-17 09:09:11 -07001580
1581class ReservationAddRefreshForm(ReservationAddForm):
1582 """ This form is displayed when the Reservation Form receives an update
1583 from the Slice dropdown onChange handler. It doesn't validate the
1584 data and doesn't save the data. This will cause the form to be
1585 redrawn.
1586 """
1587
Scott Baker8737e5f2013-05-17 09:35:32 -07001588 """ don't validate anything other than slice """
1589 dont_validate_fields = ("startTime", "duration")
1590
Scott Baker133c9212013-05-17 09:09:11 -07001591 def full_clean(self):
1592 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001593
1594 for fieldname in self.dont_validate_fields:
1595 if fieldname in self._errors:
1596 del self._errors[fieldname]
1597
Scott Baker133c9212013-05-17 09:09:11 -07001598 return result
1599
1600 """ don't save anything """
1601 def is_valid(self):
1602 return False
1603
Scott Baker022cdcd2015-02-18 15:50:11 -08001604class ReservationAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001605 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001606 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001607 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001608 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001609 form = ReservationAddForm
1610
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001611 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1612
1613 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001614 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001615
Scott Baker133c9212013-05-17 09:09:11 -07001616 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001617 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001618 request._refresh = False
1619 request._slice = None
1620 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001621 # "refresh" will be set to "1" if the form was submitted due to
1622 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001623 if request.POST.get("refresh","1") == "1":
1624 request._refresh = True
1625 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001626
1627 # Keep track of the slice that was selected, so the
1628 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001629 request._slice = request.POST.get("slice",None)
1630 if (request._slice is not None):
1631 request._slice = Slice.objects.get(id=request._slice)
1632
1633 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1634 return result
1635
Scott Bakeracd45142013-05-19 16:19:16 -07001636 def changelist_view(self, request, extra_context = None):
1637 timezone.activate(request.user.timezone)
1638 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1639
Scott Baker133c9212013-05-17 09:09:11 -07001640 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001641 request._obj_ = obj
1642 if obj is not None:
1643 # For changes, set request._slice to the slice already set in the
1644 # object.
1645 request._slice = obj.slice
1646 self.form = ReservationChangeForm
1647 else:
1648 if getattr(request, "_refresh", False):
1649 self.form = ReservationAddRefreshForm
1650 else:
1651 self.form = ReservationAddForm
1652 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1653
Scott Baker133c9212013-05-17 09:09:11 -07001654 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001655 if (obj is not None):
1656 # Prevent slice from being changed after the reservation has been
1657 # created.
1658 return ['slice']
1659 else:
Scott Baker133c9212013-05-17 09:09:11 -07001660 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001661
Tony Mack5b061472014-02-04 07:57:10 -05001662 def queryset(self, request):
1663 return Reservation.select_by_user(request.user)
1664
Scott Baker022cdcd2015-02-18 15:50:11 -08001665class NetworkParameterTypeAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001666 list_display = ("backend_status_icon", "name", )
1667 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001668 user_readonly_fields = ['name']
1669 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001670
Scott Baker022cdcd2015-02-18 15:50:11 -08001671class RouterAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001672 list_display = ("backend_status_icon", "name", )
1673 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001674 user_readonly_fields = ['name']
1675 user_readonly_inlines = []
1676
Scott Baker022cdcd2015-02-18 15:50:11 -08001677class RouterInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001678 model = Router.networks.through
1679 extra = 0
1680 verbose_name_plural = "Routers"
1681 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001682 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001683
Scott Bakerb27b62c2014-08-15 16:29:16 -07001684class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001685 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001686 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001687 verbose_name_plural = "Parameters"
1688 verbose_name = "Parameter"
1689 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001690 fields = ['backend_status_icon', 'parameter', 'value']
1691 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001692
Tony Mack3de59e32015-08-19 11:58:18 -04001693class NetworkInstancesInline(XOSTabularInline):
1694 fields = ['backend_status_icon', 'network', 'instance', 'ip', 'reserve']
Scott Baker40c00762014-08-21 16:55:59 -07001695 readonly_fields = ("backend_status_icon", "ip", )
Tony Mack3de59e32015-08-19 11:58:18 -04001696 model = NetworkInstance
1697 selflink_fieldname = "instance"
Scott Baker74d8e622013-07-29 16:04:22 -07001698 extra = 0
Tony Mack3de59e32015-08-19 11:58:18 -04001699 verbose_name_plural = "Instances"
1700 verbose_name = "Instance"
1701 suit_classes = 'suit-tab suit-tab-networkinstances'
Scott Baker74d8e622013-07-29 16:04:22 -07001702
Scott Baker022cdcd2015-02-18 15:50:11 -08001703class NetworkSlicesInline(XOSTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001704 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001705 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001706 extra = 0
1707 verbose_name_plural = "Slices"
1708 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001709 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001710 fields = ['backend_status_icon', 'network','slice']
1711 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001712
Scott Baker022cdcd2015-02-18 15:50:11 -08001713class ControllerNetworkInline(XOSTabularInline):
Tony Macka7dbd422015-01-05 22:48:11 -05001714 model = ControllerNetwork
Scott Bakerfbb45862014-10-17 16:27:23 -07001715 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001716 verbose_name_plural = "Controller Networks"
1717 verbose_name = "Controller Network"
Scott Bakerfbb45862014-10-17 16:27:23 -07001718 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001719 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Bakerfbb45862014-10-17 16:27:23 -07001720 readonly_fields = ('backend_status_icon', )
1721
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001722class NetworkForm(forms.ModelForm):
1723 class Meta:
1724 model = Network
1725 widgets = {
1726 'topologyParameters': UploadTextareaWidget,
1727 'controllerParameters': UploadTextareaWidget,
1728 }
1729
Scott Baker022cdcd2015-02-18 15:50:11 -08001730class NetworkAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001731 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1732 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001733 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001734
Tony Mack3de59e32015-08-19 11:58:18 -04001735 inlines = [NetworkParameterInline, NetworkInstancesInline, NetworkSlicesInline, RouterInline]
Tony Macka7dbd422015-01-05 22:48:11 -05001736 admin_inlines = [ControllerNetworkInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001737
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001738 form=NetworkForm
1739
Siobhan Tully2d95e482013-09-06 10:56:06 -04001740 fieldsets = [
Scott Baker0451fb62015-01-03 12:29:29 -08001741 (None, {'fields': ['backend_status_text', 'name','template','ports','labels','owner','guaranteed_bandwidth', 'permit_all_slices','permitted_slices','network_id','router_id','subnet_id','subnet'],
Scott Baker40248712014-11-17 16:04:45 -08001742 'classes':['suit-tab suit-tab-general']}),
Scott Baker0451fb62015-01-03 12:29:29 -08001743 (None, {'fields': ['topology_parameters', 'controller_url', 'controller_parameters'],
Scott Baker40248712014-11-17 16:04:45 -08001744 'classes':['suit-tab suit-tab-sdn']}),
1745 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001746
Scott Baker40c00762014-08-21 16:55:59 -07001747 readonly_fields = ('backend_status_text', )
Scott Baker0451fb62015-01-03 12:29:29 -08001748 user_readonly_fields = ['name','template','ports','labels','owner','guaranteed_bandwidth', 'permit_all_slices','permitted_slices','network_id','router_id','subnet_id','subnet']
Siobhan Tully2d95e482013-09-06 10:56:06 -04001749
Scott Bakerfbb45862014-10-17 16:27:23 -07001750 @property
1751 def suit_form_tabs(self):
1752 tabs=[('general','Network Details'),
Scott Baker40248712014-11-17 16:04:45 -08001753 ('sdn', 'SDN Configuration'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001754 ('netparams', 'Parameters'),
Tony Mack3de59e32015-08-19 11:58:18 -04001755 ('networkinstances','Instances'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001756 ('networkslices','Slices'),
1757 ('routers','Routers'),
1758 ]
1759
1760 request=getattr(_thread_locals, "request", None)
1761 if request and request.user.is_admin:
1762 tabs.append( ('admin-only', 'Admin-Only') )
1763
1764 return tabs
1765
1766
Scott Baker022cdcd2015-02-18 15:50:11 -08001767class NetworkTemplateAdmin(XOSBaseAdmin):
Scott Baker81fa17f2015-01-03 12:03:38 -08001768 list_display = ("backend_status_icon", "name", "guaranteed_bandwidth", "visibility")
Scott Baker63d1a552014-08-21 15:19:07 -07001769 list_display_links = ('backend_status_icon', 'name', )
Scott Baker81fa17f2015-01-03 12:03:38 -08001770 user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001771 user_readonly_inlines = []
Scott Baker40248712014-11-17 16:04:45 -08001772 fieldsets = [
Scott Baker81fa17f2015-01-03 12:03:38 -08001773 (None, {'fields': ['name', 'description', 'guaranteed_bandwidth', 'visibility', 'translation', 'shared_network_name', 'shared_network_id', 'topology_kind', 'controller_kind'],
Scott Baker40248712014-11-17 16:04:45 -08001774 'classes':['suit-tab suit-tab-general']}),]
1775 suit_form_tabs = (('general','Network Template Details'), )
Scott Baker74d8e622013-07-29 16:04:22 -07001776
Scott Baker022cdcd2015-02-18 15:50:11 -08001777class FlavorAdmin(XOSBaseAdmin):
Scott Baker37b47902014-09-02 14:37:41 -07001778 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1779 list_display_links = ("backend_status_icon", "name")
1780 user_readonly_fields = ("name", "flavor")
1781 fields = ("name", "description", "flavor", "order", "default")
1782
Tony Mack31c2b8f2013-04-26 20:01:42 -04001783# register a signal that caches the user's credentials when they log in
1784def cache_credentials(sender, user, request, **kwds):
1785 auth = {'username': request.POST['username'],
1786 'password': request.POST['password']}
1787 request.session['auth'] = auth
1788user_logged_in.connect(cache_credentials)
1789
Scott Baker15cddfa2013-12-09 13:45:19 -08001790def dollar_field(fieldName, short_description):
1791 def newFunc(self, obj):
1792 try:
1793 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1794 except:
1795 x=getattr(obj, fieldName, 0.0)
1796 return x
1797 newFunc.short_description = short_description
1798 return newFunc
1799
1800def right_dollar_field(fieldName, short_description):
1801 def newFunc(self, obj):
1802 try:
1803 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1804 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1805 except:
1806 x=getattr(obj, fieldName, 0.0)
1807 return x
1808 newFunc.short_description = short_description
1809 newFunc.allow_tags = True
1810 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001811
Scott Baker022cdcd2015-02-18 15:50:11 -08001812class InvoiceChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001813 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001814 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001815 verbose_name_plural = "Charges"
1816 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001817 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001818 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1819 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1820 can_delete = False
1821 max_num = 0
1822
1823 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001824
1825class InvoiceAdmin(admin.ModelAdmin):
1826 list_display = ("date", "account")
1827
1828 inlines = [InvoiceChargeInline]
1829
Scott Baker9cb88a22013-12-09 18:56:00 -08001830 fields = ["date", "account", "dollar_amount"]
1831 readonly_fields = ["date", "account", "dollar_amount"]
1832
1833 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001834
Scott Baker022cdcd2015-02-18 15:50:11 -08001835class InvoiceInline(XOSTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001836 model = Invoice
1837 extra = 0
1838 verbose_name_plural = "Invoices"
1839 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001840 fields = ["date", "dollar_amount"]
1841 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001842 suit_classes = 'suit-tab suit-tab-accountinvoice'
1843 can_delete=False
1844 max_num=0
1845
1846 dollar_amount = right_dollar_field("amount", "Amount")
1847
Scott Baker022cdcd2015-02-18 15:50:11 -08001848class PendingChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001849 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001850 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001851 verbose_name_plural = "Charges"
1852 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001853 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001854 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1855 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001856 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001857 can_delete=False
1858 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001859
1860 def queryset(self, request):
1861 qs = super(PendingChargeInline, self).queryset(request)
1862 qs = qs.filter(state="pending")
1863 return qs
1864
Scott Baker15cddfa2013-12-09 13:45:19 -08001865 dollar_amount = right_dollar_field("amount", "Amount")
1866
Scott Baker022cdcd2015-02-18 15:50:11 -08001867class PaymentInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001868 model=Payment
1869 extra = 1
1870 verbose_name_plural = "Payments"
1871 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001872 fields = ["date", "dollar_amount"]
1873 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001874 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001875 can_delete=False
1876 max_num=0
1877
1878 dollar_amount = right_dollar_field("amount", "Amount")
1879
Scott Baker43105042013-12-06 23:23:36 -08001880class AccountAdmin(admin.ModelAdmin):
1881 list_display = ("site", "balance_due")
1882
1883 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1884
1885 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001886 (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 -08001887
Scott Baker15cddfa2013-12-09 13:45:19 -08001888 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001889
1890 suit_form_tabs =(
1891 ('general','Account Details'),
1892 ('accountinvoice', 'Invoices'),
1893 ('accountpayments', 'Payments'),
1894 ('accountpendingcharges','Pending Charges'),
1895 )
1896
Scott Baker15cddfa2013-12-09 13:45:19 -08001897 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1898 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1899 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1900
Scott Baker2461bec2015-08-14 09:10:11 -07001901class ProgramForm(forms.ModelForm):
1902 class Meta:
1903 model = Program
1904 widgets = {
1905 'contents': UploadTextareaWidget(attrs={'rows': 20, 'cols': 80, 'class': "input-xxlarge"}),
1906 'description': forms.Textarea(attrs={'rows': 3, 'cols': 80, 'class': 'input-xxlarge'}),
1907 'messages': forms.Textarea(attrs={'rows': 20, 'cols': 80, 'class': 'input-xxlarge'}),
1908 'output': forms.Textarea(attrs={'rows': 3, 'cols': 80, 'class': 'input-xxlarge'})
1909 }
1910
1911class ProgramAdmin(XOSBaseAdmin):
1912 list_display = ("name", "status")
1913 list_display_links = ('name', "status")
1914
1915 form=ProgramForm
1916
1917 fieldsets = [
1918 (None, {'fields': ['name', 'command', 'kind', 'description', 'output', 'status'],
1919 'classes':['suit-tab suit-tab-general']}),
1920 (None, {'fields': ['contents'],
1921 'classes':['suit-tab suit-tab-contents']}),
1922 (None, {'fields': ['messages'],
1923 'classes':['suit-tab suit-tab-messages']}),
1924 ]
1925
1926 readonly_fields = ("status",)
1927
1928 @property
1929 def suit_form_tabs(self):
1930 tabs=[('general','Program Details'),
1931 ('contents','Program Source'),
1932 ('messages','Messages'),
1933 ]
1934
1935 request=getattr(_thread_locals, "request", None)
1936 if request and request.user.is_admin:
1937 tabs.append( ('admin-only', 'Admin-Only') )
1938
1939 return tabs
1940
Siobhan Tully53437282013-04-26 19:30:27 -04001941# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001942admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001943# ... and, since we're not using Django's builtin permissions,
1944# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001945#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001946
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001947#Do not show django evolution in the admin interface
1948from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001949#admin.site.unregister(Version)
1950#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001951
1952
1953# When debugging it is often easier to see all the classes, but for regular use
1954# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001955showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001956
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001957admin.site.register(Deployment, DeploymentAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001958admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001959admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001960admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001961admin.site.register(Service, ServiceAdmin)
Tony Mack598eaf22015-01-25 12:35:29 -05001962#admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001963admin.site.register(Network, NetworkAdmin)
1964admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001965admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Scott Baker2461bec2015-08-14 09:10:11 -07001966admin.site.register(Program, ProgramAdmin)
Tony Mack598eaf22015-01-25 12:35:29 -05001967#admin.site.register(Account, AccountAdmin)
1968#admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001969
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001970if True:
1971 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1972 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001973 admin.site.register(Tag, TagAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001974 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001975 admin.site.register(SiteRole)
1976 admin.site.register(SliceRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001977 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001978 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1979 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Tony Mack3de59e32015-08-19 11:58:18 -04001980 admin.site.register(Instance, InstanceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001981 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001982 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001983 admin.site.register(Flavor, FlavorAdmin)
Scott Baker1b06b6c2015-07-06 14:40:20 -07001984 admin.site.register(TenantRoot, TenantRootAdmin)
1985 admin.site.register(TenantRootRole, TenantRootRoleAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001986