Scott Baker | 62c7eaf | 2018-05-22 15:59:26 -0700 | [diff] [blame] | 1 | |
| 2 | # Copyright 2017-present Open Networking Foundation |
| 3 | # |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | # you may not use this file except in compliance with the License. |
| 6 | # You may obtain a copy of the License at |
| 7 | # |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. |
| 15 | |
| 16 | import base64 |
| 17 | import random |
| 18 | import string |
| 19 | |
| 20 | from synchronizers.new_base.modelaccessor import OpenStackServiceInstance, Node, NetworkSlice, Flavor |
| 21 | from newopenstacksyncstep import NewOpenStackSyncStep |
| 22 | |
| 23 | from xosconfig import Config |
| 24 | from multistructlog import create_logger |
| 25 | |
| 26 | log = create_logger(Config().get('logging')) |
| 27 | |
| 28 | class SyncOpenStackServiceInstance(NewOpenStackSyncStep): |
| 29 | provides=[OpenStackServiceInstance] |
| 30 | requested_interval=0 |
| 31 | observes=OpenStackServiceInstance |
| 32 | |
| 33 | def get_connected_networks(self, instance): |
| 34 | xos_networks = [ns.network for ns in NetworkSlice.objects.filter(slice_id=instance.slice.id)] |
| 35 | return xos_networks |
| 36 | |
| 37 | def get_user_data(self, instance): |
| 38 | pubkeys=[] |
| 39 | |
| 40 | if instance.slice.creator and instance.slice.creator.public_key: |
| 41 | pubkeys.add(instance.slice.creator.public_key) |
| 42 | |
| 43 | if instance.slice.service and instance.slice.service.public_key: |
| 44 | pubkeys.add(instance.slice.service.public_key) |
| 45 | |
| 46 | userdata = '#cloud-config\n\n' |
| 47 | # userdata += 'opencloud:\n slicename: "%s"\n hostname: "%s"\n restapi_hostname: "%s"\n restapi_port: "%s"\n' % ( |
| 48 | # instance.slice.name, instance.node.name, RESTAPI_HOSTNAME, str(RESTAPI_PORT)) |
| 49 | userdata += 'ssh_authorized_keys:\n' |
| 50 | for key in pubkeys: |
| 51 | userdata += ' - %s\n' % key |
| 52 | |
| 53 | log.info("generated userdata", userdata=userdata) |
| 54 | |
| 55 | return userdata |
| 56 | |
| 57 | def sync_record(self, instance): |
| 58 | slice = instance.slice |
| 59 | if not slice.trust_domain: |
| 60 | raise Exception("Instance's slice has no trust domain") |
| 61 | |
| 62 | service = instance.slice.trust_domain.owner.leaf_model |
| 63 | #conn = self.connect_openstack_slice(slice) |
| 64 | conn = self.connect_openstack_admin(service) |
| 65 | |
| 66 | os_domain = conn.identity.find_domain(slice.trust_domain.name) |
| 67 | os_project = conn.identity.find_project(slice.name, domain_id=os_domain.id) |
| 68 | |
| 69 | os_instances = list(conn.compute.servers(name=instance.name, project_id=os_project.id)) |
| 70 | if os_instances: |
| 71 | os_instance=os_instances[0] |
| 72 | log.info("Instance already exists in openstack", instance=instance) |
| 73 | else: |
| 74 | image_name = instance.image.name |
| 75 | image_id = conn.compute.find_image(image_name).id |
| 76 | |
| 77 | if instance.flavor: |
| 78 | flavor_name = instance.flavor.name |
| 79 | else: |
| 80 | # pick a sensible default |
| 81 | flavor_name = "m1.small" |
| 82 | flavor_id = conn.compute.find_flavor(flavor_name).id |
| 83 | |
| 84 | xos_networks = self.get_connected_networks(instance) |
| 85 | networks = [] |
| 86 | for xos_network in xos_networks: |
| 87 | networks.append({"uuid": conn.network.find_network(xos_network.name).id}) |
| 88 | |
| 89 | # TODO(smbaker): No ssh keys specified |
| 90 | |
Scott Baker | 14b74fd | 2018-06-11 17:35:58 -0700 | [diff] [blame] | 91 | if not instance.node: |
Scott Baker | 818c7bd | 2018-06-13 16:28:01 -0700 | [diff] [blame^] | 92 | availability_zone = "nova" |
| 93 | else: |
| 94 | availability_zone="nova:%s" % instance.node.name |
Scott Baker | 62c7eaf | 2018-05-22 15:59:26 -0700 | [diff] [blame] | 95 | |
| 96 | log.info("Creating Instance", instance=instance, image_id=image_id, flavor_id=flavor_id, |
| 97 | availability_zone=availability_zone, |
| 98 | networks=networks) |
| 99 | |
| 100 | if not instance.admin_password: |
| 101 | instance.admin_password = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(8)) |
| 102 | instance.save(update_fields=["admin_password"]) |
| 103 | |
| 104 | user_data = self.get_user_data(instance) |
| 105 | |
| 106 | os_instance = conn.compute.create_server(name=instance.name, |
| 107 | image_id=image_id, |
| 108 | flavor_id=flavor_id, |
| 109 | project_domain_id=os_project.id, |
| 110 | availability_zone=availability_zone, |
| 111 | networks=networks, |
| 112 | config_drive=True, |
| 113 | user_data=base64.b64encode(user_data), |
| 114 | admin_password=instance.admin_password) |
| 115 | |
| 116 | if os_instance.id != instance.backend_handle: |
| 117 | instance.backend_handle = os_instance.id |
| 118 | instance.save(update_fields=["backend_handle"]) |
| 119 | |
| 120 | def delete_record(self, instance): |
| 121 | slice = instance.slice |
| 122 | if not slice.trust_domain: |
| 123 | raise Exception("Instance's slice has no trust domain") |
| 124 | |
| 125 | service = slice.trust_domain.owner.leaf_model |
| 126 | conn = self.connect_openstack_admin(service) |
| 127 | |
| 128 | os_domain = conn.identity.find_domain(slice.trust_domain.name) |
| 129 | os_project = conn.identity.find_project(slice.name, domain_id=os_domain.id) |
| 130 | |
| 131 | os_instances = list(conn.compute.servers(name=instance.name, project_id=os_project.id)) |
| 132 | if (not os_instances): |
| 133 | log.info("Instance already does not exist in openstack", instance=instance) |
| 134 | else: |
| 135 | os_instance=os_instances[0] |
| 136 | log.info("Deleting Instance", instance=instance, os_id=os_instance.id) |
| 137 | conn.compute.delete_server(os_instance.id) |