class Package(models.Model):

    RPM = 'R'
    DEB = 'D'
    ARCH = 'A'
    UNKNOWN = 'U'

    PACKAGE_TYPES = (
        (RPM, 'rpm'),
        (DEB, 'deb'),
        (ARCH, 'arch'),
        (UNKNOWN, 'unknown'),
    )

    name = models.ForeignKey(PackageName, on_delete=models.CASCADE)
    epoch = models.CharField(max_length=255, blank=True, null=True)
    version = models.CharField(max_length=255)
    release = models.CharField(max_length=255, blank=True, null=True)
    arch = models.ForeignKey(PackageArchitecture, on_delete=models.CASCADE)
    packagetype = models.CharField(max_length=1,
                                   choices=PACKAGE_TYPES,
                                   blank=True,
                                   null=True)
    description = models.TextField(blank=True, null=True)
    url = models.URLField(max_length=255, blank=True, null=True)

    objects = PackageManager()

    class Meta(object):
        ordering = ('name', 'epoch', 'version', 'release', 'arch')
        unique_together = (
            'name', 'epoch', 'version', 'release', 'arch', 'packagetype',)

    def __str__(self):
        if self.epoch:
            epo = '{0!s}:'.format(self.epoch)
        else:
            epo = ''
        if self.release:
            rel = '-{0!s}'.format(self.release)
        else:
            rel = ''
        return '{0!s}-{1!s}{2!s}{3!s}-{4!s}'.format(self.name,
                                                    epo,
                                                    self.version,
                                                    rel,
                                                    self.arch)

    def get_absolute_url(self):
        return self.name.get_absolute_url()

    def __key(self):
        return (self.name, self.epoch, self.version, self.release, self.arch,
                self.packagetype)

    def __eq__(self, other):
        return self.__key() == other.__key()

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        if not self:
            return 0
        return hash(self.__key())

    def _version_string_rpm(self):
        return (str(self.epoch), str(self.version), str(self.release))

    def _version_string_deb_arch(self):
        epoch = ''
        version = ''
        release = ''
        if self.epoch != '':
            epoch = str(self.epoch) + ':'
        if self.version != '':
            version = str(self.version)
        if self.release != '':
            release = '-' + str(self.release)
        return (epoch + version + release)

    def get_version_string(self):
        if self.packagetype == 'R':
            return self._version_string_rpm()
        elif self.packagetype == 'D' or self.packagetype == 'A':
            return self._version_string_deb_arch()

    def compare_version(self, other):
        if self.packagetype == 'R' and other.packagetype == 'R':
            return labelCompare(self.get_version_string(),
                                other.get_version_string())
        elif self.packagetype == 'D' and other.packagetype == 'D':
            vs = Version(self.get_version_string())
            vo = Version(other.get_version_string())
            return version_compare(vs, vo)
        elif self.packagetype == 'A' and other.packagetype == 'A':
            if self.epoch == other.epoch \
                    and self.version == other.version \
                    and self.release == other.release:
                return 0
            vs = Version(self.get_version_string())
            vo = Version(other.get_version_string())
            return version_compare(vs, vo)

    def repo_count(self):
        from repos.models import Repository
        return Repository.objects.filter(
            mirror__packages=self).distinct().count() class PackageUpdate(models.Model):

    oldpackage = models.ForeignKey(Package,
                                   on_delete=models.CASCADE,
                                   related_name='oldpackage')
    newpackage = models.ForeignKey(Package,
                                   on_delete=models.CASCADE,
                                   related_name='newpackage')
    security = models.BooleanField(default=False)

    class Meta(object):
        unique_together = (('oldpackage', 'newpackage', 'security'))

    def __str__(self):
        if self.security:
            update_type = 'Security'
        else:
            update_type = 'Bugfix'
        return '{0!s} -> {1!s} ({2!s})'.format(self.oldpackage,
                                               self.newpackage,
                                               update_type) class Domain(models.Model):

    name = models.CharField(max_length=255, unique=True)

    def __str__(self):
        return self.name class Repository(models.Model):

    RPM = 'R'
    DEB = 'D'
    ARCH = 'A'

    REPO_TYPES = (
        (RPM, 'rpm'),
        (DEB, 'deb'),
        (ARCH, 'arch'),
    )

    name = models.CharField(max_length=255, unique=True)
    arch = models.ForeignKey(MachineArchitecture, on_delete=models.CASCADE)
    security = models.BooleanField(default=False)
    repotype = models.CharField(max_length=1, choices=REPO_TYPES)
    enabled = models.BooleanField(default=True)
    repo_id = models.CharField(max_length=255, null=True, blank=True)
    auth_required = models.BooleanField(default=False)

    class Meta(object):
        verbose_name_plural = 'Repository'
        verbose_name_plural = 'Repositories'

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('repos:repo_detail', args=[str(self.id)])

    def show(self):
        """ Show info about this repo, including mirrors
        """
        text = '{0!s} : {1!s}\n'.format(self.id, self.name)
        text += 'security: {0!s}    '.format(self.security)
        text += 'arch: {0!s}\n'.format(self.arch)
        text += 'Mirrors:'

        info_message.send(sender=None, text=text)

        for mirror in self.mirror_set.all():
            mirror.show()

    def refresh(self, force=False):
        """ Refresh all of a repos mirror metadata,
            force can be set to force a reset of all the mirrors metadata
        """

        if force:
            for mirror in self.mirror_set.all():
                mirror.file_checksum = None
                mirror.save()

        if not self.auth_required:
            if self.repotype == Repository.DEB:
                refresh_deb_repo(self)
            elif self.repotype == Repository.RPM:
                refresh_rpm_repo(self)
            elif self.repotype == Repository.ARCH:
                refresh_arch_repo(self)
            else:
                text = 'Error: unknown repo type for repo '
                text += '{0!s}: {1!s}'.format(self.id, self.repotype)
                error_message.send(sender=None, text=text)
        else:
            text = 'Repo requires certificate authentication, not updating'
            warning_message.send(sender=None, text=text)

    def disable(self):
        """ Disable a repo. This involves disabling each mirror, which stops it
            being considered for package updates, and disabling refresh for
            each mirror so that it doesn't try to update its package metadata.
        """

        self.enabled = False
        for mirror in self.mirror_set.all():
            mirror.enabled = False
            mirror.refresh = False
            mirror.save()

    def enable(self):
        """ Enable a repo. This involves enabling each mirror, which allows it
            to be considered for package updates, and enabling refresh for each
            mirror so that it updates its package metadata.
        """

        self.enabled = True
        for mirror in self.mirror_set.all():
            mirror.enabled = True
            mirror.refresh = True
            mirror.save() class OS(models.Model):

    name = models.CharField(max_length=255, unique=True)
    osgroup = models.ForeignKey(OSGroup, blank=True, null=True,
                                on_delete=models.SET_NULL)

    class Meta(object):
        verbose_name = 'Operating System'
        verbose_name_plural = 'Operating Systems'
        ordering = ('name',)

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('operatingsystems:os_detail', args=[str(self.id)]) class MachineArchitecture(Architecture):

    class Meta(Architecture.Meta):
        verbose_name = 'Machine Architecture' def find_best_repo(package, hostrepos):
    """ Given a package and a set of HostRepos, determine the best
        repo. Returns the best repo.
    """
    best_repo = None
    package_repos = hostrepos.filter(repo__mirror__packages=package)

    if package_repos:
        best_repo = package_repos[0]
    if package_repos.count() > 1:
        for hostrepo in package_repos:
            if hostrepo.repo.security:
                best_repo = hostrepo
            else:
                if hostrepo.priority > best_repo.priority:
                    best_repo = hostrepo
    return best_repo def update_rdns(host):
    """ Update the reverse DNS for a host
    """

    try:
        reversedns = str(gethostbyaddr(host.ipaddress)[0])
    except (gaierror, herror):
        reversedns = 'None'

    host.reversedns = reversedns.lower()
    try:
        host.save()
    except DatabaseError as e:
        error_message.send(sender=None, text=e) def remove_reports(host, timestamp):
    """ Remove all but the last 3 reports for a host
    """

    from reports.models import Report

    reports = Report.objects.filter(host=host).order_by('-created')[:3]
    report_ids = []

    for report in reports:
        report_ids.append(report.id)
        report.accessed = timestamp
        report.save()

    del_reports = Report.objects.filter(host=host).exclude(id__in=report_ids)

    rlen = del_reports.count()
    ptext = 'Cleaning {0!s} old reports'.format(rlen)
    progress_info_s.send(sender=None, ptext=ptext, plen=rlen)
    for i, report in enumerate(del_reports):
        report.delete()
        progress_update_s.send(sender=None, index=i + 1)
from django.db import models, IntegrityError, DatabaseError, transaction
from django.db.models import Q
from django.urls import reverse
from django.utils import timezone
    from version_utils.rpm import labelCompare
    from rpm import labelCompare
from tagging.fields import TagField
from packages.models import Package, PackageUpdate
from domains.models import Domain
from repos.models import Repository
from operatingsystems.models import OS
from arch.models import MachineArchitecture
from patchman.signals import info_message, error_message
from repos.utils import find_best_repo
from hosts.utils import update_rdns, remove_reports
# Copyright 2012 VPAC, http://www.vpac.org
# Copyright 2013-2021 Marcus Furlong <furlongm@gmail.com>
#
# This file is part of Patchman.
#
# Patchman is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 only.
#
# Patchman is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Patchman. If not, see <http://www.gnu.org/licenses/>


try:
except ImportError:



class Host(models.Model):

    hostname = models.CharField(max_length=255, unique=True)
    ipaddress = models.GenericIPAddressField()
    reversedns = models.CharField(max_length=255, blank=True, null=True)
    check_dns = models.BooleanField(default=False)
    os = models.ForeignKey(OS, on_delete=models.CASCADE)
    kernel = models.CharField(max_length=255)
    arch = models.ForeignKey(MachineArchitecture, on_delete=models.CASCADE)
    domain = models.ForeignKey(Domain, on_delete=models.CASCADE)
    lastreport = models.DateTimeField()

    packages = models.ManyToManyField(Package, blank=True)