#!/usr/bin/python2
#
# Copyright (c) 2015-2018, Virtuozzo International GmbH
#
# This file is based on the same file from the Virtuozzo Core project.
# You can redistribute it file and/or modify it under the terms
# of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any
# later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# Our contact details: Virtuozzo International GmbH, Vordergasse 59, 8200
# Schaffhausen, Switzerland.

import json
import re
from contextlib import contextmanager
from time import sleep
import yum
import sys
import errno

product_cpe = 'cpe:/o:virtuozzohci:vz:'
yum_lock_timeout = 60 # In seconds

def disable_non_hci_repos(yumbase):
    # check updates only in HCI repos
    enabled = []
    for repo in yumbase.repos.listEnabled():
        repo.metadata_expire = 0
        repo.mirrorlist_expire = 0
        try:
            if any(key.startswith(product_cpe) for key in
                    repo.repoXML.tags.get('distro', dict()).keys()):
                if repo._getFileRepoXML(repo.cachedir + '/repomd.xml'):
                    enabled.append(repo.id)
                continue
        except:
            # Repo can be inaccessible - failed to check for updates
            sys.exit(1)
        yumbase.repos.disableRepo(repo.id)
    return enabled


def get_hci_versions_from_repos(yumbase):
    return {
        ver
        for repo in yumbase.repos.listEnabled()
        for item in repo.repoXML.tags.get('distro', dict()).items()
        for ver in item[1] or set()
        if (item[0] or '').startswith(product_cpe)
    }


def get_max_hci_version(versions):
    version = max(list(
        tuple(int(numstr) for numstr in vermatch.groups()) for vermatch in (
            # version is "anythingX.Y.Z (R)"
            re.match("^.*(\d+)\.(\d+)\.(\d+) \((\d+)\)", ver) for ver in versions
        ) if vermatch
    ) or [None])
    return version and dict(zip(['major', 'middle', 'minor', 'release'], version))

def check_hci_updates(yumbase):
    enabled = disable_non_hci_repos(yumbase)
    versions = get_hci_versions_from_repos(yumbase)
    version = get_max_hci_version(versions)
    try:
        # doPackageLists can throw exception if repo metadata us changed in the middle,
        # see VSTOR-14151
        packages = yumbase.doPackageLists(pkgnarrow='updates', patterns='', ignore_case=True).updates
    except:
        packages = []

    return(json.dumps(dict(
        repos=enabled,
        version=version,
        packages=[dict(name=p.name, repo=p.repoid, version=p.vr) for p in packages]
    ), sort_keys=True, indent=2))

def configure_yum():
    yumbase = yum.YumBase()
    yumbase.preconf.debuglevel = 0
    yumbase.errorlevel = 0

    return yumbase

def lock_yum(yumbase, timeout=yum_lock_timeout):
    for i in range(0, timeout):
        try:
            return yumbase.doLock()
        except yum.Errors.LockError, e:
            if e.errno in (errno.ENOENT, errno.EPERM, errno.EACCES, errno.EROFS):
                print(e)
                break
            sleep(1)
    sys.exit(2)

def unlock_yum(yumbase):
    yumbase.doUnlock()

@contextmanager
def yumlock(yumbase, timeout=yum_lock_timeout):
    lock_yum(yumbase, timeout)
    try:
        yield None
    finally:
        unlock_yum(yumbase)

if __name__ == '__main__':
    yumbase = configure_yum()

    with yumlock(yumbase):
        print(check_hci_updates(yumbase))
