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