blob: 370579db66639dde349719006caa591746641f5c [file] [log] [blame]
Caroline Ticef6ef4392017-04-06 17:16:05 -07001#!/usr/bin/env python2
cmticee5bc63b2015-05-27 16:59:37 -07002#
3# Copyright 2015 Google INc. All Rights Reserved.
Caroline Ticea4486452015-12-08 13:43:23 -08004"""This module controls locking and unlocking of test machines."""
5
6from __future__ import print_function
7
cmticee5bc63b2015-05-27 16:59:37 -07008import argparse
9import getpass
10import os
11import sys
12import traceback
13
Caroline Ticea8af9a72016-07-20 12:52:59 -070014from cros_utils import logger
15from cros_utils import machines
cmticee5bc63b2015-05-27 16:59:37 -070016
Luis Lozanof2a3ef42015-12-15 13:49:30 -080017
cmticee5bc63b2015-05-27 16:59:37 -070018class AFELockException(Exception):
19 """Base class for exceptions in this module."""
20
21
22class MachineNotPingable(AFELockException):
23 """Raised when machine does not respond to ping."""
24
25
26class MissingHostInfo(AFELockException):
27 """Raised when cannot find info about machine on machine servers."""
28
29
30class UpdateNonLocalMachine(AFELockException):
31 """Raised when user requests to add/remove a ChromeOS HW Lab machine.."""
32
33
34class DuplicateAdd(AFELockException):
35 """Raised when user requests to add a machine that's already on the server."""
36
37
38class UpdateServerError(AFELockException):
39 """Raised when attempt to add/remove a machine from local server fails."""
40
41
42class LockingError(AFELockException):
43 """Raised when server fails to lock/unlock machine as requested."""
44
45
cmticee5bc63b2015-05-27 16:59:37 -070046class DontOwnLock(AFELockException):
47 """Raised when user attmepts to unlock machine locked by someone else."""
48 # This should not be raised if the user specified '--force'
49
50
51class NoAFEServer(AFELockException):
52 """Raised when cannot find/access the autotest server."""
53
54
55class AFEAccessError(AFELockException):
56 """Raised when cannot get information about lab machine from lab server."""
57
58
59class AFELockManager(object):
60 """Class for locking/unlocking machines vie Autotest Front End servers.
61
62 This class contains methods for checking the locked status of machines
63 on both the ChromeOS HW Lab AFE server and a local AFE server. It also
64 has methods for adding/removing machines from the local server, and for
65 changing the lock status of machines on either server. For the ChromeOS
66 HW Lab, it only allows access to the toolchain team lab machines, as
67 defined in toolchain-utils/crosperf/default_remotes. By default it will
68 look for a local server on chrotomation2.mtv.corp.google.com, but an
69 alternative local AFE server can be supplied, if desired.
70
71 !!!IMPORTANT NOTE!!! The AFE server can only be called from the main
72 thread/process of a program. If you launch threads and try to call it
73 from a thread, you will get an error. This has to do with restrictions
74 in the Python virtual machine (and signal handling) and cannot be changed.
75 """
76
77 LOCAL_SERVER = 'chrotomation2.mtv.corp.google.com'
78
Luis Lozanof2a3ef42015-12-15 13:49:30 -080079 def __init__(self,
80 remotes,
81 force_option,
82 chromeos_root,
83 local_server,
Caroline Tice6b161382016-09-15 15:03:46 -070084 use_local=True,
Luis Lozanof2a3ef42015-12-15 13:49:30 -080085 log=None):
cmticee5bc63b2015-05-27 16:59:37 -070086 """Initializes an AFELockManager object.
87
88 Args:
89 remotes: A list of machine names or ip addresses to be managed. Names
Caroline Ticea4486452015-12-08 13:43:23 -080090 and ip addresses should be represented as strings. If the list is
91 empty, the lock manager will get all known machines.
92 force_option: A Boolean indicating whether or not to force an unlock of
cmticee5bc63b2015-05-27 16:59:37 -070093 a machine that was locked by someone else.
94 chromeos_root: The ChromeOS chroot to use for the autotest scripts.
Caroline Ticea4486452015-12-08 13:43:23 -080095 local_server: A string containing the name or ip address of the machine
cmticee5bc63b2015-05-27 16:59:37 -070096 that is running an AFE server, which is to be used for managing
97 machines that are not in the ChromeOS HW lab.
98 local: A Boolean indicating whether or not to use/allow a local AFE
99 server to be used (see local_server argument).
Caroline Ticef6ef4392017-04-06 17:16:05 -0700100 use_local: Use the local server instead of the official one.
cmticee5bc63b2015-05-27 16:59:37 -0700101 log: If not None, this is the logger object to be used for writing out
102 informational output messages. It is expected to be an instance of
Caroline Ticea8af9a72016-07-20 12:52:59 -0700103 Logger class from cros_utils/logger.py.
cmticee5bc63b2015-05-27 16:59:37 -0700104 """
105 self.chromeos_root = chromeos_root
106 self.user = getpass.getuser()
107 self.logger = log or logger.GetLogger()
108 autotest_path = os.path.join(chromeos_root,
109 'src/third_party/autotest/files')
110
cmticed1172b42015-06-12 15:14:09 -0700111 sys.path.append(chromeos_root)
cmticee5bc63b2015-05-27 16:59:37 -0700112 sys.path.append(autotest_path)
113 sys.path.append(os.path.join(autotest_path, 'server', 'cros'))
114
115 # We have to wait to do these imports until the paths above have
116 # been fixed.
Yunlian Jiangd97422a2015-12-16 11:06:13 -0800117 # pylint: disable=import-error
cmticee5bc63b2015-05-27 16:59:37 -0700118 from client import setup_modules
Caroline Tice6b161382016-09-15 15:03:46 -0700119 setup_modules.setup(
120 base_path=autotest_path, root_module_name='autotest_lib')
cmticee5bc63b2015-05-27 16:59:37 -0700121
122 from dynamic_suite import frontend_wrappers
123
Caroline Tice6b161382016-09-15 15:03:46 -0700124 self.afe = frontend_wrappers.RetryingAFE(
125 timeout_min=30, delay_sec=10, debug=False, server='cautotest')
126
127 self.local = use_local
128 self.machines = list(set(remotes)) or []
129 self.toolchain_lab_machines = self.GetAllToolchainLabMachines()
130 if self.machines and self.AllLabMachines():
131 self.local = False
132
133 if not self.local:
cmticee5bc63b2015-05-27 16:59:37 -0700134 self.local_afe = None
135 else:
136 dargs = {}
137 dargs['server'] = local_server or AFELockManager.LOCAL_SERVER
138 # Make sure local server is pingable.
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800139 error_msg = ('Local autotest server machine %s not responding to ping.' %
140 dargs['server'])
cmticee5bc63b2015-05-27 16:59:37 -0700141 self.CheckMachine(dargs['server'], error_msg)
Caroline Tice6b161382016-09-15 15:03:46 -0700142 self.local_afe = frontend_wrappers.RetryingAFE(
143 timeout_min=30, delay_sec=10, debug=False, **dargs)
cmticee5bc63b2015-05-27 16:59:37 -0700144 if not self.machines:
145 self.machines = self.toolchain_lab_machines + self.GetAllNonlabMachines()
Caroline Tice6b161382016-09-15 15:03:46 -0700146 self.force = force_option
147
148 def AllLabMachines(self):
149 """Check to see if all machines being used are HW Lab machines."""
150 all_lab = True
151 for m in self.machines:
152 if m not in self.toolchain_lab_machines:
153 all_lab = False
154 break
155 return all_lab
cmticee5bc63b2015-05-27 16:59:37 -0700156
157 def CheckMachine(self, machine, error_msg):
158 """Verifies that machine is responding to ping.
159
160 Args:
161 machine: String containing the name or ip address of machine to check.
162 error_msg: Message to print if ping fails.
163
164 Raises:
165 MachineNotPingable: If machine is not responding to 'ping'
166 """
167 if not machines.MachineIsPingable(machine, logging_level='none'):
Caroline Ticea4486452015-12-08 13:43:23 -0800168 cros_machine = machine + '.cros'
169 if not machines.MachineIsPingable(cros_machine, logging_level='none'):
170 raise MachineNotPingable(error_msg)
cmticee5bc63b2015-05-27 16:59:37 -0700171
172 def MachineIsKnown(self, machine):
173 """Checks to see if either AFE server knows the given machine.
174
175 Args:
176 machine: String containing name or ip address of machine to check.
177
178 Returns:
179 Boolean indicating if the machine is in the list of known machines for
180 either AFE server.
181 """
182 if machine in self.toolchain_lab_machines:
183 return True
184 elif self.local_afe and machine in self.GetAllNonlabMachines():
185 return True
186
187 return False
188
189 def GetAllToolchainLabMachines(self):
190 """Gets a list of all the toolchain machines in the ChromeOS HW lab.
191
192 Returns:
193 A list of names of the toolchain machines in the ChromeOS HW lab.
194 """
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800195 machines_file = os.path.join(
196 os.path.dirname(__file__), 'crosperf', 'default_remotes')
cmticee5bc63b2015-05-27 16:59:37 -0700197 machine_list = []
198 with open(machines_file, 'r') as input_file:
199 lines = input_file.readlines()
200 for line in lines:
Caroline Ticea4486452015-12-08 13:43:23 -0800201 _, remotes = line.split(':')
cmticee5bc63b2015-05-27 16:59:37 -0700202 remotes = remotes.strip()
203 for r in remotes.split():
204 machine_list.append(r.strip())
205 return machine_list
206
207 def GetAllNonlabMachines(self):
208 """Gets a list of all known machines on the local AFE server.
209
210 Returns:
211 A list of the names of the machines on the local AFE server.
212 """
213 non_lab_machines = []
214 if self.local_afe:
215 non_lab_machines = self.local_afe.get_hostnames()
216 return non_lab_machines
217
218 def PrintStatusHeader(self, is_lab_machine):
219 """Prints the status header lines for machines.
220
Caroline Ticea4486452015-12-08 13:43:23 -0800221 Args:
222 is_lab_machine: Boolean indicating whether to print HW Lab header or
223 local machine header (different spacing).
cmticee5bc63b2015-05-27 16:59:37 -0700224 """
225 if is_lab_machine:
Caroline Ticea4486452015-12-08 13:43:23 -0800226 print('\nMachine (Board)\t\t\t\t\tStatus')
227 print('---------------\t\t\t\t\t------\n')
cmticee5bc63b2015-05-27 16:59:37 -0700228 else:
Caroline Ticea4486452015-12-08 13:43:23 -0800229 print('\nMachine (Board)\t\tStatus')
230 print('---------------\t\t------\n')
cmticee5bc63b2015-05-27 16:59:37 -0700231
232 def RemoveLocalMachine(self, m):
233 """Removes a machine from the local AFE server.
234
235 Args:
236 m: The machine to remove.
237
238 Raises:
239 MissingHostInfo: Can't find machine to be removed.
240 """
241 if self.local_afe:
242 host_info = self.local_afe.get_hosts(hostname=m)
243 if host_info:
244 host_info = host_info[0]
245 host_info.delete()
246 else:
247 raise MissingHostInfo('Cannot find/delete machine %s.' % m)
248
249 def AddLocalMachine(self, m):
250 """Adds a machine to the local AFE server.
251
252 Args:
253 m: The machine to be added.
254 """
255 if self.local_afe:
256 error_msg = 'Machine %s is not responding to ping.' % m
257 self.CheckMachine(m, error_msg)
Caroline Ticea4486452015-12-08 13:43:23 -0800258 self.local_afe.create_host(m)
cmticee5bc63b2015-05-27 16:59:37 -0700259
260 def AddMachinesToLocalServer(self):
261 """Adds one or more machines to the local AFE server.
262
263 Verify that the requested machines are legal to add to the local server,
264 i.e. that they are not ChromeOS HW lab machines, and they are not already
265 on the local server. Call AddLocalMachine for each valid machine.
266
267 Raises:
268 DuplicateAdd: Attempt to add a machine that is already on the server.
269 UpdateNonLocalMachine: Attempt to add a ChromeOS HW lab machine.
270 UpdateServerError: Something went wrong while attempting to add a
271 machine.
272 """
273 for m in self.machines:
Caroline Tice3f432712015-12-07 14:51:53 -0800274 for cros_name in [m, m + '.cros']:
275 if cros_name in self.toolchain_lab_machines:
Caroline Ticef6ef4392017-04-06 17:16:05 -0700276 raise UpdateNonLocalMachine(
277 'Machine %s is already in the ChromeOS HW'
278 'Lab. Cannot add it to local server.' % cros_name)
cmticee5bc63b2015-05-27 16:59:37 -0700279 host_info = self.local_afe.get_hosts(hostname=m)
280 if host_info:
281 raise DuplicateAdd('Machine %s is already on the local server.' % m)
282 try:
283 self.AddLocalMachine(m)
284 self.logger.LogOutput('Successfully added %s to local server.' % m)
285 except Exception as e:
286 traceback.print_exc()
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800287 raise UpdateServerError(
288 'Error occurred while attempting to add %s. %s' % (m, str(e)))
cmticee5bc63b2015-05-27 16:59:37 -0700289
290 def RemoveMachinesFromLocalServer(self):
291 """Removes one or more machines from the local AFE server.
292
293 Verify that the requested machines are legal to remove from the local
294 server, i.e. that they are not ChromeOS HW lab machines. Call
295 RemoveLocalMachine for each valid machine.
296
297 Raises:
298 UpdateServerError: Something went wrong while attempting to remove a
299 machine.
300 """
301 for m in self.machines:
Caroline Tice3f432712015-12-07 14:51:53 -0800302 for cros_name in [m, m + '.cros']:
303 if cros_name in self.toolchain_lab_machines:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800304 raise UpdateNonLocalMachine(
305 'Machine %s is in the ChromeOS HW Lab. '
306 'This script cannot remove lab machines.' % cros_name)
cmticee5bc63b2015-05-27 16:59:37 -0700307 try:
308 self.RemoveLocalMachine(m)
309 self.logger.LogOutput('Successfully removed %s from local server.' % m)
310 except Exception as e:
311 traceback.print_exc()
312 raise UpdateServerError('Error occurred while attempting to remove %s '
313 '(%s).' % (m, str(e)))
314
315 def ListMachineStates(self, machine_states):
316 """Gets and prints the current status for a list of machines.
317
318 Prints out the current status for all of the machines in the current
319 AFELockManager's list of machines (set when the object is initialized).
320
321 Args:
322 machine_states: A dictionary of the current state of every machine in
323 the current AFELockManager's list of machines. Normally obtained by
324 calling AFELockManager::GetMachineStates.
325 """
326 local_machines = []
327 printed_hdr = False
328 for m in machine_states:
329 cros_name = m + '.cros'
330 if (m in self.toolchain_lab_machines or
331 cros_name in self.toolchain_lab_machines):
Caroline Tice3f432712015-12-07 14:51:53 -0800332 name = m if m in self.toolchain_lab_machines else cros_name
cmticee5bc63b2015-05-27 16:59:37 -0700333 if not printed_hdr:
334 self.PrintStatusHeader(True)
335 printed_hdr = True
336 state = machine_states[m]
337 if state['locked']:
Caroline Ticea4486452015-12-08 13:43:23 -0800338 print('%s (%s)\tlocked by %s since %s' %
339 (name, state['board'], state['locked_by'], state['lock_time']))
cmticee5bc63b2015-05-27 16:59:37 -0700340 else:
Caroline Ticea4486452015-12-08 13:43:23 -0800341 print('%s (%s)\tunlocked' % (name, state['board']))
cmticee5bc63b2015-05-27 16:59:37 -0700342 else:
343 local_machines.append(m)
344
345 if local_machines:
346 self.PrintStatusHeader(False)
347 for m in local_machines:
348 state = machine_states[m]
349 if state['locked']:
Caroline Ticea4486452015-12-08 13:43:23 -0800350 print('%s (%s)\tlocked by %s since %s' %
351 (m, state['board'], state['locked_by'], state['lock_time']))
cmticee5bc63b2015-05-27 16:59:37 -0700352 else:
Caroline Ticea4486452015-12-08 13:43:23 -0800353 print('%s (%s)\tunlocked' % (m, state['board']))
cmticee5bc63b2015-05-27 16:59:37 -0700354
cmticee5bc63b2015-05-27 16:59:37 -0700355 def UpdateLockInAFE(self, should_lock_machine, machine):
356 """Calls an AFE server to lock/unlock a machine.
357
358 Args:
359 should_lock_machine: Boolean indicating whether to lock the machine (True)
360 or unlock the machine (False).
361 machine: The machine to update.
362
363 Raises:
364 LockingError: An error occurred while attempting to update the machine
365 state.
366 """
367 action = 'lock'
368 if not should_lock_machine:
369 action = 'unlock'
370 kwargs = {'locked': should_lock_machine}
cmtice25c94f12015-07-24 11:37:34 -0700371 kwargs['lock_reason'] = 'toolchain user request (%s)' % self.user
cmticee5bc63b2015-05-27 16:59:37 -0700372
Caroline Tice3f432712015-12-07 14:51:53 -0800373 cros_name = machine + '.cros'
374 if cros_name in self.toolchain_lab_machines:
Caroline Ticea4486452015-12-08 13:43:23 -0800375 machine = cros_name
cmticee5bc63b2015-05-27 16:59:37 -0700376 if machine in self.toolchain_lab_machines:
377 m = machine.split('.')[0]
cmticee5bc63b2015-05-27 16:59:37 -0700378 afe_server = self.afe
379 else:
380 m = machine
381 afe_server = self.local_afe
382
383 try:
Caroline Ticef6ef4392017-04-06 17:16:05 -0700384 afe_server.run(
385 'modify_hosts',
386 host_filter_data={'hostname__in': [m]},
387 update_data=kwargs)
cmticee5bc63b2015-05-27 16:59:37 -0700388 except Exception as e:
389 traceback.print_exc()
390 raise LockingError('Unable to %s machine %s. %s' % (action, m, str(e)))
391
392 def UpdateMachines(self, lock_machines):
393 """Sets the locked state of the machines to the requested value.
394
395 The machines updated are the ones in self.machines (specified when the
396 class object was intialized).
397
398 Args:
Caroline Ticea4486452015-12-08 13:43:23 -0800399 lock_machines: Boolean indicating whether to lock the machines (True) or
cmticee5bc63b2015-05-27 16:59:37 -0700400 unlock the machines (False).
cmticef3eb8032015-07-27 13:55:52 -0700401
402 Returns:
403 A list of the machines whose state was successfully updated.
cmticee5bc63b2015-05-27 16:59:37 -0700404 """
cmticef3eb8032015-07-27 13:55:52 -0700405 updated_machines = []
cmticee5bc63b2015-05-27 16:59:37 -0700406 for m in self.machines:
407 self.UpdateLockInAFE(lock_machines, m)
cmticee5bc63b2015-05-27 16:59:37 -0700408 # Since we returned from self.UpdateLockInAFE we assume the request
409 # succeeded.
410 if lock_machines:
411 self.logger.LogOutput('Locked machine(s) %s.' % m)
412 else:
413 self.logger.LogOutput('Unlocked machine(s) %s.' % m)
cmticef3eb8032015-07-27 13:55:52 -0700414 updated_machines.append(m)
415
416 return updated_machines
417
418 def _InternalRemoveMachine(self, machine):
419 """Remove machine from internal list of machines.
420
421 Args:
422 machine: Name of machine to be removed from internal list.
423 """
424 # Check to see if machine is lab machine and if so, make sure it has
425 # ".cros" on the end.
426 cros_machine = machine
427 if machine.find('rack') > 0 and machine.find('row') > 0:
428 if machine.find('.cros') == -1:
429 cros_machine = cros_machine + '.cros'
430
Caroline Ticef6ef4392017-04-06 17:16:05 -0700431 self.machines = [
432 m for m in self.machines if m != cros_machine and m != machine
433 ]
cmticee5bc63b2015-05-27 16:59:37 -0700434
435 def CheckMachineLocks(self, machine_states, cmd):
436 """Check that every machine in requested list is in the proper state.
437
438 If the cmd is 'unlock' verify that every machine is locked by requestor.
439 If the cmd is 'lock' verify that every machine is currently unlocked.
440
441 Args:
442 machine_states: A dictionary of the current state of every machine in
443 the current AFELockManager's list of machines. Normally obtained by
444 calling AFELockManager::GetMachineStates.
Caroline Ticea4486452015-12-08 13:43:23 -0800445 cmd: The user-requested action for the machines: 'lock' or 'unlock'.
cmticee5bc63b2015-05-27 16:59:37 -0700446
447 Raises:
cmticee5bc63b2015-05-27 16:59:37 -0700448 DontOwnLock: The lock on a requested machine is owned by someone else.
449 """
450 for k, state in machine_states.iteritems():
451 if cmd == 'unlock':
452 if not state['locked']:
cmticef3eb8032015-07-27 13:55:52 -0700453 self.logger.LogWarning('Attempt to unlock already unlocked machine '
454 '(%s).' % k)
455 self._InternalRemoveMachine(k)
cmticee5bc63b2015-05-27 16:59:37 -0700456
cmticef3eb8032015-07-27 13:55:52 -0700457 if state['locked'] and state['locked_by'] != self.user:
cmticee5bc63b2015-05-27 16:59:37 -0700458 raise DontOwnLock('Attempt to unlock machine (%s) locked by someone '
459 'else (%s).' % (k, state['locked_by']))
460 elif cmd == 'lock':
461 if state['locked']:
Caroline Ticef6ef4392017-04-06 17:16:05 -0700462 self.logger.LogWarning(
463 'Attempt to lock already locked machine (%s)' % k)
cmticef3eb8032015-07-27 13:55:52 -0700464 self._InternalRemoveMachine(k)
cmticee5bc63b2015-05-27 16:59:37 -0700465
466 def HasAFEServer(self, local):
467 """Verifies that the AFELockManager has appropriate AFE server.
468
469 Args:
470 local: Boolean indicating whether we are checking for the local server
471 (True) or for the global server (False).
472
473 Returns:
474 A boolean indicating if the AFELockManager has the requested AFE server.
475 """
476 if local:
477 return self.local_afe is not None
478 else:
479 return self.afe is not None
480
481 def GetMachineStates(self, cmd=''):
482 """Gets the current state of all the requested machines.
483
484 Gets the current state of all the requested machines, both from the HW lab
485 sever and from the local server. Stores the data in a dictionary keyed
486 by machine name.
487
488 Args:
489 cmd: The command for which we are getting the machine states. This is
490 important because if one of the requested machines is missing we raise
491 an exception, unless the requested command is 'add'.
492
493 Returns:
494 A dictionary of machine states for all the machines in the AFELockManager
495 object.
496
497 Raises:
498 NoAFEServer: Cannot find the HW Lab or local AFE server.
499 AFEAccessError: An error occurred when querying the server about a
500 machine.
501 """
502 if not self.HasAFEServer(False):
503 raise NoAFEServer('Error: Cannot connect to main AFE server.')
504
505 if self.local and not self.HasAFEServer(True):
506 raise NoAFEServer('Error: Cannot connect to local AFE server.')
507
Caroline Ticea4486452015-12-08 13:43:23 -0800508 machine_list = {}
cmticee5bc63b2015-05-27 16:59:37 -0700509 for m in self.machines:
510 host_info = None
Caroline Tice3f432712015-12-07 14:51:53 -0800511 cros_name = m + '.cros'
512 if (m in self.toolchain_lab_machines or
513 cros_name in self.toolchain_lab_machines):
cmticee5bc63b2015-05-27 16:59:37 -0700514 mod_host = m.split('.')[0]
515 host_info = self.afe.get_hosts(hostname=mod_host)
516 if not host_info:
517 raise AFEAccessError('Unable to get information about %s from main'
518 ' autotest server.' % m)
519 else:
520 host_info = self.local_afe.get_hosts(hostname=m)
521 if not host_info and cmd != 'add':
522 raise AFEAccessError('Unable to get information about %s from '
523 'local autotest server.' % m)
524 if host_info:
525 host_info = host_info[0]
526 name = host_info.hostname
527 values = {}
528 values['board'] = host_info.platform if host_info.platform else '??'
529 values['locked'] = host_info.locked
530 if host_info.locked:
Caroline Ticea4486452015-12-08 13:43:23 -0800531 values['locked_by'] = host_info.locked_by
532 values['lock_time'] = host_info.lock_time
cmticee5bc63b2015-05-27 16:59:37 -0700533 else:
Caroline Ticea4486452015-12-08 13:43:23 -0800534 values['locked_by'] = ''
535 values['lock_time'] = ''
536 machine_list[name] = values
cmticee5bc63b2015-05-27 16:59:37 -0700537 else:
Caroline Ticea4486452015-12-08 13:43:23 -0800538 machine_list[m] = {}
539 return machine_list
cmticee5bc63b2015-05-27 16:59:37 -0700540
541
542def Main(argv):
Caroline Ticea4486452015-12-08 13:43:23 -0800543 """Parse the options, initialize lock manager and dispatch proper method.
cmticee5bc63b2015-05-27 16:59:37 -0700544
Caroline Ticea4486452015-12-08 13:43:23 -0800545 Args:
546 argv: The options with which this script was invoked.
cmticee5bc63b2015-05-27 16:59:37 -0700547
Caroline Ticea4486452015-12-08 13:43:23 -0800548 Returns:
549 0 unless an exception is raised.
550 """
551 parser = argparse.ArgumentParser()
cmticee5bc63b2015-05-27 16:59:37 -0700552
Caroline Tice6b161382016-09-15 15:03:46 -0700553 parser.add_argument(
554 '--list',
555 dest='cmd',
556 action='store_const',
557 const='status',
558 help='List current status of all known machines.')
559 parser.add_argument(
560 '--lock',
561 dest='cmd',
562 action='store_const',
563 const='lock',
564 help='Lock given machine(s).')
565 parser.add_argument(
566 '--unlock',
567 dest='cmd',
568 action='store_const',
569 const='unlock',
570 help='Unlock given machine(s).')
571 parser.add_argument(
572 '--status',
573 dest='cmd',
574 action='store_const',
575 const='status',
576 help='List current status of given machine(s).')
577 parser.add_argument(
578 '--add_machine',
579 dest='cmd',
580 action='store_const',
581 const='add',
582 help='Add machine to local machine server.')
583 parser.add_argument(
584 '--remove_machine',
585 dest='cmd',
586 action='store_const',
587 const='remove',
588 help='Remove machine from the local machine server.')
589 parser.add_argument(
590 '--nolocal',
591 dest='local',
592 action='store_false',
593 default=True,
594 help='Do not try to use local machine server.')
595 parser.add_argument(
596 '--remote', dest='remote', help='machines on which to operate')
597 parser.add_argument(
598 '--chromeos_root',
599 dest='chromeos_root',
600 required=True,
601 help='ChromeOS root to use for autotest scripts.')
602 parser.add_argument(
603 '--local_server',
604 dest='local_server',
605 default=None,
606 help='Alternate local autotest server to use.')
607 parser.add_argument(
608 '--force',
609 dest='force',
610 action='store_true',
611 default=False,
612 help='Force lock/unlock of machines, even if not'
613 ' current lock owner.')
cmticee5bc63b2015-05-27 16:59:37 -0700614
Caroline Ticea4486452015-12-08 13:43:23 -0800615 options = parser.parse_args(argv)
cmticee5bc63b2015-05-27 16:59:37 -0700616
Caroline Ticea4486452015-12-08 13:43:23 -0800617 if not options.remote and options.cmd != 'status':
618 parser.error('No machines specified for operation.')
cmticee5bc63b2015-05-27 16:59:37 -0700619
Caroline Ticea4486452015-12-08 13:43:23 -0800620 if not os.path.isdir(options.chromeos_root):
621 parser.error('Cannot find chromeos_root: %s.' % options.chromeos_root)
cmticee5bc63b2015-05-27 16:59:37 -0700622
Caroline Ticea4486452015-12-08 13:43:23 -0800623 if not options.cmd:
624 parser.error('No operation selected (--list, --status, --lock, --unlock,'
625 ' --add_machine, --remove_machine).')
cmticee5bc63b2015-05-27 16:59:37 -0700626
Caroline Ticea4486452015-12-08 13:43:23 -0800627 machine_list = []
628 if options.remote:
629 machine_list = options.remote.split()
cmticee5bc63b2015-05-27 16:59:37 -0700630
Caroline Ticea4486452015-12-08 13:43:23 -0800631 lock_manager = AFELockManager(machine_list, options.force,
632 options.chromeos_root, options.local_server,
633 options.local)
cmticee5bc63b2015-05-27 16:59:37 -0700634
Caroline Ticea4486452015-12-08 13:43:23 -0800635 machine_states = lock_manager.GetMachineStates(cmd=options.cmd)
636 cmd = options.cmd
cmticee5bc63b2015-05-27 16:59:37 -0700637
Caroline Ticea4486452015-12-08 13:43:23 -0800638 if cmd == 'status':
639 lock_manager.ListMachineStates(machine_states)
cmticee5bc63b2015-05-27 16:59:37 -0700640
Caroline Ticea4486452015-12-08 13:43:23 -0800641 elif cmd == 'lock':
642 if not lock_manager.force:
643 lock_manager.CheckMachineLocks(machine_states, cmd)
644 lock_manager.UpdateMachines(True)
cmticee5bc63b2015-05-27 16:59:37 -0700645
Caroline Ticea4486452015-12-08 13:43:23 -0800646 elif cmd == 'unlock':
647 if not lock_manager.force:
648 lock_manager.CheckMachineLocks(machine_states, cmd)
649 lock_manager.UpdateMachines(False)
cmticee5bc63b2015-05-27 16:59:37 -0700650
Caroline Ticea4486452015-12-08 13:43:23 -0800651 elif cmd == 'add':
652 lock_manager.AddMachinesToLocalServer()
cmticee5bc63b2015-05-27 16:59:37 -0700653
Caroline Ticea4486452015-12-08 13:43:23 -0800654 elif cmd == 'remove':
655 lock_manager.RemoveMachinesFromLocalServer()
cmticee5bc63b2015-05-27 16:59:37 -0700656
Caroline Ticea4486452015-12-08 13:43:23 -0800657 return 0
cmticee5bc63b2015-05-27 16:59:37 -0700658
659
660if __name__ == '__main__':
Caroline Ticea4486452015-12-08 13:43:23 -0800661 sys.exit(Main(sys.argv[1:]))