Initial prototype of synchronizer
Change-Id: I45de04debd89e7007d7985728d51aa5d03178675
diff --git a/xos/globalxos-onboard.yaml b/xos/globalxos-onboard.yaml
new file mode 100644
index 0000000..fdbf878
--- /dev/null
+++ b/xos/globalxos-onboard.yaml
@@ -0,0 +1,16 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Onboard the GlobalXOS service
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+ servicecontroller#globalxos:
+ type: tosca.nodes.ServiceController
+ properties:
+ base_url: file:///opt/xos_services/globalxos/xos/
+ synchronizer: synchronizer/manifest
+ synchronizer_run: globalxos-synchronizer.py
+
diff --git a/xos/make_synchronizer_manifest.sh b/xos/make_synchronizer_manifest.sh
new file mode 100755
index 0000000..4058982
--- /dev/null
+++ b/xos/make_synchronizer_manifest.sh
@@ -0,0 +1,2 @@
+#! /bin/bash
+find synchronizer -type f | cut -b 14- > synchronizer/manifest
diff --git a/xos/synchronizer/__init__.py b/xos/synchronizer/__init__.py
new file mode 100644
index 0000000..e56cd39
--- /dev/null
+++ b/xos/synchronizer/__init__.py
@@ -0,0 +1,36 @@
+from xos.config import Config
+
+try:
+ observer_disabled = Config().observer_disabled
+except:
+ observer_disabled = False
+
+def EnableObserver(x):
+ """ used for manage.py --noobserver """
+ global observer_disabled
+ observer_disabled = not x
+
+print_once = True
+
+def notify_observer(model=None, delete=False, pk=None, model_dict={}):
+ if (observer_disabled):
+ global print_once
+ if (print_once):
+ print "The observer is disabled"
+ print_once = False
+ return
+
+ try:
+ from .event_manager import EventSender
+ if (model and delete):
+ if hasattr(model,"__name__"):
+ modelName = model.__name__
+ else:
+ modelName = model.__class__.__name__
+ EventSender().fire(delete_flag = delete, model = modelName, pk = pk, model_dict=model_dict)
+ else:
+ EventSender().fire()
+ except Exception,e:
+ print "Exception in Observer. This should not disrupt the front end. %s"%str(e)
+
+
diff --git a/xos/synchronizer/globalxos-synchronizer.py b/xos/synchronizer/globalxos-synchronizer.py
new file mode 100644
index 0000000..90d2c98
--- /dev/null
+++ b/xos/synchronizer/globalxos-synchronizer.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+
+# Runs the standard XOS synchronizer
+
+import importlib
+import os
+import sys
+
+synchronizer_path = os.path.join(os.path.dirname(
+ os.path.realpath(__file__)), "../../synchronizers/base")
+sys.path.append(synchronizer_path)
+mod = importlib.import_module("xos-synchronizer")
+mod.main()
+
diff --git a/xos/synchronizer/globalxos_synchronizer_config b/xos/synchronizer/globalxos_synchronizer_config
new file mode 100644
index 0000000..f06031e
--- /dev/null
+++ b/xos/synchronizer/globalxos_synchronizer_config
@@ -0,0 +1,29 @@
+[db]
+name=xos
+user=postgres
+password=password
+host=localhost
+port=5432
+
+[api]
+nova_enabled=True
+
+[nova]
+ca_ssl_cert=/etc/ssl/certs/ca-certificates.crt
+
+[observer]
+name=globalxos
+dependency_graph=/opt/xos/model-deps
+steps_dir=/opt/xos/synchronizers/globalxos/steps
+sys_dir=/opt/xos/synchronizers/globalxos/sys
+model_policies_dir=/opt/xos/synchronizers/globalxos/model_policies
+logfile=/var/log/xos_backend.log
+save_ansible_output=True
+backoff_disabled=True
+pretend=False
+images_directory=/opt/xos/images
+
+
+[feefie]
+client_id='vicci_dev_central'
+user_id='pl'
diff --git a/xos/synchronizer/manifest b/xos/synchronizer/manifest
new file mode 100644
index 0000000..051a93b
--- /dev/null
+++ b/xos/synchronizer/manifest
@@ -0,0 +1,26 @@
+__init__.py
+globalxos-synchronizer.py
+globalxos_synchronizer_config
+manifest
+model_policies/__init__.py
+model_policies/model_policy_Controller.py
+model_policies/model_policy_ControllerSlice.py
+model_policies/model_policy_ControllerUser.py
+model_policies/model_policy_Slice.py
+model_policies/model_policy_SlicePrivilege.py
+model_policies/model_policy_Sliver.py
+model_policies/model_policy_User.py
+run.sh
+steps/__init__.py
+steps/sync_controller_slice_privileges.py
+steps/sync_controller_slice_privileges.yaml
+steps/sync_controller_slices.py
+steps/sync_controller_slices.yaml
+steps/sync_controller_users.py
+steps/sync_controller_users.yaml
+steps/sync_images.py
+steps/sync_roles.py
+steps/sync_sites.py
+templates/slice.yaml.j2
+templates/slice_privileges.yaml.j2
+templates/user.yaml.j2
diff --git a/xos/synchronizer/model_policies/__init__.py b/xos/synchronizer/model_policies/__init__.py
new file mode 100644
index 0000000..36c6e25
--- /dev/null
+++ b/xos/synchronizer/model_policies/__init__.py
@@ -0,0 +1,12 @@
+from .model_policy_Slice import *
+from .model_policy_Instance import *
+from .model_policy_User import *
+from .model_policy_Network import *
+from .model_policy_Site import *
+from .model_policy_SitePrivilege import *
+from .model_policy_SlicePrivilege import *
+from .model_policy_ControllerSlice import *
+from .model_policy_ControllerSite import *
+from .model_policy_ControllerUser import *
+from .model_policy_Controller import *
+from .model_policy_Image import *
diff --git a/xos/synchronizer/model_policies/model_policy_Controller.py b/xos/synchronizer/model_policies/model_policy_Controller.py
new file mode 100644
index 0000000..c62b612
--- /dev/null
+++ b/xos/synchronizer/model_policies/model_policy_Controller.py
@@ -0,0 +1,62 @@
+
+def handle(controller):
+ from core.models import Controller, Site, ControllerSite, Slice, ControllerSlice, User, ControllerUser, ControllerImages, ControllerNetwork, Image, Network
+ from collections import defaultdict
+
+ # relations for all sites
+ ctrls_by_site = defaultdict(list)
+ ctrl_sites = ControllerSite.objects.all()
+ for ctrl_site in ctrl_sites:
+ ctrls_by_site[ctrl_site.site].append(ctrl_site.controller)
+ sites = Site.objects.all()
+ for site in sites:
+ if site not in ctrls_by_site or \
+ controller not in ctrls_by_site[site]:
+ controller_site = ControllerSite(controller=controller, site=site)
+ controller_site.save()
+ # relations for all slices
+ ctrls_by_slice = defaultdict(list)
+ ctrl_slices = ControllerSlice.objects.all()
+ for ctrl_slice in ctrl_slices:
+ ctrls_by_slice[ctrl_slice.slice].append(ctrl_slice.controller)
+ slices = Slice.objects.all()
+ for slice in slices:
+ if slice not in ctrls_by_slice or \
+ controller not in ctrls_by_slice[slice]:
+ controller_slice = ControllerSlice(controller=controller, slice=slice)
+ controller_slice.save()
+ # relations for all users
+ ctrls_by_user = defaultdict(list)
+ ctrl_users = ControllerUser.objects.all()
+ for ctrl_user in ctrl_users:
+ ctrls_by_user[ctrl_user.user].append(ctrl_user.controller)
+ users = User.objects.all()
+ for user in users:
+ if user not in ctrls_by_user or \
+ controller not in ctrls_by_user[user]:
+ controller_user = ControllerUser(controller=controller, user=user)
+ controller_user.save()
+ # relations for all networks
+ ctrls_by_network = defaultdict(list)
+ ctrl_networks = ControllerNetwork.objects.all()
+ for ctrl_network in ctrl_networks:
+ ctrls_by_network[ctrl_network.network].append(ctrl_network.controller)
+ networks = Network.objects.all()
+ for network in networks:
+ if network not in ctrls_by_network or \
+ controller not in ctrls_by_network[network]:
+ controller_network = ControllerNetwork(controller=controller, network=network)
+ if network.subnet and network.subnet.strip():
+ controller_network.subnet = network.subnet.strip()
+ controller_network.save()
+ # relations for all images
+ ctrls_by_image = defaultdict(list)
+ ctrl_images = ControllerImages.objects.all()
+ for ctrl_image in ctrl_images:
+ ctrls_by_image[ctrl_image.image].append(ctrl_image.controller)
+ images = Image.objects.all()
+ for image in images:
+ if image not in ctrls_by_image or \
+ controller not in ctrls_by_image[image]:
+ controller_image = ControllerImages(controller=controller, image=image)
+ controller_image.save()
diff --git a/xos/synchronizer/model_policies/model_policy_ControllerSlice.py b/xos/synchronizer/model_policies/model_policy_ControllerSlice.py
new file mode 100644
index 0000000..bfe7995
--- /dev/null
+++ b/xos/synchronizer/model_policies/model_policy_ControllerSlice.py
@@ -0,0 +1,25 @@
+def handle(controller_slice):
+ from core.models import ControllerSlice, Slice
+
+ try:
+ my_status_code = int(controller_slice.backend_status[0])
+ try:
+ his_status_code = int(controller_slice.slice.backend_status[0])
+ except:
+ his_status_code = 0
+
+ fields = []
+ if (my_status_code not in [0,his_status_code]):
+ controller_slice.slice.backend_status = controller_slice.backend_status
+ fields+=['backend_status']
+
+ if (controller_slice.backend_register != controller_slice.slice.backend_register):
+ controller_slice.slice.backend_register = controller_slice.backend_register
+ fields+=['backend_register']
+
+ controller_slice.slice.save(update_fields = fields)
+
+
+ except Exception,e:
+ print str(e)
+ pass
diff --git a/xos/synchronizer/model_policies/model_policy_ControllerUser.py b/xos/synchronizer/model_policies/model_policy_ControllerUser.py
new file mode 100644
index 0000000..b69c9b8
--- /dev/null
+++ b/xos/synchronizer/model_policies/model_policy_ControllerUser.py
@@ -0,0 +1,16 @@
+def handle(controller_user):
+ from core.models import ControllerUser, User
+
+ try:
+ my_status_code = int(controller_user.backend_status[0])
+ try:
+ his_status_code = int(controller_user.user.backend_status[0])
+ except:
+ his_status_code = 0
+
+ if (my_status_code not in [0,his_status_code]):
+ controller_user.user.backend_status = controller_user.backend_status
+ controller_user.user.save(update_fields = ['backend_status'])
+ except Exception,e:
+ print str(e)
+ pass
diff --git a/xos/synchronizer/model_policies/model_policy_Slice.py b/xos/synchronizer/model_policies/model_policy_Slice.py
new file mode 100644
index 0000000..088d583
--- /dev/null
+++ b/xos/synchronizer/model_policies/model_policy_Slice.py
@@ -0,0 +1,102 @@
+from xos.config import Config
+
+def handle_delete(slice):
+ from core.models import Controller, ControllerSlice, SiteDeployment, Network, NetworkSlice,NetworkTemplate, Slice
+ from collections import defaultdict
+
+ public_nets = []
+ private_net = None
+ networks = Network.objects.filter(owner=slice)
+
+ for n in networks:
+ n.delete()
+
+ # Note that sliceprivileges and slicecontrollers are autodeleted, through the dependency graph
+
+def handle(slice):
+ from core.models import Controller, ControllerSlice, SiteDeployment, Network, NetworkSlice,NetworkTemplate, Slice
+ from collections import defaultdict
+
+ # only create nat_net if not using VTN
+ support_nat_net = not getattr(Config(), "networking_use_vtn", False)
+
+ print "MODEL POLICY: slice", slice
+
+ # slice = Slice.get(slice_id)
+
+ controller_slices = ControllerSlice.objects.filter(slice=slice)
+ existing_controllers = [cs.controller for cs in controller_slices]
+
+ print "MODEL POLICY: slice existing_controllers=", existing_controllers
+
+ all_controllers = Controller.objects.all()
+ for controller in all_controllers:
+ if controller not in existing_controllers:
+ print "MODEL POLICY: slice adding controller", controller
+ sd = ControllerSlice(slice=slice, controller=controller)
+ sd.save()
+
+ if slice.network in ["host", "bridged"]:
+ # Host and Bridged docker containers need no networks and they will
+ # only get in the way.
+ print "MODEL POLICY: Skipping network creation"
+ elif slice.network in ["noauto"]:
+ # do nothing
+ pass
+ else:
+ # make sure slice has at least 1 public and 1 private networkd
+ public_nets = []
+ private_nets = []
+ networks = Network.objects.filter(owner=slice)
+ for network in networks:
+ if not network.autoconnect:
+ continue
+ if network.template.name == 'Public dedicated IPv4':
+ public_nets.append(network)
+ elif network.template.name == 'Public shared IPv4':
+ public_nets.append(network)
+ elif network.template.name == 'Private':
+ private_nets.append(network)
+ if support_nat_net and (not public_nets):
+ # ensure there is at least one public network, and default it to dedicated
+ nat_net = Network(
+ name = slice.name+'-nat',
+ template = NetworkTemplate.objects.get(name='Public shared IPv4'),
+ owner = slice
+ )
+ if slice.exposed_ports:
+ nat_net.ports = slice.exposed_ports
+ nat_net.save()
+ public_nets.append(nat_net)
+ print "MODEL POLICY: slice", slice, "made nat-net"
+
+ if not private_nets:
+ private_net = Network(
+ name = slice.name+'-private',
+ template = NetworkTemplate.objects.get(name='Private'),
+ owner = slice
+ )
+ private_net.save()
+ print "MODEL POLICY: slice", slice, "made private net"
+ private_nets = [private_net]
+ # create slice networks
+ public_net_slice = None
+ private_net_slice = None
+ net_slices = NetworkSlice.objects.filter(slice=slice, network__in=private_nets+public_nets)
+ for net_slice in net_slices:
+ if net_slice.network in public_nets:
+ public_net_slice = net_slice
+ elif net_slice.network in private_nets:
+ private_net_slice = net_slice
+ if support_nat_net and (not public_net_slice):
+ public_net_slice = NetworkSlice(slice=slice, network=public_nets[0])
+ public_net_slice.save()
+ print "MODEL POLICY: slice", slice, "made public_net_slice"
+ if not private_net_slice:
+ private_net_slice = NetworkSlice(slice=slice, network=private_nets[0])
+ private_net_slice.save()
+ print "MODEL POLICY: slice", slice, "made private_net_slice"
+
+ print "MODEL POLICY: slice", slice, "DONE"
+
+
diff --git a/xos/synchronizer/model_policies/model_policy_SlicePrivilege.py b/xos/synchronizer/model_policies/model_policy_SlicePrivilege.py
new file mode 100644
index 0000000..bca7f22
--- /dev/null
+++ b/xos/synchronizer/model_policies/model_policy_SlicePrivilege.py
@@ -0,0 +1,15 @@
+def handle(slice_privilege):
+ from core.models import Controller, SlicePrivilege, ControllerSlicePrivilege
+
+ # slice_privilege = SlicePrivilege.get(slice_privilege_id)
+ # apply slice privilage at all controllers
+ controller_slice_privileges = ControllerSlicePrivilege.objects.filter(
+ slice_privilege = slice_privilege,
+ )
+ existing_controllers = [sp.controller for sp in controller_slice_privileges]
+ all_controllers = Controller.objects.all()
+ for controller in all_controllers:
+ if controller not in existing_controllers:
+ ctrl_slice_priv = ControllerSlicePrivilege(controller=controller, slice_privilege=slice_privilege)
+ ctrl_slice_priv.save()
+
diff --git a/xos/synchronizer/model_policies/model_policy_Sliver.py b/xos/synchronizer/model_policies/model_policy_Sliver.py
new file mode 100644
index 0000000..a13428d
--- /dev/null
+++ b/xos/synchronizer/model_policies/model_policy_Sliver.py
@@ -0,0 +1,13 @@
+
+def handle(instance):
+ from core.models import Controller, ControllerSlice, ControllerNetwork, NetworkSlice
+
+ networks = [ns.network for ns in NetworkSlice.objects.filter(slice=instance.slice)]
+ controller_networks = ControllerNetwork.objects.filter(network__in=networks,
+ controller=instance.node.site_deployment.controller)
+
+ for cn in controller_networks:
+ if (cn.lazy_blocked):
+ cn.lazy_blocked=False
+ cn.backend_register = '{}'
+ cn.save()
diff --git a/xos/synchronizer/model_policies/model_policy_User.py b/xos/synchronizer/model_policies/model_policy_User.py
new file mode 100644
index 0000000..8d14244
--- /dev/null
+++ b/xos/synchronizer/model_policies/model_policy_User.py
@@ -0,0 +1,14 @@
+def handle(user):
+ from core.models import Controller, ControllerSite, ControllerUser, User
+ from collections import defaultdict
+
+ # user = User.get(user_id)
+
+ controller_users = ControllerUser.objects.filter(user=user)
+ existing_controllers = [cu.controller for cu in controller_users]
+ all_controllers = Controller.objects.all()
+ for controller in all_controllers:
+ if controller not in existing_controllers:
+ ctrl_user = ControllerUser(controller=controller, user=user)
+ ctrl_user.save()
+
diff --git a/xos/synchronizer/run.sh b/xos/synchronizer/run.sh
new file mode 100644
index 0000000..62bdacc
--- /dev/null
+++ b/xos/synchronizer/run.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+export XOS_DIR=/opt/xos
+python $XOS_DIR/synchronizers/globalxos/globalxos-synchronizer.py -C $XOS_DIR/synchronizers/globalxos/globalxos_synchronizer_config
diff --git a/xos/synchronizer/steps/__init__.py b/xos/synchronizer/steps/__init__.py
new file mode 100644
index 0000000..c70b0c0
--- /dev/null
+++ b/xos/synchronizer/steps/__init__.py
@@ -0,0 +1,6 @@
+#from .sync_controller_sites import SyncControllerSites
+#from .sync_controller_slices import SyncControllerSlices
+#from .sync_controller_users import SyncControllerUsers
+#from .sync_controller_site_privileges import SyncControllerSitePrivileges
+#from .sync_controller_slice_privileges import SyncControllerSlicePrivileges
+#from .sync_controller_networks import SyncControllerNetworks
diff --git a/xos/synchronizer/steps/sync_controller_slice_privileges.py b/xos/synchronizer/steps/sync_controller_slice_privileges.py
new file mode 100644
index 0000000..c6b9cb3
--- /dev/null
+++ b/xos/synchronizer/steps/sync_controller_slice_privileges.py
@@ -0,0 +1,47 @@
+import os
+import base64
+from collections import defaultdict
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import *
+from core.models.slice import Controller, SlicePrivilege
+from core.models.user import User
+from core.models.controlleruser import ControllerUser, ControllerSlicePrivilege
+from synchronizers.base.ansible import *
+from xos.logger import observer_logger as logger
+import json
+
+class SyncControllerSlicePrivileges(SyncStep):
+ provides=[SlicePrivilege]
+ requested_interval=0
+ observes=ControllerSlicePrivilege
+ playbook = 'sync_controller_slice_privileges.yaml'
+
+ def map_sync_inputs(self, controller_slice_privilege):
+ if not controller_slice_privilege.controller.admin_user:
+ logger.info("controller %r has no admin_user, skipping" % controller_slice_privilege.controller)
+ return
+
+ template = os_template_env.get_template('sync_controller_users.yaml')
+ role = controller_slice_privilege.slice_privilege.role.role
+ # setup user home slice roles at controller
+ if not controller_slice_privilege.slice_privilege.user.site:
+ raise Exception('Sliceless user %s'%controller_slice_privilege.slice_privilege.user.email)
+ user_fields = {
+ 'endpoint':controller_slice_privilege.controller.auth_url,
+ 'user_name': controller_slice_privilege.slice_privilege.user.email,
+ 'admin_user': controller_slice_privilege.controller.admin_user,
+ 'admin_password': controller_slice_privilege.controller.admin_password,
+ 'ansible_tag':'%s@%s@%s'%(controller_slice_privilege.slice_privilege.user.email.replace('@','-at-'),controller_slice_privilege.slice_privilege.slice.name,controller_slice_privilege.controller.name),
+ 'role':role,
+ 'slice_name':controller_slice_privilege.slice_privilege.slice.name}
+ return user_fields
+
+ def map_sync_outputs(self, controller_slice_privilege, res):
+ controller_slice_privilege.role_id = res[0]['id']
+ controller_slice_privilege.save()
+
+ def delete_record(self, controller_slice_privilege):
+ controller_register = json.loads(controller_slice_privilege.controller.backend_register)
+ if (controller_register.get('disabled',False)):
+ raise InnocuousException('Controller %s is disabled'%controller_slice_privilege.controller.name)
diff --git a/xos/synchronizer/steps/sync_controller_slice_privileges.yaml b/xos/synchronizer/steps/sync_controller_slice_privileges.yaml
new file mode 100644
index 0000000..4e0d767
--- /dev/null
+++ b/xos/synchronizer/steps/sync_controller_slice_privileges.yaml
@@ -0,0 +1,76 @@
+---
+- hosts: 127.0.0.1
+ connection: local
+ # These variables are expanded by the Synchronizer framework
+ # and used to create the TOSCA recipe from a template
+ vars:
+ slice_name: "{{ slice_name }}"
+ user_name: "{{ user_name }}"
+ role: "{{ role }}"
+ tasks:
+ {% if delete -%}
+ {% else -%}
+ - name: Lookup local name of remote site
+ uri:
+ url: "{{ endpoint }}/api/core/sites/"
+ method: GET
+ user: "{{ admin_user }}"
+ password: "{{ admin_password }}"
+ return_content: yes
+ force_basic_auth: yes
+ register: sites
+
+ - name: Save site name in local_site variable
+ set_fact:
+ local_site: "{{ '{{' }} sites.json[0]['name'] {{ '}}' }}"
+
+ - name: Save local name of slice in local_slice_name variable
+ set_fact:
+ local_slice_name: "{{ '{{' }} local_site {{ '}}' }}_{{ slice_name }}"
+
+ - name: Get list of users
+ uri:
+ url: "{{ endpoint }}/api/core/users/"
+ method: GET
+ user: "{{ admin_user }}"
+ password: "{{ admin_password }}"
+ return_content: yes
+ force_basic_auth: yes
+ register: users
+
+ - fail: msg="User {{ user_name }} is not present at {{ endpoint }}"
+ when: not ((users.json | selectattr('username', 'equalto', user_name)) | list)
+
+ - name: Get list of slices
+ uri:
+ url: "{{ endpoint }}/api/core/slices/"
+ method: GET
+ user: "{{ admin_user }}"
+ password: "{{ admin_password }}"
+ return_content: yes
+ force_basic_auth: yes
+ register: slices
+
+ - fail: msg="Slice {{ slice_name }} is not present at {{ endpoint }}"
+ when: not ((slices.json | selectattr('name', 'equalto', local_slice_name)) | list)
+
+ - name: Ensure TOSCA directory exists
+ file:
+ path=/opt/xos/synchronizers/globalxos/tosca/slice_privileges/
+ state=directory
+
+ - name: Create TOSCA recipe from the template
+ template:
+ src=/opt/xos/synchronizers/globalxos/templates/slice_privileges.yaml.j2
+ dest=/opt/xos/synchronizers/globalxos/tosca/slice_privileges/{{ ansible_tag }}.yml
+
+ - name: Create slice privilege for "{{ slice }}"
+ uri:
+ url: "{{ endpoint }}/api/utility/tosca/run/"
+ method: POST
+ user: "{{ admin_user }}"
+ password: "{{ admin_password }}"
+ body: { "recipe": "{{ '{{' }} lookup('file', '/opt/xos/synchronizers/globalxos/tosca/slice_privileges/{{ ansible_tag }}.yml') {{ '}}' }}" }
+ force_basic_auth: yes
+ body_format: json
+ {% endif %}
diff --git a/xos/synchronizer/steps/sync_controller_slices.py b/xos/synchronizer/steps/sync_controller_slices.py
new file mode 100644
index 0000000..58a0896
--- /dev/null
+++ b/xos/synchronizer/steps/sync_controller_slices.py
@@ -0,0 +1,76 @@
+import os
+import base64
+from collections import defaultdict
+from netaddr import IPAddress, IPNetwork
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import *
+from core.models import *
+from synchronizers.base.ansible import *
+from xos.logger import observer_logger as logger
+import json
+
+class SyncControllerSlices(SyncStep):
+ provides=[Slice]
+ requested_interval=0
+ observes=ControllerSlice
+ playbook='sync_controller_slices.yaml'
+
+ def map_sync_inputs(self, controller_slice):
+ logger.info("sync'ing slice controller %s" % controller_slice)
+
+ if not controller_slice.controller.admin_user:
+ logger.info("controller %r has no admin_user, skipping" % controller_slice.controller)
+ return
+
+ controller_users = ControllerUser.objects.filter(user=controller_slice.slice.creator,
+ controller=controller_slice.controller)
+ if not controller_users:
+ raise Exception("slice creator %s has not account at controller %s" % (controller_slice.slice.creator, controller_slice.controller.name))
+ else:
+ controller_user = controller_users[0]
+
+ max_instances=int(controller_slice.slice.max_instances)
+ slice_fields = {
+ 'endpoint': controller_slice.controller.auth_url,
+ 'admin_user': controller_slice.controller.admin_user,
+ 'admin_password': controller_slice.controller.admin_password,
+ 'slice_name': controller_slice.slice.name,
+ 'slice_description': controller_slice.slice.description,
+ 'name': controller_user.user.email,
+ 'ansible_tag': '%s@%s'%(controller_slice.slice.name,controller_slice.controller.name),
+ 'image': controller_slice.slice.default_image.name,
+ 'addresses': '10.168.2.0/24', # FIXME
+ 'gateway_ip': '10.168.2.1', # FIXME
+ 'gateway_mac': '02:42:0a:a8:02:01', # FIXME
+ 'max_instances': max_instances
+ }
+
+ return slice_fields
+
+ def map_sync_outputs(self, controller_slice, res):
+ if (not controller_slice.tenant_id):
+ controller_slice.tenant_id = "Not implemented"
+ controller_slice.backend_status = '1 - OK'
+ controller_slice.save()
+
+
+ def map_delete_inputs(self, controller_slice):
+ controller_users = ControllerUser.objects.filter(user=controller_slice.slice.creator,
+ controller=controller_slice.controller)
+ if not controller_users:
+ raise Exception("slice creator %s has not account at controller %s" % (controller_slice.slice.creator, controller_slice.controller.name))
+ else:
+ controller_user = controller_users[0]
+
+ slice_fields = {
+ 'endpoint': controller_slice.controller.auth_url,
+ 'admin_user': controller_slice.controller.admin_user,
+ 'admin_password': controller_slice.controller.admin_password,
+ 'slice': controller_slice.slice.name,
+ 'slice_description': controller_slice.slice.description,
+ 'name': controller_user.user.email,
+ 'ansible_tag': '%s@%s'%(controller_slice.slice.name,controller_slice.controller.name),
+ 'delete': True
+ }
+ return slice_fields
diff --git a/xos/synchronizer/steps/sync_controller_slices.yaml b/xos/synchronizer/steps/sync_controller_slices.yaml
new file mode 100644
index 0000000..7c85872
--- /dev/null
+++ b/xos/synchronizer/steps/sync_controller_slices.yaml
@@ -0,0 +1,79 @@
+---
+- hosts: 127.0.0.1
+ connection: local
+ # These variables are expanded by the Synchronizer framework
+ # and used to create the TOSCA recipe from a template
+ vars:
+ slice_name: "{{ slice_name }}"
+ slice_description: "{{ slice_description }}"
+ image: "{{ image }}"
+ addresses: "{{ addresses }}"
+ gateway_ip: "{{ gateway_ip }}"
+ gateway_mac: "{{ gateway_mac }}"
+ max_instances: "{{ max_instances }}"
+ tasks:
+ {% if delete -%}
+ {% else -%}
+ - name: Lookup local name of remote site
+ uri:
+ url: "{{ endpoint }}/api/core/sites/"
+ method: GET
+ user: "{{ admin_user }}"
+ password: "{{ admin_password }}"
+ return_content: yes
+ force_basic_auth: yes
+ register: sites
+
+ - name: Save site name in local_site variable
+ set_fact:
+ local_site: "{{ '{{' }} sites.json[0]['name'] {{ '}}' }}"
+
+ - name: Get list of images
+ uri:
+ url: "{{ endpoint }}/api/core/images/"
+ method: GET
+ user: "{{ admin_user }}"
+ password: "{{ admin_password }}"
+ return_content: yes
+ force_basic_auth: yes
+ register: images
+
+ - fail: msg="Image {{ image }} is not present at {{ endpoint }}"
+ when: not ((images.json | selectattr('name', 'equalto', image)) | list)
+
+ - name: Get list of networks
+ uri:
+ url: "{{ endpoint }}/api/core/networks/"
+ method: GET
+ user: "{{ admin_user }}"
+ password: "{{ admin_password }}"
+ return_content: yes
+ force_basic_auth: yes
+ register: networks
+
+ - fail: msg="Network {{ '{{' }} item {{ '}}' }} is not present at {{ endpoint }}"
+ when: not ((networks.json | selectattr('name', 'equalto', item)) | list)
+ with_items:
+ - "management"
+ - "public"
+
+ - name: Ensure TOSCA directory exists
+ file:
+ path=/opt/xos/synchronizers/globalxos/tosca/slices/
+ state=directory
+
+ - name: Create TOSCA recipe from the template
+ template:
+ src=/opt/xos/synchronizers/globalxos/templates/slice.yaml.j2
+ dest=/opt/xos/synchronizers/globalxos/tosca/slices/{{ ansible_tag }}.yml
+
+ - name: Create slice "{{ slice }}"
+ uri:
+ url: "{{ endpoint }}/api/utility/tosca/run/"
+ method: POST
+ user: "{{ admin_user }}"
+ password: "{{ admin_password }}"
+ body: { "recipe": "{{ '{{' }} lookup('file', '/opt/xos/synchronizers/globalxos/tosca/slices/{{ ansible_tag }}.yml') {{ '}}' }}" }
+ force_basic_auth: yes
+ body_format: json
+ {% endif %}
diff --git a/xos/synchronizer/steps/sync_controller_users.py b/xos/synchronizer/steps/sync_controller_users.py
new file mode 100644
index 0000000..7382edd
--- /dev/null
+++ b/xos/synchronizer/steps/sync_controller_users.py
@@ -0,0 +1,63 @@
+import os
+import base64
+from collections import defaultdict
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import *
+from core.models.site import Controller, SiteDeployment, SiteDeployment
+from core.models.user import User
+from core.models.controlleruser import ControllerUser
+from synchronizers.base.ansible import *
+from xos.logger import observer_logger as logger
+import json
+
+class SyncControllerUsers(SyncStep):
+ provides=[User]
+ requested_interval=0
+ observes=ControllerUser
+ playbook='sync_controller_users.yaml'
+
+ def map_sync_inputs(self, controller_user):
+ if not controller_user.controller.admin_user:
+ logger.info("controller %r has no admin_user, skipping" % controller_user.controller)
+ return
+
+ if not controller_user.user.site:
+ raise Exception('Siteless user %s'%controller_user.user.email)
+
+ if controller_user.user.email == controller_user.controller.admin_user:
+ logger.info("user %s is the admin_user at controller %r, skipping" % (controller_user.user.email, controller_user.controller))
+ return
+
+ user_fields = {
+ 'endpoint':controller_user.controller.auth_url,
+ 'name': controller_user.user.email,
+ 'firstname': controller_user.user.firstname,
+ 'lastname': controller_user.user.lastname,
+ 'phone': controller_user.user.phone,
+ 'user_url': controller_user.user.user_url,
+ 'public_key': controller_user.user.public_key,
+ 'is_active': controller_user.user.is_active,
+ 'is_admin': controller_user.user.is_admin,
+ 'is_readonly': controller_user.user.is_readonly,
+ 'is_appuser': controller_user.user.is_appuser,
+ 'password': controller_user.user.remote_password,
+ 'admin_user': controller_user.controller.admin_user,
+ 'admin_password': controller_user.controller.admin_password,
+ 'ansible_tag':'%s@%s'%(controller_user.user.email.replace('@','-at-'),controller_user.controller.name),
+ }
+ return user_fields
+
+ def map_sync_outputs(self, controller_user, res):
+ #controller_user.kuser_id = res[0]['user']['id']
+ controller_user.kuser_id = 'not implemented'
+ controller_user.backend_status = '1 - OK'
+ controller_user.save()
+
+ def delete_record(self, controller_user):
+ """
+ if controller_user.kuser_id:
+ driver = self.driver.admin_driver(controller=controller_user.controller)
+ driver.delete_user(controller_user.kuser_id)
+ """
+ pass
diff --git a/xos/synchronizer/steps/sync_controller_users.yaml b/xos/synchronizer/steps/sync_controller_users.yaml
new file mode 100644
index 0000000..2d4cc00
--- /dev/null
+++ b/xos/synchronizer/steps/sync_controller_users.yaml
@@ -0,0 +1,53 @@
+---
+- hosts: 127.0.0.1
+ connection: local
+ # These variables are expanded by the Synchronizer framework
+ # and used to create the TOSCA recipe from a template
+ vars:
+ name: "{{ name }}"
+ email: "{{ email }}"
+ password: "{{ password }}"
+ firstname: "{{ firstname }}"
+ lastname: "{{ lastname }}"
+ phone: "{{ phone }}"
+ user_url: "{{ user_url }}"
+ public_key: "{{ public_key }}"
+ is_active: "{{ is_active }}"
+ is_admin: "{{ is_admin }}"
+ is_readonly: "{{ is_readonly }}"
+ is_appuser: "{{ is_appuser }}"
+ tasks:
+
+ - name: Lookup local name of remote site
+ uri:
+ url: "{{ endpoint }}/api/core/sites/"
+ method: GET
+ user: "{{ admin_user }}"
+ password: "{{ admin_password }}"
+ return_content: yes
+ force_basic_auth: yes
+ register: sites
+
+ - name: Save site name in local_site variable
+ set_fact:
+ local_site: "{{ '{{' }} sites.json[0]['name'] {{ '}}' }}"
+
+ - name: Ensure TOSCA directory exists
+ file:
+ path=/opt/xos/synchronizers/globalxos/tosca/users/
+ state=directory
+
+ - name: Create TOSCA recipe from the template
+ template:
+ src=/opt/xos/synchronizers/globalxos/templates/user.yaml.j2
+ dest=/opt/xos/synchronizers/globalxos/tosca/users/{{ ansible_tag }}.yml
+
+ - name: Create user account for "{{ name }}"
+ uri:
+ url: "{{ endpoint }}/api/utility/tosca/run/"
+ method: POST
+ user: "{{ admin_user }}"
+ password: "{{ admin_password }}"
+ body: { "recipe": "{{ '{{' }} lookup('file', '/opt/xos/synchronizers/globalxos/tosca/users/{{ ansible_tag }}.yml') {{ '}}' }}" }
+ force_basic_auth: yes
+ body_format: json
diff --git a/xos/synchronizer/steps/sync_images.py b/xos/synchronizer/steps/sync_images.py
new file mode 100644
index 0000000..5b8e227
--- /dev/null
+++ b/xos/synchronizer/steps/sync_images.py
@@ -0,0 +1,18 @@
+import os
+import base64
+from django.db.models import F, Q
+from xos.config import Config
+from core.models.image import Image
+from xos.logger import observer_logger as logger
+from synchronizers.base.syncstep import *
+
+class SyncImages(SyncStep):
+ provides=[Image]
+ requested_interval=0
+ observes=Image
+
+ # We're not sync'ing sites with L-XOS
+ # Just mark it as sync'ed
+ def sync_record(self, image):
+ image.backend_status = "0 - not sync'ed by globalxos"
+ image.save()
diff --git a/xos/synchronizer/steps/sync_roles.py b/xos/synchronizer/steps/sync_roles.py
new file mode 100644
index 0000000..e19532e
--- /dev/null
+++ b/xos/synchronizer/steps/sync_roles.py
@@ -0,0 +1,26 @@
+import os
+import base64
+from django.db.models import F, Q
+from xos.config import Config
+from core.models.role import Role
+from core.models.site import SiteRole, Controller, ControllerRole
+from core.models.slice import SliceRole
+from xos.logger import observer_logger as logger
+from synchronizers.base.ansible import *
+from synchronizers.base.syncstep import *
+
+class SyncRoles(SyncStep):
+ provides=[Role]
+ requested_interval=0
+ observes=[SiteRole,SliceRole,ControllerRole]
+
+ def sync_record(self, role):
+ if not role.enacted:
+ controllers = Controller.objects.all()
+ """
+ for controller in controllers:
+ driver = self.driver.admin_driver(controller=controller)
+ driver.create_role(role.role)
+ """
+ role.save()
+
diff --git a/xos/synchronizer/steps/sync_sites.py b/xos/synchronizer/steps/sync_sites.py
new file mode 100644
index 0000000..82e42d2
--- /dev/null
+++ b/xos/synchronizer/steps/sync_sites.py
@@ -0,0 +1,19 @@
+import os
+import base64
+from django.db.models import F, Q
+from xos.config import Config
+from core.models.site import Site
+from xos.logger import observer_logger as logger
+from synchronizers.base.ansible import *
+from synchronizers.base.syncstep import *
+
+class SyncSites(SyncStep):
+ provides=[Site]
+ requested_interval=0
+ observes=[Site]
+
+ # We're not sync'ing sites with L-XOS
+ # Just mark it as sync'ed
+ def sync_record(self, site):
+ site.backend_status = "0 - not sync'ed by globalxos"
+ site.save()
diff --git a/xos/synchronizer/templates/slice.yaml.j2 b/xos/synchronizer/templates/slice.yaml.j2
new file mode 100644
index 0000000..6f22b48
--- /dev/null
+++ b/xos/synchronizer/templates/slice.yaml.j2
@@ -0,0 +1,48 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup a generic slice on the pod
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+
+ management:
+ type: tosca.nodes.network.Network.XOS
+ properties:
+ no-create: true
+ no-delete: true
+ no-update: true
+
+ public:
+ type: tosca.nodes.network.Network.XOS
+ properties:
+ no-create: true
+ no-delete: true
+ no-update: true
+
+ {{ local_site }}:
+ type: tosca.nodes.Site
+
+ {{ image }}:
+ type: tosca.nodes.Image
+
+ {{ local_site }}_{{ slice_name }}:
+ description: {{ slice_description }}
+ type: tosca.nodes.Slice
+ properties:
+ network: noauto
+ requirements:
+ - site:
+ node: {{ local_site }}
+ relationship: tosca.relationships.MemberOfSite
+ - management:
+ node: management
+ relationship: tosca.relationships.ConnectsToNetwork
+ - public:
+ node: public
+ relationship: tosca.relationships.ConnectsToNetwork
+ - image:
+ node: {{ image }}
+ relationship: tosca.relationships.DefaultImage
diff --git a/xos/synchronizer/templates/slice_privileges.yaml.j2 b/xos/synchronizer/templates/slice_privileges.yaml.j2
new file mode 100644
index 0000000..ac3a573
--- /dev/null
+++ b/xos/synchronizer/templates/slice_privileges.yaml.j2
@@ -0,0 +1,29 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup a slice privilege on the pod
+{% set relationship = {"admin": "tosca.relationships.AdminPrivilege", "access": "tosca.relationships.AccessPrivilege"}[role] | default("ERROR") -%}
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+
+ {{ local_site }}:
+ type: tosca.nodes.Site
+
+ {{ user_name }}:
+ type: tosca.nodes.User
+
+ {{ local_site }}_{{ slice_name }}:
+ type: tosca.nodes.Slice
+ properties:
+ no-create: true
+ no-delete: true
+ requirements:
+ - privilege:
+ node: {{ user_name }}
+ relationship: {{ relationship }}
+ - site:
+ node: {{ local_site }}
+ relationship: tosca.relationships.MemberOfSite
diff --git a/xos/synchronizer/templates/user.yaml.j2 b/xos/synchronizer/templates/user.yaml.j2
new file mode 100644
index 0000000..956051d
--- /dev/null
+++ b/xos/synchronizer/templates/user.yaml.j2
@@ -0,0 +1,29 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Add a user
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+ {{ local_site }}:
+ type: tosca.nodes.Site
+
+ {{ name }}:
+ type: tosca.nodes.User
+ properties:
+ password: "{{ password }}"
+ firstname: "{{ firstname }}"
+ lastname: "{{ lastname }}"
+ phone: "{{ phone }}"
+ user_url: "{{ user_url }}"
+ public_key: "{{ public_key }}"
+ is_active: "{{ is_active }}"
+ is_admin: "{{ is_admin }}"
+ is_readonly: "{{ is_readonly }}"
+ is_appuser: "{{ is_appuser }}"
+ requirements:
+ - site:
+ node: {{ local_site }}
+ relationship: tosca.relationships.MemberOfSite