Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (16 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
      add_on_create: true
34
    });
35
  
36
    // Subnet model
37
    models.Subnet = models.NetworkModel.extend();
38
    
39
    // Subnet collection
40
    models.Subnets = models.NetworkCollection.extend({
41
      model: models.Subnet,
42
      details: false,
43
      path: 'subnets',
44
      parse: function(resp) {
45
        return resp.subnets
46
      }
47
    });
48
    
49
    // Network 
50
    models.Network = models.NetworkModel.extend({
51
      path: 'networks',
52

    
53
      parse: function(obj) {
54
        return obj.network;
55
      },
56

    
57
      // Available network actions.
58
      // connect: 
59
      model_actions: {
60
        'connect': [['status', 'is_public'], function() {
61
          //TODO: Also check network status
62
          return !this.is_public() && _.contains(['ACTIVE'], this.get('status'));
63
        }],
64
        'remove': [['status', 'is_public', 'ports'], function() {
65
          if (this.ports && this.ports.length) {
66
            return false
67
          }
68
          return !this.is_public() && _.contains(['ACTIVE'], this.get('status'));
69
        }]
70
      },
71

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

    
123
      // call rename api
124
      rename: function(new_name, cb) {
125
          this.sync("update", this, {
126
              critical: true,
127
              data: {
128
                  'network': {
129
                      'name': new_name
130
                  }
131
              }, 
132
              // do the rename after the method succeeds
133
              success: _.bind(function(){
134
                  //this.set({name: new_name});
135
                  snf.api.trigger("call");
136
              }, this),
137
              complete: cb || function() {}
138
          });
139
      },
140

    
141
      pending_connections: 0,
142
      pending_disconnects: 0,
143

    
144
      initialize: function() {
145
        var self = this;
146
        this.subnets = new Backbone.FilteredCollection(undefined, {
147
          collection: synnefo.storage.subnets,
148
          collectionFilter: function(m) {
149
            return self.id == m.get('network_id')
150
        }});
151
        this.ports = new Backbone.FilteredCollection(undefined, {
152
          collection: synnefo.storage.ports,
153
          collectionFilter: function(m) {
154
            return self.id == m.get('network_id')
155
          }
156
        });
157
        this.ports.network = this;
158
        this.ports.bind("reset", function() {
159
          this.pending_connections = 0;
160
          this.pending_disconnects = 0;
161
          this.update_connecting_status();
162
          this.update_actions();
163
        }, this);
164
        this.ports.bind("add", function() {
165
          this.pending_connections--;
166
          this.update_connecting_status();
167
          this.update_actions();
168
        }, this);
169
        this.ports.bind("remove", function() {
170
          this.pending_disconnects--;
171
          this.update_connecting_status();
172
          this.update_actions();
173
        }, this);
174
        this.set({ports: this.ports});
175

    
176
        this.connectable_vms = new Backbone.FilteredCollection(undefined, {
177
          collection: synnefo.storage.vms,
178
          collectionFilter: function(m) {
179
            return m.can_connect();
180
          }
181
        });
182
        models.Network.__super__.initialize.apply(this, arguments);
183
        this.update_actions();
184
      },
185
      
186
      update_actions: function() {
187
        if (this.ports.length) {
188
          this.set({can_remove: false})
189
        } else {
190
          this.set({can_remove: true})
191
        }
192
      },
193

    
194
      update_connecting_status: function() {
195
        if (this.pending_connections <= 0) {
196
          this.pending_connections = 0;
197
        }
198
        if (this.pending_disconnects <= 0) {
199
          this.pending_disconnects = 0;
200
        }
201
        this.trigger('change:status', this.get('status'));
202
      },
203

    
204
      get_nics: function() {
205
        return this.nics.models
206
      },
207

    
208
      is_public: function() {
209
        return this.get('router:external')
210
      },
211

    
212
      connect_vm: function(vm, cb) {
213
        var self = this;
214
        var data = {
215
          port: {
216
            network_id: this.id,
217
            device_id: vm.id
218
          }
219
        }
220

    
221
        this.pending_connections++;
222
        this.update_connecting_status();
223
        synnefo.storage.ports.create(data, {complete: cb});
224
      }
225
    });
226

    
227
    models.CombinedPublicNetwork = models.Network.extend({
228
      defaults: {
229
        'admin_state_up': true,
230
        'id': 'snf-combined-public-network',
231
        'name': 'Internet',
232
        'status': 'ACTIVE',
233
        'router:external': true,
234
        'shared': false,
235
        'rename_disabled': true,
236
        'subnets': []
237
      },
238
      
239
      initialize: function() {
240
        var self = this;
241
        this.ports = new Backbone.FilteredCollection(undefined, {
242
          collection: synnefo.storage.ports,
243
          collectionFilter: function(m) {
244
            return m.get('network') && m.get('network').get('is_public');
245
          }
246
        });
247
        this.set({ports: this.ports});
248
        this.floating_ips = synnefo.storage.floating_ips;
249
        this.set({floating_ips: this.floating_ips});
250

    
251
        this.available_floating_ips = new Backbone.FilteredCollection(undefined, {
252
          collection: synnefo.storage.floating_ips,
253
          collectionFilter: function(m) {
254
            return !m.get('port_id');
255
          }
256
        });
257
        this.set({available_floating_ips: this.available_floating_ips});
258
        models.Network.__super__.initialize.apply(this, arguments);
259
      },
260

    
261
    })
262

    
263
    models.Networks = models.NetworkCollection.extend({
264
      model: models.Network,
265
      path: 'networks',
266
      details: true,
267
      parse: function(resp) {
268
        return resp.networks
269
      },
270

    
271
      get_floating_ips_network: function() {
272
        return this.filter(function(n) { return n.get('is_public')})[1]
273
      },
274

    
275
      create: function (name, type, cidr, dhcp, callback) {
276
        var quota = synnefo.storage.quotas;
277
        var params = {network:{name:name}};
278
        var subnet_params = {subnet:{network_id:undefined}};
279
        if (!type) { throw "Network type cannot be empty"; }
280

    
281
        params.network.type = type;
282
        if (cidr) { subnet_params.subnet.cidr = cidr; }
283
        if (dhcp) { subnet_params.subnet.dhcp_enabled = dhcp; }
284
        if (dhcp === false) { subnet_params.subnet.dhcp_enabled = false; }
285
        
286
        var cb = function() {
287
          callback();
288
        }
289
        
290
        var complete = function() {};
291
        var error = function() { cb() };
292
        // on network create success, try to create the requested 
293
        // network subnet
294
        var success = function(resp) {
295
          var network = resp.network;
296
          subnet_params.subnet.network_id = network.id;
297
          synnefo.storage.subnets.create(subnet_params, {
298
            complete: function () { cb && cb() }
299
          });
300
          quota.get('cyclades.network.private').increase();
301
        }
302
        return this.api_call(this.path, "create", params, complete, error, success);
303
      }
304
    });
305
    
306
    // dummy model/collection
307
    models.FixedIP = models.NetworkModel.extend({
308
      storage_attrs: {
309
        'subnet_id': ['subnets', 'subnet']
310
      }
311
    });
312
    models.FixedIPs = models.NetworkCollection.extend({
313
      model: models.FixedIP
314
    });
315

    
316
    models.Port = models.NetworkModel.extend({
317
      path: 'ports',
318
      parse: function(obj) {
319
        return obj.port;
320
      },
321
      initialize: function() {
322
        models.Port.__super__.initialize.apply(this, arguments);
323
        var ips = new models.FixedIPs();
324
        this.set({'ips': ips});
325
        this.bind('change:fixed_ips', function() {
326
          var ips = this.get('ips');
327
          //var ips = _.map(ips, function(ip) { ip.id = ip.a})
328
          this.update_ips()
329
        }, this);
330
        this.update_ips();
331
        this.set({'pending_firewall': null});
332
      },
333
      
334
      update_ips: function() {
335
        var self = this;
336
        var ips = _.map(this.get('fixed_ips'), function(ip_obj) {
337
          var ip = _.clone(ip_obj);
338
          var type = "v4";
339
          if (ip.ip_address.indexOf(":") >= 0) {
340
            type = "v6";
341
          }
342
          ip.id = ip.ip_address;
343
          ip.type = type;
344
          ip.subnet_id = ip.subnet;
345
          ip.port_id = self.id;
346
          delete ip.subnet;
347
          return ip;
348
        });
349
        this.get('ips').update(ips, {removeMissing: true});
350
      },
351

    
352
      model_actions: {
353
        'disconnect': [['status', 'network', 'vm'], function() {
354
          var network = this.get('network');
355
          if ((!network || network.get('is_public')) && (network && !network.get('is_floating'))) {
356
            return false
357
          }
358
          var status_ok = _.contains(['DOWN', 'ACTIVE', 'CONNECTED'], 
359
                                     this.get('status'));
360
          var vm_status_ok = this.get('vm') && this.get('vm').can_connect();
361
          return status_ok && vm_status_ok
362
        }]
363
      },
364

    
365
      storage_attrs: {
366
        'device_id': ['vms', 'vm'],
367
        'network_id': ['networks', 'network']
368
      },
369

    
370
      proxy_attrs: {
371
        'firewall_status': [
372
          ['vm'], function(vm) {
373
            var attachment = vm && vm.get_attachment(this.id);
374
            if (!attachment) { return "DISABLED" }
375
            return attachment.firewallProfile
376
          } 
377
        ],
378
        'ext_status': [
379
          ['status'], function() {
380
            if (_.contains(["DISCONNECTING"], this.get('ext_status'))) {
381
              return this.get("ext_status")
382
            }
383
            return this.get("status")
384
          }
385
        ],
386
        'in_progress': [
387
          ['ext_status'], function() {
388
            return _.contains(["DISCONNECTING", "CONNECTING"], this.get("ext_status"))
389
          }
390
        ],
391
        'firewall_running': [
392
          ['firewall_status', 'pending_firewall'], function(status, pending) {
393
              var pending = this.get('pending_firewall');
394
              var status = this.get('firewall_status');
395
              if (!pending) { return false }
396
              if (status == pending) {
397
                this.set({'pending_firewall': null});
398
              }
399
              return status != pending;
400
          }
401
        ],
402
      },
403

    
404
      disconnect: function(cb) {
405
        var network = this.get('network');
406
        var vm = this.get('vm');
407
        network.pending_disconnects++;
408
        network.update_connecting_status();
409
        var success = _.bind(function() {
410
          if (vm) {
411
            vm.set({'status': 'DISCONNECTING'});
412
          }
413
          this.set({'status': 'DISCONNECTING'});
414
          cb();
415
        }, this);
416
        this.destroy({success: success, complete: cb, silent: true});
417
      }
418
    });
419

    
420
    models.Ports = models.NetworkCollection.extend({
421
      model: models.Port,
422
      path: 'ports',
423
      details: true,
424
      noUpdate: true,
425
      updateEntries: true,
426

    
427
      parse: function(data) {
428
        return data.ports;
429
      },
430

    
431
      comparator: function(m) {
432
        try {
433
          return parseInt(m.get('device_id'));
434
        } catch (err) {
435
          return 0
436
        }
437
      }
438
    });
439

    
440
    models.FloatingIP = models.NetworkModel.extend({
441
      path: 'floatingips',
442

    
443
      parse: function(obj) {
444
        return obj.floatingip;
445
      },
446

    
447
      storage_attrs: {
448
        'port_id': ['ports', 'port'],
449
        'floating_network_id': ['networks', 'network'],
450
      },
451

    
452
      model_actions: {
453
        'remove': [['status'], function() {
454
          var status_ok = _.contains(['DISCONNECTED'], this.get('status'))
455
          return status_ok
456
        }],
457
        'connect': [['status'], function() {
458
          var status_ok = _.contains(['DISCONNECTED'], this.get('status'))
459
          return status_ok
460
        }],
461
        'disconnect': [['status', 'port_id', 'port'], function() {
462
          var port = this.get('port');
463
          var vm = port && port.get('vm');
464
          if (!vm) { return false }
465
          if (vm && vm.get("task_state")) { return false }
466
          if (vm && vm.in_error_state()) { return false }
467
          var status_ok = _.contains(['ACTIVE', 'CONNECTED'], this.get('status'))
468
          var vm_status_ok = vm.can_disconnect();
469
          return status_ok && vm_status_ok;
470
        }]
471
      },
472

    
473
      proxy_attrs: {
474
        'ip': [
475
          ['floating_ip_adress'], function() {
476
            return this.get('floating_ip_address'); 
477
        }],
478

    
479
        'in_progress': [
480
          ['status'], function() {
481
            return _.contains(['CONNECTING', 'DISCONNECTING', 'REMOVING'], 
482
                              this.get('status'))
483
          }  
484
        ],
485

    
486
        'status': [
487
          ['port_id', 'port'], function() {
488
            var port_id = this.get('port_id');
489
            if (!port_id) {
490
              return 'DISCONNECTED'
491
            } else {
492
              var port = this.get('port');
493
              if (port) {
494
                var port_status = port.get('ext_status');
495
                if (port_status == "DISCONNECTING") {
496
                  return port_status
497
                }
498
                if (port_status == "CONNECTING") {
499
                  return port_status
500
                }
501
                return 'CONNECTED'
502
              }
503
              return 'CONNECTING'  
504
            }
505
          }
506
        ]
507
      },
508
      
509
      disconnect: function(cb) {
510
        this.get('port').disconnect(cb);
511
      }
512
    });
513

    
514
    models.FloatingIPs = models.NetworkCollection.extend({
515
      model: models.FloatingIP,
516
      details: false,
517
      path: 'floatingips',
518
      parse: function(resp) {
519
        return resp.floatingips;
520
      }
521
    });
522

    
523
    models.Router = models.NetworkModel.extend({
524
    });
525

    
526
    models.Routers = models.NetworkCollection.extend({
527
      model: models.Router,
528
      path: 'routers',
529
      parse: function(resp) {
530
        return resp.routers
531
      }
532
    });
533

    
534
    snf.storage.floating_ips = new models.FloatingIPs();
535
    snf.storage.routers = new models.Routers();
536
    snf.storage.networks = new models.Networks();
537
    snf.storage.ports = new models.Ports();
538
    snf.storage.subnets = new models.Subnets();
539

    
540
})(this);