Пример #1
0
class TenantImportSerializer(serializers.HyperlinkedModelSerializer):
    service_project_link = serializers.HyperlinkedRelatedField(
        view_name='openstack-spl-detail',
        write_only=True,
        queryset=models.OpenStackServiceProjectLink.objects.all(),
    )
    quotas = quotas_serializers.QuotaSerializer(many=True, read_only=True)

    class Meta:
        model = models.Tenant
        read_only_fields = (
            'name',
            'uuid',
            'availability_zone',
            'internal_network_id',
            'external_network_id',
            'user_username',
            'user_password',
            'quotas',
        )
        fields = read_only_fields + ('service_project_link', 'backend_id')

    @transaction.atomic
    def create(self, validated_data):
        service_project_link = validated_data['service_project_link']
        backend = service_project_link.service.get_backend()
        backend_id = validated_data['backend_id']

        if models.Tenant.objects.filter(
            service_project_link__service__settings=service_project_link.service.settings,
            backend_id=backend_id,
        ).exists():
            raise serializers.ValidationError(
                _('Tenant with ID "%s" is already registered.') % backend_id
            )

        try:
            tenant = backend.import_tenant(backend_id, service_project_link)
        except OpenStackBackendError as e:
            raise serializers.ValidationError(
                {
                    'backend_id': _(
                        'Can\'t import tenant with ID %(backend_id)s. Reason: %(reason)s'
                    )
                    % {'backend_id': backend_id, 'reason': e,}
                }
            )

        tenant.user_username = models.Tenant.generate_username(tenant.name)
        tenant.user_password = core_utils.pwgen()
        tenant.save()

        return tenant
Пример #2
0
class TenantSerializer(structure_serializers.PrivateCloudSerializer):
    service = serializers.HyperlinkedRelatedField(
        source='service_project_link.service',
        view_name='openstack-detail',
        read_only=True,
        lookup_field='uuid')
    service_project_link = serializers.HyperlinkedRelatedField(
        view_name='openstack-spl-detail',
        queryset=models.OpenStackServiceProjectLink.objects.all(),
        write_only=True)
    quotas = quotas_serializers.QuotaSerializer(many=True, read_only=True)
    subnet_cidr = serializers.CharField(validators=[subnet_cidr_validator],
                                        default='192.168.42.0/24',
                                        initial='192.168.42.0/24',
                                        write_only=True)

    class Meta(structure_serializers.PrivateCloudSerializer.Meta):
        model = models.Tenant
        fields = structure_serializers.PrivateCloudSerializer.Meta.fields + (
            'availability_zone',
            'internal_network_id',
            'external_network_id',
            'user_username',
            'user_password',
            'quotas',
            'subnet_cidr',
        )
        read_only_fields = structure_serializers.PrivateCloudSerializer.Meta.read_only_fields + (
            'internal_network_id',
            'external_network_id',
        )
        protected_fields = structure_serializers.PrivateCloudSerializer.Meta.protected_fields + (
            'user_username',
            'subnet_cidr',
            'user_password',
        )

    def get_fields(self):
        fields = super(TenantSerializer, self).get_fields()
        if not settings.WALDUR_OPENSTACK['TENANT_CREDENTIALS_VISIBLE']:
            for field in ('user_username', 'user_password', 'access_url'):
                if field in fields:
                    del fields[field]

        return fields

    def _validate_service_project_link(self, spl):
        """ Administrator can create tenant only using not shared service settings """
        user = self.context['request'].user
        message = _(
            'You do not have permissions to create tenant in this project using selected service.'
        )
        if spl.service.settings.shared and not user.is_staff:
            raise serializers.ValidationError(message)
        if not spl.service.settings.shared and not structure_permissions._has_admin_access(
                user, spl.project):
            raise serializers.ValidationError(message)
        return spl

    def validate_security_groups_configuration(self):
        nc_settings = getattr(settings, 'WALDUR_OPENSTACK', {})
        config_groups = nc_settings.get('DEFAULT_SECURITY_GROUPS', [])
        for group in config_groups:
            sg_name = group.get('name')
            if sg_name in (None, ''):
                raise serializers.ValidationError(
                    _('Skipping misconfigured security group: parameter "name" not found or is empty.'
                      ))

            rules = group.get('rules')
            if type(rules) not in (list, tuple):
                raise serializers.ValidationError(
                    _('Skipping misconfigured security group: parameter "rules" should be list or tuple.'
                      ))

    def _get_neighbour_tenants(self, service_settings):
        domain = service_settings.domain
        backend_url = service_settings.backend_url
        tenants = models.Tenant.objects.filter(
            service_project_link__service__settings__backend_url=backend_url)
        if domain in (None, '', 'default'):
            tenants = tenants.filter(
                Q(service_project_link__service__settings__domain='') |
                Q(service_project_link__service__settings__domain__isnull=True)
                | Q(service_project_link__service__settings__domain__iexact=
                    'default'))
        else:
            tenants = tenants.filter(
                service_project_link__service__settings__domain=domain)
        return tenants

    def _validate_tenant_name(self, service_settings, tenant_name):
        neighbour_tenants = self._get_neighbour_tenants(service_settings)
        existing_tenant_names = [service_settings.options.get('tenant_name', 'admin')] +\
            list(neighbour_tenants.values_list('name', flat=True))
        if tenant_name in existing_tenant_names:
            raise serializers.ValidationError({
                'name':
                _('Name "%s" is already registered. Please choose another one.'
                  % tenant_name),
            })

    def _validate_username(self, service_settings, username):
        neighbour_tenants = self._get_neighbour_tenants(service_settings)
        existing_usernames = [service_settings.username] + \
            list(neighbour_tenants.values_list('user_username', flat=True))
        if username in existing_usernames:
            raise serializers.ValidationError({
                'user_username':
                _('Name "%s" is already registered. Please choose another one.'
                  ) % username
            })

        blacklisted_usernames = service_settings.options.get(
            'blacklisted_usernames',
            settings.WALDUR_OPENSTACK['DEFAULT_BLACKLISTED_USERNAMES'])
        if username in blacklisted_usernames:
            raise serializers.ValidationError({
                'user_username':
                _('Name "%s" cannot be used as tenant user username.') %
                username
            })

    def validate(self, attrs):
        attrs = super(TenantSerializer, self).validate(attrs)

        if not self.instance:
            self._validate_service_project_link(attrs['service_project_link'])

        self.validate_security_groups_configuration()

        if self.instance is not None:
            service_settings = self.instance.service_project_link.service.settings
        else:
            service_settings = attrs['service_project_link'].service.settings

        # validate tenant name
        if self.instance is not None and attrs.get('name'):
            if self.instance.name != attrs['name']:
                self._validate_tenant_name(service_settings, attrs['name'])
        else:
            self._validate_tenant_name(service_settings, attrs['name'])

        # username generation/validation
        if self.instance is not None or not settings.WALDUR_OPENSTACK[
                'TENANT_CREDENTIALS_VISIBLE']:
            return attrs
        else:
            if not attrs.get('user_username'):
                attrs['user_username'] = models.Tenant.generate_username(
                    attrs['name'])

            self._validate_username(service_settings,
                                    attrs.get('user_username'))

        return attrs

    def create(self, validated_data):
        spl = validated_data['service_project_link']
        # get availability zone from service settings if it is not defined
        if not validated_data.get('availability_zone'):
            validated_data[
                'availability_zone'] = spl.service.settings.get_option(
                    'availability_zone') or ''
        # init tenant user username(if not defined) and password
        slugified_name = slugify(validated_data['name'])[:25]
        if not validated_data.get('user_username'):
            validated_data['user_username'] = models.Tenant.generate_username(
                validated_data['name'])
        validated_data['user_password'] = core_utils.pwgen()

        subnet_cidr = validated_data.pop('subnet_cidr')
        with transaction.atomic():
            tenant = super(TenantSerializer, self).create(validated_data)
            network = models.Network.objects.create(
                name=slugified_name + '-int-net',
                description=_('Internal network for tenant %s') % tenant.name,
                tenant=tenant,
                service_project_link=tenant.service_project_link,
            )
            models.SubNet.objects.create(
                name=slugified_name + '-sub-net',
                description=_('SubNet for tenant %s internal network') %
                tenant.name,
                network=network,
                service_project_link=tenant.service_project_link,
                cidr=subnet_cidr,
                allocation_pools=_generate_subnet_allocation_pool(subnet_cidr),
                dns_nameservers=spl.service.settings.options.get(
                    'dns_nameservers', []))

            nc_settings = getattr(settings, 'WALDUR_OPENSTACK', {})
            config_groups = copy.deepcopy(
                nc_settings.get('DEFAULT_SECURITY_GROUPS', []))

            for group in config_groups:
                sg_name = group.get('name')
                sg_description = group.get('description', None)
                sg = models.SecurityGroup.objects.get_or_create(
                    service_project_link=tenant.service_project_link,
                    tenant=tenant,
                    description=sg_description,
                    name=sg_name)[0]

                for rule in group.get('rules'):
                    if 'icmp_type' in rule:
                        rule['from_port'] = rule.pop('icmp_type')
                    if 'icmp_code' in rule:
                        rule['to_port'] = rule.pop('icmp_code')

                    try:
                        rule = models.SecurityGroupRule(security_group=sg,
                                                        **rule)
                        rule.full_clean()
                    except serializers.ValidationError as e:
                        logger.error(
                            'Failed to create rule for security group %s: %s.'
                            % (sg_name, e))
                    else:
                        rule.save()

        return tenant