Merge branch 'master' of github.com:open-cloud/xos
diff --git a/xos/configurations/cord-pod/cord-vtn-vsg.yaml b/xos/configurations/cord-pod/cord-vtn-vsg.yaml
index 994ab3e..c06e237 100644
--- a/xos/configurations/cord-pod/cord-vtn-vsg.yaml
+++ b/xos/configurations/cord-pod/cord-vtn-vsg.yaml
@@ -37,6 +37,7 @@
           wan_container_gateway_ip: 10.168.0.1
           wan_container_gateway_mac: 02:42:0a:a8:00:01
           wan_container_netbits: 24
+#          node_label: label_vsg
       artifacts:
           pubkey: /opt/xos/synchronizers/vcpe/vcpe_public_key
 
diff --git a/xos/configurations/cord-pod/make-vtn-networkconfig-json.sh b/xos/configurations/cord-pod/make-vtn-networkconfig-json.sh
index 26e4066..a1e5137 100755
--- a/xos/configurations/cord-pod/make-vtn-networkconfig-json.sh
+++ b/xos/configurations/cord-pod/make-vtn-networkconfig-json.sh
@@ -69,8 +69,8 @@
                 ]
             }
         },
-        "org.onosproject.openstackswitching" : {
-            "openstackswitching" : {
+        "org.onosproject.openstackinterface" : {
+            "openstackinterface" : {
                  "do_not_push_flows" : "true",
                  "neutron_server" : "$NEUTRON_URL/v2.0/",
                  "keystone_server" : "$OS_AUTH_URL/",
diff --git a/xos/configurations/cord/cord.yaml b/xos/configurations/cord/cord.yaml
index aa2673b..4dea365 100644
--- a/xos/configurations/cord/cord.yaml
+++ b/xos/configurations/cord/cord.yaml
@@ -41,6 +41,7 @@
           backend_network_label: hpc_client
           public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
           private_key_fn: /opt/xos/synchronizers/vcpe/vcpe_private_key
+#          node_label: label_vsg
       artifacts:
           pubkey: /opt/xos/synchronizers/vcpe/vcpe_public_key
 
diff --git a/xos/configurations/cord/make-vtn-networkconfig-json.sh b/xos/configurations/cord/make-vtn-networkconfig-json.sh
index 2cccd65..77b855d 100644
--- a/xos/configurations/cord/make-vtn-networkconfig-json.sh
+++ b/xos/configurations/cord/make-vtn-networkconfig-json.sh
@@ -74,8 +74,8 @@
                 ]
             }
         },
-        "org.onosproject.openstackswitching" : {
-            "openstackswitching" : {
+        "org.onosproject.openstackinterface" : {
+            "openstackinterface" : {
                  "do_not_push_flows" : "true",
                  "neutron_server" : "http://$NEUTRONIP:9696/v2.0/",
                  "keystone_server" : "http://$KEYSTONEIP:5000/v2.0/",
diff --git a/xos/configurations/vtn/cord-vtn-vsg.yaml b/xos/configurations/vtn/cord-vtn-vsg.yaml
index c4332e2..c228a80 100644
--- a/xos/configurations/vtn/cord-vtn-vsg.yaml
+++ b/xos/configurations/vtn/cord-vtn-vsg.yaml
@@ -38,6 +38,7 @@
           wan_container_gateway_mac: 00:8c:fa:5b:09:d8
           wan_container_netbits: 24
           dns_servers: 8.8.8.8, 8.8.4.4
+#          node_label: label_vsg
       artifacts:
           pubkey: /opt/xos/synchronizers/vcpe/vcpe_public_key
 
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 51f9b3c..ee28cf6 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -437,12 +437,21 @@
 class LeastLoadedNodeScheduler(Scheduler):
     # This scheduler always return the node with the fewest number of instances.
 
-    def __init__(self, slice):
+    def __init__(self, slice, label=None):
         super(LeastLoadedNodeScheduler, self).__init__(slice)
+        self.label = label
 
     def pick(self):
         from core.models import Node
-        nodes = list(Node.objects.all())
+        nodes = Node.objects.all()
+
+        if self.label:
+           nodes = nodes.filter(labels__name=self.label)
+
+        nodes = list(nodes)
+
+        if not nodes:
+            raise Exception("LeastLoadedNodeScheduler: No suitable nodes to pick from")
 
         # TODO: logic to filter nodes by which nodes are up, and which
         #   nodes the slice can instantiate on.
diff --git a/xos/services/cord/admin.py b/xos/services/cord/admin.py
index 4ec4c6f..56e46ae 100644
--- a/xos/services/cord/admin.py
+++ b/xos/services/cord/admin.py
@@ -108,6 +108,7 @@
     wan_container_gateway_mac = forms.CharField(required=False)
     wan_container_netbits = forms.CharField(required=False)
     dns_servers = forms.CharField(required=False)
+    node_label = forms.CharField(required=False)
 
     def __init__(self,*args,**kwargs):
         super (VSGServiceForm,self ).__init__(*args,**kwargs)
@@ -121,6 +122,7 @@
             self.fields['wan_container_gateway_mac'].initial = self.instance.wan_container_gateway_mac
             self.fields['wan_container_netbits'].initial = self.instance.wan_container_netbits
             self.fields['dns_servers'].initial = self.instance.dns_servers
+            self.fields['node_label'].initial = self.instance.node_label
 
     def save(self, commit=True):
         self.instance.bbs_api_hostname = self.cleaned_data.get("bbs_api_hostname")
@@ -132,6 +134,7 @@
         self.instance.wan_container_gateway_mac = self.cleaned_data.get("wan_container_gateway_mac")
         self.instance.wan_container_netbits = self.cleaned_data.get("wan_container_netbits")
         self.instance.dns_servers = self.cleaned_data.get("dns_servers")
+        self.instance.node_label = self.cleaned_data.get("node_label")
         return super(VSGServiceForm, self).save(commit=commit)
 
     class Meta:
@@ -143,7 +146,7 @@
     verbose_name_plural = "vSG Service"
     list_display = ("backend_status_icon", "name", "enabled")
     list_display_links = ('backend_status_icon', 'name', )
-    fieldsets = [(None,             {'fields': ['backend_status_text', 'name','enabled','versionNumber', 'description', "view_url", "icon_url", "service_specific_attribute",],
+    fieldsets = [(None,             {'fields': ['backend_status_text', 'name','enabled','versionNumber', 'description', "view_url", "icon_url", "service_specific_attribute", "node_label"],
                                      'classes':['suit-tab suit-tab-general']}),
                  ("backend config", {'fields': [ "backend_network_label", "bbs_api_hostname", "bbs_api_port", "bbs_server", "bbs_slice"],
                                      'classes':['suit-tab suit-tab-backend']}),
diff --git a/xos/services/cord/models.py b/xos/services/cord/models.py
index 9ff2dcb..b7f25af 100644
--- a/xos/services/cord/models.py
+++ b/xos/services/cord/models.py
@@ -369,7 +369,8 @@
                           ("wan_container_gateway_ip", ""),
                           ("wan_container_gateway_mac", ""),
                           ("wan_container_netbits", "24"),
-                          ("dns_servers", "8.8.8.8") )
+                          ("dns_servers", "8.8.8.8"),
+                          ("node_label", None) )
 
     def __init__(self, *args, **kwargs):
         super(VSGService, self).__init__(*args, **kwargs)
@@ -644,6 +645,9 @@
         slice = self.provider_service.slices.all()[0]
         return slice
 
+    def get_vsg_service(self):
+        return VSGService.get_service_objects().get(id=self.provider_service.id)
+
     def find_instance_for_s_tag(self, s_tag):
         #s_tags = STagBlock.objects.find(s_s_tag)
         #if s_tags:
@@ -669,7 +673,7 @@
         if slice.default_isolation == "container_vm":
             (node, parent) = ContainerVmScheduler(slice).pick()
         else:
-            (node, parent) = LeastLoadedNodeScheduler(slice).pick()
+            (node, parent) = LeastLoadedNodeScheduler(slice, label=self.get_vsg_service().node_label).pick()
 
         instance = Instance(slice = slice,
                         node = node,
diff --git a/xos/tosca/README.md b/xos/tosca/README.md
new file mode 100644
index 0000000..98f0aaf
--- /dev/null
+++ b/xos/tosca/README.md
@@ -0,0 +1,13 @@
+## TOSCA Interface Definition
+
+This directory implements a TOSCA interface for XOS,
+which can be extended to include specifications for
+service models added to XOS. The directory is organized
+as follows:
+
+ * custom_types -- Defines schema for XOS-specific models.
+   * `.m4` files are source.
+   * `.yaml` files are generated.
+ * definitions -- Defines schema for TOSCA's base models.
+ * resources -- Translates TOSCA specification to Django API.
+ * sample -- Example TOSCA models.
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index dd96d03..9773822 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -230,6 +230,9 @@
             dns_servers:
                 type: string
                 required: false
+            node_label:
+                type: string
+                required: false
 
     tosca.nodes.VBNGService:
         derived_from: tosca.nodes.Root
@@ -859,6 +862,10 @@
         derived_from: tosca.relationships.Root
         valid_target_types: [ tosca.capabilities.xos.DashboardView ]
 
+    tosca.relationships.HasLabel:
+        derived_from: tosca.relationships.Root
+        valid_target_types: [ tosca.capabilities.xos.NodeLabel ]
+
     tosca.capabilities.xos.Service:
         derived_from: tosca.capabilities.Root
         description: An XOS Service
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index cf930b3..21e8b8b 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -332,6 +332,9 @@
             dns_servers:
                 type: string
                 required: false
+            node_label:
+                type: string
+                required: false
 
     tosca.nodes.VBNGService:
         derived_from: tosca.nodes.Root
@@ -1171,6 +1174,10 @@
         derived_from: tosca.relationships.Root
         valid_target_types: [ tosca.capabilities.xos.DashboardView ]
 
+    tosca.relationships.HasLabel:
+        derived_from: tosca.relationships.Root
+        valid_target_types: [ tosca.capabilities.xos.NodeLabel ]
+
     tosca.capabilities.xos.Service:
         derived_from: tosca.capabilities.Root
         description: An XOS Service
diff --git a/xos/tosca/resources/node.py b/xos/tosca/resources/node.py
index 59b915b..b7f426d 100644
--- a/xos/tosca/resources/node.py
+++ b/xos/tosca/resources/node.py
@@ -5,7 +5,7 @@
 sys.path.append("/opt/tosca")
 from translator.toscalib.tosca_template import ToscaTemplate
 
-from core.models import Node, Site, Deployment, SiteDeployment
+from core.models import Node, NodeLabel, Site, Deployment, SiteDeployment
 
 from xosresource import XOSResource
 
@@ -30,6 +30,12 @@
                 siteDeployment = self.get_xos_object(SiteDeployment, site=site, deployment=deployment, throw_exception=True)
                 args["site_deployment"] = siteDeployment
 
+        labels=[]
+        for label_name in self.get_requirements("tosca.relationships.HasLabel"):
+            labels.append(self.get_xos_object(NodeLabel, name=label_name))
+        if labels:
+            args["labels"] = labels
+
         return args
 
     def create(self):
diff --git a/xos/tosca/resources/vcpeservice.py b/xos/tosca/resources/vcpeservice.py
index 5c7b2a7..2a6a56d 100644
--- a/xos/tosca/resources/vcpeservice.py
+++ b/xos/tosca/resources/vcpeservice.py
@@ -15,5 +15,5 @@
     copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key",
                     "private_key_fn", "versionNumber", "backend_network_label",
                     "wan_container_gateway_ip", "wan_container_gateway_mac",
-                    "wan_container_netbits", "dns_servers"]
+                    "wan_container_netbits", "dns_servers", "node_label"]