blob: c5b05de0d5119ceb5d42a184951601900b9e106f [file] [log] [blame]
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# Copyright 2019 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""CLI that makes it easier to interact with remote commands."""
from datetime import datetime
import argparse
import os
import sys
import time
# pylint: disable=no-name-in-module, import-error
from google.cloud import storage
import remote_requests
import run_suite_request
import scheduler_common
import build_connector
os.environ.setdefault(
"GOOGLE_APPLICATION_CREDENTIALS",
"%s/.service_account.json" % os.environ["HOME"],
)
class MoblabRemoteSchedulerCLI(object):
"""Remote command CLI."""
def __init__(self, moblab_bucket_name):
self.moblab_bucket_name = moblab_bucket_name
self.storage_client = storage.Client()
self.build_connector = build_connector.MoblabBuildConnector(
moblab_bucket_name, self.storage_client
)
# TODO make this a time based cache
self.executed = None
def create_runsuite_command(self, board, model, suite, build, priority=2):
suite_request = run_suite_request.MoblabSuiteRunRequestWrapper(
board=board,
build=build,
model=model,
priority=priority,
suite=suite,
min_duts=1,
expires_at_sec_utc=int(time.time()) + 24 * 60 * 60,
)
return suite_request
def append_commands_to_list(self, new_commands):
blob = storage.Blob(
scheduler_common.SCHEDULE_FILENAME,
self.storage_client.bucket(self.moblab_bucket_name),
)
commands = remote_requests.MoblabRemoteRequests()
commands.load_requests_from_gcs(blob)
for command in new_commands:
commands.add_request(command)
commands.save_requests_to_gcs(blob)
def overwrite_command_list(self, new_commands):
blob = storage.Blob(
scheduler_common.SCHEDULE_FILENAME,
self.storage_client.bucket(self.moblab_bucket_name),
)
new_commands.save_requests_to_gcs(blob)
def display_all_command_status(self):
blob = storage.Blob(
scheduler_common.SCHEDULE_FILENAME,
self.storage_client.bucket(self.moblab_bucket_name),
)
commands = remote_requests.MoblabRemoteRequests()
commands.load_requests_from_gcs(blob)
for request in commands.requests:
is_executed, is_expired = self.get_request_status(request)
status = "pending"
if is_executed:
status = (
"executed\t\t\t%s"
% scheduler_common.get_executed_contents(
self.storage_client,
self.moblab_bucket_name,
request.unique_id,
).split("\n")
)
elif is_expired:
status = "expired"
print(
(
"Model: %s\t\t\t Build: %s\t\t Suite: %s\t\t\t"
" Status: %s\t\t\tPubsub: %s"
)
% (
request.model,
request.build,
request.suite,
status,
request.pubsub_message_id,
)
)
def get_input_from_list(self, options, question):
i = 0
for option in options:
print("%d: %s" % (i, option))
i += 1
sels = eval(input(question + ": "))
if isinstance(sels, int):
return [options[sels]]
selected_opts = []
for sel in sels:
selected_opts.append(options[sel])
return selected_opts
def interactive_queue_runsuite(self):
boards = [
board.split("-")[0]
for board in self.build_connector.get_boards_available()
]
boards = list(set(boards))
selected_boards = self.get_input_from_list(
boards, "Pick a board number "
)
new_commands = []
milestone = None
build = None
selected_suites = None
for board in selected_boards:
board_model_map = {
"amd64-generic-cheets": ["rainier"],
"cheza": ["cheza"],
"cheza-freedreno": ["cheza"],
"coral": [
"astronaut",
"babymega",
"babytiger",
"blacktip",
"blacktip360",
"blacktiplte",
"blue",
"bruce",
"coral",
"epaulette",
"lava",
"mako",
"nasher",
"nasher360",
"porbeagle",
"rabbid",
"robo",
"robo360",
"santa",
"thresher",
"whitetip",
"whitetip1",
"whitetip2",
],
"grunt": [
"aleena",
"barla",
"careena",
"delan",
"grunt",
"kasumi",
"kasumi360",
"liara",
],
"hatch": ["hatch", "hatch_whl"],
"kahlee": ["kahlee"],
"kalista": ["karma"],
"kukui": ["kukui"],
"nami": [
"akali",
"akali360",
"bard",
"ekko",
"nami",
"pantheon",
"sona",
"syndra",
"vayne",
],
"nautilus": ["nautilus", "nautiluslte"],
"octopus": [
"ampton",
"apel",
"bip",
"bluebird",
"bobba",
"bobba360",
"casta",
"fleex",
"grabbiter",
"korath",
"laser14",
"meep",
"mimrock",
"nospike",
"orbatrix",
"phaser",
"phaser360",
"sparky",
"sparky360",
"yorp",
],
"rammus": ["shyvana"],
"reef": [
"alan",
"basking",
"bigdaddy",
"electro",
"pyro",
"reef",
"sand",
"snappy",
],
"sarien": [
"arcada",
"arcada_signed",
"sarien",
"sarien_signed",
],
"scarlet": ["dru", "druwl", "dumo", "overkill"],
"scarlet-arcnext": ["dru", "druwl", "dumo"],
"soraka": ["soraka"],
}
if not board in board_model_map:
selected_models = [board]
else:
selected_models = self.get_input_from_list(
board_model_map[board], "Pick a model number"
)
for model in selected_models:
if not milestone:
milestones = [
milestone.split("-")[0]
for milestone in self.build_connector.get_milestones_available(
board
)
]
milestones.sort()
milestone = self.get_input_from_list(
milestones, "Pick a milestone number"
)[0]
if not build:
builds = self.build_connector.get_builds_for_milestone(
board, milestone
)
builds.sort()
build = self.get_input_from_list(
builds, "Pick a build number "
)[0]
suites = ["cts_N", "cts_P", "gts", "dummy_server"]
if not selected_suites:
selected_suites = self.get_input_from_list(
suites, "Pick a suite number "
)
for suite in selected_suites:
new_commands.append(
self.create_runsuite_command(
board, model, suite, "%s-%s" % (milestone, build)
)
)
self.append_commands_to_list(new_commands)
def remove_duplicates(self):
blob = storage.Blob(
scheduler_common.SCHEDULE_FILENAME,
self.storage_client.bucket(self.moblab_bucket_name),
)
commands = remote_requests.MoblabRemoteRequests()
commands.load_requests_from_gcs(blob)
already_seen = []
dup_count = 0
deduped_commands = remote_requests.MoblabRemoteRequests()
for request in commands.requests:
key = "%s%s%s" % (request.model, request.build, request.suite)
if key not in already_seen:
already_seen.append(key)
deduped_commands.add_request(request)
else:
dup_count += 1
print("Removed %s duplicates" % dup_count)
self.overwrite_command_list(deduped_commands)
def remove_expired(self):
blob = storage.Blob(
scheduler_common.SCHEDULE_FILENAME,
self.storage_client.bucket(self.moblab_bucket_name),
)
commands = remote_requests.MoblabRemoteRequests()
commands.load_requests_from_gcs(blob)
executed_and_pending_commands = remote_requests.MoblabRemoteRequests()
for request in commands.requests:
is_executed, is_expired = self.get_request_status(request)
if is_executed or not is_expired:
executed_and_pending_commands.add_request(request)
self.overwrite_command_list(executed_and_pending_commands)
def get_request_status(self, request):
if not self.executed:
self.executed = scheduler_common.get_executed_commands(
self.storage_client, self.moblab_bucket_name
)
current_sec_utc = (
datetime.utcnow() - datetime(1970, 1, 1)
).total_seconds()
is_executed = request.unique_id in self.executed
is_expired = request.expires_at_sec_utc < current_sec_utc
return (is_executed, is_expired)
def remove_all(self):
empty = remote_requests.MoblabRemoteRequests()
self.overwrite_command_list(empty)
def run(self):
self.display_all_command_status()
while True:
options = self.get_input_from_list(
[
"List remote commands",
"Add remote suite command",
"Remove duplicates",
"Remove expired",
"Remove all",
"Exit",
],
"Pick the number of a command",
)
if options[0] == "List remote commands":
self.display_all_command_status()
elif options[0] == "Add remote suite command":
self.interactive_queue_runsuite()
elif options[0] == "Remove duplicates":
self.remove_duplicates()
elif options[0] == "Remove expired":
self.remove_expired()
elif options[0] == "Remove all":
self.remove_all()
else:
break
def _parse_arguments(argv):
"""Creates the argument parser."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"-b",
"--bucket_name",
type=str,
default=None,
help="What partners bucket to create commands in.",
)
return parser.parse_args(argv)
def main(args):
cmd_arguments = _parse_arguments(args)
cli = MoblabRemoteSchedulerCLI(cmd_arguments.bucket_name)
cli.run()
if __name__ == "__main__":
main(sys.argv[1:])