blob: ac915e181e7ba4bfa29b46165c254b9c94cd5150 [file] [log] [blame]
Sapan Bhatiad60b30b2017-10-09 18:18:08 -04001{%- set app_label = xproto_unquote(options.app_label) -%}
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
17#!/usr/bin/env python
18
19# This imports and runs ../../xos-observer.py
20
21import importlib
22import os
23import sys
24from xosconfig import Config
25
26config_file = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + '/{{ app_label }}_config.yaml')
27
28Config.init(config_file, 'synchronizer-config-schema.yaml')
29
30observer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"../../synchronizers/new_base")
31sys.path.append(observer_path)
32mod = importlib.import_module("xos-synchronizer")
33mod.main()
34+++ {{ app_label }}-synchronizer.py
35{% for m in proto.messages -%}
36{% set base_names = m.bases | map(attribute="name") -%}
37{% if 'TenantWithContainer' in base_names | list -%}
38# Copyright 2017-present Open Networking Foundation
39#
40# Licensed under the Apache License, Version 2.0 (the "License");
41# you may not use this file except in compliance with the License.
42# You may obtain a copy of the License at
43#
44# http://www.apache.org/licenses/LICENSE-2.0
45#
46# Unless required by applicable law or agreed to in writing, software
47# distributed under the License is distributed on an "AS IS" BASIS,
48# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
49# See the License for the specific language governing permissions and
50# limitations under the License.
51
52from synchronizers.new_base.modelaccessor import *
53from synchronizers.new_base.model_policies.model_policy_tenantwithcontainer import TenantWithContainerPolicy, LeastLoadedNodeScheduler
54from synchronizers.new_base.exceptions import *
55
56class {{ m.name }}Policy(TenantWithContainerPolicy):
57 model_name = "{{ m.name }}"
58
59 def handle_delete(self, service_instance):
60 if service_instance.instance and (not service_instance.instance.deleted):
61 all_service_instances_this_instance = {{ m.name }}.objects.filter(instance_id=service_instance.instance.id)
62 other_service_instances_this_instance = [x for x in all_service_instances_this_instance if x.id != service_instance.id]
63 if (not other_service_instances_this_instance):
64 self.logger.info("{{ m.name }} Instance %s is now unused -- deleting" % service_instance.instance)
65 self.delete_instance(service_instance, service_instance.instance)
66 else:
67 self.logger.info("{{ m.name }} Instance %s has %d other service instances attached" % (service_instance.instance, len(other_service_instances_this_instance)))
68
69 def get_service(self, service_instance):
70 service_name = service_instance.owner.leaf_model_name
71 service_class = globals()[service_name]
72 return service_class.objects.get(id=service_instance.owner.id)
73
74 def find_instance_for_instance_tag(self, instance_tag):
75 tags = Tag.objects.filter(name="instance_tag", value=instance_tag)
76 if tags:
77 return tags[0].content_object
78 return None
79
80 def find_or_make_instance_for_instance_tag(self, service_instance):
81 instance_tag = self.get_instance_tag(service_instance)
82 instance = self.find_instance_for_instance_tag(instance_tag)
83 if instance:
84 if instance.no_sync:
85 # if no_sync is still set, then perhaps we failed while saving it and need to retry.
86 self.save_instance(service_instance, instance)
87 return instance
88
89 desired_image = self.get_image(service_instance)
90 desired_flavor = self.get_flavor(service_instance)
91
92 slice = service_instance.owner.slices.first()
93
94 (node, parent) = LeastLoadedNodeScheduler(slice, label=None).pick()
95
96 assert (slice is not None)
97 assert (node is not None)
98 assert (desired_image is not None)
99 assert (service_instance.creator is not None)
100 assert (node.site_deployment.deployment is not None)
101 assert (desired_image is not None)
102
103 instance = Instance(slice=slice,
104 node=node,
105 image=desired_image,
106 creator=service_instance.creator,
107 deployment=node.site_deployment.deployment,
108 flavor=flavors[0],
109 isolation=slice.default_isolation,
110 parent=parent)
111
112 self.save_instance(service_instance, instance)
113
114 return instance
115
116 def manage_container(self, service_instance):
117 if service_instance.deleted:
118 return
119
120 if service_instance.instance:
121 # We're good.
122 return
123
124 instance = self.find_or_make_instance_for_instance_tag(service_instance)
125 service_instance.instance = instance
126 # TODO: possible for partial failure here?
127 service_instance.save()
128
129 def delete_instance(self, service_instance, instance):
130 # delete the `instance_tag` tags
131 tags = Tag.objects.filter(service_id=service_instance.owner.id, content_type=instance.self_content_type_id,
132 object_id=instance.id, name="instance_tag")
133 for tag in tags:
134 tag.delete()
135
136 instance.delete()
137
138 def save_instance(self, service_instance, instance):
139 instance.no_sync = True # prevent instance from being synced until we're done with it
140 super({{ m.name }}Policy, self).save_instance(instance)
141
142 try:
143 if instance.isolation in ["container", "container_vm"]:
144 raise Exception("Not supported")
145
146 instance_tag = self.get_instance_tag(service_instance)
147 if instance_tag:
148 tags = Tag.objects.filter(name="instance_tag", value=instance_tag)
149 if not tags:
150 tag = Tag(service=service_instance.owner, content_type=instance.self_content_type_id, object_id=instance.id, name="instance_tag", value=str(instance_tag))
151 tag.save()
152
153 instance.no_sync = False # allow the synchronizer to run now
154 super({{ m.name }}Policy, self).save_instance(instance)
155 except:
156 # need to clean up any failures here
157 raise
158
159 def get_instance_tag(self, service_instance):
160 # TODO: Return a unique tag associated with your service instancek
161 # e.g. return '%d'%service_instance.id
162
163 raise Exception("Not implemented")
164
165 def get_image(self, service_instance):
166 # TODO: Return the desired image
167
168 raise Exception("Not implemented")
169
170 def get_flavor(self, service_instance):
171 # TODO: Return the desired flavor
172
173 raise Exception("Not implemented")
174
175+++ model_policies/model_policy_{{ m.name | lower }}.py
176{% endif %}
177{% endfor %}
178{% for m in proto.messages -%}
179# Copyright 2017-present Open Networking Foundation
180#
181# Licensed under the Apache License, Version 2.0 (the "License");
182# you may not use this file except in compliance with the License.
183# You may obtain a copy of the License at
184#
185# http://www.apache.org/licenses/LICENSE-2.0
186#
187# Unless required by applicable law or agreed to in writing, software
188# distributed under the License is distributed on an "AS IS" BASIS,
189# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190# See the License for the specific language governing permissions and
191# limitations under the License.
192
193
194import hashlib
195import os
196import socket
197import sys
198import base64
199import time
200from urlparse import urlparse
201from synchronizers.new_base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
202from synchronizers.new_base.modelaccessor import *
203from synchronizers.new_base.ansible_helper import run_template_ssh
204from multistructlog import create_logger
205
206parentdir = os.path.join(os.path.dirname(__file__),"..")
207sys.path.insert(0,parentdir)
208
209log = create_logger(Config().logging)
210
211class Sync{{ m.name }}(SyncInstanceUsingAnsible):
212 observes={{ m.name }}
213
214 template_name = "sync_{{ m.name | lower }}.yaml"
215
216 def __init__(self, *args, **kwargs):
217 super(Sync{{ m.name }}, self).__init__(*args, **kwargs)
218
219 def get_service(self, service_instance):
220 service_name = service_instance.owner.leaf_model_name
221 service_class = globals()[service_name]
222 return service_class.objects.get(id=service_instance.owner.id)
223
224 def get_extra_attributes(self, o):
225 service = self.get_service(o)
226
227 fields = {
228 {% for f in m.fields %}
229 "{{ f.name }}": o.{{ f.name }}
230 {%- if not loop.last %},{% endif %}
231 {% endfor %}
232 }
233
234 # TODO: Change the above map to map data model fields into parameters in the Ansible playbook
235 # Once you have done that, drop the line below
236
237 raise Exception("Not implemented")
238
239 return fields
240+++ steps/sync_{{ m.name | lower }}.py
241# Copyright 2017-present Open Networking Foundation
242#
243# Licensed under the Apache License, Version 2.0 (the "License");
244# you may not use this file except in compliance with the License.
245# You may obtain a copy of the License at
246#
247# http://www.apache.org/licenses/LICENSE-2.0
248#
249# Unless required by applicable law or agreed to in writing, software
250# distributed under the License is distributed on an "AS IS" BASIS,
251# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
252# See the License for the specific language governing permissions and
253# limitations under the License.
254
255name: {{ app_label | lower }}-synchronizer
256accessor:
257 username: xosadmin@opencord.org
258 password: "@/opt/xos/services/{{ app_label | lower }}/credentials/xosadmin@opencord.org"
259dependency_graph: "/opt/xos/synchronizers/{{ app_label | lower }}/model-deps"
260steps_dir: "/opt/xos/synchronizers/{{ app_label | lower }}/steps"
261sys_dir: "/opt/xos/synchronizers/{{ app_label | lower }}/sys"
262model_policies_dir: "/opt/xos/synchronizers/{{ app_label | lower }}/model_policies"
263+++ {{ app_label | lower }}_config.yaml
264{% endfor %}