Beispiel #1
0
    def get(self):
        limit = int(self.request.get('limit', 100))
        cursor = datastore_query.Cursor(urlsafe=self.request.get('cursor'))
        sort_by = self.request.get('sort_by', '__key__')
        if sort_by not in self.ACCEPTABLE_BOTS_SORTS:
            self.abort(400, 'Invalid sort_by query parameter')

        if sort_by[0] == '-':
            order = datastore_query.PropertyOrder(
                sort_by[1:], datastore_query.PropertyOrder.DESCENDING)
        else:
            order = datastore_query.PropertyOrder(
                sort_by, datastore_query.PropertyOrder.ASCENDING)

        now = utils.utcnow()
        cutoff = now - datetime.timedelta(
            seconds=config.settings().bot_death_timeout_secs)

        num_bots_busy_future = bot_management.BotInfo.query(
            bot_management.BotInfo.is_busy == True).count_async()
        num_bots_dead_future = bot_management.BotInfo.query(
            bot_management.BotInfo.last_seen_ts < cutoff).count_async()
        num_bots_quarantined_future = bot_management.BotInfo.query(
            bot_management.BotInfo.quarantined == True).count_async()
        num_bots_total_future = bot_management.BotInfo.query().count_async()
        fetch_future = bot_management.BotInfo.query().order(
            order).fetch_page_async(limit, start_cursor=cursor)

        # TODO(maruel): self.request.host_url should be the default AppEngine url
        # version and not the current one. It is only an issue when
        # version-dot-appid.appspot.com urls are used to access this page.
        version = bot_code.get_bot_version(self.request.host_url)
        bots, cursor, more = fetch_future.get_result()
        # Prefetch the tasks. We don't actually use the value here, it'll be
        # implicitly used by ndb local's cache when refetched by the html template.
        tasks = filter(None, (b.task for b in bots))
        ndb.get_multi(tasks)
        num_bots_busy = num_bots_busy_future.get_result()
        num_bots_dead = num_bots_dead_future.get_result()
        num_bots_quarantined = num_bots_quarantined_future.get_result()
        num_bots_total = num_bots_total_future.get_result()
        params = {
            'bots': bots,
            'current_version': version,
            'cursor': cursor.urlsafe() if cursor and more else '',
            'is_admin': acl.is_admin(),
            'is_privileged_user': acl.is_privileged_user(),
            'limit': limit,
            'now': now,
            'num_bots_alive': num_bots_total - num_bots_dead,
            'num_bots_busy': num_bots_busy,
            'num_bots_dead': num_bots_dead,
            'num_bots_quarantined': num_bots_quarantined,
            'sort_by': sort_by,
            'sort_options': self.SORT_OPTIONS,
            'xsrf_token': self.generate_xsrf_token(),
        }
        self.response.write(
            template.render('swarming/restricted_botslist.html', params))
Beispiel #2
0
  def get(self):
    limit = int(self.request.get('limit', 100))
    cursor = datastore_query.Cursor(urlsafe=self.request.get('cursor'))
    sort_by = self.request.get('sort_by', '__key__')
    if sort_by not in self.ACCEPTABLE_BOTS_SORTS:
      self.abort(400, 'Invalid sort_by query parameter')

    if sort_by[0] == '-':
      order = datastore_query.PropertyOrder(
          sort_by[1:], datastore_query.PropertyOrder.DESCENDING)
    else:
      order = datastore_query.PropertyOrder(
          sort_by, datastore_query.PropertyOrder.ASCENDING)

    now = utils.utcnow()
    cutoff = now - datetime.timedelta(
        seconds=config.settings().bot_death_timeout_secs)

    num_bots_busy_future = bot_management.BotInfo.query(
        bot_management.BotInfo.is_busy == True).count_async()
    num_bots_dead_future = bot_management.BotInfo.query(
        bot_management.BotInfo.last_seen_ts < cutoff).count_async()
    num_bots_quarantined_future = bot_management.BotInfo.query(
        bot_management.BotInfo.quarantined == True).count_async()
    num_bots_total_future = bot_management.BotInfo.query().count_async()
    fetch_future = bot_management.BotInfo.query().order(order).fetch_page_async(
        limit, start_cursor=cursor)

    # TODO(maruel): self.request.host_url should be the default AppEngine url
    # version and not the current one. It is only an issue when
    # version-dot-appid.appspot.com urls are used to access this page.
    version = bot_code.get_bot_version(self.request.host_url)
    bots, cursor, more = fetch_future.get_result()
    # Prefetch the tasks. We don't actually use the value here, it'll be
    # implicitly used by ndb local's cache when refetched by the html template.
    tasks = filter(None, (b.task for b in bots))
    ndb.get_multi(tasks)
    num_bots_busy = num_bots_busy_future.get_result()
    num_bots_dead = num_bots_dead_future.get_result()
    num_bots_quarantined = num_bots_quarantined_future.get_result()
    num_bots_total = num_bots_total_future.get_result()
    params = {
      'bots': bots,
      'current_version': version,
      'cursor': cursor.urlsafe() if cursor and more else '',
      'is_admin': acl.is_admin(),
      'is_privileged_user': acl.is_privileged_user(),
      'limit': limit,
      'now': now,
      'num_bots_alive': num_bots_total - num_bots_dead,
      'num_bots_busy': num_bots_busy,
      'num_bots_dead': num_bots_dead,
      'num_bots_quarantined': num_bots_quarantined,
      'sort_by': sort_by,
      'sort_options': self.SORT_OPTIONS,
      'xsrf_token': self.generate_xsrf_token(),
    }
    self.response.write(
        template.render('swarming/restricted_botslist.html', params))
Beispiel #3
0
 def get(self):
     params = {
         "host_url": self.request.host_url,
         "is_admin": acl.is_admin(),
         "is_bot": acl.is_bot(),
         "is_privileged_user": acl.is_privileged_user(),
         "is_user": acl.is_user(),
         "mapreduce_jobs": [],
         "user_type": acl.get_user_type(),
     }
     if acl.is_admin():
         params["mapreduce_jobs"] = [
             {"id": job_id, "name": job_def["name"]} for job_id, job_def in mapreduce_jobs.MAPREDUCE_JOBS.iteritems()
         ]
         params["xsrf_token"] = self.generate_xsrf_token()
     self.response.write(template.render("swarming/root.html", params))
Beispiel #4
0
 def get(self):
   params = {
     'host_url': self.request.host_url,
     'is_admin': acl.is_admin(),
     'is_bot': acl.is_bot(),
     'is_privileged_user': acl.is_privileged_user(),
     'is_user': acl.is_user(),
     'mapreduce_jobs': [],
     'user_type': acl.get_user_type(),
   }
   if acl.is_admin():
     params['mapreduce_jobs'] = [
       {'id': job_id, 'name': job_def['name']}
       for job_id, job_def in mapreduce_jobs.MAPREDUCE_JOBS.iteritems()
     ]
     params['xsrf_token'] = self.generate_xsrf_token()
   self.response.write(template.render('swarming/root.html', params))
Beispiel #5
0
 def get(self):
   params = {
     'host_url': self.request.host_url,
     'is_admin': acl.is_admin(),
     'is_bot': acl.is_bot(),
     'is_privileged_user': acl.is_privileged_user(),
     'is_user': acl.is_user(),
     'mapreduce_jobs': [],
     'user_type': acl.get_user_type(),
   }
   if acl.is_admin():
     params['mapreduce_jobs'] = [
       {'id': job_id, 'name': job_def['job_name']}
       for job_id, job_def in mapreduce_jobs.MAPREDUCE_JOBS.iteritems()
     ]
     params['xsrf_token'] = self.generate_xsrf_token()
   self.response.write(template.render('swarming/root.html', params))
Beispiel #6
0
  def get(self, task_id):
    request, result = self.get_request_and_result(task_id)
    parent_task_future = None
    if request.parent_task_id:
      parent_key = task_pack.unpack_run_result_key(request.parent_task_id)
      parent_task_future = parent_key.get_async()
    children_tasks_futures = [
      task_pack.unpack_result_summary_key(c).get_async()
      for c in result.children_task_ids
    ]

    bot_id = result.bot_id
    following_task_future = None
    previous_task_future = None
    if result.started_ts:
      # Use a shortcut name because it becomes unwieldy otherwise.
      cls = task_result.TaskRunResult

      # Note that the links will be to the TaskRunResult, not to
      # TaskResultSummary.
      following_task_future = cls.query(
          cls.bot_id == bot_id,
          cls.started_ts > result.started_ts,
          ).order(cls.started_ts).get_async()
      previous_task_future = cls.query(
          cls.bot_id == bot_id,
          cls.started_ts < result.started_ts,
          ).order(-cls.started_ts).get_async()

    bot_future = (
        bot_management.get_info_key(bot_id).get_async() if bot_id else None)

    following_task = None
    if following_task_future:
      following_task = following_task_future.get_result()

    previous_task = None
    if previous_task_future:
      previous_task = previous_task_future.get_result()

    parent_task = None
    if parent_task_future:
      parent_task = parent_task_future.get_result()
    children_tasks = [c.get_result() for c in children_tasks_futures]

    cipd = None
    if request.properties.cipd_input:
      cipd = {
        'server': request.properties.cipd_input.server,
        'client_package': request.properties.cipd_input.client_package,
        'packages': self.packages_grouped_by_path(
            request.properties.cipd_input.packages),
      }

    cipd_pins = None
    if result.cipd_pins:
      cipd_pins = {
        'client_package': result.cipd_pins.client_package,
        'packages': self.packages_grouped_by_path(result.cipd_pins.packages),
      }

    params = {
      'bot': bot_future.get_result() if bot_future else None,
      'children_tasks': children_tasks,
      'cipd': cipd,
      'cipd_pins': cipd_pins,
      'is_admin': acl.is_admin(),
      'is_gae_admin': users.is_current_user_admin(),
      'is_privileged_user': acl.is_privileged_user(),
      'following_task': following_task,
      'full_appid': os.environ['APPLICATION_ID'],
      'host_url': self.request.host_url,
      'is_running': result.state == task_result.State.RUNNING,
      'parent_task': parent_task,
      'previous_task': previous_task,
      'request': request,
      'task': result,
      'try_link': '/task?id=%s' % task_id,
      'xsrf_token': self.generate_xsrf_token(),
    }
    self.response.write(template.render('swarming/user_task.html', params))
Beispiel #7
0
  def get(self):
    cursor_str = self.request.get('cursor')
    limit = int(self.request.get('limit', 100))
    sort = self.request.get('sort', self.SORT_CHOICES[0][0])
    state = self.request.get('state', self.STATE_CHOICES[0][0][0])
    counts = self.request.get('counts', '').strip()
    task_tags = [
      line for line in self.request.get('task_tag', '').splitlines() if line
    ]

    if not any(sort == i[0] for i in self.SORT_CHOICES):
      self.abort(400, 'Invalid sort')
    if not any(any(state == i[0] for i in j) for j in self.STATE_CHOICES):
      self.abort(400, 'Invalid state')

    if sort != 'created_ts':
      # Zap all filters in this case to reduce the number of required indexes.
      # Revisit according to the user requests.
      state = 'all'

    now = utils.utcnow()
    # "Temporarily" disable the count. This is too slow on the prod server
    # (>10s). The fix is to have the web page do a XHR query to get the values
    # asynchronously.
    counts_future = None
    if counts == 'true':
      counts_future = self._get_counts_future(now)

    try:
      if task_tags:
        # Enforce created_ts when tags are used.
        sort = 'created_ts'
      query = task_result.get_result_summaries_query(
          None, None, sort, state, task_tags)
      tasks, cursor_str = datastore_utils.fetch_page(query, limit, cursor_str)

      # Prefetch the TaskRequest all at once, so that ndb's in-process cache has
      # it instead of fetching them one at a time indirectly when using
      # TaskResultSummary.request_key.get().
      futures = ndb.get_multi_async(t.request_key for t in tasks)

      # Evaluate the counts to print the filtering columns with the associated
      # numbers.
      state_choices = self._get_state_choices(counts_future)
    except ValueError as e:
      self.abort(400, str(e))

    def safe_sum(items):
      return sum(items, datetime.timedelta())

    def avg(items):
      if not items:
        return 0.
      return safe_sum(items) / len(items)

    def median(items):
      if not items:
        return 0.
      middle = len(items) / 2
      if len(items) % 2:
        return items[middle]
      return (items[middle-1]+items[middle]) / 2

    gen = (t.duration_now(now) for t in tasks)
    durations = sorted(t for t in gen if t is not None)
    gen = (t.pending_now(now) for t in tasks)
    pendings = sorted(t for t in gen if t is not None)
    total_cost_usd = sum(t.cost_usd for t in tasks)
    total_cost_saved_usd = sum(
        t.cost_saved_usd for t in tasks if t.cost_saved_usd)
    # Include the overhead in the total amount of time saved, since it's
    # overhead saved.
    # In theory, t.duration_as_seen_by_server should always be set when
    # t.deduped_from is set but there has some broken entities in the datastore.
    total_saved = safe_sum(
        t.duration_as_seen_by_server for t in tasks
        if t.deduped_from and t.duration_as_seen_by_server)
    duration_sum = safe_sum(durations)
    total_saved_percent = (
        (100. * total_saved.total_seconds() / duration_sum.total_seconds())
        if duration_sum else 0.)

    try_link = '/tasklist?l=%d' % limit
    if task_tags:
      try_link += '&f=' + '&f='.join(task_tags)
    params = {
      'cursor': cursor_str,
      'duration_average': avg(durations),
      'duration_median': median(durations),
      'duration_sum': duration_sum,
      'has_pending': any(t.is_pending for t in tasks),
      'has_running': any(t.is_running for t in tasks),
      'is_admin': acl.is_admin(),
      'is_privileged_user': acl.is_privileged_user(),
      'limit': limit,
      'now': now,
      'pending_average': avg(pendings),
      'pending_median': median(pendings),
      'pending_sum': safe_sum(pendings),
      'show_footer': bool(pendings or durations),
      'sort': sort,
      'sort_choices': self.SORT_CHOICES,
      'state': state,
      'state_choices': state_choices,
      'task_tag': '\n'.join(task_tags),
      'tasks': tasks,
      'total_cost_usd': total_cost_usd,
      'total_cost_saved_usd': total_cost_saved_usd,
      'total_saved': total_saved,
      'total_saved_percent': total_saved_percent,
      'try_link': try_link,
      'xsrf_token': self.generate_xsrf_token(),
    }
    # TODO(maruel): If admin or if the user is task's .user, show the Cancel
    # button. Do not show otherwise.
    self.response.write(template.render('swarming/user_tasks.html', params))

    # Do not let dangling futures linger around.
    ndb.Future.wait_all(futures)
Beispiel #8
0
  def get(self, task_id):
    try:
      key = task_pack.unpack_result_summary_key(task_id)
      request_key = task_pack.result_summary_key_to_request_key(key)
    except ValueError:
      try:
        key = task_pack.unpack_run_result_key(task_id)
        request_key = task_pack.result_summary_key_to_request_key(
            task_pack.run_result_key_to_result_summary_key(key))
      except (NotImplementedError, ValueError):
        self.abort(404, 'Invalid key format.')

    # 'result' can be either a TaskRunResult or TaskResultSummary.
    result_future = key.get_async()
    request_future = request_key.get_async()
    result = result_future.get_result()
    if not result:
      self.abort(404, 'Invalid key.')

    if not acl.is_privileged_user():
      self.abort(403, 'Implement access control based on the user')

    request = request_future.get_result()
    parent_task_future = None
    if request.parent_task_id:
      parent_key = task_pack.unpack_run_result_key(request.parent_task_id)
      parent_task_future = parent_key.get_async()
    children_tasks_futures = [
      task_pack.unpack_result_summary_key(c).get_async()
      for c in result.children_task_ids
    ]

    bot_id = result.bot_id
    following_task_future = None
    previous_task_future = None
    if result.started_ts:
      # Use a shortcut name because it becomes unwieldy otherwise.
      cls = task_result.TaskRunResult

      # Note that the links will be to the TaskRunResult, not to
      # TaskResultSummary.
      following_task_future = cls.query(
          cls.bot_id == bot_id,
          cls.started_ts > result.started_ts,
          ).order(cls.started_ts).get_async()
      previous_task_future = cls.query(
          cls.bot_id == bot_id,
          cls.started_ts < result.started_ts,
          ).order(-cls.started_ts).get_async()

    bot_future = (
        bot_management.get_info_key(bot_id).get_async() if bot_id else None)

    following_task = None
    if following_task_future:
      following_task = following_task_future.get_result()

    previous_task = None
    if previous_task_future:
      previous_task = previous_task_future.get_result()

    parent_task = None
    if parent_task_future:
      parent_task = parent_task_future.get_result()
    children_tasks = [c.get_result() for c in children_tasks_futures]

    params = {
      'bot': bot_future.get_result() if bot_future else None,
      'children_tasks': children_tasks,
      'is_admin': acl.is_admin(),
      'is_gae_admin': users.is_current_user_admin(),
      'is_privileged_user': acl.is_privileged_user(),
      'following_task': following_task,
      'full_appid': os.environ['APPLICATION_ID'],
      'host_url': self.request.host_url,
      'is_running': result.state == task_result.State.RUNNING,
      'now': utils.utcnow(),
      'parent_task': parent_task,
      'previous_task': previous_task,
      'request': request,
      'task': result,
      'xsrf_token': self.generate_xsrf_token(),
    }
    self.response.write(template.render('swarming/user_task.html', params))
Beispiel #9
0
  def get(self):
    """Handles both ndb.Query searches and search.Index().search() queries.

    If |task_name| is set or not affects the meaning of |cursor|. When set, the
    cursor is for search.Index, otherwise the cursor is for a ndb.Query.
    """
    cursor_str = self.request.get('cursor')
    limit = int(self.request.get('limit', 100))
    sort = self.request.get('sort', self.SORT_CHOICES[0][0])
    state = self.request.get('state', self.STATE_CHOICES[0][0][0])
    task_name = self.request.get('task_name', '').strip()
    task_tags = [
      line for line in self.request.get('task_tag', '').splitlines() if line
    ]

    if not any(sort == i[0] for i in self.SORT_CHOICES):
      self.abort(400, 'Invalid sort')
    if not any(any(state == i[0] for i in j) for j in self.STATE_CHOICES):
      self.abort(400, 'Invalid state')

    if sort != 'created_ts':
      # Zap all filters in this case to reduce the number of required indexes.
      # Revisit according to the user requests.
      state = 'all'

    now = utils.utcnow()
    counts_future = self._get_counts_future(now)

    # This call is synchronous.
    try:
      tasks, cursor_str, sort, state = task_result.get_tasks(
          limit, cursor_str, sort, state, task_tags, task_name)

      # Prefetch the TaskRequest all at once, so that ndb's in-process cache has
      # it instead of fetching them one at a time indirectly when using
      # TaskResultSummary.request_key.get().
      futures = ndb.get_multi_async(t.request_key for t in tasks)

      # Evaluate the counts to print the filtering columns with the associated
      # numbers.
      state_choices = self._get_state_choices(counts_future)
    except (search.QueryError, ValueError) as e:
      self.abort(400, str(e))

    def safe_sum(items):
      return sum(items, datetime.timedelta())

    def avg(items):
      if not items:
        return 0.
      return safe_sum(items) / len(items)

    def median(items):
      if not items:
        return 0.
      middle = len(items) / 2
      if len(items) % 2:
        return items[middle]
      return (items[middle-1]+items[middle]) / 2

    gen = (t.duration_now(now) for t in tasks)
    durations = sorted(t for t in gen if t is not None)
    gen = (t.pending_now(now) for t in tasks)
    pendings = sorted(t for t in gen if t is not None)
    total_cost_usd = sum(t.cost_usd for t in tasks)
    total_cost_saved_usd = sum(
        t.cost_saved_usd for t in tasks if t.cost_saved_usd)
    # Include the overhead in the total amount of time saved, since it's
    # overhead saved.
    # In theory, t.duration_total should always be set when t.deduped_from is
    # set but there has some broken entities in the datastore.
    total_saved = safe_sum(
        t.duration_total for t in tasks if t.deduped_from and t.duration_total)
    duration_sum = safe_sum(durations)
    total_saved_percent = (
        (100. * total_saved.total_seconds() / duration_sum.total_seconds())
        if duration_sum else 0.)
    params = {
      'cursor': cursor_str,
      'duration_average': avg(durations),
      'duration_median': median(durations),
      'duration_sum': duration_sum,
      'has_pending': any(t.is_pending for t in tasks),
      'has_running': any(t.is_running for t in tasks),
      'is_admin': acl.is_admin(),
      'is_privileged_user': acl.is_privileged_user(),
      'limit': limit,
      'now': now,
      'pending_average': avg(pendings),
      'pending_median': median(pendings),
      'pending_sum': safe_sum(pendings),
      'show_footer': bool(pendings or durations),
      'sort': sort,
      'sort_choices': self.SORT_CHOICES,
      'state': state,
      'state_choices': state_choices,
      'task_name': task_name,
      'task_tag': '\n'.join(task_tags),
      'tasks': tasks,
      'total_cost_usd': total_cost_usd,
      'total_cost_saved_usd': total_cost_saved_usd,
      'total_saved': total_saved,
      'total_saved_percent': total_saved_percent,
      'xsrf_token': self.generate_xsrf_token(),
    }
    # TODO(maruel): If admin or if the user is task's .user, show the Cancel
    # button. Do not show otherwise.
    self.response.write(template.render('swarming/user_tasks.html', params))

    # Do not let dangling futures linger around.
    ndb.Future.wait_all(futures)
Beispiel #10
0
  def get(self, task_id):
    try:
      key = task_pack.unpack_result_summary_key(task_id)
      request_key = task_pack.result_summary_key_to_request_key(key)
    except ValueError:
      try:
        key = task_pack.unpack_run_result_key(task_id)
        request_key = task_pack.result_summary_key_to_request_key(
            task_pack.run_result_key_to_result_summary_key(key))
      except (NotImplementedError, ValueError):
        self.abort(404, 'Invalid key format.')

    # 'result' can be either a TaskRunResult or TaskResultSummary.
    result_future = key.get_async()
    request_future = request_key.get_async()
    result = result_future.get_result()
    if not result:
      self.abort(404, 'Invalid key.')

    if not acl.is_privileged_user():
      self.abort(403, 'Implement access control based on the user')

    request = request_future.get_result()
    parent_task_future = None
    if request.parent_task_id:
      parent_key = task_pack.unpack_run_result_key(request.parent_task_id)
      parent_task_future = parent_key.get_async()
    children_tasks_futures = [
      task_pack.unpack_result_summary_key(c).get_async()
      for c in result.children_task_ids
    ]

    bot_id = result.bot_id
    following_task_future = None
    previous_task_future = None
    if result.started_ts:
      # Use a shortcut name because it becomes unwieldy otherwise.
      cls = task_result.TaskRunResult

      # Note that the links will be to the TaskRunResult, not to
      # TaskResultSummary.
      following_task_future = cls.query(
          cls.bot_id == bot_id,
          cls.started_ts > result.started_ts,
          ).order(cls.started_ts).get_async()
      previous_task_future = cls.query(
          cls.bot_id == bot_id,
          cls.started_ts < result.started_ts,
          ).order(-cls.started_ts).get_async()

    bot_future = (
        bot_management.get_info_key(bot_id).get_async() if bot_id else None)

    following_task = None
    if following_task_future:
      following_task = following_task_future.get_result()

    previous_task = None
    if previous_task_future:
      previous_task = previous_task_future.get_result()

    parent_task = None
    if parent_task_future:
      parent_task = parent_task_future.get_result()
    children_tasks = [c.get_result() for c in children_tasks_futures]

    params = {
      'bot': bot_future.get_result() if bot_future else None,
      'children_tasks': children_tasks,
      'is_admin': acl.is_admin(),
      'is_gae_admin': users.is_current_user_admin(),
      'is_privileged_user': acl.is_privileged_user(),
      'following_task': following_task,
      'full_appid': os.environ['APPLICATION_ID'],
      'host_url': self.request.host_url,
      'is_running': result.state == task_result.State.RUNNING,
      'now': utils.utcnow(),
      'parent_task': parent_task,
      'previous_task': previous_task,
      'request': request,
      'task': result,
      'xsrf_token': self.generate_xsrf_token(),
    }
    self.response.write(template.render('swarming/user_task.html', params))
Beispiel #11
0
    def get(self, task_id):
        try:
            key = task_pack.unpack_result_summary_key(task_id)
            request_key = task_pack.result_summary_key_to_request_key(key)
        except ValueError:
            try:
                key = task_pack.unpack_run_result_key(task_id)
                request_key = task_pack.result_summary_key_to_request_key(
                    task_pack.run_result_key_to_result_summary_key(key)
                )
            except (NotImplementedError, ValueError):
                self.abort(404, "Invalid key format.")

        # 'result' can be either a TaskRunResult or TaskResultSummary.
        result_future = key.get_async()
        request_future = request_key.get_async()
        result = result_future.get_result()
        if not result:
            self.abort(404, "Invalid key.")

        if not acl.is_privileged_user():
            self.abort(403, "Implement access control based on the user")

        request = request_future.get_result()
        parent_task_future = None
        if request.parent_task_id:
            parent_key = task_pack.unpack_run_result_key(request.parent_task_id)
            parent_task_future = parent_key.get_async()
        children_tasks_futures = [task_pack.unpack_result_summary_key(c).get_async() for c in result.children_task_ids]

        bot_id = result.bot_id
        following_task_future = None
        previous_task_future = None
        if result.started_ts:
            # Use a shortcut name because it becomes unwieldy otherwise.
            cls = task_result.TaskRunResult

            # Note that the links will be to the TaskRunResult, not to
            # TaskResultSummary.
            following_task_future = (
                cls.query(cls.bot_id == bot_id, cls.started_ts > result.started_ts).order(cls.started_ts).get_async()
            )
            previous_task_future = (
                cls.query(cls.bot_id == bot_id, cls.started_ts < result.started_ts).order(-cls.started_ts).get_async()
            )

        bot_future = bot_management.get_info_key(bot_id).get_async() if bot_id else None

        following_task = None
        if following_task_future:
            following_task = following_task_future.get_result()

        previous_task = None
        if previous_task_future:
            previous_task = previous_task_future.get_result()

        parent_task = None
        if parent_task_future:
            parent_task = parent_task_future.get_result()
        children_tasks = [c.get_result() for c in children_tasks_futures]

        params = {
            "bot": bot_future.get_result() if bot_future else None,
            "children_tasks": children_tasks,
            "is_admin": acl.is_admin(),
            "is_gae_admin": users.is_current_user_admin(),
            "is_privileged_user": acl.is_privileged_user(),
            "following_task": following_task,
            "full_appid": os.environ["APPLICATION_ID"],
            "host_url": self.request.host_url,
            "is_running": result.state == task_result.State.RUNNING,
            "now": utils.utcnow(),
            "parent_task": parent_task,
            "previous_task": previous_task,
            "request": request,
            "task": result,
            "xsrf_token": self.generate_xsrf_token(),
        }
        self.response.write(template.render("swarming/user_task.html", params))
Beispiel #12
0
    def get(self):
        """Handles both ndb.Query searches and search.Index().search() queries.

    If |task_name| is set or not affects the meaning of |cursor|. When set, the
    cursor is for search.Index, otherwise the cursor is for a ndb.Query.
    """
        cursor_str = self.request.get('cursor')
        limit = int(self.request.get('limit', 100))
        sort = self.request.get('sort', self.SORT_CHOICES[0][0])
        state = self.request.get('state', self.STATE_CHOICES[0][0][0])
        task_name = self.request.get('task_name', '').strip()
        task_tags = [
            line for line in self.request.get('task_tag', '').splitlines()
            if line
        ]

        if not any(sort == i[0] for i in self.SORT_CHOICES):
            self.abort(400, 'Invalid sort')
        if not any(any(state == i[0] for i in j) for j in self.STATE_CHOICES):
            self.abort(400, 'Invalid state')

        if sort != 'created_ts':
            # Zap all filters in this case to reduce the number of required indexes.
            # Revisit according to the user requests.
            state = 'all'

        now = utils.utcnow()
        counts_future = self._get_counts_future(now)

        # This call is synchronous.
        try:
            tasks, cursor_str, sort, state = task_result.get_tasks(
                limit, cursor_str, sort, state, task_tags, task_name)

            # Prefetch the TaskRequest all at once, so that ndb's in-process cache has
            # it instead of fetching them one at a time indirectly when using
            # TaskResultSummary.request_key.get().
            futures = ndb.get_multi_async(t.request_key for t in tasks)

            # Evaluate the counts to print the filtering columns with the associated
            # numbers.
            state_choices = self._get_state_choices(counts_future)
        except (search.QueryError, ValueError) as e:
            self.abort(400, str(e))

        def safe_sum(items):
            return sum(items, datetime.timedelta())

        def avg(items):
            if not items:
                return 0.
            return safe_sum(items) / len(items)

        def median(items):
            if not items:
                return 0.
            middle = len(items) / 2
            if len(items) % 2:
                return items[middle]
            return (items[middle - 1] + items[middle]) / 2

        gen = (t.duration_now(now) for t in tasks)
        durations = sorted(t for t in gen if t is not None)
        gen = (t.pending_now(now) for t in tasks)
        pendings = sorted(t for t in gen if t is not None)
        total_cost_usd = sum(t.cost_usd for t in tasks)
        total_cost_saved_usd = sum(t.cost_saved_usd for t in tasks
                                   if t.cost_saved_usd)
        # Include the overhead in the total amount of time saved, since it's
        # overhead saved.
        # In theory, t.duration_total should always be set when t.deduped_from is
        # set but there has some broken entities in the datastore.
        total_saved = safe_sum(t.duration_total for t in tasks
                               if t.deduped_from and t.duration_total)
        duration_sum = safe_sum(durations)
        total_saved_percent = ((100. * total_saved.total_seconds() /
                                duration_sum.total_seconds())
                               if duration_sum else 0.)
        params = {
            'cursor': cursor_str,
            'duration_average': avg(durations),
            'duration_median': median(durations),
            'duration_sum': duration_sum,
            'has_pending': any(t.is_pending for t in tasks),
            'has_running': any(t.is_running for t in tasks),
            'is_admin': acl.is_admin(),
            'is_privileged_user': acl.is_privileged_user(),
            'limit': limit,
            'now': now,
            'pending_average': avg(pendings),
            'pending_median': median(pendings),
            'pending_sum': safe_sum(pendings),
            'show_footer': bool(pendings or durations),
            'sort': sort,
            'sort_choices': self.SORT_CHOICES,
            'state': state,
            'state_choices': state_choices,
            'task_name': task_name,
            'task_tag': '\n'.join(task_tags),
            'tasks': tasks,
            'total_cost_usd': total_cost_usd,
            'total_cost_saved_usd': total_cost_saved_usd,
            'total_saved': total_saved,
            'total_saved_percent': total_saved_percent,
            'xsrf_token': self.generate_xsrf_token(),
        }
        # TODO(maruel): If admin or if the user is task's .user, show the Cancel
        # button. Do not show otherwise.
        self.response.write(template.render('swarming/user_tasks.html',
                                            params))

        # Do not let dangling futures linger around.
        ndb.Future.wait_all(futures)