Exemplo n.º 1
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))
Exemplo n.º 2
0
def _check_dimension_acls(request):
    """Raises AuthorizationError if some requested dimensions are forbidden.

  Uses 'dimension_acls' field from the settings. See proto/config.proto.
  """
    dim_acls = config.settings().dimension_acls
    if not dim_acls or not dim_acls.entry:
        return  # not configured, this is fine

    ident = request.authenticated
    dims = request.properties.dimensions
    assert 'id' in dims or 'pool' in dims, dims  # see _validate_dimensions
    assert ident is not None  # see task_request.init_new_request

    # Forbid targeting individual bots for non-admins, but allow using 'id' if
    # 'pool' is used as well (so whoever can posts tasks to 'pool', can target an
    # individual bot in that pool).
    if 'id' in dims and 'pool' not in dims:
        if not acl.is_admin():
            raise auth.AuthorizationError(
                'Only Swarming administrators can post tasks with "id" dimension '
                'without specifying a "pool" dimension.')

    for k, v in sorted(dims.iteritems()):
        if not _can_use_dimension(dim_acls, ident, k, v):
            raise auth.AuthorizationError(
                'User %s is not allowed to schedule tasks with dimension "%s:%s"'
                % (ident.to_bytes(), k, v))
Exemplo n.º 3
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))
Exemplo n.º 4
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))
Exemplo n.º 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['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))
Exemplo n.º 6
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))
Exemplo n.º 7
0
 def post(self, task_id):
   original_request, _ = self.get_request_and_result(task_id)
   # Retrying a task is essentially reusing the same task request as the
   # original one, but with new parameters.
   new_request = task_request.new_request_clone(
       original_request, allow_high_priority=acl.is_admin())
   result_summary = task_scheduler.schedule_request(new_request)
   self.redirect('/user/task/%s' % result_summary.task_id)
Exemplo n.º 8
0
    def get(self, bot_id):
        # pagination is currently for tasks, not events.
        limit = int(self.request.get('limit', 100))
        cursor = datastore_query.Cursor(urlsafe=self.request.get('cursor'))
        bot_future = bot_management.get_info_key(bot_id).get_async()
        run_results, cursor, more = task_result.TaskRunResult.query(
            task_result.TaskRunResult.bot_id == bot_id).order(
                -task_result.TaskRunResult.started_ts).fetch_page(
                    limit, start_cursor=cursor)

        events_future = bot_management.get_events_query(bot_id).fetch_async(
            100)

        now = utils.utcnow()
        bot = bot_future.get_result()
        # Calculate the time this bot was idle.
        idle_time = datetime.timedelta()
        run_time = datetime.timedelta()
        if run_results:
            run_time = run_results[0].duration_now(now) or datetime.timedelta()
            if not cursor and run_results[0].state != task_result.State.RUNNING:
                # Add idle time since last task completed. Do not do this when a cursor
                # is used since it's not representative.
                idle_time = now - run_results[0].ended_ts
            for index in xrange(1, len(run_results)):
                # .started_ts will always be set by definition but .ended_ts may be None
                # if the task was abandoned. We can't count idle time since the bot may
                # have been busy running *another task*.
                # TODO(maruel): One option is to add a third value "broken_time".
                # Looking at timestamps specifically could help too, e.g. comparing
                # ended_ts of this task vs the next one to see if the bot was assigned
                # two tasks simultaneously.
                if run_results[index].ended_ts:
                    idle_time += (run_results[index - 1].started_ts -
                                  run_results[index].ended_ts)
                    duration = run_results[index].duration
                    if duration:
                        run_time += duration

        params = {
            'bot': bot,
            'bot_id': bot_id,
            'current_version': bot_code.get_bot_version(self.request.host_url),
            'cursor': cursor.urlsafe() if cursor and more else None,
            'events': events_future.get_result(),
            'idle_time': idle_time,
            'is_admin': acl.is_admin(),
            'limit': limit,
            'now': now,
            'run_results': run_results,
            'run_time': run_time,
            'xsrf_token': self.generate_xsrf_token(),
        }
        self.response.write(
            template.render('swarming/restricted_bot.html', params))
Exemplo n.º 9
0
  def get(self, bot_id):
    # pagination is currently for tasks, not events.
    limit = int(self.request.get('limit', 100))
    cursor = datastore_query.Cursor(urlsafe=self.request.get('cursor'))
    bot_future = bot_management.get_info_key(bot_id).get_async()
    run_results, cursor, more = task_result.TaskRunResult.query(
        task_result.TaskRunResult.bot_id == bot_id).order(
            -task_result.TaskRunResult.started_ts).fetch_page(
                limit, start_cursor=cursor)

    events_future = bot_management.get_events_query(bot_id).fetch_async(100)

    now = utils.utcnow()
    bot = bot_future.get_result()
    # Calculate the time this bot was idle.
    idle_time = datetime.timedelta()
    run_time = datetime.timedelta()
    if run_results:
      run_time = run_results[0].duration_now(now) or datetime.timedelta()
      if not cursor and run_results[0].state != task_result.State.RUNNING:
        # Add idle time since last task completed. Do not do this when a cursor
        # is used since it's not representative.
        idle_time = now - run_results[0].ended_ts
      for index in xrange(1, len(run_results)):
        # .started_ts will always be set by definition but .ended_ts may be None
        # if the task was abandoned. We can't count idle time since the bot may
        # have been busy running *another task*.
        # TODO(maruel): One option is to add a third value "broken_time".
        # Looking at timestamps specifically could help too, e.g. comparing
        # ended_ts of this task vs the next one to see if the bot was assigned
        # two tasks simultaneously.
        if run_results[index].ended_ts:
          idle_time += (
              run_results[index-1].started_ts - run_results[index].ended_ts)
          duration = run_results[index].duration
          if duration:
            run_time += duration

    params = {
      'bot': bot,
      'bot_id': bot_id,
      'current_version': bot_code.get_bot_version(self.request.host_url),
      'cursor': cursor.urlsafe() if cursor and more else None,
      'events': events_future.get_result(),
      'idle_time': idle_time,
      'is_admin': acl.is_admin(),
      'limit': limit,
      'now': now,
      'run_results': run_results,
      'run_time': run_time,
      'xsrf_token': self.generate_xsrf_token(),
    }
    self.response.write(
        template.render('swarming/restricted_bot.html', params))
Exemplo n.º 10
0
  def get(self, bot_id):
    # pagination is currently for tasks, not events.
    limit = int(self.request.get('limit', 100))
    cursor = datastore_query.Cursor(urlsafe=self.request.get('cursor'))
    run_results_future = task_result.TaskRunResult.query(
        task_result.TaskRunResult.bot_id == bot_id).order(
            -task_result.TaskRunResult.started_ts).fetch_page_async(
                limit, start_cursor=cursor)
    bot_future = bot_management.get_info_key(bot_id).get_async()
    events_future = bot_management.get_events_query(
        bot_id, True).fetch_async(100)

    now = utils.utcnow()

    # Calculate the time this bot was idle.
    idle_time = datetime.timedelta()
    run_time = datetime.timedelta()
    run_results, cursor, more = run_results_future.get_result()
    if run_results:
      run_time = run_results[0].duration_now(now) or datetime.timedelta()
      if not cursor and run_results[0].state != task_result.State.RUNNING:
        # Add idle time since last task completed. Do not do this when a cursor
        # is used since it's not representative.
        idle_time = now - run_results[0].ended_ts
      for index in xrange(1, len(run_results)):
        # .started_ts will always be set by definition but .ended_ts may be None
        # if the task was abandoned. We can't count idle time since the bot may
        # have been busy running *another task*.
        # TODO(maruel): One option is to add a third value "broken_time".
        # Looking at timestamps specifically could help too, e.g. comparing
        # ended_ts of this task vs the next one to see if the bot was assigned
        # two tasks simultaneously.
        if run_results[index].ended_ts:
          idle_time += (
              run_results[index-1].started_ts - run_results[index].ended_ts)
          # We are taking the whole time the bot was doing work, not just the
          # duration associated with the task.
          duration = run_results[index].duration_total
          if duration:
            run_time += duration

    events = events_future.get_result()
    bot = bot_future.get_result()
    if not bot and events:
      # If there is not BotInfo, look if there are BotEvent child of this
      # entity. If this is the case, it means the bot was deleted but it's
      # useful to show information about it to the user even if the bot was
      # deleted. For example, it could be an auto-scaled bot.
      bot = bot_management.BotInfo(
          key=bot_management.get_info_key(bot_id),
          dimensions=events[0].dimensions,
          state=events[0].state,
          external_ip=events[0].external_ip,
          version=events[0].version,
          quarantined=events[0].quarantined,
          task_id=events[0].task_id,
          last_seen_ts=events[0].ts)

    params = {
      'bot': bot,
      'bot_id': bot_id,
      'current_version': bot_code.get_bot_version(self.request.host_url),
      'cursor': cursor.urlsafe() if cursor and more else None,
      'events': events,
      'idle_time': idle_time,
      'is_admin': acl.is_admin(),
      'limit': limit,
      'now': now,
      'run_results': run_results,
      'run_time': run_time,
      'xsrf_token': self.generate_xsrf_token(),
    }
    self.response.write(
        template.render('swarming/restricted_bot.html', params))
Exemplo n.º 11
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))
Exemplo n.º 12
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)
Exemplo n.º 13
0
  def get(self, bot_id):
    # pagination is currently for tasks, not events.
    limit = int(self.request.get('limit', 100))
    cursor = datastore_query.Cursor(urlsafe=self.request.get('cursor'))
    run_results_future = task_result.TaskRunResult.query(
        task_result.TaskRunResult.bot_id == bot_id).order(
            -task_result.TaskRunResult.started_ts).fetch_page_async(
                limit, start_cursor=cursor)
    bot_future = bot_management.get_info_key(bot_id).get_async()
    events_future = bot_management.get_events_query(
        bot_id, True).fetch_async(100)

    now = utils.utcnow()

    # Calculate the time this bot was idle.
    idle_time = datetime.timedelta()
    run_time = datetime.timedelta()
    run_results, cursor, more = run_results_future.get_result()
    if run_results:
      run_time = run_results[0].duration_now(now) or datetime.timedelta()
      if not cursor and run_results[0].state != task_result.State.RUNNING:
        # Add idle time since last task completed. Do not do this when a cursor
        # is used since it's not representative.
        idle_time = now - run_results[0].ended_ts
      for index in xrange(1, len(run_results)):
        # .started_ts will always be set by definition but .ended_ts may be None
        # if the task was abandoned. We can't count idle time since the bot may
        # have been busy running *another task*.
        # TODO(maruel): One option is to add a third value "broken_time".
        # Looking at timestamps specifically could help too, e.g. comparing
        # ended_ts of this task vs the next one to see if the bot was assigned
        # two tasks simultaneously.
        if run_results[index].ended_ts:
          idle_time += (
              run_results[index-1].started_ts - run_results[index].ended_ts)
          # We are taking the whole time the bot was doing work, not just the
          # duration associated with the task.
          duration = run_results[index].duration_as_seen_by_server
          if duration:
            run_time += duration

    events = events_future.get_result()
    bot = bot_future.get_result()
    if not bot and events:
      # If there is not BotInfo, look if there are BotEvent child of this
      # entity. If this is the case, it means the bot was deleted but it's
      # useful to show information about it to the user even if the bot was
      # deleted. For example, it could be an auto-scaled bot.
      bot = bot_management.BotInfo(
          key=bot_management.get_info_key(bot_id),
          dimensions_flat=bot_management.dimensions_to_flat(
              events[0].dimensions),
          state=events[0].state,
          external_ip=events[0].external_ip,
          authenticated_as=events[0].authenticated_as,
          version=events[0].version,
          quarantined=events[0].quarantined,
          task_id=events[0].task_id,
          last_seen_ts=events[0].ts)

    params = {
      'bot': bot,
      'bot_id': bot_id,
      'current_version': bot_code.get_bot_version(self.request.host_url),
      'cursor': cursor.urlsafe() if cursor and more else None,
      'events': events,
      'idle_time': idle_time,
      'is_admin': acl.is_admin(),
      'limit': limit,
      'now': now,
      'run_results': run_results,
      'run_time': run_time,
      'try_link': '/bot?id=%s' % bot_id,
      'xsrf_token': self.generate_xsrf_token(),
    }
    self.response.write(
        template.render('swarming/restricted_bot.html', params))
Exemplo n.º 14
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)
Exemplo n.º 15
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))
Exemplo n.º 16
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)
Exemplo n.º 17
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))
Exemplo n.º 18
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))