Merge branch 'master' of ssh://git.onelab.eu/git/myslice
[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.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         page.expose_js_metadata()
62     
63         metadata = page.get_metadata()
64         resource_md = metadata.details_by_object('resource')
65         resource_fields = [column['name'] for column in resource_md['column']]
66     
67         user_md = metadata.details_by_object('user')
68         user_fields = ['user_hrn'] # [column['name'] for column in user_md['column']]
69     
70         # TODO The query to run is embedded in the URL
71         main_query = Query.get('slice').filter_by('slice_hrn', '=', slicename)
72         main_query.select(
73                 'slice_hrn',
74                 #'resource.hrn', 'resource.urn', 
75                 'resource.hostname', 'resource.type', 
76                 'resource.network_hrn',
77                 'lease.urn',
78                 'user.user_hrn',
79                 #'application.measurement_point.counter'
80         )
81         # for internal use in the querytable plugin;
82         # needs to be a unique column present for each returned record
83         main_query_init_key = 'hostname'
84     
85         query_resource_all = Query.get('resource').select(resource_fields)
86
87         aq = AnalyzedQuery(main_query, metadata=metadata)
88         page.enqueue_query(main_query, analyzed_query=aq)
89         page.enqueue_query(query_resource_all)
90         if do_query_users:
91             # Required: the user must have an authority in its user.config
92             # XXX Temporary solution
93             user_query  = Query().get('local:user').select('config','email')
94             user_details = execute_query(self.request, user_query)
95             
96             # not always found in user_details...
97             config={}
98             for user_detail in user_details:
99                 #email = user_detail['email']
100                 if user_detail['config']:
101                     config = json.loads(user_detail['config'])
102             user_detail['authority'] = config.get('authority',"Unknown Authority")
103
104             if user_detail['authority'] is not None:
105                 sub_authority = user_detail['authority'].split('.')
106                 root_authority = sub_authority[0]
107                 query_user_all = Query.get(root_authority+':user').select(user_fields)
108
109                 # XXX TODO this filter doesn't work - to be improved in Manifold
110                 #.filter_by('authority.authority_hrn', '=', user_detail['authority'])
111
112                 page.enqueue_query(query_user_all)
113             else:
114                 print "authority of the user is not in local:user db"
115                 query_user_all = Query.get('user').select(user_fields)
116             #    query_user_all = None
117     
118         # ... and for the relations
119         # XXX Let's hardcode resources for now
120         sq_resource    = aq.subquery('resource')
121         sq_user        = aq.subquery('user')
122         sq_lease       = aq.subquery('lease')
123         sq_measurement = aq.subquery('measurement')
124         
125     
126         # Prepare the display according to all metadata
127         # (some parts will be pending, others can be triggered by users).
128         # 
129         # For example slice measurements will not be requested by default...
130     
131         # Create the base layout (Stack)...
132         main_stack = Stack (
133             page=page,
134             title="Slice %s"%slicename,
135             sons=[],
136         )
137     
138         # ... responsible for the slice properties...
139     
140         # a nice header
141         main_stack.insert (
142             Raw (page=page,
143                  togglable=False, 
144                  toggled=True,
145                  html="<h2 class='well well-lg'> Slice %s</h2>"%slicename)
146         )
147     
148         # --------------------------------------------------------------------------
149         # QueryUpdater (Pending Operations)
150
151         main_stack.insert(QueryUpdater(
152             page                = page,
153             title               = 'Pending operations',
154             query               = main_query,
155             togglable           = True,
156             # start turned off, it will open up itself when stuff comes in
157             toggled             = False, 
158             domid               = 'pending',
159             outline_complete    = True,
160         ))
161
162         # --------------------------------------------------------------------------
163         # Filter Resources
164        
165 # turn off for now -- see above
166 #        filter_query_editor = QueryEditor(
167 #            page  = page,
168 #            query = sq_resource, 
169 #            query_all = query_resource_all,
170 #            title = "Select Columns",
171 #            domid = 'select-columns',
172 #            )
173         filter_active_filters = ActiveFilters(
174             page  = page,
175             query = sq_resource,
176             title = "Active Filters",
177             )
178         filters_area = Stack(
179             page                = page,
180             title               = 'Filter Resources',
181             domid               = 'filters',
182             sons                = [# filter_query_editor, 
183                                    filter_active_filters],
184             togglable           = True,
185             toggled             = 'persistent',
186             outline_complete    = True, 
187         )
188         main_stack.insert (filters_area)
189
190         # --------------------------------------------------------------------------
191         # RESOURCES
192         # the resources part is made of a Tabs (Geographic, List), 
193
194         resources_as_gmap = GoogleMap(
195             page       = page,
196             title      = 'Geographic view',
197             domid      = 'resources-map',
198             # tab's sons preferably turn this off
199             togglable  = False,
200             query      = sq_resource,
201             query_all  = query_resource_all,
202             # this key is the one issued by google
203             googlemap_api_key = Config().googlemap_api_key(),
204             # the key to use at init-time
205             init_key   = main_query_init_key,
206             checkboxes = True,
207             # center on Paris
208             latitude   = 49.,
209             longitude  = 9,
210             zoom       = 4,
211         )
212
213         resources_as_3dmap = SensLabMap(
214             page       = page,
215             title      = '3D Map',
216             domid      = 'senslabmap',
217             query      = sq_resource,
218             query_all  = query_resource_all,
219         )
220
221         resources_as_list = QueryTable( 
222             page       = page,
223             domid      = 'resources-list',
224             title      = 'List view',
225             # this is the query at the core of the slice list
226             query      = sq_resource,
227             query_all  = query_resource_all,
228             # use 'hrn' as the internal unique key for this plugin
229             init_key     = main_query_init_key,
230             checkboxes = True,
231             datatables_options = { 
232                 'iDisplayLength': 25,
233                 'bLengthChange' : True,
234                 'bAutoWidth'    : True,
235                 },
236             )
237
238         if insert_grid:
239             resources_as_grid = QueryGrid( 
240                 page       = page,
241                 domid      = 'resources-grid',
242                 title      = 'Grid view',
243                 # this is the query at the core of the slice list
244                 query      = sq_resource,
245                 query_all  = query_resource_all,
246                 # use 'hrn' as the internal unique key for this plugin
247                 # xxx todo on querygrid as well
248                 # init_key     = main_query_init_key,
249                 checkboxes = True,
250                 )
251
252         if do_query_leases:
253             resources_as_scheduler = Scheduler(
254                 page        = page,
255                 title       = 'Scheduler',
256                 domid       = 'scheduler',
257                 query       = sq_resource,
258                 query_all_resources = query_resource_all,
259                 query_lease = sq_lease,
260                 )
261
262        # with the new 'Filter' stuff on top, no need for anything but the querytable
263         resources_as_list_area = resources_as_list 
264
265         resources_sons = [
266             resources_as_gmap, 
267             resources_as_3dmap,
268             resources_as_scheduler,
269             resources_as_list_area,
270             ] if do_query_leases else [
271             resources_as_gmap, 
272             resources_as_3dmap,
273             resources_as_list_area,
274             ]
275         if insert_grid:
276             resources_sons.append(resources_as_grid)
277
278         resources_area = Tabs ( page=page, 
279                                 domid="resources",
280                                 togglable=True,
281                                 title="Resources",
282                                 outline_complete=True,
283                                 sons= resources_sons,
284                                 active_domid = 'resources-map',
285                                 persistent_active=True,
286                                 )
287         main_stack.insert (resources_area)
288
289         # --------------------------------------------------------------------------
290         # USERS
291     
292         if do_query_users and query_user_all is not None:
293             tab_users = Tabs(
294                 page                = page,
295                 domid               = 'users',
296                 outline_complete    = True,
297                 togglable           = True,
298                 title               = 'Users',
299                 active_domid        = 'users-list',
300                 )
301             main_stack.insert(tab_users)
302     
303             tab_users.insert(QueryTable( 
304                 page        = page,
305                 title       = 'Users List',
306                 domid       = 'users-list',
307                 # tab's sons preferably turn this off
308                 togglable   = False,
309                 # this is the query at the core of the slice list
310                 query       = sq_user,
311                 query_all  = query_user_all,
312                 checkboxes  = True,
313                 datatables_options = { 
314                     'iDisplayLength' : 25,
315                     'bLengthChange'  : True,
316                     'bAutoWidth'     : True,
317                 },
318             ))
319
320 # DEMO    
321         # --------------------------------------------------------------------------
322         # MEASUREMENTS
323         measurements_stats_cpu = SliceStat(
324             title = "CPU Usage",
325             domid = 'resources-stats-cpu',
326             page  = page,
327             stats = 'slice',
328             key   = 'hrn',
329             query = 'none',
330             slicename = slicename,
331             o = 'cpu'
332         )
333         
334         measurements_stats_mem = SliceStat(
335             title = "Memory Usage",
336             domid = 'resources-stats-mem',
337             page  = page,
338             stats = 'slice',
339             key   = 'hrn',
340             query = 'none',
341             slicename = slicename,
342             o = 'mem'
343         )
344         
345         measurements_stats_asb = SliceStat(
346             title = "Traffic Sent",
347             domid = 'resources-stats-asb',
348             page  = page,
349             stats = 'slice',
350             key   = 'hrn',
351             query = 'none',
352             slicename = slicename,
353             o = 'asb'
354         )
355         
356         measurements_stats_arb = SliceStat(
357             title = "Traffic Received",
358             domid = 'resources-stats-arb',
359             page  = page,
360             stats = 'slice',
361             key   = 'hrn',
362             query = 'none',
363             slicename = slicename,
364             o = 'arb'
365         )
366
367         tab_measurements = Tabs ( page=page,
368                                 domid="measurements",
369                                 togglable=True,
370                                 toggled  = False,
371                                 title="Measurements",
372                                 outline_complete=True,
373                                 sons=[ measurements_stats_cpu, measurements_stats_mem, measurements_stats_asb, measurements_stats_arb ],
374                                 active_domid = 'measurements_stats_cpu',
375                                 )
376         main_stack.insert (tab_measurements)
377         
378 #        tab_measurements = Tabs (
379 #            page                = page,
380 #            active_domid        = 'measurements-list',
381 #            outline_complete    = True,
382 #            togglable           = True,
383 #            title               = 'Measurements',
384 #            domid               = 'measurements',
385 #        )
386 #        main_stack.insert(tab_measurements)
387 #    
388 #        tab_measurements.insert(QueryTable( 
389 #            page        = page,
390 #            title       = 'Measurements',
391 #            domid       = 'measurements-list',
392 #            # tab's sons preferably turn this off
393 #            togglable   = False,
394 #            # this is the query at the core of the slice list
395 #            query       = sq_measurement,
396 #            # do NOT set checkboxes to False
397 #            # this table being otherwise empty, it just does not fly with dataTables
398 #            checkboxes  = True,
399 #            datatables_options = { 
400 #                'iDisplayLength' : 25,
401 #                'bLengthChange'  : True,
402 #                'bAutoWidth'     : True,
403 #            },
404 #        ))
405 #    
406 #        # --------------------------------------------------------------------------
407 #        # MESSAGES (we use transient=False for now)
408         if insert_messages:
409             main_stack.insert(Messages(
410                     page   = page,
411                     title  = "Runtime messages for slice %s"%slicename,
412                     domid  = "msgs-pre",
413                     levels = "ALL",
414                     # plain messages are probably less nice for production but more reliable for development for now
415                     transient = False,
416                     # these make sense only in non-transient mode..
417                     togglable = True,
418                     toggled = 'persistent',
419                     outline_complete = True,
420                     ))
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('Slice', request) 
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