#!/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 datetime
import ast
import subprocess

from common.repomaker import find_files, RepoMaker, RepoMakerError
from common.buildtrigger import trigger_info, trigger_next
from common.buildservice import BuildService
from common.backenddb import BackendDB
from common.snapshot import Snapshot, SnapshotError, snapshot_project_enabled
from common.utils import make_latest_link
from common.send_mail import prepare_mail
from common.gbsutils import gbs_remote_jenkins_build_job,gbs_update_dashboard

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

def bm_update_gbsdbbuild_snapshot_failed(status_reason,gbsbuild_tag):
    bm_stage = 'GBSDBBuild_Snapshot_Failed'
    bm_end_datetime = datetime.datetime.now()
    bm_data = {"bm_stage": bm_stage,
               "bm_end_datetime" : str(bm_end_datetime),
               "gbsbuild_tag" : gbsbuild_tag,
              }
    trigger_next("BUILD-MONITOR-4-%s" %bm_stage, bm_data)

def replace_ks_file_for_gbsdbbuild(kickstart,build_id,gbsbuild_trigger_category):
    gbsdbbuild_dl_postfix = os.getenv('GBSDBBUILD_DL_POSTFIX')
    replaced_ks=''

    snapshot_build_id = ''
    for x in build_id.split('_')[:-1]:
        snapshot_build_id = snapshot_build_id+x+'_'
    snapshot_build_id = snapshot_build_id.rstrip('_')

    for line in kickstart.split('\n'):
        if line.find('snapshots/tizen') != -1:
            if line.find('base/') != -1:
                line+=' --priority=99'
            else:
                repo_name=line.split('--name=')[1].split(' ')[0]
                orig_line=line.replace('/@BUILD_ID@/repos/','/'+snapshot_build_id+'/repos/')+' --priority=99\n'
                if gbsbuild_trigger_category.lower() == 'release build':
                    add_line=line.replace(repo_name,'gbs_repo').replace('/snapshots/tizen/','/'+gbsdbbuild_dl_postfix+'/releases/tizen/').replace('/@BUILD_ID@/repos/','/'+snapshot_build_id+'/'+build_id+'/repos/')+' --priority=1'
                else:
                    add_line=line.replace(repo_name,'gbs_repo').replace('/snapshots/tizen/','/'+gbsdbbuild_dl_postfix+'/tizen/').replace('/@BUILD_ID@/repos/','/'+snapshot_build_id+'/'+build_id+'/repos/')+' --priority=1'
                if gbsbuild_trigger_category.lower() == 'dependency build':
                    line=orig_line + add_line
                else:
                    line=add_line
        replaced_ks+=line+'\n'
    
    return replaced_ks

def get_overall_build_status(live_buildlog_path,gbsbuild_dic):
    overall_build_status = 'S'
    for repo in gbsbuild_dic['repository']:
        for arch in gbsbuild_dic['arch_list'][repo]:
            summary_log_file=os.path.join(live_buildlog_path,repo,arch,'summary.log')
            f = open(summary_log_file,'rb')
            summary_log_contents = f.read()
            f.close()
            print 'summary_log_file: %s, summary_log_contents: %s'\
                   %(summary_log_file,summary_log_contents)
            #if one of repo-arch build fails, overall build status will be 'F'
            if summary_log_contents.split('GBS Build status: "')[1].split('"')[0] == 'F':
                overall_build_status = 'F'
                return overall_build_status

    return overall_build_status

def prepare_trigger_data(images, build_id, path_repo, project,
                           url_pub_base,gbsbuild_tag,gbsbuild_trigger_category):
    """
    prepare_trigger_data:
        Prepare the trigger data
    Args:
         images_ks (truple list): [(ks_file_name, ks_file_content),]
         build_id (str): the prerelease repo build_id
    """
    download_host = os.getenv('DOWNLOAD_HOST')
    trigger_data = {} #trigger job_imager
    trigger_snapdiff = {}

    print 'prepare_trigger_data, images: %s' %images
    for repo_name in images:
        trigger_data[repo_name] = []
        trigger_snapdiff[repo_name] = []
        for ksname, kickstart in images[repo_name].ksi.items():
            print 'ksname: %s\nkickstart:%s' %(ksname,kickstart)
            #temprary test
#            if ksname.find('mobile-wayland-armv7l-tm1') == -1 and ksname.find('iot-headless-2parts-armv7l-rpi3') == -1:
#            if ksname.find('iot-headless-2parts-armv7l-rpi3') == -1:
#                continue
            #end of temprary
            name = ksname.replace('.ks', '')

            kickstart = replace_ks_file_for_gbsdbbuild(kickstart,build_id,gbsbuild_trigger_category)

            if download_host:
                # replace host name
                kickstart = re.sub('^(repo .*--baseurl=[^:]+://)[^/]+(/.*)',
                                   '\\1%s\\2' % download_host, kickstart,
                                   count=1, flags=re.MULTILINE)
            data = {'name': name,
                    'kickstart': kickstart,
                    'buildid': build_id,
                    'images_path': os.path.join("images", repo_name, name),
                    'project': project,
                    'repo': repo_name,
                    'repo_path': path_repo,
                    'url_pub_base': url_pub_base,
                    'gbsbuild_tag': gbsbuild_tag
                    }

            trigger_data[repo_name].append(data)
            data_snapdiff = data.copy()
            data_snapdiff.pop('kickstart')
            trigger_snapdiff[repo_name].append(data_snapdiff)

    return trigger_data, trigger_snapdiff


def trigger_image_creation(trigger_data):
    """Trigger the image_creation jobs"""
    ks_name_list = []
    count = 0
    for repo in trigger_data.keys():
        for index, data in enumerate(trigger_data[repo]):
            trigger_next('gbsfullbuild_image_trigger_%s_%s' % (repo, index), data)
            count += 1
            ks_name_list.append(data['name'])
    # Request number of imager nodes
    if os.getenv("ONDEMAND_SLAVE_CONFIGURATION_ENABLED", "0") == "1":
        if count > 0:
            trigger_next("SLAVE_BUILDER", {"data":"dummy"}, \
                         extra_params={"ACTION": "REQUEST_WORKER", \
                                       "PURPOSE": "JENKINS_IMAGER", \
                                       "REQUESTED_NUM_EXECUTORS": "%d" % count})
    if buildmonitor_enabled:
        gbs_update_data = {"tag" : data['gbsbuild_tag'],
                           "mode" : "image_queued",
                           "reason" : {"ks_name_list":ks_name_list}}
        gbs_update_dashboard(gbs_update_data,via='direct_call')



def create_local_targets(gbsbuild_dic):
    local_targets = []
    for repository in gbsbuild_dic['arch_list']:
        local_targets.append({'Name':repository})
        local_targets[-1]['Architectures'] = []
        tmp_arch_list = gbsbuild_dic['arch_list'][repository]
        for arch in tmp_arch_list:
            if arch.startswith('i') and arch.endswith('86'):
                local_targets[-1]['Architectures'].append('ia32')
            else:
                local_targets[-1]['Architectures'].append(arch)

    return local_targets


def make_repo(project, backenddb, base_path, live_repo_base, gbsbuild_dic):
    """
    make repo.

    Args:
        project (str): OBS prerelease project name
        repo (str): name of the OBS live repository
        backenddb (BackendDB): backenddb instance
        base_path (str): path to the location of snapshot
        live_repo_base (str): path to live repo
    Raises:
        LocalError if can't create repos or can't find image configurations
    """

    try:
#        snapshot = Snapshot(backenddb, base_path, obs_project=project)

        # Increment snapshot
#        snapshot.inc()

        # Store variables into local rather than accessing Snapshot() instance.
#        local_build_id = snapshot.build_id
#        local_targets  = snapshot.targets
#        local_path     = snapshot.path
#        local_dir      = snapshot.dir
        if gbsbuild_dic['trigger_category'].lower() == 'release build':
            local_dir = os.path.join(os.getenv('GBSDBBUILD_DL_POSTFIX'),'releases/tizen',gbsbuild_dic['t_ver']+gbsbuild_dic['profile'])
        else:
            local_dir = os.path.join(os.getenv('GBSDBBUILD_DL_POSTFIX'),'tizen',gbsbuild_dic['t_ver']+gbsbuild_dic['profile'])
        local_build_id = gbsbuild_dic['snapshot_build_id']+'_'+gbsbuild_dic['gbsbuild_tag'].split('/')[-1]
        local_targets  = create_local_targets(gbsbuild_dic)
        local_path = os.path.join(base_path,local_dir,gbsbuild_dic['snapshot_build_id'],local_build_id)

        # Delete Snapshot() instance.
#        del snapshot

        print 'We are working on:'
        print '\t%s\n\t%s\n\t%s\n\t%s\n\n' % (local_build_id, local_targets, local_path, local_dir)
        sys.stdout.flush()
    except SnapshotError, err:
        if buildmonitor_enabled:
            status_reason = "No Snapshot Info"
            bm_update_gbsdbbuild_snapshot_failed(status_reason,gbsbuild_dic['gbsbuild_tag'])
        raise LocalError("Error getting snapshot info: %s" % str(err))

    targets = local_targets
    live_repo_path = os.path.join(live_repo_base,
                                  project.replace(':', ':/'),gbsbuild_dic['gbsbuild_tag'].split('/')[-1])
    parent_snapshot_path = os.path.join(base_path,'snapshots/tizen',
                                        gbsbuild_dic['t_ver']+gbsbuild_dic['profile'],
                                        gbsbuild_dic['snapshot_build_id'])
    repos = {}
    imagedatas = {}

    snapshot_name = local_build_id
    snapshot_url = os.path.join(os.getenv('URL_PUBLIC_REPO_BASE'),local_dir,gbsbuild_dic['snapshot_build_id'],snapshot_name)

    # Convert live repo to download structure
    for repo in targets:
        repomaker = RepoMaker(local_build_id, local_path)

        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(parent_snapshot_path, 'repos',
                                               repo['Name']),
                                  prefix="image-configurations-",
                                  suffix='noarch.rpm')
            print 'parent_snapshot_path: %s' %parent_snapshot_path
            print "image-conf: %s" %img_conf
            img_conf_list = list(img_conf)
            # whether exist package of image-configuration
            if not img_conf_list:
                if buildmonitor_enabled:
                    status_reason = "No Snapshot Info"
                    bm_update_gbsdbbuild_snapshot_failed(status_reason,gbsbuild_dic['gbsbuild_tag'])
                #raise LocalError("Image configuration not found in %s" %
                #        snapshot.path)
                print "Image configuration not found in %s, repo:%s" %(parent_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

        repos.update(repomaker.repos)
        imagedatas[repo['Name']] = repomaker.imagedata

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

        # Generate repo manifest
        manifest_items = repomaker.gen_manifest_info(repo['Name'],
                                    os.getenv('GERRIT_FETCH_URL'),
                                    os.getenv('GERRIT_REVIEW_URL'))
        repomaker.gen_manifest_info_app_from_rpm(repo['Name'],
                                    os.getenv('GERRIT_FETCH_URL'),
                                    os.getenv('GERRIT_REVIEW_URL'),
                                    live_repo_path, repo['Architectures'])

#    if gbsbuild_dic['trigger_category'].lower() == 'release build':
#        repo_path = os.path.join(local_dir,local_build_id)
#    else:
#        repo_path = os.path.join(local_dir,gbsbuild_dic['snapshot_build_id'],local_build_id)
    repo_path = os.path.join(local_dir,gbsbuild_dic['snapshot_build_id'],local_build_id)
    return {'project': project,
            'repo': repos,
            'repo_path': repo_path,
            'build_id': local_build_id,
            'imagedata': imagedatas
        }

def main():
    """Script entry point."""

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

    base_url = os.getenv("URL_PUBLIC_REPO_BASE")
    base_path = os.getenv('PATH_REPO_BASE')
    live_repo_base = os.path.join(os.getenv('PATH_LIVE_REPO_BASE'),os.getenv('GBSDBBUILD_DL_POSTFIX'))

    fields = trigger_info(os.getenv("TRIGGER_INFO"))
    gbsbuild_dic = fields['gbsbuild_dic']
    snapshot_name = gbsbuild_dic['snapshot_build_id']+'_'+gbsbuild_dic['gbsbuild_tag'].split('/')[-1]
    project = gbsbuild_dic['obs_prj']

#    if gbsbuild_dic['gbsbuild_tag'].startswith("RELEASE"):
    if gbsbuild_dic['trigger_category'].lower() == 'release build':
        snapshot_url = os.path.join(os.getenv('URL_PUBLIC_REPO_BASE'),os.getenv('GBSDBBUILD_DL_POSTFIX'),\
                           'releases/tizen',gbsbuild_dic['t_ver']+gbsbuild_dic['profile'],\
                           gbsbuild_dic['snapshot_build_id'],snapshot_name)
    else:
        snapshot_url = os.path.join(os.getenv('URL_PUBLIC_REPO_BASE'),os.getenv('GBSDBBUILD_DL_POSTFIX'),\
                           'tizen',gbsbuild_dic['t_ver']+gbsbuild_dic['profile'],\
                           gbsbuild_dic['snapshot_build_id'],snapshot_name)
    bm_start_datetime = datetime.datetime.now()

##    gbs_remote_jenkins_build_job({'bm_stage':'GBSDBBuild_Snapshot_Start',
##                                  'gbsbuild_dic':gbsbuild_dic,
##                                  'snapshot_name':snapshot_name,
##                                  'snapshot_url':snapshot_url})

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

    #clear live root except current build_id results
    build_id_liverepo = live_repo_base
    for subdir in project.split(':'):
        if subdir != project.split(':')[-1]:
            build_id_liverepo=os.path.join(build_id_liverepo,subdir+':')
        else:
            build_id_liverepo=os.path.join(build_id_liverepo,subdir)
    print "build_id_liverepo = %s" %build_id_liverepo
    cmd = 'sudo rm -rf `find '+build_id_liverepo+\
          ' ! -name '+os.path.basename(build_id_liverepo)+\
          ' | grep -v '+gbsbuild_dic['gbsbuild_tag'].split('/')[-1]+'`'
    print "clear live root command: %s" %cmd
    subprocess.call(cmd, stdout=sys.stdout,stderr=sys.stderr, shell=True)

    if buildmonitor_enabled:
        gbs_update_data = {"tag" : gbsbuild_dic['gbsbuild_tag'],
                           "mode" : "snapshot_started"}
        gbs_update_dashboard(gbs_update_data,via='direct_call')

    if buildmonitor_enabled:
        bm_stage = 'GBSDBBuild_Snapshot_Start'
        bm_data = {"bm_stage": bm_stage,
                   "snapshot_name": snapshot_name,
                   "snapshot_url": snapshot_url,
                   "bm_start_datetime": str(bm_start_datetime),
                   "gbsbuild_tag" : gbsbuild_dic['gbsbuild_tag']
                  }
        trigger_next("BUILD-MONITOR-1-%s" % bm_stage, bm_data)

    repo_data = make_repo(project, backenddb, base_path, live_repo_base, gbsbuild_dic)

    # prepare trigger data for image creation jobs and snapdiff sync jobs
    trigger_data, trigger_snapdiff = prepare_trigger_data(
                                        repo_data['imagedata'],
                                        repo_data['build_id'],
                                        repo_data['repo_path'],
                                        project, base_url, gbsbuild_dic['gbsbuild_tag'], gbsbuild_dic['trigger_category'])

    overall_gbsbuild_status = get_overall_build_status(os.path.join(build_id_liverepo,gbsbuild_dic['gbsbuild_tag'].split('/')[-1],'buildlogs'),gbsbuild_dic)
    # trigger image creation jobs
    if overall_gbsbuild_status == 'S':
        trigger_image_creation(trigger_data)

    # trigger post snapshot creation job with repo data
    data = repo_data.copy()
    # remove unused item
    data.pop('imagedata')
    data['gbsbuild_dic'] = gbsbuild_dic
    trigger_next("gbsfullbuild-buildlogs", data)

    if buildmonitor_enabled:
        # TRIGGER BUILDMONITOR_GBS
        mode = "snapshot_finished"
        gbs_update_data = {"tag" : gbsbuild_dic['gbsbuild_tag'],
                           "mode" : mode}
        gbs_update_dashboard(gbs_update_data,via='trigger',trigger_name=mode)
        # TRIGGER BUILDMONITOR_GBS
        mode = "completed"
        gbs_update_data = {"tag" : gbsbuild_dic['gbsbuild_tag'],
                           "mode" : mode}
        gbs_update_dashboard(gbs_update_data,via='trigger',trigger_name=mode)

    if buildmonitor_enabled:
        bm_stage = 'GBSDBBuild_Snapshot_End'
        bm_end_datetime = datetime.datetime.now()
        bm_data = {"bm_stage": bm_stage,
                   "snapshot_name": snapshot_name,
                   "snapshot_url": snapshot_url,
                   "bm_end_datetime" : str(bm_end_datetime),
                   "gbsbuild_tag" : gbsbuild_dic["gbsbuild_tag"]
                  }
        trigger_next("BUILD-MONITOR_%s" %bm_stage, bm_data)

    # TRIGGER NEXT BUILD-MONITOR-Success (Success or Build failed)
    # In case of build failed, create-snapshot status will be marked as success
    # In case of image creation failed, create-snpahost status will be marked as failed
    if overall_gbsbuild_status == 'S':
        update_message="Succeeded"
    else:
        update_message="Build Failed"
    if len(update_message) < 119:
        trigger_next("BUILD-MONITOR-Success", \
                {'bm_stage':'GBSFULLBUILD_SNAPSHOT',
                 'snapshot_name':gbsbuild_dic['snapshot_build_id'],
                 'gbsfullbuild_string': update_message})

    # TRIGGER NEXT BUILD-MONITOR-Fail
    update_message="Image Creation Failed"
    if len(update_message) < 119:
        trigger_next("BUILD-MONITOR-Failed", \
                {'bm_stage':'GBSFULLBUILD_SNAPSHOT',
                 'snapshot_name':gbsbuild_dic['snapshot_build_id'],
                 'gbsfullbuild_string': update_message})

    # trigger gbsdbbuild update meta in case of reference snapshot building
    git_ref_mapping_info = fields.get('git_ref_mapping_info',{})
    if gbsbuild_dic["gbsbuild_tag"].startswith('REF') and overall_gbsbuild_status == 'S': 
        gbsmeta_data = {"triggered_from" : 'gbsdbbuild_create_snapshot',
                        "git_ref_mapping_info" : git_ref_mapping_info
                        }
        trigger_next("gbsdbbuild-update-meta",gbsmeta_data)

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

