root / snf-cyclades-app / synnefo / ui / static / snf / js / neutron.js @ 442e88bf
History | View | Annotate | Download (18.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 |
toJSON: function() { |
26 |
var res = {};
|
27 |
_.each(this.attributes, function(attr, key) { |
28 |
if (attr instanceof bb.Collection) { |
29 |
attr = "[Collection object]";
|
30 |
} |
31 |
res[key] = attr; |
32 |
}); |
33 |
return res;
|
34 |
} |
35 |
}); |
36 |
|
37 |
// Neutron base collection, common neutron collection params are shared
|
38 |
models.NetworkCollection = snfmodels.Collection.extend({ |
39 |
api_type: 'network', |
40 |
details: true, |
41 |
noUpdate: true, |
42 |
updateEntries: true, |
43 |
add_on_create: true |
44 |
}); |
45 |
|
46 |
// Subnet model
|
47 |
models.Subnet = models.NetworkModel.extend(); |
48 |
|
49 |
// Subnet collection
|
50 |
models.Subnets = models.NetworkCollection.extend({ |
51 |
model: models.Subnet,
|
52 |
details: false, |
53 |
path: 'subnets', |
54 |
parse: function(resp) { |
55 |
return resp.subnets
|
56 |
} |
57 |
}); |
58 |
|
59 |
// Network
|
60 |
models.Network = models.NetworkModel.extend({ |
61 |
path: 'networks', |
62 |
|
63 |
parse: function(obj) { |
64 |
return obj.network;
|
65 |
}, |
66 |
|
67 |
// Available network actions.
|
68 |
// connect:
|
69 |
model_actions: {
|
70 |
'connect': [['status', 'is_public'], function() { |
71 |
//TODO: Also check network status
|
72 |
return !this.is_public() && _.contains(['ACTIVE'], this.get('status')); |
73 |
}], |
74 |
'remove': [['status', 'is_public', 'ports'], function() { |
75 |
if (this.ports && this.ports.length) { |
76 |
return false |
77 |
} |
78 |
return !this.is_public() && _.contains(['ACTIVE'], this.get('status')); |
79 |
}] |
80 |
}, |
81 |
|
82 |
do_remove: function(succ, err) { |
83 |
this.actions.reset_pending();
|
84 |
this.destroy({
|
85 |
success: _.bind(function() { |
86 |
this.set({status: 'REMOVING'}); |
87 |
this.set({ext_status: 'REMOVING'}); |
88 |
// force status display update
|
89 |
this.set({cidr: 'REMOVING'}); |
90 |
}, this),
|
91 |
silent: true |
92 |
}); |
93 |
}, |
94 |
|
95 |
proxy_attrs: {
|
96 |
'is_public': [
|
97 |
['router:external', 'public'], function() { |
98 |
return this.get('router:external') || this.get('public') |
99 |
} |
100 |
], |
101 |
'is_floating': [
|
102 |
['SNF:floating_ip_pool'], function() { |
103 |
return this.get('SNF:floating_ip_pool') |
104 |
} |
105 |
], |
106 |
'cidr': [
|
107 |
['subnet'], function() { |
108 |
var subnet = this.get('subnet'); |
109 |
if (subnet && subnet.get('cidr')) { |
110 |
return subnet.get('cidr') |
111 |
} else {
|
112 |
return undefined |
113 |
} |
114 |
} |
115 |
], |
116 |
'ext_status': [
|
117 |
['status', 'cidr'], function(st) { |
118 |
if (this.get('ext_status') == 'REMOVING') { |
119 |
return 'REMOVING' |
120 |
} |
121 |
if (this.pending_connections) { |
122 |
return 'CONNECTING' |
123 |
} else if (this.pending_disconnects) { |
124 |
return 'DISCONNECTING' |
125 |
} else {
|
126 |
return this.get('status') |
127 |
} |
128 |
}], |
129 |
'in_progress': [
|
130 |
['ext_status'], function() { |
131 |
return _.contains(['CONNECTING', |
132 |
'DISCONNECTING',
|
133 |
'REMOVING'],
|
134 |
this.get('ext_status')) |
135 |
} |
136 |
] |
137 |
}, |
138 |
|
139 |
storage_attrs: {
|
140 |
'subnets': ['subnets', 'subnet', function(model, attr) { |
141 |
var subnets = model.get(attr);
|
142 |
if (subnets && subnets.length) { return subnets[0] } |
143 |
}] |
144 |
}, |
145 |
|
146 |
// call rename api
|
147 |
rename: function(new_name, cb) { |
148 |
this.sync("update", this, { |
149 |
critical: true, |
150 |
data: {
|
151 |
'network': {
|
152 |
'name': new_name
|
153 |
} |
154 |
}, |
155 |
// do the rename after the method succeeds
|
156 |
success: _.bind(function(){ |
157 |
//this.set({name: new_name});
|
158 |
snf.api.trigger("call");
|
159 |
}, this),
|
160 |
complete: cb || function() {} |
161 |
}); |
162 |
}, |
163 |
|
164 |
pending_connections: 0, |
165 |
pending_disconnects: 0, |
166 |
|
167 |
initialize: function() { |
168 |
var self = this; |
169 |
this.subnets = new Backbone.FilteredCollection(undefined, { |
170 |
collection: synnefo.storage.subnets,
|
171 |
collectionFilter: function(m) { |
172 |
return self.id == m.get('network_id') |
173 |
}}); |
174 |
this.ports = new Backbone.FilteredCollection(undefined, { |
175 |
collection: synnefo.storage.ports,
|
176 |
collectionFilter: function(m) { |
177 |
return self.id == m.get('network_id') |
178 |
} |
179 |
}); |
180 |
this.ports.network = this; |
181 |
this.ports.bind("reset", function() { |
182 |
this.pending_connections = 0; |
183 |
this.pending_disconnects = 0; |
184 |
this.update_connecting_status();
|
185 |
this.update_actions();
|
186 |
}, this);
|
187 |
this.ports.bind("add", function() { |
188 |
this.pending_connections--;
|
189 |
this.update_connecting_status();
|
190 |
this.update_actions();
|
191 |
}, this);
|
192 |
this.ports.bind("remove", function() { |
193 |
this.pending_disconnects--;
|
194 |
this.update_connecting_status();
|
195 |
this.update_actions();
|
196 |
}, this);
|
197 |
this.set({ports: this.ports}); |
198 |
|
199 |
this.connectable_vms = new Backbone.FilteredCollection(undefined, { |
200 |
collection: synnefo.storage.vms,
|
201 |
collectionFilter: function(m) { |
202 |
return m.can_connect();
|
203 |
} |
204 |
}); |
205 |
models.Network.__super__.initialize.apply(this, arguments); |
206 |
this.update_actions();
|
207 |
}, |
208 |
|
209 |
update_actions: function() { |
210 |
if (this.ports.length) { |
211 |
this.set({can_remove: false}) |
212 |
} else {
|
213 |
this.set({can_remove: true}) |
214 |
} |
215 |
}, |
216 |
|
217 |
update_connecting_status: function() { |
218 |
if (this.pending_connections <= 0) { |
219 |
this.pending_connections = 0; |
220 |
} |
221 |
if (this.pending_disconnects <= 0) { |
222 |
this.pending_disconnects = 0; |
223 |
} |
224 |
this.trigger('change:status', this.get('status')); |
225 |
}, |
226 |
|
227 |
get_nics: function() { |
228 |
return this.nics.models |
229 |
}, |
230 |
|
231 |
is_public: function() { |
232 |
return this.get('router:external') |
233 |
}, |
234 |
|
235 |
connect_vm: function(vm, cb) { |
236 |
var self = this; |
237 |
var data = {
|
238 |
port: {
|
239 |
network_id: this.id, |
240 |
device_id: vm.id
|
241 |
} |
242 |
} |
243 |
|
244 |
this.pending_connections++;
|
245 |
this.update_connecting_status();
|
246 |
synnefo.storage.ports.create(data, {complete: cb});
|
247 |
} |
248 |
}); |
249 |
|
250 |
models.CombinedPublicNetwork = models.Network.extend({ |
251 |
defaults: {
|
252 |
'admin_state_up': true, |
253 |
'id': 'snf-combined-public-network', |
254 |
'name': 'Internet', |
255 |
'status': 'ACTIVE', |
256 |
'router:external': true, |
257 |
'shared': false, |
258 |
'rename_disabled': true, |
259 |
'subnets': []
|
260 |
}, |
261 |
|
262 |
initialize: function() { |
263 |
var self = this; |
264 |
this.ports = new Backbone.FilteredCollection(undefined, { |
265 |
collection: synnefo.storage.ports,
|
266 |
collectionFilter: function(m) { |
267 |
return m.get('network') && m.get('network').get('is_public'); |
268 |
} |
269 |
}); |
270 |
this.set({ports: this.ports}); |
271 |
this.floating_ips = synnefo.storage.floating_ips;
|
272 |
this.set({floating_ips: this.floating_ips}); |
273 |
|
274 |
this.available_floating_ips = new Backbone.FilteredCollection(undefined, { |
275 |
collection: synnefo.storage.floating_ips,
|
276 |
collectionFilter: function(m) { |
277 |
return !m.get('port_id'); |
278 |
} |
279 |
}); |
280 |
this.set({available_floating_ips: this.available_floating_ips}); |
281 |
models.Network.__super__.initialize.apply(this, arguments); |
282 |
}, |
283 |
|
284 |
}) |
285 |
|
286 |
models.Networks = models.NetworkCollection.extend({ |
287 |
model: models.Network,
|
288 |
path: 'networks', |
289 |
details: true, |
290 |
parse: function(resp) { |
291 |
var data = _.map(resp.networks, function(net) { |
292 |
if (!net.name) {
|
293 |
net.name = '(no name set)';
|
294 |
} |
295 |
return net
|
296 |
}) |
297 |
return resp.networks
|
298 |
}, |
299 |
|
300 |
get_floating_ips_network: function() { |
301 |
return this.filter(function(n) { return n.get('is_public')})[1] |
302 |
}, |
303 |
|
304 |
create_subnet: function(subnet_params, complete, error) { |
305 |
synnefo.storage.subnets.create(subnet_params, { |
306 |
complete: function () { complete && complete() }, |
307 |
error: function() { error && error() } |
308 |
}); |
309 |
}, |
310 |
|
311 |
create: function (name, type, cidr, dhcp, callback) { |
312 |
var quota = synnefo.storage.quotas;
|
313 |
var params = {network:{name:name}}; |
314 |
var subnet_params = {subnet:{network_id:undefined}}; |
315 |
if (!type) { throw "Network type cannot be empty"; } |
316 |
|
317 |
params.network.type = type; |
318 |
if (cidr) { subnet_params.subnet.cidr = cidr; }
|
319 |
if (dhcp) { subnet_params.subnet.dhcp_enabled = dhcp; }
|
320 |
if (dhcp === false) { subnet_params.subnet.dhcp_enabled = false; } |
321 |
|
322 |
var cb = function() { |
323 |
callback && callback(); |
324 |
} |
325 |
|
326 |
var complete = function() { |
327 |
if (!create_subnet) { cb && cb() }
|
328 |
}; |
329 |
var error = function() { cb() }; |
330 |
var create_subnet = !!cidr;
|
331 |
|
332 |
// on network create success, try to create the requested network
|
333 |
// subnet.
|
334 |
var self = this; |
335 |
var success = function(resp) { |
336 |
var network = resp.network;
|
337 |
if (create_subnet) {
|
338 |
subnet_params.subnet.network_id = network.id; |
339 |
self.create_subnet(subnet_params, cb, function() {
|
340 |
// rollback network creation
|
341 |
var created_network = new synnefo.models.networks.Network({ |
342 |
id: network.id
|
343 |
}); |
344 |
created_network.destroy({no_skip: true}); |
345 |
}); |
346 |
} |
347 |
quota.get('cyclades.network.private').increase();
|
348 |
} |
349 |
return this.api_call(this.path, "create", params, complete, error, success); |
350 |
} |
351 |
}); |
352 |
|
353 |
// dummy model/collection
|
354 |
models.FixedIP = models.NetworkModel.extend({ |
355 |
storage_attrs: {
|
356 |
'subnet_id': ['subnets', 'subnet'] |
357 |
} |
358 |
}); |
359 |
models.FixedIPs = models.NetworkCollection.extend({ |
360 |
model: models.FixedIP
|
361 |
}); |
362 |
|
363 |
models.Port = models.NetworkModel.extend({ |
364 |
path: 'ports', |
365 |
parse: function(obj) { |
366 |
return obj.port;
|
367 |
}, |
368 |
initialize: function() { |
369 |
models.Port.__super__.initialize.apply(this, arguments); |
370 |
var ips = new models.FixedIPs(); |
371 |
this.set({'ips': ips}); |
372 |
this.bind('change:fixed_ips', function() { |
373 |
var ips = this.get('ips'); |
374 |
//var ips = _.map(ips, function(ip) { ip.id = ip.a})
|
375 |
this.update_ips()
|
376 |
}, this);
|
377 |
this.update_ips();
|
378 |
this.set({'pending_firewall': null}); |
379 |
}, |
380 |
|
381 |
update_ips: function() { |
382 |
var self = this; |
383 |
var ips = _.map(this.get('fixed_ips'), function(ip_obj) { |
384 |
var ip = _.clone(ip_obj);
|
385 |
var type = "v4"; |
386 |
if (ip.ip_address.indexOf(":") >= 0) { |
387 |
type = "v6";
|
388 |
} |
389 |
ip.id = ip.ip_address; |
390 |
ip.type = type; |
391 |
ip.subnet_id = ip.subnet; |
392 |
ip.port_id = self.id; |
393 |
delete ip.subnet;
|
394 |
return ip;
|
395 |
}); |
396 |
this.get('ips').update(ips, {removeMissing: true}); |
397 |
}, |
398 |
|
399 |
model_actions: {
|
400 |
'disconnect': [['status', 'network', 'vm'], function() { |
401 |
var network = this.get('network'); |
402 |
if ((!network || network.get('is_public')) && (network && !network.get('is_floating'))) { |
403 |
return false |
404 |
} |
405 |
var vm_active = this.get('vm') && this.get('vm').is_active(); |
406 |
if (!synnefo.config.hotplug_enabled && this.get('vm') && vm_active) { |
407 |
return false; |
408 |
} |
409 |
var status_ok = _.contains(['DOWN', 'ACTIVE', 'CONNECTED'], |
410 |
this.get('status')); |
411 |
var vm_status_ok = this.get('vm') && this.get('vm').can_connect(); |
412 |
var vm_status = this.get('vm') && this.get('vm').get('status'); |
413 |
return status_ok && vm_status_ok
|
414 |
}] |
415 |
}, |
416 |
|
417 |
storage_attrs: {
|
418 |
'device_id': ['vms', 'vm'], |
419 |
'network_id': ['networks', 'network'] |
420 |
}, |
421 |
|
422 |
proxy_attrs: {
|
423 |
'firewall_status': [
|
424 |
['vm'], function(vm) { |
425 |
var attachment = vm && vm.get_attachment(this.id); |
426 |
if (!attachment) { return "DISABLED" } |
427 |
return attachment.firewallProfile
|
428 |
} |
429 |
], |
430 |
'ext_status': [
|
431 |
['status'], function() { |
432 |
if (_.contains(["DISCONNECTING"], this.get('ext_status'))) { |
433 |
return this.get("ext_status") |
434 |
} |
435 |
return this.get("status") |
436 |
} |
437 |
], |
438 |
'in_progress': [
|
439 |
['ext_status', 'vm'], function() { |
440 |
var vm_progress = this.get('vm') && this.get('vm').get('in_progress'); |
441 |
if (vm_progress) { return true } |
442 |
return _.contains(["BUILD", "DISCONNECTING", "CONNECTING"], this.get("ext_status")) |
443 |
} |
444 |
], |
445 |
// check progress of port instance only
|
446 |
'in_progress_no_vm': [
|
447 |
['ext_status'], function() { |
448 |
return _.contains(["BUILD", "DISCONNECTING", "CONNECTING"], this.get("ext_status")) |
449 |
} |
450 |
], |
451 |
'firewall_running': [
|
452 |
['firewall_status', 'pending_firewall'], function(status, pending) { |
453 |
var pending = this.get('pending_firewall'); |
454 |
var status = this.get('firewall_status'); |
455 |
if (!pending) { return false } |
456 |
if (status == pending) {
|
457 |
this.set({'pending_firewall': null}); |
458 |
} |
459 |
return status != pending;
|
460 |
} |
461 |
], |
462 |
}, |
463 |
|
464 |
disconnect: function(cb) { |
465 |
var network = this.get('network'); |
466 |
var vm = this.get('vm'); |
467 |
network.pending_disconnects++; |
468 |
network.update_connecting_status(); |
469 |
var success = _.bind(function() { |
470 |
if (vm) {
|
471 |
vm.set({'status': 'DISCONNECTING'}); |
472 |
} |
473 |
this.set({'status': 'DISCONNECTING'}); |
474 |
cb && cb(); |
475 |
}, this);
|
476 |
this.destroy({success: success, complete: cb, silent: true}); |
477 |
} |
478 |
}); |
479 |
|
480 |
models.Ports = models.NetworkCollection.extend({ |
481 |
model: models.Port,
|
482 |
path: 'ports', |
483 |
details: true, |
484 |
noUpdate: true, |
485 |
updateEntries: true, |
486 |
|
487 |
parse: function(data) { |
488 |
return data.ports;
|
489 |
}, |
490 |
|
491 |
comparator: function(m) { |
492 |
return parseInt(m.id);
|
493 |
} |
494 |
}); |
495 |
|
496 |
models.FloatingIP = models.NetworkModel.extend({ |
497 |
path: 'floatingips', |
498 |
|
499 |
parse: function(obj) { |
500 |
return obj.floatingip;
|
501 |
}, |
502 |
|
503 |
storage_attrs: {
|
504 |
'port_id': ['ports', 'port'], |
505 |
'floating_network_id': ['networks', 'network'], |
506 |
}, |
507 |
|
508 |
model_actions: {
|
509 |
'remove': [['status'], function() { |
510 |
var status_ok = _.contains(['DISCONNECTED'], this.get('status')) |
511 |
return status_ok
|
512 |
}], |
513 |
'connect': [['status'], function() { |
514 |
var status_ok = _.contains(['DISCONNECTED'], this.get('status')) |
515 |
return status_ok
|
516 |
}], |
517 |
'disconnect': [['status', 'port_id', 'port'], function() { |
518 |
var port = this.get('port'); |
519 |
if (!port) { return false } |
520 |
return port.get('can_disconnect'); |
521 |
}] |
522 |
}, |
523 |
|
524 |
do_remove: function(succ, err) { return this.do_destroy(succ, err) }, |
525 |
do_destroy: function(succ, err) { |
526 |
this.actions.reset_pending();
|
527 |
this.destroy({
|
528 |
success: _.bind(function() { |
529 |
this.set({status: 'REMOVING'}); |
530 |
succ && succ(); |
531 |
}, this),
|
532 |
error: err || function() {}, |
533 |
silent: true |
534 |
}); |
535 |
}, |
536 |
|
537 |
do_disconnect: function(succ, err) { |
538 |
this.actions.reset_pending();
|
539 |
this.get('port').disconnect(succ); |
540 |
}, |
541 |
|
542 |
proxy_attrs: {
|
543 |
'ip': [
|
544 |
['floating_ip_adress'], function() { |
545 |
return this.get('floating_ip_address'); |
546 |
}], |
547 |
|
548 |
'in_progress': [
|
549 |
['status'], function() { |
550 |
return _.contains(['CONNECTING', 'DISCONNECTING', 'REMOVING'], |
551 |
this.get('status')) |
552 |
} |
553 |
], |
554 |
|
555 |
'status': [
|
556 |
['port_id', 'port'], function() { |
557 |
var port_id = this.get('port_id'); |
558 |
if (!port_id) {
|
559 |
return 'DISCONNECTED' |
560 |
} else {
|
561 |
var port = this.get('port'); |
562 |
if (port) {
|
563 |
var port_status = port.get('ext_status'); |
564 |
if (port_status == "DISCONNECTING") { |
565 |
return port_status
|
566 |
} |
567 |
if (port_status == "CONNECTING") { |
568 |
return port_status
|
569 |
} |
570 |
return 'CONNECTED' |
571 |
} |
572 |
return 'CONNECTING' |
573 |
} |
574 |
} |
575 |
] |
576 |
} |
577 |
}); |
578 |
|
579 |
models.FloatingIPs = models.NetworkCollection.extend({ |
580 |
model: models.FloatingIP,
|
581 |
details: false, |
582 |
path: 'floatingips', |
583 |
parse: function(resp) { |
584 |
return resp.floatingips;
|
585 |
} |
586 |
}); |
587 |
|
588 |
models.Router = models.NetworkModel.extend({ |
589 |
}); |
590 |
|
591 |
models.Routers = models.NetworkCollection.extend({ |
592 |
model: models.Router,
|
593 |
path: 'routers', |
594 |
parse: function(resp) { |
595 |
return resp.routers
|
596 |
} |
597 |
}); |
598 |
|
599 |
snf.storage.floating_ips = new models.FloatingIPs();
|
600 |
snf.storage.routers = new models.Routers();
|
601 |
snf.storage.networks = new models.Networks();
|
602 |
snf.storage.ports = new models.Ports();
|
603 |
snf.storage.subnets = new models.Subnets();
|
604 |
|
605 |
})(this);
|