Ejemplo n.º 1
0
 def test_deserialize(self):
     hdr = Header(None)
     data = {
         "header": {
             "version": "1.0",
         }
     }
     hdr.deserialize(data)
     self.assertEqual(hdr.version, "1.0")
Ejemplo n.º 2
0
 def test_deserialize(self):
     hdr = Header(None)
     data = {
         "header": {
             "version": "1.0",
         }
     }
     hdr.deserialize(data)
     self.assertEqual(hdr.version, "1.0")
Ejemplo n.º 3
0
    def __init__(self):
        super(ComposeInfo, self).__init__()

        self.header = Header(self, "productmd.composeinfo")     #: (:class:`.Header`) -- Metadata header
        self.compose = Compose(self)            #: (:class:`.Compose`) -- Compose details
        self.release = Release(self)            #: (:class:`.Release`) -- Release details
        self.base_product = BaseProduct(self)   #: (:class:`.BaseProduct`) -- Base product details (optional)
        self.variants = Variants(self)          #: (:class:`.Variants`) -- release variants
        self.validate()
        self.header.set_current_version()
Ejemplo n.º 4
0
 def test_deserialize(self):
     hdr = Header(None, "productmd.header")
     data = {
         "header": {
             "type": "productmd.header",
             "version": "1.0",
         }
     }
     hdr.deserialize(data)
     self.assertEqual(hdr.version, "1.0")
Ejemplo n.º 5
0
 def test_deserialize(self):
     hdr = Header(None, "productmd.header")
     data = {
         "header": {
             "type": "productmd.header",
             "version": "1.0",
         }
     }
     hdr.deserialize(data)
     self.assertEqual(hdr.version, "1.0")
Ejemplo n.º 6
0
 def test_serialize(self):
     hdr = Header(None)
     hdr.version = "1.0"
     serialized_data = {}
     hdr.serialize(serialized_data)
     expected_data = {
         "header": {
             "version": "1.0",
         }
     }
     self.assertEqual(serialized_data, expected_data)
Ejemplo n.º 7
0
 def test_serialize(self):
     hdr = Header(None)
     hdr.version = "1.0"
     serialized_data = {}
     hdr.serialize(serialized_data)
     expected_data = {
         "header": {
             "version": "1.0",
         }
     }
     self.assertEqual(serialized_data, expected_data)
Ejemplo n.º 8
0
 def test_serialize(self):
     hdr = Header(None, "productmd.header")
     hdr.version = "1.0"
     serialized_data = {}
     hdr.serialize(serialized_data)
     expected_data = {
         "header": {
             "type": "productmd.header",
             "version": "1.2",
         }
     }
     self.assertEqual(serialized_data, expected_data)
Ejemplo n.º 9
0
 def test_serialize(self):
     hdr = Header(None, "productmd.header")
     hdr.version = "1.0"
     serialized_data = {}
     hdr.serialize(serialized_data)
     expected_data = {
         "header": {
             "type": "productmd.header",
             "version": "1.2",
         }
     }
     self.assertEqual(serialized_data, expected_data)
Ejemplo n.º 10
0
    def __init__(self):
        super(ComposeInfo, self).__init__()

        self.header = Header(self)              #: (:class:`.Header`) -- Metadata header
        self.compose = Compose(self)            #: (:class:`.Compose`) -- Compose details
        self.release = Release(self)            #: (:class:`.Release`) -- Release details
        self.base_product = BaseProduct(self)   #: (:class:`.BaseProduct`) -- Base product details (optional)
        self.variants = Variants(self)          #: (:class:`.Variants`) -- release variants
        self.validate()
        self.header.set_current_version()
Ejemplo n.º 11
0
 def __init__(self):
     super(productmd.common.MetadataBase, self)
     self.header = Header(self)              #: (:class:`productmd.common.Header`) -- Metadata header
     self.release = Release(self)            #: (:class:`.Release`) -- Release details
     self.base_product = BaseProduct(self)   #: (:class:`.BaseProduct`) -- Base product details (optional)
     self.tree = Tree(self)                  #: (:class:`.Tree`) -- Tree details
     self.variants = Variants(self)          #: (:class:`.Variants`) -- Release variants
     self.checksums = Checksums(self)        #: (:class:`.Checksums`) -- Checksums of images included in a tree
     self.images = Images(self)              #: (:class:`.Images`) -- Paths to images included in a tree
     self.stage2 = Stage2(self)              #: (:class:`.Stage2`) -- Stage 2 image path (for Anaconda installer)
     self.media = Media(self)                #: (:class:`.Media`) -- Media set information (optional)
Ejemplo n.º 12
0
    def test_version(self):
        hdr = Header(None)

        # empty version
        hdr.version = None
        self.assertRaises(TypeError, hdr.validate)

        # invalid version
        hdr.version = "first"
        self.assertRaises(ValueError, hdr.validate)

        hdr.version = "1.alpha2"
        self.assertRaises(ValueError, hdr.validate)

        hdr.version = "1"
        self.assertRaises(ValueError, hdr.validate)

        # valid version
        hdr.version = "1.22"
        hdr.validate()
Ejemplo n.º 13
0
class Images(productmd.common.MetadataBase):
    def __init__(self):
        super(Images, self).__init__()
        self.header = Header(self)
        self.compose = Compose(self)
        self.images = {}

    def __getitem__(self, variant):
        return self.images[variant]

    def __delitem__(self, variant):
        del self.images[variant]

    def serialize(self, parser):
        data = parser
        self.header.serialize(data)
        data["payload"] = {}
        data["payload"]["images"] = {}
        self.compose.serialize(data["payload"])
        for variant in self.images:
            for arch in self.images[variant]:
                for image_obj in self.images[variant][arch]:
                    images = data["payload"]["images"].setdefault(
                        variant, {}).setdefault(arch, [])
                    image_obj.serialize(images)
                    images.sort(key=lambda x: x["path"])
        return data

    def deserialize(self, data):
        self.header.deserialize(data)
        self.compose.deserialize(data["payload"])
        for variant in data["payload"]["images"]:
            for arch in data["payload"]["images"][variant]:
                for image in data["payload"]["images"][variant][arch]:
                    image_obj = Image(self)
                    image_obj.deserialize(image)
                    self.add(variant, arch, image_obj)
        self.header.set_current_version()

    def add(self, variant, arch, image):
        """
        Assign an :class:`.Image` object to variant and arch.

        :param variant: compose variant UID
        :type  variant: str
        :param arch:    compose architecture
        :type  arch:    str
        :param image:   image
        :type  image:   :class:`.Image`
        """

        if arch not in productmd.common.RPM_ARCHES:
            raise ValueError("Arch not found in RPM_ARCHES: %s" % arch)
        self.images.setdefault(variant, {}).setdefault(arch, set()).add(image)
Ejemplo n.º 14
0
class Images(productmd.common.MetadataBase):
    def __init__(self):
        super(Images, self).__init__()
        self.header = Header(self)
        self.compose = Compose(self)
        self.images = {}

    def __getitem__(self, variant):
        return self.images[variant]

    def __delitem__(self, variant):
        del self.images[variant]

    def serialize(self, parser):
        data = parser
        self.header.serialize(data)
        data["payload"] = {}
        data["payload"]["images"] = {}
        self.compose.serialize(data["payload"])
        for variant in self.images:
            for arch in self.images[variant]:
                for image_obj in self.images[variant][arch]:
                    images = data["payload"]["images"].setdefault(variant, {}).setdefault(arch, [])
                    image_obj.serialize(images)
                    images.sort(key=lambda x: x["path"])
        return data

    def deserialize(self, data):
        self.header.deserialize(data)
        self.compose.deserialize(data["payload"])
        for variant in data["payload"]["images"]:
            for arch in data["payload"]["images"][variant]:
                for image in data["payload"]["images"][variant][arch]:
                    image_obj = Image(self)
                    image_obj.deserialize(image)
                    self.add(variant, arch, image_obj)
        self.header.set_current_version()

    def add(self, variant, arch, image):
        """
        Assign an :class:`.Image` object to variant and arch.

        :param variant: compose variant UID
        :type  variant: str
        :param arch:    compose architecture
        :type  arch:    str
        :param image:   image
        :type  image:   :class:`.Image`
        """

        if arch not in productmd.common.RPM_ARCHES:
            raise ValueError("Arch not found in RPM_ARCHES: %s" % arch)
        self.images.setdefault(variant, {}).setdefault(arch, set()).add(image)
Ejemplo n.º 15
0
    def test_version(self):
        hdr = Header(None)

        # empty version
        hdr.version = None
        self.assertRaises(TypeError, hdr.validate)

        # invalid version
        hdr.version = "first"
        self.assertRaises(ValueError, hdr.validate)

        hdr.version = "1.alpha2"
        self.assertRaises(ValueError, hdr.validate)

        hdr.version = "1"
        self.assertRaises(ValueError, hdr.validate)

        # valid version
        hdr.version = "1.22"
        hdr.validate()
Ejemplo n.º 16
0
class Images(productmd.common.MetadataBase):
    def __init__(self):
        super(Images, self).__init__()
        self.header = Header(self, "productmd.images")
        self.compose = Compose(self)
        self.images = {}

    def __getitem__(self, variant):
        return self.images[variant]

    def __delitem__(self, variant):
        del self.images[variant]

    def serialize(self, parser):
        data = parser
        self.header.serialize(data)
        data["payload"] = {}
        data["payload"]["images"] = {}
        self.compose.serialize(data["payload"])
        for variant in self.images:
            for arch in self.images[variant]:
                for image_obj in self.images[variant][arch]:
                    images = data["payload"]["images"].setdefault(
                        variant, {}).setdefault(arch, [])
                    image_obj.serialize(images)
                    images.sort(key=lambda x: x["path"])
        return data

    def deserialize(self, data):
        self.header.deserialize(data)
        self.compose.deserialize(data["payload"])
        for variant in data["payload"]["images"]:
            for arch in data["payload"]["images"][variant]:
                for image in data["payload"]["images"][variant][arch]:
                    image_obj = Image(self)
                    image_obj.deserialize(image)
                    if self.header.version_tuple <= (1, 1):
                        self._add_1_1(data, variant, arch, image_obj)
                    else:
                        self.add(variant, arch, image_obj)
        self.header.set_current_version()

    def _add_1_1(self, data, variant, arch, image):
        if arch == "src":
            # move src under binary arches
            for variant_arch in data["payload"]["images"][variant]:
                if variant_arch == "src":
                    continue
                self.add(variant, variant_arch, image)
        else:
            self.add(variant, arch, image)

    def add(self, variant, arch, image):
        """
        Assign an :class:`.Image` object to variant and arch.

        :param variant: compose variant UID
        :type  variant: str
        :param arch:    compose architecture
        :type  arch:    str
        :param image:   image
        :type  image:   :class:`.Image`
        """

        if arch not in productmd.common.RPM_ARCHES:
            raise ValueError("Arch not found in RPM_ARCHES: %s" % arch)
        if arch in ["src", "nosrc"]:
            raise ValueError(
                "Source arch is not allowed. Map source files under binary arches."
            )
        if self.header.version_tuple >= (1, 1):
            # disallow adding a different image with same 'unique'
            # attributes. can't do this pre-1.1 as we couldn't truly
            # identify images before subvariant
            for checkvar in self.images:
                for checkarch in self.images[checkvar]:
                    for curimg in self.images[checkvar][checkarch]:
                        if identify_image(curimg) == identify_image(
                                image) and curimg.checksums != image.checksums:
                            raise ValueError(
                                "Image {0} shares all UNIQUE_IMAGE_ATTRIBUTES with "
                                "image {1}! This is forbidden.".format(
                                    image, curimg))
        self.images.setdefault(variant, {}).setdefault(arch, set()).add(image)
Ejemplo n.º 17
0
 def __init__(self):
     super(Modules, self).__init__()
     self.header = Header(self, "productmd.modules")
     self.compose = Compose(self)
     self.modules = {}
Ejemplo n.º 18
0
class ComposeInfo(productmd.common.MetadataBase):
    """
    This class only encapsulates other classes with actual data.
    """

    def __init__(self):
        super(ComposeInfo, self).__init__()

        self.header = Header(self)              #: (:class:`.Header`) -- Metadata header
        self.compose = Compose(self)            #: (:class:`.Compose`) -- Compose details
        self.release = Release(self)            #: (:class:`.Release`) -- Release details
        self.base_product = BaseProduct(self)   #: (:class:`.BaseProduct`) -- Base product details (optional)
        self.variants = Variants(self)          #: (:class:`.Variants`) -- release variants
        self.validate()
        self.header.set_current_version()

    def __str__(self):
        result = self.release_id
        if self.compose.label:
            result += " (%s)" % self.compose.label
        return result

    def __cmp__(self, other):
        result = cmp(self.release, other.product)
        if result != 0:
            return result

        result = cmp(self.base_product, other.base_product)
        if result != 0:
            return result

        result = cmp(self.compose, other.compose)
        if result != 0:
            return result

        return 0

    def get_release_id(self, major_version=False):
        if major_version:
            result = "%s-%s" % (self.release.short, self.release.major_version)
        else:
            result = "%s-%s" % (self.release.short, self.release.version)
        if self.release.is_layered:
            result += "-%s-%s" % (self.base_product.short, self.base_product.version)
        return result

    @property
    def release_id(self):
        return self.get_release_id()

    def create_compose_id(self):
        result = "%s-%s" % (self.release.short, self.release.version)
        if self.release.is_layered:
            result += "-%s-%s" % (self.base_product.short, self.base_product.version)

        rhel5 = (self.release.short == "RHEL" and self.release.major_version == "5")
        rhel5 &= (self.base_product.short == "RHEL" and self.base_product.major_version == "5")
        if rhel5:
            # HACK: there are 2 RHEL 5 composes -> need to add Server or Client variant to compose ID
            if self.variants.variants:
                variant = sorted(self.variants.variants)[0]
                if variant in ("Client", "Server"):
                    result += "-%s" % variant
        result += "-%s%s.%s" % (self.compose.date, self.compose.type_suffix, self.compose.respin)
        return result

    def serialize(self, parser):
        data = parser
        self.header.serialize(data)
        data["payload"] = {}
        self.compose.serialize(data["payload"])
        self.release.serialize(data["payload"])
        if self.release.is_layered:
            self.base_product.serialize(data["payload"])
        self.variants.serialize(data["payload"])
        return data

    def deserialize(self, data):
        self.header.deserialize(data)
        self.compose.deserialize(data["payload"])
        self.release.deserialize(data["payload"])
        if self.release.is_layered:
            self.base_product.deserialize(data["payload"])
        self.variants.deserialize(data["payload"])
        self.header.set_current_version()

    def __getitem__(self, name):
        return self.variants[name]

    def get_variants(self, *args, **kwargs):
        return self.variants.get_variants(*args, **kwargs)
Ejemplo n.º 19
0
class Rpms(productmd.common.MetadataBase):
    def __init__(self):
        super(Rpms, self).__init__()
        self.header = Header(self)
        self.compose = Compose(self)
        self.rpms = {}

    def __getitem__(self, variant):
        return self.rpms[variant]

    def __delitem__(self, variant):
        del self.rpms[variant]

    def _check_nevra(self, nevra):
        if ":" not in nevra:
            raise ValueError("Missing epoch in N-E:V-R.A: %s" % nevra)

        try:
            nevra_dict = productmd.common.parse_nvra(nevra)
        except ValueError:
            raise ValueError("Invalid N-E:V-R.A: %s" % nevra)

        nevra_dict["epoch"] = nevra_dict["epoch"] or 0
        nevra = "%(name)s-%(epoch)s:%(version)s-%(release)s.%(arch)s" % nevra_dict
        return nevra, nevra_dict

    def serialize(self, parser):
        data = parser
        self.header.serialize(data)
        data["payload"] = {}
        data["payload"]["rpms"] = {}
        self.compose.serialize(data["payload"])
        data["payload"]["rpms"] = self.rpms
        return data

    def deserialize(self, data):
        self.header.deserialize(data)
        if self.header.version_tuple <= (0, 3):
            self.deserialize_0_3(data)
        else:
            self.deserialize_1_0(data)
        self.validate()

        self.header.set_current_version()

    def deserialize_0_3(self, data):
        self.compose.deserialize(data["payload"])
        payload = data["payload"]["manifest"]
        self.rpms = {}
        for variant in payload:
            for arch in payload[variant]:
                if arch == "src":
                    continue
                for srpm_nevra, rpms in payload[variant][arch].items():
                    srpm_data = payload[variant].get("src", {}).get(srpm_nevra, None)
                    for rpm_nevra, rpm_data in rpms.items():
                        category = rpm_data["type"]
                        if category == "package":
                            category = "binary"
                        self.add(variant, arch, rpm_nevra, rpm_data["path"], rpm_data["sigkey"], category, srpm_nevra)
                        if srpm_data is not None:
                            self.add(variant, arch, srpm_nevra, srpm_data["path"], srpm_data["sigkey"], "source")

    def deserialize_1_0(self, data):
        self.compose.deserialize(data["payload"])
        self.rpms = data["payload"]["rpms"]

    def add(self, variant, arch, nevra, path, sigkey, category, srpm_nevra=None):
        """
        Map RPM to to variant and arch.

        :param variant: compose variant UID
        :type  variant: str
        :param arch:    compose architecture
        :type  arch:    str
        :param nevra:   name-epoch:version-release.arch
        :type  nevra:   str
        :param sigkey:  sigkey hash
        :type  sigkey:  str or None
        :param category:    RPM category, one of binary, debug, source
        :type  category:    str
        :param srpm_nevra:  name-epoch:version-release.arch of RPM's SRPM
        :type  srpm_nevra:  str
        """

        if arch not in productmd.common.RPM_ARCHES:
            raise ValueError("Arch not found in RPM_ARCHES: %s" % arch)

        if category not in SUPPORTED_CATEGORIES:
            raise ValueError("Invalid category value: %s" % category)

        if path.startswith("/"):
            raise ValueError("Relative path expected: %s" % path)

        nevra, nevra_dict = self._check_nevra(nevra)

        if category == "source" and srpm_nevra is not None:
            raise ValueError("Expected blank srpm_nevra for source package: %s" % nevra)

        if category != "source" and srpm_nevra is None:
            raise ValueError("Missing srpm_nevra for package: %s" % nevra)

        if (category == "source") != (nevra_dict["arch"] in ("src", "nosrc")):
            raise ValueError("Invalid category/arch combination: %s/%s" % (category, nevra))

        if sigkey is not None:
            sigkey = sigkey.lower()

        if srpm_nevra:
            srpm_nevra, _ = self._check_nevra(srpm_nevra)
        else:
            srpm_nevra = nevra

        arches = self.rpms.setdefault(variant, {})
        srpms = arches.setdefault(arch, {})
        rpms = srpms.setdefault(srpm_nevra, {})
        rpms[nevra] = {"sigkey": sigkey, "path": path, "category": category}
Ejemplo n.º 20
0
class Modules(productmd.common.MetadataBase):
    def __init__(self):
        super(Modules, self).__init__()
        self.header = Header(self, "productmd.modules")
        self.compose = Compose(self)
        self.modules = {}

    def __getitem__(self, variant):
        return self.modules[variant]

    def __delitem__(self, variant):
        del self.modules[variant]

    @staticmethod
    def parse_uid(uid):
        if not isinstance(uid, six.string_types):
            raise ValueError("Uid has to be string: %s" % uid)

        # pattern to parse uid MODULE_NAME:STREAM[:VERSION[:CONTEXT]]
        UID_RE = re.compile(
            r"^(.*/)?(?P<module_name>[^:]+):(?P<stream>[^:]+)(:(?P<version>[^:]+))?(:(?P<context>[^:]+))?$"
        )
        matched = UID_RE.match(uid)
        if matched:
            uid_dict = matched.groupdict()
        else:
            raise ValueError("Invalid uid: %s" % uid)

        if uid_dict["version"] is None:
            uid_dict["version"] = ""
        if uid_dict["context"] is None:
            uid_dict["context"] = ""

        return uid_dict

    def _check_uid(self, uid):
        if not isinstance(uid, six.string_types):
            raise ValueError("Uid has to be string: %s" % uid)
        if ":" not in uid:
            raise ValueError("Missing stream in uid: %s" % uid)

        try:
            uid_dict = self.parse_uid(uid)
        except ValueError:
            raise ValueError("Invalid uid format: %s" % uid)

        uid = "%(module_name)s:%(stream)s" % uid_dict
        uid += ":%s" % uid_dict['version'] if uid_dict['version'] else ""
        uid += ":%s" % uid_dict['context'] if uid_dict['context'] else ""
        return uid, uid_dict

    def serialize(self, parser):
        self.validate()
        data = parser
        self.header.serialize(data)
        data["payload"] = {}
        self.compose.serialize(data["payload"])
        data["payload"]["modules"] = self.modules
        return data

    def deserialize(self, data):
        self.header.deserialize(data)
        self.compose.deserialize(data["payload"])
        self.modules = data["payload"]["modules"]
        self.validate()

    def add(self, variant, arch, uid, koji_tag, modulemd_path, category, rpms):
        if not variant:
            raise ValueError("Non-empty variant is expected")

        if arch not in RPM_ARCHES:
            raise ValueError("Arch not found in RPM_ARCHES: %s" % arch)

        if category not in SUPPORTED_CATEGORIES:
            raise ValueError("Invalid category value: %s" % category)

        uid, uid_dict = self._check_uid(uid)
        name = uid_dict["module_name"]
        stream = uid_dict["stream"]
        version = uid_dict["version"]
        context = uid_dict["context"]

        if modulemd_path.startswith("/"):
            raise ValueError("Relative path expected: %s" % modulemd_path)

        if not koji_tag:
            raise ValueError("Non-empty 'koji_tag' is expected")

        for param_name, param in {
                "variant": variant,
                "koji_tag": koji_tag,
                "modulemd_path": modulemd_path
        }.items():
            if not param:
                raise ValueError("Non-empty '%s' is expected" % param_name)

        if not isinstance(rpms, (list, tuple)):
            raise ValueError("Wrong type of 'rpms'")

        arches = self.modules.setdefault(variant, {})
        uids = arches.setdefault(arch, {})
        metadata = uids.setdefault(uid, {})
        metadata["metadata"] = {
            "uid": uid,
            "name": name,
            "stream": stream,
            "version": version,
            "context": context,
            "koji_tag": koji_tag,
        }
        metadata.setdefault("modulemd_path", {})[category] = modulemd_path
        metadata.setdefault("rpms", []).extend(list(rpms))
Ejemplo n.º 21
0
 def __init__(self):
     super(Images, self).__init__()
     self.header = Header(self)
     self.compose = Compose(self)
     self.images = {}
Ejemplo n.º 22
0
 def __init__(self):
     super(Rpms, self).__init__()
     self.header = Header(self)
     self.compose = Compose(self)
     self.rpms = {}
Ejemplo n.º 23
0
 def __init__(self):
     super(ExtraFiles, self).__init__()
     self.header = Header(self, "productmd.extra_files")
     self.compose = Compose(self)
     self.extra_files = {}
Ejemplo n.º 24
0
class Images(productmd.common.MetadataBase):
    def __init__(self):
        super(Images, self).__init__()
        self.header = Header(self, "productmd.images")
        self.compose = Compose(self)
        self.images = {}

    def __getitem__(self, variant):
        return self.images[variant]

    def __delitem__(self, variant):
        del self.images[variant]

    def serialize(self, parser):
        data = parser
        self.header.serialize(data)
        data["payload"] = {}
        data["payload"]["images"] = {}
        self.compose.serialize(data["payload"])
        for variant in self.images:
            for arch in self.images[variant]:
                for image_obj in self.images[variant][arch]:
                    images = data["payload"]["images"].setdefault(variant, {}).setdefault(arch, [])
                    image_obj.serialize(images)
                    images.sort(key=lambda x: x["path"])
        return data

    def deserialize(self, data):
        self.header.deserialize(data)
        self.compose.deserialize(data["payload"])
        for variant in data["payload"]["images"]:
            for arch in data["payload"]["images"][variant]:
                for image in data["payload"]["images"][variant][arch]:
                    image_obj = Image(self)
                    image_obj.deserialize(image)
                    if self.header.version_tuple <= (1, 1):
                        self._add_1_1(data, variant, arch, image_obj)
                    else:
                        self.add(variant, arch, image_obj)
        self.header.set_current_version()

    def _add_1_1(self, data, variant, arch, image):
        if arch == "src":
            # move src under binary arches
            for variant_arch in data["payload"]["images"][variant]:
                if variant_arch == "src":
                    continue
                self.add(variant, variant_arch, image)
        else:
            self.add(variant, arch, image)

    def add(self, variant, arch, image):
        """
        Assign an :class:`.Image` object to variant and arch.

        :param variant: compose variant UID
        :type  variant: str
        :param arch:    compose architecture
        :type  arch:    str
        :param image:   image
        :type  image:   :class:`.Image`
        """

        if arch not in productmd.common.RPM_ARCHES:
            raise ValueError("Arch not found in RPM_ARCHES: %s" % arch)
        if arch in ["src", "nosrc"]:
            raise ValueError("Source arch is not allowed. Map source files under binary arches.")
        self.images.setdefault(variant, {}).setdefault(arch, set()).add(image)
Ejemplo n.º 25
0
class Images(productmd.common.MetadataBase):
    def __init__(self):
        super(Images, self).__init__()
        self.header = Header(self, "productmd.images")
        self.compose = Compose(self)
        self.images = {}

    def __getitem__(self, variant):
        return self.images[variant]

    def __delitem__(self, variant):
        del self.images[variant]

    def serialize(self, parser):
        data = parser
        self.header.serialize(data)
        data["payload"] = {}
        data["payload"]["images"] = {}
        self.compose.serialize(data["payload"])
        for variant in self.images:
            for arch in self.images[variant]:
                for image_obj in self.images[variant][arch]:
                    images = data["payload"]["images"].setdefault(variant, {}).setdefault(arch, [])
                    image_obj.serialize(images)
                    images.sort(key=lambda x: x["path"])
        return data

    def deserialize(self, data):
        self.header.deserialize(data)
        self.compose.deserialize(data["payload"])
        for variant in data["payload"]["images"]:
            for arch in data["payload"]["images"][variant]:
                for image in data["payload"]["images"][variant][arch]:
                    image_obj = Image(self)
                    image_obj.deserialize(image)
                    if self.header.version_tuple <= (1, 1):
                        self._add_1_1(data, variant, arch, image_obj)
                    else:
                        self.add(variant, arch, image_obj)
        self.header.set_current_version()

    def _add_1_1(self, data, variant, arch, image):
        if arch == "src":
            # move src under binary arches
            for variant_arch in data["payload"]["images"][variant]:
                if variant_arch == "src":
                    continue
                self.add(variant, variant_arch, image)
        else:
            self.add(variant, arch, image)

    def add(self, variant, arch, image):
        """
        Assign an :class:`.Image` object to variant and arch.

        :param variant: compose variant UID
        :type  variant: str
        :param arch:    compose architecture
        :type  arch:    str
        :param image:   image
        :type  image:   :class:`.Image`
        """

        if arch not in productmd.common.RPM_ARCHES:
            raise ValueError("Arch not found in RPM_ARCHES: %s" % arch)
        if arch in ["src", "nosrc"]:
            raise ValueError("Source arch is not allowed. Map source files under binary arches.")
        if self.header.version_tuple >= (1, 1):
            # disallow adding a different image with same 'unique'
            # attributes. can't do this pre-1.1 as we couldn't truly
            # identify images before subvariant
            for checkvar in self.images:
                for checkarch in self.images[checkvar]:
                    for curimg in self.images[checkvar][checkarch]:
                        if identify_image(curimg) == identify_image(image) and curimg.checksums != image.checksums:
                            raise ValueError("Image {0} shares all UNIQUE_IMAGE_ATTRIBUTES with "
                                             "image {1}! This is forbidden.".format(image, curimg))
        self.images.setdefault(variant, {}).setdefault(arch, set()).add(image)
Ejemplo n.º 26
0
class TreeInfo(productmd.common.MetadataBase):
    def __init__(self):
        super(productmd.common.MetadataBase, self)
        self.header = Header(self)              #: (:class:`productmd.common.Header`) -- Metadata header
        self.release = Release(self)            #: (:class:`.Release`) -- Release details
        self.base_product = BaseProduct(self)   #: (:class:`.BaseProduct`) -- Base product details (optional)
        self.tree = Tree(self)                  #: (:class:`.Tree`) -- Tree details
        self.variants = Variants(self)          #: (:class:`.Variants`) -- Release variants
        self.checksums = Checksums(self)        #: (:class:`.Checksums`) -- Checksums of images included in a tree
        self.images = Images(self)              #: (:class:`.Images`) -- Paths to images included in a tree
        self.stage2 = Stage2(self)              #: (:class:`.Stage2`) -- Stage 2 image path (for Anaconda installer)
        self.media = Media(self)                #: (:class:`.Media`) -- Media set information (optional)

    def __str__(self):
        result = "%s-%s" % (self.release.short, self.release.version)
        if self.release.is_layered:
            result += "-%s-%s" % (self.base_product.short, self.base_product.version)
        variant = sorted(self.variants)[0]
        result += " %s.%s" % (variant, self.tree.arch)
        return result

    def __getitem__(self, name):
        return self.variants[name]

    def __delitem__(self, name):
        del self.variants[name]

    def _get_parser(self):
        return productmd.common.SortedConfigParser()

    def parse_file(self, f):
        # parse file, return parser or dict with data
        f.seek(0)
        parser = productmd.common.SortedConfigParser()
        parser.read_file(f)
        return parser

    def build_file(self, parser, f):
        # build file from parser or dict with data
        parser.write(f)

    def serialize(self, parser):
        self.validate()
        self.header.serialize(parser)
        self.release.serialize(parser)
        if self.release.is_layered:
            self.base_product.serialize(parser)
        self.tree.serialize(parser)
        self.variants.serialize(parser)
        self.checksums.serialize(parser)
        self.images.serialize(parser)
        self.stage2.serialize(parser)
        self.media.serialize(parser)
        # HACK: generate [general] section for compatibility
        general = General(self)
        general.serialize(parser)

    def deserialize(self, parser):
        self.header.deserialize(parser)
        self.release.deserialize(parser)
        if self.release.is_layered:
            self.base_product.deserialize(parser)
        self.tree.deserialize(parser)
        self.variants.deserialize(parser)
        self.checksums.deserialize(parser)
        self.images.deserialize(parser)
        self.stage2.deserialize(parser)
        self.media.deserialize(parser)
        self.validate()
        self.header.set_current_version()
        return parser
Ejemplo n.º 27
0
class ExtraFiles(productmd.common.MetadataBase):
    def __init__(self):
        super(ExtraFiles, self).__init__()
        self.header = Header(self, "productmd.extra_files")
        self.compose = Compose(self)
        self.extra_files = {}

    def __getitem__(self, variant):
        return self.extra_files[variant]

    def __delitem__(self, variant):
        del self.extra_files[variant]

    def serialize(self, parser):
        self.validate()
        data = parser
        self.header.serialize(data)
        data["payload"] = {}
        self.compose.serialize(data["payload"])
        data["payload"]["extra_files"] = self.extra_files
        return data

    def deserialize(self, data):
        self.header.deserialize(data)
        self.compose.deserialize(data["payload"])
        self.extra_files = data["payload"]["extra_files"]
        self.validate()

    def add(self, variant, arch, path, size, checksums):
        if not variant:
            raise ValueError("Non-empty variant is expected")

        if arch not in RPM_ARCHES:
            raise ValueError("Arch not found in RPM_ARCHES: %s" % arch)

        if not path:
            raise ValueError("Path can not be empty.")

        if path.startswith("/"):
            raise ValueError("Relative path expected: %s" % path)

        if not isinstance(checksums, dict):
            raise TypeError("Checksums must be a dict.")

        metadata = self.extra_files.setdefault(variant,
                                               {}).setdefault(arch, [])
        metadata.append({"file": path, "size": size, "checksums": checksums})

    def dump_for_tree(self, output, variant, arch, basepath):
        """Dump the serialized metadata for given tree. The basepath is
        stripped from all paths.
        """
        metadata = {"header": {"version": "1.0"}, "data": []}
        for item in self.extra_files[variant][arch]:
            metadata["data"].append({
                "file":
                _relative_to(item["file"], basepath),
                "size":
                item["size"],
                "checksums":
                item["checksums"],
            })

        json.dump(metadata,
                  output,
                  sort_keys=True,
                  indent=4,
                  separators=(",", ": "))
Ejemplo n.º 28
0
class ComposeInfo(productmd.common.MetadataBase):
    """
    This class only encapsulates other classes with actual data.
    """
    def __init__(self):
        super(ComposeInfo, self).__init__()

        self.header = Header(
            self,
            "productmd.composeinfo")  #: (:class:`.Header`) -- Metadata header
        self.compose = Compose(self)  #: (:class:`.Compose`) -- Compose details
        self.release = Release(self)  #: (:class:`.Release`) -- Release details
        self.base_product = BaseProduct(
            self
        )  #: (:class:`.BaseProduct`) -- Base product details (optional)
        self.variants = Variants(
            self)  #: (:class:`.Variants`) -- release variants
        self.validate()
        self.header.set_current_version()

    def __str__(self):
        result = self.release_id
        if self.compose.label:
            result += " (%s)" % self.compose.label
        return result

    def __cmp__(self, other):
        result = cmp(self.release, other.release)
        if result != 0:
            return result

        result = cmp(self.base_product, other.base_product)
        if result != 0:
            return result

        result = cmp(self.compose, other.compose)
        if result != 0:
            return result

        return 0

    def get_release_id(self, major_version=False):
        if major_version:
            result = "%s-%s" % (self.release.short, self.release.major_version)
        else:
            result = "%s-%s" % (self.release.short, self.release.version)
        if self.release.is_layered:
            result += "-%s-%s" % (self.base_product.short,
                                  self.base_product.version)
        return result

    @property
    def release_id(self):
        return self.get_release_id()

    def create_compose_id(self):
        result = "%s-%s%s" % (self.release.short, self.release.version,
                              self.release.type_suffix)
        if self.release.is_layered:
            result += "-%s-%s%s" % (self.base_product.short,
                                    self.base_product.version,
                                    self.base_product.type_suffix)

        rhel5 = (self.release.short == "RHEL"
                 and self.release.major_version == "5")
        rhel5 &= (self.base_product.short == "RHEL"
                  and self.base_product.major_version == "5")
        if rhel5:
            # HACK: there are 2 RHEL 5 composes -> need to add Server or Client variant to compose ID
            if self.variants.variants:
                variant = sorted(self.variants.variants)[0]
                if variant in ("Client", "Server"):
                    result += "-%s" % variant
        result += "-%s%s.%s" % (self.compose.date, self.compose.type_suffix,
                                self.compose.respin)
        return result

    def serialize(self, parser):
        data = parser
        self.header.serialize(data)
        data["payload"] = {}
        self.compose.serialize(data["payload"])
        self.release.serialize(data["payload"])
        if self.release.is_layered:
            self.base_product.serialize(data["payload"])
        self.variants.serialize(data["payload"])
        return data

    def deserialize(self, data):
        self.header.deserialize(data)
        self.compose.deserialize(data["payload"])
        self.release.deserialize(data["payload"])
        if self.release.is_layered:
            self.base_product.deserialize(data["payload"])
        self.variants.deserialize(data["payload"])
        self.header.set_current_version()

    def __getitem__(self, name):
        return self.variants[name]

    def get_variants(self, *args, **kwargs):
        return self.variants.get_variants(*args, **kwargs)
Ejemplo n.º 29
0
class Rpms(productmd.common.MetadataBase):
    def __init__(self):
        super(Rpms, self).__init__()
        self.header = Header(self, "productmd.rpms")
        self.compose = Compose(self)
        self.rpms = {}

    def __getitem__(self, variant):
        return self.rpms[variant]

    def __delitem__(self, variant):
        del self.rpms[variant]

    def _check_nevra(self, nevra):
        if ":" not in nevra:
            raise ValueError("Missing epoch in N-E:V-R.A: %s" % nevra)

        try:
            nevra_dict = productmd.common.parse_nvra(nevra)
        except ValueError:
            raise ValueError("Invalid N-E:V-R.A: %s" % nevra)

        nevra_dict["epoch"] = nevra_dict["epoch"] or 0
        nevra = "%(name)s-%(epoch)s:%(version)s-%(release)s.%(arch)s" % nevra_dict
        return nevra, nevra_dict

    def serialize(self, parser):
        data = parser
        self.header.serialize(data)
        data["payload"] = {}
        data["payload"]["rpms"] = {}
        self.compose.serialize(data["payload"])
        data["payload"]["rpms"] = self.rpms
        return data

    def deserialize(self, data):
        self.header.deserialize(data)
        if self.header.version_tuple <= (0, 3):
            self.deserialize_0_3(data)
        else:
            self.deserialize_1_0(data)
        self.validate()

        self.header.set_current_version()

    def deserialize_0_3(self, data):
        self.compose.deserialize(data["payload"])
        payload = data["payload"]["manifest"]
        self.rpms = {}
        for variant in payload:
            for arch in payload[variant]:
                if arch == "src":
                    continue
                for srpm_nevra, rpms in payload[variant][arch].items():
                    srpm_data = payload[variant].get("src",
                                                     {}).get(srpm_nevra, None)
                    for rpm_nevra, rpm_data in rpms.items():
                        category = rpm_data["type"]
                        if category == "package":
                            category = "binary"
                        self.add(variant, arch, rpm_nevra, rpm_data["path"],
                                 rpm_data["sigkey"], category, srpm_nevra)
                        if srpm_data is not None:
                            self.add(variant, arch, srpm_nevra,
                                     srpm_data["path"], srpm_data["sigkey"],
                                     "source")

    def deserialize_1_0(self, data):
        self.compose.deserialize(data["payload"])
        self.rpms = data["payload"]["rpms"]

    def add(self,
            variant,
            arch,
            nevra,
            path,
            sigkey,
            category,
            srpm_nevra=None):
        """
        Map RPM to to variant and arch.

        :param variant: compose variant UID
        :type  variant: str
        :param arch:    compose architecture
        :type  arch:    str
        :param nevra:   name-epoch:version-release.arch
        :type  nevra:   str
        :param sigkey:  sigkey hash
        :type  sigkey:  str or None
        :param category:    RPM category, one of binary, debug, source
        :type  category:    str
        :param srpm_nevra:  name-epoch:version-release.arch of RPM's SRPM
        :type  srpm_nevra:  str
        """

        if arch not in productmd.common.RPM_ARCHES:
            raise ValueError("Arch not found in RPM_ARCHES: %s" % arch)

        if arch in ["src", "nosrc"]:
            raise ValueError(
                "Source arch is not allowed. Map source files under binary arches."
            )

        if category not in SUPPORTED_CATEGORIES:
            raise ValueError("Invalid category value: %s" % category)

        if path.startswith("/"):
            raise ValueError("Relative path expected: %s" % path)

        nevra, nevra_dict = self._check_nevra(nevra)

        if category == "source" and srpm_nevra is not None:
            raise ValueError(
                "Expected blank srpm_nevra for source package: %s" % nevra)

        if category != "source" and srpm_nevra is None:
            raise ValueError("Missing srpm_nevra for package: %s" % nevra)

        if (category == "source") != (nevra_dict["arch"] in ("src", "nosrc")):
            raise ValueError("Invalid category/arch combination: %s/%s" %
                             (category, nevra))

        if sigkey is not None:
            sigkey = sigkey.lower()

        if srpm_nevra:
            srpm_nevra, _ = self._check_nevra(srpm_nevra)
        else:
            srpm_nevra = nevra

        arches = self.rpms.setdefault(variant, {})
        srpms = arches.setdefault(arch, {})
        rpms = srpms.setdefault(srpm_nevra, {})
        rpms[nevra] = {"sigkey": sigkey, "path": path, "category": category}
Ejemplo n.º 30
0
 def __init__(self):
     super(Images, self).__init__()
     self.header = Header(self, "productmd.images")
     self.compose = Compose(self)
     self.images = {}
Ejemplo n.º 31
0
 def __init__(self):
     super(Rpms, self).__init__()
     self.header = Header(self, "productmd.rpms")
     self.compose = Compose(self)
     self.rpms = {}