source: trunk/web2/addons/job_monarch/js/jobgrid.js @ 566

Last change on this file since 566 was 566, checked in by ramonb, 15 years ago

web2/addons/job_monarch/jobstore.php:

  • added getnodes task to retrieve node graphs
  • changed getList to getjobs

web2/addons/job_monarch/js/jobgrid.js:

  • added Graphs window popup
File size: 15.6 KB
Line 
1var JobsDataStore;
2var JobsColumnModel;
3var JobListingEditorGrid;
4var JobListingWindow;
5var JobProxy;
6var myfilters = { };
7var myparams = { };
8var mylimit = 15;
9var ClusterImageArgs = { };
10
11var filterfields = [ "jid", "queue", "name", "owner" ];
12
13Ext.namespace('Ext.ux');
14
15Ext.ux.PageSizePlugin = function() {
16    Ext.ux.PageSizePlugin.superclass.constructor.call(this, {
17        store: new Ext.data.SimpleStore({
18            fields: ['text', 'value'],
19            data: [['10', 10], ['15', 15], ['20', 20], ['30', 30], ['50', 50], ['100', 100], ['max', 'max' ]]
20        }),
21        mode: 'local',
22        displayField: 'text',
23        valueField: 'value',
24        editable: false,
25        allowBlank: false,
26        triggerAction: 'all',
27        width: 40
28    });
29};
30
31Ext.extend(Ext.ux.PageSizePlugin, Ext.form.ComboBox, {
32    init: function(paging) {
33        paging.on('render', this.onInitView, this);
34    },
35   
36    onInitView: function(paging) {
37        paging.add('-',
38            this,
39            'jobs per page'
40        );
41        this.setValue(paging.pageSize);
42        this.on('select', this.onPageSizeChanged, paging);
43    },
44   
45    onPageSizeChanged: function(combo) {
46        if ( combo.getValue() == 'max' )
47          mylimit = JobsDataStore.getTotalCount();
48        else
49          mylimit = parseInt(combo.getValue());
50        this.pageSize = mylimit;
51        this.doLoad(0);
52    }
53});
54
55Ext.namespace( 'Ext' );
56
57function makeArrayURL( somearr )
58{
59  filter_url = '';
60  filter_sep = '';
61
62  for( filtername in somearr )
63  {
64    filter_url = filter_url + filter_sep + filtername + '=' + somearr[filtername];
65    filter_sep = '&';
66  }
67
68  return filter_url;
69}
70
71
72function isset( somevar )
73{
74  try
75  {
76    if( eval( somevar ) ) { }
77  }
78  catch( err )
79  {
80    return false;
81  }
82  return true;
83}
84
85function inMyArray( arr, someval )
86{
87  for( arval in arr )
88  {
89    if( arval == someval )
90    {
91      return true;
92    }
93  }
94  return false;
95}
96
97function inMyArrayValues( arr, someval )
98{
99  for( arkey in arr )
100  {
101    if( arr[arkey] == someval )
102    {
103      return true;
104    }
105  }
106  return false;
107}
108
109function inMyArrayKeys( arr, someval )
110{
111  for( arkey in arr )
112  {
113    if( arkey == someval )
114    {
115      return true;
116    }
117  }
118  return false;
119}
120
121function joinMyArray( arr1, arr2 )
122{
123  for( arkey in arr2 )
124  {
125    arr1[arkey] = arr2[arkey];
126  }
127
128  return arr1;
129}
130
131function ClusterImageSelectHost( somehost )
132{
133
134  if( !inMyArrayKeys( myfilters, 'host' ) )
135  {
136    myfilters['host'] = somehost;
137  }
138  else
139  {
140    delete myfilters['host'];
141    delete myparams['host'];
142  }
143
144  reloadClusterImage();
145  reloadJobStore();
146
147  return false;
148}
149
150function reloadJobStore()
151{
152  // Respect any other parameters that may have been set outside filters
153  //
154  myparams = joinMyArray( myparams, myfilters );
155
156  // Can't be sure if there are enough pages for new filter: reset to page 1
157  //
158  myparams = joinMyArray( myparams, { start: 0, limit: mylimit } );
159
160  JobsDataStore.reload( { params: myparams } );
161}
162
163function addListener(element, type, expression, bubbling)
164{
165  bubbling = bubbling || false;
166  if(window.addEventListener)
167  { // Standard
168    element.addEventListener(type, expression, bubbling);
169    return true;
170  } 
171  else if(window.attachEvent) 
172  { // IE
173    element.attachEvent('on' + type, expression);
174    return true;
175  } 
176  else 
177    return false;
178}
179
180var ImageLoader = function( id, url )
181{
182  this.url = url;
183  this.image = document.getElementById( id );
184  this.loadEvent = null;
185};
186
187ImageLoader.prototype = 
188{
189  load:function()
190  {
191    var url = this.url;
192    var image = this.image;
193    var loadEvent = this.loadEvent;
194    addListener( this.image, 'load', function(e)
195    {
196      if( loadEvent != null )
197      {
198        loadEvent( url, image );
199      }
200    }, false);
201    this.image.src = this.url;
202  },
203  getImage: function()
204  {
205    return this.image;
206  }
207};
208
209function achorJobListing()
210{
211  JobListingWindow.anchorTo( "ClusterImageWindow", "tr-br", [ 0, 10 ] );
212}
213
214function setClusterImagePosition()
215{
216  ci_x = (window.innerWidth - ClusterImageWindow.getSize()['width'] - 20); 
217  ClusterImageWindow.setPosition( ci_x, 10 );
218}
219
220function reloadClusterImage()
221{
222  ClusterImageArgs['view'] = 'big-clusterimage';
223
224  filt_url = makeArrayURL( myfilters );
225  imag_url = makeArrayURL( ClusterImageArgs );
226  img_url = './image.php?' + filt_url + '&' + imag_url;
227
228  var newClusterImage = new ImageLoader( 'clusterimage', img_url );
229  newClusterImage.loadEvent = function( url, image ) 
230    {
231      ClusterImageWindow.getBottomToolbar().clearStatus( { useDefaults:true } );
232      setTimeout( "resizeClusterImage()", 250 );
233      setTimeout( "setClusterImagePosition()", 500 );
234      setTimeout( "achorJobListing()", 1000 );
235    }
236
237  ClusterImageWindow.getBottomToolbar().showBusy();
238  newClusterImage.load();
239}
240
241function resizeClusterImage()
242{
243  var ci_height = document.getElementById( "clusterimage" ).height + ClusterImageWindow.getFrameHeight();
244  var ci_width = document.getElementById( "clusterimage" ).width + ClusterImageWindow.getFrameWidth();
245
246  ClusterImageWindow.setSize( ci_width, ci_height );
247}
248
249Ext.apply(Ext.form.VTypes, {
250        num: function(val, field) {
251
252                if (val) {
253                   var strValidChars = "0123456789";
254                   var blnResult = true;
255
256                   if (val.length == 0) return false;
257
258                   //  test strString consists of valid characters listed above
259                   for (i = 0; i < val.length && blnResult == true; i++)
260                      {
261                      strChar = val.charAt(i);
262                      if (strValidChars.indexOf(strChar) == -1)
263                         {
264                         blnResult = false;
265                         }
266                      }
267                   return blnResult;
268
269                }
270                },
271        numText: 'Must be numeric'
272});
273
274function initJobGrid() {
275
276  Ext.QuickTips.init();
277
278  function jobCellClick(grid, rowIndex, columnIndex, e)
279  {
280    var record = grid.getStore().getAt(rowIndex);  // Get the Record
281    var fieldName = grid.getColumnModel().getDataIndex(columnIndex);
282    var data = record.get(fieldName);
283    var view = grid.getView();
284    var cell = view.getCell( rowIndex, columnIndex );
285
286    if( fieldName == 'owner' || fieldName == 'jid' || fieldName == 'status' || fieldName == 'queue' )
287    {
288      if( inMyArrayKeys( myfilters, fieldName ) )
289      {
290        Ext.fly(cell).removeClass( 'filterenabled' );
291        Ext.fly(cell).addClass( 'filter' );
292
293        // Remove this filter
294        //
295        delete myfilters[fieldName];
296        delete myparams[fieldName];
297
298        reloadJobStore();
299        reloadClusterImage();
300      }
301      else
302      {
303        Ext.fly(cell).removeClass( 'filter' );
304        Ext.fly(cell).addClass( 'filterenabled' );
305
306        // Set filter for selected column to selected cell value
307        //
308        myfilters[fieldName] = data;
309
310        reloadJobStore();
311        reloadClusterImage();
312      }
313    }
314  }
315
316  function jobCellRender( value, metadata, record, rowindex, colindex, store )
317  {
318    var fieldName = JobsColumnModel.getColumnById( colindex ).dataIndex;
319
320    if( fieldName == 'owner' || fieldName == 'jid' || fieldName == 'status' || fieldName == 'queue' )
321    {
322      if( myfilters[fieldName] != null )
323      {
324        metadata.css = 'filterenabled';
325      }
326      else
327      {
328        metadata.css = 'filter';
329      }
330    }
331    return value;
332  }
333
334  JobProxy = new Ext.data.HttpProxy({
335                url: 'jobstore.php',
336                method: 'POST'
337            });
338
339  var SearchField;
340
341  JobsDataStore = new Ext.data.Store({
342      id: 'JobsDataStore',
343      proxy: JobProxy,
344      baseParams: { task: "GETJOBS" },
345      reader: new Ext.data.JsonReader({
346        root: 'results',
347        totalProperty: 'total',
348        id: 'id'
349      },[
350        {name: 'jid', type: 'int', mapping: 'jid'},
351        {name: 'status', type: 'string', mapping: 'status'},
352        {name: 'owner', type: 'string', mapping: 'owner'},
353        {name: 'queue', type: 'string', mapping: 'queue'},
354        {name: 'name', type: 'string', mapping: 'name'},
355        {name: 'requested_time', type: 'string', mapping: 'requested_time'},
356        {name: 'requested_memory', type: 'string', mapping: 'requested_memory'},
357        {name: 'ppn', type: 'int', mapping: 'ppn'},
358        {name: 'nodect', type: 'int', mapping: 'nodect'},
359        {name: 'nodes', type: 'string', mapping: 'nodes'},
360        {name: 'queued_timestamp', type: 'string', mapping: 'queued_timestamp'},
361        {name: 'start_timestamp', type: 'string', mapping: 'start_timestamp'},
362        {name: 'runningtime', type: 'string', mapping: 'runningtime'}
363      ]),
364      sortInfo: { field: 'jid', direction: "DESC" },
365      remoteSort: true,
366      listeners: { 'load': {
367                        scope: this,
368                        fn: function() {
369                                        if( SearchField ) {
370                                                search_value = SearchField.getEl().dom.value;
371
372                                                if( search_value != '' )
373                                                {
374                                                        myfilters['query']      = search_value;
375                                                }
376
377                                                reloadClusterImage();
378
379                                                if( search_value != '' )
380                                                {
381                                                        delete myfilters['query'];
382                                                }
383                                        }
384                                }
385                        }
386                }
387    });
388   
389  var CheckJobs = new Ext.grid.CheckboxSelectionModel();
390
391  JobsColumnModel = new Ext.grid.ColumnModel(
392    [ CheckJobs,
393    {
394        header: '#',
395        tooltip: 'Job id',
396        readOnly: true,
397        dataIndex: 'jid',
398        width: 50,
399        hidden: false,
400        renderer: jobCellRender
401      },{
402        header: 'S',
403        tooltip: 'Job status',
404        readOnly: true,
405        dataIndex: 'status',
406        width: 20,
407        hidden: false,
408        renderer: jobCellRender
409      },{
410        header: 'User',
411        tooltip: 'Owner of job',
412        readOnly: true,
413        dataIndex: 'owner',
414        width: 60,
415        hidden: false,
416        renderer: jobCellRender
417      },{
418        header: 'Queue',
419        tooltip: 'In which queue does this job reside',
420        readOnly: true,
421        dataIndex: 'queue',
422        width: 60,
423        hidden: false,
424        renderer: jobCellRender
425      },{
426        header: 'Name',
427        tooltip: 'Name of job',
428        readOnly: true,
429        dataIndex: 'name',
430        width: 100,
431        hidden: false
432      },{
433        header: 'Requested Time',
434        tooltip: 'Amount of requested time (wallclock)',
435        readOnly: true,
436        dataIndex: 'requested_time',
437        width: 100,
438        hidden: false
439      },{
440        header: 'Requested Memory',
441        tooltip: 'Amount of requested memory',
442        readOnly: true,
443        dataIndex: 'requested_memory',
444        width: 100,
445        hidden: true
446      },{
447        header: 'P',
448        tooltip: 'Number of processors per node (PPN)',
449        readOnly: true,
450        dataIndex: 'ppn',
451        width: 25,
452        hidden: false
453      },{
454        header: 'N',
455        tooltip: 'Number of nodes (hosts)',
456        readOnly: true,
457        dataIndex: 'nodect',
458        width: 25,
459        hidden: false
460      },{
461        header: 'Nodes',
462        readOnly: true,
463        dataIndex: 'nodes',
464        width: 100,
465        hidden: true
466      },{
467        header: 'Queued',
468        tooltip: 'At what time did this job enter the queue',
469        readOnly: true,
470        dataIndex: 'queued_timestamp',
471        width: 120,
472        hidden: false
473      },{
474        header: 'Started',
475        tooltip: 'At what time did this job enter the running status',
476        readOnly: true,
477        dataIndex: 'start_timestamp',
478        width: 120,
479        hidden: false
480      },{
481        header: 'Runningtime',
482        tooltip: 'How long has this job been in the running status',
483        readOnly: true,
484        dataIndex: 'runningtime',
485        width: 140,
486        hidden: false
487      }]
488    );
489    JobsColumnModel.defaultSortable= true;
490
491  var win;
492
493  SearchField   = new Ext.app.SearchField({
494                                store: JobsDataStore,
495                                params: {start: 0, limit: mylimit},
496                                width: 200
497                    });
498
499  NodesDataStore = new Ext.data.Store({
500      id: 'NodesDataStore',
501      proxy: JobProxy,
502      autoLoad: false,
503      baseParams: { task: "GETNODES" },
504      reader: new Ext.data.JsonReader({
505        root: 'results',
506        totalProperty: 'total',
507        id: 'id'
508      },[
509        {name: 'c', type: 'string', mapping: 'c'},
510        {name: 'h', type: 'string', mapping: 'h'},
511        {name: 'v', type: 'string', mapping: 'v'},
512        {name: 'x', type: 'string', mapping: 'x'},
513      ]),
514      listeners: {
515                'beforeload': {
516                        scope: this,
517                        fn: function() {
518                                        var jids;
519
520                                        var row_records = CheckJobs.getSelections();
521
522                                        for(var i=0; i<row_records.length; i++ )
523                                        {
524                                                rsel = row_records[i];
525                                                if( !jids )
526                                                {
527                                                        jids = rsel.get('jid');
528                                                }
529                                                else
530                                                {
531                                                        jids = jids + ',' + rsel.get('jid');
532                                                }
533                                        }
534                                        NodesDataStore.baseParams.jids  = jids;
535                                        NodesDataStore.baseParams.c     = myparams.c;
536                        }
537                }
538        }
539    });
540
541function ShowGraphs( Button, Event ) {
542   
543    var GraphView = new Ext.DataView({
544        itemSelector: 'thumb',
545        style:'overflow:auto',
546        multiSelect: true,
547        store: NodesDataStore,
548        tpl: new Ext.XTemplate(
549            '<tpl for=".">',
550            '<div class="thumb"><img src="../../graph.php?z=small&h={h}&x={x}&v={v}&c={c}" border="0"></div>',
551            '</tpl>'
552        )
553    });
554
555    var images = new Ext.Panel({
556        id:'images',
557        title:'My Images',
558        region:'center',
559        margins: '5 5 5 0',
560        layout:'fit',
561        items: GraphView
562    });
563
564        if(!win){
565            win = new Ext.Window({
566                        width       : 500,
567                        height      : 300,
568                        closeAction :'hide',
569                        items:  [ images ]
570                    });
571                }
572        NodesDataStore.load();
573        win.show();
574}
575
576  JobListingEditorGrid =  new Ext.grid.EditorGridPanel({
577      id: 'JobListingEditorGrid',
578      store: JobsDataStore,
579      cm: JobsColumnModel,
580      enableColLock:false,
581      clicksToEdit:1,
582      loadMask: true,
583      selModel: new Ext.grid.RowSelectionModel({singleSelect:false}),
584      stripeRows: true,
585      sm: CheckJobs,
586      bbar: new Ext.PagingToolbar({
587                pageSize: 15,
588                store: JobsDataStore,
589                displayInfo: true,
590                displayMsg: 'Displaying jobs {0} - {1} out of {2} jobs total found.',
591                emptyMsg: 'No jobs found to display',
592                plugins: [new Ext.ux.PageSizePlugin()]
593            }),
594      tbar: [ SearchField,
595                new Ext.Button({
596                                text: 'Show graphs',
597                                tooltip: 'Show node graphs for selected jobs',
598                                iconCls: 'option',
599                                listeners: {
600                                        'click': {
601                                                scope: this,
602                                                fn: ShowGraphs
603                                        }
604                                }
605                        }) ]
606    });
607
608  ClusterImageWindow = new Ext.Window({
609      id: 'ClusterImageWindow',
610      title: 'Nodes',
611      closable: true,
612      collapsible: true,
613      animCollapse: true,
614      width: 1,
615      height: 1,
616      y: 15,
617      plain: true,
618      shadow: true,
619      resizable: false,
620      shadowOffset: 10,
621      layout: 'fit',
622      bbar: new Ext.StatusBar({
623                defaultText: 'Ready.',
624                id: 'basic-statusbar',
625                defaultIconCls: ''
626        })
627    });
628
629  GraphSummaryWindow = new Ext.Window({
630      id: 'GraphSummaryWindow',
631      title: 'Graph Summary',
632      closable: true,
633      collapsible: true,
634      animCollapse: true,
635      width: 300,
636      height: 500,
637      x: 10,
638      y: 10,
639      plain: true,
640      shadow: true,
641      resizable: true,
642      shadowOffset: 10,
643      layout: 'table',
644      layoutConfig: {
645                columns: 2
646        },
647      defaults:{border: false},
648      items: [{
649        id: 'monarchlogo',
650        cls: 'monarch',
651        bodyStyle: 'background: transparent',
652        html: '<A HREF="https://subtrac.sara.nl/oss/jobmonarch/" TARGET="_blank"><IMG SRC="./jobmonarch.gif" ALT="Job Monarch" BORDER="0"></A>'
653        //colspan: 2
654       },{
655        id: 'summarycount'
656       },{
657        id: 'rjqjgraph'
658       },{
659        id: 'pie',
660        colspan: 2
661       }],
662      bbar: new Ext.StatusBar({
663                defaultText: 'Ready.',
664                id: 'basic-statusbar',
665                defaultIconCls: ''
666        })
667    });
668
669  JobListingWindow = new Ext.Window({
670      id: 'JobListingWindow',
671      title: 'Cluster Jobs Overview',
672      closable:true,
673      collapsible: true,
674      animCollapse: true,
675      maximizable: true,
676      y: 375,
677      width:860,
678      height:427,
679      plain:true,
680      shadow: true,
681      shadowOffset: 10,
682      layout: 'fit',
683      items: JobListingEditorGrid
684    });
685
686  JobListingEditorGrid.addListener( 'cellclick', jobCellClick );
687}
Note: See TracBrowser for help on using the repository browser.