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: note that this yields a new collection. If you pass that
262 collection to a CompositeView, then the CompositeView won't get
263 any events that trigger on the original collection.
265 Using this function is probably wrong, and I wrote
266 FilteredCompositeView() to replace it.
269 filterBy: function(fieldName, value) {
270 filtered = this.filter(function(obj) {
271 return obj.get(fieldName) == value;
273 return new this.constructor(filtered);
276 /* from backbone-tastypie.js */
277 url: function( models ) {
278 var url = this.urlRoot || ( models && models.length && models[0].urlRoot );
279 url && ( url += ( url.length > 0 && url.charAt( url.length - 1 ) === '/' ) ? '' : '/' );
281 // Build a url to retrieve a set of models. This assume the last part of each model's idAttribute
282 // (set to 'resource_uri') contains the model's id.
283 if ( models && models.length ) {
284 var ids = _.map( models, function( model ) {
285 var parts = _.compact( model.id.split('/') );
286 return parts[ parts.length - 1 ];
288 url += 'set/' + ids.join(';') + '/';
291 url && ( url += "?no_hyperlinks=1" );
296 listMethods: function() {
298 for(var m in this) {
\r
299 if(typeof this[m] == "function") {
\r
307 function define_model(lib, attrs) {
308 modelName = attrs.modelName;
309 modelClassName = modelName;
310 collectionClassName = modelName + "Collection";
312 if (!attrs.addFields) {
313 attrs.addFields = attrs.detailFields;
316 if (!attrs.inputType) {
317 attrs.inputType = {};
320 if (!attrs.foreignFields) {
321 attrs.foreignFields = {};
324 if (!attrs.collectionName) {
325 attrs.collectionName = modelName + "s";
327 collectionName = attrs.collectionName;
334 if ($.inArray(key, ["urlRoot", "modelName", "collectionName", "addFields", "detailFields", "foreignFields", "inputType", "relatedCollections", "foreignCollections"])>=0) {
335 modelAttrs[key] = value;
336 collectionAttrs[key] = value;
338 if ($.inArray(key, ["validate"])) {
339 modelAttrs[key] = value;
343 if (xosdefaults && xosdefaults[modelName]) {
344 modelAttrs["defaults"] = xosdefaults[modelName];
347 if (xosvalidators && xosvalidators[modelName]) {
348 modelAttrs["validators"] = xosvalidators[modelName];
351 lib[modelName] = XOSModel.extend(modelAttrs);
353 collectionAttrs["model"] = lib[modelName];
355 lib[collectionClassName] = XOSCollection.extend(collectionAttrs);
356 lib[collectionName] = new lib[collectionClassName]();
358 lib.allCollectionNames.push(collectionName);
359 lib.allCollections.push(lib[collectionName]);
363 this.allCollectionNames = [];
364 this.allCollections = [];
366 define_model(this, {urlRoot: SLIVER_API,
367 relatedCollections: {"networkSlivers": "sliver"},
368 foreignCollections: ["slices", "deployments", "images", "nodes", "users"],
369 foreignFields: {"creator": "users", "image": "images", "node": "nodes", "deploymentNetwork": "deployments", "slice": "slices"},
371 addFields: ["slice", "deploymentNetwork", "image", "node"],
372 detailFields: ["name", "instance_id", "instance_name", "slice", "deploymentNetwork", "image", "node", "creator"],
375 define_model(this, {urlRoot: SLICE_API,
376 relatedCollections: {"slivers": "slice", "sliceDeployments": "slice", "slicePrivileges": "slice", "networks": "owner"},
377 foreignCollections: ["services", "sites"],
378 foreignFields: {"service": "services", "site": "sites"},
379 detailFields: ["name", "site", "enabled", "description", "url", "max_slivers"],
380 inputType: {"enabled": "checkbox"},
382 xosValidate: function(attrs, options) {
383 errors = XOSModel.prototype.xosValidate(this, attrs, options);
384 // validate that slice.name starts with site.login_base
385 site = attrs.site || this.site;
386 if ((site!=undefined) && (attrs.name!=undefined)) {
387 site = xos.sites.get(site);
388 if (attrs.name.indexOf(site.attributes.login_base+"_") != 0) {
389 errors = errors || {};
390 errors["name"] = "must start with " + site.attributes.login_base + "_";
397 define_model(this, {urlRoot: SLICEDEPLOYMENT_API,
398 foreignCollections: ["slices", "deployments"],
399 modelName: "sliceDeployment",
400 foreignFields: {"slice": "slices", "deployment": "deployments"},
401 detailFields: ["slice", "deployment", "tenant_id"],
404 define_model(this, {urlRoot: SLICEPRIVILEGE_API,
405 foreignCollections: ["slices", "users", "sliceRoles"],
406 modelName: "slicePrivilege",
407 foreignFields: {"user": "users", "slice": "slices", "role": "sliceRoles"},
408 detailFields: ["user", "slice", "role"],
411 define_model(this, {urlRoot: SLICEROLE_API,
412 modelName: "sliceRole",
413 detailFields: ["role"],
416 define_model(this, {urlRoot: NODE_API,
417 foreignCollections: ["sites", "deployments"],
419 foreignFields: {"site": "sites", "deployment": "deployments"},
420 detailFields: ["name", "site", "deployment"],
423 define_model(this, {urlRoot: SITE_API,
424 relatedCollections: {"users": "site", "slices": "site", "nodes": "site"},
426 detailFields: ["name", "abbreviated_name", "url", "enabled", "is_public", "login_base"],
427 inputType: {"enabled": "checkbox", "is_public": "checkbox"},
430 define_model(this, {urlRoot: USER_API,
431 relatedCollections: {"slicePrivileges": "user", "slices": "owner", "userDeployments": "user"},
432 foreignCollections: ["sites"],
434 foreignFields: {"site": "sites"},
435 detailFields: ["username", "firstname", "lastname", "phone", "user_url", "site"],
438 define_model(this, {urlRoot: USERDEPLOYMENT_API,
439 foreignCollections: ["users","deployments"],
440 modelName: "userDeployment",
441 foreignFields: {"deployment": "deployments", "user": "users"},
442 detailFields: ["user", "deployment", "kuser_id"],
445 define_model(this, { urlRoot: DEPLOYMENT_API,
446 relatedCollections: {"nodes": "deployment", "slivers": "deploymentNetwork", "networkDeployments": "deployment", "userDeployments": "deployment"},
447 modelName: "deployment",
448 detailFields: ["name", "backend_type", "admin_tenant"],
451 define_model(this, {urlRoot: IMAGE_API,
454 detailFields: ["name", "disk_format", "admin_tenant"],
457 define_model(this, {urlRoot: NETWORKTEMPLATE_API,
458 modelName: "networkTemplate",
459 detailFields: ["name", "description", "visibility", "translation", "sharedNetworkName", "sharedNetworkId"],
462 define_model(this, {urlRoot: NETWORK_API,
463 relatedCollections: {"networkDeployments": "network", "networkSlivers": "network"},
464 foreignCollections: ["slices", "networkTemplates"],
465 modelName: "network",
466 foreignFields: {"template": "networkTemplates", "owner": "slices"},
467 detailFields: ["name", "template", "ports", "labels", "owner"],
470 define_model(this, {urlRoot: NETWORKSLIVER_API,
471 modelName: "networkSliver",
472 foreignFields: {"network": "networks", "sliver": "slivers"},
473 detailFields: ["network", "sliver", "ip", "port_id"],
476 define_model(this, {urlRoot: NETWORKDEPLOYMENT_API,
477 modelName: "networkDeployment",
478 foreignFields: {"network": "networks", "deployment": "deployments"},
479 detailFields: ["network", "deployment", "net_id"],
482 define_model(this, {urlRoot: SERVICE_API,
483 modelName: "service",
484 detailFields: ["name", "description", "versionNumber"],
488 define_model(this, {urlRoot: SLICEPLUS_API,
489 relatedCollections: {'slivers': "slice"},
490 modelName: "slicePlus",
491 collectionName: "slicesPlus"});
493 this.listObjects = function() { return this.allCollectionNames; };
495 this.getCollectionStatus = function() {
496 stats = {isLoaded: 0, failedLoad: 0, startedLoad: 0};
497 for (index in this.allCollections) {
498 collection = this.allCollections[index];
499 if (collection.isLoaded) {
500 stats["isLoaded"] = stats["isLoaded"] + 1;
502 if (collection.failedLoad) {
503 stats["failedLoad"] = stats["failedLoad"] + 1;
505 if (collection.startedLoad) {
506 stats["startedLoad"] = stats["startedLoad"] + 1;
509 stats["completedLoad"] = stats["failedLoad"] + stats["isLoaded"];
516 function getCookie(name) {
517 var cookieValue = null;
\r
518 if (document.cookie && document.cookie != '') {
\r
519 var cookies = document.cookie.split(';');
\r
520 for (var i = 0; i < cookies.length; i++) {
\r
521 var cookie = jQuery.trim(cookies[i]);
\r
522 // Does this cookie string begin with the name we want?
\r
523 if (cookie.substring(0, name.length + 1) == (name + '=')) {
\r
524 cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
\r
529 return cookieValue;
\r
533 var _sync = Backbone.sync;
\r
534 Backbone.sync = function(method, model, options){
\r
535 options.beforeSend = function(xhr){
\r
536 var token = getCookie("csrftoken");
\r
537 xhr.setRequestHeader('X-CSRFToken', token);
\r
539 return _sync(method, model, options);
\r