blob: 41dfa9b86c8f2d7b65cbf81eaf2dce608de4d229 [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
Tony Mack3de59e32015-08-19 11:58:18 -0400405class InstanceInline(XOSTabularInline):
406 model = Instance
Scott Baker4103eb32015-09-21 14:52:15 -0700407 fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400408 extra = 0
Scott Baker4103eb32015-09-21 14:52:15 -0700409 max_num = 0
Scott Baker887d4a82015-01-19 11:32:20 -0800410 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name']
Tony Mack3de59e32015-08-19 11:58:18 -0400411 suit_classes = 'suit-tab suit-tab-instances'
Scott Baker74d8e622013-07-29 16:04:22 -0700412
Tony Mack5b061472014-02-04 07:57:10 -0500413 def queryset(self, request):
Tony Mack3de59e32015-08-19 11:58:18 -0400414 return Instance.select_by_user(request.user)
Tony Mack5b061472014-02-04 07:57:10 -0500415
Scott Bakerb24cc932014-06-09 10:51:16 -0700416 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Tony Mackbf6aa302014-12-26 13:38:02 -0500417 if db_field.name == 'deployment':
Tony Mackbd908ae2015-02-24 15:41:49 -0500418
419 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
Tony Mack3de59e32015-08-19 11:58:18 -0400420 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_deployment_changed(this);"})
Tony Mackbf6aa302014-12-26 13:38:02 -0500421 if db_field.name == 'flavor':
Tony Mack3de59e32015-08-19 11:58:18 -0400422 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700423
Tony Mack3de59e32015-08-19 11:58:18 -0400424 field = super(InstanceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700425
426 return field
427
Tony Mack3de59e32015-08-19 11:58:18 -0400428class CordInstanceInline(XOSTabularInline):
429 model = Instance
Scott Baker0e289fd2015-06-12 10:40:15 -0700430 fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'flavor', 'image', 'node']
431 extra = 0
432 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name']
Tony Mack3de59e32015-08-19 11:58:18 -0400433 suit_classes = 'suit-tab suit-tab-instances'
Scott Baker0e289fd2015-06-12 10:40:15 -0700434
435 def queryset(self, request):
Tony Mack3de59e32015-08-19 11:58:18 -0400436 return Instance.select_by_user(request.user)
Scott Baker0e289fd2015-06-12 10:40:15 -0700437
438 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
439 if db_field.name == 'deployment':
440
441 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
Tony Mack3de59e32015-08-19 11:58:18 -0400442 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_deployment_changed(this);"})
Scott Baker0e289fd2015-06-12 10:40:15 -0700443 if db_field.name == 'flavor':
Tony Mack3de59e32015-08-19 11:58:18 -0400444 kwargs['widget'] = forms.Select(attrs={'onChange': "instance_flavor_changed(this);"})
Scott Baker0e289fd2015-06-12 10:40:15 -0700445
Tony Mack3de59e32015-08-19 11:58:18 -0400446 field = super(CordInstanceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Baker0e289fd2015-06-12 10:40:15 -0700447
448 return field
449
Scott Baker022cdcd2015-02-18 15:50:11 -0800450class SiteInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400451 model = Site
452 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400453 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400454
Tony Mack5b061472014-02-04 07:57:10 -0500455 def queryset(self, request):
456 return Site.select_by_user(request.user)
457
Tony Mack15136b52015-08-04 17:53:23 -0400458class SiteHostsNodesInline(SiteInline):
459 def queryset(self, request):
460 return Site.select_by_user(request.user).filter(hosts_nodes=True)
461
462class SiteHostsUsersInline(SiteInline):
463 def queryset(self, request):
464 return Site.select_by_user(request.user).filter(hosts_users=True)
465
Scott Baker022cdcd2015-02-18 15:50:11 -0800466class UserInline(XOSTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400467 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700468 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
469 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400470 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400471 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400472
Tony Mack5b061472014-02-04 07:57:10 -0500473 def queryset(self, request):
474 return User.select_by_user(request.user)
475
Scott Baker022cdcd2015-02-18 15:50:11 -0800476class SliceInline(XOSTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400477 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700478 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
479 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400480 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400481 suit_classes = 'suit-tab suit-tab-slices'
482
Tony Mack5b061472014-02-04 07:57:10 -0500483 def queryset(self, request):
484 return Slice.select_by_user(request.user)
485
Scott Baker022cdcd2015-02-18 15:50:11 -0800486class NodeInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400487 model = Node
488 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400489 suit_classes = 'suit-tab suit-tab-nodes'
Tony Mack93d1b032014-12-08 16:43:02 -0500490 fields = ['backend_status_icon', 'name', 'site_deployment']
Scott Baker40c00762014-08-21 16:55:59 -0700491 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400492
Scott Baker022cdcd2015-02-18 15:50:11 -0800493class DeploymentPrivilegeInline(XOSTabularInline):
Tony Mack93d1b032014-12-08 16:43:02 -0500494 model = DeploymentPrivilege
495 extra = 0
Tony Mack4ce14c42015-02-09 21:41:57 -0500496 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Tony Mack93d1b032014-12-08 16:43:02 -0500497 fields = ['backend_status_icon', 'user','role','deployment']
498 readonly_fields = ('backend_status_icon', )
499
500 def queryset(self, request):
501 return DeploymentPrivilege.select_by_user(request.user)
502
Scott Baker022cdcd2015-02-18 15:50:11 -0800503class ControllerSiteInline(XOSTabularInline):
Tony Macka7dbd422015-01-05 22:48:11 -0500504 model = ControllerSite
505 extra = 0
506 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack8f30ebe2015-01-06 15:08:20 -0500507 fields = ['controller', 'site', 'tenant_id']
Tony Macka7dbd422015-01-05 22:48:11 -0500508
509
Scott Baker022cdcd2015-02-18 15:50:11 -0800510class SitePrivilegeInline(XOSTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400511 model = SitePrivilege
512 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400513 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700514 fields = ['backend_status_icon', 'user','site', 'role']
515 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400516
Tony Mackc2835a92013-05-28 09:18:49 -0400517 def formfield_for_foreignkey(self, db_field, request, **kwargs):
518 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500519 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400520
521 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500522 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400523 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
524
Tony Mack5b061472014-02-04 07:57:10 -0500525 def queryset(self, request):
526 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400527
Tony Mack8d108e22015-05-11 20:39:32 -0400528
529class ServicePrivilegeInline(XOSTabularInline):
530 model = ServicePrivilege
531 extra = 0
532 suit_classes = 'suit-tab suit-tab-serviceprivileges'
533 fields = ['backend_status_icon', 'user','service', 'role']
534 readonly_fields = ('backend_status_icon', )
535
536 def formfield_for_foreignkey(self, db_field, request, **kwargs):
537 if db_field.name == 'service':
538 kwargs['queryset'] = Service.select_by_user(request.user)
Tony Mackcf88f8c2015-05-15 06:33:45 -0400539 if db_field.name == 'user':
540 kwargs['queryset'] = User.select_by_user(request.user)
541 return super(ServicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mack8d108e22015-05-11 20:39:32 -0400542
543 def queryset(self, request):
544 return ServicePrivilege.select_by_user(request.user)
545
Scott Baker022cdcd2015-02-18 15:50:11 -0800546class SiteDeploymentInline(XOSTabularInline):
Tony Macka7dbd422015-01-05 22:48:11 -0500547 model = SiteDeployment
Tony Macke4be32f2014-03-11 20:45:25 -0400548 extra = 0
Tony Mack10812252015-01-30 10:58:29 -0500549 suit_classes = 'suit-tab suit-tab-sitedeployments'
Tony Mack528d4222014-12-05 17:13:08 -0500550 fields = ['backend_status_icon', 'deployment','site', 'controller']
Scott Baker40c00762014-08-21 16:55:59 -0700551 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400552
553 def formfield_for_foreignkey(self, db_field, request, **kwargs):
554 if db_field.name == 'site':
555 kwargs['queryset'] = Site.select_by_user(request.user)
556
557 if db_field.name == 'deployment':
558 kwargs['queryset'] = Deployment.select_by_user(request.user)
Tony Mack528d4222014-12-05 17:13:08 -0500559
560 if db_field.name == 'controller':
Scott Baker4103eb32015-09-21 14:52:15 -0700561 if len(resolve(request.path).args) > 0:
562 kwargs['queryset'] = Controller.select_by_user(request.user).filter(deployment__id=int(resolve(request.path).args[0]))
Tony Mack528d4222014-12-05 17:13:08 -0500563
Tony Macka7dbd422015-01-05 22:48:11 -0500564 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Macke4be32f2014-03-11 20:45:25 -0400565
566 def queryset(self, request):
Tony Macka7dbd422015-01-05 22:48:11 -0500567 return SiteDeployment.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400568
569
Scott Baker022cdcd2015-02-18 15:50:11 -0800570class SlicePrivilegeInline(XOSTabularInline):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400571 model = SlicePrivilege
572 suit_classes = 'suit-tab suit-tab-sliceprivileges'
573 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700574 fields = ('backend_status_icon', 'user', 'slice', 'role')
575 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400576
Tony Mackc2835a92013-05-28 09:18:49 -0400577 def formfield_for_foreignkey(self, db_field, request, **kwargs):
578 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700579 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400580 if db_field.name == 'user':
Scott Baker521abcc2015-07-16 12:40:07 -0700581 # all users are available to be granted SlicePrivilege
582 kwargs['queryset'] = User.objects.all()
Tony Mackc2835a92013-05-28 09:18:49 -0400583
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400584 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400585
Tony Mack5b061472014-02-04 07:57:10 -0500586 def queryset(self, request):
587 return SlicePrivilege.select_by_user(request.user)
588
Scott Baker022cdcd2015-02-18 15:50:11 -0800589class SliceNetworkInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700590 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800591 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700592 extra = 0
593 verbose_name = "Network Connection"
594 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400595 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700596 fields = ['backend_status_icon', 'network']
597 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700598
Scott Baker022cdcd2015-02-18 15:50:11 -0800599class ImageDeploymentsInline(XOSTabularInline):
Sapan Bhatiae9f96f62014-11-19 15:10:16 -0500600 model = ImageDeployments
Scott Baker2170b972014-06-03 12:14:07 -0700601 extra = 0
602 verbose_name = "Image Deployments"
603 verbose_name_plural = "Image Deployments"
604 suit_classes = 'suit-tab suit-tab-imagedeployments'
Tony Mack336e0f92014-11-30 15:53:08 -0500605 fields = ['backend_status_icon', 'image', 'deployment']
606 readonly_fields = ['backend_status_icon']
607
Scott Baker022cdcd2015-02-18 15:50:11 -0800608class ControllerImagesInline(XOSTabularInline):
Tony Mack336e0f92014-11-30 15:53:08 -0500609 model = ControllerImages
610 extra = 0
611 verbose_name = "Controller Images"
612 verbose_name_plural = "Controller Images"
613 suit_classes = 'suit-tab suit-tab-admin-only'
614 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
Scott Baker40c00762014-08-21 16:55:59 -0700615 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700616
Scott Baker022cdcd2015-02-18 15:50:11 -0800617class SliceRoleAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400618 model = SliceRole
619 pass
620
Scott Baker022cdcd2015-02-18 15:50:11 -0800621class SiteRoleAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400622 model = SiteRole
623 pass
624
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400625class DeploymentAdminForm(forms.ModelForm):
Scott Bakerde0f4412014-06-11 15:40:26 -0700626 images = forms.ModelMultipleChoiceField(
627 queryset=Image.objects.all(),
628 required=False,
629 help_text="Select which images should be deployed on this deployment",
630 widget=FilteredSelectMultiple(
631 verbose_name=('Images'), is_stacked=False
632 )
633 )
Scott Baker37b47902014-09-02 14:37:41 -0700634 flavors = forms.ModelMultipleChoiceField(
635 queryset=Flavor.objects.all(),
636 required=False,
637 help_text="Select which flavors should be usable on this deployment",
638 widget=FilteredSelectMultiple(
639 verbose_name=('Flavors'), is_stacked=False
640 )
641 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400642 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400643 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700644 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400645
Siobhan Tully320b4622014-01-17 15:11:14 -0500646 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700647 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500648 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
649
Scott Baker5380c522014-06-06 14:49:43 -0700650 self.fields['accessControl'].initial = "allow site " + request.user.site.name
651
Siobhan Tully320b4622014-01-17 15:11:14 -0500652 if self.instance and self.instance.pk:
Scott Baker9f6b8ed2014-11-17 23:44:03 -0800653 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700654 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700655
656 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
657 """ helper function for handling m2m relations from the MultipleChoiceField
658
659 this_obj: the source object we want to link from
660
661 selected_objs: a list of destination objects we want to link to
662
663 all_relations: the full set of relations involving this_obj, including ones we don't want
664
665 relation_class: the class that implements the relation from source to dest
666
667 local_attrname: field name representing this_obj in relation_class
668
669 foreign_attrname: field name representing selected_objs in relation_class
670
671 This function will remove all newobjclass relations from this_obj
672 that are not contained in selected_objs, and add any relations that
673 are in selected_objs but don't exist in the data model yet.
674 """
675
676 existing_dest_objs = []
677 for relation in list(all_relations):
678 if getattr(relation, foreign_attrname) not in selected_objs:
679 #print "deleting site", sdp.site
680 relation.delete()
681 else:
682 existing_dest_objs.append(getattr(relation, foreign_attrname))
683
684 for dest_obj in selected_objs:
685 if dest_obj not in existing_dest_objs:
686 #print "adding site", site
687 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
688 relation = relation_class(**kwargs)
689 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500690
691 def save(self, commit=True):
692 deployment = super(DeploymentAdminForm, self).save(commit=False)
693
694 if commit:
695 deployment.save()
Scott Baker61b6aec2014-10-06 17:17:40 -0700696 # this has to be done after save() if/when a deployment is first created
697 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500698
699 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700700 # save_m2m() doesn't seem to work with 'through' relations. So we
701 # create/destroy the through models ourselves. There has to be
702 # a better way...
703
Tony Mack592aa952014-12-15 11:45:02 -0500704 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ImageDeployments, "deployment", "image")
705 # manipulate_m2m_objs doesn't work for Flavor/Deployment relationship
706 # so well handle that manually here
707 for flavor in deployment.flavors.all():
708 if getattr(flavor, 'name') not in self.cleaned_data['flavors']:
Tony Mack11f4d202014-12-15 12:37:59 -0500709 deployment.flavors.remove(flavor)
Tony Mack592aa952014-12-15 11:45:02 -0500710 for flavor in self.cleaned_data['flavors']:
711 if flavor not in deployment.flavors.all():
712 flavor.deployments.add(deployment)
Scott Bakerc9b14f72014-05-22 13:44:20 -0700713
Scott Baker37b47902014-09-02 14:37:41 -0700714 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500715
716 return deployment
717
Scott Bakerff5e0f32014-05-22 14:40:27 -0700718class DeploymentAdminROForm(DeploymentAdminForm):
719 def save(self, commit=True):
720 raise PermissionDenied
721
Scott Baker022cdcd2015-02-18 15:50:11 -0800722class SiteAssocInline(XOSTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500723 model = Site.deployments.through
724 extra = 0
725 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400726
Scott Baker022cdcd2015-02-18 15:50:11 -0800727class DeploymentAdmin(XOSBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500728 model = Deployment
Scott Bakerae233f42015-02-10 08:40:34 -0800729 fieldList = ['backend_status_text', 'name', 'images', 'flavors', 'accessControl']
730 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack93d1b032014-12-08 16:43:02 -0500731 # node no longer directly connected to deployment
732 #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
Tony Mack10812252015-01-30 10:58:29 -0500733 inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline,SiteDeploymentInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700734 list_display = ['backend_status_icon', 'name']
735 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700736 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500737
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500738 user_readonly_fields = ['name']
739
Tony Mack93d1b032014-12-08 16:43:02 -0500740 # nodes no longer direclty connected to deployments
Scott Bakerae233f42015-02-10 08:40:34 -0800741 suit_form_tabs =(('general','Deployment Details'),('deploymentprivileges','Privileges'), ('sitedeployments', 'Sites'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500742
Scott Bakerff5e0f32014-05-22 14:40:27 -0700743 def get_form(self, request, obj=None, **kwargs):
Tony Mackd893dfb2015-02-05 06:13:04 -0500744 if request.user.isReadOnlyUser() or not request.user.is_admin:
Scott Bakerff5e0f32014-05-22 14:40:27 -0700745 kwargs["form"] = DeploymentAdminROForm
746 else:
747 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700748 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
749
750 # from stackexchange: pass the request object into the form
751
752 class AdminFormMetaClass(adminForm):
753 def __new__(cls, *args, **kwargs):
754 kwargs['request'] = request
755 return adminForm(*args, **kwargs)
756
757 return AdminFormMetaClass
758
Scott Baker022cdcd2015-02-18 15:50:11 -0800759class ControllerAdmin(XOSBaseAdmin):
Scott Bakerae233f42015-02-10 08:40:34 -0800760 model = Controller
Scott Baker00eae8c2015-02-16 11:55:09 -0800761 fieldList = ['deployment', 'name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password', 'domain']
Scott Bakerae233f42015-02-10 08:40:34 -0800762 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Macka7dbd422015-01-05 22:48:11 -0500763 inlines = [ControllerSiteInline] # ,ControllerImagesInline]
Tony Mack528d4222014-12-05 17:13:08 -0500764 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
765 list_display_links = ('backend_status_icon', 'name', )
766 readonly_fields = ('backend_status_text',)
767
768 user_readonly_fields = []
769
Tony Mackc36cafb2015-01-13 17:33:08 -0500770 def save_model(self, request, obj, form, change):
771 # update openstack connection to use this site/tenant
772 obj.save_by_user(request.user)
773
774 def delete_model(self, request, obj):
Scott Bakerae233f42015-02-10 08:40:34 -0800775 obj.delete_by_user(request.user)
776
Tony Mack79e2e662015-02-18 11:41:36 -0500777 def queryset(self, request):
778 return Controller.select_by_user(request.user)
779
Scott Bakerae233f42015-02-10 08:40:34 -0800780 @property
781 def suit_form_tabs(self):
782 tabs = [('general', 'Controller Details'),
783 ]
784
785 request=getattr(_thread_locals, "request", None)
786 if request and request.user.is_admin:
787 tabs.append( ('admin-only', 'Admin-Only') )
788
789 return tabs
Tony Mackc36cafb2015-01-13 17:33:08 -0500790
Scott Baker1b06b6c2015-07-06 14:40:20 -0700791class TenantRootRoleAdmin(XOSBaseAdmin):
792 model = TenantRootRole
793 fields = ('role',)
794
795class TenantRootTenantInline(XOSTabularInline):
796 model = Tenant
797 fields = ['provider_service', 'subscriber_root']
798 extra = 0
799 suit_classes = 'suit-tab suit-tab-tenantroots'
800 fk_name = 'subscriber_root'
801 verbose_name = 'subscribed tenant'
802 verbose_name_plural = 'subscribed tenants'
803
804 #def queryset(self, request):
805 # qs = super(TenantRootTenantInline, self).queryset(request)
806 # return qs.filter(kind="coarse")
807
808class TenantRootPrivilegeInline(XOSTabularInline):
809 model = TenantRootPrivilege
810 extra = 0
811 suit_classes = 'suit-tab suit-tab-tenantrootprivileges'
812 fields = ['backend_status_icon', 'user', 'role', 'tenant_root']
813 readonly_fields = ('backend_status_icon', )
814
815 def queryset(self, request):
816 return TenantRootPrivilege.select_by_user(request.user)
817
818class TenantRootAdmin(XOSBaseAdmin):
819 model = TenantRoot
820 list_display = ('backend_status_icon', 'name', 'kind')
821 list_display_links = ('backend_status_icon', 'name')
822 fieldList = ('backend_status_text', 'name', 'kind', )
823 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
824 inlines = (TenantRootTenantInline, TenantRootPrivilegeInline)
825 readonly_fields = ('backend_status_text', )
826
827 suit_form_tabs =(('general', 'Tenant Root Details'),
828 ('tenantroots','Tenancy'),
829 ('tenantrootprivileges','Privileges')
830 )
831
Scott Baker925a8fa2015-04-26 20:30:40 -0700832class ProviderTenantInline(XOSTabularInline):
833 model = CoarseTenant
834 fields = ['provider_service', 'subscriber_service', 'connect_method']
835 extra = 0
836 suit_classes = 'suit-tab suit-tab-servicetenants'
837 fk_name = 'provider_service'
838 verbose_name = 'provided tenant'
839 verbose_name_plural = 'provided tenants'
840
841 def queryset(self, request):
842 qs = super(ProviderTenantInline, self).queryset(request)
843 return qs.filter(kind="coarse")
844
845class SubscriberTenantInline(XOSTabularInline):
846 model = CoarseTenant
847 fields = ['provider_service', 'subscriber_service', 'connect_method']
848 extra = 0
849 suit_classes = 'suit-tab suit-tab-servicetenants'
850 fk_name = 'subscriber_service'
851 verbose_name = 'subscribed tenant'
852 verbose_name_plural = 'subscribed tenants'
853
854 def queryset(self, request):
855 qs = super(SubscriberTenantInline, self).queryset(request)
856 return qs.filter(kind="coarse")
857
Scott Baker022cdcd2015-02-18 15:50:11 -0800858class ServiceAttrAsTabInline(XOSTabularInline):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400859 model = ServiceAttribute
860 fields = ['name','value']
861 extra = 0
862 suit_classes = 'suit-tab suit-tab-serviceattrs'
863
Scott Baker022cdcd2015-02-18 15:50:11 -0800864class ServiceAdmin(XOSBaseAdmin):
Scott Baker008a9962015-04-15 20:58:20 -0700865 list_display = ("backend_status_icon","name","kind","versionNumber","enabled","published")
Scott Baker63d1a552014-08-21 15:19:07 -0700866 list_display_links = ('backend_status_icon', 'name', )
Scott Baker2f0828e2015-07-13 12:33:28 -0700867 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 -0500868 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack8d108e22015-05-11 20:39:32 -0400869 inlines = [ServiceAttrAsTabInline,SliceInline,ProviderTenantInline,SubscriberTenantInline,ServicePrivilegeInline]
Scott Baker40c00762014-08-21 16:55:59 -0700870 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500871
872 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500873
874 suit_form_tabs =(('general', 'Service Details'),
875 ('slices','Slices'),
876 ('serviceattrs','Additional Attributes'),
Scott Baker925a8fa2015-04-26 20:30:40 -0700877 ('servicetenants','Tenancy'),
Scott Baker1b06b6c2015-07-06 14:40:20 -0700878 ('serviceprivileges','Privileges')
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500879 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400880
Scott Baker022cdcd2015-02-18 15:50:11 -0800881class SiteNodeInline(XOSTabularInline):
Tony Mack82df1d02015-01-14 20:58:38 -0500882 model = Node
883 fields = ['name', 'site_deployment']
884 extra = 0
885 suit_classes = 'suit-tab suit-tab-nodes'
886
Tony Mack26b59532015-02-25 11:39:34 -0500887 def formfield_for_foreignkey(self, db_field, request, **kwargs):
888 # only display site deployments associated with this site
889 if db_field.name == 'site_deployment':
890 kwargs['queryset'] = SiteDeployment.objects.filter(site__id=int(request.path.split('/')[-2]))
891
892 return super(SiteNodeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
893
Scott Baker022cdcd2015-02-18 15:50:11 -0800894class SiteAdmin(XOSBaseAdmin):
Tony Mack598eaf22015-01-25 12:35:29 -0500895 #fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Tony Mack2862dca2015-08-04 17:21:55 -0400896 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'login_base', 'location', 'is_public', 'hosts_nodes', 'hosts_users']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400897 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500898 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400899 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400900 ]
Tony Mack598eaf22015-01-25 12:35:29 -0500901 #readonly_fields = ['backend_status_text', 'accountLink']
902 readonly_fields = ['backend_status_text']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500903
Tony Mack598eaf22015-01-25 12:35:29 -0500904 #user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Tony Mack2862dca2015-08-04 17:21:55 -0400905 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'hosts_nodes', 'hosts_users']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500906
Scott Baker63d1a552014-08-21 15:19:07 -0700907 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
908 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400909 filter_horizontal = ('deployments',)
Tony Mack10812252015-01-30 10:58:29 -0500910 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteNodeInline]
Tony Mack10328a12015-01-14 12:11:05 -0500911 admin_inlines = [ControllerSiteInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400912 search_fields = ['name']
913
Tony Mack3c01ff92015-01-10 23:08:10 -0500914 @property
915 def suit_form_tabs(self):
916 tabs = [('general', 'Site Details'),
917 ('users','Users'),
918 ('siteprivileges','Privileges'),
Tony Mack3c01ff92015-01-10 23:08:10 -0500919 ('slices','Slices'),
Tony Mack82df1d02015-01-14 20:58:38 -0500920 ('nodes','Nodes'),
Tony Mack3c01ff92015-01-10 23:08:10 -0500921 ]
922
923 request=getattr(_thread_locals, "request", None)
924 if request and request.user.is_admin:
925 tabs.append( ('admin-only', 'Admin-Only') )
926
927 return tabs
928
Tony Mack04062832013-05-10 08:22:44 -0400929 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500930 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400931
Tony Mack5cd13202013-05-01 21:48:38 -0400932 def get_formsets(self, request, obj=None):
933 for inline in self.get_inline_instances(request, obj):
934 # hide MyInline in the add view
935 if obj is None:
936 continue
Tony Mack3de59e32015-08-19 11:58:18 -0400937 if isinstance(inline, InstanceInline):
Tony Mack2bd5b412013-06-11 21:05:06 -0400938 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400939 yield inline.get_formset(request, obj)
940
Scott Baker545db2a2013-12-09 18:44:43 -0800941 def accountLink(self, obj):
942 link_obj = obj.accounts.all()
943 if link_obj:
944 reverse_path = "admin:core_account_change"
945 url = reverse(reverse_path, args =(link_obj[0].id,))
946 return "<a href='%s'>%s</a>" % (url, "view billing details")
947 else:
948 return "no billing data for this site"
949 accountLink.allow_tags = True
950 accountLink.short_description = "Billing"
951
Tony Mack332ee1d2014-02-04 15:33:45 -0500952 def save_model(self, request, obj, form, change):
953 # update openstack connection to use this site/tenant
954 obj.save_by_user(request.user)
955
956 def delete_model(self, request, obj):
957 obj.delete_by_user(request.user)
958
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500959
Scott Baker022cdcd2015-02-18 15:50:11 -0800960class SitePrivilegeAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700961 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400962 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500963 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400964 ]
Scott Baker40c00762014-08-21 16:55:59 -0700965 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700966 list_display = ('backend_status_icon', 'user', 'site', 'role')
967 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500968 user_readonly_fields = fieldList
969 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400970
Tony Mackc2835a92013-05-28 09:18:49 -0400971 def formfield_for_foreignkey(self, db_field, request, **kwargs):
972 if db_field.name == 'site':
973 if not request.user.is_admin:
974 # only show sites where user is an admin or pi
975 sites = set()
976 for site_privilege in SitePrivilege.objects.filer(user=request.user):
977 if site_privilege.role.role_type in ['admin', 'pi']:
978 sites.add(site_privilege.site)
979 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
980
981 if db_field.name == 'user':
982 if not request.user.is_admin:
983 # only show users from sites where caller has admin or pi role
984 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
985 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
986 sites = [site_privilege.site for site_privilege in site_privileges]
987 site_privileges = SitePrivilege.objects.filter(site__in=sites)
988 emails = [site_privilege.user.email for site_privilege in site_privileges]
989 users = User.objects.filter(email__in=emails)
990 kwargs['queryset'] = users
991
992 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
993
Tony Mack04062832013-05-10 08:22:44 -0400994 def queryset(self, request):
995 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400996 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400997 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500998 #if not request.user.is_admin:
999 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
1000 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
1001 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
1002 # sites = Site.objects.filter(login_base__in=login_bases)
1003 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -04001004 return qs
1005
Siobhan Tullyce652d02013-10-08 21:52:35 -04001006class SliceForm(forms.ModelForm):
1007 class Meta:
1008 model = Slice
1009 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -07001010 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -04001011 }
1012
Tony Mack2cbd3802014-09-29 16:10:52 -04001013 def clean(self):
1014 cleaned_data = super(SliceForm, self).clean()
1015 name = cleaned_data.get('name')
Scott Baker6efad462014-10-06 23:09:59 -07001016 site = cleaned_data.get('site')
Tony Mack585cb192014-10-22 12:54:19 -04001017 slice_id = self.instance.id
1018 if not site and slice_id:
1019 site = Slice.objects.get(id=slice_id).site
Scott Baker6efad462014-10-06 23:09:59 -07001020 if (not isinstance(site,Site)):
1021 # previous code indicates 'site' could be a site_id and not a site?
1022 site = Slice.objects.get(id=site.id)
Tony Mack2cbd3802014-09-29 16:10:52 -04001023 if not name.startswith(site.login_base):
1024 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
1025 return cleaned_data
1026
Scott Baker022cdcd2015-02-18 15:50:11 -08001027class ControllerSliceInline(XOSTabularInline):
Tony Macka7dbd422015-01-05 22:48:11 -05001028 model = ControllerSlice
Scott Bakerc4efdc72014-10-15 16:54:04 -07001029 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001030 verbose_name = "Controller Slices"
1031 verbose_name_plural = "Controller Slices"
Scott Bakerc4efdc72014-10-15 16:54:04 -07001032 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001033 fields = ['backend_status_icon', 'controller', 'tenant_id']
Tony Mack3c01ff92015-01-10 23:08:10 -05001034 readonly_fields = ('backend_status_icon', 'controller' )
Scott Bakerc4efdc72014-10-15 16:54:04 -07001035
Scott Baker022cdcd2015-02-18 15:50:11 -08001036class SliceAdmin(XOSBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -04001037 form = SliceForm
Tony Mack3de59e32015-08-19 11:58:18 -04001038 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_instances']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001039 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -07001040 readonly_fields = ('backend_status_text', )
Tony Mack3de59e32015-08-19 11:58:18 -04001041 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_instances')
Tony Mack7d459902014-09-03 13:18:57 -04001042 list_display_links = ('backend_status_icon', 'name', )
Tony Mack3de59e32015-08-19 11:58:18 -04001043 normal_inlines = [SlicePrivilegeInline, InstanceInline, TagInline, ReservationInline, SliceNetworkInline]
Scott Baker0e289fd2015-06-12 10:40:15 -07001044 inlines = normal_inlines
Tony Macka7dbd422015-01-05 22:48:11 -05001045 admin_inlines = [ControllerSliceInline]
Scott Baker591fb062015-09-15 15:21:50 -07001046 suit_form_includes = (('slice_instance_tab.html', 'bottom', 'instances'),)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001047
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001048 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001049
Scott Bakerc4efdc72014-10-15 16:54:04 -07001050 @property
1051 def suit_form_tabs(self):
1052 tabs =[('general', 'Slice Details'),
1053 ('slicenetworks','Networks'),
1054 ('sliceprivileges','Privileges'),
Tony Mack3de59e32015-08-19 11:58:18 -04001055 ('instances','Instances'),
Tony Mack598eaf22015-01-25 12:35:29 -05001056 #('reservations','Reservations'),
Tony Mackb34553e2015-01-15 14:44:06 -05001057 ('tags','Tags'),
Scott Bakerc4efdc72014-10-15 16:54:04 -07001058 ]
1059
1060 request=getattr(_thread_locals, "request", None)
1061 if request and request.user.is_admin:
1062 tabs.append( ('admin-only', 'Admin-Only') )
1063
1064 return tabs
Tony Mack7b8505a2014-10-22 11:54:29 -04001065
1066 def add_view(self, request, form_url='', extra_context=None):
Scott Baker0e289fd2015-06-12 10:40:15 -07001067 # Ugly hack for CORD
1068 self.inlines = self.normal_inlines
Tony Mack7b8505a2014-10-22 11:54:29 -04001069 # revert to default read-only fields
1070 self.readonly_fields = ('backend_status_text',)
1071 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
1072
1073 def change_view(self, request, object_id, form_url='', extra_context=None):
Tony Mack7b8505a2014-10-22 11:54:29 -04001074 # cannot change the site of an existing slice so make the site field read only
1075 if object_id:
1076 self.readonly_fields = ('backend_status_text','site')
Scott Baker0e289fd2015-06-12 10:40:15 -07001077
Tony Mack7b8505a2014-10-22 11:54:29 -04001078 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001079
Scott Baker510fdbb2014-08-05 17:19:24 -07001080 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -07001081 deployment_nodes = []
1082 for node in Node.objects.all():
Scott Baker39293d72015-01-21 16:24:07 -08001083 deployment_nodes.append( (node.site_deployment.deployment.id, node.id, node.name) )
Scott Baker510fdbb2014-08-05 17:19:24 -07001084
Scott Baker7a61dc42014-09-02 17:08:20 -07001085 deployment_flavors = []
1086 for flavor in Flavor.objects.all():
1087 for deployment in flavor.deployments.all():
1088 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
1089
Tony Mack93d1b032014-12-08 16:43:02 -05001090 deployment_images = []
Scott Baker93e80cd2014-09-09 09:58:49 -07001091 for image in Image.objects.all():
Tony Mack93d1b032014-12-08 16:43:02 -05001092 for deployment_image in image.imagedeployments.all():
Scott Bakera6a0c772014-12-22 17:35:34 -08001093 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
Scott Baker93e80cd2014-09-09 09:58:49 -07001094
Tony Mackec23b992014-09-02 21:18:45 -04001095 site_login_bases = []
1096 for site in Site.objects.all():
Scott Baker93e80cd2014-09-09 09:58:49 -07001097 site_login_bases.append((site.id, site.login_base))
1098
Scott Baker510fdbb2014-08-05 17:19:24 -07001099 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -07001100 context["deployment_flavors"] = deployment_flavors
Scott Baker93e80cd2014-09-09 09:58:49 -07001101 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -04001102 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -07001103 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
1104
Tony Mackc2835a92013-05-28 09:18:49 -04001105 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1106 if db_field.name == 'site':
Tony Mack15136b52015-08-04 17:53:23 -04001107 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_users=True)
Tony Mackec23b992014-09-02 21:18:45 -04001108 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 -07001109
Tony Mackc2835a92013-05-28 09:18:49 -04001110 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1111
Tony Mack04062832013-05-10 08:22:44 -04001112 def queryset(self, request):
1113 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001114 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -04001115
Tony Mack79748612013-05-01 14:52:03 -04001116 def get_formsets(self, request, obj=None):
1117 for inline in self.get_inline_instances(request, obj):
1118 # hide MyInline in the add view
1119 if obj is None:
1120 continue
Tony Mack3de59e32015-08-19 11:58:18 -04001121 if isinstance(inline, InstanceInline):
Tony Mack2bd5b412013-06-11 21:05:06 -04001122 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -04001123 yield inline.get_formset(request, obj)
1124
Scott Baker03394b32015-09-15 17:48:57 -07001125 def add_extra_context(self, request, extra_context):
1126 super(SliceAdmin, self).add_extra_context(request, extra_context)
1127 # set context["slice_id"] to the PK passed in the URL to this view
1128 if len(request.resolver_match.args)>0:
1129 extra_context["slice_id"] = request.resolver_match.args[0]
1130
Scott Baker0e289fd2015-06-12 10:40:15 -07001131 def UNUSED_get_inline_instances(self, request, obj=None):
1132 # HACK for CORD to do something special on vcpe slice page
1133 # this was a good idea, but failed miserably, as something still
1134 # expects there to be a deployment field.
1135 # XXX this approach is better than clobbering self.inlines, so
1136 # try to make this work post-demo.
1137 if (obj is not None) and (obj.name == "mysite_vcpe"):
Tony Mack3de59e32015-08-19 11:58:18 -04001138 cord_vcpe_inlines = [ SlicePrivilegeInline, CordInstanceInline, TagInline, ReservationInline,SliceNetworkInline]
Scott Baker0e289fd2015-06-12 10:40:15 -07001139
1140 inlines=[]
1141 for inline_class in cord_vcpe_inlines:
1142 inlines.append(inline_class(self.model, self.admin_site))
1143 else:
1144 inlines = super(SliceAdmin, self).get_inline_instances(request, obj)
1145
1146 return inlines
1147
Scott Baker022cdcd2015-02-18 15:50:11 -08001148class SlicePrivilegeAdmin(XOSBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -04001149 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -07001150 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -04001151 ]
Scott Baker40c00762014-08-21 16:55:59 -07001152 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -07001153 list_display = ('backend_status_icon', 'user', 'slice', 'role')
1154 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -04001155
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001156 user_readonly_fields = ['user', 'slice', 'role']
1157 user_readonly_inlines = []
1158
Tony Mackc2835a92013-05-28 09:18:49 -04001159 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1160 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001161 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001162
1163 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -05001164 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001165
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001166 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001167
Tony Mack04062832013-05-10 08:22:44 -04001168 def queryset(self, request):
1169 # admins can see all memberships. Users can only see memberships of
1170 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -05001171 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001172
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001173 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -04001174 # update openstack connection to use this site/tenant
1175 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001176 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001177 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001178 obj.save()
1179
1180 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -04001181 # update openstack connection to use this site/tenant
1182 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001183 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001184 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001185 obj.delete()
1186
Scott Baker022cdcd2015-02-18 15:50:11 -08001187class ImageAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001188
Scott Baker36f50872014-08-21 13:01:25 -07001189 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -07001190 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001191 'classes': ['suit-tab suit-tab-general']})
1192 ]
Scott Baker40c00762014-08-21 16:55:59 -07001193 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001194
Tony Mack3de59e32015-08-19 11:58:18 -04001195 suit_form_tabs =(('general','Image Details'),('instances','Instances'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001196
Tony Mack3de59e32015-08-19 11:58:18 -04001197 inlines = [InstanceInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -07001198
Tony Mack32e1ce32014-05-07 13:29:41 -04001199 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -07001200
Scott Baker63d1a552014-08-21 15:19:07 -07001201 list_display = ['backend_status_icon', 'name']
1202 list_display_links = ('backend_status_icon', 'name', )
1203
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001204class NodeForm(forms.ModelForm):
1205 class Meta:
1206 widgets = {
1207 'site': LinkedSelect,
1208 'deployment': LinkedSelect
1209 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001210
Scott Baker022cdcd2015-02-18 15:50:11 -08001211class NodeAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001212 form = NodeForm
Tony Mack93d1b032014-12-08 16:43:02 -05001213 list_display = ('backend_status_icon', 'name', 'site_deployment')
Scott Baker63d1a552014-08-21 15:19:07 -07001214 list_display_links = ('backend_status_icon', 'name', )
Tony Mack93d1b032014-12-08 16:43:02 -05001215 list_filter = ('site_deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001216
Tony Mack3de59e32015-08-19 11:58:18 -04001217 inlines = [TagInline,InstanceInline]
Tony Mack93d1b032014-12-08 16:43:02 -05001218 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site_deployment'], 'classes':['suit-tab suit-tab-details']})]
Scott Baker40c00762014-08-21 16:55:59 -07001219 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001220
Tony Mack93d1b032014-12-08 16:43:02 -05001221 user_readonly_fields = ['name','site_deployment']
Tony Mack3de59e32015-08-19 11:58:18 -04001222 user_readonly_inlines = [TagInline,InstanceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001223
Tony Mack3de59e32015-08-19 11:58:18 -04001224 suit_form_tabs =(('details','Node Details'),('instances','Instances'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001225
Tony Mack17b97b82015-08-04 17:32:32 -04001226 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1227 if db_field.name == 'site':
1228 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_nodes=True)
Siobhan Tully567e3e62013-06-21 18:03:16 -04001229
Tony Mack3de59e32015-08-19 11:58:18 -04001230class InstanceForm(forms.ModelForm):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001231 class Meta:
Tony Mack3de59e32015-08-19 11:58:18 -04001232 model = Instance
Tony Mackd90cdbf2013-04-16 22:48:40 -04001233 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001234 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001235 widgets = {
1236 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001237 'instance_name': PlainTextWidget(),
Scott Baker887d4a82015-01-19 11:32:20 -08001238 'instance_id': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001239 'slice': LinkedSelect,
Tony Mackbf6aa302014-12-26 13:38:02 -05001240 'deployment': LinkedSelect,
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001241 'node': LinkedSelect,
1242 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001243 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001244
Scott Baker022cdcd2015-02-18 15:50:11 -08001245class TagAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001246 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1247 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001248 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1249 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001250
Tony Mack8bc36b92015-09-01 16:06:52 +00001251class InstancePortInline(XOSTabularInline):
Tony Mackea30da82015-09-10 21:58:15 +00001252 fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
Scott Baker0672e982015-09-08 18:22:15 -07001253 readonly_fields = ("backend_status_icon", "ip", "mac")
Scott Baker43facad2015-08-26 09:43:33 -07001254 model = Port
Scott Baker0bdb6a52015-08-25 18:00:15 -07001255 selflink_fieldname = "network"
1256 extra = 0
1257 verbose_name_plural = "Ports"
1258 verbose_name = "Port"
1259 suit_classes = 'suit-tab suit-tab-ports'
1260
Tony Mack3de59e32015-08-19 11:58:18 -04001261class InstanceAdmin(XOSBaseAdmin):
1262 form = InstanceForm
Tony Mackcdec0902013-04-15 00:38:49 -04001263 fieldsets = [
Tony Mack39c19b22015-08-25 22:29:13 +00001264 ('Instance Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'node', 'all_ips_string', 'instance_id', 'instance_name', 'flavor', 'image', 'ssh_command', 'no_sync'], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -04001265 ]
Tony Mack7d61efb2015-01-30 17:20:46 -05001266 readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Tony Mackd8336582015-01-30 12:52:46 -05001267 list_display = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deployment']
Scott Bakerf0b403f2015-02-13 14:38:21 -08001268 list_display_links = ('backend_status_icon', 'all_ips_string', 'instance_id', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001269
Tony Mack8bc36b92015-09-01 16:06:52 +00001270 suit_form_tabs =(('general', 'Instance Details'), ('ports', 'Ports'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001271
Tony Mack6a782f92015-09-13 22:50:39 +00001272 inlines = [TagInline, InstancePortInline]
Tony Mack53106f32013-04-27 16:43:01 -04001273
Tony Mackbf6aa302014-12-26 13:38:02 -05001274 user_readonly_fields = ['slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001275
Scott Baker970314b2015-01-25 22:16:13 -08001276 def ssh_command(self, obj):
1277 ssh_command = obj.get_ssh_command()
1278 if ssh_command:
1279 return ssh_command
1280 else:
1281 return "(not available)"
1282
Tony Mackc2835a92013-05-28 09:18:49 -04001283 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1284 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001285 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001286
Tony Mack3de59e32015-08-19 11:58:18 -04001287 return super(InstanceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001288
Tony Mack04062832013-05-10 08:22:44 -04001289 def queryset(self, request):
Tony Mack3de59e32015-08-19 11:58:18 -04001290 # admins can see all instances. Users can only see instances of
Tony Mack04062832013-05-10 08:22:44 -04001291 # the slices they belong to.
Tony Mack3de59e32015-08-19 11:58:18 -04001292 return Instance.select_by_user(request.user)
Tony Mack5b061472014-02-04 07:57:10 -05001293
Tony Mack04062832013-05-10 08:22:44 -04001294
Tony Mack1d6b85f2013-05-07 18:49:14 -04001295 def get_formsets(self, request, obj=None):
1296 # make some fields read only if we are updating an existing record
1297 if obj == None:
Tony Mack26dbd8d2015-01-30 17:42:10 -05001298 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001299 else:
Tony Mack26dbd8d2015-01-30 17:42:10 -05001300 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string', 'slice', 'flavor', 'image', 'node')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001301
1302 for inline in self.get_inline_instances(request, obj):
Scott Baker591fb062015-09-15 15:21:50 -07001303 # dead code was eliminated here
Scott Baker526b71e2014-05-13 13:18:01 -07001304 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -04001305
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001306 #def save_model(self, request, obj, form, change):
1307 # # update openstack connection to use this site/tenant
1308 # auth = request.session.get('auth', {})
1309 # auth['tenant'] = obj.slice.name
1310 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1311 # obj.creator = request.user
1312 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001313
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001314 #def delete_model(self, request, obj):
1315 # # update openstack connection to use this site/tenant
1316 # auth = request.session.get('auth', {})
1317 # auth['tenant'] = obj.slice.name
1318 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1319 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001320
Siobhan Tully53437282013-04-26 19:30:27 -04001321class UserCreationForm(forms.ModelForm):
1322 """A form for creating new users. Includes all the required
1323 fields, plus a repeated password."""
1324 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1325 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1326
1327 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001328 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001329 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001330
1331 def clean_password2(self):
1332 # Check that the two password entries match
1333 password1 = self.cleaned_data.get("password1")
1334 password2 = self.cleaned_data.get("password2")
1335 if password1 and password2 and password1 != password2:
1336 raise forms.ValidationError("Passwords don't match")
1337 return password2
1338
1339 def save(self, commit=True):
1340 # Save the provided password in hashed format
1341 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001342 user.password = self.cleaned_data["password1"]
1343 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001344 if commit:
1345 user.save()
1346 return user
1347
Siobhan Tully567e3e62013-06-21 18:03:16 -04001348
Siobhan Tully53437282013-04-26 19:30:27 -04001349class UserChangeForm(forms.ModelForm):
1350 """A form for updating users. Includes all the fields on
1351 the user, but replaces the password field with admin's
1352 password hash display field.
1353 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001354 password = ReadOnlyPasswordHashField(label='Password',
1355 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001356
Scott Baker811d4472015-05-19 16:39:48 -07001357 PROFILE_CHOICES = ((None, '------'), ('regular', 'Regular user'), ('cp', 'Content Provider'))
1358 profile = forms.ChoiceField(choices=PROFILE_CHOICES, required=False, label="Quick Profile")
1359
Siobhan Tully53437282013-04-26 19:30:27 -04001360 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001361 model = User
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001362 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001363
1364 def clean_password(self):
1365 # Regardless of what the user provides, return the initial value.
1366 # This is done here, rather than on the field, because the
1367 # field does not have access to the initial value
1368 return self.initial["password"]
1369
Scott Baker811d4472015-05-19 16:39:48 -07001370 def save(self, *args, **kwargs):
1371 if self.cleaned_data['profile']:
1372 self.instance.apply_profile(self.cleaned_data['profile'])
1373
1374 return super(UserChangeForm, self).save(*args, **kwargs)
1375
Scott Baker022cdcd2015-02-18 15:50:11 -08001376class UserDashboardViewInline(XOSTabularInline):
Scott Baker2c3cb642014-05-19 17:55:56 -07001377 model = UserDashboardView
1378 extra = 0
1379 suit_classes = 'suit-tab suit-tab-dashboards'
1380 fields = ['user', 'dashboardView', 'order']
1381
Scott Baker022cdcd2015-02-18 15:50:11 -08001382class ControllerUserInline(XOSTabularInline):
Tony Mack3c01ff92015-01-10 23:08:10 -05001383 model = ControllerUser
1384 extra = 0
1385 suit_classes = 'suit-tab suit-tab-admin-only'
1386 fields = ['controller', 'user', 'kuser_id']
1387 readonly_fields=['controller']
1388
1389
Scott Bakera9412c32015-02-27 12:21:22 -08001390class UserAdmin(XOSAdminMixin, UserAdmin):
1391 # Note: Make sure XOSAdminMixin is listed before
Scott Baker86c83ab2014-10-03 13:10:47 -07001392 # admin.ModelAdmin in the class declaration.
1393
Siobhan Tully53437282013-04-26 19:30:27 -04001394 class Meta:
1395 app_label = "core"
1396
1397 # The forms to add and change user instances
1398 form = UserChangeForm
1399 add_form = UserCreationForm
1400
1401 # The fields to be used in displaying the User model.
1402 # These override the definitions on the base UserAdmin
1403 # that reference specific fields on auth.User.
Scott Bakerf587f442015-01-24 13:33:26 -08001404 list_display = ('backend_status_icon', 'email', 'firstname', 'lastname', 'site', 'last_login')
1405 list_display_links = ("email",)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001406 list_filter = ('site',)
Scott Baker6e9027f2015-01-29 10:55:53 -08001407 inlines = [SlicePrivilegeInline,SitePrivilegeInline]
Tony Mack3c01ff92015-01-10 23:08:10 -05001408 admin_inlines = [ControllerUserInline]
Scott Bakere61e3a02015-06-10 16:14:58 -07001409 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 -05001410 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1411
Siobhan Tully53437282013-04-26 19:30:27 -04001412 fieldsets = (
Scott Baker74ebc7a2015-05-15 09:19:36 -07001413 ('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 -04001414 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001415 #('Important dates', {'fields': ('last_login',)}),
1416 )
1417 add_fieldsets = (
1418 (None, {
1419 'classes': ('wide',),
Scott Baker74ebc7a2015-05-15 09:19:36 -07001420 'fields': ('site', 'email', 'firstname', 'lastname', 'is_admin', 'is_readonly', 'is_appuser', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001421 ),
1422 )
Scott Baker40c00762014-08-21 16:55:59 -07001423 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001424 search_fields = ('email',)
1425 ordering = ('email',)
1426 filter_horizontal = ()
1427
Scott Baker3ca51f62014-05-23 12:05:11 -07001428 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001429
Scott Baker0a5633b2014-10-06 17:51:20 -07001430 @property
1431 def suit_form_tabs(self):
1432 if getattr(_thread_locals, "obj", None) is None:
1433 return []
1434 else:
Tony Mack3c01ff92015-01-10 23:08:10 -05001435 tabs = [('general','Login Details'),
Scott Baker0a5633b2014-10-06 17:51:20 -07001436 ('contact','Contact Information'),
1437 ('sliceprivileges','Slice Privileges'),
Scott Baker6e9027f2015-01-29 10:55:53 -08001438 ('siteprivileges','Site Privileges')]
Tony Mack3c01ff92015-01-10 23:08:10 -05001439
1440 request=getattr(_thread_locals, "request", None)
1441 if request and request.user.is_admin:
1442 tabs.append( ('admin-only', 'Admin-Only') )
1443
1444 return tabs
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001445
Tony Mackc2835a92013-05-28 09:18:49 -04001446 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1447 if db_field.name == 'site':
Tony Mack17b97b82015-08-04 17:32:32 -04001448 kwargs['queryset'] = Site.select_by_user(request.user).filter(hosts_users=True)
Tony Mackc2835a92013-05-28 09:18:49 -04001449
1450 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1451
Tony Mack5b061472014-02-04 07:57:10 -05001452 def queryset(self, request):
1453 return User.select_by_user(request.user)
1454
Tony Mack6235fc82015-01-25 21:58:30 -05001455 def get_form(self, request, obj=None, **kwargs):
Tony Mackb2c407f2015-01-28 12:37:12 -05001456 # copy login details list
1457 login_details_fields = list(self.fieldListLoginDetails)
Tony Mack92b12052015-01-28 12:49:58 -05001458 if not request.user.is_admin:
Scott Baker6e9027f2015-01-29 10:55:53 -08001459 # only admins can see 'is_admin' and 'is_readonly' fields
Tony Mackb2c407f2015-01-28 12:37:12 -05001460 if 'is_admin' in login_details_fields:
1461 login_details_fields.remove('is_admin')
1462 if 'is_readonly' in login_details_fields:
Scott Baker811d4472015-05-19 16:39:48 -07001463 login_details_fields.remove('is_readonly')
1464 if 'is_appuser' in login_details_fields:
1465 login_details_fields.remove('is_admin')
1466 if 'profile' in login_details_fields:
1467 login_details_fields.remove('profile')
Tony Mack92b12052015-01-28 12:49:58 -05001468 #if len(request.user.siteprivileges.filter(role__role = 'pi')) > 0:
Scott Baker811d4472015-05-19 16:39:48 -07001469 # only admins and pis can change a user's site
Tony Mack92b12052015-01-28 12:49:58 -05001470 # self.readonly_fields = ('backend_status_text', 'site')
Tony Mackb2c407f2015-01-28 12:37:12 -05001471 self.fieldsets = (
1472 ('Login Details', {'fields': login_details_fields, 'classes':['suit-tab suit-tab-general']}),
1473 ('Contact Information', {'fields': self.fieldListContactInfo, 'classes':['suit-tab suit-tab-contact']}),
1474 )
Tony Mack6235fc82015-01-25 21:58:30 -05001475 return super(UserAdmin, self).get_form(request, obj, **kwargs)
1476
Scott Baker022cdcd2015-02-18 15:50:11 -08001477class ControllerDashboardViewInline(XOSTabularInline):
Scott Bakera6a0c772014-12-22 17:35:34 -08001478 model = ControllerDashboardView
Scott Bakereef5a6b2014-12-19 16:41:12 -08001479 extra = 0
1480 fields = ["controller", "url"]
1481 suit_classes = 'suit-tab suit-tab-controllers'
1482
Scott Baker022cdcd2015-02-18 15:50:11 -08001483class DashboardViewAdmin(XOSBaseAdmin):
Scott Baker2c3cb642014-05-19 17:55:56 -07001484 fieldsets = [('Dashboard View Details',
Scott Bakerecc55ac2015-02-17 13:34:32 -08001485 {'fields': ['backend_status_text', 'name', 'url', 'enabled', 'deployments'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001486 'classes': ['suit-tab suit-tab-general']})
1487 ]
Scott Baker9daf19c2015-01-18 16:46:26 -08001488 list_display = ["name", "enabled", "url"]
Scott Baker40c00762014-08-21 16:55:59 -07001489 readonly_fields = ('backend_status_text', )
Scott Bakera6a0c772014-12-22 17:35:34 -08001490 inlines = [ControllerDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001491
Scott Bakereef5a6b2014-12-19 16:41:12 -08001492 suit_form_tabs =(('general','Dashboard View Details'),
1493 ('controllers', 'Per-controller Dashboard Details'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001494
Scott Baker022cdcd2015-02-18 15:50:11 -08001495class ServiceResourceInline(XOSTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001496 model = ServiceResource
1497 extra = 0
1498
Scott Baker022cdcd2015-02-18 15:50:11 -08001499class ServiceClassAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001500 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1501 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001502 inlines = [ServiceResourceInline]
1503
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001504 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1505 user_readonly_inlines = []
1506
Scott Baker022cdcd2015-02-18 15:50:11 -08001507class ReservedResourceInline(XOSTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001508 model = ReservedResource
1509 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001510 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001511
1512 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1513 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1514
1515 if db_field.name == 'resource':
1516 # restrict resources to those that the slice's service class allows
1517 if request._slice is not None:
1518 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1519 if len(field.queryset) > 0:
1520 field.initial = field.queryset.all()[0]
S.Çağlar Onur0e591832015-02-24 17:28:09 -05001521 else:
1522 field.queryset = field.queryset.none()
Tony Mack3de59e32015-08-19 11:58:18 -04001523 elif db_field.name == 'instance':
1524 # restrict instances to those that belong to the slice
S.Çağlar Onur0e591832015-02-24 17:28:09 -05001525 if request._slice is not None:
Scott Baker133c9212013-05-17 09:09:11 -07001526 field.queryset = field.queryset.filter(slice = request._slice)
1527 else:
S.Çağlar Onur0e591832015-02-24 17:28:09 -05001528 field.queryset = field.queryset.none()
1529
Scott Baker133c9212013-05-17 09:09:11 -07001530 return field
1531
Tony Mack5b061472014-02-04 07:57:10 -05001532 def queryset(self, request):
1533 return ReservedResource.select_by_user(request.user)
1534
Scott Baker133c9212013-05-17 09:09:11 -07001535class ReservationChangeForm(forms.ModelForm):
1536 class Meta:
1537 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001538 widgets = {
1539 'slice' : LinkedSelect
1540 }
Scott Baker133c9212013-05-17 09:09:11 -07001541
1542class ReservationAddForm(forms.ModelForm):
1543 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1544 refresh = forms.CharField(widget=forms.HiddenInput())
1545
1546 class Media:
Scott Baker97468b72015-02-18 15:15:58 -08001547 css = {'all': ('xos.css',)} # .field-refresh { display: none; }
Scott Baker133c9212013-05-17 09:09:11 -07001548
1549 def clean_slice(self):
1550 slice = self.cleaned_data.get("slice")
1551 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1552 if len(x) == 0:
1553 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1554 return slice
1555
1556 class Meta:
1557 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001558 widgets = {
1559 'slice' : LinkedSelect
1560 }
1561
Scott Baker133c9212013-05-17 09:09:11 -07001562
1563class ReservationAddRefreshForm(ReservationAddForm):
1564 """ This form is displayed when the Reservation Form receives an update
1565 from the Slice dropdown onChange handler. It doesn't validate the
1566 data and doesn't save the data. This will cause the form to be
1567 redrawn.
1568 """
1569
Scott Baker8737e5f2013-05-17 09:35:32 -07001570 """ don't validate anything other than slice """
1571 dont_validate_fields = ("startTime", "duration")
1572
Scott Baker133c9212013-05-17 09:09:11 -07001573 def full_clean(self):
1574 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001575
1576 for fieldname in self.dont_validate_fields:
1577 if fieldname in self._errors:
1578 del self._errors[fieldname]
1579
Scott Baker133c9212013-05-17 09:09:11 -07001580 return result
1581
1582 """ don't save anything """
1583 def is_valid(self):
1584 return False
1585
Scott Baker022cdcd2015-02-18 15:50:11 -08001586class ReservationAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001587 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001588 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001589 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001590 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001591 form = ReservationAddForm
1592
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001593 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1594
1595 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001596 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001597
Scott Baker133c9212013-05-17 09:09:11 -07001598 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001599 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001600 request._refresh = False
1601 request._slice = None
1602 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001603 # "refresh" will be set to "1" if the form was submitted due to
1604 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001605 if request.POST.get("refresh","1") == "1":
1606 request._refresh = True
1607 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001608
1609 # Keep track of the slice that was selected, so the
1610 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001611 request._slice = request.POST.get("slice",None)
1612 if (request._slice is not None):
1613 request._slice = Slice.objects.get(id=request._slice)
1614
1615 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1616 return result
1617
Scott Bakeracd45142013-05-19 16:19:16 -07001618 def changelist_view(self, request, extra_context = None):
1619 timezone.activate(request.user.timezone)
1620 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1621
Scott Baker133c9212013-05-17 09:09:11 -07001622 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001623 request._obj_ = obj
1624 if obj is not None:
1625 # For changes, set request._slice to the slice already set in the
1626 # object.
1627 request._slice = obj.slice
1628 self.form = ReservationChangeForm
1629 else:
1630 if getattr(request, "_refresh", False):
1631 self.form = ReservationAddRefreshForm
1632 else:
1633 self.form = ReservationAddForm
1634 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1635
Scott Baker133c9212013-05-17 09:09:11 -07001636 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001637 if (obj is not None):
1638 # Prevent slice from being changed after the reservation has been
1639 # created.
1640 return ['slice']
1641 else:
Scott Baker133c9212013-05-17 09:09:11 -07001642 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001643
Tony Mack5b061472014-02-04 07:57:10 -05001644 def queryset(self, request):
1645 return Reservation.select_by_user(request.user)
1646
Scott Baker022cdcd2015-02-18 15:50:11 -08001647class NetworkParameterTypeAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001648 list_display = ("backend_status_icon", "name", )
1649 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001650 user_readonly_fields = ['name']
1651 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001652
Scott Baker022cdcd2015-02-18 15:50:11 -08001653class RouterAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001654 list_display = ("backend_status_icon", "name", )
1655 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001656 user_readonly_fields = ['name']
1657 user_readonly_inlines = []
1658
Scott Baker022cdcd2015-02-18 15:50:11 -08001659class RouterInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001660 model = Router.networks.through
1661 extra = 0
1662 verbose_name_plural = "Routers"
1663 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001664 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001665
Scott Bakerb27b62c2014-08-15 16:29:16 -07001666class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001667 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001668 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001669 verbose_name_plural = "Parameters"
1670 verbose_name = "Parameter"
1671 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001672 fields = ['backend_status_icon', 'parameter', 'value']
1673 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001674
Scott Baker0bdb6a52015-08-25 18:00:15 -07001675class NetworkPortInline(XOSTabularInline):
Tony Mackea30da82015-09-10 21:58:15 +00001676 fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
Scott Baker0672e982015-09-08 18:22:15 -07001677 readonly_fields = ("backend_status_icon", "ip", "mac")
Scott Baker43facad2015-08-26 09:43:33 -07001678 model = Port
Tony Mack3de59e32015-08-19 11:58:18 -04001679 selflink_fieldname = "instance"
Scott Baker74d8e622013-07-29 16:04:22 -07001680 extra = 0
Scott Bakera68e6e32015-08-25 17:11:30 -07001681 verbose_name_plural = "Ports"
1682 verbose_name = "Port"
1683 suit_classes = 'suit-tab suit-tab-ports'
Scott Baker74d8e622013-07-29 16:04:22 -07001684
Scott Baker022cdcd2015-02-18 15:50:11 -08001685class NetworkSlicesInline(XOSTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001686 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001687 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001688 extra = 0
1689 verbose_name_plural = "Slices"
1690 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001691 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001692 fields = ['backend_status_icon', 'network','slice']
1693 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001694
Scott Baker022cdcd2015-02-18 15:50:11 -08001695class ControllerNetworkInline(XOSTabularInline):
Tony Macka7dbd422015-01-05 22:48:11 -05001696 model = ControllerNetwork
Scott Bakerfbb45862014-10-17 16:27:23 -07001697 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001698 verbose_name_plural = "Controller Networks"
1699 verbose_name = "Controller Network"
Scott Bakerfbb45862014-10-17 16:27:23 -07001700 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001701 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Bakerfbb45862014-10-17 16:27:23 -07001702 readonly_fields = ('backend_status_icon', )
1703
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001704class NetworkForm(forms.ModelForm):
1705 class Meta:
1706 model = Network
1707 widgets = {
1708 'topologyParameters': UploadTextareaWidget,
1709 'controllerParameters': UploadTextareaWidget,
1710 }
1711
Scott Baker022cdcd2015-02-18 15:50:11 -08001712class NetworkAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001713 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1714 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001715 readonly_fields = ("subnet", )
Scott Baker0bdb6a52015-08-25 18:00:15 -07001716 inlines = [NetworkParameterInline, NetworkPortInline, NetworkSlicesInline, RouterInline]
Tony Macka7dbd422015-01-05 22:48:11 -05001717 admin_inlines = [ControllerNetworkInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001718
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001719 form=NetworkForm
1720
Siobhan Tully2d95e482013-09-06 10:56:06 -04001721 fieldsets = [
Scott Bakera4c11bd2015-08-21 16:40:53 -07001722 (None, {'fields': ['backend_status_text', 'name','template','ports','labels',
1723 'owner','guaranteed_bandwidth', 'permit_all_slices',
1724 'permitted_slices','network_id','router_id','subnet_id',
1725 'subnet', 'autoconnect'],
Scott Baker40248712014-11-17 16:04:45 -08001726 'classes':['suit-tab suit-tab-general']}),
Scott Baker0451fb62015-01-03 12:29:29 -08001727 (None, {'fields': ['topology_parameters', 'controller_url', 'controller_parameters'],
Scott Baker40248712014-11-17 16:04:45 -08001728 'classes':['suit-tab suit-tab-sdn']}),
1729 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001730
Scott Baker40c00762014-08-21 16:55:59 -07001731 readonly_fields = ('backend_status_text', )
Scott Bakera4c11bd2015-08-21 16:40:53 -07001732 user_readonly_fields = ['name','template','ports','labels','owner','guaranteed_bandwidth',
1733 'permit_all_slices','permitted_slices','network_id','router_id',
1734 'subnet_id','subnet','autoconnect']
Siobhan Tully2d95e482013-09-06 10:56:06 -04001735
Scott Bakerfbb45862014-10-17 16:27:23 -07001736 @property
1737 def suit_form_tabs(self):
1738 tabs=[('general','Network Details'),
Scott Baker40248712014-11-17 16:04:45 -08001739 ('sdn', 'SDN Configuration'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001740 ('netparams', 'Parameters'),
Scott Bakera68e6e32015-08-25 17:11:30 -07001741 ('ports','Ports'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001742 ('networkslices','Slices'),
1743 ('routers','Routers'),
1744 ]
1745
1746 request=getattr(_thread_locals, "request", None)
1747 if request and request.user.is_admin:
1748 tabs.append( ('admin-only', 'Admin-Only') )
1749
1750 return tabs
1751
1752
Scott Baker022cdcd2015-02-18 15:50:11 -08001753class NetworkTemplateAdmin(XOSBaseAdmin):
Scott Baker81fa17f2015-01-03 12:03:38 -08001754 list_display = ("backend_status_icon", "name", "guaranteed_bandwidth", "visibility")
Scott Baker63d1a552014-08-21 15:19:07 -07001755 list_display_links = ('backend_status_icon', 'name', )
Scott Baker81fa17f2015-01-03 12:03:38 -08001756 user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001757 user_readonly_inlines = []
Scott Baker40248712014-11-17 16:04:45 -08001758 fieldsets = [
Scott Baker81fa17f2015-01-03 12:03:38 -08001759 (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 -08001760 'classes':['suit-tab suit-tab-general']}),]
1761 suit_form_tabs = (('general','Network Template Details'), )
Scott Baker74d8e622013-07-29 16:04:22 -07001762
Scott Baker022cdcd2015-02-18 15:50:11 -08001763class FlavorAdmin(XOSBaseAdmin):
Scott Baker37b47902014-09-02 14:37:41 -07001764 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1765 list_display_links = ("backend_status_icon", "name")
1766 user_readonly_fields = ("name", "flavor")
1767 fields = ("name", "description", "flavor", "order", "default")
1768
Tony Mack31c2b8f2013-04-26 20:01:42 -04001769# register a signal that caches the user's credentials when they log in
1770def cache_credentials(sender, user, request, **kwds):
1771 auth = {'username': request.POST['username'],
1772 'password': request.POST['password']}
1773 request.session['auth'] = auth
1774user_logged_in.connect(cache_credentials)
1775
Scott Baker15cddfa2013-12-09 13:45:19 -08001776def dollar_field(fieldName, short_description):
1777 def newFunc(self, obj):
1778 try:
1779 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1780 except:
1781 x=getattr(obj, fieldName, 0.0)
1782 return x
1783 newFunc.short_description = short_description
1784 return newFunc
1785
1786def right_dollar_field(fieldName, short_description):
1787 def newFunc(self, obj):
1788 try:
1789 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1790 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1791 except:
1792 x=getattr(obj, fieldName, 0.0)
1793 return x
1794 newFunc.short_description = short_description
1795 newFunc.allow_tags = True
1796 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001797
Scott Baker022cdcd2015-02-18 15:50:11 -08001798class InvoiceChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001799 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001800 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001801 verbose_name_plural = "Charges"
1802 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001803 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001804 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1805 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1806 can_delete = False
1807 max_num = 0
1808
1809 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001810
1811class InvoiceAdmin(admin.ModelAdmin):
1812 list_display = ("date", "account")
1813
1814 inlines = [InvoiceChargeInline]
1815
Scott Baker9cb88a22013-12-09 18:56:00 -08001816 fields = ["date", "account", "dollar_amount"]
1817 readonly_fields = ["date", "account", "dollar_amount"]
1818
1819 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001820
Scott Baker022cdcd2015-02-18 15:50:11 -08001821class InvoiceInline(XOSTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001822 model = Invoice
1823 extra = 0
1824 verbose_name_plural = "Invoices"
1825 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001826 fields = ["date", "dollar_amount"]
1827 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001828 suit_classes = 'suit-tab suit-tab-accountinvoice'
1829 can_delete=False
1830 max_num=0
1831
1832 dollar_amount = right_dollar_field("amount", "Amount")
1833
Scott Baker022cdcd2015-02-18 15:50:11 -08001834class PendingChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001835 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001836 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001837 verbose_name_plural = "Charges"
1838 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001839 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001840 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1841 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001842 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001843 can_delete=False
1844 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001845
1846 def queryset(self, request):
1847 qs = super(PendingChargeInline, self).queryset(request)
1848 qs = qs.filter(state="pending")
1849 return qs
1850
Scott Baker15cddfa2013-12-09 13:45:19 -08001851 dollar_amount = right_dollar_field("amount", "Amount")
1852
Scott Baker022cdcd2015-02-18 15:50:11 -08001853class PaymentInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001854 model=Payment
1855 extra = 1
1856 verbose_name_plural = "Payments"
1857 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001858 fields = ["date", "dollar_amount"]
1859 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001860 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001861 can_delete=False
1862 max_num=0
1863
1864 dollar_amount = right_dollar_field("amount", "Amount")
1865
Scott Baker43105042013-12-06 23:23:36 -08001866class AccountAdmin(admin.ModelAdmin):
1867 list_display = ("site", "balance_due")
1868
1869 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1870
1871 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001872 (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 -08001873
Scott Baker15cddfa2013-12-09 13:45:19 -08001874 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001875
1876 suit_form_tabs =(
1877 ('general','Account Details'),
1878 ('accountinvoice', 'Invoices'),
1879 ('accountpayments', 'Payments'),
1880 ('accountpendingcharges','Pending Charges'),
1881 )
1882
Scott Baker15cddfa2013-12-09 13:45:19 -08001883 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1884 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1885 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1886
Scott Baker2461bec2015-08-14 09:10:11 -07001887class ProgramForm(forms.ModelForm):
1888 class Meta:
1889 model = Program
1890 widgets = {
1891 'contents': UploadTextareaWidget(attrs={'rows': 20, 'cols': 80, 'class': "input-xxlarge"}),
1892 'description': forms.Textarea(attrs={'rows': 3, 'cols': 80, 'class': 'input-xxlarge'}),
1893 'messages': forms.Textarea(attrs={'rows': 20, 'cols': 80, 'class': 'input-xxlarge'}),
1894 'output': forms.Textarea(attrs={'rows': 3, 'cols': 80, 'class': 'input-xxlarge'})
1895 }
1896
1897class ProgramAdmin(XOSBaseAdmin):
1898 list_display = ("name", "status")
1899 list_display_links = ('name', "status")
1900
1901 form=ProgramForm
1902
1903 fieldsets = [
1904 (None, {'fields': ['name', 'command', 'kind', 'description', 'output', 'status'],
1905 'classes':['suit-tab suit-tab-general']}),
1906 (None, {'fields': ['contents'],
1907 'classes':['suit-tab suit-tab-contents']}),
1908 (None, {'fields': ['messages'],
1909 'classes':['suit-tab suit-tab-messages']}),
1910 ]
1911
1912 readonly_fields = ("status",)
1913
1914 @property
1915 def suit_form_tabs(self):
1916 tabs=[('general','Program Details'),
1917 ('contents','Program Source'),
1918 ('messages','Messages'),
1919 ]
1920
1921 request=getattr(_thread_locals, "request", None)
1922 if request and request.user.is_admin:
1923 tabs.append( ('admin-only', 'Admin-Only') )
1924
1925 return tabs
1926
Siobhan Tully53437282013-04-26 19:30:27 -04001927# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001928admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001929# ... and, since we're not using Django's builtin permissions,
1930# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001931#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001932
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001933#Do not show django evolution in the admin interface
1934from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001935#admin.site.unregister(Version)
1936#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001937
1938
1939# When debugging it is often easier to see all the classes, but for regular use
1940# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001941showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001942
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001943admin.site.register(Deployment, DeploymentAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001944admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001945admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001946admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001947admin.site.register(Service, ServiceAdmin)
Tony Mack598eaf22015-01-25 12:35:29 -05001948#admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001949admin.site.register(Network, NetworkAdmin)
1950admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001951admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Scott Baker2461bec2015-08-14 09:10:11 -07001952admin.site.register(Program, ProgramAdmin)
Tony Mack598eaf22015-01-25 12:35:29 -05001953#admin.site.register(Account, AccountAdmin)
1954#admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001955
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001956if True:
1957 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1958 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001959 admin.site.register(Tag, TagAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001960 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001961 admin.site.register(SiteRole)
1962 admin.site.register(SliceRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001963 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001964 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1965 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Tony Mack3de59e32015-08-19 11:58:18 -04001966 admin.site.register(Instance, InstanceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001967 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001968 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001969 admin.site.register(Flavor, FlavorAdmin)
Scott Baker1b06b6c2015-07-06 14:40:20 -07001970 admin.site.register(TenantRoot, TenantRootAdmin)
1971 admin.site.register(TenantRootRole, TenantRootRoleAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001972