Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / models.js @ 681c9851

History | View | Annotate | Download (87 kB)

1 00469232 Kostas Papadimitriou
// Copyright 2011 GRNET S.A. All rights reserved.
2 00469232 Kostas Papadimitriou
// 
3 00469232 Kostas Papadimitriou
// Redistribution and use in source and binary forms, with or
4 00469232 Kostas Papadimitriou
// without modification, are permitted provided that the following
5 00469232 Kostas Papadimitriou
// conditions are met:
6 00469232 Kostas Papadimitriou
// 
7 00469232 Kostas Papadimitriou
//   1. Redistributions of source code must retain the above
8 00469232 Kostas Papadimitriou
//      copyright notice, this list of conditions and the following
9 00469232 Kostas Papadimitriou
//      disclaimer.
10 00469232 Kostas Papadimitriou
// 
11 00469232 Kostas Papadimitriou
//   2. Redistributions in binary form must reproduce the above
12 00469232 Kostas Papadimitriou
//      copyright notice, this list of conditions and the following
13 00469232 Kostas Papadimitriou
//      disclaimer in the documentation and/or other materials
14 00469232 Kostas Papadimitriou
//      provided with the distribution.
15 00469232 Kostas Papadimitriou
// 
16 00469232 Kostas Papadimitriou
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 00469232 Kostas Papadimitriou
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 00469232 Kostas Papadimitriou
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 00469232 Kostas Papadimitriou
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 00469232 Kostas Papadimitriou
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 00469232 Kostas Papadimitriou
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 00469232 Kostas Papadimitriou
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 00469232 Kostas Papadimitriou
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 00469232 Kostas Papadimitriou
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 00469232 Kostas Papadimitriou
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 00469232 Kostas Papadimitriou
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 00469232 Kostas Papadimitriou
// POSSIBILITY OF SUCH DAMAGE.
28 00469232 Kostas Papadimitriou
// 
29 00469232 Kostas Papadimitriou
// The views and conclusions contained in the software and
30 00469232 Kostas Papadimitriou
// documentation are those of the authors and should not be
31 00469232 Kostas Papadimitriou
// interpreted as representing official policies, either expressed
32 00469232 Kostas Papadimitriou
// or implied, of GRNET S.A.
33 00469232 Kostas Papadimitriou
// 
34 00469232 Kostas Papadimitriou
35 8d08f18a Kostas Papadimitriou
;(function(root){
36 8d08f18a Kostas Papadimitriou
    
37 8d08f18a Kostas Papadimitriou
    // root
38 8d08f18a Kostas Papadimitriou
    var root = root;
39 8d08f18a Kostas Papadimitriou
    
40 8d08f18a Kostas Papadimitriou
    // setup namepsaces
41 8d08f18a Kostas Papadimitriou
    var snf = root.synnefo = root.synnefo || {};
42 8d08f18a Kostas Papadimitriou
    var models = snf.models = snf.models || {}
43 8d08f18a Kostas Papadimitriou
    var storage = snf.storage = snf.storage || {};
44 8d08f18a Kostas Papadimitriou
    var util = snf.util = snf.util || {};
45 8d08f18a Kostas Papadimitriou
46 8d08f18a Kostas Papadimitriou
    // shortcuts
47 8d08f18a Kostas Papadimitriou
    var bb = root.Backbone;
48 8d08f18a Kostas Papadimitriou
    var slice = Array.prototype.slice
49 8d08f18a Kostas Papadimitriou
50 8d08f18a Kostas Papadimitriou
    // logging
51 8d08f18a Kostas Papadimitriou
    var logger = new snf.logging.logger("SNF-MODELS");
52 8d08f18a Kostas Papadimitriou
    var debug = _.bind(logger.debug, logger);
53 8d08f18a Kostas Papadimitriou
    
54 8d08f18a Kostas Papadimitriou
    // get url helper
55 c4fff449 Kostas Papadimitriou
    var getUrl = function(baseurl) {
56 47e34b99 Kostas Papadimitriou
        var baseurl = baseurl || snf.config.api_urls[this.api_type];
57 7f16b9b1 Kostas Papadimitriou
        var append = "/";
58 7f16b9b1 Kostas Papadimitriou
        if (baseurl.split("").reverse()[0] == "/") {
59 7f16b9b1 Kostas Papadimitriou
          append = "";
60 7f16b9b1 Kostas Papadimitriou
        }
61 7f16b9b1 Kostas Papadimitriou
        return baseurl + append + this.path;
62 8d08f18a Kostas Papadimitriou
    }
63 126a01f2 Kostas Papadimitriou
64 8d08f18a Kostas Papadimitriou
    // i18n
65 8d08f18a Kostas Papadimitriou
    BUILDING_MESSAGES = window.BUILDING_MESSAGES || {'INIT': 'init', 'COPY': '{0}, {1}, {2}', 'FINAL': 'final'};
66 8d08f18a Kostas Papadimitriou
67 8d08f18a Kostas Papadimitriou
    // Base object for all our models
68 8d08f18a Kostas Papadimitriou
    models.Model = bb.Model.extend({
69 8d08f18a Kostas Papadimitriou
        sync: snf.api.sync,
70 8d08f18a Kostas Papadimitriou
        api: snf.api,
71 47e34b99 Kostas Papadimitriou
        api_type: 'compute',
72 8d08f18a Kostas Papadimitriou
        has_status: false,
73 95ff92b4 Kostas Papadimitriou
        auto_bind: [],
74 95ff92b4 Kostas Papadimitriou
75 8d08f18a Kostas Papadimitriou
76 8d08f18a Kostas Papadimitriou
        initialize: function() {
77 95ff92b4 Kostas Papadimitriou
            var self = this;
78 95ff92b4 Kostas Papadimitriou
            
79 95ff92b4 Kostas Papadimitriou
            this._proxy_model_cache = {};
80 95ff92b4 Kostas Papadimitriou
            _.each(this.auto_bind, function(fname) {
81 95ff92b4 Kostas Papadimitriou
              self[fname] = _.bind(self[fname], self);
82 95ff92b4 Kostas Papadimitriou
            });
83 95ff92b4 Kostas Papadimitriou
84 8d08f18a Kostas Papadimitriou
            if (this.has_status) {
85 8d08f18a Kostas Papadimitriou
                this.bind("change:status", this.handle_remove);
86 8d08f18a Kostas Papadimitriou
                this.handle_remove();
87 8d08f18a Kostas Papadimitriou
            }
88 47e34b99 Kostas Papadimitriou
            
89 258235f4 Kostas Papadimitriou
            this.api_call = _.bind(this.api.call, this);
90 95ff92b4 Kostas Papadimitriou
              
91 95ff92b4 Kostas Papadimitriou
            if (this.proxy_attrs) {
92 95ff92b4 Kostas Papadimitriou
              this.init_proxy_attrs();             
93 95ff92b4 Kostas Papadimitriou
            }
94 95ff92b4 Kostas Papadimitriou
95 95ff92b4 Kostas Papadimitriou
            if (this.storage_attrs) {
96 95ff92b4 Kostas Papadimitriou
              this.init_storage_attrs();
97 95ff92b4 Kostas Papadimitriou
            }
98 95ff92b4 Kostas Papadimitriou
99 95ff92b4 Kostas Papadimitriou
            if (this.model_actions) {
100 95ff92b4 Kostas Papadimitriou
              this.init_model_actions();             
101 95ff92b4 Kostas Papadimitriou
            }
102 95ff92b4 Kostas Papadimitriou
103 df251d55 Kostas Papadimitriou
            models.Model.__super__.initialize.apply(this, arguments);
104 95ff92b4 Kostas Papadimitriou
105 95ff92b4 Kostas Papadimitriou
        },
106 95ff92b4 Kostas Papadimitriou
        
107 95ff92b4 Kostas Papadimitriou
        // Initialize model actions object
108 95ff92b4 Kostas Papadimitriou
        // For each entry in model's model_action object register the relevant 
109 95ff92b4 Kostas Papadimitriou
        // model proxy `can_<actionname>` attributes.
110 95ff92b4 Kostas Papadimitriou
        init_model_actions: function() {
111 95ff92b4 Kostas Papadimitriou
          var actions = _.keys(this.model_actions);
112 95ff92b4 Kostas Papadimitriou
          this.set({
113 95ff92b4 Kostas Papadimitriou
            "actions": new models._ActionsModel({}, {
114 95ff92b4 Kostas Papadimitriou
              actions: actions,
115 95ff92b4 Kostas Papadimitriou
              model: this
116 95ff92b4 Kostas Papadimitriou
            })
117 95ff92b4 Kostas Papadimitriou
          });
118 95ff92b4 Kostas Papadimitriou
          this.actions = this.get("actions");
119 3c446f85 Kostas Papadimitriou
          this.actions.bind("set-pending", function(action) {
120 3c446f85 Kostas Papadimitriou
            this.trigger("action:set-pending", action, this.actions, this);
121 3c446f85 Kostas Papadimitriou
          }, this);
122 3c446f85 Kostas Papadimitriou
          this.actions.bind("unset-pending", function(action) {
123 3c446f85 Kostas Papadimitriou
            this.trigger("action:unset-pending", action, this.actions, this);
124 3c446f85 Kostas Papadimitriou
          }, this);
125 3c446f85 Kostas Papadimitriou
          this.actions.bind("reset-pending", function() {
126 3c446f85 Kostas Papadimitriou
            this.trigger("action:reset-pending", this.actions, this);
127 3c446f85 Kostas Papadimitriou
          }, this);
128 95ff92b4 Kostas Papadimitriou
129 95ff92b4 Kostas Papadimitriou
          _.each(this.model_actions, function(params, key){
130 95ff92b4 Kostas Papadimitriou
            var attr = 'can_' + key;
131 95ff92b4 Kostas Papadimitriou
            if (params.length == 0) { return }
132 95ff92b4 Kostas Papadimitriou
            var deps = params[0];
133 95ff92b4 Kostas Papadimitriou
            var cb = _.bind(params[1], this);
134 95ff92b4 Kostas Papadimitriou
            _.each(deps, function(dep) {
135 95ff92b4 Kostas Papadimitriou
              this._set_proxy_attr(attr, dep, cb);
136 95ff92b4 Kostas Papadimitriou
            }, this);
137 95ff92b4 Kostas Papadimitriou
          }, this);
138 8d08f18a Kostas Papadimitriou
        },
139 95ff92b4 Kostas Papadimitriou
        
140 95ff92b4 Kostas Papadimitriou
        // Initialize proxy storage model attributes. These attribues allows 
141 95ff92b4 Kostas Papadimitriou
        // us to automatically access cross collection associated objects.
142 95ff92b4 Kostas Papadimitriou
        init_storage_attrs: function() {
143 95ff92b4 Kostas Papadimitriou
          _.each(this.storage_attrs, function(params, attr) {
144 95ff92b4 Kostas Papadimitriou
            var store, key, attr_name;
145 95ff92b4 Kostas Papadimitriou
            store = synnefo.storage[params[0]];
146 95ff92b4 Kostas Papadimitriou
            key = params[1];
147 31bc4729 Kostas Papadimitriou
            attr_resolver = params[2];
148 31bc4729 Kostas Papadimitriou
            if (!attr_resolver) {
149 31bc4729 Kostas Papadimitriou
              attr_resolver = function(model, attr) {
150 31bc4729 Kostas Papadimitriou
                return model.get(attr);
151 31bc4729 Kostas Papadimitriou
              }
152 31bc4729 Kostas Papadimitriou
            }
153 95ff92b4 Kostas Papadimitriou
            attr_name = attr;
154 95ff92b4 Kostas Papadimitriou
          
155 95ff92b4 Kostas Papadimitriou
            var resolve_related_instance = function(storage, attr_name, val) {
156 95ff92b4 Kostas Papadimitriou
              var data = {};
157 95ff92b4 Kostas Papadimitriou
158 95ff92b4 Kostas Papadimitriou
              if (!val) { 
159 95ff92b4 Kostas Papadimitriou
                // update with undefined and return
160 95ff92b4 Kostas Papadimitriou
                data[key] = undefined;
161 95ff92b4 Kostas Papadimitriou
                this.set(data);
162 95ff92b4 Kostas Papadimitriou
                return;
163 95ff92b4 Kostas Papadimitriou
              };
164 95ff92b4 Kostas Papadimitriou
            
165 95ff92b4 Kostas Papadimitriou
              // retrieve related object (check if its a Model??)
166 95ff92b4 Kostas Papadimitriou
              var obj = store.get(val);
167 95ff92b4 Kostas Papadimitriou
              
168 95ff92b4 Kostas Papadimitriou
              if (obj) {
169 95ff92b4 Kostas Papadimitriou
                // set related object
170 95ff92b4 Kostas Papadimitriou
                data[attr_name] = obj;
171 95ff92b4 Kostas Papadimitriou
                this.set(data, {silent:true})
172 95ff92b4 Kostas Papadimitriou
                this.trigger("change:" + attr_name, obj);
173 95ff92b4 Kostas Papadimitriou
              } else {
174 95ff92b4 Kostas Papadimitriou
                var self = this;
175 69dac1ba Kostas Papadimitriou
                var retry_to_resolve = function(store, val, key) {
176 69dac1ba Kostas Papadimitriou
                  var retries = 0;
177 69dac1ba Kostas Papadimitriou
                  var retry = window.setInterval(function(){
178 69dac1ba Kostas Papadimitriou
                    retries++;
179 69dac1ba Kostas Papadimitriou
                    if (retries > 200) {
180 69dac1ba Kostas Papadimitriou
                      clearInterval(retry);
181 69dac1ba Kostas Papadimitriou
                    }
182 69dac1ba Kostas Papadimitriou
                    var obj = store.get(val);
183 69dac1ba Kostas Papadimitriou
                    if (obj) {
184 69dac1ba Kostas Papadimitriou
                      data[key] = obj;
185 69dac1ba Kostas Papadimitriou
                      self.set(data, {silent:false});
186 69dac1ba Kostas Papadimitriou
                      clearInterval(retry);
187 69dac1ba Kostas Papadimitriou
                    }
188 69dac1ba Kostas Papadimitriou
                  }, 500);
189 69dac1ba Kostas Papadimitriou
                  return retry
190 69dac1ba Kostas Papadimitriou
                }
191 69dac1ba Kostas Papadimitriou
                retry_to_resolve(store, val, key);
192 95ff92b4 Kostas Papadimitriou
              }
193 95ff92b4 Kostas Papadimitriou
            }
194 31bc4729 Kostas Papadimitriou
            
195 31bc4729 Kostas Papadimitriou
            var self = this;
196 69dac1ba Kostas Papadimitriou
            function init_bindings(instance, store, key, attr, attr_resolver) {
197 69dac1ba Kostas Papadimitriou
              instance.bind('change:' + attr, function(model) {
198 69dac1ba Kostas Papadimitriou
                resolve_related_instance.call(model, store, key, attr_resolver(model, attr));
199 69dac1ba Kostas Papadimitriou
              }, this);
200 69dac1ba Kostas Papadimitriou
201 69dac1ba Kostas Papadimitriou
              instance.bind('add', function(model) {
202 69dac1ba Kostas Papadimitriou
                resolve_related_instance.call(model, store, key, attr_resolver(model, attr));
203 69dac1ba Kostas Papadimitriou
              }, this);
204 69dac1ba Kostas Papadimitriou
            }
205 95ff92b4 Kostas Papadimitriou
206 69dac1ba Kostas Papadimitriou
            init_bindings(this, store, key, attr, attr_resolver);
207 31bc4729 Kostas Papadimitriou
            resolve_related_instance.call(this, store, key, attr_resolver(this, attr));
208 95ff92b4 Kostas Papadimitriou
          }, this);
209 95ff92b4 Kostas Papadimitriou
        },
210 95ff92b4 Kostas Papadimitriou
        
211 95ff92b4 Kostas Papadimitriou
        _proxy_model_cache: {},
212 95ff92b4 Kostas Papadimitriou
        
213 68cc89f6 Kostas Papadimitriou
        _bind_model: function(model, attr, check_attr, cb) {
214 68cc89f6 Kostas Papadimitriou
          var proxy_cache_key = attr + '_' + check_attr;
215 68cc89f6 Kostas Papadimitriou
          if (this._proxy_model_cache[proxy_cache_key]) {
216 68cc89f6 Kostas Papadimitriou
            var proxy = this._proxy_model_cache[proxy_cache_key];
217 68cc89f6 Kostas Papadimitriou
            proxy[0].unbind('change', proxy[1]);
218 95ff92b4 Kostas Papadimitriou
          }
219 89dc40ca Kostas Papadimitriou
          var data = {};
220 68cc89f6 Kostas Papadimitriou
          var changebind = _.bind(function() {
221 68cc89f6 Kostas Papadimitriou
            data[attr] = cb.call(this, this.get(check_attr));
222 68cc89f6 Kostas Papadimitriou
            this.set(data);
223 68cc89f6 Kostas Papadimitriou
          }, this);
224 68cc89f6 Kostas Papadimitriou
          model.bind('change', changebind);
225 68cc89f6 Kostas Papadimitriou
          this._proxy_model_cache[proxy_cache_key] = [model, changebind];
226 68cc89f6 Kostas Papadimitriou
        },
227 68cc89f6 Kostas Papadimitriou
228 68cc89f6 Kostas Papadimitriou
        _bind_attr: function(attr, check_attr, cb) {
229 95ff92b4 Kostas Papadimitriou
          this.bind('change:' + check_attr, function() {
230 95ff92b4 Kostas Papadimitriou
            if (this.get(check_attr) instanceof models.Model) {
231 95ff92b4 Kostas Papadimitriou
              var model = this.get(check_attr);
232 68cc89f6 Kostas Papadimitriou
              this._bind_model(model, attr, check_attr, cb);
233 95ff92b4 Kostas Papadimitriou
            }
234 95ff92b4 Kostas Papadimitriou
            var val = cb.call(this, this.get(check_attr));
235 95ff92b4 Kostas Papadimitriou
            var data = {};
236 95ff92b4 Kostas Papadimitriou
            if (this.get(attr) !== val) {
237 95ff92b4 Kostas Papadimitriou
              data[attr] = val;
238 95ff92b4 Kostas Papadimitriou
              this.set(data);
239 95ff92b4 Kostas Papadimitriou
            }
240 95ff92b4 Kostas Papadimitriou
          }, this);
241 95ff92b4 Kostas Papadimitriou
        },
242 95ff92b4 Kostas Papadimitriou
243 68cc89f6 Kostas Papadimitriou
        _set_proxy_attr: function(attr, check_attr, cb) {
244 68cc89f6 Kostas Papadimitriou
          // initial set
245 68cc89f6 Kostas Papadimitriou
          var data = {};
246 68cc89f6 Kostas Papadimitriou
          data[attr] = cb.call(this, this.get(check_attr));
247 68cc89f6 Kostas Papadimitriou
          if (data[attr] !== undefined) {
248 68cc89f6 Kostas Papadimitriou
            this.set(data, {silent:true});
249 68cc89f6 Kostas Papadimitriou
          }
250 68cc89f6 Kostas Papadimitriou
          if(this.get(check_attr) instanceof models.Model) {
251 68cc89f6 Kostas Papadimitriou
            this._bind_model(this.get(check_attr), attr, check_attr, cb);
252 68cc89f6 Kostas Papadimitriou
          }
253 68cc89f6 Kostas Papadimitriou
          this._bind_attr(attr, check_attr, cb);
254 68cc89f6 Kostas Papadimitriou
        },
255 68cc89f6 Kostas Papadimitriou
256 95ff92b4 Kostas Papadimitriou
        init_proxy_attrs: function() {
257 95ff92b4 Kostas Papadimitriou
          _.each(this.proxy_attrs, function(opts, attr){
258 95ff92b4 Kostas Papadimitriou
            var cb = opts[1];
259 95ff92b4 Kostas Papadimitriou
            _.each(opts[0], function(check_attr){
260 95ff92b4 Kostas Papadimitriou
              this._set_proxy_attr(attr, check_attr, cb)
261 95ff92b4 Kostas Papadimitriou
            }, this);
262 95ff92b4 Kostas Papadimitriou
          }, this);
263 95ff92b4 Kostas Papadimitriou
        },
264 95ff92b4 Kostas Papadimitriou
        
265 8d08f18a Kostas Papadimitriou
        handle_remove: function() {
266 643de8c0 Kostas Papadimitriou
            if (this.get("status") == 'DELETED') {
267 8d08f18a Kostas Papadimitriou
                if (this.collection) {
268 643de8c0 Kostas Papadimitriou
                    try { this.clear_pending_action();} catch (err) {};
269 643de8c0 Kostas Papadimitriou
                    try { this.reset_pending_actions();} catch (err) {};
270 2c9bfad1 Kostas Papadimitriou
                    try { this.stop_stats_update();} catch (err) {};
271 8d08f18a Kostas Papadimitriou
                    this.collection.remove(this.id);
272 8d08f18a Kostas Papadimitriou
                }
273 8d08f18a Kostas Papadimitriou
            }
274 8d08f18a Kostas Papadimitriou
        },
275 8d08f18a Kostas Papadimitriou
        
276 8d08f18a Kostas Papadimitriou
        // custom set method to allow submodels to use
277 8d08f18a Kostas Papadimitriou
        // set_<attr> methods for handling the value of each
278 8d08f18a Kostas Papadimitriou
        // attribute and overriding the default set method
279 8d08f18a Kostas Papadimitriou
        // for specific parameters
280 8d08f18a Kostas Papadimitriou
        set: function(params, options) {
281 8d08f18a Kostas Papadimitriou
            _.each(params, _.bind(function(value, key){
282 8d08f18a Kostas Papadimitriou
                if (this["set_" + key]) {
283 8d08f18a Kostas Papadimitriou
                    params[key] = this["set_" + key](value);
284 8d08f18a Kostas Papadimitriou
                }
285 8d08f18a Kostas Papadimitriou
            }, this))
286 8d08f18a Kostas Papadimitriou
            var ret = bb.Model.prototype.set.call(this, params, options);
287 8d08f18a Kostas Papadimitriou
            return ret;
288 8d08f18a Kostas Papadimitriou
        },
289 8d08f18a Kostas Papadimitriou
290 8d08f18a Kostas Papadimitriou
        url: function(options) {
291 c4fff449 Kostas Papadimitriou
            return getUrl.call(this, this.base_url) + "/" + this.id;
292 8d08f18a Kostas Papadimitriou
        },
293 8d08f18a Kostas Papadimitriou
294 8d08f18a Kostas Papadimitriou
        api_path: function(options) {
295 8d08f18a Kostas Papadimitriou
            return this.path + "/" + this.id;
296 8d08f18a Kostas Papadimitriou
        },
297 8d08f18a Kostas Papadimitriou
298 8d08f18a Kostas Papadimitriou
        parse: function(resp, xhr) {
299 8d08f18a Kostas Papadimitriou
        },
300 8d08f18a Kostas Papadimitriou
301 a429705e Kostas Papadimitriou
        remove: function(complete, error, success) {
302 a429705e Kostas Papadimitriou
            this.api_call(this.api_path(), "delete", undefined, complete, error, success);
303 2506f741 Kostas Papadimitriou
        },
304 2506f741 Kostas Papadimitriou
305 2506f741 Kostas Papadimitriou
        changedKeys: function() {
306 2506f741 Kostas Papadimitriou
            return _.keys(this.changedAttributes() || {});
307 2506f741 Kostas Papadimitriou
        },
308 1e882dd7 Kostas Papadimitriou
            
309 1e882dd7 Kostas Papadimitriou
        // return list of changed attributes that included in passed list
310 1e882dd7 Kostas Papadimitriou
        // argument
311 1e882dd7 Kostas Papadimitriou
        getKeysChanged: function(keys) {
312 1e882dd7 Kostas Papadimitriou
            return _.intersection(keys, this.changedKeys());
313 1e882dd7 Kostas Papadimitriou
        },
314 1e882dd7 Kostas Papadimitriou
        
315 1e882dd7 Kostas Papadimitriou
        // boolean check of keys changed
316 1e882dd7 Kostas Papadimitriou
        keysChanged: function(keys) {
317 1e882dd7 Kostas Papadimitriou
            return this.getKeysChanged(keys).length > 0;
318 1e882dd7 Kostas Papadimitriou
        },
319 2506f741 Kostas Papadimitriou
320 1e882dd7 Kostas Papadimitriou
        // check if any of the passed attribues has changed
321 2506f741 Kostas Papadimitriou
        hasOnlyChange: function(keys) {
322 2506f741 Kostas Papadimitriou
            var ret = false;
323 2506f741 Kostas Papadimitriou
            _.each(keys, _.bind(function(key) {
324 2506f741 Kostas Papadimitriou
                if (this.changedKeys().length == 1 && this.changedKeys().indexOf(key) > -1) { ret = true};
325 2506f741 Kostas Papadimitriou
            }, this));
326 2506f741 Kostas Papadimitriou
            return ret;
327 8d08f18a Kostas Papadimitriou
        }
328 8d08f18a Kostas Papadimitriou
329 8d08f18a Kostas Papadimitriou
    })
330 8d08f18a Kostas Papadimitriou
    
331 8d08f18a Kostas Papadimitriou
    // Base object for all our model collections
332 8d08f18a Kostas Papadimitriou
    models.Collection = bb.Collection.extend({
333 8d08f18a Kostas Papadimitriou
        sync: snf.api.sync,
334 8d08f18a Kostas Papadimitriou
        api: snf.api,
335 47e34b99 Kostas Papadimitriou
        api_type: 'compute',
336 16392650 Kostas Papadimitriou
        supportIncUpdates: true,
337 47e34b99 Kostas Papadimitriou
338 47e34b99 Kostas Papadimitriou
        initialize: function() {
339 47e34b99 Kostas Papadimitriou
            models.Collection.__super__.initialize.apply(this, arguments);
340 258235f4 Kostas Papadimitriou
            this.api_call = _.bind(this.api.call, this);
341 69dac1ba Kostas Papadimitriou
            if (this.sortFields) {
342 69dac1ba Kostas Papadimitriou
              _.each(this.sortFields, function(f) {
343 69dac1ba Kostas Papadimitriou
                this.bind("change:" + f, _.bind(this.resort, this));
344 69dac1ba Kostas Papadimitriou
              }, this);
345 69dac1ba Kostas Papadimitriou
            }
346 69dac1ba Kostas Papadimitriou
        },
347 69dac1ba Kostas Papadimitriou
          
348 69dac1ba Kostas Papadimitriou
        resort: function() {
349 69dac1ba Kostas Papadimitriou
          this.sort();
350 47e34b99 Kostas Papadimitriou
        },
351 47e34b99 Kostas Papadimitriou
352 2cee7cb0 Kostas Papadimitriou
        url: function(options, method) {
353 2cee7cb0 Kostas Papadimitriou
            return getUrl.call(this, this.base_url) + (
354 2cee7cb0 Kostas Papadimitriou
                    options.details || this.details && method != 'create' ? '/detail' : '');
355 8d08f18a Kostas Papadimitriou
        },
356 8d08f18a Kostas Papadimitriou
357 8d08f18a Kostas Papadimitriou
        fetch: function(options) {
358 859a4609 Kostas Papadimitriou
            if (!options) { options = {} };
359 8d08f18a Kostas Papadimitriou
            // default to update
360 8d08f18a Kostas Papadimitriou
            if (!this.noUpdate) {
361 8d08f18a Kostas Papadimitriou
                if (options.update === undefined) { options.update = true };
362 6be6f0b6 Kostas Papadimitriou
                if (!options.removeMissing && options.refresh) { 
363 6be6f0b6 Kostas Papadimitriou
                  options.removeMissing = true;
364 6be6f0b6 Kostas Papadimitriou
                };
365 6be6f0b6 Kostas Papadimitriou
                // for collections which associated models don't support 
366 6be6f0b6 Kostas Papadimitriou
                // deleted state identification through attributes, resolve  
367 6be6f0b6 Kostas Papadimitriou
                // deleted entries by checking for missing objects in fetch 
368 6be6f0b6 Kostas Papadimitriou
                // responses.
369 6be6f0b6 Kostas Papadimitriou
                if (this.updateEntries && options.removeMissing === undefined) {
370 6be6f0b6 Kostas Papadimitriou
                  options.removeMissing = true;
371 6be6f0b6 Kostas Papadimitriou
                }
372 859a4609 Kostas Papadimitriou
            } else {
373 859a4609 Kostas Papadimitriou
                if (options.refresh === undefined) {
374 859a4609 Kostas Papadimitriou
                    options.refresh = true;
375 6be6f0b6 Kostas Papadimitriou
                    if (this.updateEntries) {
376 6be6f0b6 Kostas Papadimitriou
                      options.update = true;
377 6be6f0b6 Kostas Papadimitriou
                      options.removeMissing = true;
378 6be6f0b6 Kostas Papadimitriou
                    }
379 859a4609 Kostas Papadimitriou
                }
380 8d08f18a Kostas Papadimitriou
            }
381 8d08f18a Kostas Papadimitriou
            // custom event foreach fetch
382 8d08f18a Kostas Papadimitriou
            return bb.Collection.prototype.fetch.call(this, options)
383 8d08f18a Kostas Papadimitriou
        },
384 8d08f18a Kostas Papadimitriou
385 859a4609 Kostas Papadimitriou
        create: function(model, options) {
386 859a4609 Kostas Papadimitriou
            var coll = this;
387 859a4609 Kostas Papadimitriou
            options || (options = {});
388 859a4609 Kostas Papadimitriou
            model = this._prepareModel(model, options);
389 859a4609 Kostas Papadimitriou
            if (!model) return false;
390 859a4609 Kostas Papadimitriou
            var success = options.success;
391 859a4609 Kostas Papadimitriou
            options.success = function(nextModel, resp, xhr) {
392 213a8396 Kostas Papadimitriou
                if (coll.add_on_create) {
393 213a8396 Kostas Papadimitriou
                  coll.add(nextModel, options);
394 213a8396 Kostas Papadimitriou
                }
395 859a4609 Kostas Papadimitriou
                if (success) success(nextModel, resp, xhr);
396 859a4609 Kostas Papadimitriou
            };
397 859a4609 Kostas Papadimitriou
            model.save(null, options);
398 859a4609 Kostas Papadimitriou
            return model;
399 859a4609 Kostas Papadimitriou
        },
400 859a4609 Kostas Papadimitriou
401 1cc6e94f Kostas Papadimitriou
        get_fetcher: function(interval, increase, fast, increase_after_calls, max, initial_call, params) {
402 9ce969a7 Kostas Papadimitriou
            var fetch_params = params || {};
403 1cc6e94f Kostas Papadimitriou
            var handler_options = {};
404 258235f4 Kostas Papadimitriou
405 258235f4 Kostas Papadimitriou
            fetch_params.skips_timeouts = true;
406 1cc6e94f Kostas Papadimitriou
            handler_options.interval = interval;
407 1cc6e94f Kostas Papadimitriou
            handler_options.increase = increase;
408 1cc6e94f Kostas Papadimitriou
            handler_options.fast = fast;
409 1cc6e94f Kostas Papadimitriou
            handler_options.increase_after_calls = increase_after_calls;
410 1cc6e94f Kostas Papadimitriou
            handler_options.max= max;
411 258235f4 Kostas Papadimitriou
            handler_options.id = "collection id";
412 1cc6e94f Kostas Papadimitriou
413 8d08f18a Kostas Papadimitriou
            var last_ajax = undefined;
414 258235f4 Kostas Papadimitriou
            var callback = _.bind(function() {
415 9ce969a7 Kostas Papadimitriou
                // clone to avoid referenced objects
416 9ce969a7 Kostas Papadimitriou
                var params = _.clone(fetch_params);
417 8d08f18a Kostas Papadimitriou
                updater._ajax = last_ajax;
418 a89ef0f5 Kostas Papadimitriou
                
419 42fb39fb Kostas Papadimitriou
                // wait for previous request to finish
420 a89ef0f5 Kostas Papadimitriou
                if (last_ajax && last_ajax.readyState < 4 && last_ajax.statusText != "timeout") {
421 a89ef0f5 Kostas Papadimitriou
                    // opera readystate for 304 responses is 0
422 a89ef0f5 Kostas Papadimitriou
                    if (!($.browser.opera && last_ajax.readyState == 0 && last_ajax.status == 304)) {
423 a89ef0f5 Kostas Papadimitriou
                        return;
424 a89ef0f5 Kostas Papadimitriou
                    }
425 8d08f18a Kostas Papadimitriou
                }
426 9ce969a7 Kostas Papadimitriou
                last_ajax = this.fetch(params);
427 8d08f18a Kostas Papadimitriou
            }, this);
428 258235f4 Kostas Papadimitriou
            handler_options.callback = callback;
429 8d08f18a Kostas Papadimitriou
430 258235f4 Kostas Papadimitriou
            var updater = new snf.api.updateHandler(_.clone(_.extend(handler_options, fetch_params)));
431 35855ff5 Kostas Papadimitriou
            snf.api.bind("call", _.throttle(_.bind(function(){ updater.faster(true)}, this)), 1000);
432 8d08f18a Kostas Papadimitriou
            return updater;
433 8d08f18a Kostas Papadimitriou
        }
434 8d08f18a Kostas Papadimitriou
    });
435 8d08f18a Kostas Papadimitriou
    
436 8d08f18a Kostas Papadimitriou
    // Image model
437 8d08f18a Kostas Papadimitriou
    models.Image = models.Model.extend({
438 8d08f18a Kostas Papadimitriou
        path: 'images',
439 8f53cfa3 Kostas Papadimitriou
        
440 8d08f18a Kostas Papadimitriou
        get_size: function() {
441 0dee4086 Kostas Papadimitriou
            return parseInt(this.get('metadata') ? this.get('metadata').size : -1)
442 820ef2f0 Kostas Papadimitriou
        },
443 820ef2f0 Kostas Papadimitriou
444 88d6d677 Kostas Papadimitriou
        get_description: function(escape) {
445 88d6d677 Kostas Papadimitriou
            if (escape == undefined) { escape = true };
446 88d6d677 Kostas Papadimitriou
            if (escape) { return this.escape('description') || "No description available"}
447 88d6d677 Kostas Papadimitriou
            return this.get('description') || "No description available."
448 88d6d677 Kostas Papadimitriou
        },
449 88d6d677 Kostas Papadimitriou
450 948042ca Kostas Papadimitriou
        get_meta: function(key) {
451 0dee4086 Kostas Papadimitriou
            if (this.get('metadata') && this.get('metadata')) {
452 0dee4086 Kostas Papadimitriou
                if (!this.get('metadata')[key]) { return null }
453 0dee4086 Kostas Papadimitriou
                return _.escape(this.get('metadata')[key]);
454 88d6d677 Kostas Papadimitriou
            } else {
455 88d6d677 Kostas Papadimitriou
                return null;
456 88d6d677 Kostas Papadimitriou
            }
457 88d6d677 Kostas Papadimitriou
        },
458 88d6d677 Kostas Papadimitriou
459 88d6d677 Kostas Papadimitriou
        get_meta_keys: function() {
460 0dee4086 Kostas Papadimitriou
            if (this.get('metadata') && this.get('metadata')) {
461 0dee4086 Kostas Papadimitriou
                return _.keys(this.get('metadata'));
462 88d6d677 Kostas Papadimitriou
            } else {
463 88d6d677 Kostas Papadimitriou
                return [];
464 948042ca Kostas Papadimitriou
            }
465 948042ca Kostas Papadimitriou
        },
466 948042ca Kostas Papadimitriou
467 948042ca Kostas Papadimitriou
        get_owner: function() {
468 1faf0b9c Kostas Papadimitriou
            return this.get('owner') || _.keys(synnefo.config.system_images_owners)[0];
469 948042ca Kostas Papadimitriou
        },
470 948042ca Kostas Papadimitriou
471 8f53cfa3 Kostas Papadimitriou
        get_owner_uuid: function() {
472 8f53cfa3 Kostas Papadimitriou
            return this.get('owner_uuid');
473 8f53cfa3 Kostas Papadimitriou
        },
474 8f53cfa3 Kostas Papadimitriou
475 fc6f43c3 Kostas Papadimitriou
        is_system_image: function() {
476 fc6f43c3 Kostas Papadimitriou
          var owner = this.get_owner();
477 fc6f43c3 Kostas Papadimitriou
          return _.include(_.keys(synnefo.config.system_images_owners), owner)
478 fc6f43c3 Kostas Papadimitriou
        },
479 fc6f43c3 Kostas Papadimitriou
480 fc6f43c3 Kostas Papadimitriou
        owned_by: function(user) {
481 fc6f43c3 Kostas Papadimitriou
          if (!user) { user = synnefo.user }
482 afe9ca75 Kostas Papadimitriou
          return user.get_username() == this.get('owner_uuid');
483 fc6f43c3 Kostas Papadimitriou
        },
484 fc6f43c3 Kostas Papadimitriou
485 babfdb9b Kostas Papadimitriou
        display_owner: function() {
486 babfdb9b Kostas Papadimitriou
            var owner = this.get_owner();
487 babfdb9b Kostas Papadimitriou
            if (_.include(_.keys(synnefo.config.system_images_owners), owner)) {
488 babfdb9b Kostas Papadimitriou
                return synnefo.config.system_images_owners[owner];
489 babfdb9b Kostas Papadimitriou
            } else {
490 babfdb9b Kostas Papadimitriou
                return owner;
491 babfdb9b Kostas Papadimitriou
            }
492 babfdb9b Kostas Papadimitriou
        },
493 03639c6d Kostas Papadimitriou
    
494 5a434360 Kostas Papadimitriou
        get_readable_size: function() {
495 03639c6d Kostas Papadimitriou
            if (this.is_deleted()) {
496 03639c6d Kostas Papadimitriou
                return synnefo.config.image_deleted_size_title || '(none)';
497 03639c6d Kostas Papadimitriou
            }
498 03639c6d Kostas Papadimitriou
            return this.get_size() > 0 ? util.readablizeBytes(this.get_size() * 1024 * 1024) : '(none)';
499 5a434360 Kostas Papadimitriou
        },
500 5a434360 Kostas Papadimitriou
501 820ef2f0 Kostas Papadimitriou
        get_os: function() {
502 88d6d677 Kostas Papadimitriou
            return this.get_meta('OS');
503 ccb14ad0 Kostas Papadimitriou
        },
504 ccb14ad0 Kostas Papadimitriou
505 6a0915f7 Kostas Papadimitriou
        get_gui: function() {
506 6a0915f7 Kostas Papadimitriou
            return this.get_meta('GUI');
507 6a0915f7 Kostas Papadimitriou
        },
508 6a0915f7 Kostas Papadimitriou
509 bdfb089f Kostas Papadimitriou
        get_created_users: function() {
510 bdfb089f Kostas Papadimitriou
            try {
511 bdfb089f Kostas Papadimitriou
              var users = this.get_meta('users').split(" ");
512 bdfb089f Kostas Papadimitriou
            } catch (err) { users = null }
513 a6213c91 Kostas Papadimitriou
            if (!users) {
514 a6213c91 Kostas Papadimitriou
                var osfamily = this.get_meta('osfamily');
515 a6213c91 Kostas Papadimitriou
                if (osfamily == 'windows') { 
516 a6213c91 Kostas Papadimitriou
                  users = ['Administrator'];
517 a6213c91 Kostas Papadimitriou
                } else {
518 a6213c91 Kostas Papadimitriou
                  users = ['root'];
519 a6213c91 Kostas Papadimitriou
                }
520 a6213c91 Kostas Papadimitriou
            }
521 bdfb089f Kostas Papadimitriou
            return users;
522 47276ec2 Kostas Papadimitriou
        },
523 47276ec2 Kostas Papadimitriou
524 ccb14ad0 Kostas Papadimitriou
        get_sort_order: function() {
525 0dee4086 Kostas Papadimitriou
            return parseInt(this.get('metadata') ? this.get('metadata').sortorder : -1)
526 47276ec2 Kostas Papadimitriou
        },
527 2cee7cb0 Kostas Papadimitriou
528 2cee7cb0 Kostas Papadimitriou
        get_vm: function() {
529 2cee7cb0 Kostas Papadimitriou
            var vm_id = this.get("serverRef");
530 2cee7cb0 Kostas Papadimitriou
            var vm = undefined;
531 2cee7cb0 Kostas Papadimitriou
            vm = storage.vms.get(vm_id);
532 2cee7cb0 Kostas Papadimitriou
            return vm;
533 2cee7cb0 Kostas Papadimitriou
        },
534 948042ca Kostas Papadimitriou
535 948042ca Kostas Papadimitriou
        is_public: function() {
536 77baab80 Kostas Papadimitriou
            return this.get('is_public') == undefined ? true : this.get('is_public');
537 948042ca Kostas Papadimitriou
        },
538 03639c6d Kostas Papadimitriou
539 03639c6d Kostas Papadimitriou
        is_deleted: function() {
540 03639c6d Kostas Papadimitriou
            return this.get('status') == "DELETED"
541 03639c6d Kostas Papadimitriou
        },
542 47276ec2 Kostas Papadimitriou
        
543 bdfb089f Kostas Papadimitriou
        ssh_keys_paths: function() {
544 bdfb089f Kostas Papadimitriou
            return _.map(this.get_created_users(), function(username) {
545 bdfb089f Kostas Papadimitriou
                prepend = '';
546 bdfb089f Kostas Papadimitriou
                if (username != 'root') {
547 bdfb089f Kostas Papadimitriou
                    prepend = '/home'
548 bdfb089f Kostas Papadimitriou
                }
549 5d9ac244 Kostas Papadimitriou
                return {'user': username, 'path': '{1}/{0}/.ssh/authorized_keys'.format(username, 
550 5d9ac244 Kostas Papadimitriou
                                                             prepend)};
551 bdfb089f Kostas Papadimitriou
            });
552 47276ec2 Kostas Papadimitriou
        },
553 47276ec2 Kostas Papadimitriou
554 47276ec2 Kostas Papadimitriou
        _supports_ssh: function() {
555 5acb8b8a Kostas Papadimitriou
            var os_list = synnefo.config.support_ssh_os_list;
556 5acb8b8a Kostas Papadimitriou
            var osfamily_list = synnefo.config.support_ssh_os_family_list;
557 5acb8b8a Kostas Papadimitriou
            
558 5acb8b8a Kostas Papadimitriou
            var os = this.get_os();
559 5acb8b8a Kostas Papadimitriou
            if (os_list.indexOf(os) > -1) {
560 47276ec2 Kostas Papadimitriou
                return true;
561 47276ec2 Kostas Papadimitriou
            }
562 5acb8b8a Kostas Papadimitriou
            
563 5acb8b8a Kostas Papadimitriou
            var osfamily = this.get_meta("osfamily");
564 5acb8b8a Kostas Papadimitriou
            if (osfamily_list.indexOf(osfamily) > -1) {
565 5acb8b8a Kostas Papadimitriou
              return true
566 d3e4bca7 Kostas Papadimitriou
            }
567 5acb8b8a Kostas Papadimitriou
568 47276ec2 Kostas Papadimitriou
            return false;
569 47276ec2 Kostas Papadimitriou
        },
570 47276ec2 Kostas Papadimitriou
571 47276ec2 Kostas Papadimitriou
        supports: function(feature) {
572 47276ec2 Kostas Papadimitriou
            if (feature == "ssh") {
573 47276ec2 Kostas Papadimitriou
                return this._supports_ssh()
574 47276ec2 Kostas Papadimitriou
            }
575 47276ec2 Kostas Papadimitriou
            return false;
576 47276ec2 Kostas Papadimitriou
        },
577 47276ec2 Kostas Papadimitriou
578 47276ec2 Kostas Papadimitriou
        personality_data_for_keys: function(keys) {
579 5d9ac244 Kostas Papadimitriou
            return _.map(this.ssh_keys_paths(), function(pathinfo) {
580 bdfb089f Kostas Papadimitriou
                var contents = '';
581 bdfb089f Kostas Papadimitriou
                _.each(keys, function(key){
582 bdfb089f Kostas Papadimitriou
                    contents = contents + key.get("content") + "\n"
583 bdfb089f Kostas Papadimitriou
                });
584 bdfb089f Kostas Papadimitriou
                contents = $.base64.encode(contents);
585 bdfb089f Kostas Papadimitriou
586 bdfb089f Kostas Papadimitriou
                return {
587 5d9ac244 Kostas Papadimitriou
                    path: pathinfo.path,
588 5d9ac244 Kostas Papadimitriou
                    contents: contents,
589 5d9ac244 Kostas Papadimitriou
                    mode: 0600,
590 8b14f29d Kostas Papadimitriou
                    owner: pathinfo.user
591 bdfb089f Kostas Papadimitriou
                }
592 47276ec2 Kostas Papadimitriou
            });
593 8d08f18a Kostas Papadimitriou
        }
594 8d08f18a Kostas Papadimitriou
    });
595 8d08f18a Kostas Papadimitriou
596 8d08f18a Kostas Papadimitriou
    // Flavor model
597 8d08f18a Kostas Papadimitriou
    models.Flavor = models.Model.extend({
598 8d08f18a Kostas Papadimitriou
        path: 'flavors',
599 8d08f18a Kostas Papadimitriou
600 8d08f18a Kostas Papadimitriou
        details_string: function() {
601 8d08f18a Kostas Papadimitriou
            return "{0} CPU, {1}MB, {2}GB".format(this.get('cpu'), this.get('ram'), this.get('disk'));
602 820ef2f0 Kostas Papadimitriou
        },
603 820ef2f0 Kostas Papadimitriou
604 820ef2f0 Kostas Papadimitriou
        get_disk_size: function() {
605 9c8f31ad Kostas Papadimitriou
            return parseInt(this.get("disk") * 1024)
606 d965e6dc Kostas Papadimitriou
        },
607 d965e6dc Kostas Papadimitriou
608 7ac49c2e Kostas Papadimitriou
        get_ram_size: function() {
609 7ac49c2e Kostas Papadimitriou
            return parseInt(this.get("ram"))
610 7ac49c2e Kostas Papadimitriou
        },
611 7ac49c2e Kostas Papadimitriou
612 d965e6dc Kostas Papadimitriou
        get_disk_template_info: function() {
613 d965e6dc Kostas Papadimitriou
            var info = snf.config.flavors_disk_templates_info[this.get("disk_template")];
614 d965e6dc Kostas Papadimitriou
            if (!info) {
615 d965e6dc Kostas Papadimitriou
                info = { name: this.get("disk_template"), description:'' };
616 d965e6dc Kostas Papadimitriou
            }
617 d965e6dc Kostas Papadimitriou
            return info
618 7ac49c2e Kostas Papadimitriou
        },
619 7ac49c2e Kostas Papadimitriou
620 7ac49c2e Kostas Papadimitriou
        disk_to_bytes: function() {
621 7ac49c2e Kostas Papadimitriou
            return parseInt(this.get("disk")) * 1024 * 1024 * 1024;
622 7ac49c2e Kostas Papadimitriou
        },
623 7ac49c2e Kostas Papadimitriou
624 7ac49c2e Kostas Papadimitriou
        ram_to_bytes: function() {
625 7ac49c2e Kostas Papadimitriou
            return parseInt(this.get("ram")) * 1024 * 1024;
626 7ac49c2e Kostas Papadimitriou
        },
627 820ef2f0 Kostas Papadimitriou
628 8d08f18a Kostas Papadimitriou
    });
629 8d08f18a Kostas Papadimitriou
    
630 101e6604 Kostas Papadimitriou
    models.ParamsList = function(){this.initialize.apply(this, arguments)};
631 101e6604 Kostas Papadimitriou
    _.extend(models.ParamsList.prototype, bb.Events, {
632 101e6604 Kostas Papadimitriou
633 101e6604 Kostas Papadimitriou
        initialize: function(parent, param_name) {
634 101e6604 Kostas Papadimitriou
            this.parent = parent;
635 101e6604 Kostas Papadimitriou
            this.actions = {};
636 101e6604 Kostas Papadimitriou
            this.param_name = param_name;
637 101e6604 Kostas Papadimitriou
            this.length = 0;
638 101e6604 Kostas Papadimitriou
        },
639 101e6604 Kostas Papadimitriou
        
640 101e6604 Kostas Papadimitriou
        has_action: function(action) {
641 101e6604 Kostas Papadimitriou
            return this.actions[action] ? true : false;
642 101e6604 Kostas Papadimitriou
        },
643 101e6604 Kostas Papadimitriou
            
644 101e6604 Kostas Papadimitriou
        _parse_params: function(arguments) {
645 101e6604 Kostas Papadimitriou
            if (arguments.length <= 1) {
646 101e6604 Kostas Papadimitriou
                return [];
647 101e6604 Kostas Papadimitriou
            }
648 101e6604 Kostas Papadimitriou
649 101e6604 Kostas Papadimitriou
            var args = _.toArray(arguments);
650 101e6604 Kostas Papadimitriou
            return args.splice(1);
651 101e6604 Kostas Papadimitriou
        },
652 101e6604 Kostas Papadimitriou
653 101e6604 Kostas Papadimitriou
        contains: function(action, params) {
654 101e6604 Kostas Papadimitriou
            params = this._parse_params(arguments);
655 101e6604 Kostas Papadimitriou
            var has_action = this.has_action(action);
656 101e6604 Kostas Papadimitriou
            if (!has_action) { return false };
657 101e6604 Kostas Papadimitriou
658 101e6604 Kostas Papadimitriou
            var paramsEqual = false;
659 101e6604 Kostas Papadimitriou
            _.each(this.actions[action], function(action_params) {
660 101e6604 Kostas Papadimitriou
                if (_.isEqual(action_params, params)) {
661 101e6604 Kostas Papadimitriou
                    paramsEqual = true;
662 101e6604 Kostas Papadimitriou
                }
663 101e6604 Kostas Papadimitriou
            });
664 101e6604 Kostas Papadimitriou
                
665 101e6604 Kostas Papadimitriou
            return paramsEqual;
666 101e6604 Kostas Papadimitriou
        },
667 101e6604 Kostas Papadimitriou
        
668 101e6604 Kostas Papadimitriou
        is_empty: function() {
669 101e6604 Kostas Papadimitriou
            return _.isEmpty(this.actions);
670 101e6604 Kostas Papadimitriou
        },
671 101e6604 Kostas Papadimitriou
672 101e6604 Kostas Papadimitriou
        add: function(action, params) {
673 101e6604 Kostas Papadimitriou
            params = this._parse_params(arguments);
674 101e6604 Kostas Papadimitriou
            if (this.contains.apply(this, arguments)) { return this };
675 101e6604 Kostas Papadimitriou
            var isnew = false
676 101e6604 Kostas Papadimitriou
            if (!this.has_action(action)) {
677 101e6604 Kostas Papadimitriou
                this.actions[action] = [];
678 101e6604 Kostas Papadimitriou
                isnew = true;
679 101e6604 Kostas Papadimitriou
            };
680 101e6604 Kostas Papadimitriou
681 101e6604 Kostas Papadimitriou
            this.actions[action].push(params);
682 101e6604 Kostas Papadimitriou
            this.parent.trigger("change:" + this.param_name, this.parent, this);
683 101e6604 Kostas Papadimitriou
            if (isnew) {
684 101e6604 Kostas Papadimitriou
                this.trigger("add", action, params);
685 101e6604 Kostas Papadimitriou
            } else {
686 101e6604 Kostas Papadimitriou
                this.trigger("change", action, params);
687 101e6604 Kostas Papadimitriou
            }
688 101e6604 Kostas Papadimitriou
            return this;
689 101e6604 Kostas Papadimitriou
        },
690 101e6604 Kostas Papadimitriou
        
691 101e6604 Kostas Papadimitriou
        remove_all: function(action) {
692 101e6604 Kostas Papadimitriou
            if (this.has_action(action)) {
693 101e6604 Kostas Papadimitriou
                delete this.actions[action];
694 101e6604 Kostas Papadimitriou
                this.parent.trigger("change:" + this.param_name, this.parent, this);
695 101e6604 Kostas Papadimitriou
                this.trigger("remove", action);
696 101e6604 Kostas Papadimitriou
            }
697 101e6604 Kostas Papadimitriou
            return this;
698 101e6604 Kostas Papadimitriou
        },
699 101e6604 Kostas Papadimitriou
700 101e6604 Kostas Papadimitriou
        reset: function() {
701 101e6604 Kostas Papadimitriou
            this.actions = {};
702 101e6604 Kostas Papadimitriou
            this.parent.trigger("change:" + this.param_name, this.parent, this);
703 101e6604 Kostas Papadimitriou
            this.trigger("reset");
704 101e6604 Kostas Papadimitriou
            this.trigger("remove");
705 101e6604 Kostas Papadimitriou
        },
706 101e6604 Kostas Papadimitriou
707 101e6604 Kostas Papadimitriou
        remove: function(action, params) {
708 101e6604 Kostas Papadimitriou
            params = this._parse_params(arguments);
709 101e6604 Kostas Papadimitriou
            if (!this.has_action(action)) { return this };
710 101e6604 Kostas Papadimitriou
            var index = -1;
711 101e6604 Kostas Papadimitriou
            _.each(this.actions[action], _.bind(function(action_params) {
712 101e6604 Kostas Papadimitriou
                if (_.isEqual(action_params, params)) {
713 101e6604 Kostas Papadimitriou
                    index = this.actions[action].indexOf(action_params);
714 101e6604 Kostas Papadimitriou
                }
715 101e6604 Kostas Papadimitriou
            }, this));
716 101e6604 Kostas Papadimitriou
            
717 101e6604 Kostas Papadimitriou
            if (index > -1) {
718 101e6604 Kostas Papadimitriou
                this.actions[action].splice(index, 1);
719 101e6604 Kostas Papadimitriou
                if (_.isEmpty(this.actions[action])) {
720 101e6604 Kostas Papadimitriou
                    delete this.actions[action];
721 101e6604 Kostas Papadimitriou
                }
722 101e6604 Kostas Papadimitriou
                this.parent.trigger("change:" + this.param_name, this.parent, this);
723 101e6604 Kostas Papadimitriou
                this.trigger("remove", action, params);
724 101e6604 Kostas Papadimitriou
            }
725 101e6604 Kostas Papadimitriou
        }
726 101e6604 Kostas Papadimitriou
727 101e6604 Kostas Papadimitriou
    });
728 8d08f18a Kostas Papadimitriou
729 8d08f18a Kostas Papadimitriou
    // Virtualmachine model
730 8d08f18a Kostas Papadimitriou
    models.VM = models.Model.extend({
731 8d08f18a Kostas Papadimitriou
732 8d08f18a Kostas Papadimitriou
        path: 'servers',
733 8d08f18a Kostas Papadimitriou
        has_status: true,
734 45b964d3 Kostas Papadimitriou
        proxy_attrs: {
735 45b964d3 Kostas Papadimitriou
          'busy': [
736 45b964d3 Kostas Papadimitriou
            ['status', 'state'], function() {
737 ba7421ce Kostas Papadimitriou
              return !_.contains(['ACTIVE', 'STOPPED'], this.get('status'));
738 45b964d3 Kostas Papadimitriou
            }
739 b3aee2e0 Kostas Papadimitriou
          ],
740 b3aee2e0 Kostas Papadimitriou
          'in_progress': [
741 b3aee2e0 Kostas Papadimitriou
            ['status', 'state'], function() {
742 b3aee2e0 Kostas Papadimitriou
              return this.in_transition();
743 b3aee2e0 Kostas Papadimitriou
            }
744 3cac4a52 Kostas Papadimitriou
          ]
745 45b964d3 Kostas Papadimitriou
        },
746 45b964d3 Kostas Papadimitriou
747 8d08f18a Kostas Papadimitriou
        initialize: function(params) {
748 365af933 Kostas Papadimitriou
            var self = this;
749 365af933 Kostas Papadimitriou
            this.ports = new Backbone.FilteredCollection(undefined, {
750 365af933 Kostas Papadimitriou
              collection: synnefo.storage.ports,
751 365af933 Kostas Papadimitriou
              collectionFilter: function(m) {
752 365af933 Kostas Papadimitriou
                return self.id == m.get('device_id')
753 365af933 Kostas Papadimitriou
            }});
754 365af933 Kostas Papadimitriou
755 8d08f18a Kostas Papadimitriou
            this.pending_firewalls = {};
756 8d08f18a Kostas Papadimitriou
            
757 8d08f18a Kostas Papadimitriou
            models.VM.__super__.initialize.apply(this, arguments);
758 8d08f18a Kostas Papadimitriou
759 365af933 Kostas Papadimitriou
760 8d08f18a Kostas Papadimitriou
            this.set({state: params.status || "ERROR"});
761 8d08f18a Kostas Papadimitriou
            this.log = new snf.logging.logger("VM " + this.id);
762 8d08f18a Kostas Papadimitriou
            this.pending_action = undefined;
763 8d08f18a Kostas Papadimitriou
            
764 8d08f18a Kostas Papadimitriou
            // init stats parameter
765 8d08f18a Kostas Papadimitriou
            this.set({'stats': undefined}, {silent: true});
766 8d08f18a Kostas Papadimitriou
            // defaults to not update the stats
767 8d08f18a Kostas Papadimitriou
            // each view should handle this vm attribute 
768 8d08f18a Kostas Papadimitriou
            // depending on if it displays stat images or not
769 8d08f18a Kostas Papadimitriou
            this.do_update_stats = false;
770 8d08f18a Kostas Papadimitriou
            
771 8d08f18a Kostas Papadimitriou
            // interval time
772 8d08f18a Kostas Papadimitriou
            // this will dynamicaly change if the server responds that
773 8d08f18a Kostas Papadimitriou
            // images get refreshed on different intervals
774 8d08f18a Kostas Papadimitriou
            this.stats_update_interval = synnefo.config.STATS_INTERVAL || 5000;
775 96d725ac Kostas Papadimitriou
            this.stats_available = false;
776 8d08f18a Kostas Papadimitriou
777 8d08f18a Kostas Papadimitriou
            // initialize interval
778 8d08f18a Kostas Papadimitriou
            this.init_stats_intervals(this.stats_update_interval);
779 8d08f18a Kostas Papadimitriou
            
780 1e882dd7 Kostas Papadimitriou
            // handle progress message on instance change
781 1e882dd7 Kostas Papadimitriou
            this.bind("change", _.bind(this.update_status_message, this));
782 a3ac649e Kostas Papadimitriou
            this.bind("change:task_state", _.bind(this.update_status, this));
783 1e882dd7 Kostas Papadimitriou
            // force update of progress message
784 1e882dd7 Kostas Papadimitriou
            this.update_status_message(true);
785 1e882dd7 Kostas Papadimitriou
            
786 8d08f18a Kostas Papadimitriou
            // default values
787 126a01f2 Kostas Papadimitriou
            this.bind("change:state", _.bind(function(){
788 126a01f2 Kostas Papadimitriou
                if (this.state() == "DESTROY") { 
789 126a01f2 Kostas Papadimitriou
                    this.handle_destroy() 
790 126a01f2 Kostas Papadimitriou
                }
791 126a01f2 Kostas Papadimitriou
            }, this));
792 8d08f18a Kostas Papadimitriou
793 8d08f18a Kostas Papadimitriou
        },
794 46987d1c Kostas Papadimitriou
        
795 46987d1c Kostas Papadimitriou
        get_public_ips: function() {
796 46987d1c Kostas Papadimitriou
          var ips = [];
797 46987d1c Kostas Papadimitriou
          this.ports.filter(function(port) {
798 46987d1c Kostas Papadimitriou
            if (port.get('network') && !port.get('network').get('is_public')) { return }
799 46987d1c Kostas Papadimitriou
            if (!port.get("ips")) { return }
800 46987d1c Kostas Papadimitriou
            port.get("ips").each(function(ip) {
801 46987d1c Kostas Papadimitriou
              ips.push(ip);
802 46987d1c Kostas Papadimitriou
            });
803 46987d1c Kostas Papadimitriou
          });
804 46987d1c Kostas Papadimitriou
          return ips;
805 46987d1c Kostas Papadimitriou
        },
806 46987d1c Kostas Papadimitriou
807 3cac4a52 Kostas Papadimitriou
        has_public_ip: function() {
808 3cac4a52 Kostas Papadimitriou
          return this.ports.filter(function(port) {
809 3cac4a52 Kostas Papadimitriou
            return port.get("network") && 
810 3cac4a52 Kostas Papadimitriou
                   port.get("network").get("is_public") && 
811 3cac4a52 Kostas Papadimitriou
                   port.get("ips").length > 0;
812 3cac4a52 Kostas Papadimitriou
          }).length > 0;
813 3cac4a52 Kostas Papadimitriou
        },
814 3cac4a52 Kostas Papadimitriou
815 3cac4a52 Kostas Papadimitriou
        has_public_ipv6: function() {
816 3cac4a52 Kostas Papadimitriou
          return this.has_ip_version("v6", true);
817 3cac4a52 Kostas Papadimitriou
        },
818 3cac4a52 Kostas Papadimitriou
819 3cac4a52 Kostas Papadimitriou
        has_public_ipv4: function() {
820 3cac4a52 Kostas Papadimitriou
          return this.has_ip_version("v4", true);
821 3cac4a52 Kostas Papadimitriou
        },
822 3cac4a52 Kostas Papadimitriou
        
823 3cac4a52 Kostas Papadimitriou
        has_ip_version: function(ver, public) {
824 3cac4a52 Kostas Papadimitriou
          var found = false;
825 3cac4a52 Kostas Papadimitriou
          this.ports.each(function(port) {
826 3cac4a52 Kostas Papadimitriou
            if (found) { return }
827 3cac4a52 Kostas Papadimitriou
            if (public !== undefined) {
828 3cac4a52 Kostas Papadimitriou
              if (port.get("network") && 
829 3cac4a52 Kostas Papadimitriou
                  port.get("network").get("is_public") != public) {
830 3cac4a52 Kostas Papadimitriou
                return
831 3cac4a52 Kostas Papadimitriou
              }
832 3cac4a52 Kostas Papadimitriou
            }
833 3cac4a52 Kostas Papadimitriou
            port.get('ips').each(function(ip) {
834 3cac4a52 Kostas Papadimitriou
              if (found) { return }
835 3cac4a52 Kostas Papadimitriou
              if (ip.get("type") == ver) {
836 3cac4a52 Kostas Papadimitriou
                found = true
837 3cac4a52 Kostas Papadimitriou
              }
838 3cac4a52 Kostas Papadimitriou
            })
839 3cac4a52 Kostas Papadimitriou
          }, this)
840 3cac4a52 Kostas Papadimitriou
          return found;
841 3cac4a52 Kostas Papadimitriou
        },
842 8d08f18a Kostas Papadimitriou
843 8d08f18a Kostas Papadimitriou
        status: function(st) {
844 8d08f18a Kostas Papadimitriou
            if (!st) { return this.get("status")}
845 8d08f18a Kostas Papadimitriou
            return this.set({status:st});
846 8d08f18a Kostas Papadimitriou
        },
847 a3ac649e Kostas Papadimitriou
        
848 a3ac649e Kostas Papadimitriou
        update_status: function() {
849 a3ac649e Kostas Papadimitriou
            this.set_status(this.get('status'));
850 a3ac649e Kostas Papadimitriou
        },
851 8d08f18a Kostas Papadimitriou
852 8d08f18a Kostas Papadimitriou
        set_status: function(st) {
853 8d08f18a Kostas Papadimitriou
            var new_state = this.state_for_api_status(st);
854 8d08f18a Kostas Papadimitriou
            var transition = false;
855 8d08f18a Kostas Papadimitriou
856 8d08f18a Kostas Papadimitriou
            if (this.state() != new_state) {
857 8d08f18a Kostas Papadimitriou
                if (models.VM.STATES_TRANSITIONS[this.state()]) {
858 8d08f18a Kostas Papadimitriou
                    transition = this.state();
859 8d08f18a Kostas Papadimitriou
                }
860 8d08f18a Kostas Papadimitriou
            }
861 8d08f18a Kostas Papadimitriou
            
862 8d08f18a Kostas Papadimitriou
            // call it silently to avoid double change trigger
863 a3ac649e Kostas Papadimitriou
            var state = this.state_for_api_status(st);
864 a3ac649e Kostas Papadimitriou
            this.set({'state': state}, {silent: true});
865 8d08f18a Kostas Papadimitriou
            
866 8d08f18a Kostas Papadimitriou
            // trigger transition
867 572dbe3d Kostas Papadimitriou
            if (transition && models.VM.TRANSITION_STATES.indexOf(new_state) == -1) { 
868 572dbe3d Kostas Papadimitriou
                this.trigger("transition", {from:transition, to:new_state}) 
869 572dbe3d Kostas Papadimitriou
            };
870 8d08f18a Kostas Papadimitriou
            return st;
871 8d08f18a Kostas Papadimitriou
        },
872 1e882dd7 Kostas Papadimitriou
            
873 1e882dd7 Kostas Papadimitriou
        get_diagnostics: function(success) {
874 1e882dd7 Kostas Papadimitriou
            this.__make_api_call(this.get_diagnostics_url(),
875 1e882dd7 Kostas Papadimitriou
                                 "read", // create so that sync later uses POST to make the call
876 1e882dd7 Kostas Papadimitriou
                                 null, // payload
877 1e882dd7 Kostas Papadimitriou
                                 function(data) {
878 1e882dd7 Kostas Papadimitriou
                                     success(data);
879 1e882dd7 Kostas Papadimitriou
                                 },  
880 1e882dd7 Kostas Papadimitriou
                                 null, 'diagnostics');
881 1e882dd7 Kostas Papadimitriou
        },
882 8d08f18a Kostas Papadimitriou
883 1e882dd7 Kostas Papadimitriou
        has_diagnostics: function() {
884 1e882dd7 Kostas Papadimitriou
            return this.get("diagnostics") && this.get("diagnostics").length;
885 1e882dd7 Kostas Papadimitriou
        },
886 1e882dd7 Kostas Papadimitriou
887 1e882dd7 Kostas Papadimitriou
        get_progress_info: function() {
888 1e882dd7 Kostas Papadimitriou
            // details about progress message
889 1e882dd7 Kostas Papadimitriou
            // contains a list of diagnostic messages
890 1e882dd7 Kostas Papadimitriou
            return this.get("status_messages");
891 1e882dd7 Kostas Papadimitriou
        },
892 1e882dd7 Kostas Papadimitriou
893 1e882dd7 Kostas Papadimitriou
        get_status_message: function() {
894 1e882dd7 Kostas Papadimitriou
            return this.get('status_message');
895 1e882dd7 Kostas Papadimitriou
        },
896 1e882dd7 Kostas Papadimitriou
        
897 1e882dd7 Kostas Papadimitriou
        // extract status message from diagnostics
898 1e882dd7 Kostas Papadimitriou
        status_message_from_diagnostics: function(diagnostics) {
899 1e882dd7 Kostas Papadimitriou
            var valid_sources_map = synnefo.config.diagnostics_status_messages_map;
900 1e882dd7 Kostas Papadimitriou
            var valid_sources = valid_sources_map[this.get('status')];
901 1e882dd7 Kostas Papadimitriou
            if (!valid_sources) { return null };
902 1e882dd7 Kostas Papadimitriou
            
903 1e882dd7 Kostas Papadimitriou
            // filter messsages based on diagnostic source
904 1e882dd7 Kostas Papadimitriou
            var messages = _.filter(diagnostics, function(diag) {
905 1e882dd7 Kostas Papadimitriou
                return valid_sources.indexOf(diag.source) > -1;
906 1e882dd7 Kostas Papadimitriou
            });
907 1e882dd7 Kostas Papadimitriou
908 1e882dd7 Kostas Papadimitriou
            var msg = messages[0];
909 1e882dd7 Kostas Papadimitriou
            if (msg) {
910 1e882dd7 Kostas Papadimitriou
              var message = msg.message;
911 1e882dd7 Kostas Papadimitriou
              var message_tpl = snf.config.diagnostic_messages_tpls[msg.source];
912 1e882dd7 Kostas Papadimitriou
913 1e882dd7 Kostas Papadimitriou
              if (message_tpl) {
914 1e882dd7 Kostas Papadimitriou
                  message = message_tpl.replace('MESSAGE', msg.message);
915 1e882dd7 Kostas Papadimitriou
              }
916 1e882dd7 Kostas Papadimitriou
              return message;
917 1e882dd7 Kostas Papadimitriou
            }
918 1e882dd7 Kostas Papadimitriou
            
919 1e882dd7 Kostas Papadimitriou
            // no message to display, but vm in build state, display
920 1e882dd7 Kostas Papadimitriou
            // finalizing message.
921 1e882dd7 Kostas Papadimitriou
            if (this.is_building() == 'BUILD') {
922 1e882dd7 Kostas Papadimitriou
                return synnefo.config.BUILDING_MESSAGES['FINAL'];
923 1e882dd7 Kostas Papadimitriou
            }
924 1e882dd7 Kostas Papadimitriou
            return null;
925 1e882dd7 Kostas Papadimitriou
        },
926 1e882dd7 Kostas Papadimitriou
927 1e882dd7 Kostas Papadimitriou
        update_status_message: function(force) {
928 1e882dd7 Kostas Papadimitriou
            // update only if one of the specified attributes has changed
929 1e882dd7 Kostas Papadimitriou
            if (
930 1e882dd7 Kostas Papadimitriou
              !this.keysChanged(['diagnostics', 'progress', 'status', 'state'])
931 1e882dd7 Kostas Papadimitriou
                && !force
932 1e882dd7 Kostas Papadimitriou
            ) { return };
933 1e882dd7 Kostas Papadimitriou
            
934 1e882dd7 Kostas Papadimitriou
            // if user requested to destroy the vm set the appropriate 
935 1e882dd7 Kostas Papadimitriou
            // message.
936 1e882dd7 Kostas Papadimitriou
            if (this.get('state') == "DESTROY") { 
937 1e882dd7 Kostas Papadimitriou
                message = "Terminating..."
938 1e882dd7 Kostas Papadimitriou
                this.set({status_message: message})
939 1e882dd7 Kostas Papadimitriou
                return;
940 1e882dd7 Kostas Papadimitriou
            }
941 1e882dd7 Kostas Papadimitriou
            
942 1e882dd7 Kostas Papadimitriou
            // set error message, if vm has diagnostic message display it as
943 1e882dd7 Kostas Papadimitriou
            // progress message
944 1e882dd7 Kostas Papadimitriou
            if (this.in_error_state()) {
945 1e882dd7 Kostas Papadimitriou
                var d = this.get('diagnostics');
946 1e882dd7 Kostas Papadimitriou
                if (d && d.length) {
947 1e882dd7 Kostas Papadimitriou
                    var message = this.status_message_from_diagnostics(d);
948 1e882dd7 Kostas Papadimitriou
                    this.set({status_message: message});
949 1e882dd7 Kostas Papadimitriou
                } else {
950 1e882dd7 Kostas Papadimitriou
                    this.set({status_message: null});
951 8d08f18a Kostas Papadimitriou
                }
952 1e882dd7 Kostas Papadimitriou
                return;
953 1e882dd7 Kostas Papadimitriou
            }
954 1e882dd7 Kostas Papadimitriou
            
955 1e882dd7 Kostas Papadimitriou
            // identify building status message
956 1e882dd7 Kostas Papadimitriou
            if (this.is_building()) {
957 1e882dd7 Kostas Papadimitriou
                var self = this;
958 1e882dd7 Kostas Papadimitriou
                var success = function(msg) {
959 1e882dd7 Kostas Papadimitriou
                    self.set({status_message: msg});
960 8d08f18a Kostas Papadimitriou
                }
961 1e882dd7 Kostas Papadimitriou
                this.get_building_status_message(success);
962 1e882dd7 Kostas Papadimitriou
                return;
963 1e882dd7 Kostas Papadimitriou
            }
964 1e882dd7 Kostas Papadimitriou
965 1e882dd7 Kostas Papadimitriou
            this.set({status_message:null});
966 1e882dd7 Kostas Papadimitriou
        },
967 1e882dd7 Kostas Papadimitriou
            
968 1e882dd7 Kostas Papadimitriou
        // get building status message. Asynchronous function since it requires
969 1e882dd7 Kostas Papadimitriou
        // access to vm image.
970 1e882dd7 Kostas Papadimitriou
        get_building_status_message: function(callback) {
971 1e882dd7 Kostas Papadimitriou
            // no progress is set, vm is in initial build status
972 1e882dd7 Kostas Papadimitriou
            var progress = this.get("progress");
973 1e882dd7 Kostas Papadimitriou
            if (progress == 0 || !progress) {
974 1e882dd7 Kostas Papadimitriou
                return callback(BUILDING_MESSAGES['INIT']);
975 1e882dd7 Kostas Papadimitriou
            }
976 1e882dd7 Kostas Papadimitriou
            
977 1e882dd7 Kostas Papadimitriou
            // vm has copy progress, display copy percentage
978 1e882dd7 Kostas Papadimitriou
            if (progress > 0 && progress <= 99) {
979 1e882dd7 Kostas Papadimitriou
                this.get_copy_details(true, undefined, _.bind(
980 1e882dd7 Kostas Papadimitriou
                    function(details){
981 1e882dd7 Kostas Papadimitriou
                        callback(BUILDING_MESSAGES['COPY'].format(details.copy, 
982 1e882dd7 Kostas Papadimitriou
                                                           details.size, 
983 1e882dd7 Kostas Papadimitriou
                                                           details.progress));
984 1e882dd7 Kostas Papadimitriou
                }, this));
985 1e882dd7 Kostas Papadimitriou
                return;
986 1e882dd7 Kostas Papadimitriou
            }
987 1e882dd7 Kostas Papadimitriou
988 1e882dd7 Kostas Papadimitriou
            // copy finished display FINAL message or identify status message
989 1e882dd7 Kostas Papadimitriou
            // from diagnostics.
990 1e882dd7 Kostas Papadimitriou
            if (progress >= 100) {
991 1e882dd7 Kostas Papadimitriou
                if (!this.has_diagnostics()) {
992 1e882dd7 Kostas Papadimitriou
                        callback(BUILDING_MESSAGES['FINAL']);
993 1e882dd7 Kostas Papadimitriou
                } else {
994 1e882dd7 Kostas Papadimitriou
                        var d = this.get("diagnostics");
995 1e882dd7 Kostas Papadimitriou
                        var msg = this.status_message_from_diagnostics(d);
996 1e882dd7 Kostas Papadimitriou
                        if (msg) {
997 1e882dd7 Kostas Papadimitriou
                              callback(msg);
998 1e882dd7 Kostas Papadimitriou
                        }
999 8d08f18a Kostas Papadimitriou
                }
1000 8d08f18a Kostas Papadimitriou
            }
1001 8d08f18a Kostas Papadimitriou
        },
1002 8d08f18a Kostas Papadimitriou
1003 c45bed88 Kostas Papadimitriou
        get_copy_details: function(human, image, callback) {
1004 8d08f18a Kostas Papadimitriou
            var human = human || false;
1005 c45bed88 Kostas Papadimitriou
            var image = image || this.get_image(_.bind(function(image){
1006 c45bed88 Kostas Papadimitriou
                var progress = this.get('progress');
1007 c45bed88 Kostas Papadimitriou
                var size = image.get_size();
1008 c45bed88 Kostas Papadimitriou
                var size_copied = (size * progress / 100).toFixed(2);
1009 c45bed88 Kostas Papadimitriou
                
1010 c45bed88 Kostas Papadimitriou
                if (human) {
1011 c45bed88 Kostas Papadimitriou
                    size = util.readablizeBytes(size*1024*1024);
1012 c45bed88 Kostas Papadimitriou
                    size_copied = util.readablizeBytes(size_copied*1024*1024);
1013 c45bed88 Kostas Papadimitriou
                }
1014 8d08f18a Kostas Papadimitriou
1015 c45bed88 Kostas Papadimitriou
                callback({'progress': progress, 'size': size, 'copy': size_copied})
1016 c45bed88 Kostas Papadimitriou
            }, this));
1017 8d08f18a Kostas Papadimitriou
        },
1018 8d08f18a Kostas Papadimitriou
1019 1f7ea2f5 Kostas Papadimitriou
        start_stats_update: function(force_if_empty) {
1020 2c9bfad1 Kostas Papadimitriou
            var prev_state = this.do_update_stats;
1021 2c9bfad1 Kostas Papadimitriou
1022 2c9bfad1 Kostas Papadimitriou
            this.do_update_stats = true;
1023 1f7ea2f5 Kostas Papadimitriou
            
1024 2c9bfad1 Kostas Papadimitriou
            // fetcher initialized ??
1025 2c9bfad1 Kostas Papadimitriou
            if (!this.stats_fetcher) {
1026 2c9bfad1 Kostas Papadimitriou
                this.init_stats_intervals();
1027 2c9bfad1 Kostas Papadimitriou
            }
1028 2c9bfad1 Kostas Papadimitriou
1029 1f7ea2f5 Kostas Papadimitriou
1030 2c9bfad1 Kostas Papadimitriou
            // fetcher running ???
1031 2c9bfad1 Kostas Papadimitriou
            if (!this.stats_fetcher.running || !prev_state) {
1032 2c9bfad1 Kostas Papadimitriou
                this.stats_fetcher.start();
1033 2c9bfad1 Kostas Papadimitriou
            }
1034 1f7ea2f5 Kostas Papadimitriou
1035 1f7ea2f5 Kostas Papadimitriou
            if (force_if_empty && this.get("stats") == undefined) {
1036 1f7ea2f5 Kostas Papadimitriou
                this.update_stats(true);
1037 1f7ea2f5 Kostas Papadimitriou
            }
1038 2c9bfad1 Kostas Papadimitriou
        },
1039 2c9bfad1 Kostas Papadimitriou
1040 2c9bfad1 Kostas Papadimitriou
        stop_stats_update: function(stop_calls) {
1041 2c9bfad1 Kostas Papadimitriou
            this.do_update_stats = false;
1042 2c9bfad1 Kostas Papadimitriou
1043 2c9bfad1 Kostas Papadimitriou
            if (stop_calls) {
1044 2c9bfad1 Kostas Papadimitriou
                this.stats_fetcher.stop();
1045 2c9bfad1 Kostas Papadimitriou
            }
1046 2c9bfad1 Kostas Papadimitriou
        },
1047 2c9bfad1 Kostas Papadimitriou
1048 8d08f18a Kostas Papadimitriou
        // clear and reinitialize update interval
1049 8d08f18a Kostas Papadimitriou
        init_stats_intervals: function (interval) {
1050 96d725ac Kostas Papadimitriou
            this.stats_fetcher = this.get_stats_fetcher(this.stats_update_interval);
1051 96d725ac Kostas Papadimitriou
            this.stats_fetcher.start();
1052 8d08f18a Kostas Papadimitriou
        },
1053 8d08f18a Kostas Papadimitriou
        
1054 96d725ac Kostas Papadimitriou
        get_stats_fetcher: function(timeout) {
1055 96d725ac Kostas Papadimitriou
            var cb = _.bind(function(data){
1056 96d725ac Kostas Papadimitriou
                this.update_stats();
1057 96d725ac Kostas Papadimitriou
            }, this);
1058 258235f4 Kostas Papadimitriou
            var fetcher = new snf.api.updateHandler({'callback': cb, interval: timeout, id:'stats'});
1059 96d725ac Kostas Papadimitriou
            return fetcher;
1060 96d725ac Kostas Papadimitriou
        },
1061 96d725ac Kostas Papadimitriou
1062 8d08f18a Kostas Papadimitriou
        // do the api call
1063 8d08f18a Kostas Papadimitriou
        update_stats: function(force) {
1064 8d08f18a Kostas Papadimitriou
            // do not update stats if flag not set
1065 1f7ea2f5 Kostas Papadimitriou
            if ((!this.do_update_stats && !force) || this.updating_stats) {
1066 8d08f18a Kostas Papadimitriou
                return;
1067 8d08f18a Kostas Papadimitriou
            }
1068 96d725ac Kostas Papadimitriou
1069 8d08f18a Kostas Papadimitriou
            // make the api call, execute handle_stats_update on sucess
1070 8d08f18a Kostas Papadimitriou
            // TODO: onError handler ???
1071 8d08f18a Kostas Papadimitriou
            stats_url = this.url() + "/stats";
1072 1f7ea2f5 Kostas Papadimitriou
            this.updating_stats = true;
1073 258235f4 Kostas Papadimitriou
            this.sync("read", this, {
1074 96d725ac Kostas Papadimitriou
                handles_error:true, 
1075 96d725ac Kostas Papadimitriou
                url: stats_url, 
1076 96d725ac Kostas Papadimitriou
                refresh:true, 
1077 96d725ac Kostas Papadimitriou
                success: _.bind(this.handle_stats_update, this),
1078 ec511098 Kostas Papadimitriou
                error: _.bind(this.handle_stats_error, this),
1079 1f7ea2f5 Kostas Papadimitriou
                complete: _.bind(function(){this.updating_stats = false;}, this),
1080 ec511098 Kostas Papadimitriou
                critical: false,
1081 0375f6c5 Kostas Papadimitriou
                log_error: false,
1082 0375f6c5 Kostas Papadimitriou
                skips_timeouts: true
1083 96d725ac Kostas Papadimitriou
            });
1084 96d725ac Kostas Papadimitriou
        },
1085 96d725ac Kostas Papadimitriou
1086 45b964d3 Kostas Papadimitriou
        get_attachment: function(id) {
1087 45b964d3 Kostas Papadimitriou
          var attachment = undefined;
1088 45b964d3 Kostas Papadimitriou
          _.each(this.get("attachments"), function(a) {
1089 45b964d3 Kostas Papadimitriou
            if (a.id == id) {
1090 45b964d3 Kostas Papadimitriou
              attachment = a;
1091 45b964d3 Kostas Papadimitriou
            }
1092 45b964d3 Kostas Papadimitriou
          });
1093 45b964d3 Kostas Papadimitriou
          return attachment
1094 8d08f18a Kostas Papadimitriou
        },
1095 45b964d3 Kostas Papadimitriou
1096 96d725ac Kostas Papadimitriou
        _set_stats: function(stats) {
1097 96d725ac Kostas Papadimitriou
            var silent = silent === undefined ? false : silent;
1098 96d725ac Kostas Papadimitriou
            // unavailable stats while building
1099 96d725ac Kostas Papadimitriou
            if (this.get("status") == "BUILD") { 
1100 96d725ac Kostas Papadimitriou
                this.stats_available = false;
1101 96d725ac Kostas Papadimitriou
            } else { this.stats_available = true; }
1102 96d725ac Kostas Papadimitriou
1103 96d725ac Kostas Papadimitriou
            if (this.get("status") == "DESTROY") { this.stats_available = false; }
1104 96d725ac Kostas Papadimitriou
            
1105 96d725ac Kostas Papadimitriou
            this.set({stats: stats}, {silent:true});
1106 96d725ac Kostas Papadimitriou
            this.trigger("stats:update", stats);
1107 96d725ac Kostas Papadimitriou
        },
1108 96d725ac Kostas Papadimitriou
1109 96d725ac Kostas Papadimitriou
        unbind: function() {
1110 96d725ac Kostas Papadimitriou
            models.VM.__super__.unbind.apply(this, arguments);
1111 96d725ac Kostas Papadimitriou
        },
1112 a37c5497 Kostas Papadimitriou
        
1113 80bb2140 Kostas Papadimitriou
        can_start: function(flv, count_current) {
1114 80bb2140 Kostas Papadimitriou
          var get_quota = function(key) {
1115 80bb2140 Kostas Papadimitriou
            return synnefo.storage.quotas.get(key).get('available');
1116 80bb2140 Kostas Papadimitriou
          }
1117 80bb2140 Kostas Papadimitriou
          var flavor = flv || this.get_flavor();
1118 80bb2140 Kostas Papadimitriou
          var vm_ram_current = 0, vm_cpu_current = 0;
1119 80bb2140 Kostas Papadimitriou
          if (flv && this.is_active() || flv && count_current) {
1120 80bb2140 Kostas Papadimitriou
            var current = this.get_flavor();
1121 80bb2140 Kostas Papadimitriou
            vm_ram_current = current.ram_to_bytes();
1122 80bb2140 Kostas Papadimitriou
            vm_cpu_current = parseInt(current.get('cpu'));
1123 80bb2140 Kostas Papadimitriou
          }
1124 80bb2140 Kostas Papadimitriou
          var vm_ram = flavor.ram_to_bytes();
1125 80bb2140 Kostas Papadimitriou
          var vm_cpu = parseInt(flavor.get('cpu'));
1126 80bb2140 Kostas Papadimitriou
          var available_cpu = get_quota('cyclades.cpu') + vm_cpu_current;
1127 80bb2140 Kostas Papadimitriou
          var available_ram = get_quota('cyclades.ram') + vm_ram_current;
1128 80bb2140 Kostas Papadimitriou
          if (vm_ram > available_ram || vm_cpu > available_cpu) { return false }
1129 80bb2140 Kostas Papadimitriou
          return true
1130 80bb2140 Kostas Papadimitriou
        },
1131 80bb2140 Kostas Papadimitriou
1132 a37c5497 Kostas Papadimitriou
        can_connect: function() {
1133 f27e976b Kostas Papadimitriou
          if (!synnefo.config.hotplug_enabled && this.is_active()) { return false }
1134 1fee361d Kostas Papadimitriou
          return _.contains(["ACTIVE", "STOPPED"], this.get("status")) && 
1135 1fee361d Kostas Papadimitriou
                 !this.get('suspended')
1136 a37c5497 Kostas Papadimitriou
        },
1137 96d725ac Kostas Papadimitriou
1138 69dac1ba Kostas Papadimitriou
        can_disconnect: function() {
1139 69dac1ba Kostas Papadimitriou
          return _.contains(["ACTIVE", "STOPPED"], this.get("status"))
1140 69dac1ba Kostas Papadimitriou
        },
1141 69dac1ba Kostas Papadimitriou
1142 ab3df8df Kostas Papadimitriou
        can_resize: function() {
1143 ab3df8df Kostas Papadimitriou
          return this.get('status') == 'STOPPED';
1144 ab3df8df Kostas Papadimitriou
        },
1145 ab3df8df Kostas Papadimitriou
1146 96d725ac Kostas Papadimitriou
        handle_stats_error: function() {
1147 96d725ac Kostas Papadimitriou
            stats = {};
1148 96d725ac Kostas Papadimitriou
            _.each(['cpuBar', 'cpuTimeSeries', 'netBar', 'netTimeSeries'], function(k) {
1149 96d725ac Kostas Papadimitriou
                stats[k] = false;
1150 96d725ac Kostas Papadimitriou
            });
1151 96d725ac Kostas Papadimitriou
1152 96d725ac Kostas Papadimitriou
            this.set({'stats': stats});
1153 96d725ac Kostas Papadimitriou
        },
1154 96d725ac Kostas Papadimitriou
1155 8d08f18a Kostas Papadimitriou
        // this method gets executed after a successful vm stats api call
1156 8d08f18a Kostas Papadimitriou
        handle_stats_update: function(data) {
1157 96d725ac Kostas Papadimitriou
            var self = this;
1158 8d08f18a Kostas Papadimitriou
            // avoid browser caching
1159 96d725ac Kostas Papadimitriou
            
1160 8d08f18a Kostas Papadimitriou
            if (data.stats && _.size(data.stats) > 0) {
1161 8d08f18a Kostas Papadimitriou
                var ts = $.now();
1162 8d08f18a Kostas Papadimitriou
                var stats = data.stats;
1163 96d725ac Kostas Papadimitriou
                var images_loaded = 0;
1164 96d725ac Kostas Papadimitriou
                var images = {};
1165 96d725ac Kostas Papadimitriou
1166 96d725ac Kostas Papadimitriou
                function check_images_loaded() {
1167 96d725ac Kostas Papadimitriou
                    images_loaded++;
1168 96d725ac Kostas Papadimitriou
1169 96d725ac Kostas Papadimitriou
                    if (images_loaded == 4) {
1170 96d725ac Kostas Papadimitriou
                        self._set_stats(images);
1171 96d725ac Kostas Papadimitriou
                    }
1172 96d725ac Kostas Papadimitriou
                }
1173 8d08f18a Kostas Papadimitriou
                _.each(['cpuBar', 'cpuTimeSeries', 'netBar', 'netTimeSeries'], function(k) {
1174 96d725ac Kostas Papadimitriou
                    
1175 8d08f18a Kostas Papadimitriou
                    stats[k] = stats[k] + "?_=" + ts;
1176 96d725ac Kostas Papadimitriou
                    
1177 96d725ac Kostas Papadimitriou
                    var stat = k.slice(0,3);
1178 96d725ac Kostas Papadimitriou
                    var type = k.slice(3,6) == "Bar" ? "bar" : "time";
1179 96d725ac Kostas Papadimitriou
                    var img = $("<img />");
1180 96d725ac Kostas Papadimitriou
                    var val = stats[k];
1181 96d725ac Kostas Papadimitriou
                    
1182 96d725ac Kostas Papadimitriou
                    // load stat image to a temporary dom element
1183 96d725ac Kostas Papadimitriou
                    // update model stats on image load/error events
1184 96d725ac Kostas Papadimitriou
                    img.load(function() {
1185 96d725ac Kostas Papadimitriou
                        images[k] = val;
1186 96d725ac Kostas Papadimitriou
                        check_images_loaded();
1187 96d725ac Kostas Papadimitriou
                    });
1188 96d725ac Kostas Papadimitriou
1189 96d725ac Kostas Papadimitriou
                    img.error(function() {
1190 96d725ac Kostas Papadimitriou
                        images[stat + type] = false;
1191 96d725ac Kostas Papadimitriou
                        check_images_loaded();
1192 96d725ac Kostas Papadimitriou
                    });
1193 96d725ac Kostas Papadimitriou
1194 96d725ac Kostas Papadimitriou
                    img.attr({'src': stats[k]});
1195 8d08f18a Kostas Papadimitriou
                })
1196 8d08f18a Kostas Papadimitriou
                data.stats = stats;
1197 8d08f18a Kostas Papadimitriou
            }
1198 96d725ac Kostas Papadimitriou
1199 8d08f18a Kostas Papadimitriou
            // do we need to change the interval ??
1200 8d08f18a Kostas Papadimitriou
            if (data.stats.refresh * 1000 != this.stats_update_interval) {
1201 8d08f18a Kostas Papadimitriou
                this.stats_update_interval = data.stats.refresh * 1000;
1202 1cc6e94f Kostas Papadimitriou
                this.stats_fetcher.interval = this.stats_update_interval;
1203 1cc6e94f Kostas Papadimitriou
                this.stats_fetcher.maximum_interval = this.stats_update_interval;
1204 96d725ac Kostas Papadimitriou
                this.stats_fetcher.stop();
1205 2c9bfad1 Kostas Papadimitriou
                this.stats_fetcher.start(false);
1206 8d08f18a Kostas Papadimitriou
            }
1207 8d08f18a Kostas Papadimitriou
        },
1208 96d725ac Kostas Papadimitriou
1209 8d08f18a Kostas Papadimitriou
        // helper method that sets the do_update_stats
1210 8d08f18a Kostas Papadimitriou
        // in the future this method could also make an api call
1211 8d08f18a Kostas Papadimitriou
        // immediaetly if needed
1212 8d08f18a Kostas Papadimitriou
        enable_stats_update: function() {
1213 8d08f18a Kostas Papadimitriou
            this.do_update_stats = true;
1214 8d08f18a Kostas Papadimitriou
        },
1215 96d725ac Kostas Papadimitriou
        
1216 96d725ac Kostas Papadimitriou
        handle_destroy: function() {
1217 96d725ac Kostas Papadimitriou
            this.stats_fetcher.stop();
1218 96d725ac Kostas Papadimitriou
        },
1219 122850c5 Kostas Papadimitriou
1220 122850c5 Kostas Papadimitriou
        require_reboot: function() {
1221 122850c5 Kostas Papadimitriou
            if (this.is_active()) {
1222 122850c5 Kostas Papadimitriou
                this.set({'reboot_required': true});
1223 122850c5 Kostas Papadimitriou
            }
1224 122850c5 Kostas Papadimitriou
        },
1225 8d08f18a Kostas Papadimitriou
        
1226 8d08f18a Kostas Papadimitriou
        set_pending_action: function(data) {
1227 8d08f18a Kostas Papadimitriou
            this.pending_action = data;
1228 8d08f18a Kostas Papadimitriou
            return data;
1229 8d08f18a Kostas Papadimitriou
        },
1230 8d08f18a Kostas Papadimitriou
1231 8d08f18a Kostas Papadimitriou
        // machine has pending action
1232 8d08f18a Kostas Papadimitriou
        update_pending_action: function(action, force) {
1233 8d08f18a Kostas Papadimitriou
            this.set({pending_action: action});
1234 8d08f18a Kostas Papadimitriou
        },
1235 8d08f18a Kostas Papadimitriou
1236 8d08f18a Kostas Papadimitriou
        clear_pending_action: function() {
1237 8d08f18a Kostas Papadimitriou
            this.set({pending_action: undefined});
1238 8d08f18a Kostas Papadimitriou
        },
1239 8d08f18a Kostas Papadimitriou
1240 8d08f18a Kostas Papadimitriou
        has_pending_action: function() {
1241 8d08f18a Kostas Papadimitriou
            return this.get("pending_action") ? this.get("pending_action") : false;
1242 8d08f18a Kostas Papadimitriou
        },
1243 8d08f18a Kostas Papadimitriou
        
1244 8d08f18a Kostas Papadimitriou
        // machine is active
1245 8d08f18a Kostas Papadimitriou
        is_active: function() {
1246 8d08f18a Kostas Papadimitriou
            return models.VM.ACTIVE_STATES.indexOf(this.state()) > -1;
1247 8d08f18a Kostas Papadimitriou
        },
1248 8d08f18a Kostas Papadimitriou
        
1249 122850c5 Kostas Papadimitriou
        // machine is building 
1250 8d08f18a Kostas Papadimitriou
        is_building: function() {
1251 8d08f18a Kostas Papadimitriou
            return models.VM.BUILDING_STATES.indexOf(this.state()) > -1;
1252 8d08f18a Kostas Papadimitriou
        },
1253 3162bebd Kostas Papadimitriou
        
1254 828f802d Kostas Papadimitriou
        is_rebooting: function() {
1255 828f802d Kostas Papadimitriou
            return this.state() == 'REBOOT';
1256 828f802d Kostas Papadimitriou
        },
1257 828f802d Kostas Papadimitriou
1258 3162bebd Kostas Papadimitriou
        in_error_state: function() {
1259 3162bebd Kostas Papadimitriou
            return this.state() === "ERROR"
1260 3162bebd Kostas Papadimitriou
        },
1261 8d08f18a Kostas Papadimitriou
1262 8d08f18a Kostas Papadimitriou
        // user can connect to machine
1263 8d08f18a Kostas Papadimitriou
        is_connectable: function() {
1264 8d08f18a Kostas Papadimitriou
            return models.VM.CONNECT_STATES.indexOf(this.state()) > -1;
1265 8d08f18a Kostas Papadimitriou
        },
1266 8d08f18a Kostas Papadimitriou
        
1267 8d08f18a Kostas Papadimitriou
        remove_meta: function(key, complete, error) {
1268 d5fd0d3c Kostas Papadimitriou
            var url = this.api_path() + "/metadata/" + key;
1269 258235f4 Kostas Papadimitriou
            this.api_call(url, "delete", undefined, complete, error);
1270 8d08f18a Kostas Papadimitriou
        },
1271 8d08f18a Kostas Papadimitriou
1272 8d08f18a Kostas Papadimitriou
        save_meta: function(meta, complete, error) {
1273 d5fd0d3c Kostas Papadimitriou
            var url = this.api_path() + "/metadata/" + meta.key;
1274 8d08f18a Kostas Papadimitriou
            var payload = {meta:{}};
1275 8d08f18a Kostas Papadimitriou
            payload.meta[meta.key] = meta.value;
1276 9ffd10ce Kostas Papadimitriou
            payload._options = {
1277 9ffd10ce Kostas Papadimitriou
                critical:false, 
1278 9ffd10ce Kostas Papadimitriou
                error_params: {
1279 9ffd10ce Kostas Papadimitriou
                    title: "Machine metadata error",
1280 9ffd10ce Kostas Papadimitriou
                    extra_details: {"Machine id": this.id}
1281 9ffd10ce Kostas Papadimitriou
            }};
1282 8d08f18a Kostas Papadimitriou
1283 258235f4 Kostas Papadimitriou
            this.api_call(url, "update", payload, complete, error);
1284 8d08f18a Kostas Papadimitriou
        },
1285 8d08f18a Kostas Papadimitriou
1286 8d08f18a Kostas Papadimitriou
1287 8d08f18a Kostas Papadimitriou
        // update/get the state of the machine
1288 8d08f18a Kostas Papadimitriou
        state: function() {
1289 8d08f18a Kostas Papadimitriou
            var args = slice.call(arguments);
1290 8d08f18a Kostas Papadimitriou
                
1291 8d08f18a Kostas Papadimitriou
            if (args.length > 0 && models.VM.STATES.indexOf(args[0]) > -1) {
1292 8d08f18a Kostas Papadimitriou
                this.set({'state': args[0]});
1293 8d08f18a Kostas Papadimitriou
            }
1294 8d08f18a Kostas Papadimitriou
1295 8d08f18a Kostas Papadimitriou
            return this.get('state');
1296 8d08f18a Kostas Papadimitriou
        },
1297 8d08f18a Kostas Papadimitriou
        
1298 8d08f18a Kostas Papadimitriou
        // get the state that the api status corresponds to
1299 8d08f18a Kostas Papadimitriou
        state_for_api_status: function(status) {
1300 8d08f18a Kostas Papadimitriou
            return this.state_transition(this.state(), status);
1301 8d08f18a Kostas Papadimitriou
        },
1302 8d08f18a Kostas Papadimitriou
        
1303 8d08f18a Kostas Papadimitriou
        // get transition state for the corresponging api status
1304 8d08f18a Kostas Papadimitriou
        state_transition: function(state, new_status) {
1305 8d08f18a Kostas Papadimitriou
            var statuses = models.VM.STATES_TRANSITIONS[state];
1306 8d08f18a Kostas Papadimitriou
            if (statuses) {
1307 8d08f18a Kostas Papadimitriou
                if (statuses.indexOf(new_status) > -1) {
1308 8d08f18a Kostas Papadimitriou
                    return new_status;
1309 8d08f18a Kostas Papadimitriou
                } else {
1310 8d08f18a Kostas Papadimitriou
                    return state;
1311 8d08f18a Kostas Papadimitriou
                }
1312 8d08f18a Kostas Papadimitriou
            } else {
1313 8d08f18a Kostas Papadimitriou
                return new_status;
1314 8d08f18a Kostas Papadimitriou
            }
1315 8d08f18a Kostas Papadimitriou
        },
1316 8d08f18a Kostas Papadimitriou
        
1317 8d08f18a Kostas Papadimitriou
        // the current vm state is a transition state
1318 8d08f18a Kostas Papadimitriou
        in_transition: function() {
1319 8d08f18a Kostas Papadimitriou
            return models.VM.TRANSITION_STATES.indexOf(this.state()) > -1 || 
1320 8d08f18a Kostas Papadimitriou
                models.VM.TRANSITION_STATES.indexOf(this.get('status')) > -1;
1321 8d08f18a Kostas Papadimitriou
        },
1322 8d08f18a Kostas Papadimitriou
        
1323 8d08f18a Kostas Papadimitriou
        // get image object
1324 c45bed88 Kostas Papadimitriou
        get_image: function(callback) {
1325 cde8cde3 Kostas Papadimitriou
            if (callback == undefined) { callback = function(){} }
1326 0dee4086 Kostas Papadimitriou
            var image = storage.images.get(this.get('image'));
1327 bedfe884 Kostas Papadimitriou
            if (!image) {
1328 0dee4086 Kostas Papadimitriou
                storage.images.update_unknown_id(this.get('image'), callback);
1329 c45bed88 Kostas Papadimitriou
                return;
1330 bedfe884 Kostas Papadimitriou
            }
1331 c45bed88 Kostas Papadimitriou
            callback(image);
1332 bedfe884 Kostas Papadimitriou
            return image;
1333 8d08f18a Kostas Papadimitriou
        },
1334 8d08f18a Kostas Papadimitriou
        
1335 8d08f18a Kostas Papadimitriou
        // get flavor object
1336 8d08f18a Kostas Papadimitriou
        get_flavor: function() {
1337 0dee4086 Kostas Papadimitriou
            var flv = storage.flavors.get(this.get('flavor'));
1338 bedfe884 Kostas Papadimitriou
            if (!flv) {
1339 0dee4086 Kostas Papadimitriou
                storage.flavors.update_unknown_id(this.get('flavor'));
1340 0dee4086 Kostas Papadimitriou
                flv = storage.flavors.get(this.get('flavor'));
1341 bedfe884 Kostas Papadimitriou
            }
1342 bedfe884 Kostas Papadimitriou
            return flv;
1343 8d08f18a Kostas Papadimitriou
        },
1344 8d08f18a Kostas Papadimitriou
1345 ab3df8df Kostas Papadimitriou
        get_resize_flavors: function() {
1346 ab3df8df Kostas Papadimitriou
          var vm_flavor = this.get_flavor();
1347 ab3df8df Kostas Papadimitriou
          var flavors = synnefo.storage.flavors.filter(function(f){
1348 ab3df8df Kostas Papadimitriou
              return f.get('disk_template') ==
1349 ab3df8df Kostas Papadimitriou
              vm_flavor.get('disk_template') && f.get('disk') ==
1350 ab3df8df Kostas Papadimitriou
              vm_flavor.get('disk');
1351 ab3df8df Kostas Papadimitriou
          });
1352 ab3df8df Kostas Papadimitriou
          return flavors;
1353 ab3df8df Kostas Papadimitriou
        },
1354 ab3df8df Kostas Papadimitriou
1355 ab3df8df Kostas Papadimitriou
        get_flavor_quotas: function() {
1356 ab3df8df Kostas Papadimitriou
          var flavor = this.get_flavor();
1357 ab3df8df Kostas Papadimitriou
          return {
1358 80bb2140 Kostas Papadimitriou
            cpu: flavor.get('cpu'), 
1359 80bb2140 Kostas Papadimitriou
            ram: flavor.get_ram_size(), 
1360 80bb2140 Kostas Papadimitriou
            disk:flavor.get_disk_size()
1361 ab3df8df Kostas Papadimitriou
          }
1362 ab3df8df Kostas Papadimitriou
        },
1363 ab3df8df Kostas Papadimitriou
1364 cde8cde3 Kostas Papadimitriou
        get_meta: function(key, deflt) {
1365 0dee4086 Kostas Papadimitriou
            if (this.get('metadata') && this.get('metadata')) {
1366 0dee4086 Kostas Papadimitriou
                if (!this.get('metadata')[key]) { return deflt }
1367 0dee4086 Kostas Papadimitriou
                return _.escape(this.get('metadata')[key]);
1368 88d6d677 Kostas Papadimitriou
            } else {
1369 cde8cde3 Kostas Papadimitriou
                return deflt;
1370 88d6d677 Kostas Papadimitriou
            }
1371 88d6d677 Kostas Papadimitriou
        },
1372 88d6d677 Kostas Papadimitriou
1373 88d6d677 Kostas Papadimitriou
        get_meta_keys: function() {
1374 0dee4086 Kostas Papadimitriou
            if (this.get('metadata') && this.get('metadata')) {
1375 0dee4086 Kostas Papadimitriou
                return _.keys(this.get('metadata'));
1376 88d6d677 Kostas Papadimitriou
            } else {
1377 88d6d677 Kostas Papadimitriou
                return [];
1378 8d08f18a Kostas Papadimitriou
            }
1379 8d08f18a Kostas Papadimitriou
        },
1380 8d08f18a Kostas Papadimitriou
        
1381 8d08f18a Kostas Papadimitriou
        // get metadata OS value
1382 8d08f18a Kostas Papadimitriou
        get_os: function() {
1383 cde8cde3 Kostas Papadimitriou
            var image = this.get_image();
1384 cde8cde3 Kostas Papadimitriou
            return this.get_meta('OS') || (image ? 
1385 cde8cde3 Kostas Papadimitriou
                                            image.get_os() || "okeanos" : "okeanos");
1386 8d08f18a Kostas Papadimitriou
        },
1387 258235f4 Kostas Papadimitriou
1388 43ec1206 Kostas Papadimitriou
        get_gui: function() {
1389 43ec1206 Kostas Papadimitriou
            return this.get_meta('GUI');
1390 43ec1206 Kostas Papadimitriou
        },
1391 126a01f2 Kostas Papadimitriou
        
1392 3796f345 Kostas Papadimitriou
        get_hostname: function() {
1393 f8df95f9 Kostas Papadimitriou
          return this.get_meta('hostname') || this.get('fqdn') || synnefo.config.no_fqdn_message;
1394 3796f345 Kostas Papadimitriou
        },
1395 3796f345 Kostas Papadimitriou
1396 8d08f18a Kostas Papadimitriou
        // get actions that the user can execute
1397 8d08f18a Kostas Papadimitriou
        // depending on the vm state/status
1398 8d08f18a Kostas Papadimitriou
        get_available_actions: function() {
1399 8d08f18a Kostas Papadimitriou
            return models.VM.AVAILABLE_ACTIONS[this.state()];
1400 8d08f18a Kostas Papadimitriou
        },
1401 8d08f18a Kostas Papadimitriou
1402 8d08f18a Kostas Papadimitriou
        set_profile: function(profile, net_id) {
1403 8d08f18a Kostas Papadimitriou
        },
1404 8d08f18a Kostas Papadimitriou
        
1405 8d08f18a Kostas Papadimitriou
        // call rename api
1406 8d08f18a Kostas Papadimitriou
        rename: function(new_name) {
1407 8d08f18a Kostas Papadimitriou
            //this.set({'name': new_name});
1408 8d08f18a Kostas Papadimitriou
            this.sync("update", this, {
1409 9ffd10ce Kostas Papadimitriou
                critical: true,
1410 8d08f18a Kostas Papadimitriou
                data: {
1411 8d08f18a Kostas Papadimitriou
                    'server': {
1412 8d08f18a Kostas Papadimitriou
                        'name': new_name
1413 8d08f18a Kostas Papadimitriou
                    }
1414 8d08f18a Kostas Papadimitriou
                }, 
1415 8d08f18a Kostas Papadimitriou
                success: _.bind(function(){
1416 8d08f18a Kostas Papadimitriou
                    snf.api.trigger("call");
1417 8d08f18a Kostas Papadimitriou
                }, this)
1418 8d08f18a Kostas Papadimitriou
            });
1419 8d08f18a Kostas Papadimitriou
        },
1420 8d08f18a Kostas Papadimitriou
        
1421 8d08f18a Kostas Papadimitriou
        get_console_url: function(data) {
1422 8d08f18a Kostas Papadimitriou
            var url_params = {
1423 8d08f18a Kostas Papadimitriou
                machine: this.get("name"),
1424 d7d154c9 Kostas Papadimitriou
                host_ip: this.get_hostname(),
1425 d7d154c9 Kostas Papadimitriou
                host_ip_v6: this.get_hostname(),
1426 8d08f18a Kostas Papadimitriou
                host: data.host,
1427 8d08f18a Kostas Papadimitriou
                port: data.port,
1428 8d08f18a Kostas Papadimitriou
                password: data.password
1429 8d08f18a Kostas Papadimitriou
            }
1430 0c2166b9 Kostas Papadimitriou
            return synnefo.config.ui_console_url + '?' + $.param(url_params);
1431 8d08f18a Kostas Papadimitriou
        },
1432 e4330db2 Kostas Papadimitriou
        
1433 e4330db2 Kostas Papadimitriou
        set_firewall: function(nic, value, success_cb, error_cb) {
1434 a1e272d0 Kostas Papadimitriou
          var self = this;
1435 a1e272d0 Kostas Papadimitriou
          var success = function() { self.require_reboot(); success_cb() }
1436 e4330db2 Kostas Papadimitriou
          var error = function() { error_cb() }
1437 e4330db2 Kostas Papadimitriou
          var data = {'nic': nic.id, 'profile': value, 'display': true};
1438 e4330db2 Kostas Papadimitriou
          var url = this.url() + "/action";
1439 e4330db2 Kostas Papadimitriou
          //var params = {skip_api_error: false, display: true};
1440 e4330db2 Kostas Papadimitriou
          this.call('firewallProfile', success, error, data);
1441 e4330db2 Kostas Papadimitriou
        },
1442 e4330db2 Kostas Papadimitriou
1443 681c9851 Kostas Papadimitriou
        connect_floating_ip: function(ip, cb, error) {
1444 681c9851 Kostas Papadimitriou
          var self = this;
1445 681c9851 Kostas Papadimitriou
          var from_status = this.get('status');
1446 69dac1ba Kostas Papadimitriou
          this.set({'status': 'CONNECTING'});
1447 02effd9c Kostas Papadimitriou
          synnefo.storage.ports.create({
1448 02effd9c Kostas Papadimitriou
            port: {
1449 02effd9c Kostas Papadimitriou
              network_id: ip.get('floating_network_id'),
1450 02effd9c Kostas Papadimitriou
              device_id: this.id,
1451 02effd9c Kostas Papadimitriou
              fixed_ips: [{'ip_address': ip.get('floating_ip_address')}]
1452 02effd9c Kostas Papadimitriou
            }
1453 681c9851 Kostas Papadimitriou
          }, {
1454 681c9851 Kostas Papadimitriou
            success: cb, 
1455 681c9851 Kostas Papadimitriou
            error: function() { error && error() },
1456 681c9851 Kostas Papadimitriou
            skip_api_error: false
1457 681c9851 Kostas Papadimitriou
          });
1458 45b964d3 Kostas Papadimitriou
        },
1459 8d08f18a Kostas Papadimitriou
1460 8d08f18a Kostas Papadimitriou
        // action helper
1461 738a9b18 Kostas Papadimitriou
        call: function(action_name, success, error, params) {
1462 8d08f18a Kostas Papadimitriou
            var id_param = [this.id];
1463 738a9b18 Kostas Papadimitriou
            
1464 738a9b18 Kostas Papadimitriou
            params = params || {};
1465 8d08f18a Kostas Papadimitriou
            success = success || function() {};
1466 8d08f18a Kostas Papadimitriou
            error = error || function() {};
1467 8d08f18a Kostas Papadimitriou
1468 8d08f18a Kostas Papadimitriou
            var self = this;
1469 8d08f18a Kostas Papadimitriou
1470 8d08f18a Kostas Papadimitriou
            switch(action_name) {
1471 8d08f18a Kostas Papadimitriou
                case 'start':
1472 8d08f18a Kostas Papadimitriou
                    this.__make_api_call(this.get_action_url(), // vm actions url
1473 8d08f18a Kostas Papadimitriou
                                         "create", // create so that sync later uses POST to make the call
1474 8d08f18a Kostas Papadimitriou
                                         {start:{}}, // payload
1475 8d08f18a Kostas Papadimitriou
                                         function() {
1476 8d08f18a Kostas Papadimitriou
                                             // set state after successful call
1477 8d08f18a Kostas Papadimitriou
                                             self.state("START"); 
1478 8d08f18a Kostas Papadimitriou
                                             success.apply(this, arguments);
1479 8d08f18a Kostas Papadimitriou
                                             snf.api.trigger("call");
1480 8d08f18a Kostas Papadimitriou
                                         },  
1481 738a9b18 Kostas Papadimitriou
                                         error, 'start', params);
1482 8d08f18a Kostas Papadimitriou
                    break;
1483 8d08f18a Kostas Papadimitriou
                case 'reboot':
1484 8d08f18a Kostas Papadimitriou
                    this.__make_api_call(this.get_action_url(), // vm actions url
1485 8d08f18a Kostas Papadimitriou
                                         "create", // create so that sync later uses POST to make the call
1486 bbae3e45 Christos Stavrakakis
                                         {reboot:{}}, // payload
1487 8d08f18a Kostas Papadimitriou
                                         function() {
1488 8d08f18a Kostas Papadimitriou
                                             // set state after successful call
1489 8d08f18a Kostas Papadimitriou
                                             self.state("REBOOT"); 
1490 8d08f18a Kostas Papadimitriou
                                             success.apply(this, arguments)
1491 8d08f18a Kostas Papadimitriou
                                             snf.api.trigger("call");
1492 122850c5 Kostas Papadimitriou
                                             self.set({'reboot_required': false});
1493 8d08f18a Kostas Papadimitriou
                                         },
1494 738a9b18 Kostas Papadimitriou
                                         error, 'reboot', params);
1495 8d08f18a Kostas Papadimitriou
                    break;
1496 8d08f18a Kostas Papadimitriou
                case 'shutdown':
1497 8d08f18a Kostas Papadimitriou
                    this.__make_api_call(this.get_action_url(), // vm actions url
1498 8d08f18a Kostas Papadimitriou
                                         "create", // create so that sync later uses POST to make the call
1499 8d08f18a Kostas Papadimitriou
                                         {shutdown:{}}, // payload
1500 8d08f18a Kostas Papadimitriou
                                         function() {
1501 8d08f18a Kostas Papadimitriou
                                             // set state after successful call
1502 8d08f18a Kostas Papadimitriou
                                             self.state("SHUTDOWN"); 
1503 8d08f18a Kostas Papadimitriou
                                             success.apply(this, arguments)
1504 8d08f18a Kostas Papadimitriou
                                             snf.api.trigger("call");
1505 8d08f18a Kostas Papadimitriou
                                         },  
1506 738a9b18 Kostas Papadimitriou
                                         error, 'shutdown', params);
1507 8d08f18a Kostas Papadimitriou
                    break;
1508 8d08f18a Kostas Papadimitriou
                case 'console':
1509 1fc7640d Kostas Papadimitriou
                    this.__make_api_call(this.url() + "/action", "create", 
1510 1fc7640d Kostas Papadimitriou
                                         {'console': {'type':'vnc'}}, 
1511 1fc7640d Kostas Papadimitriou
                                         function(data) {
1512 8d08f18a Kostas Papadimitriou
                        var cons_data = data.console;
1513 8d08f18a Kostas Papadimitriou
                        success.apply(this, [cons_data]);
1514 738a9b18 Kostas Papadimitriou
                    }, undefined, 'console', params)
1515 8d08f18a Kostas Papadimitriou
                    break;
1516 8d08f18a Kostas Papadimitriou
                case 'destroy':
1517 8d08f18a Kostas Papadimitriou
                    this.__make_api_call(this.url(), // vm actions url
1518 8d08f18a Kostas Papadimitriou
                                         "delete", // create so that sync later uses POST to make the call
1519 8d08f18a Kostas Papadimitriou
                                         undefined, // payload
1520 8d08f18a Kostas Papadimitriou
                                         function() {
1521 8d08f18a Kostas Papadimitriou
                                             // set state after successful call
1522 8d08f18a Kostas Papadimitriou
                                             self.state('DESTROY');
1523 b14e3b9e Kostas Papadimitriou
                                             success.apply(this, arguments);
1524 426e1fb9 Kostas Papadimitriou
                                             synnefo.storage.quotas.get('cyclades.vm').decrease();
1525 b14e3b9e Kostas Papadimitriou
1526 8d08f18a Kostas Papadimitriou
                                         },  
1527 738a9b18 Kostas Papadimitriou
                                         error, 'destroy', params);
1528 8d08f18a Kostas Papadimitriou
                    break;
1529 ab3df8df Kostas Papadimitriou
                case 'resize':
1530 ab3df8df Kostas Papadimitriou
                    this.__make_api_call(this.get_action_url(), // vm actions url
1531 ab3df8df Kostas Papadimitriou
                                         "create", // create so that sync later uses POST to make the call
1532 ab3df8df Kostas Papadimitriou
                                         {resize: {flavorRef:params.flavor}}, // payload
1533 ab3df8df Kostas Papadimitriou
                                         function() {
1534 a3ac649e Kostas Papadimitriou
                                             self.state('RESIZE');
1535 a3ac649e Kostas Papadimitriou
                                             success.apply(this, arguments);
1536 ab3df8df Kostas Papadimitriou
                                             snf.api.trigger("call");
1537 ab3df8df Kostas Papadimitriou
                                         },  
1538 ab3df8df Kostas Papadimitriou
                                         error, 'resize', params);
1539 ab3df8df Kostas Papadimitriou
                    break;
1540 ab3df8df Kostas Papadimitriou
                case 'destroy':
1541 ab3df8df Kostas Papadimitriou
                    this.__make_api_call(this.url(), // vm actions url
1542 ab3df8df Kostas Papadimitriou
                                         "delete", // create so that sync later uses POST to make the call
1543 ab3df8df Kostas Papadimitriou
                                         undefined, // payload
1544 ab3df8df Kostas Papadimitriou
                                         function() {
1545 ab3df8df Kostas Papadimitriou
                                             // set state after successful call
1546 ab3df8df Kostas Papadimitriou
                                             self.state('DESTROY');
1547 ab3df8df Kostas Papadimitriou
                                             success.apply(this, arguments);
1548 ab3df8df Kostas Papadimitriou
                                             synnefo.storage.quotas.get('cyclades.vm').decrease();
1549 ab3df8df Kostas Papadimitriou
1550 ab3df8df Kostas Papadimitriou
                                         },  
1551 ab3df8df Kostas Papadimitriou
                                         error, 'destroy', params);
1552 ab3df8df Kostas Papadimitriou
                    break;
1553 e4330db2 Kostas Papadimitriou
                case 'firewallProfile':
1554 e4330db2 Kostas Papadimitriou
                    this.__make_api_call(this.get_action_url(), // vm actions url
1555 e4330db2 Kostas Papadimitriou
                                         "create",
1556 e4330db2 Kostas Papadimitriou
                                         {firewallProfile:{nic:params.nic, profile:params.profile}}, // payload
1557 e4330db2 Kostas Papadimitriou
                                         function() {
1558 e4330db2 Kostas Papadimitriou
                                             success.apply(this, arguments);
1559 e4330db2 Kostas Papadimitriou
                                             snf.api.trigger("call");
1560 e4330db2 Kostas Papadimitriou
                                         },  
1561 e4330db2 Kostas Papadimitriou
                                         error, 'start', params);
1562 e4330db2 Kostas Papadimitriou
                    break;
1563 8d08f18a Kostas Papadimitriou
                default:
1564 8d08f18a Kostas Papadimitriou
                    throw "Invalid VM action ("+action_name+")";
1565 8d08f18a Kostas Papadimitriou
            }
1566 8d08f18a Kostas Papadimitriou
        },
1567 8d08f18a Kostas Papadimitriou
        
1568 1fc7640d Kostas Papadimitriou
        __make_api_call: function(url, method, data, success, error, action, 
1569 1fc7640d Kostas Papadimitriou
                                  extra_params) {
1570 8d08f18a Kostas Papadimitriou
            var self = this;
1571 8d08f18a Kostas Papadimitriou
            error = error || function(){};
1572 8d08f18a Kostas Papadimitriou
            success = success || function(){};
1573 8d08f18a Kostas Papadimitriou
1574 8d08f18a Kostas Papadimitriou
            var params = {
1575 8d08f18a Kostas Papadimitriou
                url: url,
1576 8d08f18a Kostas Papadimitriou
                data: data,
1577 1fc7640d Kostas Papadimitriou
                success: function() { 
1578 1fc7640d Kostas Papadimitriou
                  self.handle_action_succeed.apply(self, arguments); 
1579 1fc7640d Kostas Papadimitriou
                  success.apply(this, arguments)
1580 1fc7640d Kostas Papadimitriou
                },
1581 45b964d3 Kostas Papadimitriou
                error: function() { 
1582 45b964d3 Kostas Papadimitriou
                  self.handle_action_fail.apply(self, arguments);
1583 45b964d3 Kostas Papadimitriou
                  error.apply(this, arguments)
1584 45b964d3 Kostas Papadimitriou
                },
1585 edd1d565 Kostas Papadimitriou
                error_params: { ns: "Machines actions", 
1586 9ffd10ce Kostas Papadimitriou
                                title: "'" + this.get("name") + "'" + " " + action + " failed", 
1587 45b964d3 Kostas Papadimitriou
                                extra_details: {
1588 45b964d3 Kostas Papadimitriou
                                  'Machine ID': this.id, 
1589 45b964d3 Kostas Papadimitriou
                                  'URL': url, 
1590 45b964d3 Kostas Papadimitriou
                                  'Action': action || "undefined" },
1591 6a3a5bf7 Kostas Papadimitriou
                                allow_reload: false
1592 6a3a5bf7 Kostas Papadimitriou
                              },
1593 9ce969a7 Kostas Papadimitriou
                display: false,
1594 9ce969a7 Kostas Papadimitriou
                critical: false
1595 8d08f18a Kostas Papadimitriou
            }
1596 1fc7640d Kostas Papadimitriou
            _.extend(params, extra_params);
1597 8d08f18a Kostas Papadimitriou
            this.sync(method, this, params);
1598 8d08f18a Kostas Papadimitriou
        },
1599 8d08f18a Kostas Papadimitriou
1600 8d08f18a Kostas Papadimitriou
        handle_action_succeed: function() {
1601 8d08f18a Kostas Papadimitriou
            this.trigger("action:success", arguments);
1602 8d08f18a Kostas Papadimitriou
        },
1603 6a3a5bf7 Kostas Papadimitriou
        
1604 6a3a5bf7 Kostas Papadimitriou
        reset_action_error: function() {
1605 6a3a5bf7 Kostas Papadimitriou
            this.action_error = false;
1606 6a3a5bf7 Kostas Papadimitriou
            this.trigger("action:fail:reset", this.action_error);
1607 6a3a5bf7 Kostas Papadimitriou
        },
1608 8d08f18a Kostas Papadimitriou
1609 8d08f18a Kostas Papadimitriou
        handle_action_fail: function() {
1610 6a3a5bf7 Kostas Papadimitriou
            this.action_error = arguments;
1611 8d08f18a Kostas Papadimitriou
            this.trigger("action:fail", arguments);
1612 8d08f18a Kostas Papadimitriou
        },
1613 8d08f18a Kostas Papadimitriou
1614 8d08f18a Kostas Papadimitriou
        get_action_url: function(name) {
1615 8d08f18a Kostas Papadimitriou
            return this.url() + "/action";
1616 1c922789 Kostas Papadimitriou
        },
1617 1c922789 Kostas Papadimitriou
1618 1e882dd7 Kostas Papadimitriou
        get_diagnostics_url: function() {
1619 1e882dd7 Kostas Papadimitriou
            return this.url() + "/diagnostics";
1620 1e882dd7 Kostas Papadimitriou
        },
1621 1e882dd7 Kostas Papadimitriou
1622 a6213c91 Kostas Papadimitriou
        get_users: function() {
1623 a6213c91 Kostas Papadimitriou
            var image;
1624 a6213c91 Kostas Papadimitriou
            var users = [];
1625 a6213c91 Kostas Papadimitriou
            try {
1626 a6213c91 Kostas Papadimitriou
              var users = this.get_meta('users').split(" ");
1627 a6213c91 Kostas Papadimitriou
            } catch (err) { users = null }
1628 a6213c91 Kostas Papadimitriou
            if (!users) {
1629 a6213c91 Kostas Papadimitriou
              image = this.get_image();
1630 a6213c91 Kostas Papadimitriou
              if (image) {
1631 a6213c91 Kostas Papadimitriou
                  users = image.get_created_users();
1632 a6213c91 Kostas Papadimitriou
              }
1633 a6213c91 Kostas Papadimitriou
            }
1634 a6213c91 Kostas Papadimitriou
            return users;
1635 a6213c91 Kostas Papadimitriou
        },
1636 a6213c91 Kostas Papadimitriou
1637 1c922789 Kostas Papadimitriou
        get_connection_info: function(host_os, success, error) {
1638 0c2166b9 Kostas Papadimitriou
            var url = synnefo.config.ui_connect_url;
1639 a6213c91 Kostas Papadimitriou
            var users = this.get_users();
1640 a6213c91 Kostas Papadimitriou
1641 1c922789 Kostas Papadimitriou
            params = {
1642 d7d154c9 Kostas Papadimitriou
                ip_address: this.get_hostname(),
1643 3796f345 Kostas Papadimitriou
                hostname: this.get_hostname(),
1644 1c922789 Kostas Papadimitriou
                os: this.get_os(),
1645 ce6fb98b Kostas Papadimitriou
                host_os: host_os,
1646 74831b0d Kostas Papadimitriou
                ports: JSON.stringify(this.get('SNF:port_forwarding') || {}),
1647 1c922789 Kostas Papadimitriou
                srv: this.id
1648 1c922789 Kostas Papadimitriou
            }
1649 a6213c91 Kostas Papadimitriou
            
1650 a6213c91 Kostas Papadimitriou
            if (users.length) { 
1651 a6213c91 Kostas Papadimitriou
                params['username'] = _.last(users)
1652 a6213c91 Kostas Papadimitriou
            }
1653 1c922789 Kostas Papadimitriou
1654 1c922789 Kostas Papadimitriou
            url = url + "?" + $.param(params);
1655 1c922789 Kostas Papadimitriou
1656 1c922789 Kostas Papadimitriou
            var ajax = snf.api.sync("read", undefined, { url: url, 
1657 1c922789 Kostas Papadimitriou
                                                         error:error, 
1658 1c922789 Kostas Papadimitriou
                                                         success:success, 
1659 1c922789 Kostas Papadimitriou
                                                         handles_error:1});
1660 8d08f18a Kostas Papadimitriou
        }
1661 d7d154c9 Kostas Papadimitriou
    });
1662 8d08f18a Kostas Papadimitriou
    
1663 8d08f18a Kostas Papadimitriou
    models.VM.ACTIONS = [
1664 8d08f18a Kostas Papadimitriou
        'start',
1665 8d08f18a Kostas Papadimitriou
        'shutdown',
1666 8d08f18a Kostas Papadimitriou
        'reboot',
1667 0f7214b3 Kostas Papadimitriou
        'console',
1668 a3ac649e Kostas Papadimitriou
        'destroy',
1669 de0693f8 Kostas Papadimitriou
        'resize',
1670 de0693f8 Kostas Papadimitriou
        'snapshot'
1671 8d08f18a Kostas Papadimitriou
    ]
1672 8d08f18a Kostas Papadimitriou
1673 a3ac649e Kostas Papadimitriou
    models.VM.TASK_STATE_STATUS_MAP = {
1674 a3ac649e Kostas Papadimitriou
      'BULDING': 'BUILD',
1675 a3ac649e Kostas Papadimitriou
      'REBOOTING': 'REBOOT',
1676 a3ac649e Kostas Papadimitriou
      'STOPPING': 'SHUTDOWN',
1677 a3ac649e Kostas Papadimitriou
      'STARTING': 'START',
1678 a3ac649e Kostas Papadimitriou
      'RESIZING': 'RESIZE',
1679 a3ac649e Kostas Papadimitriou
      'CONNECTING': 'CONNECT',
1680 a3ac649e Kostas Papadimitriou
      'DISCONNECTING': 'DISCONNECT',
1681 a3ac649e Kostas Papadimitriou
      'DESTROYING': 'DESTROY'
1682 a3ac649e Kostas Papadimitriou
    }
1683 a3ac649e Kostas Papadimitriou
1684 8d08f18a Kostas Papadimitriou
    models.VM.AVAILABLE_ACTIONS = {
1685 d632e425 Kostas Papadimitriou
        'UNKNWON'       : ['destroy'],
1686 d632e425 Kostas Papadimitriou
        'BUILD'         : ['destroy'],
1687 d632e425 Kostas Papadimitriou
        'REBOOT'        : ['destroy'],
1688 de0693f8 Kostas Papadimitriou
        'STOPPED'       : ['start', 'destroy', 'resize', 'snapshot'],
1689 de0693f8 Kostas Papadimitriou
        'ACTIVE'        : ['shutdown', 'destroy', 'reboot', 'console', 'resize', 'snapshot'],
1690 8d08f18a Kostas Papadimitriou
        'ERROR'         : ['destroy'],
1691 d632e425 Kostas Papadimitriou
        'DELETED'       : ['destroy'],
1692 d632e425 Kostas Papadimitriou
        'DESTROY'       : ['destroy'],
1693 d632e425 Kostas Papadimitriou
        'SHUTDOWN'      : ['destroy'],
1694 d632e425 Kostas Papadimitriou
        'START'         : ['destroy'],
1695 d632e425 Kostas Papadimitriou
        'CONNECT'       : ['destroy'],
1696 d632e425 Kostas Papadimitriou
        'DISCONNECT'    : ['destroy'],
1697 d632e425 Kostas Papadimitriou
        'RESIZE'        : ['destroy']
1698 8d08f18a Kostas Papadimitriou
    }
1699 13925182 Kostas Papadimitriou
    
1700 ddb7229d Kostas Papadimitriou
    models.VM.AVAILABLE_ACTIONS_INACTIVE = {}
1701 8d08f18a Kostas Papadimitriou
1702 8d08f18a Kostas Papadimitriou
    // api status values
1703 8d08f18a Kostas Papadimitriou
    models.VM.STATUSES = [
1704 8d08f18a Kostas Papadimitriou
        'UNKNWON',
1705 8d08f18a Kostas Papadimitriou
        'BUILD',
1706 8d08f18a Kostas Papadimitriou
        'REBOOT',
1707 8d08f18a Kostas Papadimitriou
        'STOPPED',
1708 8d08f18a Kostas Papadimitriou
        'ACTIVE',
1709 8d08f18a Kostas Papadimitriou
        'ERROR',
1710 a3ac649e Kostas Papadimitriou
        'DELETED',
1711 a3ac649e Kostas Papadimitriou
        'RESIZE'
1712 8d08f18a Kostas Papadimitriou
    ]
1713 8d08f18a Kostas Papadimitriou
1714 8d08f18a Kostas Papadimitriou
    // api status values
1715 8d08f18a Kostas Papadimitriou
    models.VM.CONNECT_STATES = [
1716 8d08f18a Kostas Papadimitriou
        'ACTIVE',
1717 8d08f18a Kostas Papadimitriou
        'REBOOT',
1718 df12e032 Kostas Papadimitriou
        'SHUTDOWN'
1719 8d08f18a Kostas Papadimitriou
    ]
1720 8d08f18a Kostas Papadimitriou
1721 8d08f18a Kostas Papadimitriou
    // vm states
1722 8d08f18a Kostas Papadimitriou
    models.VM.STATES = models.VM.STATUSES.concat([
1723 8d08f18a Kostas Papadimitriou
        'DESTROY',
1724 8d08f18a Kostas Papadimitriou
        'SHUTDOWN',
1725 8d08f18a Kostas Papadimitriou
        'START',
1726 8d08f18a Kostas Papadimitriou
        'CONNECT',
1727 8d08f18a Kostas Papadimitriou
        'DISCONNECT',
1728 a3ac649e Kostas Papadimitriou
        'FIREWALL',
1729 a3ac649e Kostas Papadimitriou
        'RESIZE'
1730 8d08f18a Kostas Papadimitriou
    ]);
1731 8d08f18a Kostas Papadimitriou
    
1732 8d08f18a Kostas Papadimitriou
    models.VM.STATES_TRANSITIONS = {
1733 643de8c0 Kostas Papadimitriou
        'DESTROY' : ['DELETED'],
1734 8d08f18a Kostas Papadimitriou
        'SHUTDOWN': ['ERROR', 'STOPPED', 'DESTROY'],
1735 8d08f18a Kostas Papadimitriou
        'STOPPED': ['ERROR', 'ACTIVE', 'DESTROY'],
1736 8d08f18a Kostas Papadimitriou
        'ACTIVE': ['ERROR', 'STOPPED', 'REBOOT', 'SHUTDOWN', 'DESTROY'],
1737 8d08f18a Kostas Papadimitriou
        'START': ['ERROR', 'ACTIVE', 'DESTROY'],
1738 8d08f18a Kostas Papadimitriou
        'REBOOT': ['ERROR', 'ACTIVE', 'STOPPED', 'DESTROY'],
1739 8d08f18a Kostas Papadimitriou
        'BUILD': ['ERROR', 'ACTIVE', 'DESTROY'],
1740 a3ac649e Kostas Papadimitriou
        'RESIZE': ['ERROR', 'STOPPED']
1741 8d08f18a Kostas Papadimitriou
    }
1742 8d08f18a Kostas Papadimitriou
1743 8d08f18a Kostas Papadimitriou
    models.VM.TRANSITION_STATES = [
1744 8d08f18a Kostas Papadimitriou
        'DESTROY',
1745 8d08f18a Kostas Papadimitriou
        'SHUTDOWN',
1746 8d08f18a Kostas Papadimitriou
        'START',
1747 8d08f18a Kostas Papadimitriou
        'REBOOT',
1748 a3ac649e Kostas Papadimitriou
        'BUILD',
1749 b3aee2e0 Kostas Papadimitriou
        'RESIZE',
1750 b3aee2e0 Kostas Papadimitriou
        'DISCONNECT',
1751 b3aee2e0 Kostas Papadimitriou
        'CONNECT'
1752 8d08f18a Kostas Papadimitriou
    ]
1753 8d08f18a Kostas Papadimitriou
1754 8d08f18a Kostas Papadimitriou
    models.VM.ACTIVE_STATES = [
1755 8d08f18a Kostas Papadimitriou
        'BUILD', 'REBOOT', 'ACTIVE',
1756 4e365467 Kostas Papadimitriou
        'SHUTDOWN', 'CONNECT', 'DISCONNECT'
1757 8d08f18a Kostas Papadimitriou
    ]
1758 8d08f18a Kostas Papadimitriou
1759 8d08f18a Kostas Papadimitriou
    models.VM.BUILDING_STATES = [
1760 a3ac649e Kostas Papadimitriou
        'BUILD'
1761 8d08f18a Kostas Papadimitriou
    ]
1762 8d08f18a Kostas Papadimitriou
1763 8d08f18a Kostas Papadimitriou
    models.Images = models.Collection.extend({
1764 8d08f18a Kostas Papadimitriou
        model: models.Image,
1765 8d08f18a Kostas Papadimitriou
        path: 'images',
1766 8d08f18a Kostas Papadimitriou
        details: true,
1767 8d08f18a Kostas Papadimitriou
        noUpdate: true,
1768 16392650 Kostas Papadimitriou
        supportIncUpdates: false,
1769 8d08f18a Kostas Papadimitriou
        meta_keys_as_attrs: ["OS", "description", "kernel", "size", "GUI"],
1770 88d6d677 Kostas Papadimitriou
        meta_labels: {},
1771 258235f4 Kostas Papadimitriou
        read_method: 'read',
1772 8d08f18a Kostas Papadimitriou
1773 bedfe884 Kostas Papadimitriou
        // update collection model with id passed
1774 1420aae4 Kostas Papadimitriou
        // making a direct call to the image
1775 bedfe884 Kostas Papadimitriou
        // api url
1776 c45bed88 Kostas Papadimitriou
        update_unknown_id: function(id, callback) {
1777 bedfe884 Kostas Papadimitriou
            var url = getUrl.call(this) + "/" + id;
1778 126a01f2 Kostas Papadimitriou
            this.api_call(this.path + "/" + id, this.read_method, {
1779 126a01f2 Kostas Papadimitriou
              _options:{
1780 126a01f2 Kostas Papadimitriou
                async:true, 
1781 126a01f2 Kostas Papadimitriou
                skip_api_error:true}
1782 126a01f2 Kostas Papadimitriou
              }, undefined, 
1783 bedfe884 Kostas Papadimitriou
            _.bind(function() {
1784 327f0d58 Kostas Papadimitriou
                if (!this.get(id)) {
1785 d7612b6e Kostas Papadimitriou
                            if (this.fallback_service) {
1786 d7612b6e Kostas Papadimitriou
                        // if current service has fallback_service attribute set
1787 d7612b6e Kostas Papadimitriou
                        // use this service to retrieve the missing image model
1788 d7612b6e Kostas Papadimitriou
                        var tmpservice = new this.fallback_service();
1789 d7612b6e Kostas Papadimitriou
                        tmpservice.update_unknown_id(id, _.bind(function(img){
1790 d7612b6e Kostas Papadimitriou
                            img.attributes.status = "DELETED";
1791 d7612b6e Kostas Papadimitriou
                            this.add(img.attributes);
1792 d7612b6e Kostas Papadimitriou
                            callback(this.get(id));
1793 d7612b6e Kostas Papadimitriou
                        }, this));
1794 d7612b6e Kostas Papadimitriou
                    } else {
1795 03639c6d Kostas Papadimitriou
                        var title = synnefo.config.image_deleted_title || 'Deleted';
1796 d7612b6e Kostas Papadimitriou
                        // else add a dummy DELETED state image entry
1797 03639c6d Kostas Papadimitriou
                        this.add({id:id, name:title, size:-1, 
1798 d7612b6e Kostas Papadimitriou
                                  progress:100, status:"DELETED"});
1799 d7612b6e Kostas Papadimitriou
                        callback(this.get(id));
1800 d7612b6e Kostas Papadimitriou
                    }   
1801 d7612b6e Kostas Papadimitriou
                } else {
1802 d7612b6e Kostas Papadimitriou
                    callback(this.get(id));
1803 327f0d58 Kostas Papadimitriou
                }
1804 258235f4 Kostas Papadimitriou
            }, this), _.bind(function(image, msg, xhr) {
1805 f70c8d47 Kostas Papadimitriou
                if (!image) {
1806 03639c6d Kostas Papadimitriou
                    var title = synnefo.config.image_deleted_title || 'Deleted';
1807 03639c6d Kostas Papadimitriou
                    this.add({id:id, name:title, size:-1, 
1808 f70c8d47 Kostas Papadimitriou
                              progress:100, status:"DELETED"});
1809 f70c8d47 Kostas Papadimitriou
                    callback(this.get(id));
1810 f70c8d47 Kostas Papadimitriou
                    return;
1811 f70c8d47 Kostas Papadimitriou
                }
1812 258235f4 Kostas Papadimitriou
                var img_data = this._read_image_from_request(image, msg, xhr);
1813 258235f4 Kostas Papadimitriou
                this.add(img_data);
1814 c45bed88 Kostas Papadimitriou
                callback(this.get(id));
1815 bedfe884 Kostas Papadimitriou
            }, this));
1816 bedfe884 Kostas Papadimitriou
        },
1817 bedfe884 Kostas Papadimitriou
1818 258235f4 Kostas Papadimitriou
        _read_image_from_request: function(image, msg, xhr) {
1819 258235f4 Kostas Papadimitriou
            return image.image;
1820 258235f4 Kostas Papadimitriou
        },
1821 258235f4 Kostas Papadimitriou
1822 8d08f18a Kostas Papadimitriou
        parse: function (resp, xhr) {
1823 0dee4086 Kostas Papadimitriou
            var parsed = _.map(resp.images, _.bind(this.parse_meta, this));
1824 8f53cfa3 Kostas Papadimitriou
            parsed = this.fill_owners(parsed);
1825 8f53cfa3 Kostas Papadimitriou
            return parsed;
1826 8f53cfa3 Kostas Papadimitriou
        },
1827 8f53cfa3 Kostas Papadimitriou
1828 8f53cfa3 Kostas Papadimitriou
        fill_owners: function(images) {
1829 8f53cfa3 Kostas Papadimitriou
            // do translate uuid->displayname if needed
1830 8f53cfa3 Kostas Papadimitriou
            // store display name in owner attribute for compatibility
1831 8f53cfa3 Kostas Papadimitriou
            var uuids = [];
1832 8f53cfa3 Kostas Papadimitriou
1833 8f53cfa3 Kostas Papadimitriou
            var images = _.map(images, function(img, index) {
1834 8f53cfa3 Kostas Papadimitriou
                if (synnefo.config.translate_uuids) {
1835 8f53cfa3 Kostas Papadimitriou
                    uuids.push(img['owner']);
1836 8f53cfa3 Kostas Papadimitriou
                }
1837 8f53cfa3 Kostas Papadimitriou
                img['owner_uuid'] = img['owner'];
1838 8f53cfa3 Kostas Papadimitriou
                return img;
1839 8f53cfa3 Kostas Papadimitriou
            });
1840 8f53cfa3 Kostas Papadimitriou
            
1841 8f53cfa3 Kostas Papadimitriou
            if (uuids.length > 0) {
1842 8f53cfa3 Kostas Papadimitriou
                var handle_results = function(data) {
1843 8f53cfa3 Kostas Papadimitriou
                    _.each(images, function (img) {
1844 8f53cfa3 Kostas Papadimitriou
                        img['owner'] = data.uuid_catalog[img['owner_uuid']];
1845 8f53cfa3 Kostas Papadimitriou
                    });
1846 8f53cfa3 Kostas Papadimitriou
                }
1847 8f53cfa3 Kostas Papadimitriou
                // notice the async false
1848 8f53cfa3 Kostas Papadimitriou
                var uuid_map = this.translate_uuids(uuids, false, 
1849 8f53cfa3 Kostas Papadimitriou
                                                    handle_results)
1850 8f53cfa3 Kostas Papadimitriou
            }
1851 8f53cfa3 Kostas Papadimitriou
            return images;
1852 8f53cfa3 Kostas Papadimitriou
        },
1853 8f53cfa3 Kostas Papadimitriou
1854 8f53cfa3 Kostas Papadimitriou
        translate_uuids: function(uuids, async, cb) {
1855 8f53cfa3 Kostas Papadimitriou
            var url = synnefo.config.user_catalog_url;
1856 8f53cfa3 Kostas Papadimitriou
            var data = JSON.stringify({'uuids': uuids});
1857 8f53cfa3 Kostas Papadimitriou
          
1858 8f53cfa3 Kostas Papadimitriou
            // post to user_catalogs api
1859 8f53cfa3 Kostas Papadimitriou
            snf.api.sync('create', undefined, {
1860 8f53cfa3 Kostas Papadimitriou
                url: url,
1861 8f53cfa3 Kostas Papadimitriou
                data: data,
1862 8f53cfa3 Kostas Papadimitriou
                async: async,
1863 8f53cfa3 Kostas Papadimitriou
                success:  cb
1864 8f53cfa3 Kostas Papadimitriou
            });
1865 8d08f18a Kostas Papadimitriou
        },
1866 8d08f18a Kostas Papadimitriou
1867 8d08f18a Kostas Papadimitriou
        get_meta_key: function(img, key) {
1868 0dee4086 Kostas Papadimitriou
            if (img.metadata && img.metadata && img.metadata[key]) {
1869 0dee4086 Kostas Papadimitriou
                return _.escape(img.metadata[key]);
1870 8d08f18a Kostas Papadimitriou
            }
1871 8d08f18a Kostas Papadimitriou
            return undefined;
1872 8d08f18a Kostas Papadimitriou
        },
1873 8d08f18a Kostas Papadimitriou
1874 ccb14ad0 Kostas Papadimitriou
        comparator: function(img) {
1875 09092536 Kostas Papadimitriou
            return -img.get_sort_order("sortorder") || 0;
1876 ccb14ad0 Kostas Papadimitriou
        },
1877 ccb14ad0 Kostas Papadimitriou
1878 8d08f18a Kostas Papadimitriou
        parse_meta: function(img) {
1879 8d08f18a Kostas Papadimitriou
            _.each(this.meta_keys_as_attrs, _.bind(function(key){
1880 258235f4 Kostas Papadimitriou
                if (img[key]) { return };
1881 2a5ae934 Kostas Papadimitriou
                img[key] = this.get_meta_key(img, key) || "";
1882 8d08f18a Kostas Papadimitriou
            }, this));
1883 8d08f18a Kostas Papadimitriou
            return img;
1884 bedfe884 Kostas Papadimitriou
        },
1885 bedfe884 Kostas Papadimitriou
1886 bedfe884 Kostas Papadimitriou
        active: function() {
1887 bedfe884 Kostas Papadimitriou
            return this.filter(function(img){return img.get('status') != "DELETED"});
1888 2cee7cb0 Kostas Papadimitriou
        },
1889 2cee7cb0 Kostas Papadimitriou
1890 2cee7cb0 Kostas Papadimitriou
        predefined: function() {
1891 2cee7cb0 Kostas Papadimitriou
            return _.filter(this.active(), function(i) { return !i.get("serverRef")});
1892 948042ca Kostas Papadimitriou
        },
1893 948042ca Kostas Papadimitriou
        
1894 948042ca Kostas Papadimitriou
        fetch_for_type: function(type, complete, error) {
1895 948042ca Kostas Papadimitriou
            this.fetch({update:true, 
1896 948042ca Kostas Papadimitriou
                        success: complete, 
1897 948042ca Kostas Papadimitriou
                        error: error, 
1898 948042ca Kostas Papadimitriou
                        skip_api_error: true });
1899 948042ca Kostas Papadimitriou
        },
1900 948042ca Kostas Papadimitriou
        
1901 948042ca Kostas Papadimitriou
        get_images_for_type: function(type) {
1902 948042ca Kostas Papadimitriou
            if (this['get_{0}_images'.format(type)]) {
1903 948042ca Kostas Papadimitriou
                return this['get_{0}_images'.format(type)]();
1904 948042ca Kostas Papadimitriou
            }
1905 948042ca Kostas Papadimitriou
1906 948042ca Kostas Papadimitriou
            return this.active();
1907 948042ca Kostas Papadimitriou
        },
1908 948042ca Kostas Papadimitriou
1909 948042ca Kostas Papadimitriou
        update_images_for_type: function(type, onStart, onComplete, onError, force_load) {
1910 948042ca Kostas Papadimitriou
            var load = false;
1911 948042ca Kostas Papadimitriou
            error = onError || function() {};
1912 948042ca Kostas Papadimitriou
            function complete(collection) { 
1913 948042ca Kostas Papadimitriou
                onComplete(collection.get_images_for_type(type)); 
1914 948042ca Kostas Papadimitriou
            }
1915 948042ca Kostas Papadimitriou
            
1916 948042ca Kostas Papadimitriou
            // do we need to fetch/update current collection entries
1917 948042ca Kostas Papadimitriou
            if (load) {
1918 948042ca Kostas Papadimitriou
                onStart();
1919 948042ca Kostas Papadimitriou
                this.fetch_for_type(type, complete, error);
1920 948042ca Kostas Papadimitriou
            } else {
1921 948042ca Kostas Papadimitriou
                // fallback to complete
1922 948042ca Kostas Papadimitriou
                complete(this);
1923 948042ca Kostas Papadimitriou
            }
1924 8d08f18a Kostas Papadimitriou
        }
1925 8d08f18a Kostas Papadimitriou
    })
1926 8d08f18a Kostas Papadimitriou
1927 8d08f18a Kostas Papadimitriou
    models.Flavors = models.Collection.extend({
1928 8d08f18a Kostas Papadimitriou
        model: models.Flavor,
1929 8d08f18a Kostas Papadimitriou
        path: 'flavors',
1930 8d08f18a Kostas Papadimitriou
        details: true,
1931 8d08f18a Kostas Papadimitriou
        noUpdate: true,
1932 16392650 Kostas Papadimitriou
        supportIncUpdates: false,
1933 bedfe884 Kostas Papadimitriou
        // update collection model with id passed
1934 bedfe884 Kostas Papadimitriou
        // making a direct call to the flavor
1935 bedfe884 Kostas Papadimitriou
        // api url
1936 c45bed88 Kostas Papadimitriou
        update_unknown_id: function(id, callback) {
1937 bedfe884 Kostas Papadimitriou
            var url = getUrl.call(this) + "/" + id;
1938 258235f4 Kostas Papadimitriou
            this.api_call(this.path + "/" + id, "read", {_options:{async:false, skip_api_error:true}}, undefined, 
1939 bedfe884 Kostas Papadimitriou
            _.bind(function() {
1940 9555268a Kostas Papadimitriou
                this.add({id:id, cpu:"Unknown", ram:"Unknown", disk:"Unknown", name: "Unknown", status:"DELETED"})
1941 bedfe884 Kostas Papadimitriou
            }, this), _.bind(function(flv) {
1942 bedfe884 Kostas Papadimitriou
                if (!flv.flavor.status) { flv.flavor.status = "DELETED" };
1943 bedfe884 Kostas Papadimitriou
                this.add(flv.flavor);
1944 bedfe884 Kostas Papadimitriou
            }, this));
1945 bedfe884 Kostas Papadimitriou
        },
1946 8d08f18a Kostas Papadimitriou
1947 8d08f18a Kostas Papadimitriou
        parse: function (resp, xhr) {
1948 29fef7b9 Christos Stavrakakis
            return _.map(resp.flavors, function(o) {
1949 29fef7b9 Christos Stavrakakis
              o.cpu = o['vcpus'];
1950 29fef7b9 Christos Stavrakakis
              o.disk_template = o['SNF:disk_template'];
1951 29fef7b9 Christos Stavrakakis
              return o
1952 29fef7b9 Christos Stavrakakis
            });
1953 8d08f18a Kostas Papadimitriou
        },
1954 8d08f18a Kostas Papadimitriou
1955 ccb14ad0 Kostas Papadimitriou
        comparator: function(flv) {
1956 ccb14ad0 Kostas Papadimitriou
            return flv.get("disk") * flv.get("cpu") * flv.get("ram");
1957 ccb14ad0 Kostas Papadimitriou
        },
1958 ab3df8df Kostas Papadimitriou
          
1959 ab3df8df Kostas Papadimitriou
        unavailable_values_for_quotas: function(quotas, flavors, extra) {
1960 7ac49c2e Kostas Papadimitriou
            var flavors = flavors || this.active();
1961 7ac49c2e Kostas Papadimitriou
            var index = {cpu:[], disk:[], ram:[]};
1962 ab3df8df Kostas Papadimitriou
            var extra = extra == undefined ? {cpu:0, disk:0, ram:0} : extra;
1963 7ac49c2e Kostas Papadimitriou
            
1964 7ac49c2e Kostas Papadimitriou
            _.each(flavors, function(el) {
1965 7ac49c2e Kostas Papadimitriou
1966 ab3df8df Kostas Papadimitriou
                var disk_available = quotas['disk'] + extra.disk;
1967 7ac49c2e Kostas Papadimitriou
                var disk_size = el.get_disk_size();
1968 7ac49c2e Kostas Papadimitriou
                if (index.disk.indexOf(disk_size) == -1) {
1969 7ac49c2e Kostas Papadimitriou
                  var disk = el.disk_to_bytes();
1970 7ac49c2e Kostas Papadimitriou
                  if (disk > disk_available) {
1971 ac5a4134 Kostas Papadimitriou
                    index.disk.push(el.get('disk'));
1972 7ac49c2e Kostas Papadimitriou
                  }
1973 7ac49c2e Kostas Papadimitriou
                }
1974 ab3df8df Kostas Papadimitriou
                
1975 ab3df8df Kostas Papadimitriou
                var ram_available = quotas['ram'] + extra.ram * 1024 * 1024;
1976 7ac49c2e Kostas Papadimitriou
                var ram_size = el.get_ram_size();
1977 2d35cfcd Kostas Papadimitriou
                if (index.ram.indexOf(ram_size) == -1) {
1978 7ac49c2e Kostas Papadimitriou
                  var ram = el.ram_to_bytes();
1979 7ac49c2e Kostas Papadimitriou
                  if (ram > ram_available) {
1980 7ac49c2e Kostas Papadimitriou
                    index.ram.push(el.get('ram'))
1981 7ac49c2e Kostas Papadimitriou
                  }
1982 7ac49c2e Kostas Papadimitriou
                }
1983 7ac49c2e Kostas Papadimitriou
1984 7ac49c2e Kostas Papadimitriou
                var cpu = el.get('cpu');
1985 ab3df8df Kostas Papadimitriou
                var cpu_available = quotas['cpu'] + extra.cpu;
1986 ab3df8df Kostas Papadimitriou
                if (index.cpu.indexOf(cpu) == -1) {
1987 7ac49c2e Kostas Papadimitriou
                  if (cpu > cpu_available) {
1988 7ac49c2e Kostas Papadimitriou
                    index.cpu.push(el.get('cpu'))
1989 7ac49c2e Kostas Papadimitriou
                  }
1990 7ac49c2e Kostas Papadimitriou
                }
1991 7ac49c2e Kostas Papadimitriou
            });
1992 7ac49c2e Kostas Papadimitriou
            return index;
1993 7ac49c2e Kostas Papadimitriou
        },
1994 7ac49c2e Kostas Papadimitriou
1995 820ef2f0 Kostas Papadimitriou
        unavailable_values_for_image: function(img, flavors) {
1996 820ef2f0 Kostas Papadimitriou
            var flavors = flavors || this.active();
1997 820ef2f0 Kostas Papadimitriou
            var size = img.get_size();
1998 820ef2f0 Kostas Papadimitriou
            
1999 820ef2f0 Kostas Papadimitriou
            var index = {cpu:[], disk:[], ram:[]};
2000 820ef2f0 Kostas Papadimitriou
2001 820ef2f0 Kostas Papadimitriou
            _.each(this.active(), function(el) {
2002 820ef2f0 Kostas Papadimitriou
                var img_size = size;
2003 820ef2f0 Kostas Papadimitriou
                var flv_size = el.get_disk_size();
2004 820ef2f0 Kostas Papadimitriou
                if (flv_size < img_size) {
2005 ac5a4134 Kostas Papadimitriou
                    if (index.disk.indexOf(el.get("disk")) == -1) {
2006 ac5a4134 Kostas Papadimitriou
                        index.disk.push(el.get("disk"));
2007 820ef2f0 Kostas Papadimitriou
                    }
2008 820ef2f0 Kostas Papadimitriou
                };
2009 820ef2f0 Kostas Papadimitriou
            });
2010 820ef2f0 Kostas Papadimitriou
            
2011 820ef2f0 Kostas Papadimitriou
            return index;
2012 8d08f18a Kostas Papadimitriou
        },
2013 820ef2f0 Kostas Papadimitriou
2014 d965e6dc Kostas Papadimitriou
        get_flavor: function(cpu, mem, disk, disk_template, filter_list) {
2015 8d08f18a Kostas Papadimitriou
            if (!filter_list) { filter_list = this.models };
2016 d965e6dc Kostas Papadimitriou
            
2017 8d08f18a Kostas Papadimitriou
            return this.select(function(flv){
2018 8d08f18a Kostas Papadimitriou
                if (flv.get("cpu") == cpu + "" &&
2019 8d08f18a Kostas Papadimitriou
                   flv.get("ram") == mem + "" &&
2020 8d08f18a Kostas Papadimitriou
                   flv.get("disk") == disk + "" &&
2021 d965e6dc Kostas Papadimitriou
                   flv.get("disk_template") == disk_template &&
2022 820ef2f0 Kostas Papadimitriou
                   filter_list.indexOf(flv) > -1) { return true; }
2023 9ce969a7 Kostas Papadimitriou
            })[0];
2024 8d08f18a Kostas Papadimitriou
        },
2025 820ef2f0 Kostas Papadimitriou
        
2026 8d08f18a Kostas Papadimitriou
        get_data: function(lst) {
2027 ab3df8df Kostas Papadimitriou
            var data = {'cpu': [], 'mem':[], 'disk':[], 'disk_template':[]};
2028 820ef2f0 Kostas Papadimitriou
2029 8d08f18a Kostas Papadimitriou
            _.each(lst, function(flv) {
2030 820ef2f0 Kostas Papadimitriou
                if (data.cpu.indexOf(flv.get("cpu")) == -1) {
2031 820ef2f0 Kostas Papadimitriou
                    data.cpu.push(flv.get("cpu"));
2032 8d08f18a Kostas Papadimitriou
                }
2033 820ef2f0 Kostas Papadimitriou
                if (data.mem.indexOf(flv.get("ram")) == -1) {
2034 820ef2f0 Kostas Papadimitriou
                    data.mem.push(flv.get("ram"));
2035 8d08f18a Kostas Papadimitriou
                }
2036 820ef2f0 Kostas Papadimitriou
                if (data.disk.indexOf(flv.get("disk")) == -1) {
2037 820ef2f0 Kostas Papadimitriou
                    data.disk.push(flv.get("disk"));
2038 8d08f18a Kostas Papadimitriou
                }
2039 ab3df8df Kostas Papadimitriou
                if (data.disk_template.indexOf(flv.get("disk_template")) == -1) {
2040 ab3df8df Kostas Papadimitriou
                    data.disk_template.push(flv.get("disk_template"));
2041 ab3df8df Kostas Papadimitriou
                }
2042 8d08f18a Kostas Papadimitriou
            })
2043 820ef2f0 Kostas Papadimitriou
            
2044 8d08f18a Kostas Papadimitriou
            return data;
2045 bedfe884 Kostas Papadimitriou
        },
2046 bedfe884 Kostas Papadimitriou
2047 bedfe884 Kostas Papadimitriou
        active: function() {
2048 bedfe884 Kostas Papadimitriou
            return this.filter(function(flv){return flv.get('status') != "DELETED"});
2049 8d08f18a Kostas Papadimitriou
        }
2050 8d08f18a Kostas Papadimitriou
            
2051 8d08f18a Kostas Papadimitriou
    })
2052 8d08f18a Kostas Papadimitriou
2053 8d08f18a Kostas Papadimitriou
    models.VMS = models.Collection.extend({
2054 8d08f18a Kostas Papadimitriou
        model: models.VM,
2055 8d08f18a Kostas Papadimitriou
        path: 'servers',
2056 8d08f18a Kostas Papadimitriou
        details: true,
2057 8d08f18a Kostas Papadimitriou
        copy_image_meta: true,
2058 126a01f2 Kostas Papadimitriou
2059 8d08f18a Kostas Papadimitriou
        parse: function (resp, xhr) {
2060 8d08f18a Kostas Papadimitriou
            var data = resp;
2061 8d08f18a Kostas Papadimitriou
            if (!resp) { return [] };
2062 45b964d3 Kostas Papadimitriou
            data = _.filter(_.map(resp.servers, 
2063 45b964d3 Kostas Papadimitriou
                                  _.bind(this.parse_vm_api_data, this)), 
2064 45b964d3 Kostas Papadimitriou
                                  function(v){return v});
2065 8d08f18a Kostas Papadimitriou
            return data;
2066 8d08f18a Kostas Papadimitriou
        },
2067 126a01f2 Kostas Papadimitriou
2068 1e882dd7 Kostas Papadimitriou
        parse_vm_api_data: function(data) {
2069 1e882dd7 Kostas Papadimitriou
            // do not add non existing DELETED entries
2070 1e882dd7 Kostas Papadimitriou
            if (data.status && data.status == "DELETED") {
2071 1e882dd7 Kostas Papadimitriou
                if (!this.get(data.id)) {
2072 1e882dd7 Kostas Papadimitriou
                    return false;
2073 1e882dd7 Kostas Papadimitriou
                }
2074 1e882dd7 Kostas Papadimitriou
            }
2075 a3ac649e Kostas Papadimitriou
            
2076 a3ac649e Kostas Papadimitriou
            if ('SNF:task_state' in data) { 
2077 a3ac649e Kostas Papadimitriou
                data['task_state'] = data['SNF:task_state'];
2078 a3ac649e Kostas Papadimitriou
                if (data['task_state']) {
2079 a3ac649e Kostas Papadimitriou
                    var status = models.VM.TASK_STATE_STATUS_MAP[data['task_state']];
2080 a3ac649e Kostas Papadimitriou
                    if (status) { data['status'] = status }
2081 a3ac649e Kostas Papadimitriou
                }
2082 a3ac649e Kostas Papadimitriou
            }
2083 1e882dd7 Kostas Papadimitriou
2084 1e882dd7 Kostas Papadimitriou
            // OS attribute
2085 1e882dd7 Kostas Papadimitriou
            if (this.has_meta(data)) {
2086 0dee4086 Kostas Papadimitriou
                data['OS'] = data.metadata.OS || snf.config.unknown_os;
2087 1e882dd7 Kostas Papadimitriou
            }
2088 1e882dd7 Kostas Papadimitriou
            
2089 1e882dd7 Kostas Papadimitriou
            if (!data.diagnostics) {
2090 1e882dd7 Kostas Papadimitriou
                data.diagnostics = [];
2091 1e882dd7 Kostas Papadimitriou
            }
2092 1e882dd7 Kostas Papadimitriou
2093 1e882dd7 Kostas Papadimitriou
            // network metadata
2094 1e882dd7 Kostas Papadimitriou
            data['firewalls'] = {};
2095 6fe8c2ca Kostas Papadimitriou
            data['fqdn'] = data['SNF:fqdn'];
2096 d7d154c9 Kostas Papadimitriou
2097 1e882dd7 Kostas Papadimitriou
            // if vm has no metadata, no metadata object
2098 1e882dd7 Kostas Papadimitriou
            // is in json response, reset it to force
2099 1e882dd7 Kostas Papadimitriou
            // value update
2100 1e882dd7 Kostas Papadimitriou
            if (!data['metadata']) {
2101 0dee4086 Kostas Papadimitriou
                data['metadata'] = {};
2102 1e882dd7 Kostas Papadimitriou
            }
2103 60ba7fc6 Kostas Papadimitriou
            
2104 60ba7fc6 Kostas Papadimitriou
            // v2.0 API returns objects
2105 60ba7fc6 Kostas Papadimitriou
            data.image_obj = data.image;
2106 60ba7fc6 Kostas Papadimitriou
            data.image = data.image_obj.id;
2107 60ba7fc6 Kostas Papadimitriou
            data.flavor_obj = data.flavor;
2108 60ba7fc6 Kostas Papadimitriou
            data.flavor = data.flavor_obj.id;
2109 1e882dd7 Kostas Papadimitriou
2110 1e882dd7 Kostas Papadimitriou
            return data;
2111 1e882dd7 Kostas Papadimitriou
        },
2112 1e882dd7 Kostas Papadimitriou
2113 122850c5 Kostas Papadimitriou
        get_reboot_required: function() {
2114 122850c5 Kostas Papadimitriou
            return this.filter(function(vm){return vm.get("reboot_required") == true})
2115 122850c5 Kostas Papadimitriou
        },
2116 122850c5 Kostas Papadimitriou
2117 122850c5 Kostas Papadimitriou
        has_pending_actions: function() {
2118 122850c5 Kostas Papadimitriou
            return this.filter(function(vm){return vm.pending_action}).length > 0;
2119 122850c5 Kostas Papadimitriou
        },
2120 8d08f18a Kostas Papadimitriou
2121 8d08f18a Kostas Papadimitriou
        reset_pending_actions: function() {
2122 8d08f18a Kostas Papadimitriou
            this.each(function(vm) {
2123 8d08f18a Kostas Papadimitriou
                vm.clear_pending_action();
2124 8d08f18a Kostas Papadimitriou
            })
2125 8d08f18a Kostas Papadimitriou
        },
2126 101e6604 Kostas Papadimitriou
2127 101e6604 Kostas Papadimitriou
        do_all_pending_actions: function(success, error) {
2128 101e6604 Kostas Papadimitriou
            this.each(function(vm) {
2129 101e6604 Kostas Papadimitriou
                if (vm.has_pending_action()) {
2130 101e6604 Kostas Papadimitriou
                    vm.call(vm.pending_action, success, error);
2131 101e6604 Kostas Papadimitriou
                    vm.clear_pending_action();
2132 101e6604 Kostas Papadimitriou
                }
2133 101e6604 Kostas Papadimitriou
            })
2134 101e6604 Kostas Papadimitriou
        },
2135 101e6604 Kostas Papadimitriou
        
2136 101e6604 Kostas Papadimitriou
        do_all_reboots: function(success, error) {
2137 101e6604 Kostas Papadimitriou
            this.each(function(vm) {
2138 101e6604 Kostas Papadimitriou
                if (vm.get("reboot_required")) {
2139 101e6604 Kostas Papadimitriou
                    vm.call("reboot", success, error);
2140 101e6604 Kostas Papadimitriou
                }
2141 101e6604 Kostas Papadimitriou
            });
2142 101e6604 Kostas Papadimitriou
        },
2143 101e6604 Kostas Papadimitriou
2144 101e6604 Kostas Papadimitriou
        reset_reboot_required: function() {
2145 101e6604 Kostas Papadimitriou
            this.each(function(vm) {
2146 101e6604 Kostas Papadimitriou
                vm.set({'reboot_required': undefined});
2147 101e6604 Kostas Papadimitriou
            })
2148 101e6604 Kostas Papadimitriou
        },
2149 2c9bfad1 Kostas Papadimitriou
        
2150 2c9bfad1 Kostas Papadimitriou
        stop_stats_update: function(exclude) {
2151 2c9bfad1 Kostas Papadimitriou
            var exclude = exclude || [];
2152 96d725ac Kostas Papadimitriou
            this.each(function(vm) {
2153 2c9bfad1 Kostas Papadimitriou
                if (exclude.indexOf(vm) > -1) {
2154 2c9bfad1 Kostas Papadimitriou
                    return;
2155 2c9bfad1 Kostas Papadimitriou
                }
2156 2c9bfad1 Kostas Papadimitriou
                vm.stop_stats_update();
2157 96d725ac Kostas Papadimitriou
            })
2158 96d725ac Kostas Papadimitriou
        },
2159 8d08f18a Kostas Papadimitriou
        
2160 8d08f18a Kostas Papadimitriou
        has_meta: function(vm_data) {
2161 0dee4086 Kostas Papadimitriou
            return vm_data.metadata && vm_data.metadata
2162 8d08f18a Kostas Papadimitriou
        },
2163 8d08f18a Kostas Papadimitriou
2164 8d08f18a Kostas Papadimitriou
        has_addresses: function(vm_data) {
2165 0dee4086 Kostas Papadimitriou
            return vm_data.metadata && vm_data.metadata
2166 8d08f18a Kostas Papadimitriou
        },
2167 8d08f18a Kostas Papadimitriou
2168 8d08f18a Kostas Papadimitriou
        create: function (name, image, flavor, meta, extra, callback) {
2169 88d6d677 Kostas Papadimitriou
2170 8d08f18a Kostas Papadimitriou
            if (this.copy_image_meta) {
2171 88d6d677 Kostas Papadimitriou
                if (synnefo.config.vm_image_common_metadata) {
2172 88d6d677 Kostas Papadimitriou
                    _.each(synnefo.config.vm_image_common_metadata, 
2173 88d6d677 Kostas Papadimitriou
                        function(key){
2174 88d6d677 Kostas Papadimitriou
                            if (image.get_meta(key)) {
2175 88d6d677 Kostas Papadimitriou
                                meta[key] = image.get_meta(key);
2176 88d6d677 Kostas Papadimitriou
                            }
2177 88d6d677 Kostas Papadimitriou
                    });
2178 88d6d677 Kostas Papadimitriou
                }
2179 88d6d677 Kostas Papadimitriou
2180 d5ba5588 Kostas Papadimitriou
                if (image.get("OS")) {
2181 d5ba5588 Kostas Papadimitriou
                    meta['OS'] = image.get("OS");
2182 d5ba5588 Kostas Papadimitriou
                }
2183 88d6d677 Kostas Papadimitriou
            }
2184 8d08f18a Kostas Papadimitriou
            
2185 1489e9b1 Kostas Papadimitriou
            opts = {name: name, imageRef: image.id, flavorRef: flavor.id, 
2186 426e1fb9 Kostas Papadimitriou
                    metadata:meta}
2187 8d08f18a Kostas Papadimitriou
            opts = _.extend(opts, extra);
2188 b14e3b9e Kostas Papadimitriou
            
2189 ee5d26fc Kostas Papadimitriou
            var cb = function(data) {
2190 426e1fb9 Kostas Papadimitriou
              synnefo.storage.quotas.get('cyclades.vm').increase();
2191 ee5d26fc Kostas Papadimitriou
              callback(data);
2192 b14e3b9e Kostas Papadimitriou
            }
2193 8d08f18a Kostas Papadimitriou
2194 426e1fb9 Kostas Papadimitriou
            this.api_call(this.path, "create", {'server': opts}, undefined, 
2195 426e1fb9 Kostas Papadimitriou
                          undefined, cb, {critical: true});
2196 336ddd59 Kostas Papadimitriou
        },
2197 8d08f18a Kostas Papadimitriou
2198 336ddd59 Kostas Papadimitriou
        load_missing_images: function(callback) {
2199 336ddd59 Kostas Papadimitriou
          var missing_ids = [];
2200 614fb66b Kostas Papadimitriou
          var resolved = 0;
2201 614fb66b Kostas Papadimitriou
2202 614fb66b Kostas Papadimitriou
          // fill missing_ids
2203 336ddd59 Kostas Papadimitriou
          this.each(function(el) {
2204 0dee4086 Kostas Papadimitriou
            var imgid = el.get("image");
2205 336ddd59 Kostas Papadimitriou
            var existing = synnefo.storage.images.get(imgid);
2206 336ddd59 Kostas Papadimitriou
            if (!existing && missing_ids.indexOf(imgid) == -1) {
2207 614fb66b Kostas Papadimitriou
              missing_ids.push(imgid);
2208 336ddd59 Kostas Papadimitriou
            }
2209 336ddd59 Kostas Papadimitriou
          });
2210 614fb66b Kostas Papadimitriou
          var check = function() {
2211 614fb66b Kostas Papadimitriou
            // once all missing ids where resolved continue calling the 
2212 614fb66b Kostas Papadimitriou
            // callback
2213 614fb66b Kostas Papadimitriou
            resolved++;
2214 614fb66b Kostas Papadimitriou
            if (resolved == missing_ids.length) {
2215 614fb66b Kostas Papadimitriou
              callback(missing_ids)
2216 614fb66b Kostas Papadimitriou
            }
2217 614fb66b Kostas Papadimitriou
          }
2218 73f9a3d5 Kostas Papadimitriou
          if (missing_ids.length == 0) {
2219 73f9a3d5 Kostas Papadimitriou
            callback(missing_ids);
2220 73f9a3d5 Kostas Papadimitriou
            return;
2221 73f9a3d5 Kostas Papadimitriou
          }
2222 614fb66b Kostas Papadimitriou
          // start resolving missing image ids
2223 614fb66b Kostas Papadimitriou
          _(missing_ids).each(function(imgid){
2224 614fb66b Kostas Papadimitriou
            synnefo.storage.images.update_unknown_id(imgid, check);
2225 614fb66b Kostas Papadimitriou
          });
2226 1fc7640d Kostas Papadimitriou
        },
2227 1fc7640d Kostas Papadimitriou
2228 1fc7640d Kostas Papadimitriou
        get_connectable: function() {
2229 1fc7640d Kostas Papadimitriou
            return storage.vms.filter(function(vm){
2230 1fc7640d Kostas Papadimitriou
                return !vm.in_error_state() && !vm.is_building();
2231 1fc7640d Kostas Papadimitriou
            });
2232 8d08f18a Kostas Papadimitriou
        }
2233 8d08f18a Kostas Papadimitriou
    })
2234 126a01f2 Kostas Papadimitriou
    
2235 c4fff449 Kostas Papadimitriou
    models.PublicKey = models.Model.extend({
2236 859a4609 Kostas Papadimitriou
        path: 'keys',
2237 7f16b9b1 Kostas Papadimitriou
        api_type: 'userdata',
2238 45b964d3 Kostas Papadimitriou
        detail: false,
2239 45b964d3 Kostas Papadimitriou
        model_actions: {
2240 45b964d3 Kostas Papadimitriou
          'remove': [['name'], function() {
2241 45b964d3 Kostas Papadimitriou
            return true;
2242 45b964d3 Kostas Papadimitriou
          }]
2243 45b964d3 Kostas Papadimitriou
        },
2244 859a4609 Kostas Papadimitriou
2245 859a4609 Kostas Papadimitriou
        get_public_key: function() {
2246 859a4609 Kostas Papadimitriou
            return cryptico.publicKeyFromString(this.get("content"));
2247 859a4609 Kostas Papadimitriou
        },
2248 859a4609 Kostas Papadimitriou
2249 859a4609 Kostas Papadimitriou
        get_filename: function() {
2250 859a4609 Kostas Papadimitriou
            return "{0}.pub".format(this.get("name"));
2251 859a4609 Kostas Papadimitriou
        },
2252 859a4609 Kostas Papadimitriou
2253 859a4609 Kostas Papadimitriou
        identify_type: function() {
2254 859a4609 Kostas Papadimitriou
            try {
2255 859a4609 Kostas Papadimitriou
                var cont = snf.util.validatePublicKey(this.get("content"));
2256 859a4609 Kostas Papadimitriou
                var type = cont.split(" ")[0];
2257 859a4609 Kostas Papadimitriou
                return synnefo.util.publicKeyTypesMap[type];
2258 859a4609 Kostas Papadimitriou
            } catch (err) { return false };
2259 45b964d3 Kostas Papadimitriou
        },
2260 859a4609 Kostas Papadimitriou
2261 45b964d3 Kostas Papadimitriou
        rename: function(new_name) {
2262 45b964d3 Kostas Papadimitriou
          //this.set({'name': new_name});
2263 45b964d3 Kostas Papadimitriou
          this.sync("update", this, {
2264 45b964d3 Kostas Papadimitriou
            critical: true,
2265 45b964d3 Kostas Papadimitriou
            data: {'name': new_name}, 
2266 45b964d3 Kostas Papadimitriou
            success: _.bind(function(){
2267 45b964d3 Kostas Papadimitriou
              snf.api.trigger("call");
2268 45b964d3 Kostas Papadimitriou
            }, this)
2269 45b964d3 Kostas Papadimitriou
          });
2270 3c446f85 Kostas Papadimitriou
        },
2271 3c446f85 Kostas Papadimitriou
2272 3c446f85 Kostas Papadimitriou
        do_remove: function() {
2273 3c446f85 Kostas Papadimitriou
          this.actions.reset_pending();
2274 3c446f85 Kostas Papadimitriou
          this.remove(function() {
2275 3c446f85 Kostas Papadimitriou
            synnefo.storage.keys.fetch();
2276 3c446f85 Kostas Papadimitriou
          });
2277 45b964d3 Kostas Papadimitriou
        }
2278 c4fff449 Kostas Papadimitriou
    })
2279 c4fff449 Kostas Papadimitriou
    
2280 95ff92b4 Kostas Papadimitriou
    models._ActionsModel = models.Model.extend({
2281 95ff92b4 Kostas Papadimitriou
      defaults: { pending: null },
2282 95ff92b4 Kostas Papadimitriou
      actions: [],
2283 95ff92b4 Kostas Papadimitriou
      status: {
2284 95ff92b4 Kostas Papadimitriou
        INACTIVE: 0,
2285 95ff92b4 Kostas Papadimitriou
        PENDING: 1,
2286 95ff92b4 Kostas Papadimitriou
        CALLED: 2
2287 95ff92b4 Kostas Papadimitriou
      },
2288 95ff92b4 Kostas Papadimitriou
2289 95ff92b4 Kostas Papadimitriou
      initialize: function(attrs, opts) {
2290 95ff92b4 Kostas Papadimitriou
        models._ActionsModel.__super__.initialize.call(this, attrs);
2291 95ff92b4 Kostas Papadimitriou
        this.actions = opts.actions;
2292 95ff92b4 Kostas Papadimitriou
        this.model = opts.model;
2293 95ff92b4 Kostas Papadimitriou
        this.bind("change", function() {
2294 95ff92b4 Kostas Papadimitriou
          this.set({'pending': this.get_pending()});
2295 95ff92b4 Kostas Papadimitriou
        }, this);
2296 95ff92b4 Kostas Papadimitriou
        this.clear();
2297 95ff92b4 Kostas Papadimitriou
      },
2298 95ff92b4 Kostas Papadimitriou
      
2299 95ff92b4 Kostas Papadimitriou
      _in_status: function(st) {
2300 95ff92b4 Kostas Papadimitriou
        var actions = null;
2301 95ff92b4 Kostas Papadimitriou
        _.each(this.attributes, function(status, action){
2302 95ff92b4 Kostas Papadimitriou
          if (status == st) {
2303 95ff92b4 Kostas Papadimitriou
            if (!actions) {
2304 95ff92b4 Kostas Papadimitriou
              actions = []
2305 95ff92b4 Kostas Papadimitriou
            }
2306 95ff92b4 Kostas Papadimitriou
            actions.push(action);
2307 95ff92b4 Kostas Papadimitriou
          }
2308 95ff92b4 Kostas Papadimitriou
        });
2309 95ff92b4 Kostas Papadimitriou
        return actions;
2310 95ff92b4 Kostas Papadimitriou
      },
2311 95ff92b4 Kostas Papadimitriou
2312 95ff92b4 Kostas Papadimitriou
      get_pending: function() {
2313 95ff92b4 Kostas Papadimitriou
        return this._in_status(this.status.PENDING);
2314 95ff92b4 Kostas Papadimitriou
      },
2315 95ff92b4 Kostas Papadimitriou
2316 95ff92b4 Kostas Papadimitriou
      unset_pending_action: function(action) {
2317 95ff92b4 Kostas Papadimitriou
        var data = {};
2318 95ff92b4 Kostas Papadimitriou
        data[action] = this.status.INACTIVE;
2319 95ff92b4 Kostas Papadimitriou
        this.set(data);
2320 3c446f85 Kostas Papadimitriou
        this.trigger("unset-pending", action);
2321 95ff92b4 Kostas Papadimitriou
      },
2322 95ff92b4 Kostas Papadimitriou
2323 95ff92b4 Kostas Papadimitriou
      set_pending_action: function(action, reset_pending) {
2324 95ff92b4 Kostas Papadimitriou
        reset_pending = reset_pending === undefined ? true : reset_pending;
2325 95ff92b4 Kostas Papadimitriou
        var data = {};
2326 95ff92b4 Kostas Papadimitriou
        data[action] = this.status.PENDING;
2327 95ff92b4 Kostas Papadimitriou
        if (reset_pending) {
2328 95ff92b4 Kostas Papadimitriou
          this.reset_pending();
2329 95ff92b4 Kostas Papadimitriou
        }
2330 95ff92b4 Kostas Papadimitriou
        this.set(data);
2331 bfb11987 Kostas Papadimitriou
        this.trigger("set-pending", action);
2332 95ff92b4 Kostas Papadimitriou
      },
2333 95ff92b4 Kostas Papadimitriou
      
2334 95ff92b4 Kostas Papadimitriou
      reset_pending: function() {
2335 95ff92b4 Kostas Papadimitriou
        var data = {};
2336 95ff92b4 Kostas Papadimitriou
        _.each(this.actions, function(action) {
2337 95ff92b4 Kostas Papadimitriou
          data[action] = this.status.INACTIVE;
2338 95ff92b4 Kostas Papadimitriou
        }, this);
2339 95ff92b4 Kostas Papadimitriou
        this.set(data);
2340 bfb11987 Kostas Papadimitriou
        this.trigger("reset-pending");
2341 95ff92b4 Kostas Papadimitriou
      }
2342 95ff92b4 Kostas Papadimitriou
    });
2343 95ff92b4 Kostas Papadimitriou
2344 eb17e27a Kostas Papadimitriou
    models.PublicPool = models.Model.extend({});
2345 eb17e27a Kostas Papadimitriou
    models.PublicPools = models.Collection.extend({
2346 eb17e27a Kostas Papadimitriou
      model: models.PublicPool,
2347 eb17e27a Kostas Papadimitriou
      path: 'os-floating-ip-pools',
2348 eb17e27a Kostas Papadimitriou
      api_type: 'compute',
2349 eb17e27a Kostas Papadimitriou
      noUpdate: true,
2350 eb17e27a Kostas Papadimitriou
2351 eb17e27a Kostas Papadimitriou
      parse: function(data) {
2352 eb17e27a Kostas Papadimitriou
        return _.map(data.floating_ip_pools, function(pool) {
2353 eb17e27a Kostas Papadimitriou
          pool.id = pool.name;
2354 eb17e27a Kostas Papadimitriou
          return pool;
2355 eb17e27a Kostas Papadimitriou
        });
2356 eb17e27a Kostas Papadimitriou
      }
2357 eb17e27a Kostas Papadimitriou
    });
2358 eb17e27a Kostas Papadimitriou
2359 c4fff449 Kostas Papadimitriou
    models.PublicKeys = models.Collection.extend({
2360 859a4609 Kostas Papadimitriou
        model: models.PublicKey,
2361 859a4609 Kostas Papadimitriou
        details: false,
2362 859a4609 Kostas Papadimitriou
        path: 'keys',
2363 7f16b9b1 Kostas Papadimitriou
        api_type: 'userdata',
2364 859a4609 Kostas Papadimitriou
        noUpdate: true,
2365 45b964d3 Kostas Papadimitriou
        updateEntries: true,
2366 859a4609 Kostas Papadimitriou
2367 556dcd88 Kostas Papadimitriou
        generate_new: function(success, error) {
2368 386481eb Kostas Papadimitriou
            snf.api.sync('create', undefined, {
2369 556dcd88 Kostas Papadimitriou
                url: getUrl.call(this, this.base_url) + "/generate", 
2370 556dcd88 Kostas Papadimitriou
                success: success, 
2371 556dcd88 Kostas Papadimitriou
                error: error,
2372 556dcd88 Kostas Papadimitriou
                skip_api_error: true
2373 556dcd88 Kostas Papadimitriou
            });
2374 859a4609 Kostas Papadimitriou
        },
2375 d7d154c9 Kostas Papadimitriou
        
2376 859a4609 Kostas Papadimitriou
        add_crypto_key: function(key, success, error, options) {
2377 859a4609 Kostas Papadimitriou
            var options = options || {};
2378 859a4609 Kostas Papadimitriou
            var m = new models.PublicKey();
2379 556dcd88 Kostas Papadimitriou
2380 556dcd88 Kostas Papadimitriou
            // guess a name
2381 1efe6159 Kostas Papadimitriou
            var name_tpl = "my generated public key";
2382 859a4609 Kostas Papadimitriou
            var name = name_tpl;
2383 859a4609 Kostas Papadimitriou
            var name_count = 1;
2384 859a4609 Kostas Papadimitriou
            
2385 859a4609 Kostas Papadimitriou
            while(this.filter(function(m){ return m.get("name") == name }).length > 0) {
2386 859a4609 Kostas Papadimitriou
                name = name_tpl + " " + name_count;
2387 859a4609 Kostas Papadimitriou
                name_count++;
2388 859a4609 Kostas Papadimitriou
            }
2389 556dcd88 Kostas Papadimitriou
            
2390 859a4609 Kostas Papadimitriou
            m.set({name: name});
2391 556dcd88 Kostas Papadimitriou
            m.set({content: key});
2392 859a4609 Kostas Papadimitriou
            
2393 859a4609 Kostas Papadimitriou
            options.success = function () { return success(m) };
2394 859a4609 Kostas Papadimitriou
            options.errror = error;
2395 c72a830d Kostas Papadimitriou
            options.skip_api_error = true;
2396 859a4609 Kostas Papadimitriou
            
2397 859a4609 Kostas Papadimitriou
            this.create(m.attributes, options);
2398 859a4609 Kostas Papadimitriou
        }
2399 426e1fb9 Kostas Papadimitriou
    });
2400 426e1fb9 Kostas Papadimitriou
2401 426e1fb9 Kostas Papadimitriou
  
2402 426e1fb9 Kostas Papadimitriou
    models.Quota = models.Model.extend({
2403 426e1fb9 Kostas Papadimitriou
2404 426e1fb9 Kostas Papadimitriou
        initialize: function() {
2405 426e1fb9 Kostas Papadimitriou
            models.Quota.__super__.initialize.apply(this, arguments);
2406 426e1fb9 Kostas Papadimitriou
            this.bind("change", this.check, this);
2407 426e1fb9 Kostas Papadimitriou
            this.check();
2408 426e1fb9 Kostas Papadimitriou
        },
2409 426e1fb9 Kostas Papadimitriou
        
2410 426e1fb9 Kostas Papadimitriou
        check: function() {
2411 426e1fb9 Kostas Papadimitriou
            var usage, limit;
2412 426e1fb9 Kostas Papadimitriou
            usage = this.get('usage');
2413 426e1fb9 Kostas Papadimitriou
            limit = this.get('limit');
2414 426e1fb9 Kostas Papadimitriou
            if (usage >= limit) {
2415 426e1fb9 Kostas Papadimitriou
                this.trigger("available");
2416 426e1fb9 Kostas Papadimitriou
            } else {
2417 426e1fb9 Kostas Papadimitriou
                this.trigger("unavailable");
2418 426e1fb9 Kostas Papadimitriou
            }
2419 426e1fb9 Kostas Papadimitriou
        },
2420 426e1fb9 Kostas Papadimitriou
2421 426e1fb9 Kostas Papadimitriou
        increase: function(val) {
2422 426e1fb9 Kostas Papadimitriou
            if (val === undefined) { val = 1};
2423 426e1fb9 Kostas Papadimitriou
            this.set({'usage': this.get('usage') + val})
2424 426e1fb9 Kostas Papadimitriou
        },
2425 426e1fb9 Kostas Papadimitriou
2426 426e1fb9 Kostas Papadimitriou
        decrease: function(val) {
2427 426e1fb9 Kostas Papadimitriou
            if (val === undefined) { val = 1};
2428 426e1fb9 Kostas Papadimitriou
            this.set({'usage': this.get('usage') - val})
2429 426e1fb9 Kostas Papadimitriou
        },
2430 426e1fb9 Kostas Papadimitriou
2431 426e1fb9 Kostas Papadimitriou
        can_consume: function() {
2432 426e1fb9 Kostas Papadimitriou
            var usage, limit;
2433 426e1fb9 Kostas Papadimitriou
            usage = this.get('usage');
2434 426e1fb9 Kostas Papadimitriou
            limit = this.get('limit');
2435 426e1fb9 Kostas Papadimitriou
            if (usage >= limit) {
2436 426e1fb9 Kostas Papadimitriou
                return false
2437 426e1fb9 Kostas Papadimitriou
            } else {
2438 426e1fb9 Kostas Papadimitriou
                return true
2439 426e1fb9 Kostas Papadimitriou
            }
2440 426e1fb9 Kostas Papadimitriou
        },
2441 426e1fb9 Kostas Papadimitriou
        
2442 426e1fb9 Kostas Papadimitriou
        is_bytes: function() {
2443 426e1fb9 Kostas Papadimitriou
            return this.get('resource').get('unit') == 'bytes';
2444 426e1fb9 Kostas Papadimitriou
        },
2445 426e1fb9 Kostas Papadimitriou
        
2446 ab3df8df Kostas Papadimitriou
        get_available: function(active) {
2447 ab3df8df Kostas Papadimitriou
            suffix = '';
2448 ab3df8df Kostas Papadimitriou
            if (active) { suffix = '_active'}
2449 ab3df8df Kostas Papadimitriou
            var value = this.get('limit'+suffix) - this.get('usage'+suffix);
2450 b76769ef Kostas Papadimitriou
            if (active) {
2451 b76769ef Kostas Papadimitriou
              if (this.get('available') <= value) {
2452 b76769ef Kostas Papadimitriou
                value = this.get('available');
2453 b76769ef Kostas Papadimitriou
              }
2454 b76769ef Kostas Papadimitriou
            }
2455 426e1fb9 Kostas Papadimitriou
            if (value < 0) { return value }
2456 426e1fb9 Kostas Papadimitriou
            return value
2457 426e1fb9 Kostas Papadimitriou
        },
2458 426e1fb9 Kostas Papadimitriou
2459 ab3df8df Kostas Papadimitriou
        get_readable: function(key, active) {
2460 426e1fb9 Kostas Papadimitriou
            var value;
2461 426e1fb9 Kostas Papadimitriou
            if (key == 'available') {
2462 ab3df8df Kostas Papadimitriou
                value = this.get_available(active);
2463 426e1fb9 Kostas Papadimitriou
            } else {
2464 426e1fb9 Kostas Papadimitriou
                value = this.get(key)
2465 426e1fb9 Kostas Papadimitriou
            }
2466 b76769ef Kostas Papadimitriou
            if (value <= 0) { value = 0 }
2467 80bb2140 Kostas Papadimitriou
            // greater than max js int (assume infinite quota)
2468 80bb2140 Kostas Papadimitriou
            if (value > Math.pow(2, 53)) { 
2469 80bb2140 Kostas Papadimitriou
              return "Infinite"
2470 80bb2140 Kostas Papadimitriou
            }
2471 417bd7d4 Kostas Papadimitriou
2472 417bd7d4 Kostas Papadimitriou
            if (!this.is_bytes()) {
2473 417bd7d4 Kostas Papadimitriou
              return value + "";
2474 417bd7d4 Kostas Papadimitriou
            }
2475 417bd7d4 Kostas Papadimitriou
            
2476 426e1fb9 Kostas Papadimitriou
            return snf.util.readablizeBytes(value);
2477 426e1fb9 Kostas Papadimitriou
        }
2478 426e1fb9 Kostas Papadimitriou
    });
2479 426e1fb9 Kostas Papadimitriou
2480 426e1fb9 Kostas Papadimitriou
    models.Quotas = models.Collection.extend({
2481 426e1fb9 Kostas Papadimitriou
        model: models.Quota,
2482 426e1fb9 Kostas Papadimitriou
        api_type: 'accounts',
2483 426e1fb9 Kostas Papadimitriou
        path: 'quotas',
2484 426e1fb9 Kostas Papadimitriou
        parse: function(resp) {
2485 ab3df8df Kostas Papadimitriou
            filtered = _.map(resp.system, function(value, key) {
2486 426e1fb9 Kostas Papadimitriou
                var available = (value.limit - value.usage) || 0;
2487 ab3df8df Kostas Papadimitriou
                var available_active = available;
2488 ab3df8df Kostas Papadimitriou
                var keysplit = key.split(".");
2489 ab3df8df Kostas Papadimitriou
                var limit_active = value.limit;
2490 ab3df8df Kostas Papadimitriou
                var usage_active = value.usage;
2491 8e13afa9 Kostas Papadimitriou
                keysplit[keysplit.length-1] = "total_" + keysplit[keysplit.length-1];
2492 ab3df8df Kostas Papadimitriou
                var activekey = keysplit.join(".");
2493 ab3df8df Kostas Papadimitriou
                var exists = resp.system[activekey];
2494 ab3df8df Kostas Papadimitriou
                if (exists) {
2495 ab3df8df Kostas Papadimitriou
                    available_active = exists.limit - exists.usage;
2496 ab3df8df Kostas Papadimitriou
                    limit_active = exists.limit;
2497 ab3df8df Kostas Papadimitriou
                    usage_active = exists.usage;
2498 ab3df8df Kostas Papadimitriou
                }
2499 426e1fb9 Kostas Papadimitriou
                return _.extend(value, {'name': key, 'id': key, 
2500 426e1fb9 Kostas Papadimitriou
                          'available': available,
2501 ab3df8df Kostas Papadimitriou
                          'available_active': available_active,
2502 ab3df8df Kostas Papadimitriou
                          'limit_active': limit_active,
2503 ab3df8df Kostas Papadimitriou
                          'usage_active': usage_active,
2504 426e1fb9 Kostas Papadimitriou
                          'resource': snf.storage.resources.get(key)});
2505 ab3df8df Kostas Papadimitriou
            });
2506 ab3df8df Kostas Papadimitriou
            return filtered;
2507 ab3df8df Kostas Papadimitriou
        },
2508 ab3df8df Kostas Papadimitriou
        
2509 ab3df8df Kostas Papadimitriou
        get_by_id: function(k) {
2510 ab3df8df Kostas Papadimitriou
          return this.filter(function(q) { return q.get('name') == k})[0]
2511 ab3df8df Kostas Papadimitriou
        },
2512 ab3df8df Kostas Papadimitriou
2513 b76769ef Kostas Papadimitriou
        get_available_for_vm: function(options) {
2514 ab3df8df Kostas Papadimitriou
          var quotas = synnefo.storage.quotas;
2515 ab3df8df Kostas Papadimitriou
          var key = 'available';
2516 b76769ef Kostas Papadimitriou
          var available_quota = {};
2517 b76769ef Kostas Papadimitriou
          _.each(['cyclades.ram', 'cyclades.cpu', 'cyclades.disk'], 
2518 b76769ef Kostas Papadimitriou
            function (key) {
2519 b76769ef Kostas Papadimitriou
              var value = quotas.get(key).get_available(true);
2520 b76769ef Kostas Papadimitriou
              available_quota[key.replace('cyclades.', '')] = value;
2521 b76769ef Kostas Papadimitriou
          });
2522 b76769ef Kostas Papadimitriou
          return available_quota;
2523 426e1fb9 Kostas Papadimitriou
        }
2524 c4fff449 Kostas Papadimitriou
    })
2525 426e1fb9 Kostas Papadimitriou
2526 426e1fb9 Kostas Papadimitriou
    models.Resource = models.Model.extend({
2527 426e1fb9 Kostas Papadimitriou
        api_type: 'accounts',
2528 426e1fb9 Kostas Papadimitriou
        path: 'resources'
2529 426e1fb9 Kostas Papadimitriou
    });
2530 426e1fb9 Kostas Papadimitriou
2531 426e1fb9 Kostas Papadimitriou
    models.Resources = models.Collection.extend({
2532 426e1fb9 Kostas Papadimitriou
        api_type: 'accounts',
2533 426e1fb9 Kostas Papadimitriou
        path: 'resources',
2534 426e1fb9 Kostas Papadimitriou
        model: models.Network,
2535 426e1fb9 Kostas Papadimitriou
2536 426e1fb9 Kostas Papadimitriou
        parse: function(resp) {
2537 426e1fb9 Kostas Papadimitriou
            return _.map(resp, function(value, key) {
2538 426e1fb9 Kostas Papadimitriou
                return _.extend(value, {'name': key, 'id': key});
2539 426e1fb9 Kostas Papadimitriou
            })
2540 426e1fb9 Kostas Papadimitriou
        }
2541 426e1fb9 Kostas Papadimitriou
    });
2542 8d08f18a Kostas Papadimitriou
    
2543 8d08f18a Kostas Papadimitriou
    // storage initialization
2544 8d08f18a Kostas Papadimitriou
    snf.storage.images = new models.Images();
2545 8d08f18a Kostas Papadimitriou
    snf.storage.flavors = new models.Flavors();
2546 8d08f18a Kostas Papadimitriou
    snf.storage.vms = new models.VMS();
2547 c4fff449 Kostas Papadimitriou
    snf.storage.keys = new models.PublicKeys();
2548 426e1fb9 Kostas Papadimitriou
    snf.storage.resources = new models.Resources();
2549 426e1fb9 Kostas Papadimitriou
    snf.storage.quotas = new models.Quotas();
2550 eb17e27a Kostas Papadimitriou
    snf.storage.public_pools = new models.PublicPools();
2551 8d08f18a Kostas Papadimitriou
2552 8d08f18a Kostas Papadimitriou
})(this);