#!/usr/bin/env python
# vim: ai ts=4 sts=4 et sw=4
#
# Copyright (C) 2010, 2011, 2012, 2013, 2014 Intel, Inc.
#
#    This program 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 2
#    of the License.
#
#    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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
"""
This code is called by jenkins jobs triggered by OBS events.
"""

import os
import sys
import re
import shutil
import base64
import datetime
import xml.etree.ElementTree as ElementTree

from common.repomaker import find_files, RepoMaker, RepoMakerError
from common.buildtrigger import trigger_info, trigger_next, get_pending_builds_with_parameter
from common.buildservice import BuildService
from common.backenddb import BackendDB
from common.prerelease import get_info_from_prerelease_name, is_devbase_project
from common.snapshot import Snapshot, SnapshotError
from common.iris_rest_client import IrisRestClient
from common.utils import sync

class LocalError(Exception):
    """Local error exception."""
    pass

def update_ks(imagedata, snapshot_id, pkg_urls, base_project=None, exclude_ref_repo=None):
    """
    update the repo url point to right URL and add prerelease repo
    url with highest priority
    Args:
         imagedata (ImageData): [(ks_file_name, ks_file_content),]
         backenddb (BackendDB): backend database instance
         data: data from backend db
    """

    # Figure out repo line
    repo_lines = {}
    for arch, url in pkg_urls.iteritems():
        repo_lines[arch] = "repo --name=prerelease --baseurl=%s --save "\
                          "--ssl_verify=no --priority 1" % url
    images_ks = {}
    # update ULRs in ks file
    for name, content in imagedata.ksi.items():
        repo_line = repo_lines.get(imagedata.images[name]['arch'])
        if not repo_line:
            # skip architectures without repos
            continue
        new_ks_lines = []
        if exclude_ref_repo:
            for line in content.splitlines():
                if line.startswith('repo ') and \
                     'baseurl=' in line:
                    match = re.match(r"repo --name=([\w\-\.]*) --baseurl=.*", line)
                    if match:
                        continue

                new_ks_lines.append(line)

        else:
            for line in content.splitlines():
                if line.startswith('repo ') and \
                        'baseurl=' in line and \
                        '@BUILD_ID@' in line:
                    match = re.match(r"repo --name=([\w\-\.]*) --baseurl=.*",
                                     line)
                    if match:
                        line = re.sub(r'@BUILD_ID@', snapshot_id, line)
                elif line.startswith('repo ') and \
                        'baseurl=' in line:
                    match = re.match(r"repo --name=([\w\-\.]*) --baseurl=.*",
                                     line)
                    #BASE replace
                    if base_project and base_project != 'latest':
                        match = re.match(r"repo --name=base([\w\-\.]*) --baseurl=.*/latest/repos/.*",
                                         line)
                        if match:
                            base_path = [ x for x in base_project.lower().split(':') if x != 'ref' ]
                            line = line.replace('/latest/', '/%s/' % ('-'.join(base_path[0:-1]) + '_' + base_path[-1]))

                new_ks_lines.append(line)

        new_ks_lines.insert(new_ks_lines.index('%packages')-1, repo_line)
        # Update the ks files in imagedata
        new_ks_content = '\n'.join(new_ks_lines)
        images_ks[name] = new_ks_content

    return images_ks

def trigger_image_creation(images_ks, build_id, path_repo,
                           project, url_pub_base, repo_name, download_num=1):
    """
    trigger_image_creation:
        Prepare the data and trigger the image_creation jobs
    Args:
         images_ks (truple list): [(ks_file_name, ks_file_content),]
         build_id (str): the prerelease repo build_id
    """
    count = 0
    for index, (ksname, kickstart) in enumerate(images_ks.items()):
        count += 1
        name = ksname.replace('.ks', '')
        data = {'name': name,
                'kickstart': kickstart,
                'buildid': build_id,
                'images_path': os.path.join("images", repo_name, name),
                'project': project,
                'repo_path': path_repo,
                'repo': repo_name,
                'url_pub_base': url_pub_base,
                'download_num': download_num
                }
        # add image_building for iris
        pub_enabled = os.getenv("IRIS_PUB_ENABLED", "0") != "0"
        if pub_enabled:
            rest = IrisRestClient(
                os.getenv("IRIS_SERVER"),
                os.getenv("IRIS_USERNAME"),
                base64.b64decode(os.getenv('IRIS_PASSWORDX', '')))
            rest.publish_event("image_building", {
                "project": project,
                "repo": repo_name,
                "name": name,
                })
        trigger_next('%s/image_trigger_%s_%s' % (os.getenv('WORKSPACE'),
                                                 repo_name, index), data)
    return count


def make_live_sync_repo(project, repo, backenddb, base_url, base_path,
              live_repo_base, build, block=False):
    """
    make live sync repo.
    """
    images_count = 0
    repos = {}
    imagedatas = {}

    build_info = build.get_info(project)
    if build_info.get('download_num'):
        current_download_num = build_info.get('download_num') + 1
    else:
        current_download_num = int(1)
    print 'Current download_num = %d' % current_download_num

    # Set obs_target_prj to ref project if it is enabled
    basebuildid = None
    base_project = None
    if os.getenv("REF_USE_FOR_PRERELEASE","0") == "1":
        if build_info.get('ref_obs_target_prj') and \
            ':ref:' in build_info.get('ref_obs_target_prj'):
            basebuildid = build_info.get('ref_obs_target_prj').split(':ref:')[1]
        base_project = build_info.get('base', None)
        if not base_project or ':ref:' not in base_project:
            base_project = None
    print 'BASE PROJECT : %s' % (base_project)
    print 'basebuildid=%s' % basebuildid

    # Make build id from latest snapshot + project suffix
    target_project, tstamp = get_info_from_prerelease_name(project)

    try:
        snapshot = Snapshot(backenddb, base_path, obs_project=target_project)
    except SnapshotError, err:
        raise LocalError("Error getting snapshot info: %s" % str(err))

    try:
        # Set obs_target_prj to ref project if it is enabled
        if os.getenv("REF_USE_FOR_PRERELEASE","0") == "1" and basebuildid:
            prerelease = snapshot.get_prerelease(base_url, tstamp, basebuildid)
            snapshot_path = prerelease.snapshot_path
        else:
            prerelease = snapshot.get_prerelease(base_url, tstamp)
            snapshot_path = snapshot.path
    except SnapshotError, err:
        raise LocalError("Error getting prerelease info: %s" % str(err))

    # Convert live repo to download structure
    live_repo_path = os.path.join(live_repo_base,
                                  project.replace(':', ':/'))
    print 'snapshot-path : %s ' %(snapshot_path)

    local_path = os.path.join(prerelease.path, prerelease.build_id)

    # Cleanup repo directory
    if os.path.isdir(local_path):
        print " repo path=%s exits. Cleanup repo directory" % local_path
        shutil.rmtree(local_path)

   # sync snapshot to release
    sync(live_repo_path, local_path, remove=False, hardlinks=True)

    if buildmonitor_enabled:
        targets = snapshot.targets
        for repo in targets:
            global bm_snapshot_name
            global bm_snapshot_url
            bm_snapshot_name = prerelease.build_id
            bm_snapshot_path = prerelease.path
            bm_snapshot_url = os.path.join(bm_snapshot_path.replace(base_path, base_url),
                                           bm_snapshot_name)
 
            bm_repo = repo['Name']
            bm_arch = repo['Architectures'][0]
            bm_pkg_url = os.path.join(local_path, bm_repo)
            bm_pkg_dir = bm_pkg_url.replace(base_url, base_path)

            if bm_arch == 'ia32':
                bm_arch = 'i686'

            # get rpm files
            bm_pkg_name_lst = []
            bm_pkg_mdate_lst = []
            bm_pkg_size_lst = []
            bm_trg_count = 0
            bm_pkg_count = 0
            BM_PKG_LIMIT = 1100
            for root, dirs, files in os.walk(bm_pkg_dir):
                for each_file in files:
                    if each_file.endswith(".rpm") or each_file.endswith(".deb"):
                        rpm_file_path = root + '/' + each_file
                        rpm_file_mdate = os.path.getmtime(rpm_file_path)
                        rpm_file_size = os.path.getsize(rpm_file_path)
                        bm_pkg_name_lst.append(each_file)
                        bm_pkg_mdate_lst.append(rpm_file_mdate)
                        bm_pkg_size_lst.append(rpm_file_size)

                        # divide the big pkgs
                        bm_pkg_count += 1
                        if bm_pkg_count >= BM_PKG_LIMIT:
                            # for trigger
                            bm_stage = 'Pre_Snap_packages'
                            bm_data = {"bm_stage" : bm_stage,
                                       "project" : project,
                                       "bm_repo" : bm_repo,
                                       "bm_arch" : bm_arch,
                                       "bm_pkg_url" : bm_pkg_url,
                                       "bm_pkg_name_lst" : bm_pkg_name_lst,
                                       "bm_pkg_mdate_lst" : bm_pkg_mdate_lst,
                                       "bm_pkg_size_lst" : bm_pkg_size_lst,
                                       "bm_trg_count" : bm_trg_count,
                                       "bm_pkg_count" : bm_pkg_count,
                                       "BM_PKG_LIMIT" : BM_PKG_LIMIT,
                                      }
                            trigger_next("BUILD-MONITOR-2-%s-%s-%s-trg_%s" % \
                                    (bm_stage, bm_repo, bm_arch, bm_trg_count), bm_data)

                            # clear the data
                            bm_pkg_count = 0
                            bm_trg_count += 1
                            bm_pkg_name_lst = []
                            bm_pkg_mdate_lst = []
                            bm_pkg_size_lst = []

            bm_stage = 'Pre_Snap_packages'
            bm_data = {"bm_stage" : bm_stage,
                       "project" : project,
                       "bm_repo" : bm_repo,
                       "bm_arch" : bm_arch,
                       "bm_pkg_url" : bm_pkg_url,
                       "bm_pkg_name_lst" : bm_pkg_name_lst,
                       "bm_pkg_mdate_lst" : bm_pkg_mdate_lst,
                       "bm_pkg_size_lst" : bm_pkg_size_lst,
                       "bm_trg_count" : bm_trg_count,
                       "bm_pkg_count" : bm_pkg_count,
                       "BM_PKG_LIMIT" : BM_PKG_LIMIT,
                      }
            trigger_next("BUILD-MONITOR-2-%s-%s-%s-trg_%s" % \
                    (bm_stage, bm_repo, bm_arch, bm_trg_count), bm_data)

    # reset 'images' and add download_url meta info
    build.update_info({'images_count': images_count,
                       'images': [],
                       'download_url': os.path.join(base_url,
                                                    prerelease.dir,
                                                    prerelease.build_id),
                       'download_num': int(1)},
                       project)


    #TODO: IMAGER want to read download_num to give up; rsync operation.
    try:
        with open(os.path.join(prerelease.path, prerelease.build_id, 'buildinfo.in'), 'w') as df:
            df.write('download_num=%d\n' % current_download_num)
    except Exception, err:
        print 'not to update download_num note. %s' % str(err)

    return {'project': project,
            'repo' : repos,
            'repo_path': os.path.join(prerelease.dir, prerelease.build_id),
            'build_id': prerelease.build_id,
            'imagedata': imagedatas}


def make_repo(project, repo, backenddb, base_url, base_path,
              live_repo_base, build, block=False):
    """
    make repo.

    Args:
        project (str): OBS prerelease project name
        repo (str): name of the OBS live repository
        backenddb (BackendDB): backenddb instance
    Raises:
        LocalError if can't create repos or can't find image configurations
    """
    images_count = 0
    repos = {}
    imagedatas = {}

    build_info = build.get_info(project)
    if build_info.get('download_num'):
        current_download_num = build_info.get('download_num') + 1
    else:
        current_download_num = int(1)
    print 'Current download_num = %d' % current_download_num

    # Set obs_target_prj to ref project if it is enabled
    basebuildid = None
    base_project = None
    if os.getenv("REF_USE_FOR_PRERELEASE","0") == "1":
        if build_info.get('ref_obs_target_prj') and \
            ':ref:' in build_info.get('ref_obs_target_prj'):
            basebuildid = build_info.get('ref_obs_target_prj').split(':ref:')[1]
        base_project = build_info.get('base', None)
        if not base_project or ':ref:' not in base_project:
            base_project = None
    print 'BASE PROJECT : %s' % (base_project)
    print 'basebuildid=%s' % basebuildid

    # Make build id from latest snapshot + project suffix
    target_project, tstamp = get_info_from_prerelease_name(project)

    try:
        snapshot = Snapshot(backenddb, base_path, obs_project=target_project)
    except SnapshotError, err:
        raise LocalError("Error getting snapshot info: %s" % str(err))

    try:
        # Set obs_target_prj to ref project if it is enabled
        if os.getenv("REF_USE_FOR_PRERELEASE","0") == "1" and basebuildid:
            prerelease = snapshot.get_prerelease(base_url, tstamp, basebuildid)
            snapshot_path = prerelease.snapshot_path
        else:
            prerelease = snapshot.get_prerelease(base_url, tstamp)
            snapshot_path = snapshot.path
    except SnapshotError, err:
        raise LocalError("Error getting prerelease info: %s" % str(err))

    # Convert live repo to download structure
    live_repo_path = os.path.join(live_repo_base,
                                  project.replace(':', ':/'))
    targets = snapshot.targets
    print 'snapshot-path : %s ' %(snapshot_path)
    if not os.path.exists(snapshot_path):
        snapshot_path = snapshot_path.replace('snapshots','releases/daily')
        if not os.path.exists(snapshot_path):
            print("The snapshot %s doesn't exists on download server"
                  %(snapshot_path))

    # Cleanup repo directory
    if os.path.isdir(os.path.join(prerelease.path, prerelease.build_id)):
        print " repo path=%s exits. Cleanup repo directory" % os.path.join(prerelease.path, prerelease.build_id)
        shutil.rmtree(os.path.join(prerelease.path, prerelease.build_id))

    if buildmonitor_enabled:
        global bm_snapshot_name
        global bm_snapshot_url
        bm_snapshot_name = prerelease.build_id
        bm_snapshot_path = prerelease.path
        bm_snapshot_url = os.path.join(bm_snapshot_path.replace(base_path, base_url),
                                       bm_snapshot_name)

    for repo in targets:
        print 'Processing repo:%s' % repo['Name']
        sys.stdout.flush()

        repomaker = RepoMaker(prerelease.build_id,
                              os.path.join(prerelease.path,
                                           prerelease.build_id))
        try:
            repomaker.add_repo(live_repo_path, repo['Name'],
                               repo['Architectures'], move=False)
        except RepoMakerError, err:
            #raise LocalError("Unable to create download repo: %s" % err)
            print "Unable to create download repo: %s" % repr(err)
            repos.update({repo['Name']: {'archs': list(set(repo['Architectures']))}})
            continue

        # Assuming that there can be just one image-configurations-
        # rpm in the repo
        if not repomaker.has_images():
            # repomaker did not found image-configurations in pre_release repo,
            # let's take it from target repo, only one package repo is enough

            # Add image configuration to prerelease repo
            img_conf = find_files(os.path.join(snapshot_path, 'repos',
                                               repo['Name']),
                                  prefix="image-configurations-",
                                  suffix='noarch.rpm')
            img_conf_list = list(img_conf)
            # whether exist package of image-configuration
            if not img_conf_list:
                if buildmonitor_enabled:
                    print '[%s][LocalError] bm_git_tag(%s)\n' % (__file__, bm_git_tag)
                    #buildmonitor.update_fail_status_for_sr_stage(project, bm_git_tag)
                    bm_stage = 'Pre_Snap_Fail'
                    bm_data = {"bm_stage" : bm_stage,
                               "project" : project,
                               "bm_git_tag" : bm_git_tag,
                              }
                    trigger_next("BUILD-MONITOR-4-%s" % bm_stage, bm_data)

                #raise LocalError("Image configuration not found in %s" %
                #        snapshot.path)
                print "Image configuration not found in %s, repo:%s" %(snapshot_path, repo['Name'])
                continue

            for rpm in img_conf_list:
                repomaker.load_imagedata(rpm)

        # whether exist ks poin to the repo
        if not repomaker.has_images():
            continue

        # trigger post snapshot creation job with repo data
        # buildlogs.job
        imagedatas[repo['Name']] = repomaker.imagedata
        repos.update(repomaker.repos)

        if 'basechecker' in project:
            if os.getenv("REF_USE_FOR_PRERELEASE","0") == "1" and basebuildid:
                images_ks = update_ks(repomaker.imagedata, prerelease.snap_buildid,
                                      prerelease.pkg_urls(repo['Name']),
                                      base_project, exclude_ref_repo=True)
            else:
                images_ks = update_ks(repomaker.imagedata, snapshot.build_id,
                                      prerelease.pkg_urls(repo['Name']), exclude_ref_repo=True)
        else:
            # Update ks files
            if os.getenv("REF_USE_FOR_PRERELEASE","0") == "1" and basebuildid:
                images_ks = update_ks(repomaker.imagedata, prerelease.snap_buildid,
                                      prerelease.pkg_urls(repo['Name']),
                                      base_project)
            else:
                images_ks = update_ks(repomaker.imagedata, snapshot.build_id,
                                  prerelease.pkg_urls(repo['Name']))


        if buildmonitor_enabled:
            bm_pkg_urls_dic = prerelease.pkg_urls(repo['Name'])
            print '[%s] prerelease.pkg_urls(%s), base_path(%s)\n' \
                  % (__file__, bm_pkg_urls_dic, base_path)
            bm_repo = repo['Name']             # emul32-wayland / emul64-wayland / arm-wayland / arm64-wayland / target-TM1
            bm_arch = repo['Architectures'][0] # ia32 / x86_64 / armv7l / aarch64 / armv7l
            bm_pkg_url = bm_pkg_urls_dic[bm_arch]
            bm_pkg_dir = bm_pkg_url.replace(base_url, base_path) #http -> /srv/obs
            #print '[%s] bm_arch(%s), os.listdir(bm_pkg_dir)(%s)\n' \
            #      % (__file__, bm_arch, os.listdir(bm_pkg_dir))

            if bm_arch == 'ia32':
                bm_arch = 'i686'

            # get rpm files
            bm_pkg_name_lst = []
            bm_pkg_mdate_lst = []
            bm_pkg_size_lst = []
            bm_trg_count = 0
            bm_pkg_count = 0
            BM_PKG_LIMIT = 1100
            for root, dirs, files in os.walk(bm_pkg_dir):
                for each_file in files:
                    if each_file.endswith(".rpm"):
                        rpm_file_path = root + '/' + each_file
                        rpm_file_mdate = os.path.getmtime(rpm_file_path)
                        rpm_file_size = os.path.getsize(rpm_file_path)
                        bm_pkg_name_lst.append(each_file)
                        bm_pkg_mdate_lst.append(rpm_file_mdate)
                        bm_pkg_size_lst.append(rpm_file_size)
                        #print '[%s] rpm_file_path(%s), rpm_file_mdate(%s), rpm_file_size(%s)\n' \
                        #      % (__file__, rpm_file_path, rpm_file_mdate, rpm_file_size)

                        # divide the big pkgs
                        bm_pkg_count += 1
                        #print '[%s] bm_pkg_count(%s), BM_PKG_LIMIT(%s)\n' \
                        #      % (__file__, bm_pkg_count, BM_PKG_LIMIT)
                        if bm_pkg_count >= BM_PKG_LIMIT:
                            # for trigger
                            bm_stage = 'Pre_Snap_packages'
                            bm_data = {"bm_stage" : bm_stage,
                                       "project" : project,
                                       "bm_repo" : bm_repo,
                                       "bm_arch" : bm_arch,
                                       "bm_pkg_url" : bm_pkg_url,
                                       "bm_pkg_name_lst" : bm_pkg_name_lst,
                                       "bm_pkg_mdate_lst" : bm_pkg_mdate_lst,
                                       "bm_pkg_size_lst" : bm_pkg_size_lst,
                                       "bm_trg_count" : bm_trg_count,
                                       "bm_pkg_count" : bm_pkg_count,
                                       "BM_PKG_LIMIT" : BM_PKG_LIMIT,
                                      }
                            trigger_next("BUILD-MONITOR-2-%s-%s-%s-trg_%s" % (bm_stage, bm_repo, bm_arch, bm_trg_count), bm_data)

                            # clear the data
                            bm_pkg_count = 0
                            bm_trg_count += 1
                            bm_pkg_name_lst = []
                            bm_pkg_mdate_lst = []
                            bm_pkg_size_lst = []
                            #print '[%s] reach the BM_PKG_LIMIT!!(%s), bm_pkg_count(%s), bm_trg_count(%s)\n' \
                            #      % (__file__, BM_PKG_LIMIT, bm_pkg_count, bm_trg_count)

            # for rest pkgs
            #buildmonitor.create_snapshot_packages_for_build_snapshot_package(project, bm_snapshot_name,
            #                                                                 repo['Name'], repo['Architectures'][0],
            #                                                                 bm_pkg_urls_dic, base_url, base_path)
            bm_stage = 'Pre_Snap_packages'
            bm_data = {"bm_stage" : bm_stage,
                       "project" : project,
                       "bm_repo" : bm_repo,
                       "bm_arch" : bm_arch,
                       "bm_pkg_url" : bm_pkg_url,
                       "bm_pkg_name_lst" : bm_pkg_name_lst,
                       "bm_pkg_mdate_lst" : bm_pkg_mdate_lst,
                       "bm_pkg_size_lst" : bm_pkg_size_lst,
                       "bm_trg_count" : bm_trg_count,
                       "bm_pkg_count" : bm_pkg_count,
                       "BM_PKG_LIMIT" : BM_PKG_LIMIT,
                      }
            #print '[%s] for rest pkgs!! BM_PKG_LIMIT(%s), bm_pkg_count(%s), bm_trg_count(%s)\n' \
            #      % (__file__, BM_PKG_LIMIT, bm_pkg_count, bm_trg_count)
            trigger_next("BUILD-MONITOR-2-%s-%s-%s-trg_%s" % (bm_stage, bm_repo, bm_arch, bm_trg_count), bm_data)

        # Generate image info to builddata/ dir
        repomaker.gen_image_info(images_ks)

        # Generate repo manifest
        repomaker.gen_manifest_info(repo['Name'],
                                    os.getenv('GERRIT_FETCH_URL'),
                                    os.getenv('GERRIT_REVIEW_URL'))

        # Do not create images if OBS build failed.
        if block:
            continue

        # trigger image creation jobs
        images_count += trigger_image_creation(images_ks, prerelease.build_id,
                               os.path.join(prerelease.dir,
                                            prerelease.build_id),
                               project, base_url, repo['Name'], download_num=current_download_num)

    # Request number of imager nodes
    if os.getenv("ONDEMAND_SLAVE_CONFIGURATION_ENABLED", "0") == "1":
            if images_count > 0:
                trigger_next("SLAVE_BUILDER", {"data":"dummy"}, \
                         extra_params={"ACTION": "REQUEST_WORKER", \
                                       "PURPOSE": "JENKINS_IMAGER", \
                                       "REQUESTED_NUM_EXECUTORS": "%d" % (images_count / 2 + images_count % 2)})

    # reset 'images' and add download_url meta info
    build.update_info({'images_count': images_count,
                       'images': [],
                       'download_url': os.path.join(base_url,
                                                    prerelease.dir,
                                                    prerelease.build_id),
                       'download_num': int(1)},
                       project)

    #TODO: IMAGER want to read download_num to give up; rsync operation.
    try:
        with open(os.path.join(prerelease.path, prerelease.build_id, 'buildinfo.in'), 'w') as df:
            df.write('download_num=%d\n' % current_download_num)
    except Exception, err:
        print 'not to update download_num note. %s' % str(err)

    return {'project': project,
            'repo' : repos,
            'repo_path': os.path.join(prerelease.dir, prerelease.build_id),
            'build_id': prerelease.build_id,
            'imagedata': imagedatas}


def get_published_repos(repo_path_base, time_stamp):
    """ Search publised repository recursively
    Args:
         repo_path_base (str): path to base dir
         time_stamp (str): time stamp of sbumit tag
    """
    if os.path.isdir(repo_path_base):
        for name in os.listdir(repo_path_base):
            path = os.path.join(repo_path_base, name)
            if os.path.isdir(path):
                if name.endswith(time_stamp):
                    if len(os.listdir(repo_path_base)) <= 1:
                        yield repo_path_base
                    else:
                        yield path
                else:
                    for npath in get_published_repos(path, time_stamp):
                        yield npath

def project_cleanup(backenddb, build, base_path, base_url, event_dict):
    """ request(SR) end of life, this founction should be called to
    delete the prerelease project """

    # Event is from project delted or from request accepted/rejected
    prerelease_project_name = event_dict.get("project") or\
        event_dict.get("sourceproject")

    try:
        target_project, time_stamp = \
            get_info_from_prerelease_name(prerelease_project_name)
    except ValueError:
        print "Can not get prerelease project info from project name," \
            "take no action to %s" % prerelease_project_name
        return 0

    # Delete the prerelease project
    build.cleanup(prerelease_project_name, "Cleaned up by backend automaticly")

    # Get prerelease data from db
    try:
        snapshot = Snapshot(backenddb, base_path, obs_project=target_project)
        prerelease = snapshot.get_prerelease(base_url, time_stamp)
    except SnapshotError, err:
        raise LocalError("Error getting prerelease data: %s" % str(err))

    # remove latest snapshot from prerelease_path as we need to clean up
    # published repos from old snapshots too
    root = prerelease.path.split(prerelease.snapshot.build_id)[0]

    for path in get_published_repos(root, time_stamp):
        print 'Removing the repo: %s' % path
        shutil.rmtree(path)

def project_src_rpm_cleanup(live_repo_base, project):
    """ clean src.rpm files on project
    Args:
         live_repo_base (str): path to live repo base dir
         project (str): project name
    """

    print "==========project_src_rpm_cleanup============"
    live_path = os.path.join(live_repo_base, project.replace(':', ':/'))
    print "live_path: %s" % (live_path)

    for root, dirs, files in os.walk(live_path):
        for filename in files:
            if 'src.rpm' in filename:
                print 'rm %s' % (os.path.join(root, filename))
                os.remove(os.path.join(root, filename))

def check_build_fail(unresolvable_broken_failed_status):
    for repo in unresolvable_broken_failed_status:
        for arch in unresolvable_broken_failed_status[repo]:
            for p in unresolvable_broken_failed_status[repo][arch]:
                if p[-10:] != "_aggregate":
                    return True
    return False

def check_arm_build_projects(project):
    get_list = os.getenv('CHECK_ARM_BUILD_PROJECTS',None)
    if get_list is None:
        return False

    for p in get_list.split(','):
        if p in project:
            return True
    return False

def check_build_fail_arch(unresolvable_broken_failed_status, _arch):
    for repo in unresolvable_broken_failed_status:
        for arch in unresolvable_broken_failed_status[repo]:
            for p in unresolvable_broken_failed_status[repo][arch]:
                if p[-10:] != "_aggregate" and _arch == arch:
                    return True
    return False

def get_unresolvable_broken_packages(unresolvable_broken_failed_status):
    unresolvable_broken_packages = {}

    for repo in unresolvable_broken_failed_status:
        unresolvable_broken_packages[repo] = {}
        for arch in unresolvable_broken_failed_status[repo]:
            unresolvable_broken_packages[repo][arch] = {}
            for p in unresolvable_broken_failed_status[repo][arch]:
                if unresolvable_broken_failed_status[repo][arch][p] in ("unresolvable", "broken"):
                    unresolvable_broken_packages[repo][arch][p] = unresolvable_broken_failed_status[repo][arch][p]

    return unresolvable_broken_packages

def pending_build_exists(project_name):

    try:
        pending_build_queue = get_pending_builds_with_parameter()
        if not pending_build_queue:
            return False
        for _queue in pending_build_queue:
            if 'task' not in _queue or 'name' not in _queue.get('task') \
                or os.getenv('JOB_NAME') != _queue.get('task').get('name'):
                continue
            for action in _queue.get('actions'):
                if not 'parameters' in action:
                    continue
                for param in action.get('parameters'):
                    name = param.get('name')
                    value = param.get('value')
                    if name == 'TRIGGER_INFO' and value:
                        pending_info = trigger_info(value, show=False)
                        if pending_info.get('project') == project_name \
                            and pending_info.get('event_type') == 'OBS_REPO_PUBLISHED':
                            print 'Found the same %s pending build is in the queue(%s)' \
                                % (pending_info.get('project'), _queue)
                            return True
    except Exception as err:
        print repr(err)
        print 'Fail to check queue. Going forward accepted.'

    return False

def main(action):
    """Script entry point.
       Parameters:
          action - cleanup or create_images
    """

    content = trigger_info(os.getenv("TRIGGER_INFO"))

    # Skip this if the same build is in the queue
    if pending_build_exists(content.get('project')) == True:
        print 'SKIP THIS BUILD due to the same build is in the queue'
        return 0

    print '---[JOB STARTED: %s ]-------------------------' % action
    global buildmonitor_enabled
    buildmonitor_enabled = os.getenv("BUILDMONITOR_ENABLED", "0") != "0"
    print 'buildmonitor_enabled(%s)\n' % (buildmonitor_enabled)
    if buildmonitor_enabled:
        bm_start_datetime = datetime.datetime.now()
        global bm_git_tag # for execption handling
        bm_git_tag = None

    obs_api = os.getenv("OBS_API_URL")
    obs_user = os.getenv("OBS_API_USERNAME")
    obs_passwd = os.getenv("OBS_API_PASSWD")
    base_url = os.getenv("URL_PUBLIC_REPO_BASE")
    base_path = os.getenv('PATH_REPO_BASE')
    live_repo_base = os.getenv('PATH_LIVE_REPO_BASE')

    project = content.get("project") or content.get("sourceproject")

    build = BuildService(obs_api, obs_user, obs_passwd)

    # Init backend database
    backenddb = BackendDB(os.getenv("REDIS_HOST"), int(os.getenv("REDIS_PORT")))

    if action == 'create_images':
        repo = content.get("repo")
        info = build.get_info(project)

        buildstatus = build.getbuildstatus(project)
        print 'buildstatus=%s' %(buildstatus)
        build.update_buildstatus(buildstatus,project)
        global bBuildFail
        global BuildFailReason

        unresolvable_broken_failed_status = build.get_package_build_result(project, ("unresolvable", "broken", "failed"))
        if check_arm_build_projects(project):
            bBuildFail = check_build_fail_arch(unresolvable_broken_failed_status,"armv7l")
        else:
            bBuildFail = check_build_fail(unresolvable_broken_failed_status)
        if bBuildFail == True:
            BuildFailReason = "Build"
        else:
            BuildFailReason = ""

        if buildmonitor_enabled:
            unresolvable_broken_packages = get_unresolvable_broken_packages(unresolvable_broken_failed_status)
            if content.get('build_cycle_check', 0) == 1:
                try:
                    cycle_packages = build.cycle_build_added_for_link_project(project, info.get('obs_target_prj'))
                    if cycle_packages is not None:
                        print 'Cycle build detected. %s \nSet bBuildFail True' % cycle_packages
                        bBuildFail = True
                        BuildFailReason = 'Cycles'
                        # Update cycle info
                        for cp_repo in cycle_packages:
                            for cp_item in cycle_packages[cp_repo]:
                                for cp_pkg in cp_item.get('cycles'):
                                    unresolvable_broken_packages[cp_repo][cp_item.get('arch')][cp_pkg] = 'cycle'
                except Exception as err:
                    print repr(err)

            bm_git_tag = info['git_tag']
            #buildmonitor.start_pre_create_snapshot_for_sr_stage(project, bm_git_tag, bm_start_datetime)
            #buildmonitor.start_pre_create_snapshot_for_build_snapshot(project, bm_start_datetime)
            bm_stage = 'Pre_Snap_Start' # (+Pre_Snap_snapshot)
            bm_data = {"bm_stage" : bm_stage,
                       "project" : project,
                       "bm_git_tag" : bm_git_tag,
                       "bm_start_datetime": str(bm_start_datetime),
                       "bBuildFail": bBuildFail,
                       "BuildFailReason": BuildFailReason,
                       "unresolvable_broken_packages": unresolvable_broken_packages
                      }
            trigger_next("BUILD-MONITOR-1-%s" % bm_stage, bm_data)

        # cleanup src.rpm files on Live repo
        # we don't used src.rpm files on prerelease project.
        project_src_rpm_cleanup(live_repo_base,project)

        target_project, tstamp = get_info_from_prerelease_name(project)
        if target_project in backenddb.get_obs_repo_map() and \
           backenddb.get_repos()[backenddb.get_obs_repo_map()[target_project]] and \
           'LiveSnapshot' in backenddb.get_repos()[backenddb.get_obs_repo_map()[target_project]]:
            repo_data = make_live_sync_repo(project, repo, backenddb, base_url, base_path, \
                                  live_repo_base, build, block=bBuildFail)
        else:
            repo_data = make_repo(project, repo, backenddb, base_url, base_path,
                       live_repo_base, build, block=bBuildFail)

        # trigger post snapshot creation job with repo data
        data = repo_data.copy()
        # remove unused item
        data.pop('imagedata')
        parm_backend = {}
        for bknd in list(reversed(range(1,99))):
            if os.getenv('BACKEND_%02d_REGEX' % bknd) and \
                re.search(r'%s' % os.getenv('BACKEND_%02d_REGEX' % bknd), data['project']) is not None:
                parm_backend['BACKEND_SELECTION'] = 'BACKEND_%02d' % bknd
                break
        trigger_next("post-snapshot", data, extra_params=parm_backend)

        if bBuildFail == True:
            # NOTIFY BUILDSTATUS
            trigger_next("NOTIFY-BUILDSTATUS", info)
        else:
            # Only devbase project
            if is_devbase_project(project):
                buildinfo = build.get_info(project)
                trigger_next("TEST-TRIGGER-INFO-UPDATE", buildinfo)

    elif action == 'cleanup':
        # request(SR) end of life, this founction should be called to
        # delete the prerelease project "
        project_cleanup(backenddb, build, base_path, base_url, content)
    else:
        raise LocalError("Not supported method of pre_release_obs job: %s" \
                          % action)

    if buildmonitor_enabled and action == 'create_images' and bBuildFail != True:
        info = build.get_info(project)
        bm_snapshot_num = info['download_num']
        print '[%s] bm_snapshot_num(%s)\n' \
              % (__file__, bm_snapshot_num)
        bm_end_datetime = datetime.datetime.now()
        #print '[%s] project(%s), bm_git_tag(%s), start_time(%s), end_time(%s)\n' \
        #      % (__file__, project, bm_git_tag, bm_start_datetime, bm_end_datetime)
        # for sr_stage & build_snapshot
        #buildmonitor.end_pre_create_snapshot_for_sr_stage(project, bm_git_tag,
        #                                                  bm_start_datetime,
        #                                                  bm_end_datetime)
        #buildmonitor.end_pre_create_snapshot_for_build_snapshot(project,
        #                                                        bm_snapshot_name,
        #                                                        bm_snapshot_url,
        #                                                        bm_end_datetime)
        bm_stage = 'Pre_Snap_End'
        bm_data = {"bm_stage": bm_stage,
                   "project" : project,
                   "bm_git_tag": bm_git_tag,
                   "bm_start_datetime": str(bm_start_datetime),
                   "bm_end_datetime": str(bm_end_datetime),
                   "bm_snapshot_name" : bm_snapshot_name,
                   "bm_snapshot_url" : bm_snapshot_url,
                   "bm_snapshot_num" : bm_snapshot_num,
                  }
        trigger_next("BUILD-MONITOR_%s" % bm_stage, bm_data)

if __name__ == '__main__':
    try:
        sys.exit(main(sys.argv[1]))
    except LocalError, error:
        print error
        sys.exit(1)

