Home | History | Annotate | Download | only in command_processor
      1 #
      2 # Copyright (C) 2018 The Android Open Source Project
      3 #
      4 # Licensed under the Apache License, Version 2.0 (the 'License');
      5 # you may not use this file except in compliance with the License.
      6 # You may obtain a copy of the License at
      7 #
      8 #      http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 # Unless required by applicable law or agreed to in writing, software
     11 # distributed under the License is distributed on an 'AS IS' BASIS,
     12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 # See the License for the specific language governing permissions and
     14 # limitations under the License.
     15 #
     16 
     17 import httplib2
     18 import logging
     19 import socket
     20 import threading
     21 import time
     22 
     23 from googleapiclient import errors
     24 
     25 from host_controller import common
     26 from host_controller.command_processor import base_command_processor
     27 from host_controller.console_argument_parser import ConsoleArgumentError
     28 from host_controller.tradefed import remote_operation
     29 
     30 
     31 class CommandBuild(base_command_processor.BaseCommandProcessor):
     32     """Command processor for build command.
     33 
     34     Attributes:
     35         arg_parser: ConsoleArgumentParser object, argument parser.
     36         build_thread: dict containing threading.Thread instances(s) that
     37                       update build info regularly.
     38         console: cmd.Cmd console object.
     39         command: string, command name which this processor will handle.
     40         command_detail: string, detailed explanation for the command.
     41     """
     42 
     43     command = "build"
     44     command_detail = "Specifies branches and targets to monitor."
     45 
     46     def UpdateBuild(self, account_id, branch, targets, artifact_type, method,
     47                     userinfo_file, noauth_local_webserver):
     48         """Updates the build state.
     49 
     50         Args:
     51             account_id: string, Partner Android Build account_id to use.
     52             branch: string, branch to grab the artifact from.
     53             targets: string, a comma-separate list of build target product(s).
     54             artifact_type: string, artifact type (`device`, 'gsi' or `test').
     55             method: string,  method for getting build information.
     56             userinfo_file: string, the path of a file containing email and
     57                            password (if method == POST).
     58             noauth_local_webserver: boolean, True to not use a local websever.
     59         """
     60         builds = []
     61 
     62         self.console._build_provider["pab"].Authenticate(
     63             userinfo_file=userinfo_file,
     64             noauth_local_webserver=noauth_local_webserver)
     65         for target in targets.split(","):
     66             listed_builds = self.console._build_provider["pab"].GetBuildList(
     67                 account_id=account_id,
     68                 branch=branch,
     69                 target=target,
     70                 page_token="",
     71                 max_results=100,
     72                 method=method)
     73 
     74             for listed_build in listed_builds:
     75                 if method == "GET":
     76                     if "successful" in listed_build:
     77                         if listed_build["successful"]:
     78                             build = {}
     79                             build["manifest_branch"] = branch
     80                             build["build_id"] = listed_build["build_id"]
     81                             if "-" in target:
     82                                 build["build_target"], build[
     83                                     "build_type"] = target.split("-")
     84                             else:
     85                                 build["build_target"] = target
     86                                 build["build_type"] = ""
     87                             build["artifact_type"] = artifact_type
     88                             build["artifacts"] = []
     89                             builds.append(build)
     90                     else:
     91                         print("Error: listed_build %s" % listed_build)
     92                 else:  # POST
     93                     build = {}
     94                     build["manifest_branch"] = branch
     95                     build["build_id"] = listed_build[u"1"]
     96                     if "-" in target:
     97                         (build["build_target"],
     98                          build["build_type"]) = target.split("-")
     99                     else:
    100                         build["build_target"] = target
    101                         build["build_type"] = ""
    102                     build["artifact_type"] = artifact_type
    103                     build["artifacts"] = []
    104                     builds.append(build)
    105         self.console._vti_endpoint_client.UploadBuildInfo(builds)
    106 
    107     def UpdateBuildLoop(self, account_id, branch, target, artifact_type,
    108                         method, userinfo_file, noauth_local_webserver,
    109                         update_interval):
    110         """Regularly updates the build information.
    111 
    112         Args:
    113             account_id: string, Partner Android Build account_id to use.
    114             branch: string, branch to grab the artifact from.
    115             targets: string, a comma-separate list of build target product(s).
    116             artifact_type: string, artifcat type (`device`, 'gsi' or `test).
    117             method: string,  method for getting build information.
    118             userinfo_file: string, the path of a file containing email and
    119                            password (if method == POST).
    120             noauth_local_webserver: boolean, True to not use a local websever.
    121             update_interval: int, number of seconds before repeating
    122         """
    123         thread = threading.currentThread()
    124         while getattr(thread, 'keep_running', True):
    125             try:
    126                 self.UpdateBuild(account_id, branch, target, artifact_type,
    127                                  method, userinfo_file, noauth_local_webserver)
    128             except (socket.error, remote_operation.RemoteOperationException,
    129                     httplib2.HttpLib2Error, errors.HttpError) as e:
    130                 logging.exception(e)
    131             time.sleep(update_interval)
    132 
    133     # @Override
    134     def SetUp(self):
    135         """Initializes the parser for build command."""
    136         self.build_thread = {}
    137         self.arg_parser.add_argument(
    138             "--update",
    139             choices=("single", "start", "stop", "list"),
    140             default="start",
    141             help="Update build info")
    142         self.arg_parser.add_argument(
    143             "--id",
    144             default=None,
    145             help="session ID only required for 'stop' update command")
    146         self.arg_parser.add_argument(
    147             "--interval",
    148             type=int,
    149             default=30,
    150             help="Interval (seconds) to repeat build update.")
    151         self.arg_parser.add_argument(
    152             "--artifact-type",
    153             choices=("device", "gsi", "test"),
    154             default="device",
    155             help="The type of an artifact to update")
    156         self.arg_parser.add_argument(
    157             "--branch",
    158             required=True,
    159             help="Branch to grab the artifact from.")
    160         self.arg_parser.add_argument(
    161             "--target",
    162             required=True,
    163             help="a comma-separate list of build target product(s).")
    164         self.arg_parser.add_argument(
    165             "--account_id",
    166             default=common._DEFAULT_ACCOUNT_ID,
    167             help="Partner Android Build account_id to use.")
    168         self.arg_parser.add_argument(
    169             "--method",
    170             default="GET",
    171             choices=("GET", "POST"),
    172             help="Method for getting build information")
    173         self.arg_parser.add_argument(
    174             "--userinfo-file",
    175             help=
    176             "Location of file containing email and password, if using POST.")
    177         self.arg_parser.add_argument(
    178             "--noauth_local_webserver",
    179             default=False,
    180             type=bool,
    181             help="True to not use a local webserver for authentication.")
    182 
    183     # @Override
    184     def Run(self, arg_line):
    185         """Updates build info."""
    186         args = self.arg_parser.ParseLine(arg_line)
    187         if args.update == "single":
    188             self.UpdateBuild(args.account_id, args.branch, args.target,
    189                              args.artifact_type, args.method,
    190                              args.userinfo_file, args.noauth_local_webserver)
    191         elif args.update == "list":
    192             print("Running build update sessions:")
    193             for id in self.build_thread:
    194                 print("  ID %d", id)
    195         elif args.update == "start":
    196             if args.interval <= 0:
    197                 raise ConsoleArgumentError("update interval must be positive")
    198             # do not allow user to create new
    199             # thread if one is currently running
    200             if args.id is None:
    201                 if not self.build_thread:
    202                     args.id = 1
    203                 else:
    204                     args.id = max(self.build_thread) + 1
    205             else:
    206                 args.id = int(args.id)
    207             if args.id in self.build_thread and not hasattr(
    208                     self.build_thread[args.id], 'keep_running'):
    209                 print('build update (session ID: %s) already running. '
    210                       'run build --update stop first.' % args.id)
    211                 return
    212             self.build_thread[args.id] = threading.Thread(
    213                 target=self.UpdateBuildLoop,
    214                 args=(
    215                     args.account_id,
    216                     args.branch,
    217                     args.target,
    218                     args.artifact_type,
    219                     args.method,
    220                     args.userinfo_file,
    221                     args.noauth_local_webserver,
    222                     args.interval,
    223                 ))
    224             self.build_thread[args.id].daemon = True
    225             self.build_thread[args.id].start()
    226         elif args.update == "stop":
    227             if args.id is None:
    228                 print("--id must be set for stop")
    229             else:
    230                 self.build_thread[int(args.id)].keep_running = False
    231