blob: dcee1b7db6757a946ebe1eb8248b3f5531ef23f6 [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
Siobhan Tully4bc09f22013-04-10 21:15:21 -040010from django.contrib.admin.widgets import FilteredSelectMultiple
Siobhan Tully53437282013-04-26 19:30:27 -040011from django.contrib.auth.forms import ReadOnlyPasswordHashField
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
Scott Bakere2bbf7e2014-01-13 12:09:31 -080017from django.core.urlresolvers import reverse, NoReverseMatch
Tony Mack7130ac32013-03-22 21:58:00 -040018
Siobhan Tullyde5450d2013-06-21 11:35:33 -040019import django_evolution
Siobhan Tully4bc09f22013-04-10 21:15:21 -040020
Siobhan Tullycf04fb62014-01-11 11:25:57 -050021class ReadOnlyAwareAdmin(admin.ModelAdmin):
22
23 def has_add_permission(self, request, obj=None):
24 return (not self.__user_is_readonly(request))
25
26 def has_delete_permission(self, request, obj=None):
27 return (not self.__user_is_readonly(request))
28
29 def save_model(self, request, obj, form, change):
30 if self.__user_is_readonly(request):
31 raise PermissionDenied
32 #pass
33 else:
34 return super(ReadOnlyAwareAdmin, self).save_model(request, obj, form, change)
35
36 def get_actions(self,request):
37 actions = super(ReadOnlyAwareAdmin,self).get_actions(request)
38
39 if self.__user_is_readonly(request):
40 if 'delete_selected' in actions:
41 del actions['delete_selected']
42
43 return actions
44
45 def change_view(self,request,object_id, extra_context=None):
Siobhan Tullycf04fb62014-01-11 11:25:57 -050046 if self.__user_is_readonly(request):
Scott Bakeraf73e102014-04-22 22:40:07 -070047 if not hasattr(self, "readonly_save"):
48 # save the original readonly fields
49 self.readonly_save = self.readonly_fields
50 self.inlines_save = self.inlines
Scott Bakere8859f92014-05-23 12:42:40 -070051 if hasattr(self, "user_readonly_fields"):
52 self.readonly_fields=self.user_readonly_fields
53 if hasattr(self, "user_readonly_inlines"):
54 self.inlines = self.user_readonly_inlines
Scott Bakeraf73e102014-04-22 22:40:07 -070055 else:
56 if hasattr(self, "readonly_save"):
57 # restore the original readonly fields
58 self.readonly_fields = self.readonly_save
Scott Bakere8859f92014-05-23 12:42:40 -070059 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -070060 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -050061
62 try:
63 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
64 except PermissionDenied:
65 pass
66 if request.method == 'POST':
67 raise PermissionDenied
68 request.readonly = True
69 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
70
Siobhan Tullycf04fb62014-01-11 11:25:57 -050071 def __user_is_readonly(self, request):
72 return request.user.isReadOnlyUser()
73
Scott Bakere8859f92014-05-23 12:42:40 -070074class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -040075 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -070076 if not super(SingletonAdmin, self).has_add_permission(request):
77 return False
78
Siobhan Tullyce652d02013-10-08 21:52:35 -040079 num_objects = self.model.objects.count()
80 if num_objects >= 1:
81 return False
82 else:
83 return True
84
85
Siobhan Tullyd3515752013-06-21 16:34:53 -040086class PlStackTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -080087 def __init__(self, *args, **kwargs):
88 super(PlStackTabularInline, self).__init__(*args, **kwargs)
89
90 # InlineModelAdmin as no get_fields() method, so in order to add
91 # the selflink field, we override __init__ to modify self.fields and
92 # self.readonly_fields.
93
Scott Bakere2bbf7e2014-01-13 12:09:31 -080094 self.setup_selflink()
95
Scott Baker874936e2014-01-13 18:15:34 -080096 def get_change_url(self, model, id):
97 """ Get the URL to a change form in the admin for this model """
98 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -080099 try:
Scott Baker874936e2014-01-13 18:15:34 -0800100 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800101 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800102 return None
103
104 return url
105
106 def setup_selflink(self):
107 if hasattr(self, "selflink_fieldname"):
108 """ self.selflink_model can be defined to punch through a relation
109 to its target object. For example, in SliceNetworkInline, set
110 selflink_model = "network", and the URL will lead to the Network
111 object instead of trying to bring up a change view of the
112 SliceNetwork object.
113 """
114 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
115 else:
116 self.selflink_model = self.model
117
118 url = self.get_change_url(self.selflink_model, 0)
119
120 # We don't have an admin for this object, so don't create the
121 # selflink.
122 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800123 return
124
Scott Baker874936e2014-01-13 18:15:34 -0800125 # Since we need to add "selflink" to the field list, we need to create
126 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800127 if (self.fields is None):
128 self.fields = []
129 for f in self.model._meta.fields:
130 if f.editable and f.name != "id":
131 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800132
Scott Baker874936e2014-01-13 18:15:34 -0800133 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800134
Scott Baker874936e2014-01-13 18:15:34 -0800135 if self.readonly_fields is None:
136 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800137
Scott Baker874936e2014-01-13 18:15:34 -0800138 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800139
140 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800141 if hasattr(self, "selflink_fieldname"):
142 obj = getattr(obj, self.selflink_fieldname)
143
Scott Baker86568322014-01-12 16:53:31 -0800144 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800145 url = self.get_change_url(self.selflink_model, obj.id)
146 return "<a href='%s'>Details</a>" % str(url)
Scott Baker86568322014-01-12 16:53:31 -0800147 else:
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800148 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800149
150 selflink.allow_tags = True
151 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400152
Scott Bakerb27b62c2014-08-15 16:29:16 -0700153 def has_add_permission(self, request):
154 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500155
156 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700157 readonly_fields = list(self.readonly_fields)[:]
158 if request.user.isReadOnlyUser():
159 for field in self.fields:
160 if not field in readonly_fields:
161 readonly_fields.append(field)
162 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500163
Scott Bakerb27b62c2014-08-15 16:29:16 -0700164class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500165 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700166 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500167
Scott Bakerb27b62c2014-08-15 16:29:16 -0700168 def get_readonly_fields(self, request, obj=None):
169 readonly_fields = list(self.readonly_fields)[:]
170 if request.user.isReadOnlyUser():
171 for field in self.fields:
172 if not field in readonly_fields:
173 readonly_fields.append(field)
174 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500175
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400176class ReservationInline(PlStackTabularInline):
177 model = Reservation
178 extra = 0
179 suit_classes = 'suit-tab suit-tab-reservations'
Tony Mack5b061472014-02-04 07:57:10 -0500180
181 def queryset(self, request):
182 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400183
Scott Bakerb27b62c2014-08-15 16:29:16 -0700184class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400185 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400186 extra = 0
187 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500188 fields = ['service', 'name', 'value']
189
190 def queryset(self, request):
191 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400192
Scott Baker74d8e622013-07-29 16:04:22 -0700193class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400194 """ This is a callable that looks up a network name in a sliver and returns
195 the ip address for that network.
196 """
197
Scott Baker434ca7e2014-08-15 12:29:20 -0700198 byNetworkName = {} # class variable
199
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400200 def __init__(self, name):
201 self.short_description = name
202 self.__name__ = name
203 self.network_name = name
204
205 def __call__(self, obj):
206 if obj is not None:
207 for nbs in obj.networksliver_set.all():
208 if (nbs.network.name == self.network_name):
209 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700210 return ""
211
212 def __str__(self):
213 return self.network_name
214
Scott Baker434ca7e2014-08-15 12:29:20 -0700215 @staticmethod
216 def get(network_name):
217 """ We want to make sure we alwars return the same NetworkLookerUpper
218 because sometimes django will cause them to be instantiated multiple
219 times (and we don't want different ones in form.fields vs
220 SliverInline.readonly_fields).
221 """
222 if network_name not in NetworkLookerUpper.byNetworkName:
223 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
224 return NetworkLookerUpper.byNetworkName[network_name]
225
Siobhan Tullyd3515752013-06-21 16:34:53 -0400226class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400227 model = Sliver
Scott Baker434ca7e2014-08-15 12:29:20 -0700228 fields = ['all_ips_string', 'instance_name', 'slice', 'numberCores', 'deploymentNetwork', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400229 extra = 0
Scott Baker434ca7e2014-08-15 12:29:20 -0700230 readonly_fields = ['all_ips_string', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400231 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700232
Tony Mack5b061472014-02-04 07:57:10 -0500233 def queryset(self, request):
234 return Sliver.select_by_user(request.user)
235
Scott Bakerb24cc932014-06-09 10:51:16 -0700236 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Scott Bakerb24cc932014-06-09 10:51:16 -0700237 if db_field.name == 'deploymentNetwork':
Scott Baker3b678742014-06-09 13:11:54 -0700238 kwargs['queryset'] = Deployment.select_by_acl(request.user)
Scott Baker34b502f2014-08-05 18:33:31 -0700239 # the inscrutable jquery selector below says:
240 # find the closest parent "tr" to the current element
241 # then find the child with class "field-node"
242 # then find the child with that is a select
243 # then return its id
Scott Bakerdf65d882014-08-05 18:52:14 -0700244 kwargs['widget'] = forms.Select(attrs={'onChange': "update_nodes(this, $($(this).closest('tr')[0]).find('.field-node select')[0].id)"})
Scott Baker34b502f2014-08-05 18:33:31 -0700245 #kwargs['widget'] = forms.Select(attrs={'onChange': "console.log($($($(this).closest('tr')[0]).children('.field-node')[0]).children('select')[0].id);"})
Scott Baker3b678742014-06-09 13:11:54 -0700246
247 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700248
249 return field
250
Scott Baker434ca7e2014-08-15 12:29:20 -0700251"""
252 SMBAKER: This is the old code that implemented each network type as a
253 separate column in the sliver table.
Scott Baker74d8e622013-07-29 16:04:22 -0700254
Scott Baker434ca7e2014-08-15 12:29:20 -0700255 def _declared_fieldsets(self):
256 # Return None so django will call get_fieldsets and we can insert our
257 # dynamic fields
258 return None
259
260 def get_readonly_fields(self, request, obj=None):
261 readonly_fields = list(super(SliverInline, self).get_readonly_fields(request, obj))
262
263 # Lookup the networks that are bound to the slivers, and add those
264 # network names to the list of readonly fields.
265
266 for sliver in obj.slivers.all():
267 for nbs in sliver.networksliver_set.all():
268 if nbs.ip:
269 network_name = nbs.network.name
270 if network_name not in [str(x) for x in readonly_fields]:
271 readonly_fields.append(NetworkLookerUpper.get(network_name))
272
273 return readonly_fields
274
275 def get_fieldsets(self, request, obj=None):
276 form = self.get_formset(request, obj).form
277 # fields = the read/write files + the read-only fields
278 fields = list(self.fields)
279 for fieldName in self.get_readonly_fields(request,obj):
280 if not fieldName in fields:
281 fields.append(fieldName)
282
283 return [(None, {'fields': fields})]
284"""
Siobhan Tully567e3e62013-06-21 18:03:16 -0400285
Siobhan Tullyd3515752013-06-21 16:34:53 -0400286class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400287 model = Site
288 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400289 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400290
Tony Mack5b061472014-02-04 07:57:10 -0500291 def queryset(self, request):
292 return Site.select_by_user(request.user)
293
Siobhan Tullyd3515752013-06-21 16:34:53 -0400294class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400295 model = User
Siobhan Tully47ae1b52013-05-10 15:53:14 -0400296 fields = ['email', 'firstname', 'lastname']
Siobhan Tully30fd4292013-05-10 08:59:56 -0400297 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400298 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400299
Tony Mack5b061472014-02-04 07:57:10 -0500300 def queryset(self, request):
301 return User.select_by_user(request.user)
302
Siobhan Tullyd3515752013-06-21 16:34:53 -0400303class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400304 model = Slice
Siobhan Tullyce652d02013-10-08 21:52:35 -0400305 fields = ['name','site', 'serviceClass', 'service']
Tony Mack00d361f2013-04-28 10:28:42 -0400306 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400307 suit_classes = 'suit-tab suit-tab-slices'
308
Tony Mack5b061472014-02-04 07:57:10 -0500309 def queryset(self, request):
310 return Slice.select_by_user(request.user)
311
Siobhan Tullyd3515752013-06-21 16:34:53 -0400312class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400313 model = Node
314 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400315 suit_classes = 'suit-tab suit-tab-nodes'
Scott Baker838d7df2014-06-09 11:01:16 -0700316 fields = ['name','deployment','site']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400317
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400318class DeploymentPrivilegeInline(PlStackTabularInline):
319 model = DeploymentPrivilege
320 extra = 0
321 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Scott Baker3ca51f62014-05-23 12:05:11 -0700322 fields = ['user','role','deployment']
Tony Mack5b061472014-02-04 07:57:10 -0500323
324 def queryset(self, request):
325 return DeploymentPrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400326
Siobhan Tullyd3515752013-06-21 16:34:53 -0400327class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400328 model = SitePrivilege
329 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400330 suit_classes = 'suit-tab suit-tab-siteprivileges'
Tony Mack5b061472014-02-04 07:57:10 -0500331 fields = ['user','site', 'role']
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400332
Tony Mackc2835a92013-05-28 09:18:49 -0400333 def formfield_for_foreignkey(self, db_field, request, **kwargs):
334 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500335 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400336
337 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500338 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400339 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
340
Tony Mack5b061472014-02-04 07:57:10 -0500341 def queryset(self, request):
342 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400343
Tony Macke4be32f2014-03-11 20:45:25 -0400344class SiteDeploymentInline(PlStackTabularInline):
345 model = SiteDeployments
346 #model = Site.deployments.through
347 extra = 0
348 suit_classes = 'suit-tab suit-tab-deployments'
349 fields = ['deployment','site']
350
351 def formfield_for_foreignkey(self, db_field, request, **kwargs):
352 if db_field.name == 'site':
353 kwargs['queryset'] = Site.select_by_user(request.user)
354
355 if db_field.name == 'deployment':
356 kwargs['queryset'] = Deployment.select_by_user(request.user)
357 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
358
359 def queryset(self, request):
360 return SiteDeployments.select_by_user(request.user)
361
362
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400363class SlicePrivilegeInline(PlStackTabularInline):
364 model = SlicePrivilege
365 suit_classes = 'suit-tab suit-tab-sliceprivileges'
366 extra = 0
367 fields = ('user', 'slice','role')
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400368
Tony Mackc2835a92013-05-28 09:18:49 -0400369 def formfield_for_foreignkey(self, db_field, request, **kwargs):
370 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500371 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400372 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500373 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400374
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400375 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400376
Tony Mack5b061472014-02-04 07:57:10 -0500377 def queryset(self, request):
378 return SlicePrivilege.select_by_user(request.user)
379
Scott Bakera0015eb2013-08-14 17:28:14 -0700380class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700381 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800382 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700383 extra = 0
384 verbose_name = "Network Connection"
385 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400386 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker2170b972014-06-03 12:14:07 -0700387 fields = ['network']
388
389class ImageDeploymentsInline(PlStackTabularInline):
390 model = ImageDeployments
391 extra = 0
392 verbose_name = "Image Deployments"
393 verbose_name_plural = "Image Deployments"
394 suit_classes = 'suit-tab suit-tab-imagedeployments'
Scott Bakerb6f99242014-06-11 11:34:44 -0700395 fields = ['image', 'deployment', 'glance_image_id']
396 readonly_fields = ['glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700397
Tony Mack5e71a662013-05-03 23:30:41 -0400398class PlainTextWidget(forms.HiddenInput):
399 input_type = 'hidden'
400
401 def render(self, name, value, attrs=None):
402 if value is None:
403 value = ''
Tony Mack1d6b85f2013-05-07 18:49:14 -0400404 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400405
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500406class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400407 save_on_top = False
Tony Mack332ee1d2014-02-04 15:33:45 -0500408
409 def save_model(self, request, obj, form, change):
Tony Mack3d042792014-03-17 19:18:37 -0400410 obj.caller = request.user
Tony Mack332ee1d2014-02-04 15:33:45 -0500411 # update openstack connection to use this site/tenant
412 obj.save_by_user(request.user)
413
414 def delete_model(self, request, obj):
415 obj.delete_by_user(request.user)
416
417 def save_formset(self, request, form, formset, change):
418 instances = formset.save(commit=False)
419 for instance in instances:
420 instance.save_by_user(request.user)
421 formset.save_m2m()
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400422
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400423class SliceRoleAdmin(PlanetStackBaseAdmin):
424 model = SliceRole
425 pass
426
427class SiteRoleAdmin(PlanetStackBaseAdmin):
428 model = SiteRole
429 pass
430
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400431class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400432 sites = forms.ModelMultipleChoiceField(
433 queryset=Site.objects.all(),
434 required=False,
Scott Baker70983182014-06-09 22:10:00 -0700435 help_text="Select which sites are allowed to host nodes in this deployment",
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400436 widget=FilteredSelectMultiple(
437 verbose_name=('Sites'), is_stacked=False
438 )
439 )
Scott Bakerde0f4412014-06-11 15:40:26 -0700440 images = forms.ModelMultipleChoiceField(
441 queryset=Image.objects.all(),
442 required=False,
443 help_text="Select which images should be deployed on this deployment",
444 widget=FilteredSelectMultiple(
445 verbose_name=('Images'), is_stacked=False
446 )
447 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400448 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400449 model = Deployment
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400450
Siobhan Tully320b4622014-01-17 15:11:14 -0500451 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700452 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500453 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
454
Scott Baker5380c522014-06-06 14:49:43 -0700455 self.fields['accessControl'].initial = "allow site " + request.user.site.name
456
Siobhan Tully320b4622014-01-17 15:11:14 -0500457 if self.instance and self.instance.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700458 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
Scott Bakerde0f4412014-06-11 15:40:26 -0700459 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()]
460
461 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
462 """ helper function for handling m2m relations from the MultipleChoiceField
463
464 this_obj: the source object we want to link from
465
466 selected_objs: a list of destination objects we want to link to
467
468 all_relations: the full set of relations involving this_obj, including ones we don't want
469
470 relation_class: the class that implements the relation from source to dest
471
472 local_attrname: field name representing this_obj in relation_class
473
474 foreign_attrname: field name representing selected_objs in relation_class
475
476 This function will remove all newobjclass relations from this_obj
477 that are not contained in selected_objs, and add any relations that
478 are in selected_objs but don't exist in the data model yet.
479 """
480
481 existing_dest_objs = []
482 for relation in list(all_relations):
483 if getattr(relation, foreign_attrname) not in selected_objs:
484 #print "deleting site", sdp.site
485 relation.delete()
486 else:
487 existing_dest_objs.append(getattr(relation, foreign_attrname))
488
489 for dest_obj in selected_objs:
490 if dest_obj not in existing_dest_objs:
491 #print "adding site", site
492 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
493 relation = relation_class(**kwargs)
494 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500495
496 def save(self, commit=True):
497 deployment = super(DeploymentAdminForm, self).save(commit=False)
498
499 if commit:
500 deployment.save()
501
502 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700503 # save_m2m() doesn't seem to work with 'through' relations. So we
504 # create/destroy the through models ourselves. There has to be
505 # a better way...
506
Scott Bakerde0f4412014-06-11 15:40:26 -0700507 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployments, "deployment", "site")
508 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployments, "deployment", "image")
Scott Bakerc9b14f72014-05-22 13:44:20 -0700509
Siobhan Tully320b4622014-01-17 15:11:14 -0500510 self.save_m2m()
511
512 return deployment
513
Scott Bakerff5e0f32014-05-22 14:40:27 -0700514class DeploymentAdminROForm(DeploymentAdminForm):
515 def save(self, commit=True):
516 raise PermissionDenied
517
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500518class SiteAssocInline(PlStackTabularInline):
519 model = Site.deployments.through
520 extra = 0
521 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400522
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400523class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500524 model = Deployment
Scott Bakerde0f4412014-06-11 15:40:26 -0700525 fieldList = ['name','sites', 'images', 'accessControl']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500526 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
Scott Bakerde0f4412014-06-11 15:40:26 -0700527 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentsInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500528
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500529 user_readonly_fields = ['name']
530
Scott Bakerde0f4412014-06-11 15:40:26 -0700531 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500532
Scott Bakerff5e0f32014-05-22 14:40:27 -0700533 def get_form(self, request, obj=None, **kwargs):
534 if request.user.isReadOnlyUser():
535 kwargs["form"] = DeploymentAdminROForm
536 else:
537 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700538 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
539
540 # from stackexchange: pass the request object into the form
541
542 class AdminFormMetaClass(adminForm):
543 def __new__(cls, *args, **kwargs):
544 kwargs['request'] = request
545 return adminForm(*args, **kwargs)
546
547 return AdminFormMetaClass
548
Siobhan Tullyce652d02013-10-08 21:52:35 -0400549class ServiceAttrAsTabInline(PlStackTabularInline):
550 model = ServiceAttribute
551 fields = ['name','value']
552 extra = 0
553 suit_classes = 'suit-tab suit-tab-serviceattrs'
554
Siobhan Tullyce652d02013-10-08 21:52:35 -0400555class ServiceAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500556 list_display = ("name","description","versionNumber","enabled","published")
557 fieldList = ["name","description","versionNumber","enabled","published"]
558 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
559 inlines = [ServiceAttrAsTabInline,SliceInline]
560
561 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500562
563 suit_form_tabs =(('general', 'Service Details'),
564 ('slices','Slices'),
565 ('serviceattrs','Additional Attributes'),
566 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400567
Tony Mack0553f282013-06-10 22:54:50 -0400568class SiteAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500569 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400570 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500571 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400572 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400573 ]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400574 suit_form_tabs =(('general', 'Site Details'),
575 ('users','Users'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400576 ('siteprivileges','Privileges'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400577 ('deployments','Deployments'),
578 ('slices','Slices'),
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500579 ('nodes','Nodes'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400580 ('tags','Tags'),
581 )
Scott Baker545db2a2013-12-09 18:44:43 -0800582 readonly_fields = ['accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500583
584 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500585
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400586 list_display = ('name', 'login_base','site_url', 'enabled')
587 filter_horizontal = ('deployments',)
Tony Macke4be32f2014-03-11 20:45:25 -0400588 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400589 search_fields = ['name']
590
Tony Mack04062832013-05-10 08:22:44 -0400591 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500592 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400593
Tony Mack5cd13202013-05-01 21:48:38 -0400594 def get_formsets(self, request, obj=None):
595 for inline in self.get_inline_instances(request, obj):
596 # hide MyInline in the add view
597 if obj is None:
598 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400599 if isinstance(inline, SliceInline):
600 inline.model.caller = request.user
601 yield inline.get_formset(request, obj)
602
603 def get_formsets(self, request, obj=None):
604 for inline in self.get_inline_instances(request, obj):
605 # hide MyInline in the add view
606 if obj is None:
607 continue
608 if isinstance(inline, SliverInline):
609 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400610 yield inline.get_formset(request, obj)
611
Scott Baker545db2a2013-12-09 18:44:43 -0800612 def accountLink(self, obj):
613 link_obj = obj.accounts.all()
614 if link_obj:
615 reverse_path = "admin:core_account_change"
616 url = reverse(reverse_path, args =(link_obj[0].id,))
617 return "<a href='%s'>%s</a>" % (url, "view billing details")
618 else:
619 return "no billing data for this site"
620 accountLink.allow_tags = True
621 accountLink.short_description = "Billing"
622
Tony Mack332ee1d2014-02-04 15:33:45 -0500623 def save_model(self, request, obj, form, change):
624 # update openstack connection to use this site/tenant
625 obj.save_by_user(request.user)
626
627 def delete_model(self, request, obj):
628 obj.delete_by_user(request.user)
629
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500630
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400631class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500632 fieldList = ['user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400633 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500634 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400635 ]
636 list_display = ('user', 'site', 'role')
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500637 user_readonly_fields = fieldList
638 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400639
Tony Mackc2835a92013-05-28 09:18:49 -0400640 def formfield_for_foreignkey(self, db_field, request, **kwargs):
641 if db_field.name == 'site':
642 if not request.user.is_admin:
643 # only show sites where user is an admin or pi
644 sites = set()
645 for site_privilege in SitePrivilege.objects.filer(user=request.user):
646 if site_privilege.role.role_type in ['admin', 'pi']:
647 sites.add(site_privilege.site)
648 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
649
650 if db_field.name == 'user':
651 if not request.user.is_admin:
652 # only show users from sites where caller has admin or pi role
653 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
654 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
655 sites = [site_privilege.site for site_privilege in site_privileges]
656 site_privileges = SitePrivilege.objects.filter(site__in=sites)
657 emails = [site_privilege.user.email for site_privilege in site_privileges]
658 users = User.objects.filter(email__in=emails)
659 kwargs['queryset'] = users
660
661 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
662
Tony Mack04062832013-05-10 08:22:44 -0400663 def queryset(self, request):
664 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400665 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400666 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500667 #if not request.user.is_admin:
668 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
669 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
670 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
671 # sites = Site.objects.filter(login_base__in=login_bases)
672 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400673 return qs
674
Siobhan Tullyce652d02013-10-08 21:52:35 -0400675class SliceForm(forms.ModelForm):
676 class Meta:
677 model = Slice
678 widgets = {
679 'service': LinkedSelect
680 }
681
Tony Mack2bd5b412013-06-11 21:05:06 -0400682class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400683 form = SliceForm
Tony Mack29bf5e82014-04-29 21:40:24 -0400684 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500685 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Tony Mack29bf5e82014-04-29 21:40:24 -0400686 list_display = ('name', 'site','serviceClass', 'slice_url', 'max_slivers')
Siobhan Tully2d95e482013-09-06 10:56:06 -0400687 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400688
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500689 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400690
691 suit_form_tabs =(('general', 'Slice Details'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400692 ('slicenetworks','Networks'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400693 ('sliceprivileges','Privileges'),
694 ('slivers','Slivers'),
695 ('tags','Tags'),
696 ('reservations','Reservations'),
697 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400698
Scott Baker510fdbb2014-08-05 17:19:24 -0700699 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
700 #deployment_nodes = {}
701 #for node in Node.objects.all():
702 # deployment_nodes[node.deployment.id] = get(deployment_nodes, node.deployment.id, []).append( (node.id, node.name) )
703
704 deployment_nodes = []
705 for node in Node.objects.all():
706 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
707
708 context["deployment_nodes"] = deployment_nodes
709
710 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
711
Tony Mackc2835a92013-05-28 09:18:49 -0400712 def formfield_for_foreignkey(self, db_field, request, **kwargs):
713 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500714 kwargs['queryset'] = Site.select_by_user(request.user)
715
Tony Mackc2835a92013-05-28 09:18:49 -0400716 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
717
Tony Mack04062832013-05-10 08:22:44 -0400718 def queryset(self, request):
719 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500720 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400721
Tony Mack79748612013-05-01 14:52:03 -0400722 def get_formsets(self, request, obj=None):
723 for inline in self.get_inline_instances(request, obj):
724 # hide MyInline in the add view
725 if obj is None:
726 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400727 if isinstance(inline, SliverInline):
728 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400729 yield inline.get_formset(request, obj)
730
Tony Mack2bd5b412013-06-11 21:05:06 -0400731
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400732class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400733 fieldsets = [
734 (None, {'fields': ['user', 'slice', 'role']})
735 ]
736 list_display = ('user', 'slice', 'role')
Tony Mack00d361f2013-04-28 10:28:42 -0400737
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500738 user_readonly_fields = ['user', 'slice', 'role']
739 user_readonly_inlines = []
740
Tony Mackc2835a92013-05-28 09:18:49 -0400741 def formfield_for_foreignkey(self, db_field, request, **kwargs):
742 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500743 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400744
745 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500746 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400747
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400748 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400749
Tony Mack04062832013-05-10 08:22:44 -0400750 def queryset(self, request):
751 # admins can see all memberships. Users can only see memberships of
752 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -0500753 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400754
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400755 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400756 # update openstack connection to use this site/tenant
757 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400758 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400759 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400760 obj.save()
761
762 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400763 # update openstack connection to use this site/tenant
764 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400765 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400766 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400767 obj.delete()
768
Siobhan Tully567e3e62013-06-21 18:03:16 -0400769
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400770class ImageAdmin(PlanetStackBaseAdmin):
771
772 fieldsets = [('Image Details',
Scott Baker00b00b32014-05-07 08:47:54 -0700773 {'fields': ['name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400774 'classes': ['suit-tab suit-tab-general']})
775 ]
776
Scott Baker2170b972014-06-03 12:14:07 -0700777 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400778
Scott Baker2170b972014-06-03 12:14:07 -0700779 inlines = [SliverInline, ImageDeploymentsInline]
Scott Bakerb6f99242014-06-11 11:34:44 -0700780
Tony Mack32e1ce32014-05-07 13:29:41 -0400781 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -0700782
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400783class NodeForm(forms.ModelForm):
784 class Meta:
785 widgets = {
786 'site': LinkedSelect,
787 'deployment': LinkedSelect
788 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400789
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500790class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400791 form = NodeForm
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400792 list_display = ('name', 'site', 'deployment')
793 list_filter = ('deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500794
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400795 inlines = [TagInline,SliverInline]
796 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
797
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500798 user_readonly_fields = ['name','site','deployment']
799 user_readonly_inlines = [TagInline,SliverInline]
800
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400801 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400802
Siobhan Tully567e3e62013-06-21 18:03:16 -0400803
Tony Mackd90cdbf2013-04-16 22:48:40 -0400804class SliverForm(forms.ModelForm):
805 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -0400806 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -0400807 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -0400808 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -0400809 widgets = {
810 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -0400811 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400812 'slice': LinkedSelect,
813 'deploymentNetwork': LinkedSelect,
814 'node': LinkedSelect,
815 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -0400816 }
Tony Mackd90cdbf2013-04-16 22:48:40 -0400817
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500818class TagAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400819 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500820 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
821 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -0400822
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400823class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -0400824 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -0400825 fieldsets = [
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400826 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -0400827 ]
Siobhan Tully5d7dc8d2013-07-02 13:17:33 -0400828 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400829
830 suit_form_tabs =(('general', 'Sliver Details'),
831 ('tags','Tags'),
832 )
833
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400834 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -0400835
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500836 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500837
Tony Mackc2835a92013-05-28 09:18:49 -0400838 def formfield_for_foreignkey(self, db_field, request, **kwargs):
839 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500840 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400841
842 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
843
Tony Mack04062832013-05-10 08:22:44 -0400844 def queryset(self, request):
845 # admins can see all slivers. Users can only see slivers of
846 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500847 return Sliver.select_by_user(request.user)
848
Tony Mack04062832013-05-10 08:22:44 -0400849
Tony Mack1d6b85f2013-05-07 18:49:14 -0400850 def get_formsets(self, request, obj=None):
851 # make some fields read only if we are updating an existing record
852 if obj == None:
853 #self.readonly_fields = ('ip', 'instance_name')
854 self.readonly_fields = ()
855 else:
Tony Mack1e889462013-05-10 21:34:54 -0400856 self.readonly_fields = ()
857 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400858
859 for inline in self.get_inline_instances(request, obj):
860 # hide MyInline in the add view
861 if obj is None:
862 continue
Scott Baker526b71e2014-05-13 13:18:01 -0700863 if isinstance(inline, SliverInline):
864 inline.model.caller = request.user
865 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -0400866
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500867 #def save_model(self, request, obj, form, change):
868 # # update openstack connection to use this site/tenant
869 # auth = request.session.get('auth', {})
870 # auth['tenant'] = obj.slice.name
871 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
872 # obj.creator = request.user
873 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -0400874
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500875 #def delete_model(self, request, obj):
876 # # update openstack connection to use this site/tenant
877 # auth = request.session.get('auth', {})
878 # auth['tenant'] = obj.slice.name
879 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
880 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -0400881
Siobhan Tully53437282013-04-26 19:30:27 -0400882class UserCreationForm(forms.ModelForm):
883 """A form for creating new users. Includes all the required
884 fields, plus a repeated password."""
885 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
886 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
887
888 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400889 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400890 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -0400891
892 def clean_password2(self):
893 # Check that the two password entries match
894 password1 = self.cleaned_data.get("password1")
895 password2 = self.cleaned_data.get("password2")
896 if password1 and password2 and password1 != password2:
897 raise forms.ValidationError("Passwords don't match")
898 return password2
899
900 def save(self, commit=True):
901 # Save the provided password in hashed format
902 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -0400903 user.password = self.cleaned_data["password1"]
904 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -0400905 if commit:
906 user.save()
907 return user
908
Siobhan Tully567e3e62013-06-21 18:03:16 -0400909
Siobhan Tully53437282013-04-26 19:30:27 -0400910class UserChangeForm(forms.ModelForm):
911 """A form for updating users. Includes all the fields on
912 the user, but replaces the password field with admin's
913 password hash display field.
914 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -0500915 password = ReadOnlyPasswordHashField(label='Password',
916 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -0400917
918 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400919 model = User
Siobhan Tully53437282013-04-26 19:30:27 -0400920
921 def clean_password(self):
922 # Regardless of what the user provides, return the initial value.
923 # This is done here, rather than on the field, because the
924 # field does not have access to the initial value
925 return self.initial["password"]
926
Scott Baker2c3cb642014-05-19 17:55:56 -0700927class UserDashboardViewInline(PlStackTabularInline):
928 model = UserDashboardView
929 extra = 0
930 suit_classes = 'suit-tab suit-tab-dashboards'
931 fields = ['user', 'dashboardView', 'order']
932
Tony Mack2bd5b412013-06-11 21:05:06 -0400933class UserAdmin(UserAdmin):
Siobhan Tully53437282013-04-26 19:30:27 -0400934 class Meta:
935 app_label = "core"
936
937 # The forms to add and change user instances
938 form = UserChangeForm
939 add_form = UserCreationForm
940
941 # The fields to be used in displaying the User model.
942 # These override the definitions on the base UserAdmin
943 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500944 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500945 list_filter = ('site',)
Scott Baker2c3cb642014-05-19 17:55:56 -0700946 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500947
Scott Bakeradae55f2014-08-14 17:32:35 -0700948 fieldListLoginDetails = ['email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500949 fieldListContactInfo = ['firstname','lastname','phone','timezone']
950
Siobhan Tully53437282013-04-26 19:30:27 -0400951 fieldsets = (
Scott Bakeradae55f2014-08-14 17:32:35 -0700952 ('Login Details', {'fields': ['email', 'site','password', 'is_active', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400953 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -0700954 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -0400955 #('Important dates', {'fields': ('last_login',)}),
956 )
957 add_fieldsets = (
958 (None, {
959 'classes': ('wide',),
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500960 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
Siobhan Tully53437282013-04-26 19:30:27 -0400961 ),
962 )
963 search_fields = ('email',)
964 ordering = ('email',)
965 filter_horizontal = ()
966
Scott Baker3ca51f62014-05-23 12:05:11 -0700967 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500968
Scott Baker2c3cb642014-05-19 17:55:56 -0700969 suit_form_tabs =(('general','Login Details'),
970 ('contact','Contact Information'),
971 ('sliceprivileges','Slice Privileges'),
972 ('siteprivileges','Site Privileges'),
973 ('deploymentprivileges','Deployment Privileges'),
974 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400975
Tony Mackc2835a92013-05-28 09:18:49 -0400976 def formfield_for_foreignkey(self, db_field, request, **kwargs):
977 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500978 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400979
980 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
981
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500982 def has_add_permission(self, request, obj=None):
983 return (not self.__user_is_readonly(request))
984
985 def has_delete_permission(self, request, obj=None):
986 return (not self.__user_is_readonly(request))
987
988 def get_actions(self,request):
989 actions = super(UserAdmin,self).get_actions(request)
990
991 if self.__user_is_readonly(request):
992 if 'delete_selected' in actions:
993 del actions['delete_selected']
994
995 return actions
996
997 def change_view(self,request,object_id, extra_context=None):
998
999 if self.__user_is_readonly(request):
Scott Bakerf875eba2014-05-23 12:09:15 -07001000 if not hasattr(self, "readonly_save"):
1001 # save the original readonly fields
1002 self.readonly_save = self.readonly_fields
1003 self.inlines_save = self.inlines
Scott Bakerb27b62c2014-08-15 16:29:16 -07001004 if hasattr(self, "user_readonly_fields"):
1005 self.readonly_fields=self.user_readonly_fields
1006 if hasattr(self, "user_readonly_inlines"):
1007 self.inlines = self.user_readonly_inlines
Scott Bakerf875eba2014-05-23 12:09:15 -07001008 else:
1009 if hasattr(self, "readonly_save"):
1010 # restore the original readonly fields
1011 self.readonly_fields = self.readonly_save
1012 self.inlines = self.inlines_save
1013
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001014 try:
1015 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1016 except PermissionDenied:
1017 pass
1018 if request.method == 'POST':
1019 raise PermissionDenied
1020 request.readonly = True
1021 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1022
1023 def __user_is_readonly(self, request):
1024 #groups = [x.name for x in request.user.groups.all() ]
1025 #return "readonly" in groups
1026 return request.user.isReadOnlyUser()
1027
Tony Mack5b061472014-02-04 07:57:10 -05001028 def queryset(self, request):
1029 return User.select_by_user(request.user)
1030
Scott Baker2c3cb642014-05-19 17:55:56 -07001031class DashboardViewAdmin(PlanetStackBaseAdmin):
1032 fieldsets = [('Dashboard View Details',
1033 {'fields': ['name', 'url'],
1034 'classes': ['suit-tab suit-tab-general']})
1035 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001036
Scott Baker2c3cb642014-05-19 17:55:56 -07001037 suit_form_tabs =(('general','Dashboard View Details'),)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001038
Scott Baker0165fac2014-01-13 11:49:26 -08001039class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001040 model = ServiceResource
1041 extra = 0
1042
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001043class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker3de3e372013-05-10 16:50:44 -07001044 list_display = ('name', 'commitment', 'membershipFee')
1045 inlines = [ServiceResourceInline]
1046
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001047 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1048 user_readonly_inlines = []
1049
Scott Baker0165fac2014-01-13 11:49:26 -08001050class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001051 model = ReservedResource
1052 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001053 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001054
1055 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1056 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1057
1058 if db_field.name == 'resource':
1059 # restrict resources to those that the slice's service class allows
1060 if request._slice is not None:
1061 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1062 if len(field.queryset) > 0:
1063 field.initial = field.queryset.all()[0]
1064 else:
1065 field.queryset = field.queryset.none()
1066 elif db_field.name == 'sliver':
1067 # restrict slivers to those that belong to the slice
1068 if request._slice is not None:
1069 field.queryset = field.queryset.filter(slice = request._slice)
1070 else:
1071 field.queryset = field.queryset.none()
1072
1073 return field
1074
Tony Mack5b061472014-02-04 07:57:10 -05001075 def queryset(self, request):
1076 return ReservedResource.select_by_user(request.user)
1077
Scott Baker133c9212013-05-17 09:09:11 -07001078class ReservationChangeForm(forms.ModelForm):
1079 class Meta:
1080 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001081 widgets = {
1082 'slice' : LinkedSelect
1083 }
Scott Baker133c9212013-05-17 09:09:11 -07001084
1085class ReservationAddForm(forms.ModelForm):
1086 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1087 refresh = forms.CharField(widget=forms.HiddenInput())
1088
1089 class Media:
1090 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1091
1092 def clean_slice(self):
1093 slice = self.cleaned_data.get("slice")
1094 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1095 if len(x) == 0:
1096 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1097 return slice
1098
1099 class Meta:
1100 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001101 widgets = {
1102 'slice' : LinkedSelect
1103 }
1104
Scott Baker133c9212013-05-17 09:09:11 -07001105
1106class ReservationAddRefreshForm(ReservationAddForm):
1107 """ This form is displayed when the Reservation Form receives an update
1108 from the Slice dropdown onChange handler. It doesn't validate the
1109 data and doesn't save the data. This will cause the form to be
1110 redrawn.
1111 """
1112
Scott Baker8737e5f2013-05-17 09:35:32 -07001113 """ don't validate anything other than slice """
1114 dont_validate_fields = ("startTime", "duration")
1115
Scott Baker133c9212013-05-17 09:09:11 -07001116 def full_clean(self):
1117 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001118
1119 for fieldname in self.dont_validate_fields:
1120 if fieldname in self._errors:
1121 del self._errors[fieldname]
1122
Scott Baker133c9212013-05-17 09:09:11 -07001123 return result
1124
1125 """ don't save anything """
1126 def is_valid(self):
1127 return False
1128
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001129class ReservationAdmin(PlanetStackBaseAdmin):
1130 fieldList = ['slice', 'startTime', 'duration']
1131 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker133c9212013-05-17 09:09:11 -07001132 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001133 form = ReservationAddForm
1134
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001135 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1136
1137 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001138 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001139
Scott Baker133c9212013-05-17 09:09:11 -07001140 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001141 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001142 request._refresh = False
1143 request._slice = None
1144 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001145 # "refresh" will be set to "1" if the form was submitted due to
1146 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001147 if request.POST.get("refresh","1") == "1":
1148 request._refresh = True
1149 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001150
1151 # Keep track of the slice that was selected, so the
1152 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001153 request._slice = request.POST.get("slice",None)
1154 if (request._slice is not None):
1155 request._slice = Slice.objects.get(id=request._slice)
1156
1157 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1158 return result
1159
Scott Bakeracd45142013-05-19 16:19:16 -07001160 def changelist_view(self, request, extra_context = None):
1161 timezone.activate(request.user.timezone)
1162 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1163
Scott Baker133c9212013-05-17 09:09:11 -07001164 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001165 request._obj_ = obj
1166 if obj is not None:
1167 # For changes, set request._slice to the slice already set in the
1168 # object.
1169 request._slice = obj.slice
1170 self.form = ReservationChangeForm
1171 else:
1172 if getattr(request, "_refresh", False):
1173 self.form = ReservationAddRefreshForm
1174 else:
1175 self.form = ReservationAddForm
1176 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1177
Scott Baker133c9212013-05-17 09:09:11 -07001178 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001179 if (obj is not None):
1180 # Prevent slice from being changed after the reservation has been
1181 # created.
1182 return ['slice']
1183 else:
Scott Baker133c9212013-05-17 09:09:11 -07001184 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001185
Tony Mack5b061472014-02-04 07:57:10 -05001186 def queryset(self, request):
1187 return Reservation.select_by_user(request.user)
1188
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001189class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001190 list_display = ("name", )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001191 user_readonly_fields = ['name']
1192 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001193
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001194class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001195 list_display = ("name", )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001196 user_readonly_fields = ['name']
1197 user_readonly_inlines = []
1198
Scott Baker0165fac2014-01-13 11:49:26 -08001199class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001200 model = Router.networks.through
1201 extra = 0
1202 verbose_name_plural = "Routers"
1203 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001204 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001205
Scott Bakerb27b62c2014-08-15 16:29:16 -07001206class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001207 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001208 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001209 verbose_name_plural = "Parameters"
1210 verbose_name = "Parameter"
1211 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker618e3792014-08-15 13:42:29 -07001212 fields = ['parameter', 'value']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001213
Scott Baker0165fac2014-01-13 11:49:26 -08001214class NetworkSliversInline(PlStackTabularInline):
Scott Baker618e3792014-08-15 13:42:29 -07001215 fields = ['network','sliver','ip']
Scott Baker74d8e622013-07-29 16:04:22 -07001216 readonly_fields = ("ip", )
1217 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001218 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001219 extra = 0
1220 verbose_name_plural = "Slivers"
1221 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001222 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001223
Scott Baker0165fac2014-01-13 11:49:26 -08001224class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001225 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001226 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001227 extra = 0
1228 verbose_name_plural = "Slices"
1229 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001230 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker618e3792014-08-15 13:42:29 -07001231 fields = ['network','slice']
Scott Bakerd7d2a392013-08-06 08:57:30 -07001232
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001233class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001234 list_display = ("name", "subnet", "ports", "labels")
1235 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001236
Scott Bakerd7d2a392013-08-06 08:57:30 -07001237 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001238
Siobhan Tully2d95e482013-09-06 10:56:06 -04001239 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001240 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1241
1242 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
Siobhan Tully2d95e482013-09-06 10:56:06 -04001243
1244 suit_form_tabs =(
1245 ('general','Network Details'),
1246 ('netparams', 'Parameters'),
1247 ('networkslivers','Slivers'),
1248 ('networkslices','Slices'),
1249 ('routers','Routers'),
1250 )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001251class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001252 list_display = ("name", "guaranteedBandwidth", "visibility")
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001253 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1254 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001255
Tony Mack31c2b8f2013-04-26 20:01:42 -04001256# register a signal that caches the user's credentials when they log in
1257def cache_credentials(sender, user, request, **kwds):
1258 auth = {'username': request.POST['username'],
1259 'password': request.POST['password']}
1260 request.session['auth'] = auth
1261user_logged_in.connect(cache_credentials)
1262
Scott Baker15cddfa2013-12-09 13:45:19 -08001263def dollar_field(fieldName, short_description):
1264 def newFunc(self, obj):
1265 try:
1266 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1267 except:
1268 x=getattr(obj, fieldName, 0.0)
1269 return x
1270 newFunc.short_description = short_description
1271 return newFunc
1272
1273def right_dollar_field(fieldName, short_description):
1274 def newFunc(self, obj):
1275 try:
1276 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1277 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1278 except:
1279 x=getattr(obj, fieldName, 0.0)
1280 return x
1281 newFunc.short_description = short_description
1282 newFunc.allow_tags = True
1283 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001284
Scott Baker0165fac2014-01-13 11:49:26 -08001285class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001286 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001287 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001288 verbose_name_plural = "Charges"
1289 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001290 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001291 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1292 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1293 can_delete = False
1294 max_num = 0
1295
1296 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001297
1298class InvoiceAdmin(admin.ModelAdmin):
1299 list_display = ("date", "account")
1300
1301 inlines = [InvoiceChargeInline]
1302
Scott Baker9cb88a22013-12-09 18:56:00 -08001303 fields = ["date", "account", "dollar_amount"]
1304 readonly_fields = ["date", "account", "dollar_amount"]
1305
1306 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001307
Scott Baker0165fac2014-01-13 11:49:26 -08001308class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001309 model = Invoice
1310 extra = 0
1311 verbose_name_plural = "Invoices"
1312 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001313 fields = ["date", "dollar_amount"]
1314 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001315 suit_classes = 'suit-tab suit-tab-accountinvoice'
1316 can_delete=False
1317 max_num=0
1318
1319 dollar_amount = right_dollar_field("amount", "Amount")
1320
Scott Baker0165fac2014-01-13 11:49:26 -08001321class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001322 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001323 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001324 verbose_name_plural = "Charges"
1325 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001326 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001327 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1328 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001329 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001330 can_delete=False
1331 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001332
1333 def queryset(self, request):
1334 qs = super(PendingChargeInline, self).queryset(request)
1335 qs = qs.filter(state="pending")
1336 return qs
1337
Scott Baker15cddfa2013-12-09 13:45:19 -08001338 dollar_amount = right_dollar_field("amount", "Amount")
1339
Scott Baker0165fac2014-01-13 11:49:26 -08001340class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001341 model=Payment
1342 extra = 1
1343 verbose_name_plural = "Payments"
1344 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001345 fields = ["date", "dollar_amount"]
1346 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001347 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001348 can_delete=False
1349 max_num=0
1350
1351 dollar_amount = right_dollar_field("amount", "Amount")
1352
Scott Baker43105042013-12-06 23:23:36 -08001353class AccountAdmin(admin.ModelAdmin):
1354 list_display = ("site", "balance_due")
1355
1356 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1357
1358 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001359 (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 -08001360
Scott Baker15cddfa2013-12-09 13:45:19 -08001361 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001362
1363 suit_form_tabs =(
1364 ('general','Account Details'),
1365 ('accountinvoice', 'Invoices'),
1366 ('accountpayments', 'Payments'),
1367 ('accountpendingcharges','Pending Charges'),
1368 )
1369
Scott Baker15cddfa2013-12-09 13:45:19 -08001370 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1371 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1372 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1373
Siobhan Tullyce652d02013-10-08 21:52:35 -04001374
Siobhan Tully53437282013-04-26 19:30:27 -04001375# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001376admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001377# ... and, since we're not using Django's builtin permissions,
1378# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001379#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001380
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001381#Do not show django evolution in the admin interface
1382from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001383#admin.site.unregister(Version)
1384#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001385
1386
1387# When debugging it is often easier to see all the classes, but for regular use
1388# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001389showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001390
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001391admin.site.register(Deployment, DeploymentAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001392admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001393admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001394admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001395admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001396admin.site.register(Network, NetworkAdmin)
1397admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001398admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001399admin.site.register(Account, AccountAdmin)
1400admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001401
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001402if True:
1403 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1404 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001405 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001406 admin.site.register(Tag, TagAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001407 admin.site.register(DeploymentRole)
1408 admin.site.register(SiteRole)
1409 admin.site.register(SliceRole)
1410 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001411 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001412 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1413 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001414 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001415 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001416 admin.site.register(DashboardView, DashboardViewAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001417