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()