class RepositoryVersionSerializer(ModelSerializer, NestedHyperlinkedModelSerializer): pulp_href = RepositoryVersionIdentityField() number = serializers.IntegerField(read_only=True) repository = DetailRelatedField( view_name_pattern=r"repositories(-.*/.*)?-detail", read_only=True, ) base_version = RepositoryVersionRelatedField( required=False, help_text=_( "A repository version whose content was used as the initial set of content " "for this repository version"), ) content_summary = ContentSummarySerializer( help_text= _("Various count summaries of the content in the version and the HREF to view them." ), source="*", read_only=True, ) class Meta: model = models.RepositoryVersion fields = ModelSerializer.Meta.fields + ( "pulp_href", "number", "repository", "base_version", "content_summary", )
class RepositoryAddRemoveContentSerializer(ModelSerializer, NestedHyperlinkedModelSerializer): add_content_units = serializers.ListField( help_text=_( "A list of content units to add to a new repository version. This content is " "added after remove_content_units are removed."), required=False, ) remove_content_units = serializers.ListField( help_text= _("A list of content units to remove from the latest repository version. " "You may also specify '*' as an entry to remove all content. This content is " "removed before add_content_units are added."), required=False, ) base_version = RepositoryVersionRelatedField( required=False, help_text=_( "A repository version whose content will be used as the initial set of content " "for the new repository version"), ) def validate_remove_content_units(self, value): if len(value) > 1 and "*" in value: raise serializers.ValidationError( "Cannot supply content units and '*'.") return value class Meta: model = models.RepositoryVersion fields = ["add_content_units", "remove_content_units", "base_version"]
class ReclaimSpaceSerializer(serializers.Serializer, ValidateFieldsMixin): """ Serializer for reclaim disk space operation. """ repo_hrefs = fields.ListField( required=True, help_text=_("Will reclaim space for the specified list of repos."), ) repo_versions_keeplist = RepositoryVersionRelatedField( help_text=_("Will exclude repo versions from space reclaim."), many=True, required=False, ) def validate_repo_hrefs(self, value): """ Check that the repo_hrefs is not an empty list and contains all valid hrefs. Args: value (list): The list supplied by the user Returns: The list of pks (not hrefs) after validation Raises: ValidationError: If the list is empty or contains invalid hrefs. """ if len(value) == 0: raise serializers.ValidationError("Must not be [].") from pulpcore.app.viewsets import NamedModelViewSet hrefs_to_return = [] for href in value: hrefs_to_return.append( NamedModelViewSet.get_resource(href, Repository)) return hrefs_to_return
class RepositoryVersionSerializer(ModelSerializer, NestedHyperlinkedModelSerializer): pulp_href = RepositoryVersionIdentityField() number = serializers.IntegerField(read_only=True) base_version = RepositoryVersionRelatedField( required=False, help_text=_( 'A repository version whose content was used as the initial set of content ' 'for this repository version'), ) content_summary = ContentSummarySerializer( help_text=_( 'Various count summaries of the content in the version and the HREF to view ' 'them.'), source="*", read_only=True, ) class Meta: model = models.RepositoryVersion fields = ModelSerializer.Meta.fields + ( 'pulp_href', 'number', 'base_version', 'content_summary', )
class FilesystemExportSerializer(ExportSerializer): """ Serializer for FilesystemExports. """ publication = DetailRelatedField( required=False, help_text=_("A URI of the publication to be exported."), view_name_pattern=r"publications(-.*/.*)-detail", queryset=models.Publication.objects.all(), write_only=True, ) repository_version = RepositoryVersionRelatedField( help_text=_("A URI of the repository version export."), required=False, write_only=True, ) def validate(self, data): if ("publication" not in data and "repository_version" not in data) or ( "publication" in data and "repository_version" in data ): raise serializers.ValidationError( _("publication or repository_version must either be supplied but not both.") ) return data class Meta: model = models.FilesystemExport fields = ExportSerializer.Meta.fields + ("publication", "repository_version")
class RepositoryVersionDistributionSerializer(BaseDistributionSerializer): repository = DetailRelatedField( required=False, help_text=_( "The latest RepositoryVersion for this Repository will be served." ), view_name_pattern=r"repositories(-.*/.*)?-detail", queryset=models.Repository.objects.all(), allow_null=True, ) repository_version = RepositoryVersionRelatedField( required=False, help_text=_("RepositoryVersion to be served"), allow_null=True) def __init__(self, *args, **kwargs): """Initialize a RepositoryVersionDistributionSerializer and emit deprecation warnings""" deprecation_logger.warn( _("PublicationDistributionSerializer is deprecated and could be removed as early as " "pulpcore==3.13; use pulpcore.plugin.serializers.DistributionSerializer instead. " "See its docstring for more details.")) return super().__init__(*args, **kwargs) class Meta: abstract = True fields = BaseDistributionSerializer.Meta.fields + ( "repository", "repository_version") def validate(self, data): super().validate(data) repository_in_data = "repository" in data repository_version_in_data = "repository_version" in data repository_in_instance = self.instance.repository if self.instance else None repository_version_in_instance = self.instance.repository_version if self.instance else None if repository_in_data and repository_version_in_data: error = True elif repository_in_data and repository_version_in_instance: error = True elif repository_in_instance and repository_version_in_data: error = True else: error = False if error: msg = _( "Only one of the attributes 'publication' and 'repository_version' may be used " "simultaneously.") raise serializers.ValidationError(msg) return data
class RepositoryVersionDistributionSerializer(BaseDistributionSerializer): repository = DetailRelatedField( required=False, help_text=_( "The latest RepositoryVersion for this Repository will be served." ), view_name_pattern=r"repositories(-.*/.*)?-detail", queryset=models.Repository.objects.all(), allow_null=True, ) repository_version = RepositoryVersionRelatedField( required=False, help_text=_("RepositoryVersion to be served"), allow_null=True, ) class Meta: abstract = True fields = BaseDistributionSerializer.Meta.fields + ( "repository", "repository_version", ) def validate(self, data): super().validate(data) repository_in_data = "repository" in data repository_version_in_data = "repository_version" in data repository_in_instance = self.instance.repository if self.instance else None repository_version_in_instance = self.instance.repository_version if self.instance else None if repository_in_data and repository_version_in_data: error = True elif repository_in_data and repository_version_in_instance: error = True elif repository_in_instance and repository_version_in_data: error = True else: error = False if error: msg = _( "The attributes 'repository' and 'repository_version' must be used" " exclusively.") raise serializers.ValidationError(msg) return data
class PublicationSerializer(ModelSerializer): pulp_href = DetailIdentityField( view_name_pattern=r"publications(-.*/.*)-detail") repository_version = RepositoryVersionRelatedField(required=False) repository = DetailRelatedField( help_text=_("A URI of the repository to be published."), required=False, label=_("Repository"), view_name_pattern=r"repositories(-.*/.*)?-detail", queryset=models.Repository.objects.all(), ) def validate(self, data): if hasattr(self, "initial_data"): validate_unknown_fields(self.initial_data, self.fields) repository = data.pop("repository", None) # not an actual field on publication repository_version = data.get("repository_version") if not repository and not repository_version: raise serializers.ValidationError( _("Either the 'repository' or 'repository_version' need to be specified" )) elif not repository and repository_version: return data elif repository and not repository_version: version = repository.latest_version() if version: new_data = {"repository_version": version} new_data.update(data) return new_data else: raise serializers.ValidationError(detail=_( "Repository has no version available to create Publication from" )) raise serializers.ValidationError( _("Either the 'repository' or 'repository_version' need to be specified " "but not both.")) class Meta: abstract = True model = models.Publication fields = ModelSerializer.Meta.fields + ("repository_version", "repository")
class Pulp2RepositoriesSerializer(ModelSerializer): """ A serializer for the Pulp2Repositories """ pulp_href = IdentityField(view_name='pulp2repositories-detail') pulp2_object_id = serializers.CharField(max_length=255) pulp2_repo_id = serializers.CharField() pulp2_repo_type = serializers.CharField() is_migrated = serializers.BooleanField(default=False) not_in_plan = serializers.BooleanField(default=False) pulp3_repository_version = RepositoryVersionRelatedField( required=False, help_text=_('RepositoryVersion to be served'), allow_null=True, ) pulp3_remote_href = serializers.SerializerMethodField(read_only=True) pulp3_publication_href = serializers.SerializerMethodField(read_only=True) pulp3_distribution_hrefs = serializers.SerializerMethodField( read_only=True) pulp3_repository_href = serializers.SerializerMethodField(read_only=True) @extend_schema_serializer(many=False) def get_pulp3_repository_href(self, obj): """ Get pulp3_repository_href from pulp2repo """ rv = obj.pulp3_repository_version if rv: return get_pulp_href(rv.repository) @extend_schema_serializer(many=False) def get_pulp3_remote_href(self, obj): """ Get pulp3_remote_href from pulp2repo """ remote = obj.pulp3_repository_remote if remote: return get_pulp_href(remote) @extend_schema_serializer(many=False) def get_pulp3_publication_href(self, obj): """ Get pulp3_publication_href from pulp3_repository_version """ rv = obj.pulp3_repository_version if rv: return get_pulp_href(rv.publication_set.first()) @extend_schema_serializer(many=True) def get_pulp3_distribution_hrefs(self, obj): """ Get pulp3_distribution_hrefs from pulp3_repository_version """ dist_list = [] rv = obj.pulp3_repository_version if not rv: return dist_list result_qs = rv.publication_set.all() if result_qs: # repo_version has publication, therefore is a PublicationDistribution for publication in result_qs: distribution_type = f'{publication.pulp_type.replace(".", "_")}distribution' plugin_distribution = getattr(publication, distribution_type) dist_list.extend(plugin_distribution.all()) else: # empty result_qs means that repo_version does not need publication, # or repo_version was not published/distributed pulp_type = rv.repository.pulp_type distribution_type = f'{pulp_type.replace(".", "_")}distribution' if hasattr(rv, distribution_type): # repo_version is distributed directly trough RepositoryDistribution plugin_distribution = getattr(rv, distribution_type) dist_list = plugin_distribution.all() else: # repo_version was not published/distributed return dist_list return [get_pulp_href(dist) for dist in dist_list] class Meta: fields = ModelSerializer.Meta.fields + ( "pulp2_object_id", "pulp2_repo_id", "pulp2_repo_type", "is_migrated", "not_in_plan", "pulp3_repository_version", "pulp3_remote_href", "pulp3_publication_href", "pulp3_distribution_hrefs", "pulp3_repository_href", ) model = Pulp2Repository
class PulpExportSerializer(ExportSerializer): """ Serializer for PulpExports. """ output_file_info = serializers.JSONField( help_text= _("Dictionary of filename: sha256hash entries for export-output-file(s)" ), read_only=True, ) toc_info = serializers.JSONField( help_text=_( "Filename and sha256-checksum of table-of-contents for this export" ), read_only=True, ) dry_run = serializers.BooleanField( help_text=_( "Generate report on what would be exported and disk-space required." ), default=False, required=False, write_only=True, ) full = serializers.BooleanField( help_text=_("Do a Full (true) or Incremental (false) export."), default=True, required=False, write_only=True, ) versions = RepositoryVersionRelatedField( help_text= _("List of explicit repo-version hrefs to export (replaces current_version)." ), many=True, required=False, write_only=True, ) MAX_CHUNK_BYTES = 1024 * 1024 * 1024 * 1024 # 1 TB chunk_size = serializers.CharField( help_text=_( "Chunk export-tarfile into pieces of chunk_size bytes. " + "Recognizes units of B/KB/MB/GB/TB. A chunk has a maximum " + "size of 1TB."), required=False, write_only=True, ) start_versions = RepositoryVersionRelatedField( help_text= _("List of explicit last-exported-repo-version hrefs (replaces last_export)." ), many=True, required=False, write_only=True, ) def _validate_versions_to_repos(self, the_versions): """ If specifying repo-versions explicitly, must provide a version for each exporter-repository """ # make sure counts match the_exporter = self.context.get("exporter", None) num_repos = the_exporter.repositories.count() if num_repos != len(the_versions): raise serializers.ValidationError( _("Number of versions ({}) does not match the number of Repositories ({}) for " + "the owning Exporter!").format(num_repos, len(the_versions))) # make sure the specified versions 'belong to' the exporter.repositories exporter_repos = set(the_exporter.repositories.all()) version_repos = set([vers.repository for vers in the_versions]) if exporter_repos != version_repos: raise serializers.ValidationError( _("Requested RepositoryVersions must belong to the Repositories named by the " + "Exporter!")) return the_versions def validate_versions(self, versions): return self._validate_versions_to_repos(versions) def validate_start_versions(self, start_versions): return self._validate_versions_to_repos(start_versions) def validate(self, data): # If we requested start_versions, make sure we did not forget to specify full=False if data.get("start_versions", None) and data.get("full", True): raise serializers.ValidationError( _("start_versions is only valid for incremental exports (full=False)" )) # If we requested full=False, make sure we either specified start_versions= or # have a previous-export for our Exporter. if not data.get("full", True): the_exporter = self.context.get("exporter", None) if not data.get("start_versions", None) and not the_exporter.last_export: raise serializers.ValidationError( _("Incremental export can only be requested when there is a previous export " + "or start_versions= has been specified.")) return super().validate(data) @staticmethod def _parse_size(size): try: # based on https://stackoverflow.com/a/42865957/2002471 units = { "B": 1, "KB": 2**10, "MB": 2**20, "GB": 2**30, "TB": 2**40 } size = size.upper() if not re.match(r" ", size): size = re.sub(r"([KMGT]?B)", r" \1", size) number, unit = [string.strip() for string in size.split()] return int(float(number) * units[unit]) except ValueError: raise serializers.ValidationError( _("chunk_size '{}' is not valid (valid units are B/KB/MB/GB/TB)" ).format(size)) def validate_chunk_size(self, chunk_size): the_size = self._parse_size(chunk_size) if the_size <= 0: raise serializers.ValidationError( _("Chunk size {} is not greater than zero!").format(the_size)) if the_size > self.MAX_CHUNK_BYTES: raise serializers.ValidationError( _("Chunk size in bytes {} is greater than max-chunk-size {}!"). format(the_size, self.MAX_CHUNK_BYTES)) return the_size class Meta: model = models.PulpExport fields = ExportSerializer.Meta.fields + ( "full", "dry_run", "versions", "chunk_size", "output_file_info", "start_versions", "toc_info", )
class Pulp2RepositoriesSerializer(ModelSerializer): """ A serializer for the Pulp2Repositories """ pulp_href = IdentityField( view_name='pulp2repositories-detail' ) pulp2_object_id = serializers.CharField(max_length=255) pulp2_repo_id = serializers.CharField() pulp2_repo_type = serializers.CharField() is_migrated = serializers.BooleanField(default=False) not_in_plan = serializers.BooleanField(default=False) pulp3_repository_version = RepositoryVersionRelatedField( required=False, help_text=_('RepositoryVersion to be served'), allow_null=True, ) pulp3_remote_href = serializers.SerializerMethodField(read_only=True) pulp3_publication_href = serializers.SerializerMethodField(read_only=True) pulp3_distribution_hrefs = serializers.SerializerMethodField(read_only=True) pulp3_repository_href = serializers.SerializerMethodField(read_only=True) @extend_schema_field(field=serializers.CharField) def get_pulp3_repository_href(self, obj): """ Get pulp3_repository_href from pulp2repo """ rv = obj.pulp3_repository_version if rv: return get_pulp_href(rv.repository) @extend_schema_field(field=serializers.CharField) def get_pulp3_remote_href(self, obj): """ Get pulp3_remote_href from pulp2repo """ remote = obj.pulp3_repository_remote if remote: return get_pulp_href(remote) @extend_schema_field(field=serializers.CharField) def get_pulp3_publication_href(self, obj): """ Get pulp3_publication_href from pulp3_repository_version """ rv = obj.pulp3_repository_version if rv: return get_pulp_href(rv.publication_set.filter(complete=True).first()) @extend_schema_field(field=serializers.ListField(child=serializers.CharField())) def get_pulp3_distribution_hrefs(self, obj): """ Get pulp3_distribution_hrefs from pulp3_repository_version """ pulp2dists = obj.pulp2_dists.filter(not_in_plan=False, is_migrated=True) return [get_pulp_href(dist.pulp3_distribution) for dist in pulp2dists] class Meta: fields = ModelSerializer.Meta.fields + ( "pulp2_object_id", "pulp2_repo_id", "pulp2_repo_type", "is_migrated", "not_in_plan", "pulp3_repository_version", "pulp3_remote_href", "pulp3_publication_href", "pulp3_distribution_hrefs", "pulp3_repository_href", ) model = Pulp2Repository