blob: 030d7d457404b43d3f002ec2ff04db5edf3ebdc7 [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#
George Burgess IV2124be52022-04-21 10:27:37 -07004# Copyright 2019 The ChromiumOS Authors. All rights reserved.
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
10from __future__ import print_function
11
cmticee5bc63b2015-05-27 16:59:37 -070012import argparse
Zhizhou Yang5322d4a2019-09-30 13:10:29 -070013import enum
cmticee5bc63b2015-05-27 16:59:37 -070014import getpass
15import os
16import sys
cmticee5bc63b2015-05-27 16:59:37 -070017
Zhizhou Yangcdd9e342019-09-19 20:56:32 -070018from cros_utils import command_executer
Caroline Ticea8af9a72016-07-20 12:52:59 -070019from cros_utils import logger
20from cros_utils import machines
Denis Nikitin444382a2022-05-16 12:30:45 -070021import file_lock_machine
cmticee5bc63b2015-05-27 16:59:37 -070022
Luis Lozanof2a3ef42015-12-15 13:49:30 -080023
Zhizhou Yangbf7ee872019-10-14 17:36:09 -070024class LockException(Exception):
George Burgess IV74bd3802022-09-02 16:59:27 -070025 """Base class for exceptions in this module."""
cmticee5bc63b2015-05-27 16:59:37 -070026
27
Zhizhou Yangbf7ee872019-10-14 17:36:09 -070028class MachineNotPingable(LockException):
George Burgess IV74bd3802022-09-02 16:59:27 -070029 """Raised when machine does not respond to ping."""
cmticee5bc63b2015-05-27 16:59:37 -070030
31
Zhizhou Yangbf7ee872019-10-14 17:36:09 -070032class LockingError(LockException):
George Burgess IV74bd3802022-09-02 16:59:27 -070033 """Raised when server fails to lock/unlock machine as requested."""
cmticee5bc63b2015-05-27 16:59:37 -070034
35
Zhizhou Yangbf7ee872019-10-14 17:36:09 -070036class DontOwnLock(LockException):
George Burgess IV74bd3802022-09-02 16:59:27 -070037 """Raised when user attmepts to unlock machine locked by someone else."""
38
39 # This should not be raised if the user specified '--force'
cmticee5bc63b2015-05-27 16:59:37 -070040
41
Zhizhou Yang5322d4a2019-09-30 13:10:29 -070042class MachineType(enum.Enum):
George Burgess IV74bd3802022-09-02 16:59:27 -070043 """Enum class to hold machine type."""
44
45 LOCAL = "local"
46 CROSFLEET = "crosfleet"
Zhizhou Yang5322d4a2019-09-30 13:10:29 -070047
48
Zhizhou Yangbf7ee872019-10-14 17:36:09 -070049class LockManager(object):
George Burgess IV74bd3802022-09-02 16:59:27 -070050 """Class for locking/unlocking machines vie three different modes.
cmticee5bc63b2015-05-27 16:59:37 -070051
George Burgess IV74bd3802022-09-02 16:59:27 -070052 This class contains methods for checking the locked status of machines,
53 and for changing the locked status. It handles HW lab machines and local
54 machines, using appropriate locking mechanisms for each.
cmticee5bc63b2015-05-27 16:59:37 -070055 """
cmticee5bc63b2015-05-27 16:59:37 -070056
George Burgess IV74bd3802022-09-02 16:59:27 -070057 CROSFLEET_PATH = "crosfleet"
cmticee5bc63b2015-05-27 16:59:37 -070058
George Burgess IV74bd3802022-09-02 16:59:27 -070059 # TODO(zhizhouy): lease time may needs to be dynamically adjusted. For now we
60 # set it long enough to cover the period to finish nightly rotation tests.
61 LEASE_MINS = 1439
Zhizhou Yangcdd9e342019-09-19 20:56:32 -070062
George Burgess IV74bd3802022-09-02 16:59:27 -070063 CROSFLEET_CREDENTIAL = (
64 "/usr/local/google/home/mobiletc-prebuild"
65 "/sheriff_utils/credentials/skylab"
66 "/chromeos-swarming-credential.json"
67 )
68 SWARMING = "~/cipd_binaries/swarming"
69 SUCCESS = 0
Caroline Tice6b161382016-09-15 15:03:46 -070070
George Burgess IV74bd3802022-09-02 16:59:27 -070071 def __init__(
72 self, remotes, force_option, chromeos_root, locks_dir="", log=None
73 ):
74 """Initializes an LockManager object.
Caroline Tice6b161382016-09-15 15:03:46 -070075
George Burgess IV74bd3802022-09-02 16:59:27 -070076 Args:
77 remotes: A list of machine names or ip addresses to be managed. Names
78 and ip addresses should be represented as strings. If the list is
79 empty, the lock manager will get all known machines.
80 force_option: A Boolean indicating whether or not to force an unlock of
81 a machine that was locked by someone else.
82 chromeos_root: The ChromeOS chroot to use for the autotest scripts.
83 locks_dir: A directory used for file locking local devices.
84 log: If not None, this is the logger object to be used for writing out
85 informational output messages. It is expected to be an instance of
86 Logger class from cros_utils/logger.py.
87 """
88 self.chromeos_root = chromeos_root
89 self.user = getpass.getuser()
90 self.logger = log or logger.GetLogger()
91 self.ce = command_executer.GetCommandExecuter(self.logger)
Zhizhou Yangcdd9e342019-09-19 20:56:32 -070092
George Burgess IV74bd3802022-09-02 16:59:27 -070093 sys.path.append(chromeos_root)
cmticee5bc63b2015-05-27 16:59:37 -070094
George Burgess IV74bd3802022-09-02 16:59:27 -070095 self.locks_dir = locks_dir
cmticee5bc63b2015-05-27 16:59:37 -070096
George Burgess IV74bd3802022-09-02 16:59:27 -070097 self.machines = list(set(remotes)) or []
98 self.toolchain_lab_machines = self.GetAllToolchainLabMachines()
cmticee5bc63b2015-05-27 16:59:37 -070099
George Burgess IV74bd3802022-09-02 16:59:27 -0700100 if not self.machines:
101 self.machines = self.toolchain_lab_machines
102 self.force = force_option
cmticee5bc63b2015-05-27 16:59:37 -0700103
George Burgess IV74bd3802022-09-02 16:59:27 -0700104 self.local_machines = []
105 self.crosfleet_machines = []
cmticee5bc63b2015-05-27 16:59:37 -0700106
George Burgess IV74bd3802022-09-02 16:59:27 -0700107 def CheckMachine(self, machine, error_msg):
108 """Verifies that machine is responding to ping.
cmticee5bc63b2015-05-27 16:59:37 -0700109
George Burgess IV74bd3802022-09-02 16:59:27 -0700110 Args:
111 machine: String containing the name or ip address of machine to check.
112 error_msg: Message to print if ping fails.
Zhizhou Yang5322d4a2019-09-30 13:10:29 -0700113
George Burgess IV74bd3802022-09-02 16:59:27 -0700114 Raises:
115 MachineNotPingable: If machine is not responding to 'ping'
116 """
117 if not machines.MachineIsPingable(machine, logging_level="none"):
118 cros_machine = machine + ".cros"
119 if not machines.MachineIsPingable(
120 cros_machine, logging_level="none"
121 ):
122 raise MachineNotPingable(error_msg)
Zhizhou Yang5322d4a2019-09-30 13:10:29 -0700123
George Burgess IV74bd3802022-09-02 16:59:27 -0700124 def GetAllToolchainLabMachines(self):
125 """Gets a list of all the toolchain machines in the ChromeOS HW lab.
Zhizhou Yang5322d4a2019-09-30 13:10:29 -0700126
George Burgess IV74bd3802022-09-02 16:59:27 -0700127 Returns:
128 A list of names of the toolchain machines in the ChromeOS HW lab.
129 """
130 machines_file = os.path.join(
131 os.path.dirname(__file__), "crosperf", "default_remotes"
132 )
133 machine_list = []
134 with open(machines_file, "r") as input_file:
135 lines = input_file.readlines()
136 for line in lines:
137 _, remotes = line.split(":")
138 remotes = remotes.strip()
139 for r in remotes.split():
140 machine_list.append(r.strip())
141 return machine_list
Zhizhou Yang5322d4a2019-09-30 13:10:29 -0700142
George Burgess IV74bd3802022-09-02 16:59:27 -0700143 def GetMachineType(self, m):
144 """Get where the machine is located.
cmticee5bc63b2015-05-27 16:59:37 -0700145
George Burgess IV74bd3802022-09-02 16:59:27 -0700146 Args:
147 m: String containing the name or ip address of machine.
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700148
George Burgess IV74bd3802022-09-02 16:59:27 -0700149 Returns:
150 Value of the type in MachineType Enum.
151 """
152 if m in self.local_machines:
153 return MachineType.LOCAL
154 if m in self.crosfleet_machines:
155 return MachineType.CROSFLEET
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700156
George Burgess IV74bd3802022-09-02 16:59:27 -0700157 def PrintStatusHeader(self):
158 """Prints the status header lines for machines."""
159 print("\nMachine (Board)\t\t\t\t\tStatus")
160 print("---------------\t\t\t\t\t------")
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700161
George Burgess IV74bd3802022-09-02 16:59:27 -0700162 def PrintStatus(self, m, state, machine_type):
163 """Prints status for a single machine.
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700164
George Burgess IV74bd3802022-09-02 16:59:27 -0700165 Args:
166 m: String containing the name or ip address of machine.
167 state: A dictionary of the current state of the machine.
168 machine_type: MachineType to determine where the machine is located.
169 """
170 if state["locked"]:
171 print(
172 "%s (%s)\t\t%slocked by %s since %s"
173 % (
174 m,
175 state["board"],
176 "\t\t" if machine_type == MachineType.LOCAL else "",
177 state["locked_by"],
178 state["lock_time"],
179 )
180 )
181 else:
182 print(
183 "%s (%s)\t\t%sunlocked"
184 % (
185 m,
186 state["board"],
187 "\t\t" if machine_type == MachineType.LOCAL else "",
188 )
189 )
cmticee5bc63b2015-05-27 16:59:37 -0700190
George Burgess IV74bd3802022-09-02 16:59:27 -0700191 def AddMachineToLocal(self, machine):
192 """Adds a machine to local machine list.
cmticee5bc63b2015-05-27 16:59:37 -0700193
George Burgess IV74bd3802022-09-02 16:59:27 -0700194 Args:
195 machine: The machine to be added.
196 """
197 if machine not in self.local_machines:
198 self.local_machines.append(machine)
cmticee5bc63b2015-05-27 16:59:37 -0700199
George Burgess IV74bd3802022-09-02 16:59:27 -0700200 def AddMachineToCrosfleet(self, machine):
201 """Adds a machine to crosfleet machine list.
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700202
George Burgess IV74bd3802022-09-02 16:59:27 -0700203 Args:
204 machine: The machine to be added.
205 """
206 if machine not in self.crosfleet_machines:
207 self.crosfleet_machines.append(machine)
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700208
George Burgess IV74bd3802022-09-02 16:59:27 -0700209 def ListMachineStates(self, machine_states):
210 """Gets and prints the current status for a list of machines.
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700211
George Burgess IV74bd3802022-09-02 16:59:27 -0700212 Prints out the current status for all of the machines in the current
213 LockManager's list of machines (set when the object is initialized).
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700214
George Burgess IV74bd3802022-09-02 16:59:27 -0700215 Args:
216 machine_states: A dictionary of the current state of every machine in
217 the current LockManager's list of machines. Normally obtained by
218 calling LockManager::GetMachineStates.
219 """
220 self.PrintStatusHeader()
221 for m in machine_states:
222 machine_type = self.GetMachineType(m)
223 state = machine_states[m]
224 self.PrintStatus(m, state, machine_type)
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700225
George Burgess IV74bd3802022-09-02 16:59:27 -0700226 def UpdateLockInCrosfleet(self, should_lock_machine, machine):
227 """Ask crosfleet to lease/release a machine.
cmticee5bc63b2015-05-27 16:59:37 -0700228
George Burgess IV74bd3802022-09-02 16:59:27 -0700229 Args:
230 should_lock_machine: Boolean indicating whether to lock the machine (True)
231 or unlock the machine (False).
232 machine: The machine to update.
cmticee5bc63b2015-05-27 16:59:37 -0700233
George Burgess IV74bd3802022-09-02 16:59:27 -0700234 Returns:
235 True if requested action succeeded, else False.
236 """
237 try:
238 if should_lock_machine:
239 ret = self.LeaseCrosfleetMachine(machine)
240 else:
241 ret = self.ReleaseCrosfleetMachine(machine)
242 except Exception:
243 return False
244 return ret
cmticee5bc63b2015-05-27 16:59:37 -0700245
George Burgess IV74bd3802022-09-02 16:59:27 -0700246 def UpdateFileLock(self, should_lock_machine, machine):
247 """Use file lock for local machines,
cmticef3eb8032015-07-27 13:55:52 -0700248
George Burgess IV74bd3802022-09-02 16:59:27 -0700249 Args:
250 should_lock_machine: Boolean indicating whether to lock the machine (True)
251 or unlock the machine (False).
252 machine: The machine to update.
cmticef3eb8032015-07-27 13:55:52 -0700253
George Burgess IV74bd3802022-09-02 16:59:27 -0700254 Returns:
255 True if requested action succeeded, else False.
256 """
257 try:
258 if should_lock_machine:
259 ret = file_lock_machine.Machine(machine, self.locks_dir).Lock(
260 True, sys.argv[0]
261 )
262 else:
263 ret = file_lock_machine.Machine(machine, self.locks_dir).Unlock(
264 True
265 )
266 except Exception:
267 return False
268 return ret
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700269
George Burgess IV74bd3802022-09-02 16:59:27 -0700270 def UpdateMachines(self, lock_machines):
271 """Sets the locked state of the machines to the requested value.
cmticef3eb8032015-07-27 13:55:52 -0700272
George Burgess IV74bd3802022-09-02 16:59:27 -0700273 The machines updated are the ones in self.machines (specified when the
274 class object was intialized).
cmticef3eb8032015-07-27 13:55:52 -0700275
George Burgess IV74bd3802022-09-02 16:59:27 -0700276 Args:
277 lock_machines: Boolean indicating whether to lock the machines (True) or
278 unlock the machines (False).
cmticef3eb8032015-07-27 13:55:52 -0700279
George Burgess IV74bd3802022-09-02 16:59:27 -0700280 Returns:
281 A list of the machines whose state was successfully updated.
282 """
283 updated_machines = []
284 action = "Locking" if lock_machines else "Unlocking"
285 for m in self.machines:
286 # TODO(zhizhouy): Handling exceptions with more details when locking
287 # doesn't succeed.
288 machine_type = self.GetMachineType(m)
289 if machine_type == MachineType.CROSFLEET:
290 ret = self.UpdateLockInCrosfleet(lock_machines, m)
291 elif machine_type == MachineType.LOCAL:
292 ret = self.UpdateFileLock(lock_machines, m)
cmticee5bc63b2015-05-27 16:59:37 -0700293
George Burgess IV74bd3802022-09-02 16:59:27 -0700294 if ret:
295 self.logger.LogOutput(
296 "%s %s machine succeeded: %s."
297 % (action, machine_type.value, m)
298 )
299 updated_machines.append(m)
300 else:
301 self.logger.LogOutput(
302 "%s %s machine failed: %s."
303 % (action, machine_type.value, m)
304 )
cmticee5bc63b2015-05-27 16:59:37 -0700305
George Burgess IV74bd3802022-09-02 16:59:27 -0700306 self.machines = updated_machines
307 return updated_machines
cmticee5bc63b2015-05-27 16:59:37 -0700308
George Burgess IV74bd3802022-09-02 16:59:27 -0700309 def _InternalRemoveMachine(self, machine):
310 """Remove machine from internal list of machines.
cmticee5bc63b2015-05-27 16:59:37 -0700311
George Burgess IV74bd3802022-09-02 16:59:27 -0700312 Args:
313 machine: Name of machine to be removed from internal list.
314 """
315 # Check to see if machine is lab machine and if so, make sure it has
316 # ".cros" on the end.
317 cros_machine = machine
318 if machine.find("rack") > 0 and machine.find("row") > 0:
319 if machine.find(".cros") == -1:
320 cros_machine = cros_machine + ".cros"
cmticee5bc63b2015-05-27 16:59:37 -0700321
George Burgess IV74bd3802022-09-02 16:59:27 -0700322 self.machines = [
323 m for m in self.machines if m not in (cros_machine, machine)
324 ]
cmticee5bc63b2015-05-27 16:59:37 -0700325
George Burgess IV74bd3802022-09-02 16:59:27 -0700326 def CheckMachineLocks(self, machine_states, cmd):
327 """Check that every machine in requested list is in the proper state.
cmticee5bc63b2015-05-27 16:59:37 -0700328
George Burgess IV74bd3802022-09-02 16:59:27 -0700329 If the cmd is 'unlock' verify that every machine is locked by requestor.
330 If the cmd is 'lock' verify that every machine is currently unlocked.
cmticee5bc63b2015-05-27 16:59:37 -0700331
George Burgess IV74bd3802022-09-02 16:59:27 -0700332 Args:
333 machine_states: A dictionary of the current state of every machine in
334 the current LockManager's list of machines. Normally obtained by
335 calling LockManager::GetMachineStates.
336 cmd: The user-requested action for the machines: 'lock' or 'unlock'.
cmticee5bc63b2015-05-27 16:59:37 -0700337
George Burgess IV74bd3802022-09-02 16:59:27 -0700338 Raises:
339 DontOwnLock: The lock on a requested machine is owned by someone else.
340 """
341 for k, state in machine_states.items():
342 if cmd == "unlock":
343 if not state["locked"]:
344 self.logger.LogWarning(
345 "Attempt to unlock already unlocked machine "
346 "(%s)." % k
347 )
348 self._InternalRemoveMachine(k)
Zhizhou Yang4713fd12019-09-24 10:32:00 -0700349
George Burgess IV74bd3802022-09-02 16:59:27 -0700350 # TODO(zhizhouy): Crosfleet doesn't support host info such as locked_by.
351 # Need to update this when crosfleet supports it.
352 if (
353 state["locked"]
354 and state["locked_by"]
355 and state["locked_by"] != self.user
356 ):
357 raise DontOwnLock(
358 "Attempt to unlock machine (%s) locked by someone "
359 "else (%s)." % (k, state["locked_by"])
360 )
361 elif cmd == "lock":
362 if state["locked"]:
363 self.logger.LogWarning(
364 "Attempt to lock already locked machine (%s)" % k
365 )
366 self._InternalRemoveMachine(k)
Zhizhou Yang5a53a332019-10-07 13:27:37 -0700367
George Burgess IV74bd3802022-09-02 16:59:27 -0700368 def GetMachineStates(self, cmd=""):
369 """Gets the current state of all the requested machines.
cmticee5bc63b2015-05-27 16:59:37 -0700370
George Burgess IV74bd3802022-09-02 16:59:27 -0700371 Gets the current state of all the requested machines. Stores the data in a
372 dictionary keyed by machine name.
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700373
George Burgess IV74bd3802022-09-02 16:59:27 -0700374 Args:
375 cmd: The command for which we are getting the machine states. This is
376 important because if one of the requested machines is missing we raise
377 an exception, unless the requested command is 'add'.
Ryan Beltran20d36f92022-03-26 00:13:03 +0000378
George Burgess IV74bd3802022-09-02 16:59:27 -0700379 Returns:
380 A dictionary of machine states for all the machines in the LockManager
381 object.
382 """
383 machine_list = {}
384 for m in self.machines:
385 # For local or crosfleet machines, we simply set {'locked': status} for
386 # them
387 # TODO(zhizhouy): This is a quick fix since crosfleet cannot return host
388 # info as afe does. We need to get more info such as locked_by when
389 # crosfleet supports that.
390 values = {
391 "locked": 0 if cmd == "lock" else 1,
392 "board": "??",
393 "locked_by": "",
394 "lock_time": "",
395 }
396 machine_list[m] = values
George Burgess IV900d6e72020-05-02 10:28:55 -0700397
George Burgess IV74bd3802022-09-02 16:59:27 -0700398 self.ListMachineStates(machine_list)
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700399
George Burgess IV74bd3802022-09-02 16:59:27 -0700400 return machine_list
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700401
George Burgess IV74bd3802022-09-02 16:59:27 -0700402 def CheckMachineInCrosfleet(self, machine):
403 """Run command to check if machine is in Crosfleet or not.
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700404
George Burgess IV74bd3802022-09-02 16:59:27 -0700405 Returns:
406 True if machine in crosfleet, else False
407 """
408 credential = ""
409 if os.path.exists(self.CROSFLEET_CREDENTIAL):
410 credential = "--service-account-json %s" % self.CROSFLEET_CREDENTIAL
411 server = "--server https://chromeos-swarming.appspot.com"
412 dimensions = "--dimension dut_name=%s" % machine.rstrip(".cros")
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700413
George Burgess IV74bd3802022-09-02 16:59:27 -0700414 cmd = f"{self.SWARMING} bots {server} {credential} {dimensions}"
415 exit_code, stdout, stderr = self.ce.RunCommandWOutput(cmd)
416 if exit_code:
417 raise ValueError(
418 "Querying bots failed (2); stdout: %r; stderr: %r"
419 % (stdout, stderr)
420 )
Ryan Beltran20d36f92022-03-26 00:13:03 +0000421
George Burgess IV74bd3802022-09-02 16:59:27 -0700422 # The command will return a json output as stdout. If machine not in
423 # crosfleet, stdout will look like this:
424 # {
425 # "death_timeout": "600",
426 # "now": "TIMESTAMP"
427 # }
428 # Otherwise there will be a tuple starting with 'items', we simply detect
429 # this keyword for result.
430 return stdout != "[]"
431
432 def LeaseCrosfleetMachine(self, machine):
433 """Run command to lease dut from crosfleet.
434
435 Returns:
436 True if succeeded, False if failed.
437 """
438 credential = ""
439 if os.path.exists(self.CROSFLEET_CREDENTIAL):
440 credential = "-service-account-json %s" % self.CROSFLEET_CREDENTIAL
441 cmd = ("%s dut lease -minutes %s %s %s %s") % (
442 self.CROSFLEET_PATH,
443 self.LEASE_MINS,
444 credential,
445 "-host",
446 machine.rstrip(".cros"),
447 )
448 # Wait 8 minutes for server to start the lease task, if not started,
449 # we will treat it as unavailable.
450 check_interval_time = 480
451 retval = self.ce.RunCommand(cmd, command_timeout=check_interval_time)
452 return retval == self.SUCCESS
453
454 def ReleaseCrosfleetMachine(self, machine):
455 """Run command to release dut from crosfleet.
456
457 Returns:
458 True if succeeded, False if failed.
459 """
460 credential = ""
461 if os.path.exists(self.CROSFLEET_CREDENTIAL):
462 credential = "-service-account-json %s" % self.CROSFLEET_CREDENTIAL
463
464 cmd = ("%s dut abandon %s %s") % (
465 self.CROSFLEET_PATH,
466 credential,
467 machine.rstrip(".cros"),
468 )
469 retval = self.ce.RunCommand(cmd)
470 return retval == self.SUCCESS
Zhizhou Yangcdd9e342019-09-19 20:56:32 -0700471
cmticee5bc63b2015-05-27 16:59:37 -0700472
473def Main(argv):
George Burgess IV74bd3802022-09-02 16:59:27 -0700474 """Parse the options, initialize lock manager and dispatch proper method.
cmticee5bc63b2015-05-27 16:59:37 -0700475
George Burgess IV74bd3802022-09-02 16:59:27 -0700476 Args:
477 argv: The options with which this script was invoked.
cmticee5bc63b2015-05-27 16:59:37 -0700478
George Burgess IV74bd3802022-09-02 16:59:27 -0700479 Returns:
480 0 unless an exception is raised.
481 """
482 parser = argparse.ArgumentParser()
cmticee5bc63b2015-05-27 16:59:37 -0700483
George Burgess IV74bd3802022-09-02 16:59:27 -0700484 parser.add_argument(
485 "--list",
486 dest="cmd",
487 action="store_const",
488 const="status",
489 help="List current status of all known machines.",
490 )
491 parser.add_argument(
492 "--lock",
493 dest="cmd",
494 action="store_const",
495 const="lock",
496 help="Lock given machine(s).",
497 )
498 parser.add_argument(
499 "--unlock",
500 dest="cmd",
501 action="store_const",
502 const="unlock",
503 help="Unlock given machine(s).",
504 )
505 parser.add_argument(
506 "--status",
507 dest="cmd",
508 action="store_const",
509 const="status",
510 help="List current status of given machine(s).",
511 )
512 parser.add_argument(
513 "--remote", dest="remote", help="machines on which to operate"
514 )
515 parser.add_argument(
516 "--chromeos_root",
517 dest="chromeos_root",
518 required=True,
519 help="ChromeOS root to use for autotest scripts.",
520 )
521 parser.add_argument(
522 "--force",
523 dest="force",
524 action="store_true",
525 default=False,
526 help="Force lock/unlock of machines, even if not"
527 " current lock owner.",
528 )
cmticee5bc63b2015-05-27 16:59:37 -0700529
George Burgess IV74bd3802022-09-02 16:59:27 -0700530 options = parser.parse_args(argv)
cmticee5bc63b2015-05-27 16:59:37 -0700531
George Burgess IV74bd3802022-09-02 16:59:27 -0700532 if not options.remote and options.cmd != "status":
533 parser.error("No machines specified for operation.")
cmticee5bc63b2015-05-27 16:59:37 -0700534
George Burgess IV74bd3802022-09-02 16:59:27 -0700535 if not os.path.isdir(options.chromeos_root):
536 parser.error("Cannot find chromeos_root: %s." % options.chromeos_root)
cmticee5bc63b2015-05-27 16:59:37 -0700537
George Burgess IV74bd3802022-09-02 16:59:27 -0700538 if not options.cmd:
539 parser.error(
540 "No operation selected (--list, --status, --lock, --unlock,"
541 " --add_machine, --remove_machine)."
542 )
cmticee5bc63b2015-05-27 16:59:37 -0700543
George Burgess IV74bd3802022-09-02 16:59:27 -0700544 machine_list = []
545 if options.remote:
546 machine_list = options.remote.split()
cmticee5bc63b2015-05-27 16:59:37 -0700547
George Burgess IV74bd3802022-09-02 16:59:27 -0700548 lock_manager = LockManager(
549 machine_list, options.force, options.chromeos_root
550 )
cmticee5bc63b2015-05-27 16:59:37 -0700551
George Burgess IV74bd3802022-09-02 16:59:27 -0700552 machine_states = lock_manager.GetMachineStates(cmd=options.cmd)
553 cmd = options.cmd
cmticee5bc63b2015-05-27 16:59:37 -0700554
George Burgess IV74bd3802022-09-02 16:59:27 -0700555 if cmd == "status":
556 lock_manager.ListMachineStates(machine_states)
cmticee5bc63b2015-05-27 16:59:37 -0700557
George Burgess IV74bd3802022-09-02 16:59:27 -0700558 elif cmd == "lock":
559 if not lock_manager.force:
560 lock_manager.CheckMachineLocks(machine_states, cmd)
561 lock_manager.UpdateMachines(True)
cmticee5bc63b2015-05-27 16:59:37 -0700562
George Burgess IV74bd3802022-09-02 16:59:27 -0700563 elif cmd == "unlock":
564 if not lock_manager.force:
565 lock_manager.CheckMachineLocks(machine_states, cmd)
566 lock_manager.UpdateMachines(False)
cmticee5bc63b2015-05-27 16:59:37 -0700567
George Burgess IV74bd3802022-09-02 16:59:27 -0700568 return 0
cmticee5bc63b2015-05-27 16:59:37 -0700569
570
George Burgess IV74bd3802022-09-02 16:59:27 -0700571if __name__ == "__main__":
572 sys.exit(Main(sys.argv[1:]))