Merge branch 'master' of ssh://git.planet-lab.org/git/plstackapi into HEAD
diff --git a/planetstack/apigen/api.template.py b/planetstack/apigen/api.template.py
index b17f135..2cfb54a 100644
--- a/planetstack/apigen/api.template.py
+++ b/planetstack/apigen/api.template.py
@@ -70,9 +70,9 @@
     id = serializers.Field()
     {% for ref in object.refs %}
     {% if ref.multi %}
-    {{ ref.plural }} = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='{{ ref }}-detail')
+    {{ ref.plural }} = serializers.PrimaryKeyRelatedField(many=True, read_only=True) #, view_name='{{ ref }}-detail')
     {% else %}
-    {{ ref }} = serializers.HyperlinkedRelatedField(read_only=True, view_name='{{ ref }}-detail')
+    {{ ref }} = serializers.PrimaryKeyRelatedField(read_only=True) #, view_name='{{ ref }}-detail')
     {% endif %}
     {% endfor %}
     humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
diff --git a/planetstack/apigen/modelgen b/planetstack/apigen/modelgen
old mode 100755
new mode 100644
index 8a647b1..77d1e67
--- a/planetstack/apigen/modelgen
+++ b/planetstack/apigen/modelgen
@@ -111,8 +111,6 @@
 						cobj = copy.deepcopy(obj)
 						cobj.multi = True
 						cobj.plural_name = related_name
-						#if (str(refobj)=='slice' and related_name=='networks'):
-						#	pdb.set_trace()
 						refobj.refs.append(cobj)
 				else:
 					obj.props.append(f.name)
@@ -126,7 +124,7 @@
 
 				related_name = f.related_query_name()
 				if related_model_name in self.keys():
-			#		pdb.set_trace()
+                                        #print "XXX1", obj, f, related_name, related_model_name
 					refobj = self[related_model_name]
 					cobj = copy.deepcopy(obj)
 					cobj.multi=True
@@ -137,17 +135,12 @@
                                     continue
 
 				if (related_name!='+' and related_name.lower()!=str(obj).lower()):
-					cobj = copy.deepcopy(obj)
+                                        #print "XXX2", obj, f, related_name, related_model_name, refobj.plural_name
+                                        refobj = self[related_model_name]
+					cobj = copy.deepcopy(refobj)
 					cobj.multi = True
-					cobj.plural_name = related_name
 
-					#if (str(refobj)=='slice' and related_name=='networks'):
-					#	pdb.set_trace()
-					refobj.refs.append(cobj)
-
-					#if (related_name=='networks'):
-						#pdb.set_trace()
-						#print str(refobj)
+					obj.refs.append(cobj)
 
 
 
diff --git a/planetstack/core/xoslib/static/js/xosAdminSite.js b/planetstack/core/xoslib/static/js/xosAdminSite.js
index 3aa33d3..c44dc58 100644
--- a/planetstack/core/xoslib/static/js/xosAdminSite.js
+++ b/planetstack/core/xoslib/static/js/xosAdminSite.js
@@ -1,4 +1,4 @@
-OBJS = ['deployment', 'image', 'networkTemplate', 'network', 'networkSliver', 'networkDeployment', 'node', 'service', 'site', 'slice', 'sliceDeployment', 'slicePrivilege', 'sliver', 'user', 'sliceRole', 'userDeployment'];
+OBJS = ['deployment', 'image', 'networkTemplate', 'network', 'networkSliver', 'networkDeployment', 'node', 'service', 'site', 'slice', 'sliceDeployment', 'slicePrivilege', 'sliver', 'user', 'sliceRole', 'userDeployment', 'flavor', 'imageDeployment'];
 NAV_OBJS = ['deployment', 'site', 'slice', 'user'];
 
 REWRITES = {"/admin/core/deployment/": "#deployments",
@@ -87,23 +87,29 @@
          collection_name = name + "s";
          region_name = name + "List";
 
+         if (window["XOSDetailView_" + name]) {
+             detailClass = window["XOSDetailView_" + name].extend({template: "#xos-detail-template",
+                                                                    app: XOSAdminApp});
+         } else {
+             detailClass = genericDetailClass;
+         }
          if ($(detail_template).length) {
-             detailClass = XOSDetailView.extend({
+             detailClass = detailClass.extend({
                 template: detail_template,

-                app: XOSAdminApp,

              });

-         } else {

-             detailClass = genericDetailClass;

          }

          XOSAdminApp[collection_name + "DetailView"] = detailClass;

 

-         if ($(add_child_template).length) {

-             addClass = XOSDetailView.extend({
+         if (window["XOSDetailView_" + name]) {

+             addClass = window["XOSDetailView_" + name].extend({template: "#xos-add-template",
+                                                                    app: XOSAdminApp});
+         } else {
+             addClass = genericAddChildClass;
+         }
+         if ($(add_child_template).length) {
+             addClass = detailClass.extend({
                 template: add_child_template,

-                app: XOSAdminApp,

              });

-         } else {

-             addClass = genericAddChildClass;

          }

          XOSAdminApp[collection_name + "AddChildView"] = addClass;

 

diff --git a/planetstack/core/xoslib/static/js/xoslib/xos-backbone.js b/planetstack/core/xoslib/static/js/xoslib/xos-backbone.js
index d5a0fc3..554ff5f 100644
--- a/planetstack/core/xoslib/static/js/xoslib/xos-backbone.js
+++ b/planetstack/core/xoslib/static/js/xoslib/xos-backbone.js
@@ -10,12 +10,14 @@
     USERDEPLOYMENT_API = "/plstackapi/user_deployments/";
     DEPLOYMENT_API = "/plstackapi/deployments/";
     IMAGE_API = "/plstackapi/images/";
+    IMAGEDEPLOYMENTS_API = "/plstackapi/imagedeployments/";
     NETWORKTEMPLATE_API = "/plstackapi/networktemplates/";
     NETWORK_API = "/plstackapi/networks/";
     NETWORKSLIVER_API = "/plstackapi/networkslivers/";
     SERVICE_API = "/plstackapi/services/";
     SLICEPRIVILEGE_API = "/plstackapi/slice_privileges/";
     NETWORKDEPLOYMENT_API = "/plstackapi/networkdeployments/";
+    FLAVOR_API = "/plstackapi/flavors/";
 
     /* changed as a side effect of the big rename
     SLICEDEPLOYMENT_API = "/plstackapi/slice_deployments/";
@@ -382,12 +384,12 @@
 
         define_model(this, {urlRoot: SLIVER_API,
                             relatedCollections: {"networkSlivers": "sliver"},
-                            foreignCollections: ["slices", "deployments", "images", "nodes", "users"],
-                            foreignFields: {"creator": "users", "image": "images", "node": "nodes", "deploymentNetwork": "deployments", "slice": "slices"},
+                            foreignCollections: ["slices", "deployments", "images", "nodes", "users", "flavors"],
+                            foreignFields: {"creator": "users", "image": "images", "node": "nodes", "deploymentNetwork": "deployments", "slice": "slices", "flavor": "flavors"},
                             modelName: "sliver",
                             listFields: ["backend_status", "id", "name", "instance_id", "instance_name", "slice", "deploymentNetwork", "image", "node", "flavor"],
-                            addFields: ["slice", "deploymentNetwork", "image", "node"],
-                            detailFields: ["backend_status", "name", "instance_id", "instance_name", "slice", "deploymentNetwork", "image", "node", "creator"],
+                            addFields: ["slice", "deploymentNetwork", "flavor", "image", "node"],
+                            detailFields: ["backend_status", "name", "instance_id", "instance_name", "slice", "deploymentNetwork", "flavor", "image", "node", "creator"],
                             preSave: function() { if (!this.attributes.name && this.attributes.slice) { this.attributes.name = xos.idToName(this.attributes.slice, "slices", "name"); } },
                             });
 
@@ -483,6 +485,13 @@
                             detailFields: ["backend_status", "name", "disk_format", "admin_tenant"],
                             });
 
+        define_model(this, {urlRoot: IMAGEDEPLOYMENTS_API,
+                            modelName: "imageDeployment",
+                            foreignCollections: ["images", "deployments"],
+                            listFields: ["backend_status", "id", "image", "deployment", "glance_image_id"],
+                            detailFields: ["backend_status", "image", "deployment", "glance_image_id"],
+                            });
+
         define_model(this, {urlRoot: NETWORKTEMPLATE_API,
                             modelName: "networkTemplate",
                             listFields: ["backend_status", "id", "name", "visibility", "translation", "sharedNetworkName", "sharedNetworkId"],
@@ -518,6 +527,13 @@
                             detailFields: ["backend_status", "name", "description", "versionNumber"],
                             });
 
+        define_model(this, {urlRoot: FLAVOR_API,
+                            modelName: "flavor",
+                            listFields: ["backend_status", "id", "name", "flavor", "order", "default"],
+                            detailFields: ["backend_status", "name", "description", "flavor", "order", "default"],
+                            inputType: {"default": "checkbox"},
+                            });
+
         // enhanced REST
         // XXX this really needs to somehow be combined with Slice, to avoid duplication
         define_model(this, {urlRoot: SLICEPLUS_API,
diff --git a/planetstack/core/xoslib/static/js/xoslib/xosHelper.js b/planetstack/core/xoslib/static/js/xoslib/xosHelper.js
index ece2419..65ccdbd 100644
--- a/planetstack/core/xoslib/static/js/xoslib/xosHelper.js
+++ b/planetstack/core/xoslib/static/js/xoslib/xosHelper.js
@@ -594,7 +594,66 @@
                                                     inputType: this.model.inputType,
                                                     model: this.model,
                                          }},
+});
 
+XOSDetailView_sliver = XOSDetailView.extend( {
+    events: $.extend(XOSDetailView.events,
+        {"change #field_deploymentNetwork": "onDeploymentNetworkChange"}
+    ),
+
+    onShow: function() {
+        // Note that this causes the selects to be updated a second time. The
+        // first time was when the template was originally invoked, and the
+        // selects will all have the full unfiltered set of candidates. Then
+        // onShow will fire, and we'll update them with the filtered values.
+        this.onDeploymentNetworkChange();
+    },
+
+    onDeploymentNetworkChange: function(e) {
+        var deploymentID = this.$el.find("#field_deploymentNetwork").val();
+
+        console.log("onDeploymentNetworkChange");
+        console.log(deploymentID);
+
+        filterFunc = function(model) { return (model.attributes.deployment==deploymentID); }
+        newSelect = idToSelect("node",
+                               this.model.attributes.node,
+                               this.model.foreignFields["node"],
+                               "humanReadableName",
+                               false,
+                               filterFunc);
+        this.$el.find("#field_node").html(newSelect);
+
+        filterFunc = function(model) { for (index in model.attributes.deployments) {
+                                          item=model.attributes.deployments[index];
+                                          if (item.toString()==deploymentID.toString()) return true;
+                                        };
+                                        return false;
+                                     }
+        newSelect = idToSelect("flavor",
+                               this.model.attributes.flavor,
+                               this.model.foreignFields["flavor"],
+                               "humanReadableName",
+                               false,
+                               filterFunc);
+        this.$el.find("#field_flavor").html(newSelect);
+
+        filterFunc = function(model) { for (index in xos.imageDeployments.models) {
+                                           imageDeployment = xos.imageDeployments.models[index];
+                                           if ((imageDeployment.attributes.deployment == deploymentID) && (imageDeployment.attributes.image == model.id)) {
+                                               return true;
+                                           }
+                                       }
+                                       return false;
+                                     };
+        newSelect = idToSelect("image",
+                               this.model.attributes.image,
+                               this.model.foreignFields["image"],
+                               "humanReadableName",
+                               false,
+                               filterFunc);
+        this.$el.find("#field_image").html(newSelect);
+    },
 });
 
 /* XOSItemView
@@ -920,7 +979,7 @@
    fieldName = name of field within models of collection that will be displayed
 */
 
-idToOptions = function(selectedId, collectionName, fieldName) {
+idToOptions = function(selectedId, collectionName, fieldName, filterFunc) {
     result=""
     for (index in xos[collectionName].models) {
         linkedObject = xos[collectionName].models[index];
@@ -931,6 +990,9 @@
         } else {
             selected = "";
         }
+        if ((filterFunc) && (!filterFunc(linkedObject))) {
+            continue;
+        }
         result = result + '<option value="' + linkedId + '"' + selected + '>' + linkedName + '</option>';
     }
     return result;
@@ -944,14 +1006,14 @@
    fieldName = name of field within models of collection that will be displayed
 */
 
-idToSelect = function(variable, selectedId, collectionName, fieldName, readOnly) {
+idToSelect = function(variable, selectedId, collectionName, fieldName, readOnly, filterFunc) {
     if (readOnly) {
         readOnly = " readonly";
     } else {
         readOnly = "";
     }
-    result = '<select name="' + variable + '"' + readOnly + '>' +
-             idToOptions(selectedId, collectionName, fieldName) +
+    result = '<select name="' + variable + '" id="field_' + variable + '"' + readOnly + '>' +
+             idToOptions(selectedId, collectionName, fieldName, filterFunc) +
              '</select>';
     return result;
 }