Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (15.9 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

    
170
        this.ports.bind("remove", function() {
171
          this.pending_disconnects--;
172
          this.update_connecting_status();
173
          this.update_actions();
174
        }, this);
175
        this.set({ports: this.ports});
176

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

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

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

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

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

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

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

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

    
262
    })
263

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

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

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

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

    
317
    models.Port = models.NetworkModel.extend({
318
      path: 'ports',
319
      parse: function(obj) {
320
        return obj.port;
321
      },
322
      initialize: function() {
323
        models.Port.__super__.initialize.apply(this, arguments);
324
        var ips = new models.FixedIPs();
325
        this.set({'ips': ips});
326
        this.bind('change:fixed_ips', function() {
327
          var ips = this.get('ips');
328
          //var ips = _.map(ips, function(ip) { ip.id = ip.a})
329
          this.update_ips()
330
        }, this);
331
        this.update_ips();
332
        this.set({'pending_firewall': null});
333
      },
334
      
335
      update_ips: function() {
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
          delete ip.subnet;
346
          return ip;
347
        });
348
        this.get('ips').update(ips, {removeMissing: true});
349
      },
350

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

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

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

    
403
      set_firewall: function(value, callback, error, options) {
404
        // MOCK CALL
405
        window.setTimeout(_.bind(function() {
406
          var vm = this.get('vm');
407
          var attachments = [];
408
          attachments.push({id: this.id, firewallProfile: value});
409
          vm.set({attachments: attachments});
410
        }, this),  2000);
411
        window.setTimeout(_.bind(function() {
412
          callback();
413
        }), 300);
414
      },
415

    
416
      disconnect: function(cb) {
417
        var network = this.get('network');
418
        network.pending_disconnects++;
419
        network.update_connecting_status();
420
        var complete = _.bind(function() {
421
          this.set({'status': 'DISCONNECTING'});
422
          cb();
423
        }, this);
424
        this.destroy({complete: complete, silent: true});
425
      }
426
    });
427

    
428
    models.Ports = models.NetworkCollection.extend({
429
      model: models.Port,
430
      path: 'ports',
431
      details: true,
432
      noUpdate: true,
433
      updateEntries: true,
434

    
435
      parse: function(data) {
436
        return data.ports;
437
      },
438

    
439
      comparator: function(m) {
440
        try {
441
          return parseInt(m.get('device_id'));
442
        } catch (err) {
443
          return 0
444
        }
445
      }
446
    });
447

    
448
    models.FloatingIP = models.NetworkModel.extend({
449
      path: 'floatingips',
450

    
451
      parse: function(obj) {
452
        return obj.floatingip;
453
      },
454

    
455
      storage_attrs: {
456
        'port_id': ['ports', 'port'],
457
        'floating_network_id': ['networks', 'network'],
458
      },
459

    
460
      model_actions: {
461
        'remove': [['status'], function() {
462
          var status_ok = _.contains(['DISCONNECTED'], this.get('status'))
463
          return status_ok
464
        }],
465
        'connect': [['status'], function() {
466
          var status_ok = _.contains(['DISCONNECTED'], this.get('status'))
467
          return status_ok
468
        }],
469
        'disconnect': [['status'], function() {
470
          var status_ok = _.contains(['ACTIVE', 'CONNECTED'], this.get('status'))
471
          return status_ok
472
        }]
473
      },
474

    
475
      proxy_attrs: {
476
        'ext_status': [
477
          ['status'], function() {
478
           if (_.contains(["DISCONNECTING"], this.get("ext_status"))) {
479
            return this.get("ext_status")
480
           }
481
           return this.get("status")
482
        }],
483
        'ip': [
484
          ['floating_ip_adress'], function() {
485
            return this.get('floating_ip_address'); 
486
        }],
487

    
488
        'in_progress': [
489
          ['status'], function() {
490
            return _.contains(['CONNECTING', 'DISCONNECTING', 'REMOVING'], 
491
                              this.get('status'))
492
          }  
493
        ],
494

    
495
        'status': [
496
          ['port_id', 'port'], function() {
497
            var val = this.get('port_id');
498
            if (!val) {
499
                return 'DISCONNECTED'
500
            } else {
501
              if (this.get('port')) {
502
                return 'CONNECTED'
503
              } else {
504
                return 'CONNECTING'
505
              }
506
            }
507
          }
508
        ]
509
      },
510
      
511
      disconnect: function(cb) {
512
        this.get('port').disconnect(cb);
513
      }
514
    });
515

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

    
525
    models.Router = models.NetworkModel.extend({
526
    });
527

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

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

    
542
})(this);