From 98eb8c1d1c98b04e4e30d4d2afe422521faed661 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Jaillet Date: Thu, 9 Feb 2017 15:54:53 +0100 Subject: [PATCH] ozwillo: create ozwillo app in contrib (#14935) --- hobo/contrib/__init__.py | 0 hobo/contrib/ozwillo/README.txt | 34 +++ hobo/contrib/ozwillo/__init__.py | 0 .../ozwillo/examples/import-site-agents.json | 30 +++ .../ozwillo/examples/import-site-template.json | 169 +++++++++++++ hobo/contrib/ozwillo/examples/template_recipe.json | 66 +++++ hobo/contrib/ozwillo/migrations/0001_initial.py | 21 ++ hobo/contrib/ozwillo/migrations/__init__.py | 0 hobo/contrib/ozwillo/models.py | 26 ++ .../contrib/ozwillo/scripts/create_user_ozwillo.py | 55 +++++ hobo/contrib/ozwillo/urls.py | 25 ++ hobo/contrib/ozwillo/views.py | 275 +++++++++++++++++++++ hobo/urls.py | 4 + 13 files changed, 705 insertions(+) create mode 100644 hobo/contrib/__init__.py create mode 100644 hobo/contrib/ozwillo/README.txt create mode 100644 hobo/contrib/ozwillo/__init__.py create mode 100644 hobo/contrib/ozwillo/examples/import-site-agents.json create mode 100644 hobo/contrib/ozwillo/examples/import-site-template.json create mode 100644 hobo/contrib/ozwillo/examples/template_recipe.json create mode 100644 hobo/contrib/ozwillo/migrations/0001_initial.py create mode 100644 hobo/contrib/ozwillo/migrations/__init__.py create mode 100644 hobo/contrib/ozwillo/models.py create mode 100644 hobo/contrib/ozwillo/scripts/create_user_ozwillo.py create mode 100644 hobo/contrib/ozwillo/urls.py create mode 100644 hobo/contrib/ozwillo/views.py diff --git a/hobo/contrib/__init__.py b/hobo/contrib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hobo/contrib/ozwillo/README.txt b/hobo/contrib/ozwillo/README.txt new file mode 100644 index 0000000..1c5d75d --- /dev/null +++ b/hobo/contrib/ozwillo/README.txt @@ -0,0 +1,34 @@ +To run this plugin well, you have to set some files in /etc/hobo/ozwillo : +- the files are in the folder examples. (don't forget to replace the instance name in the combo import-site-template.json by 'instance_name'). +- it's a common recipe for your publik, and two extracts of user and agent combo. +- hobo has to be in the sudoers + +You have to set several var in a settings.json too : +-OZWILLO_SECRET +-OZWILLO_ENV_DOMAIN (e.g: sictiam.dev.entrouvert.org) +-OZWILLO_DESTRUCTION_URI +-OZWILLO_DESTRUCTION_SECRET +-OZWILLO_PLATEFORM (https://dev.entrouvert.org/projects/sictiam/wiki/Raccordement_OpenID_Connect_%C3%A0_Ozwillo for the values) +-OZWILLO_SERVICES (use only for the destruction, explained down) + +And finally you have to enable in INSTALLED_APPS hobo.contrib.ozwillo. + +The views create-publik-instance receive an ozwillo request with some clients informations (secret and id), the ozwillo user sending the request, the organization name (which is the collectivity's name to deploy) and the registration uri (where you're supposed to POST when the job's done). +The script modify a template_recipe by replacing every 'instance_name' by the actual organization name, and same for the combo user extract (rewritting all the url_redirect fields). +The script then launch a cook and three commands : +- the import of the combo user with the modified extract +- the import of the combo agent +- a runscript creating a role (same as the one in wcs linked to the form sve 'agents sve'), a provider (the details are in the page linked for the parameter OZWILLO_PLATEFORM) and an admin User in Authentic who is the ozwillo user sending the request. +In the final acknolegment response, the script sends a 'services' dictionnary for ozillo to set some links and parameters in its backoffice about the app deployed). + +OZWILLO_SERVICES is a dict following this pattern: 'service_user': ['service_prefix', 'service_command_manager']. E.G: +{ +"authentic-multitenant": ["connexion-", "authentic2-multitenant-manage"], +"combo_agent": ["agents-", "combo-manage"], +"combo_usager": ["", "combo-manage"], +"fargo": ["porte-documents-", "fargo-manage"], +"hobo": ["hobo-", "hobo-manage"], +"passerelle": ["passerelle-", "passerelle-manage"], +"wcs-au-quotidien": ["demarches-", "wcsctl"] +} + diff --git a/hobo/contrib/ozwillo/__init__.py b/hobo/contrib/ozwillo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hobo/contrib/ozwillo/examples/import-site-agents.json b/hobo/contrib/ozwillo/examples/import-site-agents.json new file mode 100644 index 0000000..866c2e6 --- /dev/null +++ b/hobo/contrib/ozwillo/examples/import-site-agents.json @@ -0,0 +1,30 @@ +[ + { + "cells": [ + { + "fields": { + "extra_css_class": "", + "groups": [], + "order": 1, + "placeholder": "content", + "public": true, + "restricted_to_unlogged": false, + "slug": "services", + "text": "

Bienvenue

\r\n" + }, + "model": "data.textcell" + } + ], + "fields": { + "exclude_from_navigation": false, + "groups": [], + "order": 1, + "parent": null, + "public": false, + "redirect_url": "", + "slug": "index", + "template_name": "standard", + "title": "Accueil" + } + } +] diff --git a/hobo/contrib/ozwillo/examples/import-site-template.json b/hobo/contrib/ozwillo/examples/import-site-template.json new file mode 100644 index 0000000..e3c8aa3 --- /dev/null +++ b/hobo/contrib/ozwillo/examples/import-site-template.json @@ -0,0 +1,169 @@ +[ + { + "cells": [ + { + "fields": { + "extra_css_class": "", + "groups": [], + "order": 0, + "placeholder": "content", + "public": true, + "restricted_to_unlogged": true, + "slug": "", + "text": "

Bienvenue

\r\n\r\n

Bienvenue sur votre compte usager.

\r\n\r\n

Pour suivre vos démarches en cours, créez votre compte personnel ou identifiez-vous depuis la page de connexion.

\r\n" + }, + "model": "data.textcell" + }, + { + "fields": { + "extra_css_class": "", + "groups": [], + "order": 1, + "placeholder": "content", + "public": false, + "restricted_to_unlogged": false, + "slug": "", + "text": "

Bienvenue

\r\n\r\n

Bienvenue sur votre compte usager.

\r\n" + }, + "model": "data.textcell" + }, + { + "fields": { + "extra_css_class": "", + "groups": [], + "order": 2, + "placeholder": "right", + "public": true, + "restricted_to_unlogged": false, + "slug": "", + "wcs_site": "" + }, + "model": "wcs.trackingcodeinputcell" + }, + { + "fields": { + "extra_css_class": "", + "groups": [], + "order": 3, + "placeholder": "right", + "public": true, + "restricted_to_unlogged": true, + "slug": "", + "text": "

Demandes en cours

\r\n\r\n

Connectez-vous pour voir vos demandes en cours.

\r\n" + }, + "model": "data.textcell" + }, + { + "fields": { + "current_forms": true, + "done_forms": false, + "extra_css_class": "", + "groups": [], + "order": 4, + "placeholder": "right", + "public": false, + "restricted_to_unlogged": false, + "slug": "", + "wcs_site": "" + }, + "model": "wcs.wcscurrentformscell" + }, + { + "fields": { + "extra_css_class": "", + "groups": [], + "order": 5, + "placeholder": "footer", + "public": true, + "restricted_to_unlogged": false, + "slug": "", + "text": "

Ce service est proposé par le SICTIAM.

\r\n" + }, + "model": "data.textcell" + }, + { + "fields": { + "category_reference": "eservices:nous-contacter", + "extra_css_class": "", + "groups": [], + "limit": null, + "manual_order": { + "data": [] + }, + "order": 6, + "ordering": "popularity", + "placeholder": "content", + "public": true, + "restricted_to_unlogged": false, + "slug": "" + }, + "model": "wcs.wcsformsofcategorycell" + } + ], + "fields": { + "exclude_from_navigation": false, + "groups": [], + "order": 1, + "parent": null, + "public": true, + "redirect_url": "", + "slug": "index", + "template_name": "two-columns", + "title": "Accueil" + } + }, + { + "cells": [ + { + "fields": { + "extra_css_class": "", + "groups": [], + "order": 0, + "placeholder": "footer", + "public": true, + "restricted_to_unlogged": false, + "slug": "" + }, + "model": "data.parentcontentcell" + } + ], + "fields": { + "exclude_from_navigation": false, + "groups": [], + "order": 2, + "parent": null, + "public": true, + "redirect_url": "[idp_url]accounts/", + "slug": "mon-compte", + "template_name": "standard", + "title": "Mon compte" + } + }, + { + "cells": [ + { + "fields": { + "extra_css_class": "", + "groups": [], + "order": 0, + "placeholder": "footer", + "public": true, + "restricted_to_unlogged": false, + "slug": "" + }, + "model": "data.parentcontentcell" + } + ], + "fields": { + "exclude_from_navigation": false, + "groups": [], + "order": 3, + "parent": null, + "public": true, + "redirect_url": "[eservices_url]", + "slug": "nous-contacter", + "template_name": "standard", + "title": "Nous contacter" + } + } +] diff --git a/hobo/contrib/ozwillo/examples/template_recipe.json b/hobo/contrib/ozwillo/examples/template_recipe.json new file mode 100644 index 0000000..52ab260 --- /dev/null +++ b/hobo/contrib/ozwillo/examples/template_recipe.json @@ -0,0 +1,66 @@ +{ + "steps": [ + { + "create-hobo": { + "url": "https://${hobo}/" + } + }, + { + "create-authentic": { + "title": "Connexion", + "url": "https://${authentic}/" + } + }, + { + "set-idp": {} + }, + { + "create-combo": { + "template_name": "portal-user", + "title": "Compte citoyen", + "url": "https://${combo}/" + } + }, + { + "create-combo": { + "slug": "portal-agent", + "template_name": "portal-agent", + "title": "Portail agent", + "url": "https://${combo_agent}/" + } + }, + { + "create-wcs": { + "template_name": "export.zip", + "title": "D\u00e9marches", + "url": "https://${wcs}/" + } + }, + { + "create-fargo": { + "title": "Porte documents", + "url": "https://${fargo}/" + } + }, + { + "create-passerelle": { + "title": "Passerelle", + "url": "https://${passerelle}/" + } + }, + { + "set-theme": { + "theme": "publik" + } + } + ], + "variables": { + "authentic": "connexion-instance_name.sictiam.dev.entrouvert.org", + "combo": "instance_name.sictiam.dev.entrouvert.org", + "combo_agent": "agents-instance_name.sictiam.dev.entrouvert.org", + "fargo": "porte-documents-instance_name.sictiam.dev.entrouvert.org", + "hobo": "hobo-instance_name.sictiam.dev.entrouvert.org", + "passerelle": "passerelle-instance_name.sictiam.dev.entrouvert.org", + "wcs": "demarches-instance_name.sictiam.dev.entrouvert.org" + } +} diff --git a/hobo/contrib/ozwillo/migrations/0001_initial.py b/hobo/contrib/ozwillo/migrations/0001_initial.py new file mode 100644 index 0000000..87ff97c --- /dev/null +++ b/hobo/contrib/ozwillo/migrations/0001_initial.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='OzwilloInstance', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('domain_slug', models.CharField(unique=True, max_length=250)), + ('external_ozwillo_id', models.CharField(max_length=450)), + ], + ), + ] diff --git a/hobo/contrib/ozwillo/migrations/__init__.py b/hobo/contrib/ozwillo/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hobo/contrib/ozwillo/models.py b/hobo/contrib/ozwillo/models.py new file mode 100644 index 0000000..0f3b4a2 --- /dev/null +++ b/hobo/contrib/ozwillo/models.py @@ -0,0 +1,26 @@ +# Ozwillo plugin to deploy Publik +# Copyright (C) 2017 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.db import models + + +class OzwilloInstance(models.Model): + domain_slug = models.CharField(max_length=250, unique=True) + external_ozwillo_id = models.CharField(max_length=450) + + def __unicode__(self): + return 'external ozwillo id: %s, domain slug: %s' % (self.external_ozwillo_id, + self.domain_slug) diff --git a/hobo/contrib/ozwillo/scripts/create_user_ozwillo.py b/hobo/contrib/ozwillo/scripts/create_user_ozwillo.py new file mode 100644 index 0000000..d8d1658 --- /dev/null +++ b/hobo/contrib/ozwillo/scripts/create_user_ozwillo.py @@ -0,0 +1,55 @@ +import sys +import logging + +from django.contrib.auth import get_user_model + +from authentic2_auth_oidc.models import OIDCAccount +from authentic2_auth_oidc.models import OIDCProvider +from authentic2.a2_rbac.models import Role, OrganizationalUnit + + +def create_user(user_id, email_addressi, user_name, provider): + while True: + new_user = User.objects.create() + oidc_account, created = OIDCAccount.objects.select_related().get_or_create(provider=provider, sub=user_id, defaults={'user': new_user}) + if created: + if OIDCAccount.objects.filter(provider=provider, sub=user_id).count() > 1: + oidc_account.delete() + new_user.delete() + continue + break + else: + new_user.delete() + new_user = oidc_account.user + break + + new_user.email = email_address + new_user.ou = provider.ou + new_user.is_superuser = True + new_user.is_staff = True + new_user.first_name = 'admin' + new_user.last_name = user_name + new_user.save() + + return new_user + + +# create agent_sve role +ou = OrganizationalUnit.objects.get(default=True) +role, created = Role.objects.get_or_create(uuid='3f3367d817bb4a9aa98e7ed6c83b5b09', + defaults={'name': u'Agents SVE', + 'ou': ou}) + +# set the provider for the user creation +provider = OIDCProvider.objects.get(name='Ozwillo') + +# get user info from args sent to the script +args = sys.argv +email_address = args[1] +user_id = args[2] +user_name = args[3] + +# create admin user in Publik from Ozwillo +User = get_user_model() +user = create_user(user_id, email_address, user_name, provider) +logging.info('owzillo provisionning: user created with uuid: %s', user.uuid) diff --git a/hobo/contrib/ozwillo/urls.py b/hobo/contrib/ozwillo/urls.py new file mode 100644 index 0000000..96a1d6a --- /dev/null +++ b/hobo/contrib/ozwillo/urls.py @@ -0,0 +1,25 @@ +# Ozwillo plugin to deploy +# Copyright (C) 2017 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.conf.urls import patterns, url, include + +from . import views + + +urlpatterns = patterns('', + url(r'create-publik-instance', views.create_publik_instance, name='ozwillo-create-publik-instance'), + url(r'delete-publik-instance', views.delete_publik_instance, name='ozwillo-delete-publik-instance'), +) diff --git a/hobo/contrib/ozwillo/views.py b/hobo/contrib/ozwillo/views.py new file mode 100644 index 0000000..26ab92f --- /dev/null +++ b/hobo/contrib/ozwillo/views.py @@ -0,0 +1,275 @@ +# Ozwillo plugin to deploy Publik +# Copyright (C) 2017 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import os +import logging +import requests +import json +import subprocess +import hmac +import threading +import tempfile +from hashlib import sha1 + +from django.views.decorators.csrf import csrf_exempt +from django.conf import settings +from django.http import (HttpResponseForbidden, HttpResponseBadRequest, + HttpResponseNotFound, HttpResponse) +from django.core.management import call_command +from django.utils.text import slugify + +from .models import OzwilloInstance + +logger = logging.getLogger(__name__) + + +def valid_signature_required(func): + '''Validate Ozwillo signatures''' + + signature_header_name = 'HTTP_X_HUB_SIGNATURE' + api_secret = settings.OZWILLO_SECRET + + def wrapper(request): + if signature_header_name in request.META: + if request.META[signature_header_name].startswith('sha1='): + algo, received_hmac = request.META[signature_header_name].rsplit('=') + computed_hmac = hmac.new(api_secret, request.body, sha1).hexdigest() + # the received hmac is uppercase according to + # http://doc.ozwillo.com/#ref-3-2-1 + if received_hmac.lower() != computed_hmac: + logger.error(u'ozwillo: invalid HMAC') + return HttpResponseForbidden('invalid HMAC') + else: + logger.error(u'ozwillo: invalid HMAC algo') + return HttpResponseForbidden('invalid HMAC algo') + else: + logger.error(u'ozwillo: no HMAC in the header') + return HttpResponseForbidden('no HMAC in the header') + return func(request) + return wrapper + + +def is_ozwillo_enabled(func): + def wrapper(request): + if not getattr(settings, 'OZWILLO_ENABLED', False): + return HttpResponseNotFound('owillo providing is not active here.') + return func(request) + return wrapper + + +@csrf_exempt +@is_ozwillo_enabled +@valid_signature_required +def create_publik_instance(request): + try: + data = json.loads(request.body) + except ValueError: + logger.warning(u'ozwillo: received non JSON request') + return HttpResponseBadRequest('invalid JSON content') + + logger.debug(u'ozwillo: create publik instance request, %r', data) + + if 'organization_name' not in data.keys(): + return HttpResponseBadRequest('missing parameter "organization_name"') + + org_name = slugify(data['organization_name']) + if OzwilloInstance.objects.filter(domain_slug=org_name): + return HttpResponseBadRequest('instance %s already exists' % org_name) + + OzwilloInstance.objects.create(external_ozwillo_id=data['instance_id'], domain_slug=org_name) + + def thread_function(data): + try: + ozwillo_deploy_thread(data) + except Exception: + logger.exception(u'ozwillo: error occured while deploying instance %s', + data['organization_name']) + + thread = threading.Thread(target=thread_function, args=(data,)) + thread.start() + + return HttpResponse() + + +def ozwillo_deploy_thread(data): + # Request parsing + client_id = data['client_id'] + client_secret = data['client_secret'] + instance_id = data['instance_id'] + instance_name = data['organization_name'] + instance_name = slugify(instance_name) + registration_uri = data['instance_registration_uri'] + user = data['user'] + + # Cook new platform + template_recipe = json.load(open('/etc/hobo/ozwillo/template_recipe.json', 'rb')) + var = template_recipe['variables'] + for key, value in var.items(): + var[key] = value.replace('instance_name', instance_name) + + template_recipe['variables'] = var + domain = var['combo'] + domain_agent = var['combo_agent'] + domain_passerelle = var['passerelle'] + + recipe_file_handle, recipe_file_path = tempfile.mkstemp() + + json.dump(template_recipe, open(recipe_file_path, 'w')) + + call_command('cook', recipe_file_path, timeout=1000, verbosity=0) + + # Load user portal template + subprocess.check_call([ + 'sudo', '-u', 'combo', + 'combo-manage', 'tenant_command', 'import_site', + '/etc/hobo/ozwillo/import-site-template.json', + '-d', domain + ]) + + # Load agent portal template + subprocess.check_call([ + 'sudo', '-u', 'combo', + 'combo-manage', 'tenant_command', 'import_site', + '/etc/hobo/ozwillo/import-site-agents.json', + '-d', domain_agent + ]) + + # Configure OIDC Ozwillo authentication + domain_name = 'connexion-%s.%s' % (instance_name, settings.OZWILLO_ENV_DOMAIN) + subprocess.check_call([ + 'sudo', '-u', 'authentic-multitenant', + 'authentic2-multitenant-manage', 'tenant_command', 'oidc-register-issuer', + '-d', domain_name, + '--scope', 'profile', + '--scope', 'email', + '--issuer', settings.OZWILLO_PLATEFORM, + '--client-id', client_id, + '--client-secret', client_secret, + '--claim-mapping', '"given_name first_name always_verified"', + '--claim-mapping', '"family_name last_name always_verified"', + '--ou-slug', 'default', + '--claim-mapping', '"email email required"', + 'Ozwillo' + ]) + + # Create admin user + create_user_script = os.path.dirname(__file__) + '/scripts/create_user_ozwillo.py' + subprocess.check_call([ + 'sudo', '-u', 'authentic-multitenant', + 'authentic2-multitenant-manage', 'tenant_command', 'runscript', '-d', domain_name, + create_user_script, user['email_address'], user['id'], user['name'] + ]) + + # Load passerelle template + subprocess.check_call([ + 'sudo', '-u', 'passerelle', + 'passerelle-manage', 'tenant_command', 'import_site', + '/etc/hobo/ozwillo/import-site-passerelle.json', + '--import-user', '-d', domain_passerelle + ]) + + os.remove(recipe_file_path) + + # Sending done event to Ozwillo + services = { + 'services': [{ + 'local_id': 'publik', + 'name': 'Publik - %s' % (instance_name), + 'service_uri': 'https://connexion-%s.%s/accounts/oidc/login?iss=%s' + % (instance_name, settings.OZWILLO_ENV_DOMAIN, + settings.OZWILLO_PLATEFORM), + 'description': 'Gestion de la relation usagers', + 'tos_uri': 'https://publik.entrouvert.com/', + 'policy_uri': 'https://publik.entrouvert.com/', + 'icon': 'https://publik.entrouvert.com/static/img/logo-publik.png', + 'payment_option': 'FREE', + 'target_audience': ['PUBLIC_BODIES', + 'CITIZENS', + 'COMPANIES'], + 'contacts': ['https://publik.entrouvert.com/'], + 'redirect_uris': ['https://connexion-%s.%s/accounts/oidc/callback/' + % (instance_name, settings.OZWILLO_ENV_DOMAIN)], + }], + 'instance_id': instance_id, + 'destruction_uri': settings.OZWILLO_DESTRUCTION_URI, + 'destruction_secret': settings.OZWILLO_DESTRUCTION_SECRET, + 'needed_scopes': [] + } + logger.debug(u'ozwillo: sending registration request, %r', services) + headers = {'Content-type': 'application/json', 'Accept': 'application/json'} + response = requests.post( + registration_uri, + data=json.dumps(services), + auth=(client_id, client_secret), + headers=headers) + logger.debug(u'ozwillo: registration response, %r', response.content) + + +@csrf_exempt +@is_ozwillo_enabled +@valid_signature_required +def delete_publik_instance(request): + try: + data = json.loads(request.body) + except ValueError: + logger.warning(u'ozwillo: received non JSON request') + return HttpResponseBadRequest('invalid JSON content') + + logger.debug(u'ozwillo: delete publik instance request, %r', data) + + try: + instance = OzwilloInstance.objects.get(external_ozwillo_id=data['instance_id']) + except OzwilloInstance.DoesNotExist: + return HttpResponseBadRequest('no instance with id %s' % data['instance_id']) + + def thread_function(data): + try: + ozwillo_destroy_thread(instance) + logger.debug(u'ozwillo: instance %s destroyed', instance.domain_slug) + except Exception: + logger.exception('ozwillo: error occured while destroying instance %s', + instance.domain_slug) + thread = threading.Thread(target=ozwillo_destroy_thread, args=(instance,)) + thread.start() + + return HttpResponse(status=200) + + +def ozwillo_destroy_thread(instance): + instance_slug = instance.domain_slug + services = settings.OZWILLO_SERVICES + wcs = services['wcs-au-quotidien'] + + for s, infos in services.items(): + # to get the two combo instances which have same service + service_user = s.split('_')[0] + + tenant = '%s%s.%s' % (infos[0], instance_slug, settings.OZWILLO_ENV_DOMAIN) + + subprocess.check_call([ + 'sudo', '-u', service_user, infos[1], + 'delete_tenant', '--force-drop', tenant + ]) + + tenant = '%s%s.%s' % (wcs[0], instance_slug, settings.OZWILLO_ENV_DOMAIN) + subprocess.check_call([ + 'sudo', '-u', 'wcs-au-quotidien', + wcs[1], '-f', '/etc/wcs/wcs-au-quotidien.cfg', + 'delete_tenant', '--force-drop', tenant + ]) + + instance.delete() + return instance_slug diff --git a/hobo/urls.py b/hobo/urls.py index e7dbc24..03042a3 100644 --- a/hobo/urls.py +++ b/hobo/urls.py @@ -32,3 +32,7 @@ urlpatterns += patterns('', url(r'^login/local/$', login_local), # to be used as backup, in case of idp down url(r'^accounts/mellon/', include('mellon.urls')), ) +if 'hobo.contrib.ozwillo' in settings.INSTALLED_APPS: + urlpatterns += patterns('', + url(r'ozwillo/', include('hobo.contrib.ozwillo.urls')), + ) -- 2.11.0