| |
| # 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. |
| |
| import base64 |
| import random |
| import string |
| |
| from synchronizers.new_base.modelaccessor import OpenStackServiceInstance, Node, NetworkSlice, Flavor |
| from newopenstacksyncstep import NewOpenStackSyncStep |
| |
| from xosconfig import Config |
| from multistructlog import create_logger |
| |
| log = create_logger(Config().get('logging')) |
| |
| class SyncOpenStackServiceInstance(NewOpenStackSyncStep): |
| provides=[OpenStackServiceInstance] |
| requested_interval=0 |
| observes=OpenStackServiceInstance |
| |
| def get_connected_networks(self, instance): |
| xos_networks = [ns.network for ns in NetworkSlice.objects.filter(slice_id=instance.slice.id)] |
| return xos_networks |
| |
| def get_user_data(self, instance): |
| pubkeys=[] |
| |
| if instance.slice.creator and instance.slice.creator.public_key: |
| pubkeys.append(instance.slice.creator.public_key) |
| |
| if instance.slice.service and 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' % ( |
| # instance.slice.name, instance.node.name, RESTAPI_HOSTNAME, str(RESTAPI_PORT)) |
| userdata += 'ssh_authorized_keys:\n' |
| for key in pubkeys: |
| userdata += ' - %s\n' % key |
| |
| log.info("generated userdata", userdata=userdata) |
| |
| return userdata |
| |
| def get_network_for_first_slot(self, networks): |
| # Try to find a NIC with "public" visibility |
| for network in networks: |
| if (network.template.visibility == "public"): |
| return network |
| |
| # Otherwise try to find a private network |
| for network in networks: |
| if (network.template.visibility == "private") and (network.template.translation == "none") and \ |
| ("management" not in network.template.name): |
| return network |
| |
| return None |
| |
| def get_network_for_second_slot(self, networks): |
| for network in networks: |
| if (network.template.visibility == "private") and (network.template.translation == "none") and \ |
| ("management" in network.template.name): |
| return network |
| |
| return None |
| |
| def sort_networks(self, networks): |
| """ Implement our historic idiosyncratic network selection process. |
| |
| 1) Try to put a public network in the first slot. If you don't find a public network, then put a private |
| non-management network in the first slot. |
| |
| 2) Try to put the management network in the second slot. |
| |
| 3) Add all remaining networks that were now selected for #1 or #2. |
| """ |
| |
| networks = networks[:] |
| result = [] |
| |
| first_network = self.get_network_for_first_slot(networks) |
| if (first_network): |
| result.append(first_network) |
| networks.remove(first_network) |
| |
| second_network = self.get_network_for_second_slot(networks) |
| if (second_network): |
| result.append(second_network) |
| networks.remove(second_network) |
| |
| result.extend(networks) |
| |
| return result |
| |
| def sync_record(self, instance): |
| slice = instance.slice |
| if not slice.trust_domain: |
| raise Exception("Instance's slice has no trust domain") |
| |
| service = instance.slice.trust_domain.owner.leaf_model |
| #conn = self.connect_openstack_slice(slice) |
| conn = self.connect_openstack_admin(service) |
| |
| os_domain = conn.identity.find_domain(slice.trust_domain.name) |
| os_project = conn.identity.find_project(slice.name, domain_id=os_domain.id) |
| |
| os_instances = list(conn.compute.servers(name=instance.name, project_id=os_project.id)) |
| if os_instances: |
| os_instance=os_instances[0] |
| log.info("Instance already exists in openstack", instance=instance) |
| else: |
| image_name = instance.image.name |
| image_id = conn.compute.find_image(image_name).id |
| |
| if instance.flavor: |
| flavor_name = instance.flavor.name |
| else: |
| # pick a sensible default |
| flavor_name = "m1.small" |
| flavor_id = conn.compute.find_flavor(flavor_name).id |
| |
| xos_networks = self.get_connected_networks(instance) |
| xos_networks = self.sort_networks(xos_networks) |
| networks = [] |
| for xos_network in xos_networks: |
| networks.append({"uuid": conn.network.find_network(xos_network.name).id}) |
| |
| # TODO(smbaker): No ssh keys specified |
| |
| if not instance.node: |
| 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, |
| networks=networks) |
| |
| if not instance.admin_password: |
| instance.admin_password = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(8)) |
| instance.save(update_fields=["admin_password"]) |
| |
| user_data = self.get_user_data(instance) |
| |
| os_instance = conn.compute.create_server(name=instance.name, |
| image_id=image_id, |
| flavor_id=flavor_id, |
| project_domain_id=os_project.id, |
| availability_zone=availability_zone, |
| networks=networks, |
| config_drive=True, |
| user_data=base64.b64encode(user_data), |
| admin_password=instance.admin_password) |
| |
| if os_instance.id != instance.backend_handle: |
| instance.backend_handle = os_instance.id |
| instance.save(update_fields=["backend_handle"]) |
| |
| def delete_record(self, instance): |
| slice = instance.slice |
| if not slice.trust_domain: |
| raise Exception("Instance's slice has no trust domain") |
| |
| service = slice.trust_domain.owner.leaf_model |
| conn = self.connect_openstack_admin(service) |
| |
| os_domain = conn.identity.find_domain(slice.trust_domain.name) |
| os_project = conn.identity.find_project(slice.name, domain_id=os_domain.id) |
| |
| os_instances = list(conn.compute.servers(name=instance.name, project_id=os_project.id)) |
| if (not os_instances): |
| log.info("Instance already does not exist in openstack", instance=instance) |
| else: |
| os_instance=os_instances[0] |
| log.info("Deleting Instance", instance=instance, os_id=os_instance.id) |
| conn.compute.delete_server(os_instance.id) |