Project

General

Profile

Download (43.2 KB) Statistics
| Branch: | Revision:
1
define([
2
'dojo/_base/array',
3
'dojo/_base/connect',
4
'dojo/on',
5
'dojo/dom',
6
'dojo/dom-construct',
7
'stabile/formatters',
8
'stabile/grid',
9
'steam2/user',
10
'stabile/upload',
11
'stabile/stores',
12
'steam2/models/Server',
13
'steam2/models/Image',
14
'steam2/models/Node',
15
'helpers/uuid',
16
// used in the form
17
'dijit/form/SimpleTextarea',
18
'dijit/form/ComboBox',
19
'dijit/form/Form'
20
], function(arrayUtil, connect, on, dom, domConstruct, formatters, grid, user, upload, stores, Server, Image, Node){
21

    
22
    var images = {
23

    
24
        grid: {},
25

    
26
        /** image model */
27
        model : function(args){
28
            return dojo.mixin(
29
            {
30
                // FIXME: size / virtualsize is the same
31
                backup: '--',
32
                bschedule: 'manually',
33
                domains: '--',
34
                master: '--',
35
                name: "",
36
                path: '--',
37
                realsize: 0,
38
                backupsize: 0,
39
                size: 0,
40
                status: "new",
41
                type: "qcow2",
42
                user: user.username,
43
                uuid: Math.uuid().toLowerCase(),
44
                virtualsize: 10737418240, //10737418240, //can grow up to 10 GB
45
                storagepool: 0,
46
                managementlink: "",
47
                upgradelink: "",
48
                terminallink: "",
49
                getPath: function(){
50
                    this.name = this.name || console.error('name must be set before calling getPath');
51
                    var t = "/mnt/stabile/images/${user}/${name}.${type}";
52
                    return dojo.string.substitute(t, {user:user.username, name:this.name, type: this.type});
53
                },
54
                onSave: function(){
55
                    console.log("onSave");
56
                }
57
            }, args || {});
58
        },
59

    
60
        /** object name - for reflective lookup */
61
        name: 'images',
62
        sortInfo: 2,
63

    
64
        // initial query
65
        storeQuery: {
66
            type: 'user'
67
//            user: user.username,
68
//            complexQuery: "type:qcow2 OR type:vmdk OR type:vdi OR type:vhd OR type:img"
69
        },
70

    
71
        _searchQuery: "name:*",
72
        _storagepoolQuery:"storagepool:",
73
        _typeQuery: "type:user",
74
        _inited: false,
75

    
76
        /** grid structure */
77
        structure : [
78
            {
79
                field: 'name',
80
                name: 'Name',
81
                //width: '230px'
82
                width: 'auto'
83
            },
84
            {
85
                field: 'status',
86
                name: 'Status    <a href="https://www.origo.io/info/stabiledocs/web/images/status" rel="help" target="_blank" class="irigo-tooltip">help</a>',
87
                width: '76px'
88
                // ,
89

    
90
                // formatter: function(val, rowIdx, cell) {
91
                //     var t = '<span style="color:${color}">${val}</span>';
92
                //     var color = statusCodes.getColor(val);
93
                //     return dojo.string.substitute(t, { color: color, val: val });
94
                // }
95
            },
96
            //{ field: 'type', name: 'Type', width: '50px'},
97
            { field: 'domainnames', name: 'Server(s)', width: 'auto'},
98
            { field: 'user', name: 'Owner', width: '120px' },
99
            { field: 'type', name: 'Type', width: '40px' },
100
            { field: 'realsize', name: 'Usage (GB)', width: '70px',
101
                cellStyles: "text-align:right;",
102
                formatter: formatters.bytes2gbs
103
            },
104
            { field: 'virtualsize', name: 'Size (GB)', width: '65px',
105
                cellStyles: "text-align:right;",
106
                formatter: formatters.bytes2gbs
107
            },
108
            { field: 'snap1',
109
                name: 'Snapshot',
110
                width: '65px',
111
                constraint: { formatLength: 'short'},
112
                formatter: function(val, rowIds, cell){
113
                    if (isNaN(val)) {
114
                        return val;
115
                    }
116
                    var fn = dojo.hitch({formatLength: 'short'}, formatters.datetime);
117
                    return fn(Number(val)*1000);
118
                }
119
            },
120
            { field: 'action',
121
                name: 'Action <a href="//www.origo.io/info/stabiledocs/web/images/image-actions" rel="help" target="_blank" class="irigo-tooltip">help</a>',
122
                width: 'auto', //
123
                //    options: ["--", "clone","snapshot","revert","unsnap","master","unmaster","delete"]
124
                formatter: function(val, rowIdx, cell) {
125
                    var item = this.grid.getItem(rowIdx);
126
                    return images.getActionButtons(item);
127
                },
128
                hidden: user.is_readonly
129
            }
130
        ],
131

    
132
        dialogStructure : [
133
            {
134
                field:"name",
135
                name: "Name",
136
                type:"dijit.form.ValidationTextBox",
137
                attrs: {required:false}
138
            },
139
            {
140
                field: "status", name: "Status", type: "dijit.form.TextBox",
141
                attrs: {readonly:"readonly"}
142
            },
143
            {
144
                field: "uuid",
145
                name: "UUID",
146
                type: "dijit.form.TextBox",
147
                attrs: {readonly:"readonly"}
148
            },
149
            {
150
                field:"type", name: 'Type', type:"dijit.form.Select",
151
                help: "images/image-type",
152
                attrs:{ store: "stores.imageTypes", searchAttr:"type"    }
153
            },
154
            {
155
                field:"virtualsize", name:'Virtual Size <span id="humanVirtualSize" style="color:gray"></span>',
156
                type: "dijit.form.ComboBox",
157
                help: "images/virtual-size",
158
                style: "width: 150px;",
159
                attrs: {store: "stores.virtualSizes",
160
                    searchAttr:"size",
161
                    onChange: "images.dialogOnSizeChangeHandler(this, 'humanVirtualSize')"},
162
                extra: function(item){return " bytes";}
163
            },
164
            {
165
                field:"realsize", name:'Usage <span id="humanRealSize" style="color:gray"></span>',
166
                type: "dijit.form.TextBox",
167
                style: "width: 150px;",
168
                attrs:{readonly:"readonly", onChange: "images.dialogOnSizeChangeHandler(this, 'humanRealSize')"},
169
                extra: function(item){return " bytes";}
170
            },
171
            {
172
                field:"backupsize", name:'Backup <span id="humanBackupSize" style="color:gray"></span>',
173
                type: "dijit.form.TextBox",
174
                style: "width: 150px;",
175
                attrs:{readonly:"readonly", onChange: "images.dialogOnSizeChangeHandler(this, 'humanBackupSize')"},
176
                extra: function(item){return " bytes";}
177
            },
178
            {
179
                field:"storagepool", name:'Storage pool', type: "dijit.form.Select",
180
             //   extra: function(item){
181
             //     return "<button type=\"button\" id=\"move_button\" dojoType=\"dijit.form.Button\" onclick=\"images.move()\" style=\"font-size:80%;\">Move</button>";
182
             //   },
183
                attrs:{
184
                    store: "stores.storagePools",
185
                    searchAttr:"id",
186
                    sortByLabel:"false",
187
                    onChange: "images.dialogOnStoragepoolChangeHandler(this)"
188
                }
189
            },
190
            {
191
              field:"mac",
192
              name:'<span id="imageDialogNodeDialogLink">Node</span>',
193
              type: "dijit.form.FilteringSelect",
194
              restricted: true,
195
              attrs:{store: "stores.nodesReadOnly", searchAttr:"name", required:"false", query: "{storfree: /^\\d\\d/}" }
196
            },
197
            {
198
                formatter: function(image){
199
                    if(image.status != 'new'){
200
                        if(image.domains != '--'){
201
                            // called domains, but only one server right? - NO, masters may be used by multiple domains /co
202
                            var doms = image.domains.split(/, {0,1}/);
203
                            var domnames = image.domainnames.split(/, {0,1}/);
204
                            var serverEditLink = "";
205
                            for (var i in doms) {
206
                                serverEditLink += '<a nohref="#images" onclick="servers.grid.dialog.show(stores.servers.fetchItemByIdentity({identity: \'' + doms[i]  + '\'}));">' + domnames[i] + '</a> ';
207
                            }
208
                            return '<td>Server</td><td>' + serverEditLink + '</td>';
209
                        } else {
210
                            return '<td>Server</td><td>Not used by any of your servers</td>';
211
                        }
212
                    } else {
213
                        return '';
214
                    }
215
                }
216
            },
217
            {
218
                formatter: function(item){
219
                    if (item.status == "new") {
220
                        return "";
221
                    } else {
222
                        var dsnap1 = "--";
223
                        if (item.snap1 && item.snap1 != "--") {
224
                            dsnap1 = (new Date(Number(item.snap1)*1000)).toLocaleString();
225
                        }
226
                        return [
227
                            '<td>',
228
                            '    <div>Snapshot<a href="https://www.origo.io/info/stabiledocs/web/images/snapshot"',
229
                            '     rel="help" target="_blank" class="irigo-tooltip">help</a>',
230
                            '    </div>',
231
                            '</td>',
232
                            '<td><input id="snap1" dojoType="dijit.form.TextBox" readonly value="' + dsnap1 + '"></td>'].join('\n');
233
                    }
234
                }
235
                //field: "snap1", name:"Snapshot", type: "dijit.form.TextBox",
236
                //attrs: {readonly:"readonly"}
237
            },
238
            {
239
                field: "master", name:"Master (path)", type: "dijit.form.TextBox",
240
                restricted: true,
241
                attrs: {readonly:"readonly"}
242
            },
243
            {
244
                field: "mastername",
245
                name:"<a nohref=\"#\" onClick=\"home.showImageDialog(null);\">Master</a>",
246
                type: "dijit.form.TextBox",
247
                attrs: {readonly:"readonly"}
248
            },
249
            {
250
                field:"backup", name:'Backups',
251
                type: "dijit.form.FilteringSelect",
252
                style: "width: 250px;",
253
                extra: function(item){
254
                    return '<button type="button" id="restore_button" nodojoType="dijit.form.Button" class="btn btn-xs btn-info" onclick="window.images.restore(); return false;" style="font-size:80%; display: none;">Restore</button>';
255
                },
256
                attrs:{store: "stores.backups", searchAttr:"time", sortByLabel:"false", required:"false", value:"--"}
257
            },
258
            {
259
                field:"bschedule", name:'Backup schedule',
260
                type: "dijit.form.Select",
261
                help: "images/backup-schedule",
262
                attrs:{store: "stores.backupSchedules", searchAttr:"schedule", sortByLabel:"false"}
263
            },
264
            {
265
                field:"btime", name:'Last backup',
266
                type: "dijit.form.TextBox",
267
                attrs:{readonly:"readonly"},
268
                formatter: function(item){
269
//                    return '<td>Last backup</td><td><input id="btime" dojoType="dijit.form.TextBox" readonly value="' + item.btime + '"></td>';
270
                    if(!isNaN(item.btime) && item.status != "new"){
271
                        var dval = home.timestampToLocaleString(item.btime);
272
                        return '<td>Last backup</td><td><input id="btime" dojoType="dijit.form.TextBox" readonly value="' + dval + '"></td>';
273
                    } else {
274
                        return '<td>Last backup</td><td><input id="btime" dojoType="dijit.form.TextBox" readonly value="Never"></td>';
275
                    }
276
                }
277
            },
278
            {
279
                field:"created", name:'Created',
280
                type: "dijit.form.TextBox",
281
                attrs:{readonly:"readonly"},
282
                formatter: function(item){
283
                    if(!isNaN(item.created) && item.status != "new"){
284
                        var dval = home.timestampToLocaleString(item.created);
285
                        return '<td>Created</td><td><input id="created" dojoType="dijit.form.TextBox" readonly value="' + dval + '"></td>';
286
                    } else {
287
                        return '<td>Created</td><td><input id="created" dojoType="dijit.form.TextBox" readonly value="--"></td>';
288
                    }
289
                }
290
            },
291
            {
292
                field: "user",
293
                name: "User",
294
                type: "dijit.form.FilteringSelect",
295
                attrs: {
296
                    store: "stores.imageids",
297
                    searchAttr: "id",
298
                    required:"true",
299
//                    query: "{privileges: /(a|u)/}"
300
                }
301
            },
302
            {
303
                name: "Path",
304
                formatter: function(item){
305
                    if(user.is_admin && item.status != "new"){
306
                        return '<td>Path</td><td><input id="path" dojoType="dijit.form.TextBox" readonly value="' + item.path + '"></td>';
307
                    }
308
                    return "";
309
                }
310
            },
311
            {
312
                field:"installable",
313
                name: "Installable",
314
                type:"dijit.form.CheckBox",
315
                restricted: false, // only admins
316
                help: 'images/installable-images',
317
                attrs:{onchange: "this.value=this.checked?'true':'false';"}
318
            },
319
            {
320
                field:"managementlink",
321
                name: "Management link",
322
                type:"dijit.form.TextBox"
323
            },
324
            {
325
                field:"upgradelink",
326
                name: "Upgrade link",
327
                type:"dijit.form.TextBox"
328
            },
329
            {
330
                field:"terminallink",
331
                name: "Terminal link",
332
                type:"dijit.form.TextBox"
333
            },
334
            {
335
                field:"notes",
336
                name: "Notes",
337
                type:"dijit.form.SimpleTextarea",
338
                style: "width: 90%;"
339
            }
340
        ],
341

    
342
        dialogExtras : function(item){
343
            return "";
344
        },
345

    
346
        restore: function(){
347
            var item = images.grid.dialog.item;
348
            images.store.setValue(item, 'action', 'restore');
349
            // FIXME: which one is the right one? this
350
            //var value = ( dijit.byId('backup').get('value') ).replace("+", "--plus--"); //Todo: for some bizarre reason "+"-signs don't survive
351
            // FIXME: or this?
352
            var value = ( dijit.byId('backup').get('value') ); // Update: issue fixed in stores.js
353
            images.store.setValue(item, 'backup', value);
354
            images.store.save();
355
            images.grid.dialog.hide();
356
        },
357

    
358
        doZBackup: function() {
359
            console.log("Doing ZFS backup");
360
            $("#dozbackup").html('Backing up&hellip; <i class="fa fa-cog fa-spin"></i>').prop("disabled", true);
361
            return $.get("/stabile/images?action=zbackup", function(){$("#dozbackup").html('Backup now').prop("disabled", false);});
362
        },
363

    
364
        getActionButtons : function(item, include_save){
365
            if (user.is_readonly) return "";
366

    
367
            var name = item.name;
368
            var type = this.name;
369

    
370
            function actionButton(args){
371
                args.name = name;
372
                args.type = type;
373
                return grid.actionButton(args);
374
            }
375

    
376
            var id = item.uuid;
377
            var status = item.status; 
378
            var snapshot = item.snap1;
379

    
380
            // ['irigo'] == 'irigo' is true
381
            var _user = item.user; //store.getValue(item, 'user');
382
            var _type = item.type;
383

    
384
            if (!id) {
385
                console.log("Error - image has no uuid", item);
386
                return "";
387
            }
388
            var download_button = actionButton({'action':"download", 'id':id});
389

    
390
            var browse_button = steam2.models.Image.actionButton(id);
391

    
392
            var clone_button = actionButton({'action':"clone", 'id':id});
393
            var convert_button = actionButton({'action':"convert", 'id':id, 'title': 'convert to qcow2'});
394
            var inject_button = actionButton({'action':"inject", 'id':id, 'title': 'inject drivers'});
395
            var snapshot_button = actionButton({'action':"snapshot", 'id':id});
396
            var revert_button = actionButton({'action':"revert", 'id':id, 'confirm':true});
397
            var unsnap_button = actionButton({'action':"unsnap", 'id':id});
398
            var master_button = actionButton({'action':"master", 'id':id});
399
            var unmaster_button = actionButton({'action':"unmaster", 'id':id, 'title': 'unmaster'});
400
            var rebase_button = actionButton({'action':"rebase", 'id':id, 'title': 'rebase'});
401
            var backup_button = actionButton({'action':"backup", 'id':id});
402
            var delete_button = actionButton({'action':"delete", 'id':id, 'confirm':true});
403
            var save_button = include_save ? grid.saveButton(type) : "";
404
            var is_master = false;
405

    
406
            var buttons = "";
407

    
408

    
409
            if(status == "new"){
410
                return save_button;
411
            }
412

    
413
            /*if(status == "used" || status == "unused" || status == "active"){
414
                if (_type == "qcow2" && (_user == user.username || user.is_admin )) {
415
                    if(snapshot && snapshot[0] != '--'){
416
                        buttons += unsnap_button;
417
                        if(status == "used" || status == "unused") buttons += revert_button;
418
                    } else {
419
                        buttons += snapshot_button;
420
                    }
421
                }
422
            }*/
423

    
424
            if(status == "used" || status == "unused" || status == "paused") {
425
                if(item.storagepool != -1){
426
                    buttons += browse_button;                                    
427
                }
428

    
429
                if (_type == "qcow2" && (_user == user.username || user.is_admin )) {
430
                    var _masterReg = new RegExp("\\.master\\." + _type + "$");
431
                    is_master = _masterReg.test(item.path)?true:false;
432

    
433
                    if (!is_master && status != "paused") {
434
                        if(snapshot && snapshot != '--'){
435
                            buttons += unsnap_button;
436
                            buttons += revert_button;
437
                        } else {
438
                            buttons += snapshot_button;
439
                        }
440
                    }
441
                    if(item.master && item.master!="--" && (status=="unused" || status =="used")) { // this is a child, which may be rebased
442
                        if (is_master)  buttons += unmaster_button;
443
                    //    else
444
                        buttons += rebase_button;
445
                    }
446
                    if (item.storagepool != -1) {
447
                        if (is_master) {
448
                            is_master = true;
449
                            if (status=="unused") buttons += unmaster_button;
450
                        } else {
451
                            if (status=="unused") buttons += master_button;
452
                        }
453
                    }
454
                }
455
                if (_type != "iso") {
456
                    buttons += clone_button;
457
                }
458
                if (_type == "qcow2" && (status == "unused" || status == "used") && user.is_admin) {
459
                    buttons += inject_button;
460
                }
461
                if (_type == "img" || _type == "vmdk" || _type == "vhd") {
462
                    buttons += convert_button;
463
                }
464
                // We don't allow downloading of child images, master images may always be downloaded
465
                if(( /*(!item.master || item.master=="--") && */ (status=="used" || status =="unused") && (item.storagepool != -1))
466
                        || is_master) buttons += download_button;
467
            }
468

    
469
            if (
470
                    (_user == user.username || user.is_admin ) && item.backup!="disabled" &&
471
                    (status == "used" || status == "unused" || status == "paused" || (status == "active" && item.lvm == 1) )
472
            ) {
473
                buttons += backup_button;
474
            }
475

    
476
            if((status == "unused" || status == "uploading" || status == "downloading") && (_user == user.username || user.is_admin )) {
477
                buttons += delete_button;
478
            }
479

    
480
            if (buttons == "" && status != "active" && status.indexOf("backingup")==-1) {
481
                return '<img height="18px" alt="busy" src="/stabile/static/img/loader.gif">';
482
            }
483

    
484
            if (user.is_admin || item.user==user.username || status == "backingup") buttons += save_button;
485

    
486
            return buttons;
487
        },
488

    
489
        onBeforeDialog : function(item){
490
            if(item.path && item.path != '--'){
491
                stores.backups.url = "/stabile/images?action=listbackups&image=" +
492
                        escape(item.uuid);
493
                stores.backups.close();
494
            }
495
            else{
496
//                stores.backups.url = "/stabile/images?action=listbackups";
497
                stores.backups.url = '';
498
                stores.backups.close();
499
            }
500
        },
501

    
502
        onDialogButtons: function (item) {
503
            // update the human size field
504
            if (dijit.byId('virtualsize')) {
505
                dijit.byId('virtualsize').onChange();
506
                // disable resizing of existing images
507
                dijit.byId('realsize').onChange();
508
                dijit.byId('backupsize').onChange();
509
                var status = item.status;
510

    
511
                var disable = function(dijitId){
512
                    var elm = dijit.byId(dijitId);
513
                    elm && elm.set('disabled', true);
514
                    return elm;
515
                };
516
                var enable = function(dijitId){
517
                    var elm = dijit.byId(dijitId);
518
                    elm && elm.set('disabled', false);
519
                    return elm;
520
                };
521

    
522
                if (user.is_readonly) {
523
                    disable('name');
524
                }
525
                if((status != 'new' && status != 'unused'  && status != 'used') || user.is_readonly){
526
                    disable('virtualsize');
527
                }
528
                if(status == 'unused' && (user.is_admin || user.billto=='--' || (user.tktuser==user.billto && item.user==user.username)) && !user.is_readonly) {
529
                    enable('user');
530
                } else {
531
                    disable('user');
532
                }
533

    
534
                if(user.is_readonly || (item.user!=user.username && !user.is_admin)) {
535
                    disable('storagepool');
536
                } else if (status == 'unused') {
537
                    enable('storagepool');
538
                } else if (status == 'new') {
539
                    dijit.byId('storagepool').set('value', 0);
540
                    disable('storagepool');
541
                } else if (status == 'used') {
542
                    if (item.path.indexOf(".master." + item.type) != -1)
543
                        disable('storagepool');
544
                    else
545
                        enable('storagepool');
546
                } else {
547
                    disable('storagepool');
548
                }
549

    
550
                if (item.path.indexOf(".master.qcow2")==-1 || (item.user!=user.username && !user.is_admin)) disable('installable');
551

    
552
                if ((!user.is_admin && item.user!=user.username) || user.is_readonly) {
553
                    disable('name');
554
                    disable('notes');
555
                    disable('backup');
556
                    disable('backup');
557
                    disable('bschedule');
558
                    disable('restore_button');
559
                }
560

    
561
                if (dijit.byId('backup') && dijit.byId('backup').value=="disabled") {
562
                    document.getElementById('bschedulelabel').style.display = "none";
563
                    dijit.byId('bschedule').set('disabled', true).set('style', 'display:none');
564
                }
565

    
566
                if (status == 'new') {
567
                    var imgtypes = dijit.byId("type");
568
                    if (imgtypes) imgtypes.setStore(stores.imageTypes, imgtypes.value, {query:{type: /qcow2|vmdk|vdi|vhd|img/i}});
569
                }
570

    
571
                if (item.storagepool == -1 || status == 'active' || user.is_readonly) {
572
                    disable('mac');
573
                }
574
                
575
                if(item.status != "new"){
576
                    if (dijit.byId('type')) disable('type');
577
                } else{
578
                    disable('backup');
579
                }
580
            }
581
            var _masterReg = new RegExp("\.master\." + item.type + "$");
582
            is_master = _masterReg.test(item.path)?true:false;
583
            if (dijit.byId('master')) master = dijit.byId('master').value;
584
            if (master && master!="--") {
585
//                stores.images.fetch({query: {path:master+'*'}, onComplete: images.updateMasterName});
586
                stores.images.fetch({query: {path:master}, onComplete: images.updateMasterName});
587
            } else if (is_master) {
588
                if (dijit.byId('mastername')) dijit.byId('mastername').set("value", "This image is a master image");
589
                if (document.getElementById('masternamelabel')) document.getElementById('masternamelabel').innerHTML = "Master";
590
            } else {
591
                if (dijit.byId('mastername')) dijit.byId('mastername').set("value","--");
592
                if (document.getElementById('masternamelabel')) document.getElementById('masternamelabel').innerHTML = "Master";
593
            }
594
        },
595

    
596
        onPostRender: function(){
597
        //    images.updateSums();
598
        },
599

    
600
        rowStyler : function(row, item){
601
            var _type = item.type;
602
            var path = this.store.getValue(item, "path");
603
            if(path.indexOf(".master." + _type) != -1){
604
                row.customStyles += "font-weight:bold;";
605
            } else {
606
                row.customStyles = "cursor:pointer;";
607
            }
608
        },
609

    
610
        // since stop propagation is not working on the help button
611
        // we disable sorting.
612
        canSort: function(index){
613

    
614
            if(index === 8){ // action
615
                return false;
616
            }
617
            if(index === 2){
618
                // status! Something bug in the dojo dropdown button.
619
                // it doesn't stop the event onClicks although I have specified it!
620
                // Tooltip clicks then triggers a sort, and removal of the tooltip content.
621
                // Therefore returning false.
622
                return false;
623
            }
624
            return true;
625
        },
626

    
627
        // helpers
628
        dialogOnSizeChangeHandler : function(caller, id){
629
            var value = Number(caller.value);
630
            if (!isNaN(value)) {// i.e., not '--'
631
                dojo.byId(id).innerHTML = Math.round(value / (1024 * 1024 * 1024)) + ' GB';
632
            }
633
        },
634

    
635
        dialogOnStoragepoolChangeHandler : function(caller){
636
            var index = parseInt(caller.value);
637
            if (user.node_storage_allowed) index += 1; // "On node" is first item in array, so we add one
638
            var bschedule = dijit.byId("bschedule");
639
            var hostpath = caller.options[index].item.hostpath;
640
            var lvm = caller.options[index].item.lvm;
641
//            console.log("caller", caller.options[index].item);
642
            if (bschedule && (hostpath == "local" || hostpath == "node") && lvm==1)
643
                bschedule.setStore(stores.backupSchedules, bschedule.value);
644
            else
645
                bschedule.setStore(stores.backupSchedules, bschedule.value, {query:{schedule: /manually|none/i}});
646
        },
647

    
648
        updateMasterName : function(items) {
649
            if (items && items.name && dijit.byId("mastername")) {
650
                dijit.byId("mastername").set("value", items.name);
651
                if (dojo.byId('masternamelabel')) dojo.byId('masternamelabel').innerHTML =
652
                    '<a nohref="#" onClick="$.get(\'/stabile/images/' + items.uuid + '\',function(item) {home.showImageItemDialog(item)});">Master</a>'
653
    //                "<a href=\"#\" onClick=\"home.showImageDialog('" + items.path + "');\">Master</a>"
654
            } else {
655
                if (dijit.byId('mastername')) dijit.byId('mastername').set("value","--");
656
                if (dojo.byId('masternamelabel')) dojo.byId('masternamelabel').innerHTML = "Master";
657
            }
658
            stores.images.reset('norender');
659
        },
660

    
661
        updateMissingBackups : function(filter) {
662
            if (IRIGO.user.zfsavailable) $("#dozbackup").show();
663
            else $("#dozbackup").hide();
664
            if ($("li#imagestab.active").length == 1) {
665
                var imagesfilter = {domains: '*'};
666
                var carray;
667
                if (!home.currentItem) {
668
                } else if (home.currentItem.issystem) {
669
                    carray = home.currentItem.children;
670
                } else {
671
                    imagesfilter = {domains: home.currentItem.uuid};
672
                }
673
                stores.images.close();
674
                stores.images.fetch({query:imagesfilter, onComplete: function(result){
675
                    var d = Math.ceil( new Date() / 1000 );
676
                    var one_day=60*60*24;
677
                    var unique_imgs = new Array();
678
                    var no_backup = 0;
679
                    var no_24_backup = 0;
680
                    var master_no_backup = 0;
681
                    var lvm_no_schedule = 0;
682
                    var no_backup_list = "";
683
                    var no_24_backup_list = "";
684
                    var master_no_backup_list = "";
685
                    var lvm_no_schedule_list = "";
686
                    var backed_up = 0;
687

    
688
                    for (var i in result) {
689
                        if (result[i] && result[i].uuid && result[i].domains && result[i].domains!='--' && result[i].type!='iso') {
690
                            if (unique_imgs[result[i].uuid]) continue; // We have seen this before
691
                            else unique_imgs[result[i].uuid] = 1;
692
                            var cmatch = result[i];
693
                            if (cmatch) {
694
                                if (cmatch.lvm==1 && (!cmatch.bschedule || cmatch.bschedule=="--" || cmatch.bschedule=="manually")){
695
                                    lvm_no_schedule++;
696
                                    lvm_no_schedule_list += cmatch.name + "\n";
697
                                }
698
                                if (!cmatch.created || d-cmatch.created > one_day) {
699
                                    if (!cmatch.btime || cmatch.btime=='--') { // No backup at all
700
                                        if (cmatch.path.indexOf(".master.qcow2")!=-1) {
701
                                            if (!cmatch.appid || cmatch.appid=='--') { // no need to back up appstore masters
702
                                                if (cmatch.path.indexOf("/common/")!=-1) { // Only notify admins of missing common master backups
703
                                                    if (user.is_admin) {
704
                                                        master_no_backup++;
705
                                                        master_no_backup_list += cmatch.name + "\n";
706
                                                        console.log("missing master backup", cmatch);
707
                                                    }
708
                                                } else { // Notify of personal missing master backups
709
                                                    master_no_backup++;
710
                                                    master_no_backup_list += cmatch.name + "\n";
711
                                                }
712
                                            }
713
                                        } else {
714
                                            no_backup++;
715
                                            no_backup_list += cmatch.name + "\n";
716
                                            no_24_backup++;
717
                                            no_24_backup_list += cmatch.name + "\n";
718
                                        }
719
                                    } else if (d-cmatch.btime > one_day) { // Backup older than 24h and not a master
720
                                        if (cmatch.path.indexOf(".master.qcow2")==-1) {
721
                                            no_24_backup++;
722
                                            no_24_backup_list += cmatch.name + "\n";
723
                                        }
724
                                    } else {
725
                                        backed_up++;
726
                                    }
727
                                } else {
728
                                    if (cmatch.btime && cmatch.btime!='--') backed_up++;
729
                                }
730
                            }
731
                        }
732
                    }
733
                    var u = '<b>Missing backups:</b> ';
734
                    if (no_backup>0) u += "<span title=\"" + no_backup_list + "\">" + no_backup + " (none)</span>, ";
735
                    if (no_24_backup>0) u +=  "<span title=\"" + no_24_backup_list + "\">" + no_24_backup + " (none in 24h)</span>, ";
736
                    if (lvm_no_schedule>0) u +=  "<span title=\"" + lvm_no_schedule_list + "\">" + lvm_no_schedule + " (no schedule)</span>, ";
737
                    if (master_no_backup>0) u +=  "<span title=\"" + master_no_backup_list + "\">" + master_no_backup + " (masters)</span>, ";
738
                    u = u.slice(0,-2);
739
                    u += "";
740
                    if (no_backup+no_24_backup+lvm_no_schedule+master_no_backup>0)
741
                        home.missingbackups.innerHTML = u;
742
                    else if (backed_up>0)
743
                        home.missingbackups.innerHTML = '<span style="font-size:90%; color:#AAAAAA;">All your images are backed up</span>';
744
                    else
745
                        home.missingbackups.innerHTML = '<span style="font-size:90%; color:#AAAAAA;">No missing backups</span>';
746
                }});
747
            }
748
    }
749

    
750
};
751

    
752
    images.init = function(){
753
        if (images._inited === true) return;
754
        else images._inited = true;
755

    
756
        images.store = stores.images;
757
        images.domnode = "images-grid";
758
        images.grid = grid.create(images);
759

    
760
        // connect listeners for filtering
761
        connect.connect(dijit.byId('images_search_query'), 'onChange', this, this.onSearchQueryChange);
762
        connect.connect(dijit.byId('images_storagepool_filter_select'), 'onChange', this, this.onStoragePoolFilterChange);
763
        connect.connect(dijit.byId('images_type_filter_select'), 'onChange', this, this.onTypeFilterChange);
764

    
765
        connect.connect(this.grid, '_onFetchComplete', this, function(rows){
766
            this.updateSums(rows);
767
            if (!user.is_readonly) {
768
                if (dijit.byId("imagesNewButton")) dijit.byId("imagesNewButton").set("style", "display:inline");
769
                if (dijit.byId("imagesUploadButton")) dijit.byId("imagesUploadButton").set("style", "display:inline");
770
            }
771
            images.updateMissingBackups();
772
        });
773

    
774
        images.grid.startup();
775

    
776
        var q = dojo.query('#imagesFileUploadHelp');
777
        if(q.irigoTooltip){q.irigoTooltip();}
778

    
779
        dojo.subscribe('upload:file_uploaded', function(){
780
            images.grid.refresh();
781
            stores.cdroms.close();
782
        });
783

    
784
        // refresh stores on image delete
785
        dojo.subscribe('images:delete', function(task) {
786
            if (typeof uploader !== 'undefined' && uploader.files) { // Remove file from uploader if we just uploaded it
787
                var pathname = task.path.substring(task.path.lastIndexOf("/")+1);
788
                $.each(uploader.files, function(index, value) {
789
                    if (value && (value.name == pathname || (value.path && value.path == task.path))) {
790
                        console.log("Removing upload", pathname);
791
                        uploader.removeFile(value.id);
792
                    }
793
                });
794
            }
795
            // update stores.
796
            stores.cdroms.close();
797
            stores.masterimages.close();
798
            stores.unusedImages.close();
799
            stores.unusedImages2.close();
800
        });
801

    
802
        dojo.subscribe('images:create', function(){
803
            stores.masterimages.close();
804
            stores.unusedImages.close();
805
            stores.unusedImages2.close();
806
        });
807

    
808
        // handle ui update while in dialog
809
        dojo.subscribe("images:update", function(task){
810
            console.log("images update", task);
811

    
812
            if (task.uuid && images.grid.getRowNode(0)) images.grid.refreshRow(task); // due to bug in Dojo we refresh all if no images at all
813
            else images.grid.refresh();
814

    
815
            var item = images.grid.dialog.item;
816

    
817
            if(!item || (item.uuid !== task.uuid && item.path !== task.uuid)){
818
                return;
819
            }
820

    
821
            if (!images.grid.dialog || !images.grid.dialog.isOpen()) {
822
                return;
823
            } // No need to update fields if dialog not showing
824

    
825
            if (dojo.byId('snap1') && task.snap1) {
826
                if (task.snap1 == "--") {
827
                    item.snap1 = "--";
828
                    dijit.byId('snap1').set('value', "--");
829
                } else {
830
                    var dsnap1 = (new Date(Number(task.snap1)*1000)).toLocaleString();
831
                    item.snap1 = dsnap1;
832
                    dijit.byId('snap1').set('value', dsnap1);
833
                }
834
            }
835

    
836
            if (dojo.byId('path') && task.newpath) {
837
                item.path = task.newpath;
838
                dijit.byId('path').set('value', task.newpath);
839
            }
840
            if (dojo.byId('master') && task.master) {
841
                item.master = task.master;
842
                dijit.byId('master').set('value', task.master);
843
            }
844
            if (dojo.byId('backup') && task.backup) {
845
                stores.backups.url = "/stabile/images?action=listbackups&image=" +
846
                        escape(task.backups);
847
                stores.backups.close();
848
            }
849
            images.grid.dialog.show(item, true);
850

    
851
            if (home.currentItem) {
852
                stores.unusedImages2.close();
853
                home.updateVitals(home.currentItem);
854
            }
855
        });
856

    
857
        connect.connect(this.grid.dialog, 'show', this, function(image){
858
            // summary: create dialog node link.
859
            var self = this;
860
            if(image.status != 'new'){
861
                if(user.is_admin && image.mac && image.mac != '--'){
862
                    domConstruct.place('<a id="imageDialogNodeDialogLink" nohref="#images">Node</a>', 'imageDialogNodeDialogLink', 'replace');
863
                    on(dom.byId('imageDialogNodeDialogLink'), 'click', function(){
864
                        self.grid.dialog.hide();
865
                        nodes.grid.dialog.show(stores.nodes.fetchItemByIdentity({identity: image.mac }));
866
//                        Node.editDialogFromUuid(image.mac);
867
                    });
868
                }
869
            }
870
            connect.connect(dijit.byId('backup'), 'onChange', this, this.onBackupChange);
871
        });
872

    
873
        images.onShowItem();
874
        images.grid.refresh();
875
    };
876

    
877
    images.onShowItem = function() {
878
        if (home.imagesOnShowItem != null && images.grid.dialog) {
879
            images.grid.dialog.show(home.imagesOnShowItem);
880
            home.imagesOnShowItem = null;
881
        }
882
    };
883

    
884
    images.updateFilter = function(){
885
        var query = this._searchQuery + " AND " + this._storagepoolQuery + " AND " + this._typeQuery;
886
        console.log("filtering", query);
887
        this.grid.store.query = query;
888
        this.grid.filter(query, /*rerender*/true);
889
    };
890

    
891
    images.onSearchQueryChange = function(v){
892
        if (v) {
893
            this._searchQuery = "name:" +v + "*";
894
//            this._searchQuery = "name: '*" + v + "*'" +
895
//                " OR status: '" + v + "*'" +
896
//                " OR type: '" + v + "*'";
897
        } else {
898
            this._searchQuery = "name:*";
899
//            this._searchQuery = "name:*";
900
        }
901
        this.updateFilter();
902
    };
903

    
904
    images.onStoragePoolFilterChange = function(value){
905
        switch(value){
906
            case "all":
907
                this._storagepoolQuery = "storagepool:all";
908
//                this._storagePoolQuery = "uuid: *";
909
                break;
910
            case "shared_storage_pools":
911
                this._storagepoolQuery = "storagepool:shared";
912
//                this._storagePoolQuery = "NOT storagepool: -1";
913
                break;
914
            case "node_storage_pools":
915
                this._storagepoolQuery = "storagepool:node";
916
                break;
917
            default:
918
                alert('wtf? ' + value);
919
        }
920
        this.updateFilter();
921
    };
922

    
923
    images.onBackupChange = function(value) {
924
        if (value && value != '--') $("#restore_button").show();
925
        else $("#restore_button").hide();
926
    };
927

    
928
    images.onTypeFilterChange = function(value){
929
        switch(value){
930
            case "all":
931
                this._typeQuery = "type:all";
932
//                this._typeQuery = "uuid:*";
933
                break;
934
            case "user_images":
935
                this._typeQuery = "type:user";
936
//                this._typeQuery = "user:"+user.username+" AND (type:qcow2 OR type:vmdk OR type:vdi OR type:vhd OR type:img)";
937
                break;
938
            case "user_master_images":
939
                this._typeQuery = "type:usermasters";
940
//                this._typeQuery = "user:"+user.username+" AND path:*.master.*";
941
                break;
942
            case "user_cd_roms":
943
                this._typeQuery = "type:usercdroms";
944
//                this._typeQuery = "user:"+user.username+" AND type:iso";
945
                break;
946
            case "common_master_images":
947
                this._typeQuery = "type:commonmasters";
948
//                this._typeQuery = "user:common AND (type:qcow2 OR type:vmdk OR type:vdi OR type:vhd OR type:img)";
949
                break;
950
            case "common_cd_roms":
951
                this._typeQuery = "type:commoncdroms";
952
//                this._typeQuery = "user:common AND type:iso";
953
                break;
954
            default:
955
                alert('wtf? ' + value);
956
        }
957
        this.updateFilter();
958
    };
959

    
960
    images.updateSums = function(rows) {
961
        var totalvirtualsize = 0;
962
        var totalrealsize = 0;
963
        var totalbackupsize = 0;
964

    
965
        if (!rows) {
966
            for(var i = 0; i < images.grid.rowCount; i++){
967
                sumit(images.grid.getItem(i));
968
            }
969
        } else {
970
            for(var i in rows){
971
                sumit(rows[i]);
972
            }
973
        }
974

    
975
        function sumit(item) {
976
            if (item) {
977
                if (item.virtualsize && item.virtualsize!="--") totalvirtualsize += parseInt(item.virtualsize);
978
                if (item.realsize && item.realsize!="--") totalrealsize += parseInt(item.realsize);
979
                if (item.backupsize && item.backupsize!="--") totalbackupsize += parseInt(item.backupsize);
980
            }
981
        }
982

    
983
        var stq = (user.storagequota==0)?'&infin;':Math.round(user.storagequota/1024);
984
        var nstq = (user.storagequota==0)?'&infin;':Math.round(user.nodestoragequota/1024);
985
        document.getElementById("storage_sum").innerHTML =
986
                '<span title="Quotas: ' + stq + ' GB standard, ' + nstq + ' GB on node">Usage: ' + (Math.round(10*totalvirtualsize /1024/1024/1024)/10) + " GB" +
987
                        "&nbsp;&nbsp;Size: " + (Math.round(10*totalrealsize /1024/1024/1024)/10) + " GB" +
988
                        "&nbsp;&nbsp;Backup: " + (Math.round(10*totalbackupsize /1024/1024/1024)/10) + " GB" +
989
                "</span>";
990
    };
991
    window.images = images;
992
    return images;
993
});
994

    
(9-9/23)