Revision 9ce969a7

b/ui/static/snf/js/models.js
114 114
        },
115 115

  
116 116
        get_fetcher: function(timeout, fast, limit, initial, params) {
117
            var fetch_params = params;
117
            var fetch_params = params || {};
118
            fetch_params.skips_timeouts = true;
119

  
118 120
            var timeout = parseInt(timeout);
119 121
            var fast = fast || 1000;
120 122
            var limit = limit;
121 123
            var initial_call = initial || true;
122 124
            
123 125
            var last_ajax = undefined;
124
            var cb = _.bind(function(){
126
            var cb = _.bind(function() {
127
                // clone to avoid referenced objects
128
                var params = _.clone(fetch_params);
125 129
                updater._ajax = last_ajax;
126 130
                if (last_ajax) {
127 131
                    last_ajax.abort();
128 132
                }
129
                last_ajax = this.fetch(fetch_params);
133
                last_ajax = this.fetch(params);
130 134
            }, this);
131 135
            var updater = new snf.api.updateHandler({'callback': cb, timeout:timeout, 
132 136
                                                    fast:fast, limit:limit, 
133 137
                                                    call_on_start:initial_call});
134 138

  
135
            snf.api.bind("call", _.bind(function(){ updater.faster()}, this));
139
            snf.api.bind("call", _.throttle(_.bind(function(){ updater.faster()}, this)), 2000);
136 140
            return updater;
137 141
        }
138 142
    });
......
391 395
            return net_vm_exists && vm_net_exists;
392 396
        },
393 397

  
394
        add_vm: function (vm, callback) {
398
        add_vm: function (vm, callback, error, options) {
399
            var payload = {add:{serverRef:"" + vm.id}};
400
            payload._options = options || {};
395 401
            return this.api.call(this.api_path() + "/action", "create", 
396
                                 {add:{serverRef:"" + vm.id}},
402
                                 payload,
397 403
                                 _.bind(function(){
398 404
                                     this.vms.add_pending(vm.id);
399 405
                                     if (callback) {callback()}
400
                                 },this));
406
                                 },this), error);
401 407
        },
402 408

  
403
        remove_vm: function (vm, callback) {
409
        remove_vm: function (vm, callback, error, options) {
410
            var payload = {remove:{serverRef:"" + vm.id}};
411
            payload._options = options || {};
404 412
            return this.api.call(this.api_path() + "/action", "create", 
405 413
                                 {remove:{serverRef:"" + vm.id}},
406 414
                                 _.bind(function(){
407 415
                                     this.vms.add_pending_for_remove(vm.id);
408 416
                                     if (callback) {callback()}
409
                                 },this));
417
                                 },this), error);
410 418
        },
411 419

  
412 420
        rename: function(name, callback) {
......
758 766
            var payload = {meta:{}};
759 767
            payload.meta[meta.key] = meta.value;
760 768

  
769
            // inject error settings
770
            payload._options = {critical: false};
771

  
761 772
            this.api.call(url, "update", payload, complete, error)
762 773
        },
763 774

  
764
        set_firewall: function(net_id, value, callback) {
775
        set_firewall: function(net_id, value, callback, error, options) {
765 776
            if (this.get("firewalls") && this.get("firewalls")[net_id] == value) { return }
766 777

  
767 778
            this.pending_firewalls[net_id] = value;
768 779
            this.trigger("change", this, this);
769
            this.api.call(this.api_path() + "/action", "create", {"firewallProfile":{"profile":value}}, callback);
780
            var payload = {"firewallProfile":{"profile":value}};
781
            payload._options = _.extend({critical: false}, options);
782
            
783
            // reset firewall state on error
784
            var error_cb = _.bind(function() {
785
                thi
786
            }, this);
787

  
788
            this.api.call(this.api_path() + "/action", "create", payload, callback, error);
770 789
            storage.networks.get(net_id).update_state();
771 790
        },
772 791

  
......
1026 1045
                                extra_details: { 'Machine ID': this.id, 'URL': url, 'Action': action || "undefined" },
1027 1046
                                allow_reload: false
1028 1047
                              },
1029
                handles_error: true
1048
                display: false,
1049
                critical: false
1030 1050
            }
1031 1051
            this.sync(method, this, params);
1032 1052
        },
......
1274 1294

  
1275 1295
        get_flavor: function(cpu, mem, disk, filter_list) {
1276 1296
            if (!filter_list) { filter_list = this.models };
1297

  
1277 1298
            return this.select(function(flv){
1278 1299
                if (flv.get("cpu") == cpu + "" &&
1279 1300
                   flv.get("ram") == mem + "" &&
1280 1301
                   flv.get("disk") == disk + "" &&
1281 1302
                   filter_list.indexOf(flv) > -1) { return true; }
1282
            })[0]
1303
            })[0];
1283 1304
        },
1284 1305
        
1285 1306
        get_data: function(lst) {
......
1334 1355
            })
1335 1356
        },
1336 1357

  
1337
        reset_stats_update: function() {
1358
        stop_stats_update: function() {
1338 1359
            this.each(function(vm) {
1339 1360
                vm.do_update_stats = false;
1340 1361
            })
......
1390 1411
            opts = {name: name, imageRef: image.id, flavorRef: flavor.id, metadata:meta}
1391 1412
            opts = _.extend(opts, extra);
1392 1413

  
1393
            this.api.call(this.path, "create", {'server': opts}, undefined, undefined, callback);
1414
            this.api.call(this.path, "create", {'server': opts}, undefined, undefined, callback, {critical: false});
1394 1415
        }
1395 1416

  
1396 1417
    })
b/ui/static/snf/js/sync.js
7 7
    var snf = root.synnefo = root.synnefo || {};
8 8
    var sync = snf.sync = snf.sync || {};
9 9
    var api = snf.api = snf.api || {};
10
    var storage = snf.storage = snf.storage || {};
10 11

  
11 12
    // shortcuts
12 13
    var bb = Backbone;
......
37 38
    var addApiCallDate = function(url, d, method) {
38 39
        if (d === undefined) { d = Date() };
39 40
        var path = snf.util.parseUri(url).path;
40
        api_history[path + "_" + method] = d;
41
        var key = path + "_" + method;
42

  
43
        // TODO: check if d is very old date
44
        api_history[key] = d;
41 45
        return api_history[path]
42 46
    }
43
    
47

  
48
    var clearApiCallDate = function(url, method) {
49
        var path = snf.util.parseUri(url).path;
50
        var key = path + "_" + method;
51
        api_history[key] = false;
52
        return api_history[path]
53
    }
54

  
44 55
    var api_errors = api.errors = api.errors || [];
45 56
    var add_api_error = function(settings, data) {
46 57
        api_errors.push({url:settings.url, date:new Date, settings:settings, data:data})
......
74 85
            }
75 86
        }
76 87

  
77
        options.handles_error = options.handles_error || false;
78
        
79
        if (api.stop_calls) {
88
        // default error options
89
        options.critical = options.critical === undefined ? true : options.critical;
90
        options.display = options.display === undefined ? true : options.display;
91

  
92
        if (api.stop_calls && !options.no_skip) {
80 93
            return;
81 94
        }
82 95

  
......
103 116
        return bb.sync(method, model, api_options);
104 117
    }
105 118
    
119
    api.timeouts_occured = 0;
120

  
106 121
    api.handlerWrapper = function(wrap, method, type) {
122
        
107 123
        var cb_type = type;
124

  
108 125
        return function() {
109 126
            
110
            if (type == "error") {
127
            var xhr = undefined;
128
            var handler_type = type;
129
            var args = arguments;
130
            var ajax_options = this;
131

  
132
            // save the request date to use it as a changes-since value
133
            // for opera because we are not able to determine
134
            // response date header for 304 requests
135
            if (handler_type == "beforeSend" && $.browser.opera) {
136
                this.date_send = new Date;
137
            }
138

  
139
            // error with status code 0 in opera
140
            // act as 304 response
141
            if (handler_type == "error" && $.browser.opera) {
142
                if (arguments[0].status === 0 && arguments[1] === "error") {
143
                    arguments[0].status = 304;
144
                    arguments[1] = "notmodified";
145
                    response_type = "success";
146
                    xhr = arguments[0];
147
                }
148
            }
149
            
150
            // add error in api errors registry
151
            // api errors registry will be sent
152
            // if user reports an error using feedback form
153
            if (handler_type == "error") {
111 154
                add_api_error(this, arguments);
112 155
            }
113 156
            
157
            // identify response status
114 158
            var status = 304;
115 159
            if (arguments[0]) {
116
                var status = arguments[0].status;
160
                status = arguments[0].status;
117 161
            }
118

  
119
            if (type == "error" && this.handles_error && ((status > 499 && status < 600) || status == 400)) { 
120
                arguments.ajax = this;
121
                return method(arguments)
122
            }
123

  
124
            var args = wrap.apply(this, arguments);
125
            args = _.toArray(args);
126
            var ajax_options = this;
127 162
            
163
            // identify aborted request
128 164
            try {
129 165
                if (args[1] === "abort") {
130 166
                    api.trigger("abort");
......
133 169
            } catch(error) {
134 170
                console.error("error aborting", error);
135 171
            }
136

  
137
            // FIXME: is this good practice ??
138
            // fetch callbacks wont get called
172
            
173
            // try to set the last request date
174
            // only for notmodified or succeed responses
139 175
            try {
140 176
                // identify xhr object
141
                var xhr = args[2];
142
                if (args.length == 2) {
143
                    xhr = args[0];
144
                }
145

  
146
                // do not call success for 304 responses
147
                if (args[1] === "notmodified" || xhr.status == 0 && $.browser.opera) {
148
                    if (args[2]) {
149
                        addApiCallDate(this.url, new Date(args[2].getResponseHeader('Date')), ajax_options.type);
177
                xhr = xhr || args[2];
178
                
179
                // not modified response
180
                if (args[1] === "notmodified") {
181
                    if (xhr) {
182
                        // use date_send if exists (opera browser)
183
                        var d = this.date_send || xhr.getResponseHeader('Date');
184
                        if (d) { addApiCallDate(this.url, new Date(d), ajax_options.type); };
150 185
                    }
151 186
                    return;
152 187
                }
153 188
                
189
                // success response
190
                if (args[1] == "success" && handler_type == "success") {
191
                    try {
192
                        // use date_send if exists (opera browser)
193
                        var d = this.date_send || args[2].getResponseHeader('Date');
194
                        if (d) { addApiCallDate(this.url, new Date(d), ajax_options.type); };
195
                    } catch (err) {
196
                        console.error(err)
197
                    }
198
                }
154 199
            } catch (err) {
155 200
                console.error(err);
156 201
            }
157
            method.apply(this, args);
202
            
203
            // dont call error callback for non modified responses
204
            if (arguments[1] === "notmodified") {
205
                return;
206
            }
207
            
208
            // prepare arguments for error callbacks
209
            var cb_args = _.toArray(arguments);
210
            if (handler_type === "error") {
211
                cb_args.push(_.clone(this));
212
            }
213
            
214
            // determine if we need to call our callback wrapper
215
            var call_api_handler = true;
216

  
217
            // request handles errors by itself, s
218
            if (handler_type == "error" && this.skip_api_error) {
219
                call_api_handler = false
220
            }
221

  
222
            // aborted request, don't call error handler
223
            if (handler_type === "error" && args[1] === "abort") {
224
                call_api_handler = false;
225
            }
226
            
227
            // reset api call date, next call will be sent without changes-since
228
            // parameter set
229
            if (handler_type === "error") {
230
                if (args[1] === "error") {
231
                    clearApiCallDate(this.url, this.type);
232
                }
233
            }
234
            
235
            // call api call back and retrieve params to
236
            // be passed to the callback method set for
237
            // this type of response
238
            if (call_api_handler) {
239
                cb_args = wrap.apply(this, cb_args);
240
            }
241
            
242
            // call requested callback
243
            method.apply(this, _.toArray(cb_args));
158 244
        }
159 245
    }
160 246

  
161 247
    api.successHandler = function(data, status, xhr) {
162 248
        //debug("ajax success", arguments)
163 249
        // on success, update the last date we called the api url
164
        addApiCallDate(this.url, new Date(xhr.getResponseHeader('Date')), this.type);
165 250
        return [data, status, xhr];
166 251
    }
167 252

  
168 253
    api.errorHandler = function(event, xhr, settings, error) {
169
        //debug("ajax error", arguments, this);
170
        arguments.ajax = this;
171 254
        
172
        // skip aborts
173
        if (xhr != "abort") {
174
            arguments.ajax.critical = arguments.ajax.critical == undefined ? true : arguments.ajax.critical;
175
            if (!settings.handles_error) api.trigger("error", arguments);
255
        // dont trigger api error untill timeouts occured
256
        // exceed the skips_timeouts limit
257
        //
258
        // check only requests with skips_timeouts option set
259
        if (xhr === "timeout" && _.last(arguments).skips_timeouts) {
260
            var skip_timeouts = snf.config.skip_timeouts || 1;
261
            if (snf.api.timeouts_occured < skip_timeouts) {
262
                snf.api.timeouts_occured++;
263
                return;
264
            } else {
265
                // reset and continue to error trigger
266
                snf.api.timeouts_occured = 0;
267
            }
176 268
        }
177
        return arguments;
269
    
270
        // skip aborts, notmodified (opera)
271
        if (xhr === "error" || xhr === "timeout") {
272
            var args = _.toArray(arguments);
273
            api.trigger("error", args);
274
        }
275

  
276
        return _.toArray(arguments);
178 277
    }
179 278

  
180 279
    api.completeHandler = function(xhr, status) {
......
189 288
        return arguments;
190 289
    }
191 290

  
192

  
193
    api.call = function(url, method, data, complete, error, success) {
291
    // api call helper
292
    api.call = function(url, method, data, complete, error, success, options) {
194 293
            var self = this;
195 294
            error = error || function(){};
196 295
            success = success || function(){};
197 296
            complete = complete || function(){};
198 297
            var extra = data ? data._options || {} : {};
199
            if (data && data._options) { delete data['_options'] };
200 298

  
299
            // really ugly way to pass sync request options.
300
            // it works though....
301
            if (data && data._options) { delete data['_options'] };
302
            
303
            // prepare the params
201 304
            var params = {
202 305
                url: snf.config.api_url + "/" + url,
203 306
                data: data,
......
206 309
                error: error
207 310
            }
208 311

  
209
            params = _.extend(params, extra);
312
            params = _.extend(params, extra, options);
210 313
            this.sync(method, this, params);
211 314
        },
212 315

  
213 316
    _.extend(api, bb.Events);
214

  
215 317
    
318
    // helper for callbacks that need to get called
319
    // in fixed intervals
216 320
    api.updateHandler = function(options) {
217 321
        this.cb = options.callback;
218 322
        this.limit = options.limit;
......
269 373
        }
270 374
    }
271 375
    
376
    // api error state
272 377
    api.stop_calls = false;
378
    api.STATES = { NORMAL:1, WARN:0, ERROR:-1 };
379
    api.error_state = api.STATES.NORMAL;
380

  
381
    // on api error update the api error_state
382
    api.bind("error", function() {
383
        var args = _.toArray(_.toArray(arguments)[0]);
384
        var params = _.last(args);
385
        
386
        if (params.critical) {
387
            snf.api.error_state = api.STATES.ERROR;
388
            snf.api.stop_calls = true;
389
        } else {
390
            snf.api.error_state = api.STATES.WARN;
391
        }
392
        snf.api.trigger("change:error_state", snf.api.error_state);
393
    });
394
    
395
    // reset api error state
396
    api.bind("reset", function() {
397
        snf.api.error_state = api.STATES.NORMAL;
398
        snf.api.stop_calls = false;
399
        snf.api.trigger("change:error_state", snf.api.error_state);
400
    })
273 401

  
274 402
    // make it eventable
275 403
    _.extend(api.updateHandler.prototype, bb.Events);
b/ui/static/snf/js/ui/web/ui_error_view.js
81 81
        },
82 82
        
83 83
        show_error: function(ns, code, message, type, details, error_options) {
84
            if (!snf.api.error_state) { this.error_stack = {} };
84
            if (snf.api.error_state == snf.api.STATES.NORMAL) { this.error_stack = {} };
85 85
                
86
            if (error_options.fatal_error) {
87
                snf.api.error_state = true;
88
                snf.api.trigger("change:error_state", true);
89
            } else {
90
                snf.api.error_state = false;
91
                snf.api.trigger("change:error_state", false);
92
            }
93

  
94 86
            var error_entry = [ns, code, message, type, details, error_options];
95 87
            this.error_stack[new Date()] = error_entry;
96 88
            this.display_error.apply(this, error_entry);
......
98 90
        },
99 91

  
100 92
        display_error: function(ns, code, message, type, details, error_options) {
101
            this.error_options = {'allow_report': true, 'allow_reload': true, 'extra_details': {}, 'non_critical': false, 'allow_details': false };
102

  
93
            this.error_options = {'allow_report': true, 'allow_reload': true, 
94
                'extra_details': {}, 'non_critical': false, 
95
                'allow_details': false };
96
            
103 97
            if (error_options) {
104 98
                this.error_options = _.extend(this.error_options, error_options);
105 99
            }
......
109 103
            this.type = type;
110 104
            this.details = details ? (details.toString ? details.toString() : details) : undefined;
111 105
            this.message = message;
106
            this.title = error_options.title || undefined;
112 107

  
113 108
            this.update_details();
114 109
            
......
120 115
                this.error_options.allow_details = true;
121 116
            }
122 117
            
123
            //if (APP_DEBUG) {
124
                //this.error_options.allow_details = true;
125
            //}
126

  
118
            if (APP_DEBUG) {
119
                this.error_options.allow_details = true;
120
            }
121
            
127 122
            this.$(".actions .show-details").click();
128 123
            this.$(".key.details").click();
129 124
            this.$(".error-more-details").hide();
......
134 129
            if (this.ns && this.type) {
135 130
                title = this.title || this.type + " Error";
136 131
            }
132

  
137 133
            this.$(".header .title").text(title);
138 134
            this.$(".error-code").text(this.code || "");
139 135
            this.$(".error-type").text(this.type || "");
......
172 168
            } else {
173 169
                this.$(".reload-app").hide();
174 170
            }
171

  
172
        },
173

  
174
        onOpen: function() {
175
            var self = this;
176

  
177
            this.$(".closeme").unbind("click");
178
            this.$(".closeme").bind("click", function(){
179
                self.hide("reset")
180
            })
175 181
        },
176 182

  
177 183
        hide: function(reset_state) {
178
            if (reset_state === undefined) { reset_state = true };
179
            if (reset_state) {
180
                snf.api.error_state = false;
181
                snf.api.trigger("change:error_state", snf.api.error_state);
182
            }
184
            if (reset_state === "reset") { snf.api.trigger("reset") };
183 185
            views.ErrorView.__super__.hide.apply(this);
184 186
        },
185 187

  
b/ui/static/snf/js/ui/web/ui_feedback_view.js
83 83
                'data': $.param(data),
84 84
                'success': this.show_success,
85 85
                'error': this.show_error,
86
                'no_skip': true,
87
                'display': false,
86 88
                'handles_error': true
87 89
            }
88 90
            api.sync('create', undefined, opts);
......
93 95
        },
94 96
        
95 97
        onOpen: function() {
98
            var self = this;
96 99
            var point = this.text.val().length;
97 100
            this.text.show().focus().setCursorPosition(point);
101

  
102
            this.$(".closeme").unbind("click");
103
            this.$(".closeme").bind("click", function(){
104
                self.hide("reset")
105
            });
98 106
        },
99 107

  
100 108
        show_form: function() {
......
127 135
            this.sending.hide();
128 136
        },
129 137
        
130
        hide: function() {
131
            snf.api.error_state = false;
132
            snf.api.trigger("change:error_state", snf.api.error_state);
138
        hide: function(reset_error_state) {
139
            // trigger api reset
140
            if (reset_error_state === "reset") {
141
                // report error feedback form
142
                if (snf.api.error_state == snf.api.STATES.ERROR) {
143
                    snf.api.trigger("reset");
144
                }
145
            }
133 146
            views.FeedbackView.__super__.hide.apply(this);
134 147
        },
135 148

  
b/ui/static/snf/js/ui/web/ui_icon_view.js
40 40
                    this.has_error = true;
41 41
                    var action = "undefined";
42 42
                    try {
43
                        action = args[0].ajax.error_params.extra_details['Action'];
43
                        action = _.last(args).error_params.extra_details['Action'];
44 44
                    } catch (err) {console.log(err)};
45 45
                    
46 46
                    this.error.find(".action").text(action);
......
50 50
            
51 51
            // show error overlay
52 52
            this.show_btn.click(_.bind(function() {
53
                this.show_error_overlay(this.vm.action_error);
53
                if (this.vm.action_error) {
54
                    this.show_error_overlay(this.vm.action_error);
55
                }
54 56
                this.vm.reset_action_error();
55 57
            }, this));
56 58
            
......
66 68
            }, this));
67 69
        },
68 70

  
69
        show_error_overlay: function(arguments) {
70
            var xhr = arguments[0];
71
            var args = util.parse_api_error(arguments);
71
        show_error_overlay: function(args) {
72
            var args = util.parse_api_error.apply(util, args);
72 73
            
73 74
            // force logout if UNAUTHORIZED request arrives
74 75
            if (args.code == 401) { snf.ui.logout(); return };
75

  
76
            
76 77
            var error_entry = [args.ns, args.code, args.message, args.type, args.details, args];
77 78
            ui.main.error_view.show_error.apply(ui.main.error_view, error_entry);
78 79
        },
b/ui/static/snf/js/ui/web/ui_list_view.js
371 371
                if (vm.in_transition()) {
372 372
                    self.sel("vm_spinner", vm.id).fadeIn(200);
373 373
                } else {
374
                    if (vm.pending_action || (self.actions.hovered && this.vm(vm).find("input").is(":checked"))) {
374
                    if (vm.pending_action || (self.actions.hovered && self.vm(vm).find("input").is(":checked"))) {
375 375
                        self.vm(vm).find(".action-indicator").show();
376 376
                    } else {
377 377
                        self.sel("os_icon", vm.id).fadeIn(200);
b/ui/static/snf/js/ui/web/ui_main_view.js
339 339
        },
340 340
        
341 341
        handle_api_error_state: function(state) {
342
            if (snf.api.error_state) {
342
            if (snf.api.error_state === snf.api.STATES.ERROR) {
343 343
                this.stop_intervals();
344 344
            } else {
345 345
                if (this.intervals_stopped) {
......
348 348
            }
349 349
        },
350 350
        
351
        handle_api_error: function(xhr, type, message) {
351
        handle_api_error: function(args) {
352
            if (arguments.length == 1) { arguments = _.toArray(arguments[0])};
353

  
354
            if (!_.last(arguments).display) {
355
                return;
356
            }
357

  
352 358
            this.error_state = true;
353
            this.log.error("API ERRROR", arguments);
354 359
            
355 360
            var xhr = arguments[0];
356
            var args = util.parse_api_error(arguments);
361
            var args = util.parse_api_error.apply(util, arguments);
357 362
            
358 363
            // force logout if UNAUTHORIZED request arrives
359 364
            if (args.code == 401) { snf.ui.logout(); return };
......
401 406
            this.update_status("Loading vms...");
402 407
            storage.vms.fetch({refresh:true, update:false, success: function(){
403 408
                self.update_status("VMS Loaded.");
404
                self.check_status()
409
                self.check_status();
405 410
            }});
411

  
406 412
            this.update_status("Loading networks...");
407 413
            storage.networks.fetch({refresh:true, update:false, success: function(){
408 414
                self.update_status("Networks loaded.");
409
                self.check_status()
415
                self.check_status();
410 416
            }});
411 417
        },  
412 418

  
413 419
        init_intervals: function() {
414
            this._networks = storage.networks.get_fetcher(snf.config.update_interval, snf.config.update_interval/2, 2, true, undefined);
415
            this._vms = storage.vms.get_fetcher(snf.config.update_interval, snf.config.update_interval/2, 2, true, undefined);
420
            this._networks = storage.networks.get_fetcher(snf.config.update_interval, 
421
                                                          snf.config.update_interval - 100, 
422
                                                          1, true, undefined);
423
            this._vms = storage.vms.get_fetcher(snf.config.update_interval, 
424
                                                snf.config.update_interval - 100, 
425
                                                1, true, undefined);
416 426
        },
417 427

  
418 428
        stop_intervals: function() {
......
679 689
            $(".large-spinner").remove();
680 690

  
681 691
            storage.vms.reset_pending_actions();
682
            storage.vms.reset_stats_update();
692
            storage.vms.stop_stats_update();
683 693

  
684 694
            // show current view
685 695
            this.show_view_pane();
b/ui/static/snf/js/ui/web/ui_networks_view.js
436 436

  
437 437
                $(window).trigger("resize");
438 438
            }, this))
439

  
439
            
440 440
            this.apply.click(_.bind(function(){
441 441
                this.apply.addClass("in-progress");
442
                this.vm.set_firewall(this.network.id, this.value(), _.bind(function() {
442
                
443
                // make the api call
444
                this.vm.set_firewall(this.network.id, this.value(), 
445
                // complete
446
                _.bind(function() {
447
                    // complete callback
448
                    this.apply.removeClass("in-progress");
449
                }, this), 
450
                // error
451
                _.bind(function(){
452
                    this.vm.remove_pending_firewall(this.network.id, this.value());
443 453
                }, this));
444 454
                this.hide_firewall();
445 455
            }, this))
b/ui/static/snf/js/ui/web/ui_single_view.js
104 104
        },  
105 105

  
106 106
        update_current_vm: function() {
107
            storage.vms.reset_stats_update();
107
            storage.vms.stop_stats_update();
108 108

  
109 109
            try {
110 110
                this.current_vm_instance = storage.vms.at(this.current_vm);
b/ui/static/snf/js/utils.js
47 47

  
48 48
        this.error = function() {
49 49
            var args = [2]; args.push.call(args, arguments);
50
            try {
51
                console.trace()
52
            } catch (err) {}
53 50
            this._log.apply(this, args);
54 51
        }
55 52

  
......
210 207
        }, 
211 208

  
212 209
        'abort': {},
213
        'parserror': {}
210
        'parserror': {},
211
        '413': {
212
            'title': "Account warning"
213
        }
214 214
    }
215 215
    
216 216
    synnefo.util.array_diff = function(arr1, arr2) {
......
285 285
        }
286 286
    }
287 287

  
288
    synnefo.util.parse_api_error = function(arguments) {
289
        arguments = arguments[0];
288
    synnefo.util.parse_api_error = function() {
289
        if (arguments.length == 1) { arguments = arguments[0] };
290 290

  
291 291
        var xhr = arguments[0];
292 292
        var error_message = arguments[1];
293 293
        var error_thrown = arguments[2];
294
        var ajax_settings = arguments.ajax || {};
295
        var call_settings = arguments.ajax ? arguments.ajax.error_params || {} : {};
296

  
294
        var ajax_settings = _.last(arguments) || {};
295
        var call_settings = ajax_settings.error_params || {};
297 296
        var json_data = undefined;
297

  
298
        var critical = ajax_settings.critical === undefined ? true : ajax_settings.critical;
299

  
298 300
        if (xhr.responseText) {
299 301
            try {
300 302
                json_data = JSON.parse(xhr.responseText)
......
315 317
            'message': 'Api error',
316 318
            'type': 'API',
317 319
            'allow_report': true,
318
            'fatal_error': ajax_settings.critical || false
320
            'fatal_error': ajax_settings.critical || false,
321
            'non_critical': !critical
319 322
        }
320 323

  
321 324
        var code = -1;
......
328 331
            defaults.non_critical = true;
329 332
            defaults.allow_report = false;
330 333
            defaults.allow_reload = false;
334
            error_message = "limit_error";
335
        }
336

  
337
        if (critical) {
338
            defaults.allow_report = true;
331 339
        }
332 340
        
333 341
        if (json_data) {
......
337 345
                error_message = obj.message;
338 346
            })
339 347
        }
340
        
348

  
341 349
        extra = {'URL': ajax_settings.url};
342 350
        options = {};
343 351
        options = _.extend(options, {'details': details, 'message': error_message, 'ns': module, 'extra_details': extra});
344 352
        options = _.extend(options, call_settings);
345 353
        options = _.extend(options, synnefo.i18n.API_ERROR_MESSAGES[error_message] || {});
354
        options = _.extend(options, synnefo.i18n.API_ERROR_MESSAGES[code] || {});
346 355
        
347 356
        if (window.ERROR_OVERRIDES && window.ERROR_OVERRIDES[options.message]) {
348 357
            options.message = window.ERROR_OVERRIDES[options.message];
b/ui/static/snf/js/views.js
82 82
            this.defaults = {
83 83
                load: false,
84 84
                closeOnClick: false,
85
                closeOnEsc: false,
85 86
                mask: {
86 87
                    color: "#444",
87 88
                    loadSpeed: snf.config.overlay_speed || 0,
......
180 181
        },
181 182

  
182 183
        _onOpen: function() {
184
            // clear previously bound click events
185
            $(this.el).find(".closeme").unbind("click");
186

  
183 187
            if ($(this.el).find(".closeme").length) {
184 188
                $(this.el).find(".closeme").click(_.bind(function(){
185 189
                    this.hide();
......
270 274
        _update_vm_details: function() { 
271 275
            if (!this.vm) { console.error("invalid view state"); return }
272 276
            this.set_subtitle(this.vm.get("name") + snf.ui.helpers.vm_icon_tag(this.vm, "small"));
273

  
274
            var ico_path = snf.ui.helpers.os_icon_path(this.vm.get("OS"), "oslarge");
275
            this.$(".content").css({"background-image":"url(" + ico_path +")"})
276 277
            this.update_vm_details() 
277 278
        },
278 279

  
b/ui/templates/home.html
447 447
            <span class="show-details">{% trans "Show details" %}</span>
448 448
            <span class="hide-details">{% trans "Hide details" %}</span>
449 449
            <span class="report-error">{% trans "Send report" %}</span>
450
            <span class="reload-app">{% trans "Reload application" %}</span>
450
            <span class="reload-app">{% trans "Reload" %}</span>
451 451
        </div>
452 452
    </div>
453 453

  

Also available in: Unified diff