173 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			173 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
#!/usr/bin/python2
 | 
						|
 | 
						|
import cgi, os, socket, sys, urllib2
 | 
						|
import common
 | 
						|
from multiprocessing import pool
 | 
						|
from autotest_lib.frontend import setup_django_environment
 | 
						|
 | 
						|
from autotest_lib.client.common_lib import global_config
 | 
						|
from autotest_lib.client.bin import utils
 | 
						|
from autotest_lib.frontend.afe.json_rpc import serviceHandler
 | 
						|
from autotest_lib.server import system_utils
 | 
						|
from autotest_lib.server import utils as server_utils
 | 
						|
 | 
						|
 | 
						|
_PAGE = """\
 | 
						|
Status: 302 Found
 | 
						|
Content-Type: text/plain
 | 
						|
Location: %s\r\n\r
 | 
						|
"""
 | 
						|
 | 
						|
VIEWER_PREFIX = 'stainless.corp.google.com/browse/'
 | 
						|
 | 
						|
# Define function for retrieving logs
 | 
						|
def _retrieve_logs_dummy(job_path):
 | 
						|
    pass
 | 
						|
 | 
						|
site_retrieve_logs = utils.import_site_function(__file__,
 | 
						|
    "autotest_lib.tko.site_retrieve_logs", "site_retrieve_logs",
 | 
						|
    _retrieve_logs_dummy)
 | 
						|
 | 
						|
site_find_repository_host = utils.import_site_function(__file__,
 | 
						|
    "autotest_lib.tko.site_retrieve_logs", "site_find_repository_host",
 | 
						|
    _retrieve_logs_dummy)
 | 
						|
 | 
						|
form = cgi.FieldStorage(keep_blank_values=True)
 | 
						|
# determine if this is a JSON-RPC request. we support both so that the new TKO
 | 
						|
# client can use its RPC client code, but the old TKO can still use simple GET
 | 
						|
# params.
 | 
						|
_is_json_request = form.has_key('callback')
 | 
						|
 | 
						|
# if this key exists, we check if requested log exists in local machine,
 | 
						|
# and do not return Google Storage URL when the log doesn't exist.
 | 
						|
_local_only = form.has_key('localonly')
 | 
						|
 | 
						|
 | 
						|
def _get_requested_path():
 | 
						|
    if _is_json_request:
 | 
						|
        request_data = form['request'].value
 | 
						|
        request = serviceHandler.ServiceHandler.translateRequest(request_data)
 | 
						|
        parameters = request['params'][0]
 | 
						|
        return parameters['path']
 | 
						|
 | 
						|
    return form['job'].value
 | 
						|
 | 
						|
 | 
						|
def _check_result(args):
 | 
						|
    host = args['host']
 | 
						|
    job_path = args['job_path']
 | 
						|
    shard = args['shard']
 | 
						|
    if shard:
 | 
						|
        http_path = 'http://%s/tko/retrieve_logs.cgi?localonly&job=%s' % (
 | 
						|
                host, job_path)
 | 
						|
    else:
 | 
						|
        http_path = 'http://%s%s' % (host, job_path)
 | 
						|
 | 
						|
    try:
 | 
						|
        # HACK: This urlopen call isn't forwarding HTTP headers correctly. This
 | 
						|
        # leads to uberproxy sitting between master (orignator of this request)
 | 
						|
        # and shard (target of the request) to redirect to the the login page.
 | 
						|
        # We detect this condition and reject the target shard as a viable
 | 
						|
        # redirect. The implication is that we will not redirect to the shard
 | 
						|
        # even if the user could themselves access the shard with the correct
 | 
						|
        # credentials.
 | 
						|
        u = utils.urlopen(http_path)
 | 
						|
        redirected_url = u.geturl()
 | 
						|
        if 'accounts.google.com' in redirected_url:
 | 
						|
            return None
 | 
						|
 | 
						|
        # On Vms the shard name is set to the default gateway but the
 | 
						|
        # browser used to navigate frontends (that runs on the host of
 | 
						|
        # the vms) is immune to the same NAT routing the vms have, so we
 | 
						|
        # need to replace the gateway with 'localhost'.
 | 
						|
        if utils.DEFAULT_VM_GATEWAY in host:
 | 
						|
            normalized_host = host.replace(utils.DEFAULT_VM_GATEWAY, 'localhost')
 | 
						|
        else:
 | 
						|
            try:
 | 
						|
                normalized_host = utils.normalize_hostname(host)
 | 
						|
            except socket.herror:
 | 
						|
                # Ignore error: 'socket.herror: [Errno 1] Unknown host'
 | 
						|
                # This can happen when reverse name lookup is not stable.
 | 
						|
                normalized_host = host
 | 
						|
        return 'http', normalized_host, job_path
 | 
						|
    except urllib2.URLError:
 | 
						|
        return None
 | 
						|
 | 
						|
 | 
						|
def _get_tpool_args(hosts, job_path, is_shard, host_set):
 | 
						|
    """Get a list of arguments to be passed to multiprocessing.pool.ThreadPool.
 | 
						|
 | 
						|
    @param hosts: a list of host names.
 | 
						|
    @param job_path: a requested job path.
 | 
						|
    @param is_shard: True if hosts are shards, False otherwise.
 | 
						|
    @param host_set: a Set to filter out duplicated hosts.
 | 
						|
 | 
						|
    @return: a list of dictionaries to be used as input of _check_result().
 | 
						|
    """
 | 
						|
    args = []
 | 
						|
    for host in hosts:
 | 
						|
        host = host.strip()
 | 
						|
        if host and host != 'localhost' and host not in host_set:
 | 
						|
            host_set.add(host)
 | 
						|
            arg = {'host': host, 'job_path': job_path, 'shard': is_shard}
 | 
						|
            args.append(arg)
 | 
						|
    return args
 | 
						|
 | 
						|
 | 
						|
def find_repository_host(job_path):
 | 
						|
    """Find the machine holding the given logs and return a URL to the logs"""
 | 
						|
    site_repo_info = site_find_repository_host(job_path)
 | 
						|
    if site_repo_info is not None:
 | 
						|
        return site_repo_info
 | 
						|
 | 
						|
    # This cgi script is run only in master (cautotest) and shards.
 | 
						|
    # Drones do not run this script when receiving '/results/...' request.
 | 
						|
    # Only master should check drones and shards for the requested log.
 | 
						|
    # Also restricted users do not have access to drones or shards,
 | 
						|
    # always point them to localhost or google storage.
 | 
						|
    if (not server_utils.is_shard() and
 | 
						|
        not server_utils.is_restricted_user(os.environ.get('REMOTE_USER'))):
 | 
						|
        drones = system_utils.get_drones()
 | 
						|
        shards = system_utils.get_shards()
 | 
						|
 | 
						|
        host_set = set()
 | 
						|
        tpool_args = _get_tpool_args(drones, job_path, False, host_set)
 | 
						|
        tpool_args += _get_tpool_args(shards, job_path, True, host_set)
 | 
						|
 | 
						|
        tpool = pool.ThreadPool()
 | 
						|
        for result_path in tpool.imap_unordered(_check_result, tpool_args):
 | 
						|
            if result_path:
 | 
						|
                return result_path
 | 
						|
 | 
						|
    # If the URL requested is a test result, it is now either on the local
 | 
						|
    # host or in Google Storage.
 | 
						|
    if job_path.startswith('/results/'):
 | 
						|
        # We only care about the path after '/results/'.
 | 
						|
        job_relative_path = job_path[9:]
 | 
						|
        if not _local_only and not os.path.exists(
 | 
						|
                    os.path.join('/usr/local/autotest/results',
 | 
						|
                                 job_relative_path)):
 | 
						|
            gsuri = utils.get_offload_gsuri().split('gs://')[1]
 | 
						|
            return ['https', VIEWER_PREFIX, gsuri + job_relative_path]
 | 
						|
 | 
						|
 | 
						|
def get_full_url(info, log_path):
 | 
						|
    if info is not None:
 | 
						|
        protocol, host, path = info
 | 
						|
        prefix = '%s://%s' % (protocol, host)
 | 
						|
    else:
 | 
						|
        prefix = ''
 | 
						|
        path = log_path
 | 
						|
 | 
						|
    if _is_json_request:
 | 
						|
        return '%s/tko/jsonp_fetcher.cgi?%s' % (prefix,
 | 
						|
                                                os.environ['QUERY_STRING'])
 | 
						|
    else:
 | 
						|
        return prefix + path
 | 
						|
 | 
						|
 | 
						|
log_path = _get_requested_path()
 | 
						|
info = find_repository_host(log_path)
 | 
						|
site_retrieve_logs(log_path)
 | 
						|
print _PAGE % get_full_url(info, log_path)
 |