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