blob: e2f77d67f338e680b61abecbf6de1e07c558e73e [file] [log] [blame]
Matteo Scandolo35113f72017-08-08 13:05:25 -07001
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 Baker619de672016-06-20 12:49:38 -070017import os
Scott Baker037ad242018-06-28 09:38:16 -070018import time
Scott Baker66f00f22019-02-04 14:45:38 -080019from xossynchronizer.steps.syncstep import SyncStep, DeferredException
20from xossynchronizer.ansible_helper import run_template_ssh
21from xossynchronizer.modelaccessor import ExampleServiceInstance
Scott Baker037ad242018-06-28 09:38:16 -070022from xosconfig import Config
23from multistructlog import create_logger
Scott Baker619de672016-06-20 12:49:38 -070024
Scott Baker037ad242018-06-28 09:38:16 -070025log = create_logger(Config().get('logging'))
Scott Baker619de672016-06-20 12:49:38 -070026
Scott Baker037ad242018-06-28 09:38:16 -070027# TODO(smbaker): Move this to the core
28class SyncServiceInstanceWithComputeUsingAnsible(SyncStep):
29 def __init__(self, *args, **kwargs):
30 SyncStep.__init__(self, *args, **kwargs)
Srikanth Vavilapalli974c9ce2017-01-25 01:50:27 +000031
Scott Baker037ad242018-06-28 09:38:16 -070032 def defer_sync(self, o, reason):
33 log.info("defer object", object = str(o), reason = reason, **o.tologdict())
34 raise DeferredException("defer object %s due to %s" % (str(o), reason))
35
36 def get_extra_attributes(self, o):
37 # This is a place to include extra attributes that aren't part of the
38 # object itself.
39
40 return {}
41
42 def run_playbook(self, o, fields, template_name=None):
43 if not template_name:
44 template_name = self.template_name
45 tStart = time.time()
46 run_template_ssh(template_name, fields, object=o)
47 log.info("playbook execution time", time=int(time.time() - tStart), **o.tologdict())
48
49 def get_ssh_ip(self, instance):
50 for port in instance.ports.all():
51 if port.network.template and port.network.template.vtn_kind == "MANAGEMENT_LOCAL":
52 return port.ip
53
54 for port in instance.ports.all():
55 if port.network.template and port.network.template.vtn_kind == "MANAGEMENT_HOST":
56 return port.ip
57
58 return None
59
60 def get_ansible_fields(self, instance):
61 # return all of the fields that tell Ansible how to talk to the context
62 # that's setting up the container.
63
64 # Cast to the leaf_model. For OpenStackServiceInstance, this will allow us access to fields like "node"
65 instance = instance.leaf_model
66
67 node = getattr(instance, "node")
68 if not node:
69 raise Exception("Instance has no node for instance %s" % str(instance))
70
71 if not instance.slice:
72 raise Exception("Instance has no slice for instance %s" % str(instance))
73
74 if not instance.slice.service:
75 raise Exception("Instance's slice has no service for instance %s" % str(instance))
76
77 if not instance.slice.service.private_key_fn:
78 raise Exception("Instance's slice's service has no private_key_fn for instance %s" % str(instance))
79
80 key_name = instance.slice.service.private_key_fn
81 if not os.path.exists(key_name):
82 raise Exception("Node key %s does not exist for instance %s" % (key_name, str(instance)))
83
84 ssh_ip = self.get_ssh_ip(instance)
85 if not ssh_ip:
86 raise Exception("Unable to determine ssh ip for instance %s" % str(instance))
87
88 key = file(key_name).read()
89
90 fields = {"instance_name": instance.name,
91 "hostname": node.name,
92 "username": "ubuntu",
93 "ssh_ip": ssh_ip,
94 "private_key": key,
95 "instance_id": "none", # is not used for proxy-ssh ansible connections
96 }
97
98 return fields
99
100 def sync_record(self, o):
101 log.info("sync'ing object", object=str(o), **o.tologdict())
102
103 compute_service_instance = o.compute_instance
104 if not compute_service_instance:
105 self.defer_sync(o, "waiting on instance")
106 return
107
108 if not compute_service_instance.backend_handle:
109 self.defer_sync(o, "waiting on instance.backend_handle")
110 return
111
112 fields = self.get_ansible_fields(compute_service_instance)
113
114 fields["ansible_tag"] = getattr(o, "ansible_tag", o.__class__.__name__ + "_" + str(o.id))
115
116 fields.update(self.get_extra_attributes(o))
117
118 self.run_playbook(o, fields)
119
120 o.save()
121
122class SyncExampleServiceInstance(SyncServiceInstanceWithComputeUsingAnsible):
Scott Baker619de672016-06-20 12:49:38 -0700123
Scott Bakerffac7182017-07-27 15:21:30 -0700124 provides = [ExampleServiceInstance]
Scott Baker619de672016-06-20 12:49:38 -0700125
Scott Bakerffac7182017-07-27 15:21:30 -0700126 observes = ExampleServiceInstance
Scott Baker619de672016-06-20 12:49:38 -0700127
128 requested_interval = 0
129
Scott Bakerffac7182017-07-27 15:21:30 -0700130 template_name = "exampleserviceinstance_playbook.yaml"
Scott Baker619de672016-06-20 12:49:38 -0700131
Scott Baker619de672016-06-20 12:49:38 -0700132 def __init__(self, *args, **kwargs):
Scott Bakerffac7182017-07-27 15:21:30 -0700133 super(SyncExampleServiceInstance, self).__init__(*args, **kwargs)
Scott Baker619de672016-06-20 12:49:38 -0700134
Scott Baker619de672016-06-20 12:49:38 -0700135 # Gets the attributes that are used by the Ansible template but are not
136 # part of the set of default attributes.
137 def get_extra_attributes(self, o):
138 fields = {}
139 fields['tenant_message'] = o.tenant_message
Scott Baker037ad242018-06-28 09:38:16 -0700140 exampleservice = o.owner.leaf_model
Scott Baker619de672016-06-20 12:49:38 -0700141 fields['service_message'] = exampleservice.service_message
Scott Baker7a916f72017-10-25 16:52:52 -0700142
143 if o.foreground_color:
144 fields["foreground_color"] = o.foreground_color.html_code
145
146 if o.background_color:
147 fields["background_color"] = o.background_color.html_code
148
Scott Baker037ad242018-06-28 09:38:16 -0700149 images = []
Scott Baker7a916f72017-10-25 16:52:52 -0700150 for image in o.embedded_images.all():
151 images.append({"name": image.name,
152 "url": image.url})
153 fields["images"] = images
154
Scott Baker619de672016-06-20 12:49:38 -0700155 return fields
156
Scott Baker9b1a3eb2017-01-23 14:46:27 -0800157 def delete_record(self, port):
158 # Nothing needs to be done to delete an exampleservice; it goes away
159 # when the instance holding the exampleservice is deleted.
160 pass
161