From d9591bd03a17add0c2bd47b9f2e8c61d7c49874a Mon Sep 17 00:00:00 2001 From: Fabricio Aguiar Date: Fri, 16 Aug 2019 16:55:20 -0300 Subject: [PATCH] Kickstart models closes #5201 https://pulp.plan.io/issues/5201 --- CHANGES/5201.misc | 1 + pulp_rpm/app/migrations/0002_kickstarts.py | 112 +++++++++ pulp_rpm/app/models.py | 277 ++++++++++++++++++++- 3 files changed, 389 insertions(+), 1 deletion(-) create mode 100644 CHANGES/5201.misc create mode 100644 pulp_rpm/app/migrations/0002_kickstarts.py diff --git a/CHANGES/5201.misc b/CHANGES/5201.misc new file mode 100644 index 000000000..c8bc9f747 --- /dev/null +++ b/CHANGES/5201.misc @@ -0,0 +1 @@ +Adding Kickstart models. diff --git a/pulp_rpm/app/migrations/0002_kickstarts.py b/pulp_rpm/app/migrations/0002_kickstarts.py new file mode 100644 index 000000000..c005b49d8 --- /dev/null +++ b/pulp_rpm/app/migrations/0002_kickstarts.py @@ -0,0 +1,112 @@ +# Generated by Django 2.2.4 on 2019-08-30 20:05 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0004_add_duplicated_reserved_resources'), + ('rpm', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='DistributionTree', + fields=[ + ('content_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='rpm_distributiontree', serialize=False, to='core.Content')), + ('header_version', models.CharField(max_length=10)), + ('release_name', models.CharField(max_length=50)), + ('release_short', models.CharField(max_length=20)), + ('release_version', models.CharField(max_length=10)), + ('release_is_layered', models.BooleanField(default=False)), + ('base_product_name', models.CharField(max_length=50, null=True)), + ('base_product_short', models.CharField(max_length=20, null=True)), + ('base_product_version', models.CharField(max_length=10, null=True)), + ('arch', models.CharField(max_length=30)), + ('build_timestamp', models.FloatField()), + ('instimage', models.CharField(max_length=50, null=True)), + ('mainimage', models.CharField(max_length=50, null=True)), + ('discnum', models.IntegerField(null=True)), + ('totaldiscs', models.IntegerField(null=True)), + ], + options={ + 'default_related_name': '%(app_label)s_%(model_name)s', + 'unique_together': {('header_version', 'release_name', 'release_short', 'release_version', 'arch', 'build_timestamp')}, + }, + bases=('core.content',), + ), + migrations.CreateModel( + name='Variant', + fields=[ + ('_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('_created', models.DateTimeField(auto_now_add=True)), + ('_last_updated', models.DateTimeField(auto_now=True, null=True)), + ('variant_id', models.CharField(max_length=50)), + ('uid', models.CharField(max_length=50)), + ('name', models.CharField(max_length=50)), + ('type', models.CharField(max_length=20)), + ('packages', models.CharField(max_length=50)), + ('source_packages', models.CharField(max_length=50, null=True)), + ('source_repository', models.CharField(max_length=50, null=True)), + ('debug_packages', models.CharField(max_length=50, null=True)), + ('debug_repository', models.CharField(max_length=50, null=True)), + ('identity', models.CharField(max_length=50, null=True)), + ('distribution_tree', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='variants', to='rpm.DistributionTree')), + ('repository', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='core.Repository')), + ], + options={ + 'unique_together': {('variant_id', 'uid', 'name', 'type', 'packages', 'distribution_tree')}, + }, + ), + migrations.CreateModel( + name='Image', + fields=[ + ('_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('_created', models.DateTimeField(auto_now_add=True)), + ('_last_updated', models.DateTimeField(auto_now=True, null=True)), + ('name', models.CharField(max_length=20)), + ('path', models.CharField(max_length=128)), + ('platforms', models.CharField(max_length=20)), + ('artifact', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='core.Artifact')), + ('distribution_tree', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='rpm.DistributionTree')), + ], + options={ + 'unique_together': {('name', 'path', 'platforms', 'distribution_tree')}, + }, + ), + migrations.CreateModel( + name='Checksum', + fields=[ + ('_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('_created', models.DateTimeField(auto_now_add=True)), + ('_last_updated', models.DateTimeField(auto_now=True, null=True)), + ('path', models.CharField(max_length=128)), + ('checksum', models.CharField(max_length=128, null=True)), + ('distribution_tree', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='checksums', to='rpm.DistributionTree')), + ], + options={ + 'unique_together': {('path', 'checksum', 'distribution_tree')}, + }, + ), + migrations.CreateModel( + name='Addon', + fields=[ + ('_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('_created', models.DateTimeField(auto_now_add=True)), + ('_last_updated', models.DateTimeField(auto_now=True, null=True)), + ('addon_id', models.CharField(max_length=50)), + ('uid', models.CharField(max_length=50)), + ('name', models.CharField(max_length=50)), + ('type', models.CharField(max_length=20)), + ('packages', models.CharField(max_length=50)), + ('distribution_tree', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='addons', to='rpm.DistributionTree')), + ('repository', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='addons', to='core.Repository')), + ], + options={ + 'unique_together': {('addon_id', 'uid', 'name', 'type', 'packages', 'distribution_tree')}, + }, + ), + ] diff --git a/pulp_rpm/app/models.py b/pulp_rpm/app/models.py index 4aa480a84..96c8dd454 100644 --- a/pulp_rpm/app/models.py +++ b/pulp_rpm/app/models.py @@ -4,7 +4,15 @@ import createrepo_c as cr from django.db import models -from pulpcore.plugin.models import Content, Model, Remote, Publication, PublicationDistribution +from pulpcore.plugin.models import ( + Artifact, + Content, + Model, + Remote, + Repository, + Publication, + PublicationDistribution +) from pulp_rpm.app.constants import (CHECKSUM_CHOICES, CR_PACKAGE_ATTRS, CR_UPDATE_COLLECTION_ATTRS, @@ -784,3 +792,270 @@ class ModulemdDefaults(Content): class Meta: default_related_name = "%(app_label)s_%(model_name)s" + + +class DistributionTree(Content): + """ + Model for an RPM distribution tree (also sometimes referenced as an installable tree). + + A distribution tree is described by a file in root of an RPM repository named either + "treeinfo" or ".treeinfo". This INI file is used by system installers to boot from a URL. + It describes the operating system or product contained in the distribution tree and + where the bootable media is located for various platforms (where platform means + 'x86_64', 'xen', or similar). + + The description of the "treeinfo" format is included below, originally take from + https://release-engineering.github.io/productmd/treeinfo-1.0.html + + Fields: + header_version (Text): + Metadata version + release_name (Text): + Release name + release_short (Text): + Release short name + release_version (Text): + Release version + release_type (Text): + Release type + release_is_layered (Bool): + Typically False for an operating system, True otherwise + base_product_name (Text): + Base product name + base_product_short (Text): + Base product short name + base_product_version (Text): + Base product *major* version + base_product_type (Text): + Base product release type + arch (Text): + Tree architecture + build_timestamp (Float): + Tree build time timestamp + instimage (Text): + Relative path to Anaconda instimage + mainimage (Text): + Relative path to Anaconda stage2 image + discnum (Integer): + Disc number + totaldiscs (Integer): + Number of discs in media set + + """ + + TYPE = 'distribution_tree' + + header_version = models.CharField(max_length=10) + + release_name = models.CharField(max_length=50) + release_short = models.CharField(max_length=20) + release_version = models.CharField(max_length=10) + release_is_layered = models.BooleanField(default=False) + + base_product_name = models.CharField(max_length=50, null=True) + base_product_short = models.CharField(max_length=20, null=True) + base_product_version = models.CharField(max_length=10, null=True) + + # tree + arch = models.CharField(max_length=30) + build_timestamp = models.FloatField() + + # stage2 + instimage = models.CharField(max_length=50, null=True) + mainimage = models.CharField(max_length=50, null=True) + + # media + discnum = models.IntegerField(null=True) + totaldiscs = models.IntegerField(null=True) + + class Meta: + default_related_name = "%(app_label)s_%(model_name)s" + unique_together = ( + "header_version", + "release_name", + "release_short", + "release_version", + "arch", + "build_timestamp", + ) + + +class Checksum(Model): + """ + Distribution Tree Checksum. + + Checksums of selected files in a tree. + + Fields: + path (Text): + File path + checksum (Text): + Checksum value for the file + + Relations: + + distribution_tree (models.ForeignKey): The associated DistributionTree + + """ + + path = models.CharField(max_length=128) + checksum = models.CharField(max_length=128, null=True) + distribution_tree = models.ForeignKey( + DistributionTree, on_delete=models.CASCADE, related_name='checksums' + ) + + class Meta: + unique_together = ( + "path", + "checksum", + "distribution_tree", + ) + + +class Image(Model): + """ + Distribution Tree Image. + + Images compatible with particular platform. + + Fields: + name (Text): + File name + path (Text): + File path + platforms (Text): + Compatible platforms + + Relations: + + distribution_tree (models.ForeignKey): The associated DistributionTree + artifact (models.ForeignKey): The associated Artifact + + """ + + name = models.CharField(max_length=20) + path = models.CharField(max_length=128) + platforms = models.CharField(max_length=20) + distribution_tree = models.ForeignKey( + DistributionTree, on_delete=models.CASCADE, related_name='images' + ) + artifact = models.ForeignKey( + Artifact, on_delete=models.PROTECT, related_name='+' + ) + + class Meta: + unique_together = ( + "name", + "path", + "platforms", + "distribution_tree", + ) + + +class Addon(Model): + """ + Distribution Tree Addon. + + Kickstart functionality expansion. + + Fields: + addon_id (Text): + Addon id + uid (Text): + Addon uid + name (Text): + Addon name + type (Text): + Addon type + packages (Text): + Relative path to directory with binary RPMs + + Relations: + + distribution_tree (models.ForeignKey): The associated DistributionTree + repository (models.ForeignKey): The associated Repository + + """ + + addon_id = models.CharField(max_length=50) + uid = models.CharField(max_length=50) + name = models.CharField(max_length=50) + type = models.CharField(max_length=20) + packages = models.CharField(max_length=50) + distribution_tree = models.ForeignKey( + DistributionTree, on_delete=models.CASCADE, related_name='addons' + ) + repository = models.ForeignKey( + Repository, on_delete=models.PROTECT, related_name='addons' + ) + + class Meta: + unique_together = ( + "addon_id", + "uid", + "name", + "type", + "packages", + "distribution_tree", + ) + + +class Variant(Model): + """ + Distribution Tree Variant. + + Fields: + variant_id (Text): + Variant id + uid (Text): + Variant uid + name (Text): + Variant name + type (Text): + Variant type + packages (Text): + Relative path to directory with binary RPMs + source_packages (Text): + Relative path to directory with source RPMs + source_repository (Text): + Relative path to YUM repository with source RPMs + debug_packages (Text): + Relative path to directory with debug RPMs + debug_repository (Text): + Relative path to YUM repository with debug RPMs + identity (Text): + Relative path to a pem file that identifies a product + + Relations: + + distribution_tree (models.ForeignKey): The associated DistributionTree + repository (models.ForeignKey): The associated Repository + + """ + + variant_id = models.CharField(max_length=50) + uid = models.CharField(max_length=50) + name = models.CharField(max_length=50) + type = models.CharField(max_length=20) + packages = models.CharField(max_length=50) + source_packages = models.CharField(max_length=50, null=True) + source_repository = models.CharField(max_length=50, null=True) + debug_packages = models.CharField(max_length=50, null=True) + debug_repository = models.CharField(max_length=50, null=True) + identity = models.CharField(max_length=50, null=True) + distribution_tree = models.ForeignKey( + DistributionTree, on_delete=models.CASCADE, related_name='variants' + ) + repository = models.ForeignKey( + Repository, on_delete=models.PROTECT, related_name='+' + ) + + class Meta: + unique_together = ( + "variant_id", + "uid", + "name", + "type", + "packages", + "distribution_tree", + )