def main(args):
    compare_to_client = TreeherderClient(server_url=HOSTS[args.host])
    production_client = TreeherderClient(server_url=HOSTS["production"])

    # Support comma separated projects
    projects = args.projects.split(',')
    for _project in projects:
        logger.info("Comparing {} against production.".format(_project))
        # Remove properties that are irrelevant for the comparison
        pushes = compare_to_client.get_pushes(_project, count=50)
        for _push in sorted(pushes, key=lambda push: push["revision"]):
            del _push["id"]
            for _rev in _push["revisions"]:
                del _rev["result_set_id"]

        production_pushes = production_client.get_pushes(_project, count=50)
        for _push in sorted(production_pushes, key=lambda push: push["revision"]):
            del _push["id"]
            for _rev in _push["revisions"]:
                del _rev["result_set_id"]

        for index in range(0, len(pushes)):
            assert pushes[index]["revision"] == production_pushes[index]["revision"]
            difference = DeepDiff(pushes[index], production_pushes[index])
            if difference:
                logger.info(difference.to_json())
                logger.info("{}/#/jobs?repo={}&revision={}".format(
                            compare_to_client.server_url,
                            _project,
                            pushes[index]["revision"]))
                logger.info("{}/#/jobs?repo={}&revision={}".format(
                            production_client.server_url,
                            _project,
                            production_pushes[index]["revision"]))
Beispiel #2
0
        "Compare a push from a Treeherder instance to the production instance."
    )
    parser.add_argument(
        "--host", default="localhost", help="Host to compare. It defaults to localhost"
    )
    parser.add_argument("--revision", required=True, help="Revision to compare")
    parser.add_argument(
        "--project",
        default="mozilla-central",
        help="Project to compare. It defaults to mozilla-central",
    )

    args = parser.parse_args()

    th_instance = TreeherderClient(server_url=HOSTS[args.host])
    th_instance_pushid = th_instance.get_pushes(args.project, revision=args.revision)[0]["id"]
    th_instance_jobs = (
        th_instance.get_jobs(args.project, push_id=th_instance_pushid, count=None) or []
    )

    production = TreeherderClient(server_url=HOSTS["production"])
    production_pushid = production.get_pushes(args.project, revision=args.revision)[0]["id"]
    production_jobs = production.get_jobs(args.project, push_id=production_pushid, count=None)

    production_dict = {}
    for job in production_jobs:
        production_dict[job["job_guid"]] = job

    th_instance_dict = {}
    th_instance_not_found = []
    for job in th_instance_jobs:
Beispiel #3
0
def retrieve_test_logs(repo, revision, platform='linux64',
                       cache_dir=None, use_cache=True,
                       warning_re=WARNING_RE):
    """
    Retrieves and processes the test logs for the given revision.

    Returns list of processed files.
    """
    if not cache_dir:
        cache_dir = "%s-%s-%s" % (repo, revision, platform)

    cache = logspam.cache.Cache(cache_dir, warning_re)

    cache_dir_exists = os.path.isdir(cache_dir)
    if cache_dir_exists and use_cache:
        # We already have logs for this revision.
        print "Using cached data"
        try:
            return cache.read_results()
        except logspam.cache.CacheFileNotFoundException as e:
            print "Cache file for %s not found" % warning_re
            print e

    client = TreeherderClient()
    print "getting result set"
    pushes = client.get_pushes(repo, revision=revision)
    print "pushes = client.get_pushes('%s', revision='%s')" % (repo, revision)
    print "got pushes"
    if not pushes:
        print "Failed to find %s in %s" % (revision, repo)
        return None

    print "getting jobs"
    for x in range(5):
        try:
            # option_collection_hash is just the convoluted way of specifying
            # we want a debug build.
            print "jobs = client.get_jobs('%s',result_set_id=%d, count=5000, platform='%s', option_collection_hash='%s')" % (
                    repo, pushes[0]['id'], platform, DEBUG_OPTIONHASH)
            jobs = client.get_jobs(repo,
                                   result_set_id=pushes[0]['id'],
                                   count=5000, # Just make this really large to avoid pagination
                                   platform=platform,
                                   option_collection_hash=DEBUG_OPTIONHASH,
                                   state='completed')
            break
        except requests.exceptions.ConnectionError:
            pass

    if not jobs:
        print "No jobs found for %s %s" % (revision, platform)
        import traceback
        traceback.print_exc()
        return None

    print "got jobs"

    print "getting %d job log urls" % len(jobs)
    job_ids = [ job['id'] for job in jobs ]
    print job_ids
    for x in range(5):
        logs = []
        try:
            for y in range(0, len(job_ids), 100):
                logs = logs + client.get_job_log_url(repo, job_id=job_ids[y:y+100])
            job_logs = logs
            break
        except requests.exceptions.ConnectionError, e:
            pass
class Treeherder(object):
    """Wrapper class for TreeherderClient to ease the use of its API."""

    def __init__(self, application, branch, platform, server_url=TREEHERDER_URL):
        """Create a new instance of the Treeherder class.

        :param application: The name of the application to download.
        :param branch: Name of the branch.
        :param platform: Platform of the application.
        :param server_url: The URL of the Treeherder instance to access.
        """
        self.logger = logging.getLogger(__name__)

        self.client = TreeherderClient(server_url=server_url)
        self.application = application
        self.branch = branch
        self.platform = platform

    def get_treeherder_platform(self, platform):
        """Return the internal Treeherder platform identifier.

        :param platform: Platform of the application.
        """
        try:
            return PLATFORM_MAP[platform]
        except KeyError:
            raise NotSupportedError('Platform "{}" is not supported.'.format(platform))

    def query_builds_by_revision(self, revision, job_type_name='Build', debug_build=False):
        """Retrieve build folders for a given revision with the help of Treeherder.

        :param revision: Revision of the build to download.
        :param job_type_name: Name of the job to look for. For builds it should be
            'Build', 'Nightly', and 'L10n Nightly'. Defaults to `Build`.
        :param debug_build: Download a debug build.
        """
        builds = set()

        try:
            self.logger.info('Querying {url} for list of builds for revision: {revision}'.format(
                             url=self.client.server_url, revision=revision))

            # Retrieve the option hash to filter for type of build (opt, and debug for now)
            option_hash = None
            for key, values in self.client.get_option_collection_hash().iteritems():
                for value in values:
                    if value['name'] == ('debug' if debug_build else 'opt'):
                        option_hash = key
                        break
                if option_hash:
                    break

            resultsets = self.client.get_pushes(self.branch, revision=revision)

            # Set filters to speed-up querying jobs
            kwargs = {
                'option_collection_hash': option_hash,
                'job_type_name': job_type_name,
                'exclusion_profile': False,
            }
            kwargs.update(self.get_treeherder_platform(self.platform))

            for resultset in resultsets:
                kwargs.update({'result_set_id': resultset['id']})
                jobs = self.client.get_jobs(self.branch, **kwargs)
                for job in jobs:
                    log_urls = self.client.get_job_log_url(self.branch, job_id=job['id'])
                    for log_url in log_urls:
                        if self.application in log_url['url']:
                            self.logger.debug('Found build folder: {}'.format(log_url['url']))
                            builds.update([log_url['url']])

        except Exception:
            self.logger.exception('Failure occurred when querying Treeherder for builds')

        return list(builds)
class GetBuild(object):
    ARCHIVE_URL = "https://archive.mozilla.org"
    NIGHTLY_LATEST_URL_FOLDER = "/pub/firefox/nightly/latest-mozilla-central/"
    PLATFORM_FN_MAPPING = {
        'linux32': {
            'key': 'linux-i686',
            'ext': 'tar.bz2',
            'trydl': 'linux',
            'job': ['linux32']
        },
        'linux64': {
            'key': 'linux-x86_64',
            'ext': 'tar.bz2',
            'trydl': 'linux64',
            'job': ['linux64']
        },
        'mac': {
            'key': 'mac',
            'ext': 'dmg',
            'trydl': 'macosx64',
            'job': ['osx']
        },
        'win32': {
            'key': 'win32',
            'ext': 'zip',
            'trydl': 'win32',
            'job': ['windows', '32']
        },
        'win64': {
            'key': 'win64',
            'ext': 'zip',
            'trydl': 'win64',
            'job': ['windows', '64']
        }
    }

    def __init__(self, repo, platform, status_check):
        self.repo = repo
        self.platform = platform
        self.platform_option = 'opt'
        self.pushes = []
        self.skip_status_check = status_check
        self.thclient = TreeherderClient()

    def fetch_push(self, user_email, build_hash, default_count=500):
        tmp_pushes = self.thclient.get_pushes(self.repo, count=default_count)
        for push in tmp_pushes:
            if push['author'].lower() == user_email.lower():
                self.pushes.append(push)
                if build_hash is None:
                    return push
                elif push['revision'] == build_hash:
                    return push
        print "Can't find the specify build hash [%s] in resultsets!!" % build_hash
        return None

    def get_job(self, resultset, platform_keyword_list):
        jobs = self.thclient.get_jobs(self.repo, result_set_id=resultset['id'])
        for job in jobs:
            cnt = 0
            for platform_keyword in platform_keyword_list:
                if platform_keyword in job['platform']:
                    cnt += 1
            if job['platform_option'] == self.platform_option and cnt == len(
                    platform_keyword_list):
                return job
        print "Can't find the specify platform [%s] and platform_options [%s] in jobs!!!" % (
            self.platform, self.platform_option)
        return None

    def get_files_from_remote_url_folder(self, remote_url_str):
        return_dict = {}
        try:
            response_obj = urllib2.urlopen(remote_url_str)
            if response_obj.getcode() == 200:
                for line in response_obj.readlines():
                    match = re.search(r'(?<=href=").*?(?=")', line)
                    if match:
                        href_link = match.group(0)
                        f_name = href_link.split("/")[-1]
                        return_dict[f_name] = href_link
            else:
                print "ERROR: fetch remote file list error with code [%s]" % str(
                    response_obj.getcode())
        except Exception as e:
            print "ERROR: [%s]" % e.message
        return return_dict

    def download_file(self, output_dp, download_link):
        print "Prepare to download the build from link [%s]" % download_link
        response = requests.get(download_link, verify=False, stream=True)
        download_fn = download_link.split("/")[-1]
        if os.path.exists(output_dp) is False:
            os.makedirs(output_dp)
        download_fp = os.path.join(output_dp, download_fn)
        try:
            try:
                total_len = int(response.headers['content-length'])
            except:
                total_len = None
            with open(download_fp, 'wb') as fh:
                for data in tqdm(response.iter_content(chunk_size=512 * 1024),
                                 total=total_len / (512 * 1024)):
                    fh.write(data)
            return download_fp
        except Exception as e:
            print "ERROR: [%s]" % e.message
            return None

    def download_from_remote_url_folder(self, remote_url_str, output_dp):
        # get latest nightly build list from remote url folder
        remote_file_dict = self.get_files_from_remote_url_folder(
            remote_url_str)

        # filter with platform, and return file name with extension
        if len(remote_file_dict.keys()) == 0:
            print "ERROR: can't get remote file list, could be the network error, or url path[%s] wrong!!" % remote_url_str
            return False
        else:
            if self.platform not in self.PLATFORM_FN_MAPPING:
                print "ERROR: we are currently not support the platform[%s] you specified!" % self.platform
                print "We are currently support the platform tag: [%s]" % self.PLATFORM_FN_MAPPING.keys(
                )
                return False
            else:
                matched_keyword = self.PLATFORM_FN_MAPPING[
                    self.platform]['key'] + "." + self.PLATFORM_FN_MAPPING[
                        self.platform]['ext']
                matched_file_list = [
                    fn for fn in remote_file_dict.keys()
                    if matched_keyword in fn and "firefox" in fn
                ]
                if len(matched_file_list) != 1:
                    print "WARN: the possible match file list is not equal 1, list as below: [%s]" % matched_file_list
                    if len(matched_file_list) < 1:
                        return False
                    matched_file_list = sorted(matched_file_list)[-1:]
                    print "WARN: select following file [%s]" % matched_file_list

        # combine file name with json
        matched_file_name = matched_file_list[0]
        json_file_name = matched_file_name.replace(
            self.PLATFORM_FN_MAPPING[self.platform]['key'] + "." +
            self.PLATFORM_FN_MAPPING[self.platform]['ext'],
            self.PLATFORM_FN_MAPPING[self.platform]['key'] + ".json")
        if json_file_name not in remote_file_dict:
            print "ERROR: can't find the json file[%s] in remote file list[%s]!" % (
                json_file_name, remote_file_dict)
            return False
        else:
            print "DEBUG: matched file name: [%s], json_file_name: [%s]" % (
                matched_file_name, json_file_name)

        # download files
        download_fx_url = self.ARCHIVE_URL + remote_file_dict[matched_file_name]
        download_fx_fp = self.download_file(output_dp, download_fx_url)
        download_json_url = self.ARCHIVE_URL + remote_file_dict[json_file_name]
        download_json_fp = self.download_file(output_dp, download_json_url)

        # check download status
        if download_fx_fp and download_json_fp:
            print "SUCCESS: build files download in [%s], [%s] " % (
                download_fx_fp, download_json_fp)
            return True
        else:
            print "ERROR: build files download in [%s,%s] " % (
                download_fx_fp, download_json_fp)
            return False

    def get_try_build(self, user_email, build_hash, output_dp):
        resultset = self.fetch_push(user_email, build_hash)

        # check result set
        if resultset:
            # if build hash is not porvided, use the latest revision as build hash value
            if build_hash is None:
                build_hash = resultset['revision']
            print "Resultset is found, and build hash is [%s]" % build_hash

            # compose remote folder url
            build_folder_url_template = "%s/pub/firefox/%s-builds/%s-%s/%s-%s/"
            build_folder_url = build_folder_url_template % (
                self.ARCHIVE_URL, self.repo, user_email, build_hash, self.repo,
                self.PLATFORM_FN_MAPPING[self.platform]['trydl'])

            # skip status check will retrieve the files list from remote folder url
            if self.skip_status_check:
                return self.download_from_remote_url_folder(
                    build_folder_url, output_dp)
            else:
                job = self.get_job(
                    resultset, self.PLATFORM_FN_MAPPING[self.platform]['job'])
                if job:
                    if job['result'].lower() == "success":
                        return self.download_from_remote_url_folder(
                            build_folder_url, output_dp)
                    else:
                        "Current job status is [%s] !!" % job['result'].lower()
                        return False
                else:
                    print "ERROR: can't find the job!"
                    return False
        else:
            print "ERROR: can't get result set! skip download build from try server, [%s, %s]" % (
                user_email, build_hash)
            return False

    def get_nightly_build(self, output_dp):
        remote_url_str = self.ARCHIVE_URL + self.NIGHTLY_LATEST_URL_FOLDER
        return self.download_from_remote_url_folder(remote_url_str, output_dp)
Beispiel #6
0
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--version",
                    help="test against Thunderbird version",
                    default=None,
                    required=True)
args = parser.parse_args()

tb_version = args.version
tb_branch = branches[tb_version]

with open("testapps.json", "r") as jf:
    data = json.load(jf)

nightly_data = data[tb_version]

pushes = client.get_pushes(tb_branch, )  # gets last 10 by default
for platform in nightly_data:
    platform_data = nightly_data[platform]
    found_artifacts = False
    platform_data['testzip'] = \
        platform_data['testzip'].replace('.zip', '').replace('.tar.gz', '')

    for push in pushes:
        jobs = client.get_jobs(tb_branch, push_id=push['id'])

        for job in jobs:
            logging.debug(job['job_type_name'])
            if (
                    job['state'] == 'completed' and
                    job['job_type_name'] ==
                    mapping_builds[tb_version][platform]
class TriggerBuild(object):
    ARCHIVE_URL = "https://archive.mozilla.org"
    NIGHTLY_LATEST_URL_FOLDER = "/pub/firefox/nightly/latest-mozilla-central/"
    PLATFORM_FN_MAPPING = {
        'linux32': {
            'key': 'linux-i686',
            'ext': 'tar.bz2',
            'trydl': 'linux',
            'job': ['linux32']
        },
        'linux64': {
            'key': 'linux-x86_64',
            'ext': 'tar.bz2',
            'trydl': 'linux64',
            'job': ['linux64']
        },
        'mac': {
            'key': 'mac',
            'ext': 'dmg',
            'trydl': 'macosx64',
            'job': ['osx']
        },
        'win32': {
            'key': 'win32',
            'ext': 'zip',
            'trydl': 'win32',
            'job': ['windows', '32']
        },
        'win64': {
            'key': 'win64',
            'ext': 'zip',
            'trydl': 'win64',
            'job': ['windows', '64']
        }
    }
    ENV_KEY_TRY_REPO_USER_EMAIL = "EMAIL"
    ENV_KEY_ENABLE_WIN32 = "WIN32_FLAG"
    ENV_KEY_SKIP_STATUS_CHECK = "SKIP_STATUS_CHECK"
    ENV_KEY_OUTPUT_DP = "OUTPUT_DP"
    ENV_KEY_BUILD_HASH = "BUILD_HASH"
    ENV_KEY_BUILD_TAG = "BUILD_TAG"
    REPO_NAME = {'TRY': "try", "NIGHTLY": "nightly"}
    DEFAULT_AGENT_CONF_DIR_LINUX = "/home/hasal/Hasal/agent"
    DEFAULT_AGENT_CONF_DIR_MAC = "/Users/hasal/Hasal/agent"
    DEFAULT_AGENT_CONF_DIR_WIN = "C:\\Users\\user\\Hasal\\agent"
    DEFAULT_AGENT_STATUS_DIR = "agent_status"
    DEFAULT_AGENT_JOB_STATUS = {
        'BEGIN': 'begin',
        'FINISH': 'finish',
        'EXCEPTION': 'exception'
    }
    DEFAULT_AGENT_JOB_WACTH_TIMEOUT = 180

    def __init__(self, input_env_data):
        self.platform_option = 'opt'
        self.thclient = TreeherderClient()
        self.pushes = []
        self.env_data = {
            key.upper(): value
            for key, value in input_env_data.items()
        }
        self.dispatch_variables(self.env_data)

    def dispatch_variables(self, input_env_data):
        # if user email not in environment data, repo will be the nightly
        if self.ENV_KEY_TRY_REPO_USER_EMAIL in input_env_data.keys():
            self.user_email = input_env_data[self.ENV_KEY_TRY_REPO_USER_EMAIL]
            self.repo = self.REPO_NAME['TRY']
        else:
            self.repo = self.REPO_NAME['NIGHTLY']

        # check current platform, widnows will double check the --win32 flag enabled or not
        if sys.platform == "linux2":
            self.platform = "linux64"
        elif sys.platform == "darwin":
            self.platform = "mac"
        else:
            if self.ENV_KEY_ENABLE_WIN32 in input_env_data.keys(
            ) and input_env_data[self.ENV_KEY_ENABLE_WIN32] == 'true':
                self.platform = "win32"
            else:
                self.platform = "win64"

        # assign skip status check to variable
        if self.ENV_KEY_SKIP_STATUS_CHECK in input_env_data.keys(
        ) and input_env_data[self.ENV_KEY_SKIP_STATUS_CHECK] == 'true':
            self.skip_status_check = True
        else:
            self.skip_status_check = False

        # assign build hash to variable
        if self.ENV_KEY_BUILD_HASH in input_env_data.keys():
            self.build_hash = input_env_data[self.ENV_KEY_BUILD_HASH]
        else:
            self.build_hash = None

        # assign output dp to variable
        if self.ENV_KEY_OUTPUT_DP in input_env_data.keys():
            self.output_dp = input_env_data[self.ENV_KEY_OUTPUT_DP]
        else:
            self.output_dp = os.getcwd()

        # assign build number to variable
        if self.ENV_KEY_BUILD_TAG in input_env_data.keys():
            self.jenkins_build_tag = input_env_data[self.ENV_KEY_BUILD_TAG]
        else:
            self.jenkins_build_tag = "jenkins-unknown-0"
        self.HASAL_JSON_FN = str(self.jenkins_build_tag) + ".json"

    def check_agent_status(self):
        for i in range(0, self.DEFAULT_AGENT_JOB_WACTH_TIMEOUT):
            # extract job id from agent_status dir
            agent_status_dir_path = os.path.join(os.getcwd(),
                                                 self.DEFAULT_AGENT_STATUS_DIR)
            print "INFO: housekeeping the agent status folder [%s]" % agent_status_dir_path
            if not os.path.exists(agent_status_dir_path):
                os.mkdir(agent_status_dir_path)
            agent_status_file_list = os.listdir(agent_status_dir_path)
            print "DEBUG: current agent status file list [%s]" % agent_status_file_list

            # get latest agent id
            job_id_list = [
                os.path.splitext(id)[0] for id in agent_status_file_list
            ]
            job_id_list.sort(key=lambda x: int(x.rsplit('-', 1)[1]))
            if len(job_id_list) > 0:
                current_id = job_id_list[-1]
            else:
                current_id = 0

            # get latest agent status
            # agent status will sort by alphabetical, so the last one will be the latest status
            job_status_list = [
                os.path.splitext(status)[1].split(os.path.extsep)[1]
                for status in agent_status_file_list
                if os.path.splitext(status)[0] == str(current_id)
            ]
            job_status_list.sort()
            if len(job_status_list) > 0:
                current_job_status = job_status_list[-1]
            else:
                return True

            if current_job_status == self.DEFAULT_AGENT_JOB_STATUS['FINISH']:
                for target_name in agent_status_file_list:
                    check_target = os.path.join(agent_status_dir_path,
                                                target_name)
                    os.remove(check_target)
                return True
            else:
                time.sleep(10)
        return False

    def trigger(self):

        # check agent status folder
        if self.check_agent_status() is False:
            sys.exit(1)

        # download build
        if self.repo == self.REPO_NAME['TRY']:
            download_fx_fp, download_json_fp = self.get_try_build(
                self.user_email, self.build_hash, self.output_dp)
        else:
            download_fx_fp, download_json_fp = self.get_nightly_build(
                self.output_dp)

        if download_fx_fp is None or download_json_fp is None:
            print "ERROR: something wrong with your build download process, please check the setting and job status."
            sys.exit(1)
        else:
            current_platform_release = platform.release().strip()
            # generate hasal.json data
            with open(download_json_fp) as dl_json_fh:
                dl_json_data = json.load(dl_json_fh)
                perfherder_revision = dl_json_data['moz_source_stamp']
                build_pkg_platform = dl_json_data['moz_pkg_platform']
                # mapping the perfherder pkg platform to nomenclature of builddot
                builddot_mapping_platform = {
                    "linux-i686": {
                        "_": "linux32"
                    },
                    "linux-x86_64": {
                        "_": "linux64"
                    },
                    "mac": {
                        "_": "osx-10-10"
                    },
                    "win32": {
                        "_": "windows7-32"
                    },
                    "win64": {
                        "_": "windows8-64",
                        "7": "windows8-64",
                        "10": "windows10-64"
                    }
                }
                with open(self.HASAL_JSON_FN, "w") as write_fh:
                    write_data = copy.deepcopy(self.env_data)
                    write_data['FX-DL-PACKAGE-PATH'] = download_fx_fp
                    write_data['FX-DL-JSON-PATH'] = download_json_fp
                    write_data['PERFHERDER-REVISION'] = perfherder_revision
                    if current_platform_release in builddot_mapping_platform[
                            build_pkg_platform].keys():
                        write_data[
                            'PERFHERDER-PKG-PLATFORM'] = builddot_mapping_platform[
                                build_pkg_platform][current_platform_release]
                    else:
                        write_data[
                            'PERFHERDER-PKG-PLATFORM'] = builddot_mapping_platform[
                                build_pkg_platform]["_"]
                    json.dump(write_data, write_fh)

            if os.path.exists(os.path.join(os.getcwd(), self.HASAL_JSON_FN)):
                print "INFO: current json file created at [%s]" % os.path.join(
                    os.getcwd(), self.HASAL_JSON_FN)
            else:
                print "ERROR: json file not exist in expected path [%s]" % os.path.join(
                    os.getcwd(), self.HASAL_JSON_FN)

            # create agent status folder
            if os.path.exists(
                    os.path.join(os.getcwd(),
                                 self.DEFAULT_AGENT_STATUS_DIR)) is False:
                os.mkdir(
                    os.path.join(os.getcwd(), self.DEFAULT_AGENT_STATUS_DIR))

            # move to agent config folder
            if sys.platform == "linux2":
                new_hasal_json_fp = os.path.join(
                    self.DEFAULT_AGENT_CONF_DIR_LINUX, self.HASAL_JSON_FN)
            elif sys.platform == "darwin":
                new_hasal_json_fp = os.path.join(
                    self.DEFAULT_AGENT_CONF_DIR_MAC, self.HASAL_JSON_FN)
            else:
                new_hasal_json_fp = os.path.join(
                    self.DEFAULT_AGENT_CONF_DIR_WIN, self.HASAL_JSON_FN)
            os.rename(self.HASAL_JSON_FN, new_hasal_json_fp)

            if os.path.exists(new_hasal_json_fp):
                print "INFO: hasal json file move to new location [%s]" % new_hasal_json_fp
            else:
                print "ERROR: hasal json file in not in new location [%s]" % new_hasal_json_fp
            sys.exit(0)

    def fetch_push(self, user_email, build_hash, default_count=500):
        tmp_pushes = self.thclient.get_pushes(self.repo, count=default_count)
        for push in tmp_pushes:
            if push['author'].lower() == user_email.lower():
                self.pushes.append(push)
                if build_hash is None:
                    return push
                elif push['revision'] == build_hash:
                    return push
        print "Can't find the specify build hash [%s] in resultsets!!" % build_hash
        return None

    def get_job(self, resultset, platform_keyword_list):
        jobs = self.thclient.get_jobs(self.repo, result_set_id=resultset['id'])
        for job in jobs:
            cnt = 0
            for platform_keyword in platform_keyword_list:
                if platform_keyword in job['platform']:
                    cnt += 1
            if job['platform_option'] == self.platform_option and cnt == len(
                    platform_keyword_list):
                return job
        print "Can't find the specify platform [%s] and platform_options [%s] in jobs!!!" % (
            self.platform, self.platform_option)
        return None

    def get_files_from_remote_url_folder(self, remote_url_str):
        return_dict = {}
        try:
            response_obj = urllib2.urlopen(remote_url_str)
            if response_obj.getcode() == 200:
                for line in response_obj.readlines():
                    match = re.search(r'(?<=href=").*?(?=")', line)
                    if match:
                        href_link = match.group(0)
                        f_name = href_link.split("/")[-1]
                        return_dict[f_name] = href_link
            else:
                print "ERROR: fetch remote file list error with code [%s]" % str(
                    response_obj.getcode())
        except Exception as e:
            print "ERROR: [%s]" % e.message
        return return_dict

    def download_file(self, output_dp, download_link):
        print "Prepare to download the build from link [%s]" % download_link
        response = requests.get(download_link, verify=False, stream=True)
        download_fn = download_link.split("/")[-1]
        if os.path.exists(output_dp) is False:
            os.makedirs(output_dp)
        download_fp = os.path.join(output_dp, download_fn)
        try:
            try:
                total_len = int(response.headers['content-length'])
            except:
                total_len = None
            with open(download_fp, 'wb') as fh:
                for data in tqdm(response.iter_content(chunk_size=512 * 1024),
                                 total=total_len / (512 * 1024)):
                    fh.write(data)
            return download_fp
        except Exception as e:
            print "ERROR: [%s]" % e.message
            return None

    def download_from_remote_url_folder(self, remote_url_str, output_dp):
        # get latest nightly build list from remote url folder
        remote_file_dict = self.get_files_from_remote_url_folder(
            remote_url_str)

        # filter with platform, and return file name with extension
        if len(remote_file_dict.keys()) == 0:
            print "ERROR: can't get remote file list, could be the network error, or url path[%s] wrong!!" % remote_url_str
            return False
        else:
            if self.platform not in self.PLATFORM_FN_MAPPING:
                print "ERROR: we are currently not support the platform[%s] you specified!" % self.platform
                print "We are currently support the platform tag: [%s]" % self.PLATFORM_FN_MAPPING.keys(
                )
                return False
            else:
                matched_keyword = self.PLATFORM_FN_MAPPING[
                    self.platform]['key'] + "." + self.PLATFORM_FN_MAPPING[
                        self.platform]['ext']
                matched_file_list = [
                    fn for fn in remote_file_dict.keys()
                    if ((matched_keyword in fn) and ('firefox' in fn) and (
                        not fn.endswith('.asc')))
                ]
                if len(matched_file_list) != 1:
                    print "WARN: the possible match file list is not equal 1, list as below: [%s]" % matched_file_list
                    if len(matched_file_list) < 1:
                        return False
                    matched_file_list = sorted(matched_file_list)[-1:]
                    print "WARN: select following file [%s]" % matched_file_list

        # combine file name with json
        matched_file_name = matched_file_list[0]
        json_file_name = matched_file_name.replace(
            self.PLATFORM_FN_MAPPING[self.platform]['key'] + "." +
            self.PLATFORM_FN_MAPPING[self.platform]['ext'],
            self.PLATFORM_FN_MAPPING[self.platform]['key'] + ".json")
        if json_file_name not in remote_file_dict:
            print "ERROR: can't find the json file[%s] in remote file list[%s]!" % (
                json_file_name, remote_file_dict)
            return False
        else:
            print "DEBUG: matched file name: [%s], json_file_name: [%s]" % (
                matched_file_name, json_file_name)

        # download files
        download_fx_url = self.ARCHIVE_URL + remote_file_dict[matched_file_name]
        download_fx_fp = self.download_file(output_dp, download_fx_url)
        download_json_url = self.ARCHIVE_URL + remote_file_dict[json_file_name]
        download_json_fp = self.download_file(output_dp, download_json_url)

        # check download status
        if download_fx_fp and download_json_fp:
            print "SUCCESS: build files download in [%s], [%s] " % (
                download_fx_fp, download_json_fp)
            return (download_fx_fp, download_json_fp)
        else:
            print "ERROR: build files download in [%s,%s] " % (
                download_fx_fp, download_json_fp)
            return None

    def get_try_build(self, user_email, build_hash, output_dp):
        resultset = self.fetch_push(user_email, build_hash)

        # check result set
        if resultset:
            # if build hash is not porvided, use the latest revision as build hash value
            if build_hash is None:
                build_hash = resultset['revision']
            print "Resultset is found, and build hash is [%s]" % build_hash

            # compose remote folder url
            build_folder_url_template = "%s/pub/firefox/%s-builds/%s-%s/%s-%s/"
            build_folder_url = build_folder_url_template % (
                self.ARCHIVE_URL, self.repo, user_email, build_hash, self.repo,
                self.PLATFORM_FN_MAPPING[self.platform]['trydl'])

            # skip status check will retrieve the files list from remote folder url
            if self.skip_status_check:
                return self.download_from_remote_url_folder(
                    build_folder_url, output_dp)
            else:
                job = self.get_job(
                    resultset, self.PLATFORM_FN_MAPPING[self.platform]['job'])
                if job:
                    if job['result'].lower() == "success":
                        return self.download_from_remote_url_folder(
                            build_folder_url, output_dp)
                    else:
                        print "WARNING: Current job status is [%s] !! Your build will download when job status is success" % job[
                            'result'].lower()
                        return (None, None)
                else:
                    print "ERROR: can't find the job!"
                    return (None, None)
        else:
            print "ERROR: can't get result set! skip download build from try server, [%s, %s]" % (
                user_email, build_hash)
            return (None, None)

    def get_nightly_build(self, output_dp):
        remote_url_str = self.ARCHIVE_URL + self.NIGHTLY_LATEST_URL_FOLDER
        return self.download_from_remote_url_folder(remote_url_str, output_dp)