Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / neutron.js @ a37c5497

History | View | Annotate | Download (15.1 kB)

1
;(function(root){
2
    // Neutron api models, collections, helpers
3
  
4
    // root
5
    var root = root;
6
    
7
    // setup namepsaces
8
    var snf = root.synnefo = root.synnefo || {};
9
    var snfmodels = snf.models = snf.models || {}
10
    var models = snfmodels.networks = snfmodels.networks || {};
11
    var storage = snf.storage = snf.storage || {};
12
    var util = snf.util = snf.util || {};
13

    
14
    // shortcuts
15
    var bb = root.Backbone;
16
    var slice = Array.prototype.slice
17

    
18
    // logging
19
    var logger = new snf.logging.logger("SNF-MODELS");
20
    var debug = _.bind(logger.debug, logger);
21
    
22
    // Neutron base model, extending existing synnefo model
23
    models.NetworkModel = snfmodels.Model.extend({
24
      api_type: 'network'
25
    });
26
    
27
    // Neutron base collection, common neutron collection params are shared
28
    models.NetworkCollection = snfmodels.Collection.extend({
29
      api_type: 'network',
30
      details: true,
31
      noUpdate: true,
32
      updateEntries: true
33
    });
34
  
35
    // Subnet model
36
    models.Subnet = models.NetworkModel.extend();
37
    
38
    // Subnet collection
39
    models.Subnets = models.NetworkCollection.extend({
40
      model: models.Subnet,
41
      details: false,
42
      path: 'subnets',
43
      parse: function(resp) {
44
        return resp.subnets
45
      }
46
    });
47
    
48
    // Network 
49
    models.Network = models.NetworkModel.extend({
50
      path: 'networks',
51

    
52
      // Available network actions.
53
      // connect: 
54
      model_actions: {
55
        'connect': [['status', 'is_public'], function() {
56
          //TODO: Also check network status
57
          return !this.is_public() && _.contains(['ACTIVE'], this.get('status'));
58
        }],
59
        'remove': [['status', 'is_public'], function() {
60
          //TODO: Also check network status
61
          return !this.is_public() && _.contains(['ACTIVE'], this.get('status'));
62
        }]
63
      },
64

    
65
      proxy_attrs: {
66
        'is_public': [
67
          ['router:external', 'public'], function() {
68
            return this.get('router:external') || this.get('public')
69
          } 
70
        ],
71
        'is_floating': [
72
          ['SNF:floating_ip_pool'], function() {
73
            return this.get('SNF:floating_ip_pool')
74
          }
75
        ],
76
        'cidr': [
77
          ['subnet'], function() {
78
            var subnet = this.get('subnet');
79
            if (subnet && subnet.get('cidr')) {
80
              return subnet.get('cidr')
81
            } else {
82
              return undefined
83
            }
84
          }
85
        ],
86
        'ext_status': [
87
          ['status', 'cidr'], function(st) {
88
            if (this.get('ext_status') == 'REMOVING') {
89
              return 'REMOVING'
90
            }
91
            if (this.pending_connections) {
92
              return 'CONNECTING'
93
            } else if (this.pending_disconnects) {
94
              return 'DISCONNECTING'
95
            } else {
96
              return this.get('status')
97
            }
98
        }],
99
        'in_progress': [
100
          ['ext_status'], function() {
101
            return _.contains(['CONNECTING', 
102
                               'DISCONNECTING', 
103
                               'REMOVING'], 
104
                               this.get('ext_status'))
105
          }  
106
        ]
107
      },
108
      
109
      storage_attrs: {
110
        'subnets': ['subnets', 'subnet', function(model, attr) {
111
          var subnets = model.get(attr);
112
          if (subnets && subnets.length) { return subnets[0] }
113
        }]
114
      },
115

    
116
      // call rename api
117
      rename: function(new_name, cb) {
118
          this.sync("update", this, {
119
              critical: true,
120
              data: {
121
                  'network': {
122
                      'name': new_name
123
                  }
124
              }, 
125
              // do the rename after the method succeeds
126
              success: _.bind(function(){
127
                  //this.set({name: new_name});
128
                  snf.api.trigger("call");
129
              }, this),
130
              complete: cb || function() {}
131
          });
132
      },
133

    
134
      pending_connections: 0,
135
      pending_disconnects: 0,
136

    
137
      initialize: function() {
138
        var self = this;
139
        this.subnets = new Backbone.FilteredCollection(undefined, {
140
          collection: synnefo.storage.subnets,
141
          collectionFilter: function(m) {
142
            return self.id == m.get('network_id')
143
        }});
144
        models.Network.__super__.initialize.apply(this, arguments);
145
        this.ports = new Backbone.FilteredCollection(undefined, {
146
          collection: synnefo.storage.ports,
147
          collectionFilter: function(m) {
148
            return self.id == m.get('network_id')
149
          }
150
        });
151
        this.ports.network = this;
152
        this.ports.bind("add", function() {
153
          this.pending_connections--;
154
          this.update_connecting_status();
155
        }, this);
156
        this.ports.bind("remove", function() {
157
          this.pending_disconnects--;
158
          this.update_connecting_status();
159
        }, this);
160
        this.set({ports: this.ports});
161

    
162
        this.connectable_vms = new Backbone.FilteredCollection(undefined, {
163
          collection: synnefo.storage.vms,
164
          collectionFilter: function(m) {
165
            return m.can_connect();
166
          }
167
        });
168
      },
169
      
170
      update_connecting_status: function() {
171
        if (this.pending_connections <= 0) {
172
          this.pending_connections = 0;
173
        }
174
        if (this.pending_disconnects <= 0) {
175
          this.pending_disconnects = 0;
176
        }
177
        this.trigger('change:status', this.get('status'));
178
      },
179

    
180
      get_nics: function() {
181
        return this.nics.models
182
      },
183

    
184
      is_public: function() {
185
        return this.get('router:external')
186
      },
187

    
188
      connect_vm: function(vm, cb) {
189
        var self = this;
190
        var data = {
191
          port: {
192
            network_id: this.id,
193
            device_id: vm.id
194
          }
195
        }
196

    
197
        this.pending_connections++;
198
        this.update_connecting_status();
199
        synnefo.storage.ports.create(data, {complete: cb});
200
      }
201
    });
202

    
203
    models.CombinedPublicNetwork = models.Network.extend({
204
      defaults: {
205
        'admin_state_up': true,
206
        'id': 'snf-combined-public-network',
207
        'name': 'Public',
208
        'status': 'ACTIVE',
209
        'router:external': true,
210
        'shared': false,
211
        'rename_disabled': true,
212
        'subnets': []
213
      },
214
      
215
      initialize: function() {
216
        models.Network.__super__.initialize.apply(this, arguments);
217
        var self = this;
218
        this.ports = new Backbone.FilteredCollection(undefined, {
219
          collection: synnefo.storage.ports,
220
          collectionFilter: function(m) {
221
            return m.get('network') && m.get('network').get('is_public');
222
          }
223
        });
224
        this.set({ports: this.ports});
225
        this.floating_ips = synnefo.storage.floating_ips;
226
        this.set({floating_ips: this.floating_ips});
227

    
228
        this.available_floating_ips = new Backbone.FilteredCollection(undefined, {
229
          collection: synnefo.storage.floating_ips,
230
          collectionFilter: function(m) {
231
            return !m.get('port_id');
232
          }
233
        });
234
        this.set({available_floating_ips: this.available_floating_ips});
235
      },
236

    
237
    })
238

    
239
    models.Networks = models.NetworkCollection.extend({
240
      model: models.Network,
241
      path: 'networks',
242
      details: true,
243
      parse: function(resp) {
244
        return resp.networks
245
      },
246

    
247
      get_floating_ips_network: function() {
248
        return this.filter(function(n) { return n.get('is_public')})[1]
249
      },
250

    
251
      create: function (name, type, cidr, dhcp, callback) {
252
        var quota = synnefo.storage.quotas;
253
        var params = {network:{name:name}};
254
        var subnet_params = {subnet:{network_id:undefined}};
255
        if (!type) { throw "Network type cannot be empty"; }
256

    
257
        params.network.type = type;
258
        if (cidr) { subnet_params.subnet.cidr = cidr; }
259
        if (dhcp) { subnet_params.subnet.dhcp_enabled = dhcp; }
260
        if (dhcp === false) { subnet_params.subnet.dhcp_enabled = false; }
261
        
262
        var cb = function() {
263
          callback();
264
        }
265
        
266
        var complete = function() {};
267
        var error = function() { cb() };
268
        // on network create success, try to create the requested 
269
        // network subnet
270
        var success = function(resp) {
271
          var network = resp.network;
272
          subnet_params.subnet.network_id = network.id;
273
          synnefo.storage.subnets.create(subnet_params, {
274
            complete: function () { cb && cb() }
275
          });
276
          quota.get('cyclades.network.private').increase();
277
        }
278
        return this.api_call(this.path, "create", params, complete, error, success);
279
      }
280
    });
281
    
282
    // dummy model/collection
283
    models.FixedIP = models.NetworkModel.extend({
284
      storage_attrs: {
285
        'subnet_id': ['subnets', 'subnet']
286
      }
287
    });
288
    models.FixedIPs = models.NetworkCollection.extend({
289
      model: models.FixedIP
290
    });
291

    
292
    models.Port = models.NetworkModel.extend({
293
      path: 'ports',
294
      initialize: function() {
295
        models.Port.__super__.initialize.apply(this, arguments);
296
        var ips = new models.FixedIPs();
297
        this.set({'ips': ips});
298
        this.bind('change:fixed_ips', function() {
299
          var ips = this.get('ips');
300
          //var ips = _.map(ips, function(ip) { ip.id = ip.a})
301
          this.update_ips()
302
        }, this);
303
        this.update_ips();
304
        this.set({'pending_firewall': null});
305
      },
306
      
307
      update_ips: function() {
308
        var ips = _.map(this.get('fixed_ips'), function(ip_obj) {
309
          var ip = _.clone(ip_obj);
310
          var type = "v4";
311
          if (ip.ip_address.indexOf(":") >= 0) {
312
            type = "v6";
313
          }
314
          ip.id = ip.ip_address;
315
          ip.type = type;
316
          ip.subnet_id = ip.subnet;
317
          delete ip.subnet;
318
          return ip;
319
        });
320
        this.get('ips').update(ips, {removeMissing: true});
321
      },
322

    
323
      model_actions: {
324
        'disconnect': [['status', 'network', 'vm'], function() {
325
          var network = this.get('network');
326
          if ((!network || network.get('is_public')) && (network && !network.get('is_floating'))) {
327
            return false
328
          }
329
          var status_ok = _.contains(['DOWN', 'ACTIVE', 'CONNECTED'], 
330
                                     this.get('status'));
331
          var vm_status_ok = this.get('vm') && !this.get('vm').get('busy');
332
          return status_ok && vm_status_ok
333
        }]
334
      },
335

    
336
      storage_attrs: {
337
        'device_id': ['vms', 'vm'],
338
        'network_id': ['networks', 'network']
339
      },
340

    
341
      proxy_attrs: {
342
        'firewall_status': [
343
          ['vm'], function(vm) {
344
            var attachment = vm && vm.get_attachment(this.id);
345
            if (!attachment) { return "DISABLED" }
346
            return attachment.firewallProfile
347
          } 
348
        ],
349
        'ext_status': [
350
          ['status'], function() {
351
            if (_.contains(["DISCONNECTING"], this.get('ext_status'))) {
352
              return this.get("ext_status")
353
            }
354
            return this.get("status")
355
          }
356
        ],
357
        'in_progress': [
358
          ['ext_status'], function() {
359
            return _.contains(["DISCONNECTING", "CONNECTING"], this.get("status"))
360
          }
361
        ],
362
        'firewall_running': [
363
          ['firewall_status', 'pending_firewall'], function(status, pending) {
364
              var pending = this.get('pending_firewall');
365
              var status = this.get('firewall_status');
366
              if (!pending) { return false }
367
              if (status == pending) {
368
                this.set({'pending_firewall': null});
369
              }
370
              return status != pending;
371
          }
372
        ],
373
      },
374

    
375
      set_firewall: function(value, callback, error, options) {
376
        // MOCK CALL
377
        window.setTimeout(_.bind(function() {
378
          var vm = this.get('vm');
379
          var attachments = [];
380
          attachments.push({id: this.id, firewallProfile: value});
381
          vm.set({attachments: attachments});
382
        }, this),  2000);
383
        window.setTimeout(_.bind(function() {
384
          callback();
385
        }), 300);
386
      },
387

    
388
      disconnect: function(cb) {
389
        var network = this.get('network');
390
        network.pending_disconnects++;
391
        network.update_connecting_status();
392
        var complete = _.bind(function() {
393
          this.set({'status': 'DISCONNECTING'});
394
          cb();
395
        }, this);
396
        this.destroy({complete: complete, silent: true});
397
      }
398
    });
399

    
400
    models.Ports = models.NetworkCollection.extend({
401
      model: models.Port,
402
      path: 'ports',
403
      details: true,
404
      noUpdate: true,
405
      updateEntries: true,
406

    
407
      parse: function(data) {
408
        return data.ports;
409
      },
410

    
411
      comparator: function(m) {
412
        try {
413
          return parseInt(m.get('device_id'));
414
        } catch (err) {
415
          return 0
416
        }
417
      }
418
    });
419

    
420
    models.FloatingIP = models.NetworkModel.extend({
421
      path: 'floatingips',
422
      storage_attrs: {
423
        'port_id': ['ports', 'port'],
424
        'floating_network_id': ['networks', 'network'],
425
      },
426

    
427
      model_actions: {
428
        'remove': [['status'], function() {
429
          var status_ok = _.contains(['DISCONNECTED'], this.get('status'))
430
          return status_ok
431
        }],
432
        'connect': [['status'], function() {
433
          var status_ok = _.contains(['DISCONNECTED'], this.get('status'))
434
          return status_ok
435
        }],
436
        'disconnect': [['status'], function() {
437
          var status_ok = _.contains(['ACTIVE', 'CONNECTED'], this.get('status'))
438
          return status_ok
439
        }]
440
      },
441

    
442
      proxy_attrs: {
443
        'ext_status': [
444
          ['status'], function() {
445
           if (_.contains(["DISCONNECTING"], this.get("ext_status"))) {
446
            return this.get("ext_status")
447
           }
448
           return this.get("status")
449
        }],
450
        'ip': [
451
          ['floating_ip_adress'], function() {
452
            return this.get('floating_ip_address'); 
453
        }],
454

    
455
        'in_progress': [
456
          ['status'], function() {
457
            return _.contains(['CONNECTING', 'DISCONNECTING', 'REMOVING'], 
458
                              this.get('status'))
459
          }  
460
        ],
461

    
462
        'status': [
463
          ['port_id', 'port'], function() {
464
            var val = this.get('port_id');
465
            if (!val) {
466
                return 'DISCONNECTED'
467
            } else {
468
              if (this.get('port')) {
469
                return 'CONNECTED'
470
              } else {
471
                return 'CONNECTING'
472
              }
473
            }
474
          }
475
        ]
476
      },
477

    
478
      disconnect: function(cb) {
479
        this.get('port').disconnect(cb);
480
      }
481
    });
482

    
483
    models.FloatingIPs = models.NetworkCollection.extend({
484
      model: models.FloatingIP,
485
      details: false,
486
      path: 'floatingips',
487
      parse: function(resp) {
488
        return resp.floatingips;
489
      }
490
    });
491

    
492
    models.Router = models.NetworkModel.extend({
493
    });
494

    
495
    models.Routers = models.NetworkCollection.extend({
496
      model: models.Router,
497
      path: 'routers',
498
      parse: function(resp) {
499
        return resp.routers
500
      }
501
    });
502

    
503
    snf.storage.floating_ips = new models.FloatingIPs();
504
    snf.storage.routers = new models.Routers();
505
    snf.storage.networks = new models.Networks();
506
    snf.storage.ports = new models.Ports();
507
    snf.storage.subnets = new models.Subnets();
508

    
509
})(this);