blob: 5c2bedb33bee4faec701020a0a53a9173d4d775a [file] [log] [blame]
Zhizhou Yang5534af82020-01-15 16:25:04 -08001#!/usr/bin/env python3
Zhizhou Yangcdd9e342019-09-19 20:56:32 -07002# -*- coding: utf-8 -*-
Zhizhou Yang4713fd12019-09-24 10:32:00 -07003#
Mike Frysingerfdcd39d2022-09-13 14:19:58 -04004# Copyright 2019 The ChromiumOS Authors
Zhizhou Yangcdd9e342019-09-19 20:56:32 -07005# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
Caroline Ticea4486452015-12-08 13:43:23 -08008"""This module controls locking and unlocking of test machines."""
9
Caroline Ticea4486452015-12-08 13:43:23 -080010
cmticee5bc63b2015-05-27 16:59:37 -070011import argparse
Zhizhou Yang5322d4a2019-09-30 13:10:29 -070012import enum
cmticee5bc63b2015-05-27 16:59:37 -070013import getpass
14import os
15import sys
cmticee5bc63b2015-05-27 16:59:37 -070016
Zhizhou Yangcdd9e342019-09-19 20:56:32 -070017from cros_utils import command_executer
Caroline Ticea8af9a72016-07-20 12:52:59 -070018from cros_utils import logger
19from cros_utils import machines
Denis Nikitin444382a2022-05-16 12:30:45 -070020import file_lock_machine
cmticee5bc63b2015-05-27 16:59:37 -070021
Luis Lozanof2a3ef42015-12-15 13:49:30 -080022
Zhizhou Yangbf7ee872019-10-14 17:36:09 -070023class LockException(Exception):
George Burgess IV74bd3802022-09-02 16:59:27 -070024 """Base class for exceptions in this module."""
cmticee5bc63b2015-05-27 16:59:37 -070025
26
Zhizhou Yangbf7ee872019-10-14 17:36:09 -070027class MachineNotPingable(LockException):
George Burgess IV74bd3802022-09-02 16:59:27 -070028 """Raised when machine does not respond to ping."""
cmticee5bc63b2015-05-27 16:59:37 -070029
30
Zhizhou Yangbf7ee872019-10-14 17:36:09 -070031class LockingError(LockException):
George Burgess IV74bd3802022-09-02 16:59:27 -070032 """Raised when server fails to lock/unlock machine as requested."""
cmticee5bc63b2015-05-27 16:59:37 -070033
34
Zhizhou Yangbf7ee872019-10-14 17:36:09 -070035class DontOwnLock(LockException):
George Burgess IV74bd3802022-09-02 16:59:27 -070036 """Raised when user attmepts to unlock machine locked by someone else."""
37
38 # This should not be raised if the user specified '--force'
cmticee5bc63b2015-05-27 16:59:37 -070039
40
Zhizhou Yang5322d4a2019-09-30 13:10:29 -070041class MachineType(enum.Enum):
George Burgess IV74bd3802022-09-02 16:59:27 -070042 """Enum class to hold machine type."""
43
44 LOCAL = "local"
45 CROSFLEET = "crosfleet"
Zhizhou Yang5322d4a2019-09-30 13:10:29 -070046
47
Zhizhou Yangbf7ee872019-10-14 17:36:09 -070048class LockManager(object):
George Burgess IV74bd3802022-09-02 16:59:27 -070049 """Class for locking/unlocking machines vie three different modes.
cmticee5bc63b2015-05-27 16:59:37 -070050
George Burgess IV74bd3802022-09-02 16:59:27 -070051 This class contains methods for checking the locked status of machines,
52 and for changing the locked status. It handles HW lab machines and local
53 machines, using appropriate locking mechanisms for each.
cmticee5bc63b2015-05-27 16:59:37 -070054 """
cmticee5bc63b2015-05-27 16:59:37 -070055
George Burgess IV74bd3802022-09-02 16:59:27 -070056 CROSFLEET_PATH = "crosfleet"
cmticee5bc63b2015-05-27 16:59:37 -070057
George Burgess IV74bd3802022-09-02 16:59:27 -070058 # TODO(zhizhouy): lease time may needs to be dynamically adjusted. For now we
59 # set it long enough to cover the period to finish nightly rotation tests.
60 LEASE_MINS = 1439
Zhizhou Yangcdd9e342019-09-19 20:56:32 -070061
George Burgess IV74bd3802022-09-02 16:59:27 -070062 CROSFLEET_CREDENTIAL = (
63 "/usr/local/google/home/mobiletc-prebuild"
64 "/sheriff_utils/credentials/skylab"
65 "/chromeos-swarming-credential.json"
66 )
67 SWARMING = "~/cipd_binaries/swarming"
68 SUCCESS = 0
Caroline Tice6b161382016-09-15 15:03:46 -070069
George Burgess IV74bd3802022-09-02 16:59:27 -070070 def __init__(
71 self, remotes, force_option, chromeos_root, locks_dir="", log=None
72 ):
73 """Initializes an LockManager object.
Caroline Tice6b161382016-09-15 15:03:46 -070074
George Burgess IV74bd3802022-09-02 16:59:27 -070075 Args:
76 remotes: A list of machine names or ip addresses to be managed. Names
77 and ip addresses should be represented as strings. If the list is
78 empty, the lock manager will get all known machines.
79 force_option: A Boolean indicating whether or not to force an unlock of
80 a machine that was locked by someone else.
81 chromeos_root: The ChromeOS chroot to use for the autotest scripts.
82 locks_dir: A directory used for file locking local devices.
83 log: If not None, this is the logger object to be used for writing out
84 informational output messages. It is expected to be an instance of
85 Logger class from cros_utils/logger.py.
86 """
87 self.chromeos_root = chromeos_root
88 self.user = getpass.getuser()
89 self.logger = log or logger.GetLogger()
90 self.ce = command_executer.GetCommandExecuter(self.logger)
Zhizhou Yangcdd9e342019-09-19 20:56:32 -070091
George Burgess IV74bd3802022-09-02 16:59:27 -070092 sys.path.append(chromeos_root)
cmticee5bc63b2015-05-27 16:59:37 -070093
George Burgess IV74bd3802022-09-02 16:59:27 -070094 self.locks_dir = locks_dir
cmticee5bc63b2015-05-27 16:59:37 -070095
George Burgess IV74bd3802022-09-02 16:59:27 -070096 self.machines = list(set(remotes)) or []
97 self.toolchain_lab_machines = self.GetAllToolchainLabMachines()
cmticee5bc63b2015-05-27 16:59:37 -070098
George Burgess IV74bd3802022-09-02 16:59:27 -070099 if not self.machines:
100 self.machines = self.toolchain_lab_machines
101 self.force = force_option
cmticee5bc63b2015-05-27 16:59:37 -0700102
George Burgess IV74bd3802022-09-02 16:59:27 -0700103 self.local_machines = []
104 self.crosfleet_machines = []
cmticee5bc63b2015-05-27 16:59:37 -0700105
George Burgess IV74bd3802022-09-02 16:59:27 -0700106 def CheckMachine(self, machine, error_msg):
107 """Verifies that machine is responding to ping.
cmticee5bc63b2015-05-27 16:59:37 -0700108
George Burgess IV74bd3802022-09-02 16:59:27 -0700109 Args:
110 machine: String containing the name or ip address of machine to check.
111 error_msg: Message to print if ping fails.
Zhizhou Yang5322d4a2019-09-30 13:10:29 -0700112
George Burgess IV74bd3802022-09-02 16:59:27 -0700113 Raises:
114 MachineNotPingable: If machine is not responding to 'ping'
115 """
116 if not machines.MachineIsPingable(machine, logging_level="none"):
117 cros_machine = machine + ".cros"
118 if not machines.MachineIsPingable(
119 cros_machine, logging_level="none"
120 ):
121 raise MachineNotPingable(error_msg)
Zhizhou Yang5322d4a2019-09-30 13:10:29 -0700122
George Burgess IV74bd3802022-09-02 16:59:27 -0700123 def GetAllToolchainLabMachines(self):
124 """Gets a list of all the toolchain machines in the ChromeOS HW lab.
Zhizhou Yang5322d4a2019-09-30 13:10:29 -0700125
George Burgess IV74bd3802022-09-02 16:59:27 -0700126 Returns:
127 A list of names of the toolchain machines in the ChromeOS HW lab.
128 """
129 machines_file = os.path.join(
130 os.path.dirname(__file__), "crosperf", "default_remotes"
131 )
132 machine_list = []
133 with open(machines_file, "r") as input_file:
134 lines = input_file.readlines()
135 for line in lines:
136 _, remotes = line.split(":")
137 remotes = remotes.strip()
138 for r in remotes.split():
139 machine_list.append(r.strip())
140 return machine_list
Zhizhou Yang5322d4a2019-09-30 13:10:29 -0700141
George Burgess IV74bd3802022-09-02 16:59:27 -0700142 def GetMachineType(self, m):
143 """Get where the machine is located.
cmticee5bc63b2015-05-27 16:59:37 -0700144
George Burgess IV74bd3802022-09-02 16:59:27 -0700145 Args:
146 m: String containing the name or ip address of machine.
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700147
George Burgess IV74bd3802022-09-02 16:59:27 -0700148 Returns:
149 Value of the type in MachineType Enum.
150 """
151 if m in self.local_machines:
152 return MachineType.LOCAL
153 if m in self.crosfleet_machines:
154 return MachineType.CROSFLEET
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700155
George Burgess IV74bd3802022-09-02 16:59:27 -0700156 def PrintStatusHeader(self):
157 """Prints the status header lines for machines."""
158 print("\nMachine (Board)\t\t\t\t\tStatus")
159 print("---------------\t\t\t\t\t------")
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700160
George Burgess IV74bd3802022-09-02 16:59:27 -0700161 def PrintStatus(self, m, state, machine_type):
162 """Prints status for a single machine.
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700163
George Burgess IV74bd3802022-09-02 16:59:27 -0700164 Args:
165 m: String containing the name or ip address of machine.
166 state: A dictionary of the current state of the machine.
167 machine_type: MachineType to determine where the machine is located.
168 """
169 if state["locked"]:
170 print(
171 "%s (%s)\t\t%slocked by %s since %s"
172 % (
173 m,
174 state["board"],
175 "\t\t" if machine_type == MachineType.LOCAL else "",
176 state["locked_by"],
177 state["lock_time"],
178 )
179 )
180 else:
181 print(
182 "%s (%s)\t\t%sunlocked"
183 % (
184 m,
185 state["board"],
186 "\t\t" if machine_type == MachineType.LOCAL else "",
187 )
188 )
cmticee5bc63b2015-05-27 16:59:37 -0700189
George Burgess IV74bd3802022-09-02 16:59:27 -0700190 def AddMachineToLocal(self, machine):
191 """Adds a machine to local machine list.
cmticee5bc63b2015-05-27 16:59:37 -0700192
George Burgess IV74bd3802022-09-02 16:59:27 -0700193 Args:
194 machine: The machine to be added.
195 """
196 if machine not in self.local_machines:
197 self.local_machines.append(machine)
cmticee5bc63b2015-05-27 16:59:37 -0700198
George Burgess IV74bd3802022-09-02 16:59:27 -0700199 def AddMachineToCrosfleet(self, machine):
200 """Adds a machine to crosfleet machine list.
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700201
George Burgess IV74bd3802022-09-02 16:59:27 -0700202 Args:
203 machine: The machine to be added.
204 """
205 if machine not in self.crosfleet_machines:
206 self.crosfleet_machines.append(machine)
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700207
George Burgess IV74bd3802022-09-02 16:59:27 -0700208 def ListMachineStates(self, machine_states):
209 """Gets and prints the current status for a list of machines.
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700210
George Burgess IV74bd3802022-09-02 16:59:27 -0700211 Prints out the current status for all of the machines in the current
212 LockManager's list of machines (set when the object is initialized).
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700213
George Burgess IV74bd3802022-09-02 16:59:27 -0700214 Args:
215 machine_states: A dictionary of the current state of every machine in
216 the current LockManager's list of machines. Normally obtained by
217 calling LockManager::GetMachineStates.
218 """
219 self.PrintStatusHeader()
220 for m in machine_states:
221 machine_type = self.GetMachineType(m)
222 state = machine_states[m]
223 self.PrintStatus(m, state, machine_type)
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700224
George Burgess IV74bd3802022-09-02 16:59:27 -0700225 def UpdateLockInCrosfleet(self, should_lock_machine, machine):
226 """Ask crosfleet to lease/release a machine.
cmticee5bc63b2015-05-27 16:59:37 -0700227
George Burgess IV74bd3802022-09-02 16:59:27 -0700228 Args:
229 should_lock_machine: Boolean indicating whether to lock the machine (True)
230 or unlock the machine (False).
231 machine: The machine to update.
cmticee5bc63b2015-05-27 16:59:37 -0700232
George Burgess IV74bd3802022-09-02 16:59:27 -0700233 Returns:
234 True if requested action succeeded, else False.
235 """
236 try:
237 if should_lock_machine:
238 ret = self.LeaseCrosfleetMachine(machine)
239 else:
240 ret = self.ReleaseCrosfleetMachine(machine)
241 except Exception:
242 return False
243 return ret
cmticee5bc63b2015-05-27 16:59:37 -0700244
George Burgess IV74bd3802022-09-02 16:59:27 -0700245 def UpdateFileLock(self, should_lock_machine, machine):
246 """Use file lock for local machines,
cmticef3eb8032015-07-27 13:55:52 -0700247
George Burgess IV74bd3802022-09-02 16:59:27 -0700248 Args:
249 should_lock_machine: Boolean indicating whether to lock the machine (True)
250 or unlock the machine (False).
251 machine: The machine to update.
cmticef3eb8032015-07-27 13:55:52 -0700252
George Burgess IV74bd3802022-09-02 16:59:27 -0700253 Returns:
254 True if requested action succeeded, else False.
255 """
256 try:
257 if should_lock_machine:
258 ret = file_lock_machine.Machine(machine, self.locks_dir).Lock(
259 True, sys.argv[0]
260 )
261 else:
262 ret = file_lock_machine.Machine(machine, self.locks_dir).Unlock(
263 True
264 )
265 except Exception:
266 return False
267 return ret
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700268
George Burgess IV74bd3802022-09-02 16:59:27 -0700269 def UpdateMachines(self, lock_machines):
270 """Sets the locked state of the machines to the requested value.
cmticef3eb8032015-07-27 13:55:52 -0700271
George Burgess IV74bd3802022-09-02 16:59:27 -0700272 The machines updated are the ones in self.machines (specified when the
273 class object was intialized).
cmticef3eb8032015-07-27 13:55:52 -0700274
George Burgess IV74bd3802022-09-02 16:59:27 -0700275 Args:
276 lock_machines: Boolean indicating whether to lock the machines (True) or
277 unlock the machines (False).
cmticef3eb8032015-07-27 13:55:52 -0700278
George Burgess IV74bd3802022-09-02 16:59:27 -0700279 Returns:
280 A list of the machines whose state was successfully updated.
281 """
282 updated_machines = []
283 action = "Locking" if lock_machines else "Unlocking"
284 for m in self.machines:
285 # TODO(zhizhouy): Handling exceptions with more details when locking
286 # doesn't succeed.
287 machine_type = self.GetMachineType(m)
288 if machine_type == MachineType.CROSFLEET:
289 ret = self.UpdateLockInCrosfleet(lock_machines, m)
290 elif machine_type == MachineType.LOCAL:
291 ret = self.UpdateFileLock(lock_machines, m)
cmticee5bc63b2015-05-27 16:59:37 -0700292
George Burgess IV74bd3802022-09-02 16:59:27 -0700293 if ret:
294 self.logger.LogOutput(
295 "%s %s machine succeeded: %s."
296 % (action, machine_type.value, m)
297 )
298 updated_machines.append(m)
299 else:
300 self.logger.LogOutput(
301 "%s %s machine failed: %s."
302 % (action, machine_type.value, m)
303 )
cmticee5bc63b2015-05-27 16:59:37 -0700304
George Burgess IV74bd3802022-09-02 16:59:27 -0700305 self.machines = updated_machines
306 return updated_machines
cmticee5bc63b2015-05-27 16:59:37 -0700307
George Burgess IV74bd3802022-09-02 16:59:27 -0700308 def _InternalRemoveMachine(self, machine):
309 """Remove machine from internal list of machines.
cmticee5bc63b2015-05-27 16:59:37 -0700310
George Burgess IV74bd3802022-09-02 16:59:27 -0700311 Args:
312 machine: Name of machine to be removed from internal list.
313 """
314 # Check to see if machine is lab machine and if so, make sure it has
315 # ".cros" on the end.
316 cros_machine = machine
317 if machine.find("rack") > 0 and machine.find("row") > 0:
318 if machine.find(".cros") == -1:
319 cros_machine = cros_machine + ".cros"
cmticee5bc63b2015-05-27 16:59:37 -0700320
George Burgess IV74bd3802022-09-02 16:59:27 -0700321 self.machines = [
322 m for m in self.machines if m not in (cros_machine, machine)
323 ]
cmticee5bc63b2015-05-27 16:59:37 -0700324
George Burgess IV74bd3802022-09-02 16:59:27 -0700325 def CheckMachineLocks(self, machine_states, cmd):
326 """Check that every machine in requested list is in the proper state.
cmticee5bc63b2015-05-27 16:59:37 -0700327
George Burgess IV74bd3802022-09-02 16:59:27 -0700328 If the cmd is 'unlock' verify that every machine is locked by requestor.
329 If the cmd is 'lock' verify that every machine is currently unlocked.
cmticee5bc63b2015-05-27 16:59:37 -0700330
George Burgess IV74bd3802022-09-02 16:59:27 -0700331 Args:
332 machine_states: A dictionary of the current state of every machine in
333 the current LockManager's list of machines. Normally obtained by
334 calling LockManager::GetMachineStates.
335 cmd: The user-requested action for the machines: 'lock' or 'unlock'.
cmticee5bc63b2015-05-27 16:59:37 -0700336
George Burgess IV74bd3802022-09-02 16:59:27 -0700337 Raises:
338 DontOwnLock: The lock on a requested machine is owned by someone else.
339 """
340 for k, state in machine_states.items():
341 if cmd == "unlock":
342 if not state["locked"]:
343 self.logger.LogWarning(
344 "Attempt to unlock already unlocked machine "
345 "(%s)." % k
346 )
347 self._InternalRemoveMachine(k)
Zhizhou Yang4713fd12019-09-24 10:32:00 -0700348
George Burgess IV74bd3802022-09-02 16:59:27 -0700349 # TODO(zhizhouy): Crosfleet doesn't support host info such as locked_by.
350 # Need to update this when crosfleet supports it.
351 if (
352 state["locked"]
353 and state["locked_by"]
354 and state["locked_by"] != self.user
355 ):
356 raise DontOwnLock(
357 "Attempt to unlock machine (%s) locked by someone "
358 "else (%s)." % (k, state["locked_by"])
359 )
360 elif cmd == "lock":
361 if state["locked"]:
362 self.logger.LogWarning(
363 "Attempt to lock already locked machine (%s)" % k
364 )
365 self._InternalRemoveMachine(k)
Zhizhou Yang5a53a332019-10-07 13:27:37 -0700366
George Burgess IV74bd3802022-09-02 16:59:27 -0700367 def GetMachineStates(self, cmd=""):
368 """Gets the current state of all the requested machines.
cmticee5bc63b2015-05-27 16:59:37 -0700369
George Burgess IV74bd3802022-09-02 16:59:27 -0700370 Gets the current state of all the requested machines. Stores the data in a
371 dictionary keyed by machine name.
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700372
George Burgess IV74bd3802022-09-02 16:59:27 -0700373 Args:
374 cmd: The command for which we are getting the machine states. This is
375 important because if one of the requested machines is missing we raise
376 an exception, unless the requested command is 'add'.
Ryan Beltran20d36f92022-03-26 00:13:03 +0000377
George Burgess IV74bd3802022-09-02 16:59:27 -0700378 Returns:
379 A dictionary of machine states for all the machines in the LockManager
380 object.
381 """
382 machine_list = {}
383 for m in self.machines:
384 # For local or crosfleet machines, we simply set {'locked': status} for
385 # them
386 # TODO(zhizhouy): This is a quick fix since crosfleet cannot return host
387 # info as afe does. We need to get more info such as locked_by when
388 # crosfleet supports that.
389 values = {
390 "locked": 0 if cmd == "lock" else 1,
391 "board": "??",
392 "locked_by": "",
393 "lock_time": "",
394 }
395 machine_list[m] = values
George Burgess IV900d6e72020-05-02 10:28:55 -0700396
George Burgess IV74bd3802022-09-02 16:59:27 -0700397 self.ListMachineStates(machine_list)
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700398
George Burgess IV74bd3802022-09-02 16:59:27 -0700399 return machine_list
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700400
George Burgess IV74bd3802022-09-02 16:59:27 -0700401 def CheckMachineInCrosfleet(self, machine):
402 """Run command to check if machine is in Crosfleet or not.
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700403
George Burgess IV74bd3802022-09-02 16:59:27 -0700404 Returns:
405 True if machine in crosfleet, else False
406 """
407 credential = ""
408 if os.path.exists(self.CROSFLEET_CREDENTIAL):
409 credential = "--service-account-json %s" % self.CROSFLEET_CREDENTIAL
410 server = "--server https://chromeos-swarming.appspot.com"
411 dimensions = "--dimension dut_name=%s" % machine.rstrip(".cros")
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700412
George Burgess IV74bd3802022-09-02 16:59:27 -0700413 cmd = f"{self.SWARMING} bots {server} {credential} {dimensions}"
414 exit_code, stdout, stderr = self.ce.RunCommandWOutput(cmd)
415 if exit_code:
416 raise ValueError(
417 "Querying bots failed (2); stdout: %r; stderr: %r"
418 % (stdout, stderr)
419 )
Ryan Beltran20d36f92022-03-26 00:13:03 +0000420
George Burgess IV74bd3802022-09-02 16:59:27 -0700421 # The command will return a json output as stdout. If machine not in
422 # crosfleet, stdout will look like this:
423 # {
424 # "death_timeout": "600",
425 # "now": "TIMESTAMP"
426 # }
427 # Otherwise there will be a tuple starting with 'items', we simply detect
428 # this keyword for result.
429 return stdout != "[]"
430
431 def LeaseCrosfleetMachine(self, machine):
432 """Run command to lease dut from crosfleet.
433
434 Returns:
435 True if succeeded, False if failed.
436 """
437 credential = ""
438 if os.path.exists(self.CROSFLEET_CREDENTIAL):
439 credential = "-service-account-json %s" % self.CROSFLEET_CREDENTIAL
440 cmd = ("%s dut lease -minutes %s %s %s %s") % (
441 self.CROSFLEET_PATH,
442 self.LEASE_MINS,
443 credential,
444 "-host",
445 machine.rstrip(".cros"),
446 )
447 # Wait 8 minutes for server to start the lease task, if not started,
448 # we will treat it as unavailable.
449 check_interval_time = 480
450 retval = self.ce.RunCommand(cmd, command_timeout=check_interval_time)
451 return retval == self.SUCCESS
452
453 def ReleaseCrosfleetMachine(self, machine):
454 """Run command to release dut from crosfleet.
455
456 Returns:
457 True if succeeded, False if failed.
458 """
459 credential = ""
460 if os.path.exists(self.CROSFLEET_CREDENTIAL):
461 credential = "-service-account-json %s" % self.CROSFLEET_CREDENTIAL
462
463 cmd = ("%s dut abandon %s %s") % (
464 self.CROSFLEET_PATH,
465 credential,
466 machine.rstrip(".cros"),
467 )
468 retval = self.ce.RunCommand(cmd)
469 return retval == self.SUCCESS
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700470
cmticee5bc63b2015-05-27 16:59:37 -0700471
472def Main(argv):
George Burgess IV74bd3802022-09-02 16:59:27 -0700473 """Parse the options, initialize lock manager and dispatch proper method.
cmticee5bc63b2015-05-27 16:59:37 -0700474
George Burgess IV74bd3802022-09-02 16:59:27 -0700475 Args:
476 argv: The options with which this script was invoked.
cmticee5bc63b2015-05-27 16:59:37 -0700477
George Burgess IV74bd3802022-09-02 16:59:27 -0700478 Returns:
479 0 unless an exception is raised.
480 """
481 parser = argparse.ArgumentParser()
cmticee5bc63b2015-05-27 16:59:37 -0700482
George Burgess IV74bd3802022-09-02 16:59:27 -0700483 parser.add_argument(
484 "--list",
485 dest="cmd",
486 action="store_const",
487 const="status",
488 help="List current status of all known machines.",
489 )
490 parser.add_argument(
491 "--lock",
492 dest="cmd",
493 action="store_const",
494 const="lock",
495 help="Lock given machine(s).",
496 )
497 parser.add_argument(
498 "--unlock",
499 dest="cmd",
500 action="store_const",
501 const="unlock",
502 help="Unlock given machine(s).",
503 )
504 parser.add_argument(
505 "--status",
506 dest="cmd",
507 action="store_const",
508 const="status",
509 help="List current status of given machine(s).",
510 )
511 parser.add_argument(
512 "--remote", dest="remote", help="machines on which to operate"
513 )
514 parser.add_argument(
515 "--chromeos_root",
516 dest="chromeos_root",
517 required=True,
518 help="ChromeOS root to use for autotest scripts.",
519 )
520 parser.add_argument(
521 "--force",
522 dest="force",
523 action="store_true",
524 default=False,
525 help="Force lock/unlock of machines, even if not"
526 " current lock owner.",
527 )
cmticee5bc63b2015-05-27 16:59:37 -0700528
George Burgess IV74bd3802022-09-02 16:59:27 -0700529 options = parser.parse_args(argv)
cmticee5bc63b2015-05-27 16:59:37 -0700530
George Burgess IV74bd3802022-09-02 16:59:27 -0700531 if not options.remote and options.cmd != "status":
532 parser.error("No machines specified for operation.")
cmticee5bc63b2015-05-27 16:59:37 -0700533
George Burgess IV74bd3802022-09-02 16:59:27 -0700534 if not os.path.isdir(options.chromeos_root):
535 parser.error("Cannot find chromeos_root: %s." % options.chromeos_root)
cmticee5bc63b2015-05-27 16:59:37 -0700536
George Burgess IV74bd3802022-09-02 16:59:27 -0700537 if not options.cmd:
538 parser.error(
539 "No operation selected (--list, --status, --lock, --unlock,"
540 " --add_machine, --remove_machine)."
541 )
cmticee5bc63b2015-05-27 16:59:37 -0700542
George Burgess IV74bd3802022-09-02 16:59:27 -0700543 machine_list = []
544 if options.remote:
545 machine_list = options.remote.split()
cmticee5bc63b2015-05-27 16:59:37 -0700546
George Burgess IV74bd3802022-09-02 16:59:27 -0700547 lock_manager = LockManager(
548 machine_list, options.force, options.chromeos_root
549 )
cmticee5bc63b2015-05-27 16:59:37 -0700550
George Burgess IV74bd3802022-09-02 16:59:27 -0700551 machine_states = lock_manager.GetMachineStates(cmd=options.cmd)
552 cmd = options.cmd
cmticee5bc63b2015-05-27 16:59:37 -0700553
George Burgess IV74bd3802022-09-02 16:59:27 -0700554 if cmd == "status":
555 lock_manager.ListMachineStates(machine_states)
cmticee5bc63b2015-05-27 16:59:37 -0700556
George Burgess IV74bd3802022-09-02 16:59:27 -0700557 elif cmd == "lock":
558 if not lock_manager.force:
559 lock_manager.CheckMachineLocks(machine_states, cmd)
560 lock_manager.UpdateMachines(True)
cmticee5bc63b2015-05-27 16:59:37 -0700561
George Burgess IV74bd3802022-09-02 16:59:27 -0700562 elif cmd == "unlock":
563 if not lock_manager.force:
564 lock_manager.CheckMachineLocks(machine_states, cmd)
565 lock_manager.UpdateMachines(False)
cmticee5bc63b2015-05-27 16:59:37 -0700566
George Burgess IV74bd3802022-09-02 16:59:27 -0700567 return 0
cmticee5bc63b2015-05-27 16:59:37 -0700568
569
George Burgess IV74bd3802022-09-02 16:59:27 -0700570if __name__ == "__main__":
571 sys.exit(Main(sys.argv[1:]))