• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
17import httplib2
18import logging
19import socket
20import threading
21import time
22
23from googleapiclient import errors
24
25from host_controller.command_processor import base_command_processor
26from host_controller.command_processor import command_config
27from host_controller.console_argument_parser import ConsoleArgumentError
28from host_controller.tradefed import remote_operation
29
30
31class CommandConfigLocal(command_config.CommandConfig):
32    """Command processor for config-local command.
33
34    Attributes:
35        arg_parser: ConsoleArgumentParser object, argument parser.
36        console: cmd.Cmd console object.
37        command: string, command name which this processor will handle.
38        command_detail: string, detailed explanation for the command.
39        schedule_thread: dict containing threading.Thread instances(s) that
40                         update schedule info regularly.
41    """
42
43    command = "config_local"
44    command_detail = "Uploads configs from local path."
45
46    # @Override
47    def UpdateConfig(self, path, update_build, clear_schedule, clear_labinfo):
48        """Updates the global configuration data.
49
50        Args:
51            path: string, a path where config files are stored.
52            update_build: boolean, indicating whether to upload build info.
53        """
54        if path:
55            self.UploadConfig(
56                path=path,
57                update_build=update_build,
58                clear_schedule=clear_schedule,
59                clear_labinfo=clear_labinfo)
60
61    # @Override
62    def UpdateConfigLoop(self, path, update_build, update_interval,
63                         clear_schedule, clear_labinfo):
64        """Regularly updates the global configuration.
65
66        Args:
67            path: string, a path where config files are stored.
68            update_build: boolean, indicating whether to upload build info.
69            update_interval: int, number of seconds before repeating
70        """
71        thread = threading.currentThread()
72        while getattr(thread, 'keep_running', True):
73            try:
74                self.UpdateConfig(path, update_build, clear_schedule,
75                                  clear_labinfo)
76            except (socket.error, remote_operation.RemoteOperationException,
77                    httplib2.HttpLib2Error, errors.HttpError) as e:
78                logging.exception(e)
79            time.sleep(update_interval)
80
81    # @Override
82    def SetUp(self):
83        """Initializes the parser for config command."""
84        self.schedule_thread = {}
85        self.arg_parser.add_argument(
86            "--update",
87            choices=("single", "start", "stop", "list"),
88            default="single",
89            help="Update build info")
90        self.arg_parser.add_argument(
91            "--id",
92            default=None,
93            help="session ID only required for 'stop' update command")
94        self.arg_parser.add_argument(
95            "--interval",
96            type=int,
97            default=60,
98            help="Interval (seconds) to repeat build update.")
99        self.arg_parser.add_argument(
100            "--path",
101            required=True,
102            help="A path where config files are stored.")
103        self.arg_parser.add_argument(
104            '--update_build',
105            dest='update_build',
106            action='store_true',
107            help='A boolean value indicating whether to upload build info.')
108        self.arg_parser.add_argument(
109            "--clear_schedule",
110            default=False,
111            help="True to clear all schedule data on the scheduler cloud")
112        self.arg_parser.add_argument(
113            "--clear_labinfo",
114            default=False,
115            help="True to clear all lab info data on the scheduler cloud")
116
117    # @Override
118    def Run(self, arg_line):
119        """Updates global config."""
120        args = self.arg_parser.ParseLine(arg_line)
121        if args.update == "single":
122            self.UpdateConfig(args.path, args.update_build,
123                              args.clear_schedule, args.clear_labinfo)
124        elif args.update == "list":
125            logging.info("Running config update sessions:")
126            for id in self.schedule_thread:
127                logging.info("  ID %d", id)
128        elif args.update == "start":
129            if args.interval <= 0:
130                raise ConsoleArgumentError("update interval must be positive")
131            # do not allow user to create new
132            # thread if one is currently running
133            if args.id is None:
134                if not self.schedule_thread:
135                    args.id = 1
136                else:
137                    args.id = max(self.schedule_thread) + 1
138            else:
139                args.id = int(args.id)
140            if args.id in self.schedule_thread and not hasattr(
141                    self.schedule_thread[args.id], 'keep_running'):
142                logging.warning(
143                    'config update already running. '
144                    'run config-local --update=stop --id=%s first.', args.id)
145                return
146            self.schedule_thread[args.id] = threading.Thread(
147                target=self.UpdateConfigLoop,
148                args=(
149                    args.path,
150                    args.update_build,
151                    args.interval,
152                    args.clear_schedule,
153                    args.clear_labinfo,
154                ))
155            self.schedule_thread[args.id].daemon = True
156            self.schedule_thread[args.id].start()
157        elif args.update == "stop":
158            if args.id is None:
159                logging.error("--id must be set for stop")
160            else:
161                self.schedule_thread[int(args.id)].keep_running = False
162
163    def Help(self):
164        base_command_processor.BaseCommandProcessor.Help(self)
165        logging.info("Sample: config-local --path=/usr/local/home/config/prod "
166                     "--update=single --update_build")
167