move commented code about the ‘where am I a PI’ question into topmenuvalidation plugin
[myslice.git] / portal / sliceview.py
1 import json
2 from django.template                 import RequestContext
3 from django.shortcuts                import render_to_response
4
5 from unfold.loginrequired            import LoginRequiredAutoLogoutView
6
7 from unfold.page                     import Page
8 from manifold.core.query             import Query, AnalyzedQuery
9 from manifold.manifoldapi            import execute_query
10
11 from ui.topmenu                      import topmenu_items, the_user
12
13 from plugins.topmenuvalidation          import TopmenuValidation
14 from plugins.raw                     import Raw
15 from plugins.stack                   import Stack
16 from plugins.tabs                    import Tabs
17 from plugins.querytable              import QueryTable 
18 from plugins.querygrid               import QueryGrid
19 from plugins.queryupdater            import QueryUpdater
20 from plugins.googlemap               import GoogleMap
21 from plugins.senslabmap              import SensLabMap
22 from plugins.scheduler               import Scheduler
23 from plugins.querycode               import QueryCode
24 # Thierry
25 # stay away from query editor for now as it seems to make things go very slow
26 # see https://lists.myslice.info/pipermail/devel-myslice/2013-December/000221.html 
27 #from plugins.query_editor            import QueryEditor
28 from plugins.active_filters          import ActiveFilters
29 from plugins.quickfilter             import QuickFilter
30 from plugins.messages                import Messages
31 from plugins.slicestat               import SliceStat
32
33 from myslice.config                  import Config
34
35 tmp_default_slice='ple.upmc.myslicedemo'
36
37 # temporary : turn off the users part to speed things up
38 #do_query_users=True
39 do_query_users=False
40
41 #do_query_leases=True
42 do_query_leases=False
43
44 insert_grid=False
45 #insert_grid=True
46
47 insert_messages=False
48 #insert_messages=True
49
50 class SliceView (LoginRequiredAutoLogoutView):
51
52     def get (self,request, slicename=tmp_default_slice):
53     
54         page = Page(request)
55         page.add_css_files ('css/slice-view.css')
56         page.add_js_files  ( [ "js/common.functions.js" ] )
57         page.add_js_chunks ('$(function() { messages.debug("sliceview: jQuery version " + $.fn.jquery); });')
58         page.add_js_chunks ('$(function() { messages.debug("sliceview: users turned %s"); });'%("on" if do_query_users else "off"))
59         page.add_js_chunks ('$(function() { messages.debug("sliceview: leases turned %s"); });'%("on" if do_query_leases else "off"))
60         config=Config()
61         page.add_js_chunks ('$(function() { messages.debug("manifold URL %s"); });'%(config.manifold_url()))
62
63         page.expose_js_metadata()
64
65         metadata = page.get_metadata()
66         resource_md = metadata.details_by_object('resource')
67         resource_fields = [column['name'] for column in resource_md['column']]
68     
69         user_md = metadata.details_by_object('user')
70         user_fields = ['user_hrn'] # [column['name'] for column in user_md['column']]
71     
72         # TODO The query to run is embedded in the URL
73         main_query = Query.get('slice').filter_by('slice_hrn', '=', slicename)
74         main_query.select(
75                 'slice_hrn',
76                 #'resource.hrn', 'resource.urn', 
77                 'resource.hostname', 'resource.type', 
78                 'resource.network_hrn',
79                 'lease.urn',
80                 'user.user_hrn',
81                 #'application.measurement_point.counter'
82         )
83         # for internal use in the querytable plugin;
84         # needs to be a unique column present for each returned record
85         main_query_init_key = 'hostname'
86     
87         query_resource_all = Query.get('resource').select(resource_fields)
88
89         aq = AnalyzedQuery(main_query, metadata=metadata)
90         page.enqueue_query(main_query, analyzed_query=aq)
91         page.enqueue_query(query_resource_all)
92         if do_query_users:
93             # Required: the user must have an authority in its user.config
94             # XXX Temporary solution
95             user_query  = Query().get('local:user').select('config','email')
96             user_details = execute_query(self.request, user_query)
97             
98             # not always found in user_details...
99             config={}
100 #            for user_detail in user_details:
101 #                #email = user_detail['email']
102 #                if user_detail['config']:
103 #                    config = json.loads(user_detail['config'])
104 #            user_detail['authority'] = config.get('authority',"Unknown Authority")
105 #
106 #            if user_detail['authority'] is not None:
107 #                sub_authority = user_detail['authority'].split('.')
108 #                root_authority = sub_authority[0]
109 #                query_user_all = Query.get(root_authority+':user').select(user_fields)
110 #
111 #                # XXX TODO this filter doesn't work - to be improved in Manifold
112 #                #.filter_by('authority.authority_hrn', '=', user_detail['authority'])
113 #
114 #                page.enqueue_query(query_user_all)
115 #            else:
116 #                print "authority of the user is not in local:user db"
117             query_user_all = Query.get('user').select(user_fields)
118             #    query_user_all = None
119     
120         # ... and for the relations
121         # XXX Let's hardcode resources for now
122         sq_resource    = aq.subquery('resource')
123         sq_user        = aq.subquery('user')
124         sq_lease       = aq.subquery('lease')
125         sq_measurement = aq.subquery('measurement')
126         
127     
128         # Prepare the display according to all metadata
129         # (some parts will be pending, others can be triggered by users).
130         # 
131         # For example slice measurements will not be requested by default...
132     
133         # Create the base layout (Stack)...
134         main_stack = Stack (
135             page=page,
136             title="Slice %s"%slicename,
137             sons=[],
138         )
139     
140         # ... responsible for the slice properties...
141     
142         # a nice header
143         main_stack.insert (
144             Raw (page=page,
145                  togglable=False, 
146                  toggled=True,
147                  html="<h2 class='well well-lg'> Slice %s</h2>"%slicename)
148         )
149     
150         # --------------------------------------------------------------------------
151         # QueryUpdater (Pending Operations)
152
153         main_stack.insert(QueryUpdater(
154             page                = page,
155             title               = 'Pending operations',
156             query               = main_query,
157             togglable           = True,
158             # start turned off, it will open up itself when stuff comes in
159             toggled             = False, 
160             domid               = 'pending',
161             outline_complete    = True,
162         ))
163
164         # --------------------------------------------------------------------------
165         # Filter Resources
166        
167 # turn off for now -- see above
168 #        filter_query_editor = QueryEditor(
169 #            page  = page,
170 #            query = sq_resource, 
171 #            query_all = query_resource_all,
172 #            title = "Select Columns",
173 #            domid = 'select-columns',
174 #            )
175         filter_active_filters = ActiveFilters(
176             page  = page,
177             query = sq_resource,
178             title = "Active Filters",
179             )
180         filters_area = Stack(
181             page                = page,
182             title               = 'Filter Resources',
183             domid               = 'filters',
184             sons                = [# filter_query_editor, 
185                                    filter_active_filters],
186             togglable           = True,
187             toggled             = 'persistent',
188             outline_complete    = True, 
189         )
190         main_stack.insert (filters_area)
191
192         # --------------------------------------------------------------------------
193         # RESOURCES
194         # the resources part is made of a Tabs (Geographic, List), 
195
196         resources_as_gmap = GoogleMap(
197             page       = page,
198             title      = 'Geographic view',
199             domid      = 'resources-map',
200             # tab's sons preferably turn this off
201             togglable  = False,
202             query      = sq_resource,
203             query_all  = query_resource_all,
204             # this key is the one issued by google
205             googlemap_api_key = Config().googlemap_api_key(),
206             # the key to use at init-time
207             init_key   = main_query_init_key,
208             checkboxes = True,
209             # center on Paris
210             latitude   = 49.,
211             longitude  = 9,
212             zoom       = 4,
213         )
214
215         resources_as_3dmap = SensLabMap(
216             page       = page,
217             title      = '3D Map',
218             domid      = 'senslabmap',
219             query      = sq_resource,
220             query_all  = query_resource_all,
221         )
222
223         resources_as_list = QueryTable( 
224             page       = page,
225             domid      = 'resources-list',
226             title      = 'List view',
227             # this is the query at the core of the slice list
228             query      = sq_resource,
229             query_all  = query_resource_all,
230             # use 'hrn' as the internal unique key for this plugin
231             init_key     = main_query_init_key,
232             checkboxes = True,
233             datatables_options = { 
234                 'iDisplayLength': 25,
235                 'bLengthChange' : True,
236                 'bAutoWidth'    : True,
237                 },
238             )
239
240         if insert_grid:
241             resources_as_grid = QueryGrid( 
242                 page       = page,
243                 domid      = 'resources-grid',
244                 title      = 'Grid view',
245                 # this is the query at the core of the slice list
246                 query      = sq_resource,
247                 query_all  = query_resource_all,
248                 # use 'hrn' as the internal unique key for this plugin
249                 # xxx todo on querygrid as well
250                 # init_key     = main_query_init_key,
251                 checkboxes = True,
252                 )
253
254         if do_query_leases:
255             resources_as_scheduler = Scheduler(
256                 page        = page,
257                 title       = 'Scheduler',
258                 domid       = 'scheduler',
259                 query       = sq_resource,
260                 query_all_resources = query_resource_all,
261                 query_lease = sq_lease,
262                 )
263
264        # with the new 'Filter' stuff on top, no need for anything but the querytable
265         resources_as_list_area = resources_as_list 
266
267         resources_sons = [
268             resources_as_gmap, 
269             resources_as_3dmap,
270             resources_as_scheduler,
271             resources_as_list_area,
272             ] if do_query_leases else [
273             resources_as_gmap, 
274             resources_as_3dmap,
275             resources_as_list_area,
276             ]
277         if insert_grid:
278             resources_sons.append(resources_as_grid)
279
280         resources_area = Tabs ( page=page, 
281                                 domid="resources",
282                                 togglable=True,
283                                 title="Resources",
284                                 outline_complete=True,
285                                 sons= resources_sons,
286                                 active_domid = 'resources-map',
287                                 persistent_active=True,
288                                 )
289         main_stack.insert (resources_area)
290
291         # --------------------------------------------------------------------------
292         # USERS
293     
294         if do_query_users and query_user_all is not None:
295             tab_users = Tabs(
296                 page                = page,
297                 domid               = 'users',
298                 outline_complete    = True,
299                 togglable           = True,
300                 title               = 'Users',
301                 active_domid        = 'users-list',
302                 )
303             main_stack.insert(tab_users)
304     
305             tab_users.insert(QueryTable( 
306                 page        = page,
307                 title       = 'Users List',
308                 domid       = 'users-list',
309                 # tab's sons preferably turn this off
310                 togglable   = False,
311                 # this is the query at the core of the slice list
312                 query       = sq_user,
313                 query_all  = query_user_all,
314                 checkboxes  = True,
315                 datatables_options = { 
316                     'iDisplayLength' : 25,
317                     'bLengthChange'  : True,
318                     'bAutoWidth'     : True,
319                 },
320             ))
321
322 # DEMO    
323         # --------------------------------------------------------------------------
324         # MEASUREMENTS
325         measurements_stats_cpu = SliceStat(
326             title = "CPU Usage",
327             domid = 'resources-stats-cpu',
328             page  = page,
329             stats = 'slice',
330             key   = 'hrn',
331             query = 'none',
332             slicename = slicename,
333             o = 'cpu'
334         )
335         
336         measurements_stats_mem = SliceStat(
337             title = "Memory Usage",
338             domid = 'resources-stats-mem',
339             page  = page,
340             stats = 'slice',
341             key   = 'hrn',
342             query = 'none',
343             slicename = slicename,
344             o = 'mem'
345         )
346         
347         measurements_stats_asb = SliceStat(
348             title = "Traffic Sent",
349             domid = 'resources-stats-asb',
350             page  = page,
351             stats = 'slice',
352             key   = 'hrn',
353             query = 'none',
354             slicename = slicename,
355             o = 'asb'
356         )
357         
358         measurements_stats_arb = SliceStat(
359             title = "Traffic Received",
360             domid = 'resources-stats-arb',
361             page  = page,
362             stats = 'slice',
363             key   = 'hrn',
364             query = 'none',
365             slicename = slicename,
366             o = 'arb'
367         )
368
369         tab_measurements = Tabs ( page=page,
370                                 domid="measurements",
371                                 togglable=True,
372                                 toggled  = False,
373                                 title="Measurements",
374                                 outline_complete=True,
375                                 sons=[ measurements_stats_cpu, measurements_stats_mem, measurements_stats_asb, measurements_stats_arb ],
376                                 active_domid = 'measurements_stats_cpu',
377                                 )
378         main_stack.insert (tab_measurements)
379         
380 #        tab_measurements = Tabs (
381 #            page                = page,
382 #            active_domid        = 'measurements-list',
383 #            outline_complete    = True,
384 #            togglable           = True,
385 #            title               = 'Measurements',
386 #            domid               = 'measurements',
387 #        )
388 #        main_stack.insert(tab_measurements)
389 #    
390 #        tab_measurements.insert(QueryTable( 
391 #            page        = page,
392 #            title       = 'Measurements',
393 #            domid       = 'measurements-list',
394 #            # tab's sons preferably turn this off
395 #            togglable   = False,
396 #            # this is the query at the core of the slice list
397 #            query       = sq_measurement,
398 #            # do NOT set checkboxes to False
399 #            # this table being otherwise empty, it just does not fly with dataTables
400 #            checkboxes  = True,
401 #            datatables_options = { 
402 #                'iDisplayLength' : 25,
403 #                'bLengthChange'  : True,
404 #                'bAutoWidth'     : True,
405 #            },
406 #        ))
407 #    
408 #        # --------------------------------------------------------------------------
409 #        # MESSAGES (we use transient=False for now)
410         if insert_messages:
411             main_stack.insert(Messages(
412                     page   = page,
413                     title  = "Runtime messages for slice %s"%slicename,
414                     domid  = "msgs-pre",
415                     levels = "ALL",
416                     # plain messages are probably less nice for production but more reliable for development for now
417                     transient = False,
418                     # these make sense only in non-transient mode..
419                     togglable = True,
420                     toggled = 'persistent',
421                     outline_complete = True,
422                     ))
423     
424 # topmenu animation
425 # xxx all this should go into a plugin if its own with the topmenu and all...
426         query_pi_auths = Query.get('ple:user').filter_by('user_hrn', '==', '$user_hrn' ).select('pi_authorities')
427         page.enqueue_query(query_pi_auths)
428         # even though this plugin does not have any html materialization, the corresponding domid
429         # must exist because it is searched at init-time to create the JS plugin
430         # so we simply piggy-back the target button here
431         topmenuvalidation = TopmenuValidation (
432             page=page, 
433             # see above
434             domid='topmenu-validation',
435             query=query_pi_auths,
436             # this one is the target for a $.show() when the query comes back
437             button_domid="topmenu-validation")
438         # although the result does not matter, rendering is required for the JS init code to make it in the page
439         topmenuvalidation.render(request)
440 # end topmenu addition
441
442         # variables that will get passed to the view-unfold1.html template
443         template_env = {}
444         
445         # define 'unfold_main' to the template engine - the main contents
446         template_env [ 'unfold_main' ] = main_stack.render(request)
447     
448         # more general variables expected in the template
449         template_env [ 'title' ] = '%(slicename)s'%locals()
450         # the menu items on the top
451         template_env [ 'topmenu_items' ] = topmenu_items('Slice', request) 
452         # so we can sho who is logged
453         template_env [ 'username' ] = the_user (request) 
454     
455         # don't forget to run the requests
456         page.expose_queries ()
457     
458         # xxx create another plugin with the same query and a different layout (with_datatables)
459         # show that it worls as expected, one single api call to backend and 2 refreshed views
460     
461         # the prelude object in page contains a summary of the requirements() for all plugins
462         # define {js,css}_{files,chunks}
463         prelude_env = page.prelude_env()
464         template_env.update(prelude_env)
465         result=render_to_response ('view-unfold1.html',template_env,
466                                    context_instance=RequestContext(request))
467         return result