Matteo Scandolo | 35113f7 | 2017-08-08 13:05:25 -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 | |
Scott Baker | 619de67 | 2016-06-20 12:49:38 -0700 | [diff] [blame] | 17 | import os |
| 18 | import sys |
Scott Baker | 037ad24 | 2018-06-28 09:38:16 -0700 | [diff] [blame] | 19 | import time |
| 20 | from synchronizers.new_base.syncstep import SyncStep, DeferredException |
| 21 | from synchronizers.new_base.ansible_helper import run_template_ssh |
| 22 | from synchronizers.new_base.modelaccessor import ExampleServiceInstance |
| 23 | from xosconfig import Config |
| 24 | from multistructlog import create_logger |
Scott Baker | 619de67 | 2016-06-20 12:49:38 -0700 | [diff] [blame] | 25 | |
Scott Baker | 037ad24 | 2018-06-28 09:38:16 -0700 | [diff] [blame] | 26 | log = create_logger(Config().get('logging')) |
Scott Baker | 619de67 | 2016-06-20 12:49:38 -0700 | [diff] [blame] | 27 | |
Scott Baker | 037ad24 | 2018-06-28 09:38:16 -0700 | [diff] [blame] | 28 | # TODO(smbaker): Move this to the core |
| 29 | class SyncServiceInstanceWithComputeUsingAnsible(SyncStep): |
| 30 | def __init__(self, *args, **kwargs): |
| 31 | SyncStep.__init__(self, *args, **kwargs) |
Srikanth Vavilapalli | 974c9ce | 2017-01-25 01:50:27 +0000 | [diff] [blame] | 32 | |
Scott Baker | 037ad24 | 2018-06-28 09:38:16 -0700 | [diff] [blame] | 33 | def defer_sync(self, o, reason): |
| 34 | log.info("defer object", object = str(o), reason = reason, **o.tologdict()) |
| 35 | raise DeferredException("defer object %s due to %s" % (str(o), reason)) |
| 36 | |
| 37 | def get_extra_attributes(self, o): |
| 38 | # This is a place to include extra attributes that aren't part of the |
| 39 | # object itself. |
| 40 | |
| 41 | return {} |
| 42 | |
| 43 | def run_playbook(self, o, fields, template_name=None): |
| 44 | if not template_name: |
| 45 | template_name = self.template_name |
| 46 | tStart = time.time() |
| 47 | run_template_ssh(template_name, fields, object=o) |
| 48 | log.info("playbook execution time", time=int(time.time() - tStart), **o.tologdict()) |
| 49 | |
| 50 | def get_ssh_ip(self, instance): |
| 51 | for port in instance.ports.all(): |
| 52 | if port.network.template and port.network.template.vtn_kind == "MANAGEMENT_LOCAL": |
| 53 | return port.ip |
| 54 | |
| 55 | for port in instance.ports.all(): |
| 56 | if port.network.template and port.network.template.vtn_kind == "MANAGEMENT_HOST": |
| 57 | return port.ip |
| 58 | |
| 59 | return None |
| 60 | |
| 61 | def get_ansible_fields(self, instance): |
| 62 | # return all of the fields that tell Ansible how to talk to the context |
| 63 | # that's setting up the container. |
| 64 | |
| 65 | # Cast to the leaf_model. For OpenStackServiceInstance, this will allow us access to fields like "node" |
| 66 | instance = instance.leaf_model |
| 67 | |
| 68 | node = getattr(instance, "node") |
| 69 | if not node: |
| 70 | raise Exception("Instance has no node for instance %s" % str(instance)) |
| 71 | |
| 72 | if not instance.slice: |
| 73 | raise Exception("Instance has no slice for instance %s" % str(instance)) |
| 74 | |
| 75 | if not instance.slice.service: |
| 76 | raise Exception("Instance's slice has no service for instance %s" % str(instance)) |
| 77 | |
| 78 | if not instance.slice.service.private_key_fn: |
| 79 | raise Exception("Instance's slice's service has no private_key_fn for instance %s" % str(instance)) |
| 80 | |
| 81 | key_name = instance.slice.service.private_key_fn |
| 82 | if not os.path.exists(key_name): |
| 83 | raise Exception("Node key %s does not exist for instance %s" % (key_name, str(instance))) |
| 84 | |
| 85 | ssh_ip = self.get_ssh_ip(instance) |
| 86 | if not ssh_ip: |
| 87 | raise Exception("Unable to determine ssh ip for instance %s" % str(instance)) |
| 88 | |
| 89 | key = file(key_name).read() |
| 90 | |
| 91 | fields = {"instance_name": instance.name, |
| 92 | "hostname": node.name, |
| 93 | "username": "ubuntu", |
| 94 | "ssh_ip": ssh_ip, |
| 95 | "private_key": key, |
| 96 | "instance_id": "none", # is not used for proxy-ssh ansible connections |
| 97 | } |
| 98 | |
| 99 | return fields |
| 100 | |
| 101 | def sync_record(self, o): |
| 102 | log.info("sync'ing object", object=str(o), **o.tologdict()) |
| 103 | |
| 104 | compute_service_instance = o.compute_instance |
| 105 | if not compute_service_instance: |
| 106 | self.defer_sync(o, "waiting on instance") |
| 107 | return |
| 108 | |
| 109 | if not compute_service_instance.backend_handle: |
| 110 | self.defer_sync(o, "waiting on instance.backend_handle") |
| 111 | return |
| 112 | |
| 113 | fields = self.get_ansible_fields(compute_service_instance) |
| 114 | |
| 115 | fields["ansible_tag"] = getattr(o, "ansible_tag", o.__class__.__name__ + "_" + str(o.id)) |
| 116 | |
| 117 | fields.update(self.get_extra_attributes(o)) |
| 118 | |
| 119 | self.run_playbook(o, fields) |
| 120 | |
| 121 | o.save() |
| 122 | |
| 123 | class SyncExampleServiceInstance(SyncServiceInstanceWithComputeUsingAnsible): |
Scott Baker | 619de67 | 2016-06-20 12:49:38 -0700 | [diff] [blame] | 124 | |
Scott Baker | ffac718 | 2017-07-27 15:21:30 -0700 | [diff] [blame] | 125 | provides = [ExampleServiceInstance] |
Scott Baker | 619de67 | 2016-06-20 12:49:38 -0700 | [diff] [blame] | 126 | |
Scott Baker | ffac718 | 2017-07-27 15:21:30 -0700 | [diff] [blame] | 127 | observes = ExampleServiceInstance |
Scott Baker | 619de67 | 2016-06-20 12:49:38 -0700 | [diff] [blame] | 128 | |
| 129 | requested_interval = 0 |
| 130 | |
Scott Baker | ffac718 | 2017-07-27 15:21:30 -0700 | [diff] [blame] | 131 | template_name = "exampleserviceinstance_playbook.yaml" |
Scott Baker | 619de67 | 2016-06-20 12:49:38 -0700 | [diff] [blame] | 132 | |
Scott Baker | 619de67 | 2016-06-20 12:49:38 -0700 | [diff] [blame] | 133 | def __init__(self, *args, **kwargs): |
Scott Baker | ffac718 | 2017-07-27 15:21:30 -0700 | [diff] [blame] | 134 | super(SyncExampleServiceInstance, self).__init__(*args, **kwargs) |
Scott Baker | 619de67 | 2016-06-20 12:49:38 -0700 | [diff] [blame] | 135 | |
Scott Baker | 619de67 | 2016-06-20 12:49:38 -0700 | [diff] [blame] | 136 | # Gets the attributes that are used by the Ansible template but are not |
| 137 | # part of the set of default attributes. |
| 138 | def get_extra_attributes(self, o): |
| 139 | fields = {} |
| 140 | fields['tenant_message'] = o.tenant_message |
Scott Baker | 037ad24 | 2018-06-28 09:38:16 -0700 | [diff] [blame] | 141 | exampleservice = o.owner.leaf_model |
Scott Baker | 619de67 | 2016-06-20 12:49:38 -0700 | [diff] [blame] | 142 | fields['service_message'] = exampleservice.service_message |
Scott Baker | 7a916f7 | 2017-10-25 16:52:52 -0700 | [diff] [blame] | 143 | |
| 144 | if o.foreground_color: |
| 145 | fields["foreground_color"] = o.foreground_color.html_code |
| 146 | |
| 147 | if o.background_color: |
| 148 | fields["background_color"] = o.background_color.html_code |
| 149 | |
Scott Baker | 037ad24 | 2018-06-28 09:38:16 -0700 | [diff] [blame] | 150 | images = [] |
Scott Baker | 7a916f7 | 2017-10-25 16:52:52 -0700 | [diff] [blame] | 151 | for image in o.embedded_images.all(): |
| 152 | images.append({"name": image.name, |
| 153 | "url": image.url}) |
| 154 | fields["images"] = images |
| 155 | |
Scott Baker | 619de67 | 2016-06-20 12:49:38 -0700 | [diff] [blame] | 156 | return fields |
| 157 | |
Scott Baker | 9b1a3eb | 2017-01-23 14:46:27 -0800 | [diff] [blame] | 158 | def delete_record(self, port): |
| 159 | # Nothing needs to be done to delete an exampleservice; it goes away |
| 160 | # when the instance holding the exampleservice is deleted. |
| 161 | pass |
| 162 | |