1 if (! window.XOSLIB_LOADED ) {
2 window.XOSLIB_LOADED=true;
4 SLIVER_API = "/plstackapi/slivers/";
5 SLICE_API = "/plstackapi/slices/";
6 SLICEROLE_API = "/plstackapi/slice_roles/";
7 NODE_API = "/plstackapi/nodes/";
8 SITE_API = "/plstackapi/sites/";
9 USER_API = "/plstackapi/users/";
10 USERDEPLOYMENT_API = "/plstackapi/user_deployments/";
11 DEPLOYMENT_API = "/plstackapi/deployments/";
12 IMAGE_API = "/plstackapi/images/";
13 NETWORKTEMPLATE_API = "/plstackapi/networktemplates/";
14 NETWORK_API = "/plstackapi/networks/";
15 NETWORKSLIVER_API = "/plstackapi/networkslivers/";
16 SERVICE_API = "/plstackapi/services/";
17 SLICEPRIVILEGE_API = "/plstackapi/slice_privileges/";
18 NETWORKDEPLOYMENT_API = "/plstackapi/networkdeployments/";
20 /* changed as a side effect of the big rename
21 SLICEDEPLOYMENT_API = "/plstackapi/slice_deployments/";
22 USERDEPLOYMENT_API = "/plstackapi/user_deployments/";
25 SLICEDEPLOYMENT_API = "/plstackapi/slicedeployments/";
26 USERDEPLOYMENT_API = "/plstackapi/userdeployments/";
28 SLICEPLUS_API = "/xoslib/slicesplus/";
30 XOSModel = Backbone.Model.extend({
31 /* from backbone-tastypie.js */
32 //idAttribute: 'resource_uri',
34 /* from backbone-tastypie.js */
36 var url = this.attributes.resource_uri;
40 url = this.urlRoot + this.id;
42 // this happens when creating a new model.
48 // XXX I'm not sure this does anything useful
49 url = ( _.isFunction( this.collection.url ) ? this.collection.url() : this.collection.url );
50 url = url || this.urlRoot;
53 // remove any existing query parameters
54 url && ( url.indexOf("?") > -1 ) && ( url = url.split("?")[0] );
56 url && ( url += ( url.length > 0 && url.charAt( url.length - 1 ) === '/' ) ? '' : '/' );
58 url && ( url += "?no_hyperlinks=1" );
63 listMethods: function() {
65 for(var m in this) {
\r
66 if(typeof this[m] == "function") {
\r
73 /* If a 'validate' method is supplied, then it will be called
74 automatically on save. Unfortunately, save calls neither the
75 'error' nor the 'success' callback if the validator fails.
77 For now, we're calling our validator 'xosValidate' so this
78 autoamtic validation doesn't occur.
81 xosValidate: function(attrs, options) {
84 _.each(this.validators, function(validatorList, fieldName) {
85 _.each(validatorList, function(validator) {
86 if (fieldName in attrs) {
87 validatorResult = validateField(validator, attrs[fieldName], this)
88 if (validatorResult != true) {
89 errors[fieldName] = validatorResult;
98 // backbone.js semantics -- on successful validate, return nothing
101 /* uncommenting this would make validate() call xosValidate()
102 validate: function(attrs, options) {
103 r = this.xosValidate(attrs, options);
104 console.log("validate");
110 XOSCollection = Backbone.Collection.extend({
111 objects: function() {
112 return this.models.map(function(element) { return element.attributes; });
115 initialize: function(){
116 this.isLoaded = false;
117 this.failedLoad = false;
118 this.startedLoad = false;
119 this.sortVar = 'name';
\r
120 this.sortOrder = 'asc';
\r
121 this.on( "sort", this.sorted );
\r
124 relatedCollections: [],
\r
125 foreignCollections: [],
\r
127 sorted: function() {
\r
128 //console.log("sorted " + this.modelName);
\r
131 simpleComparator: function( model ){
\r
132 parts=this.sortVar.split(".");
\r
133 result = model.get(parts[0]);
\r
134 for (index=1; index<parts.length; ++index) {
\r
135 result=result[parts[index]];
\r
140 comparator: function (left, right) {
\r
141 var l = this.simpleComparator(left);
\r
142 var r = this.simpleComparator(right);
\r
144 if (l === void 0) return -1;
\r
145 if (r === void 0) return 1;
\r
147 if (this.sortOrder=="desc") {
\r
148 return l < r ? 1 : l > r ? -1 : 0;
\r
150 return l < r ? -1 : l > r ? 1 : 0;
\r
154 fetchSuccess: function(collection, response, options) {
\r
155 //console.log("fetch succeeded " + collection.modelName);
\r
156 this.failedLoad = false;
\r
157 this.fetching = false;
\r
158 if (!this.isLoaded) {
\r
159 this.isLoaded = true;
\r
160 Backbone.trigger("xoslib:collectionLoadChange", this);
\r
162 this.trigger("fetchStateChange");
\r
163 if (options["orig_success"]) {
\r
164 options["orig_success"](collection, response, options);
\r
168 fetchFailure: function(collection, response, options) {
\r
169 //console.log("fetch failed " + collection.modelName);
\r
170 this.fetching = false;
\r
171 if ((!this.isLoaded) && (!this.failedLoad)) {
\r
172 this.failedLoad=true;
\r
173 Backbone.trigger("xoslib:collectionLoadChange", this);
\r
175 this.trigger("fetchStateChange");
\r
176 if (options["orig_failure"]) {
\r
177 options["orig_failure"](collection, response, options);
\r
181 fetch: function(options) {
\r
183 this.fetching=true;
\r
184 //console.log("fetch " + this.modelName);
\r
185 if (!this.startedLoad) {
\r
186 this.startedLoad=true;
\r
187 Backbone.trigger("xoslib:collectionLoadChange", this);
\r
189 this.trigger("fetchStateChange");
\r
190 if (options == undefined) {
\r
193 options["orig_success"] = options["success"];
\r
194 options["orig_failure"] = options["failure"];
\r
195 options["success"] = function(collection, response, options) { self.fetchSuccess.call(self, collection, response, options); };
\r
196 options["failure"] = this.fetchFailure;
\r
197 Backbone.Collection.prototype.fetch.call(this, options);
\r
200 startPolling: function() {
\r
201 if (!this._polling) {
\r
203 setInterval(function() { collection.fetch(); }, 10000);
209 refresh: function(refreshRelated) {
210 if (!this.fetching) {
213 if (refreshRelated) {
214 for (related in this.relatedCollections) {
215 related = xos[related];
216 if (!related.fetching) {
223 maybeFetch: function(options){
224 // Helper function to fetch only if this collection has not been fetched before.
226 // If this has already been fetched, call the success, if it exists
227 options.success && options.success();
228 console.log("alreadyFetched");
232 // when the original success function completes mark this collection as fetched
234 successWrapper = function(success){
236 self._fetched = true;
237 success && success.apply(this, arguments);
240 options.success = successWrapper(options.success);
241 console.log("call fetch");
245 getOrFetch: function(id, options){
246 // Helper function to use this collection as a cache for models on the server
247 var model = this.get(id);
250 options.success && options.success(model);
254 model = new this.model({
258 model.fetch(options);
261 filterBy: function(fieldName, value) {
262 filtered = this.filter(function(obj) {
263 return obj.get(fieldName) == value;
265 return new this.constructor(filtered);
268 /* from backbone-tastypie.js */
269 url: function( models ) {
270 var url = this.urlRoot || ( models && models.length && models[0].urlRoot );
271 url && ( url += ( url.length > 0 && url.charAt( url.length - 1 ) === '/' ) ? '' : '/' );
273 // Build a url to retrieve a set of models. This assume the last part of each model's idAttribute
274 // (set to 'resource_uri') contains the model's id.
275 if ( models && models.length ) {
276 var ids = _.map( models, function( model ) {
277 var parts = _.compact( model.id.split('/') );
278 return parts[ parts.length - 1 ];
280 url += 'set/' + ids.join(';') + '/';
283 url && ( url += "?no_hyperlinks=1" );
288 listMethods: function() {
290 for(var m in this) {
\r
291 if(typeof this[m] == "function") {
\r
299 function define_model(lib, attrs) {
300 modelName = attrs.modelName;
301 modelClassName = modelName;
302 collectionClassName = modelName + "Collection";
304 if (!attrs.addFields) {
305 attrs.addFields = attrs.detailFields;
308 if (!attrs.inputType) {
309 attrs.inputType = {};
312 if (!attrs.foreignFields) {
313 attrs.foreignFields = {};
316 if (!attrs.collectionName) {
317 attrs.collectionName = modelName + "s";
319 collectionName = attrs.collectionName;
326 if ($.inArray(key, ["urlRoot", "modelName", "collectionName", "addFields", "detailFields", "foreignFields", "inputType", "relatedCollections", "foreignCollections"])>=0) {
327 modelAttrs[key] = value;
328 collectionAttrs[key] = value;
330 if ($.inArray(key, ["validate"])) {
331 modelAttrs[key] = value;
335 if (xosdefaults && xosdefaults[modelName]) {
336 modelAttrs["defaults"] = xosdefaults[modelName];
339 if (xosvalidators && xosvalidators[modelName]) {
340 modelAttrs["validators"] = xosvalidators[modelName];
343 lib[modelName] = XOSModel.extend(modelAttrs);
345 collectionAttrs["model"] = lib[modelName];
347 lib[collectionClassName] = XOSCollection.extend(collectionAttrs);
348 lib[collectionName] = new lib[collectionClassName]();
350 lib.allCollectionNames.push(collectionName);
351 lib.allCollections.push(lib[collectionName]);
355 this.allCollectionNames = [];
356 this.allCollections = [];
358 define_model(this, {urlRoot: SLIVER_API,
359 relatedCollections: {"networkSlivers": "sliver"},
360 foreignCollections: ["slices", "deployments", "images", "nodes", "users"],
361 foreignFields: {"creator": "users", "image": "images", "node": "nodes", "deploymentNetwork": "deployments", "slice": "slices"},
363 addFields: ["slice", "deploymentNetwork", "image", "node"],
364 detailFields: ["name", "instance_id", "instance_name", "slice", "deploymentNetwork", "image", "node", "creator"],
367 define_model(this, {urlRoot: SLICE_API,
368 relatedCollections: {"slivers": "slice", "sliceDeployments": "slice", "slicePrivileges": "slice", "networks": "owner"},
369 foreignCollections: ["services", "sites"],
370 foreignFields: {"service": "services", "site": "sites"},
371 detailFields: ["name", "site", "enabled", "description", "url", "max_slivers"],
372 inputType: {"enabled": "checkbox"},
374 xosValidate: function(attrs, options) {
375 errors = XOSModel.prototype.xosValidate(this, attrs, options);
376 // validate that slice.name starts with site.login_base
377 site = attrs.site || this.site;
378 if ((site!=undefined) && (attrs.name!=undefined)) {
379 site = xos.sites.get(site);
380 if (attrs.name.indexOf(site.attributes.login_base+"_") != 0) {
381 errors = errors || {};
382 errors["name"] = "must start with " + site.attributes.login_base + "_";
389 define_model(this, {urlRoot: SLICEDEPLOYMENT_API,
390 foreignCollections: ["slices", "deployments"],
391 modelName: "sliceDeployment",
392 foreignFields: {"slice": "slices", "deployment": "deployments"},
393 detailFields: ["slice", "deployment", "tenant_id"],
396 define_model(this, {urlRoot: SLICEPRIVILEGE_API,
397 foreignCollections: ["slices", "users", "sliceRoles"],
398 modelName: "slicePrivilege",
399 foreignFields: {"user": "users", "slice": "slices", "role": "sliceRoles"},
400 detailFields: ["user", "slice", "role"],
403 define_model(this, {urlRoot: SLICEROLE_API,
404 modelName: "sliceRole",
405 detailFields: ["role"],
408 define_model(this, {urlRoot: NODE_API,
409 foreignCollections: ["sites", "deployments"],
411 foreignFields: {"site": "sites", "deployment": "deployments"},
412 detailFields: ["name", "site", "deployment"],
415 define_model(this, {urlRoot: SITE_API,
416 relatedCollections: {"users": "site", "slices": "site", "nodes": "site"},
418 detailFields: ["name", "abbreviated_name", "url", "enabled", "is_public", "login_base"],
419 inputType: {"enabled": "checkbox", "is_public": "checkbox"},
422 define_model(this, {urlRoot: USER_API,
423 relatedCollections: {"slicePrivileges": "user", "slices": "owner", "userDeployments": "user"},
424 foreignCollections: ["sites"],
426 foreignFields: {"site": "sites"},
427 detailFields: ["username", "firstname", "lastname", "phone", "user_url", "site"],
430 define_model(this, {urlRoot: USERDEPLOYMENT_API,
431 foreignCollections: ["users","deployments"],
432 modelName: "userDeployment",
433 foreignFields: {"deployment": "deployments", "user": "users"},
434 detailFields: ["user", "deployment", "kuser_id"],
437 define_model(this, { urlRoot: DEPLOYMENT_API,
438 relatedCollections: {"nodes": "deployment", "slivers": "deploymentNetwork", "networkDeployments": "deployment", "userDeployments": "deployment"},
439 modelName: "deployment",
440 detailFields: ["name", "backend_type", "admin_tenant"],
443 define_model(this, {urlRoot: IMAGE_API,
446 detailFields: ["name", "disk_format", "admin_tenant"],
449 define_model(this, {urlRoot: NETWORKTEMPLATE_API,
450 modelName: "networkTemplate",
451 detailFields: ["name", "description", "visibility", "translation", "sharedNetworkName", "sharedNetworkId"],
454 define_model(this, {urlRoot: NETWORK_API,
455 relatedCollections: {"networkDeployments": "network", "networkSlivers": "network"},
456 foreignCollections: ["slices", "networkTemplates"],
457 modelName: "network",
458 foreignFields: {"template": "networkTemplates", "owner": "slices"},
459 detailFields: ["name", "template", "ports", "labels", "owner"],
462 define_model(this, {urlRoot: NETWORKSLIVER_API,
463 modelName: "networkSliver",
464 foreignFields: {"network": "networks", "sliver": "slivers"},
465 detailFields: ["network", "sliver", "ip", "port_id"],
468 define_model(this, {urlRoot: NETWORKDEPLOYMENT_API,
469 modelName: "networkDeployment",
470 foreignFields: {"network": "networks", "deployment": "deployments"},
471 detailFields: ["network", "deployment", "net_id"],
474 define_model(this, {urlRoot: SERVICE_API,
475 modelName: "service",
476 detailFields: ["name", "description", "versionNumber"],
480 define_model(this, {urlRoot: SLICEPLUS_API,
481 relatedCollections: {'slivers': "slice"},
482 modelName: "slicePlus",
483 collectionName: "slicesPlus"});
485 this.listObjects = function() { return this.allCollectionNames; };
487 this.getCollectionStatus = function() {
488 stats = {isLoaded: 0, failedLoad: 0, startedLoad: 0};
489 for (index in this.allCollections) {
490 collection = this.allCollections[index];
491 if (collection.isLoaded) {
492 stats["isLoaded"] = stats["isLoaded"] + 1;
494 if (collection.failedLoad) {
495 stats["failedLoad"] = stats["failedLoad"] + 1;
497 if (collection.startedLoad) {
498 stats["startedLoad"] = stats["startedLoad"] + 1;
501 stats["completedLoad"] = stats["failedLoad"] + stats["isLoaded"];
508 function getCookie(name) {
509 var cookieValue = null;
\r
510 if (document.cookie && document.cookie != '') {
\r
511 var cookies = document.cookie.split(';');
\r
512 for (var i = 0; i < cookies.length; i++) {
\r
513 var cookie = jQuery.trim(cookies[i]);
\r
514 // Does this cookie string begin with the name we want?
\r
515 if (cookie.substring(0, name.length + 1) == (name + '=')) {
\r
516 cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
\r
521 return cookieValue;
\r
525 var _sync = Backbone.sync;
\r
526 Backbone.sync = function(method, model, options){
\r
527 options.beforeSend = function(xhr){
\r
528 var token = getCookie("csrftoken");
\r
529 xhr.setRequestHeader('X-CSRFToken', token);
\r
531 return _sync(method, model, options);
\r