CORD-3183 Fix synchronization of instances that use keys
Change-Id: I8910c560e762e011341595212b26d81c23734313
diff --git a/xos/examples/README.md b/xos/examples/README.md
new file mode 100644
index 0000000..c2a3e94
--- /dev/null
+++ b/xos/examples/README.md
@@ -0,0 +1,12 @@
+The Cirros examples require use of a password to login. The default password is "cubswin:)"
+
+The Ubuntu examples require use of an ssh key to login. Make sure to first set a public key for the
+user account that you're going to use to create the instance. For most demos, this will likely be
+the admin@opencord.org account. You can set the SSH key using the GUI, or you can set it by using
+xossh:
+
+```python
+u=User.objects.first()
+u.public_key = "the_contents_of_my_public_key"
+u.save()
+```
diff --git a/xos/examples/make_instance.yaml b/xos/examples/make_instance_cirros.yaml
similarity index 100%
rename from xos/examples/make_instance.yaml
rename to xos/examples/make_instance_cirros.yaml
diff --git a/xos/examples/make_instance_specific_node.yaml b/xos/examples/make_instance_cirros_specific_node.yaml
similarity index 100%
rename from xos/examples/make_instance_specific_node.yaml
rename to xos/examples/make_instance_cirros_specific_node.yaml
diff --git a/xos/examples/make_instance_ubuntu.yaml b/xos/examples/make_instance_ubuntu.yaml
new file mode 100644
index 0000000..9b16659
--- /dev/null
+++ b/xos/examples/make_instance_ubuntu.yaml
@@ -0,0 +1,128 @@
+---
+# 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.
+
+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
+
+ 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_ubuntu:
+ type: tosca.nodes.Image
+ properties:
+ name: "trusty-server-cloudimg"
+ container_format: "BARE"
+ disk_format: "QCOW2"
+ path: "http://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.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-ub"
+ requirements:
+ - slice:
+ node: demo_slice
+ relationship: tosca.relationships.BelongsToOne
+ - owner:
+ node: service#openstack
+ relationship: tosca.relationships.BelongsToOne
+ - image:
+ node: image_ubuntu
+ 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 5e0b058..04f71b9 100644
--- a/xos/synchronizer/steps/sync_openstackserviceinstance.py
+++ b/xos/synchronizer/steps/sync_openstackserviceinstance.py
@@ -38,10 +38,10 @@
pubkeys=[]
if instance.slice.creator and instance.slice.creator.public_key:
- pubkeys.add(instance.slice.creator.public_key)
+ pubkeys.append(instance.slice.creator.public_key)
if instance.slice.service and instance.slice.service.public_key:
- pubkeys.add(instance.slice.service.public_key)
+ pubkeys.append(instance.slice.service.public_key)
userdata = '#cloud-config\n\n'
# userdata += 'opencloud:\n slicename: "%s"\n hostname: "%s"\n restapi_hostname: "%s"\n restapi_port: "%s"\n' % (
diff --git a/xos/synchronizer/tests/test_sync_openstackserviceinstance.py b/xos/synchronizer/tests/test_sync_openstackserviceinstance.py
index d4fb9b5..8f0ad3a 100644
--- a/xos/synchronizer/tests/test_sync_openstackserviceinstance.py
+++ b/xos/synchronizer/tests/test_sync_openstackserviceinstance.py
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import json
+import base64
import os
import sys
import unittest
@@ -146,6 +146,46 @@
user_data=ANY)
self.assertEqual(xos_instance.backend_handle, "1234")
+ def test_sync_record_create_noexist_with_keys(self):
+ fakeconn = MagicMock()
+ with patch.object(self.step_class, "connect_openstack_admin") as fake_connect_openstack_admin:
+ fake_connect_openstack_admin.return_value = fakeconn
+
+ xos_instance = OpenStackServiceInstance(name="test-instance", slice=self.slice, image=self.image,
+ node=self.node, flavor=self.flavor)
+
+ admin_user = User(email="test_user@test.com", public_key="key1")
+ self.slice.creator = admin_user
+
+ owning_service = Service(name="test_service", public_key="key2")
+ self.slice.service = owning_service
+
+ step = self.step_class()
+ fakeconn.compute.servers.return_value = []
+ fakeconn.identity.find_project.return_value = MagicMock(id=self.slice.backend_handle)
+ fakeconn.identity.find_domain.return_value = MagicMock(id=self.trust_domain.backend_handle)
+ fakeconn.compute.find_image.return_value = MagicMock(id=self.image.backend_handle)
+ fakeconn.compute.find_flavor.return_value = MagicMock(id=self.flavor.backend_handle)
+
+ os_instance = MagicMock()
+ os_instance.id = "1234"
+ fakeconn.compute.create_server.return_value = os_instance
+
+ step.sync_record(xos_instance)
+
+ expected_userdata = base64.b64encode('#cloud-config\n\nssh_authorized_keys:\n - key1\n - key2\n')
+
+ fakeconn.compute.create_server.assert_called_with(admin_password=ANY,
+ availability_zone="nova:test-node",
+ config_drive=True,
+ flavor_id=self.flavor.backend_handle,
+ image_id=self.image.backend_handle,
+ name=xos_instance.name,
+ networks=[],
+ project_domain_id=self.slice.backend_handle,
+ user_data=expected_userdata)
+ self.assertEqual(xos_instance.backend_handle, "1234")
+
def test_sync_record_create_exists(self):
fakeconn = MagicMock()
with patch.object(self.step_class, "connect_openstack_admin") as fake_connect_openstack_admin: