CORD-3148 Allow OpenStackServiceInstance not attached to a node

Change-Id: I0c6b0bdc15b775e6bff959a18f1e4731578a5da4
diff --git a/xos/examples/make_instance.yaml b/xos/examples/make_instance.yaml
index 4d3cdf5..9bef36d 100644
--- a/xos/examples/make_instance.yaml
+++ b/xos/examples/make_instance.yaml
@@ -52,12 +52,6 @@
         name: management
         must-exist: true
 
-    compute_node:
-      type: tosca.nodes.Node
-      properties:
-        name: <insert node hostname here>
-        must-exist: true
-
     m1.small:
       type: tosca.nodes.Flavor
       properties:
@@ -129,9 +123,6 @@
         - image:
             node: image_cirros
             relationship: tosca.relationships.BelongsToOne
-        - node:
-            node: compute_node
-            relationship: tosca.relationships.BelongsToOne
         - flavor:
             node: m1.small
             relationship: tosca.relationships.BelongsToOne
diff --git a/xos/examples/make_instance_specific_node.yaml b/xos/examples/make_instance_specific_node.yaml
new file mode 100644
index 0000000..709bb22
--- /dev/null
+++ b/xos/examples/make_instance_specific_node.yaml
@@ -0,0 +1,139 @@
+---
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+  Creates a TrustDomain, Principal, Slice, Image, and then brings up an openstack
+  VM attached to the management network. The VM is pinned to a specific compute
+  node. Make sure to edit the compute_node section below and set the name of
+  the node to the hostname of your compute node.
+
+imports:
+  - custom_types/trustdomain.yaml
+  - custom_types/principal.yaml
+  - custom_types/image.yaml
+  - custom_types/flavor.yaml
+  - custom_types/network.yaml
+  - custom_types/networkslice.yaml
+  - custom_types/node.yaml
+  - custom_types/site.yaml
+  - custom_types/slice.yaml
+  - custom_types/openstackservice.yaml
+  - custom_types/openstackserviceinstance.yaml
+
+topology_template:
+  node_templates:
+    service#openstack:
+          type: tosca.nodes.OpenStackService
+          properties:
+            name: OpenStack
+            must-exist: true
+
+    mysite:
+      type: tosca.nodes.Site
+      properties:
+        name: mysite
+        must-exist: true
+
+    management_network:
+      type: tosca.nodes.Network
+      properties:
+        name: management
+        must-exist: true
+
+    compute_node:
+      type: tosca.nodes.Node
+      properties:
+        name: <insert node hostname here>
+        must-exist: true
+
+    m1.small:
+      type: tosca.nodes.Flavor
+      properties:
+        name: m1.small
+        must-exist: true
+
+    demo_trustdomain:
+      type: tosca.nodes.TrustDomain
+      properties:
+        name: "demo-trust"
+      requirements:
+        - owner:
+            node: service#openstack
+            relationship: tosca.relationships.BelongsToOne
+
+    demo_principal:
+      type: tosca.nodes.Principal
+      properties:
+        name: "demo-account"
+      requirements:
+        - trust_domain:
+            node: demo_trustdomain
+            relationship: tosca.relationships.BelongsToOne
+
+    image_cirros:
+      type: tosca.nodes.Image
+      properties:
+        name: "cirros-0.3.5"
+        container_format: "BARE"
+        disk_format: "QCOW2"
+        path: "http://download.cirros-cloud.net/0.3.5/cirros-0.3.5-x86_64-disk.img"
+
+    demo_slice:
+      type: tosca.nodes.Slice
+      properties:
+        name: "demo-slice"
+      requirements:
+        - site:
+            node: mysite
+            relationship: tosca.relationships.BelongsToOne
+        - trust_domain:
+            node: demo_trustdomain
+            relationship: tosca.relationships.BelongsToOne
+        - principal:
+            node: demo_principal
+            relationship: tosca.relationships.BelongsToOne
+
+    demo_slice_management_network:
+      type: tosca.nodes.NetworkSlice
+      requirements:
+        - network:
+            node: management_network
+            relationship: tosca.relationships.BelongsToOne
+        - slice:
+            node: demo_slice
+            relationship: tosca.relationships.BelongsToOne
+
+    demo_instance:
+      type: tosca.nodes.OpenStackServiceInstance
+      properties:
+        name: "demo-instance-pinned"
+      requirements:
+        - slice:
+            node: demo_slice
+            relationship: tosca.relationships.BelongsToOne
+        - owner:
+            node: service#openstack
+            relationship: tosca.relationships.BelongsToOne
+        - image:
+            node: image_cirros
+            relationship: tosca.relationships.BelongsToOne
+        - node:
+            node: compute_node
+            relationship: tosca.relationships.BelongsToOne
+        - flavor:
+            node: m1.small
+            relationship: tosca.relationships.BelongsToOne
diff --git a/xos/synchronizer/steps/sync_openstackserviceinstance.py b/xos/synchronizer/steps/sync_openstackserviceinstance.py
index 9db35de..5e0b058 100644
--- a/xos/synchronizer/steps/sync_openstackserviceinstance.py
+++ b/xos/synchronizer/steps/sync_openstackserviceinstance.py
@@ -89,9 +89,9 @@
             # TODO(smbaker): No ssh keys specified
 
             if not instance.node:
-                raise Exception("No node specified for instance")
-
-            availability_zone="nova:%s" % instance.node.name
+                availability_zone = "nova"
+            else:
+                availability_zone="nova:%s" % instance.node.name
 
             log.info("Creating Instance", instance=instance, image_id=image_id, flavor_id=flavor_id,
                      availability_zone=availability_zone,