<commit_before>from django.db import transaction
from rest_framework import serializers

from nodeconductor.core import serializers as core_serializers
from nodeconductor.structure import serializers as structure_serializers, models as structure_models
from nodeconductor_openstack.openstack import (
    apps as openstack_apps, models as openstack_models, serializers as openstack_serializers)
from nodeconductor_openstack.openstack_tenant import models as openstack_tenant_models, apps as openstack_tenant_apps

from . import models


class PackageComponentSerializer(serializers.ModelSerializer):
    class Meta(object):
        model = models.PackageComponent
        fields = ('type', 'amount', 'price')


class PackageTemplateSerializer(core_serializers.AugmentedSerializerMixin,
                                serializers.HyperlinkedModelSerializer):
    price = serializers.DecimalField(max_digits=22, decimal_places=10)
    monthly_price = serializers.DecimalField(max_digits=16, decimal_places=2)
    components = PackageComponentSerializer(many=True)
    category = serializers.ReadOnlyField(source='get_category_display')

    class Meta(object):
        model = models.PackageTemplate
        fields = (
            'url', 'uuid', 'name', 'description', 'service_settings',
            'price', 'monthly_price', 'icon_url', 'components', 'category', 'archived'
        )
        extra_kwargs = {
            'url': {'lookup_field': 'uuid'},
            'service_settings': {'lookup_field': 'uuid'},
        }


def _check_template_service_settings(serializer, template):
    """ Template service settings should be in state OK and has type OpenStack """
    if template.service_settings.type != openstack_apps.OpenStackConfig.service_name:
        raise serializers.ValidationError('Template should be related to OpenStack service settings.')
    elif template.service_settings.state != structure_models.ServiceSettings.States.OK:
        raise serializers.ValidationError('Template\'s settings must be in OK state.')
    return template


def _set_tenant_quotas(tenant, template):
    components = {c.type: c.amount for c in template.components.all()}
    for quota_name, component_type in models.OpenStackPackage.get_quota_to_component_mapping().items():
        tenant.set_quota_limit(quota_name, components[component_type])


def _set_tenant_extra_configuration(tenant, template):
    tenant.extra_configuration = {
        'package_name': template.name,
        'package_uuid': template.uuid.hex,
        'package_category': template.get_category_display(),
    }
    tenant.save()


class OpenStackPackageCreateSerializer(openstack_serializers.TenantSerializer):
    template = serializers.HyperlinkedRelatedField(
        lookup_field='uuid',
        view_name='package-template-detail',
        write_only=True,
        queryset=models.PackageTemplate.objects.all())

    class Meta(openstack_serializers.TenantSerializer.Meta):
        fields = openstack_serializers.TenantSerializer.Meta.fields + ('template',)

    def validate_service_project_link(self, spl):
        user = self.context['request'].user
        if (not user.is_staff and not spl.project.has_user(user, structure_models.ProjectRole.MANAGER) and
                not spl.project.customer.has_user(user, structure_models.CustomerRole.OWNER)):
            raise serializers.ValidationError('Only staff, owner or manager can order package.')
        return spl

    def validate_template(self, template):
        template = _check_template_service_settings(self, template)

        if template.archived:
            raise serializers.ValidationError('New package cannot be created for archived template.')

        return template

    def validate(self, attrs):
        """ Additionally check that template and service project link belong to the same service settings """
        template = attrs['template']
        attrs = super(OpenStackPackageCreateSerializer, self).validate(attrs)
        spl = attrs['service_project_link']
        if spl.service.settings != template.service_settings:
            raise serializers.ValidationError(
                'Template and service project link should be connected to the same service settings.')
        return attrs

    @transaction.atomic
    def create(self, validated_data):
        """ Create tenant and service settings from it """
        template = validated_data.pop('template')
        tenant = super(OpenStackPackageCreateSerializer, self).create(validated_data)

        _set_tenant_quotas(tenant, template)
        _set_tenant_extra_configuration(tenant, template)

        service_settings = self._create_service_settings(tenant)
        package = models.OpenStackPackage.objects.create(
            tenant=tenant,
            template=template,
            service_settings=service_settings,
        )
        return package

    def _create_service_settings(self, tenant):
        """ Create service settings from tenant and connect them to tenant project. """
        admin_settings = tenant.service_project_link.service.settings
        customer = tenant.service_project_link.project.customer
        service_settings = structure_models.ServiceSettings.objects.create(
            name=tenant.name,
            scope=tenant,
            customer=customer,
            type=openstack_tenant_apps.OpenStackTenantConfig.service_name,
            backend_url=admin_settings.backend_url,
            username=tenant.user_username,
            password=<PASSWORD>,
            domain=admin_settings.domain,
            options={
                'availability_zone': tenant.availability_zone,
                'tenant_id': tenant.backend_id,
            },
        )
        service = openstack_tenant_models.OpenStackTenantService.objects.create(
            name=tenant.name,
            settings=service_settings,
            customer=customer,
        )
        openstack_tenant_models.OpenStackTenantServiceProjectLink.objects.create(
            service=service,
            project=tenant.service_project_link.project,
        )
        return service_settings


class OpenStackPackageSerializer(core_serializers.AugmentedSerializerMixin,
                                 serializers.HyperlinkedModelSerializer):
    name = serializers.CharField(source='tenant.name', read_only=True)
    description = serializers.CharField(source='tenant.description', read_only=True)

    class Meta(object):
        model = models.OpenStackPackage
        fields = ('url', 'uuid', 'name', 'description', 'template', 'tenant', 'service_settings',)
        extra_kwargs = {
            'url': {'lookup_field': 'uuid'},
            'template': {'lookup_field': 'uuid', 'view_name': 'package-template-detail', 'read_only': True},
            'tenant': {'lookup_field': 'uuid', 'view_name': 'openstack-tenant-detail', 'read_only': True},
            'service_settings': {'lookup_field': 'uuid', 'read_only': True},
        }<commit_msg>Remove service name initialization [WAL-496]<commit_after>from django.db import transaction
from rest_framework import serializers

from nodeconductor.core import serializers as core_serializers
from nodeconductor.structure import serializers as structure_serializers, models as structure_models
from nodeconductor_openstack.openstack import (
    apps as openstack_apps, models as openstack_models, serializers as openstack_serializers)
from nodeconductor_openstack.openstack_tenant import models as openstack_tenant_models, apps as openstack_tenant_apps

from . import models


class PackageComponentSerializer(serializers.ModelSerializer):
    class Meta(object):
        model = models.PackageComponent
        fields = ('type', 'amount', 'price')


class PackageTemplateSerializer(core_serializers.AugmentedSerializerMixin,
                                serializers.HyperlinkedModelSerializer):
    price = serializers.DecimalField(max_digits=22, decimal_places=10)
    monthly_price = serializers.DecimalField(max_digits=16, decimal_places=2)
    components = PackageComponentSerializer(many=True)
    category = serializers.ReadOnlyField(source='get_category_display')

    class Meta(object):
        model = models.PackageTemplate
        fields = (
            'url', 'uuid', 'name', 'description', 'service_settings',
            'price', 'monthly_price', 'icon_url', 'components', 'category', 'archived'
        )
        extra_kwargs = {
            'url': {'lookup_field': 'uuid'},
            'service_settings': {'lookup_field': 'uuid'},
        }


def _check_template_service_settings(serializer, template):
    """ Template service settings should be in state OK and has type OpenStack """
    if template.service_settings.type != openstack_apps.OpenStackConfig.service_name:
        raise serializers.ValidationError('Template should be related to OpenStack service settings.')
    elif template.service_settings.state != structure_models.ServiceSettings.States.OK:
        raise serializers.ValidationError('Template\'s settings must be in OK state.')
    return template


def _set_tenant_quotas(tenant, template):
    components = {c.type: c.amount for c in template.components.all()}
    for quota_name, component_type in models.OpenStackPackage.get_quota_to_component_mapping().items():
        tenant.set_quota_limit(quota_name, components[component_type])


def _set_tenant_extra_configuration(tenant, template):
    tenant.extra_configuration = {
        'package_name': template.name,
        'package_uuid': template.uuid.hex,
        'package_category': template.get_category_display(),
    }
    tenant.save()


class OpenStackPackageCreateSerializer(openstack_serializers.TenantSerializer):
    template = serializers.HyperlinkedRelatedField(
        lookup_field='uuid',
        view_name='package-template-detail',
        write_only=True,
        queryset=models.PackageTemplate.objects.all())

    class Meta(openstack_serializers.TenantSerializer.Meta):
        fields = openstack_serializers.TenantSerializer.Meta.fields + ('template',)

    def validate_service_project_link(self, spl):
        user = self.context['request'].user
        if (not user.is_staff and not spl.project.has_user(user, structure_models.ProjectRole.MANAGER) and
                not spl.project.customer.has_user(user, structure_models.CustomerRole.OWNER)):
            raise serializers.ValidationError('Only staff, owner or manager can order package.')
        return spl

    def validate_template(self, template):
        template = _check_template_service_settings(self, template)

        if template.archived:
            raise serializers.ValidationError('New package cannot be created for archived template.')

        return template

    def validate(self, attrs):
        """ Additionally check that template and service project link belong to the same service settings """
        template = attrs['template']
        attrs = super(OpenStackPackageCreateSerializer, self).validate(attrs)
        spl = attrs['service_project_link']
        if spl.service.settings != template.service_settings:
            raise serializers.ValidationError(
                'Template and service project link should be connected to the same service settings.')
        return attrs

    @transaction.atomic
    def create(self, validated_data):
        """ Create tenant and service settings from it """
        template = validated_data.pop('template')
        tenant = super(OpenStackPackageCreateSerializer, self).create(validated_data)

        _set_tenant_quotas(tenant, template)
        _set_tenant_extra_configuration(tenant, template)

        service_settings = self._create_service_settings(tenant)
        package = models.OpenStackPackage.objects.create(
            tenant=tenant,
            template=template,
            service_settings=service_settings,
        )
        return package

    def _create_service_settings(self, tenant):
        """ Create service settings from tenant and connect them to tenant project. """
        admin_settings = tenant.service_project_link.service.settings
        customer = tenant.service_project_link.project.customer
        service_settings = structure_models.ServiceSettings.objects.create(
            name=tenant.name,
            scope=tenant,
            customer=customer,
            type=openstack_tenant_apps.OpenStackTenantConfig.service_name,
            backend_url=admin_settings.backend_url,
            username=tenant.user_username,
            password=<PASSWORD>,
            domain=admin_settings.domain,
            options={
                'availability_zone': tenant.availability_zone,
                'tenant_id': tenant.backend_id,
            },
        )
        service = openstack_tenant_models.OpenStackTenantService.objects.create(
            settings=service_settings,
            customer=customer,
        )
        openstack_tenant_models.OpenStackTenantServiceProjectLink.objects.create(
            service=service,
            project=tenant.service_project_link.project,
        )
        return service_settings


class OpenStackPackageSerializer(core_serializers.AugmentedSerializerMixin,
                                 serializers.HyperlinkedModelSerializer):
    name = serializers.CharField(source='tenant.name', read_only=True)
    description = serializers.CharField(source='tenant.description', read_only=True)

    class Meta(object):
        model = models.OpenStackPackage
        fields = ('url', 'uuid', 'name', 'description', 'template', 'tenant', 'service_settings',)
        extra_kwargs = {
            'url': {'lookup_field': 'uuid'},
            'template': {'lookup_field': 'uuid', 'view_name': 'package-template-detail', 'read_only': True},
            'tenant': {'lookup_field': 'uuid', 'view_name': 'openstack-tenant-detail', 'read_only': True},
            'service_settings': {'lookup_field': 'uuid', 'read_only': True},
        }
{'fraction_non_alphanumeric': 0.08966358223943763, 'fraction_numerical': 0.0012194247184563517, 'mean_word_length': 3.3787688442211055, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4624505', 'n_tokens_mistral': 3565, 'n_tokens_neox': 3538, 'n_words': 765}