Initial prototype of synchronizer
Change-Id: I45de04debd89e7007d7985728d51aa5d03178675
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()