| # -*- 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. |
| """Service that schedules commands on the moblab.""" |
| |
| import logging |
| import time |
| import json |
| |
| from moblab_common import moblabrpc_connector |
| |
| import remote_request |
| |
| _LOGGER = logging.getLogger(__name__) |
| _LOGGER.setLevel(logging.DEBUG) |
| |
| |
| class MoblabSuiteRunRequestWrapper(remote_request.MoblabRemoteRequest): |
| """Encapsulates all the information required to run a suite on moblab.""" |
| |
| def __init__( |
| self, |
| unique_id=None, |
| board=None, |
| build=None, |
| priority=None, |
| suite=None, |
| min_duts=0, |
| suite_args="", |
| model=None, |
| test_args="", |
| expires_at_sec_utc=None, |
| pubsub_message_id=None, |
| proto=None, |
| ): |
| """Class constructor. |
| |
| Construct the suite request either from the params or from |
| a proto that contains all the params. |
| |
| unique_id (string, optional): Defaults to None. The unique |
| identifier for this request, if None a new identifier will be |
| generated. |
| board (string, optional): Defaults to None. The board to run the |
| suite with. |
| build (string, optional): Defaults to None. The build number to |
| run e.g. R75-12055.0.0 |
| priority (int, optional): Defaults to None. The priority of the |
| request, lower number is higher priority. |
| suite (string, optional): Defaults to None. The name of the |
| suite to run e.g. cts_P |
| min_duts (int, optional): Minimun number of active DUT's attached |
| with the correct board/model before the suite will run. |
| suite_args (string, optional): Defaults to None. key=value pairs |
| delimited by newlines. |
| model (string, optional): Defaults to None. The model to run the |
| suite on. |
| test_args (string, optional): Defaults to None. key=value pairs |
| delimited by newlines. |
| expires_at_sec_utc (string, optional): Defaults to None. If set |
| a time in UTC at which this command will be ignored. |
| proto (object, optional): Defaults to None.Allows construction of |
| this object from the values in a the proto equivalent. |
| """ |
| |
| if proto: |
| unique_id = proto.base.unique_id |
| board = proto.base.board |
| build = proto.build |
| priority = proto.base.priority |
| suite = proto.suite |
| min_duts = proto.base.min_duts |
| suite_args = proto.suite_args |
| test_args = proto.test_args |
| expires_at_sec_utc = proto.base.expires_at_sec_utc |
| model = proto.base.model |
| pubsub_message_id = proto.base.pubsub_message_id |
| |
| super(MoblabSuiteRunRequestWrapper, self).__init__(unique_id) |
| |
| self.board = board |
| self.build = build |
| self.priority = priority |
| self.suite = suite |
| self.min_duts = min_duts |
| self.suite_args = suite_args |
| self.model = model |
| self.test_args = test_args |
| self.expires_at_sec_utc = expires_at_sec_utc |
| self.pubsub_message_id = pubsub_message_id |
| |
| if not self.model: |
| self.model = self.board |
| |
| def __str__(self): |
| """Return a formatted string of the information held in this object. |
| |
| Returns: |
| str: Formatted string representing this object. |
| """ |
| |
| return ( |
| "unique_id=%s, build=%s, board=%s, suite=%s, model=%s, " |
| "priority=%s, min_duts=%s" |
| ) % ( |
| self.unique_id, |
| self.build, |
| self.board, |
| self.suite, |
| self.model, |
| self.priority, |
| self.min_duts, |
| ) |
| |
| def copy_to_proto(self, proto): |
| """Copy the object data into a proto format. |
| |
| Args: |
| proto (object): Proto object to copy the data into. |
| """ |
| proto.base.unique_id = self.unique_id |
| proto.build = self.build |
| proto.base.board = self.board |
| proto.base.priority = self.priority |
| proto.suite = self.suite |
| proto.base.min_duts = self.min_duts |
| proto.base.expires_at_sec_utc = self.expires_at_sec_utc |
| if self.pubsub_message_id: |
| proto.base.pubsub_message_id = self.pubsub_message_id |
| if self.model: |
| proto.base.model = self.model |
| if self.suite_args: |
| proto.suite_args = json.dumps(self.suite_args) |
| if self.test_args: |
| proto.test_args = json.dumps(self.test_args) |
| |
| def can_be_executed(self, attached_boards): |
| """Check to see if a request can be executed at this time. |
| |
| Args: |
| attached_boards (dict): Mapping of board model number to the |
| number of available DUT's |
| |
| Returns: |
| [bool]: True if the task can run False if this Moblab is not |
| currently capable of running the request. |
| """ |
| |
| board_model = "%s.%s" % (self.board, self.model) |
| if board_model not in list(attached_boards.keys()): |
| logging.debug("No attached boards for %s", board_model) |
| return False |
| |
| if self.min_duts and self.min_duts > attached_boards[board_model]: |
| logging.info( |
| ("Not enough DUTs board: %s attached: %d" " required: %d"), |
| board_model, |
| attached_boards[board_model], |
| self.min_duts, |
| ) |
| return False |
| |
| # TODO(haddowk) move the check that the build can be staged to here. |
| return True |
| |
| def execute(self, devserver_connector, autotest_connector): |
| """Execute a run suite command on this moblab. |
| |
| Args: |
| devserver_connector (object): Instanciated API object to access the |
| devserver. |
| autotest_connector (object): Instanciated API object to access the |
| autotest server. |
| """ |
| _LOGGER.info("Running suite %s", self) |
| # if the request has expired then don't run the suite |
| if ( |
| self.expires_at_sec_utc |
| and int(time.time()) > self.expires_at_sec_utc |
| ): |
| return |
| |
| # TODO Change run_suite_request proto to have a milestone, |
| # build version and build_target depricate build and board. |
| milestone = self.build.split("-")[0] |
| # TODO fix this upstream so the requests do not have an R on the |
| # milestone. |
| milestone = milestone.strip("R") |
| build_version = self.build.split("-")[1] |
| # TODO plumb through suite_args and test_args. |
| reponse = moblabrpc_connector.MoblabRpcConnector.run_suite( |
| build_target=self.board, |
| model=self.model, |
| milestone=milestone, |
| build_version=build_version, |
| suite=self.suite, |
| ) |
| return json.loads(reponse.message)["error"] |