| {%- set app_label = xproto_unquote(options.app_label) -%} |
| # 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. |
| |
| |
| #!/usr/bin/env python |
| |
| # This imports and runs ../../xos-observer.py |
| |
| import importlib |
| import os |
| import sys |
| from xosconfig import Config |
| |
| config_file = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + '/{{ app_label }}_config.yaml') |
| |
| Config.init(config_file, 'synchronizer-config-schema.yaml') |
| |
| observer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"../../synchronizers/new_base") |
| sys.path.append(observer_path) |
| mod = importlib.import_module("xos-synchronizer") |
| mod.main() |
| +++ {{ app_label }}-synchronizer.py |
| {% for m in proto.messages -%} |
| {% set base_names = m.bases | map(attribute="name") -%} |
| {% if 'TenantWithContainer' in base_names | list -%} |
| # 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. |
| |
| from synchronizers.new_base.modelaccessor import * |
| from synchronizers.new_base.model_policies.model_policy_tenantwithcontainer import TenantWithContainerPolicy, LeastLoadedNodeScheduler |
| from synchronizers.new_base.exceptions import * |
| |
| class {{ m.name }}Policy(TenantWithContainerPolicy): |
| model_name = "{{ m.name }}" |
| |
| def handle_delete(self, service_instance): |
| if service_instance.instance and (not service_instance.instance.deleted): |
| all_service_instances_this_instance = {{ m.name }}.objects.filter(instance_id=service_instance.instance.id) |
| other_service_instances_this_instance = [x for x in all_service_instances_this_instance if x.id != service_instance.id] |
| if (not other_service_instances_this_instance): |
| self.logger.info("{{ m.name }} Instance %s is now unused -- deleting" % service_instance.instance) |
| self.delete_instance(service_instance, service_instance.instance) |
| else: |
| self.logger.info("{{ m.name }} Instance %s has %d other service instances attached" % (service_instance.instance, len(other_service_instances_this_instance))) |
| |
| def get_service(self, service_instance): |
| service_name = service_instance.owner.leaf_model_name |
| service_class = globals()[service_name] |
| return service_class.objects.get(id=service_instance.owner.id) |
| |
| def find_instance_for_instance_tag(self, instance_tag): |
| tags = Tag.objects.filter(name="instance_tag", value=instance_tag) |
| if tags: |
| return tags[0].content_object |
| return None |
| |
| def find_or_make_instance_for_instance_tag(self, service_instance): |
| instance_tag = self.get_instance_tag(service_instance) |
| instance = self.find_instance_for_instance_tag(instance_tag) |
| if instance: |
| if instance.no_sync: |
| # if no_sync is still set, then perhaps we failed while saving it and need to retry. |
| self.save_instance(service_instance, instance) |
| return instance |
| |
| desired_image = self.get_image(service_instance) |
| desired_flavor = self.get_flavor(service_instance) |
| |
| slice = service_instance.owner.slices.first() |
| |
| (node, parent) = LeastLoadedNodeScheduler(slice, label=None).pick() |
| |
| assert (slice is not None) |
| assert (node is not None) |
| assert (desired_image is not None) |
| assert (service_instance.creator is not None) |
| assert (node.site_deployment.deployment is not None) |
| assert (desired_image is not None) |
| |
| instance = Instance(slice=slice, |
| node=node, |
| image=desired_image, |
| creator=service_instance.creator, |
| deployment=node.site_deployment.deployment, |
| flavor=flavors[0], |
| isolation=slice.default_isolation, |
| parent=parent) |
| |
| self.save_instance(service_instance, instance) |
| |
| return instance |
| |
| def manage_container(self, service_instance): |
| if service_instance.deleted: |
| return |
| |
| if service_instance.instance: |
| # We're good. |
| return |
| |
| instance = self.find_or_make_instance_for_instance_tag(service_instance) |
| service_instance.instance = instance |
| # TODO: possible for partial failure here? |
| service_instance.save() |
| |
| def delete_instance(self, service_instance, instance): |
| # delete the `instance_tag` tags |
| tags = Tag.objects.filter(service_id=service_instance.owner.id, content_type=instance.self_content_type_id, |
| object_id=instance.id, name="instance_tag") |
| for tag in tags: |
| tag.delete() |
| |
| instance.delete() |
| |
| def save_instance(self, service_instance, instance): |
| instance.no_sync = True # prevent instance from being synced until we're done with it |
| super({{ m.name }}Policy, self).save_instance(instance) |
| |
| try: |
| if instance.isolation in ["container", "container_vm"]: |
| raise Exception("Not supported") |
| |
| instance_tag = self.get_instance_tag(service_instance) |
| if instance_tag: |
| tags = Tag.objects.filter(name="instance_tag", value=instance_tag) |
| if not tags: |
| tag = Tag(service=service_instance.owner, content_type=instance.self_content_type_id, object_id=instance.id, name="instance_tag", value=str(instance_tag)) |
| tag.save() |
| |
| instance.no_sync = False # allow the synchronizer to run now |
| super({{ m.name }}Policy, self).save_instance(instance) |
| except: |
| # need to clean up any failures here |
| raise |
| |
| def get_instance_tag(self, service_instance): |
| # TODO: Return a unique tag associated with your service instancek |
| # e.g. return '%d'%service_instance.id |
| |
| raise Exception("Not implemented") |
| |
| def get_image(self, service_instance): |
| # TODO: Return the desired image |
| |
| raise Exception("Not implemented") |
| |
| def get_flavor(self, service_instance): |
| # TODO: Return the desired flavor |
| |
| raise Exception("Not implemented") |
| |
| +++ model_policies/model_policy_{{ m.name | lower }}.py |
| {% endif %} |
| {% endfor %} |
| {% for m in proto.messages -%} |
| # 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 hashlib |
| import os |
| import socket |
| import sys |
| import base64 |
| import time |
| from urlparse import urlparse |
| from synchronizers.new_base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible |
| from synchronizers.new_base.modelaccessor import * |
| from synchronizers.new_base.ansible_helper import run_template_ssh |
| from multistructlog import create_logger |
| |
| parentdir = os.path.join(os.path.dirname(__file__),"..") |
| sys.path.insert(0,parentdir) |
| |
| log = create_logger(Config().logging) |
| |
| class Sync{{ m.name }}(SyncInstanceUsingAnsible): |
| observes={{ m.name }} |
| |
| template_name = "sync_{{ m.name | lower }}.yaml" |
| |
| def __init__(self, *args, **kwargs): |
| super(Sync{{ m.name }}, self).__init__(*args, **kwargs) |
| |
| def get_service(self, service_instance): |
| service_name = service_instance.owner.leaf_model_name |
| service_class = globals()[service_name] |
| return service_class.objects.get(id=service_instance.owner.id) |
| |
| def get_extra_attributes(self, o): |
| service = self.get_service(o) |
| |
| fields = { |
| {% for f in m.fields %} |
| "{{ f.name }}": o.{{ f.name }} |
| {%- if not loop.last %},{% endif %} |
| {% endfor %} |
| } |
| |
| # TODO: Change the above map to map data model fields into parameters in the Ansible playbook |
| # Once you have done that, drop the line below |
| |
| raise Exception("Not implemented") |
| |
| return fields |
| +++ steps/sync_{{ m.name | lower }}.py |
| # 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. |
| |
| name: {{ app_label | lower }}-synchronizer |
| accessor: |
| username: xosadmin@opencord.org |
| password: "@/opt/xos/services/{{ app_label | lower }}/credentials/xosadmin@opencord.org" |
| dependency_graph: "/opt/xos/synchronizers/{{ app_label | lower }}/model-deps" |
| steps_dir: "/opt/xos/synchronizers/{{ app_label | lower }}/steps" |
| sys_dir: "/opt/xos/synchronizers/{{ app_label | lower }}/sys" |
| model_policies_dir: "/opt/xos/synchronizers/{{ app_label | lower }}/model_policies" |
| +++ {{ app_label | lower }}_config.yaml |
| {% endfor %} |