# hobo - portal to configure and deploy applications # Copyright (C) 2015-2019 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 sys import time import urllib.parse from django.conf import settings from django.core.management.base import CommandError from django.db import connection, transaction from django.urls import reverse from django.utils.encoding import force_str from hobo.middleware.utils import StoreRequestMiddleware from hobo.multitenant.settings_loaders import KnownServices from hobo.profile.utils import get_profile_dict def get_installed_services(types=None): from .models import AVAILABLE_SERVICES, Hobo installed_services = [] for available_service in AVAILABLE_SERVICES: if types and available_service.Extra.service_id not in types: continue if available_service is Hobo: installed_services.extend(available_service.objects.filter(local=False)) else: installed_services.extend(available_service.objects.all()) return installed_services def get_operational_services(): return [x for x in get_installed_services() if x.is_operational()] def get_local_key(url): secret1 = force_str(settings.SECRET_KEY) secret2 = url return KnownServices.shared_secret(secret1, secret2)[:40] def get_or_create_local_hobo(): from .models import Hobo try: hobo = Hobo.objects.get(local=True) except Hobo.DoesNotExist: build_absolute_uri = None if hasattr(connection, 'get_tenant') and hasattr(connection.tenant, 'build_absolute_uri'): build_absolute_uri = connection.tenant.build_absolute_uri else: request = StoreRequestMiddleware.get_request() if request: build_absolute_uri = request.build_absolute_uri if not build_absolute_uri: return None hobo, _ = Hobo.objects.get_or_create( local=True, defaults={ 'secret_key': get_local_key(build_absolute_uri('/')), 'title': 'Hobo', 'slug': 'hobo', 'base_url': build_absolute_uri(reverse('home')), 'secondary': False, }, ) return hobo def get_local_hobo_dict(): hobo = get_or_create_local_hobo() if hobo: return hobo.as_dict() return None def get_installed_services_dict(): from hobo.utils import TemplateError, get_templated_url from .models import Variable hobo_service = [] hobo_dict = get_local_hobo_dict() if hobo_dict: hobo_service.append(hobo_dict) variables = {} for v in Variable.objects.filter(service_pk__isnull=True): if v.name in ('sms_url',): try: variables[v.name] = get_templated_url(v.value, context=getattr(settings, 'TEMPLATE_VARS', {})) except TemplateError: pass else: variables[v.name] = v.json return { 'services': hobo_service + [x.as_dict() for x in get_installed_services()], 'variables': variables, } class Zone: title = None zone_icon_id = None href = None def __init__(self, title, zone_icon_id, href): self.title = title self.zone_icon_id = zone_icon_id self.href = href def get_variable(name): from .models import Variable try: variable = Variable.objects.get(name=name) except Variable.DoesNotExist: variable = Variable(name=name, auto=True) return variable def set_variable(name, value): from .models import Variable variable, created = Variable.objects.get_or_create(name=name, defaults={'auto': True}) variable.value = value variable.save() def create_base_url(hostname, service): """ Distinguish mutualised domains (matching a "-" in the first part of the netloc) from the normal scenario with a dedicated parent domain. """ ph = urllib.parse.urlparse(hostname) parts = ph.netloc.split('.') if '-' in parts[0]: netloc = '%s-%s.%s' % (service, parts[0].split('-', maxsplit=1)[1], '.'.join(parts[1:])) else: netloc = '%s.%s' % (service, '.'.join(parts[1:])) return '%s://%s' % (ph.scheme, netloc) def get_setting_variable(setting_name, label=None, service=None): from .models import ContentType, Variable kwargs = { 'name': 'SETTING_' + setting_name, } if service: kwargs['service_type'] = ContentType.objects.get_for_model(service) kwargs['service_pk'] = service.pk else: kwargs['service_type__isnull'] = True kwargs['service_pk__isnull'] = True try: variable = Variable.objects.get(**kwargs) except Variable.DoesNotExist: variable = Variable(name='SETTING_' + setting_name, label=label or '', service=service, auto=True) return variable def export_parameters(): from .models import Variable variables = [] for var in Variable.objects.filter(service_pk__isnull=True): variables.append({'name': var.name, 'label': var.label, 'value': var.value, 'auto': var.auto}) parameters = {'variables': variables} parameters.update(get_profile_dict()) return parameters def import_parameters(parameters): from hobo.profile.models import AttributeDefinition from .models import Variable with transaction.atomic(): for variables in parameters.get('variables', []): obj, created = Variable.objects.get_or_create(name=variables['name']) for key, value in variables.items(): setattr(obj, key, value) obj.save() for fields in parameters.get('profile', {}).get('fields', []): obj, created = AttributeDefinition.objects.get_or_create(name=fields['name']) for key, value in fields.items(): setattr(obj, key, value) obj.save() def wait_operationals(services, timeout, verbosity, terminal_width, notify_agents_func): t0 = time.time() i = 0 last_service_url = None last_notification = t0 while len(services) > 0: if time.time() - last_notification > 15: last_notification = time.time() notify_agents_func(None) for service in services[:]: if service.last_operational_success_timestamp: services.remove(service) continue service.check_operational() if len(services) == 0: break if verbosity == 1: sys.stderr.write('.') elif verbosity > 1: if last_service_url != services[0].base_url: last_service_url = services[0].base_url i = 0 elif i == (terminal_width - len(services[0].base_url) - 25): i = 0 i += 1 sys.stderr.write('\rWaiting for %s ' % services[0].base_url) sys.stderr.write('%5ds ' % (timeout - (time.time() - t0))) sys.stderr.write('.' * i) sys.stderr.flush() time.sleep(0.5) if time.time() - t0 > timeout: if verbosity: sys.stderr.write('\n') raise CommandError('timeout waiting for %s' % ', '.join([x.base_url for x in services]))