blob: dd958ca6297c03d05cc402770818edfb943dfa56 [file] [log] [blame]
Caroline Ticea4486452015-12-08 13:43:23 -08001#!/usr/bin/python2
cmticee5bc63b2015-05-27 16:59:37 -07002#
3# Copyright 2015 Google INc. All Rights Reserved.
4
Caroline Ticea4486452015-12-08 13:43:23 -08005"""This module controls locking and unlocking of test machines."""
6
7from __future__ import print_function
8
cmticee5bc63b2015-05-27 16:59:37 -07009import argparse
10import getpass
11import os
12import sys
13import traceback
14
15from utils import logger
16from utils import machines
cmticee5bc63b2015-05-27 16:59:37 -070017
18class 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
79 def __init__(self, remotes, force_option, chromeos_root, local_server,
80 local=True, log=None):
81 """Initializes an AFELockManager object.
82
83 Args:
84 remotes: A list of machine names or ip addresses to be managed. Names
Caroline Ticea4486452015-12-08 13:43:23 -080085 and ip addresses should be represented as strings. If the list is
86 empty, the lock manager will get all known machines.
87 force_option: A Boolean indicating whether or not to force an unlock of
cmticee5bc63b2015-05-27 16:59:37 -070088 a machine that was locked by someone else.
89 chromeos_root: The ChromeOS chroot to use for the autotest scripts.
Caroline Ticea4486452015-12-08 13:43:23 -080090 local_server: A string containing the name or ip address of the machine
cmticee5bc63b2015-05-27 16:59:37 -070091 that is running an AFE server, which is to be used for managing
92 machines that are not in the ChromeOS HW lab.
93 local: A Boolean indicating whether or not to use/allow a local AFE
94 server to be used (see local_server argument).
95 log: If not None, this is the logger object to be used for writing out
96 informational output messages. It is expected to be an instance of
97 Logger class from utils/logger.py.
98 """
99 self.chromeos_root = chromeos_root
100 self.user = getpass.getuser()
101 self.logger = log or logger.GetLogger()
102 autotest_path = os.path.join(chromeos_root,
103 'src/third_party/autotest/files')
104
cmticed1172b42015-06-12 15:14:09 -0700105 sys.path.append(chromeos_root)
cmticee5bc63b2015-05-27 16:59:37 -0700106 sys.path.append(autotest_path)
107 sys.path.append(os.path.join(autotest_path, 'server', 'cros'))
108
109 # We have to wait to do these imports until the paths above have
110 # been fixed.
111 from client import setup_modules
112 setup_modules.setup(base_path=autotest_path,
113 root_module_name='autotest_lib')
114
115 from dynamic_suite import frontend_wrappers
116
117 self.afe = frontend_wrappers.RetryingAFE(timeout_min=30,
118 delay_sec=10,
cmticed1172b42015-06-12 15:14:09 -0700119 debug=False,
120 server='cautotest')
cmticee5bc63b2015-05-27 16:59:37 -0700121 if not local:
122 self.local_afe = None
123 else:
124 dargs = {}
125 dargs['server'] = local_server or AFELockManager.LOCAL_SERVER
126 # Make sure local server is pingable.
127 error_msg = ('Local autotest server machine %s not responding to ping.'
Caroline Ticea4486452015-12-08 13:43:23 -0800128 % dargs['server'])
cmticee5bc63b2015-05-27 16:59:37 -0700129 self.CheckMachine(dargs['server'], error_msg)
130 self.local_afe = frontend_wrappers.RetryingAFE(timeout_min=30,
131 delay_sec=10,
132 debug=False,
133 **dargs)
134 self.local = local
135 self.machines = list(set(remotes)) or []
136 self.force = force_option
137 self.toolchain_lab_machines = self.GetAllToolchainLabMachines()
138 if not self.machines:
139 self.machines = self.toolchain_lab_machines + self.GetAllNonlabMachines()
140
141 def CheckMachine(self, machine, error_msg):
142 """Verifies that machine is responding to ping.
143
144 Args:
145 machine: String containing the name or ip address of machine to check.
146 error_msg: Message to print if ping fails.
147
148 Raises:
149 MachineNotPingable: If machine is not responding to 'ping'
150 """
151 if not machines.MachineIsPingable(machine, logging_level='none'):
Caroline Ticea4486452015-12-08 13:43:23 -0800152 cros_machine = machine + '.cros'
153 if not machines.MachineIsPingable(cros_machine, logging_level='none'):
154 raise MachineNotPingable(error_msg)
cmticee5bc63b2015-05-27 16:59:37 -0700155
156 def MachineIsKnown(self, machine):
157 """Checks to see if either AFE server knows the given machine.
158
159 Args:
160 machine: String containing name or ip address of machine to check.
161
162 Returns:
163 Boolean indicating if the machine is in the list of known machines for
164 either AFE server.
165 """
166 if machine in self.toolchain_lab_machines:
167 return True
168 elif self.local_afe and machine in self.GetAllNonlabMachines():
169 return True
170
171 return False
172
173 def GetAllToolchainLabMachines(self):
174 """Gets a list of all the toolchain machines in the ChromeOS HW lab.
175
176 Returns:
177 A list of names of the toolchain machines in the ChromeOS HW lab.
178 """
Han Shen441c9492015-06-11 13:56:08 -0700179 machines_file = os.path.join(os.path.dirname(__file__),
180 'crosperf', 'default_remotes')
cmticee5bc63b2015-05-27 16:59:37 -0700181 machine_list = []
182 with open(machines_file, 'r') as input_file:
183 lines = input_file.readlines()
184 for line in lines:
Caroline Ticea4486452015-12-08 13:43:23 -0800185 _, remotes = line.split(':')
cmticee5bc63b2015-05-27 16:59:37 -0700186 remotes = remotes.strip()
187 for r in remotes.split():
188 machine_list.append(r.strip())
189 return machine_list
190
191 def GetAllNonlabMachines(self):
192 """Gets a list of all known machines on the local AFE server.
193
194 Returns:
195 A list of the names of the machines on the local AFE server.
196 """
197 non_lab_machines = []
198 if self.local_afe:
199 non_lab_machines = self.local_afe.get_hostnames()
200 return non_lab_machines
201
202 def PrintStatusHeader(self, is_lab_machine):
203 """Prints the status header lines for machines.
204
Caroline Ticea4486452015-12-08 13:43:23 -0800205 Args:
206 is_lab_machine: Boolean indicating whether to print HW Lab header or
207 local machine header (different spacing).
cmticee5bc63b2015-05-27 16:59:37 -0700208 """
209 if is_lab_machine:
Caroline Ticea4486452015-12-08 13:43:23 -0800210 print('\nMachine (Board)\t\t\t\t\tStatus')
211 print('---------------\t\t\t\t\t------\n')
cmticee5bc63b2015-05-27 16:59:37 -0700212 else:
Caroline Ticea4486452015-12-08 13:43:23 -0800213 print('\nMachine (Board)\t\tStatus')
214 print('---------------\t\t------\n')
cmticee5bc63b2015-05-27 16:59:37 -0700215
216 def RemoveLocalMachine(self, m):
217 """Removes a machine from the local AFE server.
218
219 Args:
220 m: The machine to remove.
221
222 Raises:
223 MissingHostInfo: Can't find machine to be removed.
224 """
225 if self.local_afe:
226 host_info = self.local_afe.get_hosts(hostname=m)
227 if host_info:
228 host_info = host_info[0]
229 host_info.delete()
230 else:
231 raise MissingHostInfo('Cannot find/delete machine %s.' % m)
232
233 def AddLocalMachine(self, m):
234 """Adds a machine to the local AFE server.
235
236 Args:
237 m: The machine to be added.
238 """
239 if self.local_afe:
240 error_msg = 'Machine %s is not responding to ping.' % m
241 self.CheckMachine(m, error_msg)
Caroline Ticea4486452015-12-08 13:43:23 -0800242 self.local_afe.create_host(m)
cmticee5bc63b2015-05-27 16:59:37 -0700243
244 def AddMachinesToLocalServer(self):
245 """Adds one or more machines to the local AFE server.
246
247 Verify that the requested machines are legal to add to the local server,
248 i.e. that they are not ChromeOS HW lab machines, and they are not already
249 on the local server. Call AddLocalMachine for each valid machine.
250
251 Raises:
252 DuplicateAdd: Attempt to add a machine that is already on the server.
253 UpdateNonLocalMachine: Attempt to add a ChromeOS HW lab machine.
254 UpdateServerError: Something went wrong while attempting to add a
255 machine.
256 """
257 for m in self.machines:
Caroline Tice3f432712015-12-07 14:51:53 -0800258 for cros_name in [m, m + '.cros']:
259 if cros_name in self.toolchain_lab_machines:
260 raise UpdateNonLocalMachine('Machine %s is already in the ChromeOS HW'
261 'Lab. Cannot add it to local server.'
262 % cros_name)
cmticee5bc63b2015-05-27 16:59:37 -0700263 host_info = self.local_afe.get_hosts(hostname=m)
264 if host_info:
265 raise DuplicateAdd('Machine %s is already on the local server.' % m)
266 try:
267 self.AddLocalMachine(m)
268 self.logger.LogOutput('Successfully added %s to local server.' % m)
269 except Exception as e:
270 traceback.print_exc()
271 raise UpdateServerError('Error occurred while attempting to add %s. %s'
272 % (m, str(e)))
273
274 def RemoveMachinesFromLocalServer(self):
275 """Removes one or more machines from the local AFE server.
276
277 Verify that the requested machines are legal to remove from the local
278 server, i.e. that they are not ChromeOS HW lab machines. Call
279 RemoveLocalMachine for each valid machine.
280
281 Raises:
282 UpdateServerError: Something went wrong while attempting to remove a
283 machine.
284 """
285 for m in self.machines:
Caroline Tice3f432712015-12-07 14:51:53 -0800286 for cros_name in [m, m + '.cros']:
287 if cros_name in self.toolchain_lab_machines:
288 raise UpdateNonLocalMachine('Machine %s is in the ChromeOS HW Lab. '
289 'This script cannot remove lab machines.'
290 % cros_name)
cmticee5bc63b2015-05-27 16:59:37 -0700291 try:
292 self.RemoveLocalMachine(m)
293 self.logger.LogOutput('Successfully removed %s from local server.' % m)
294 except Exception as e:
295 traceback.print_exc()
296 raise UpdateServerError('Error occurred while attempting to remove %s '
297 '(%s).' % (m, str(e)))
298
299 def ListMachineStates(self, machine_states):
300 """Gets and prints the current status for a list of machines.
301
302 Prints out the current status for all of the machines in the current
303 AFELockManager's list of machines (set when the object is initialized).
304
305 Args:
306 machine_states: A dictionary of the current state of every machine in
307 the current AFELockManager's list of machines. Normally obtained by
308 calling AFELockManager::GetMachineStates.
309 """
310 local_machines = []
311 printed_hdr = False
312 for m in machine_states:
313 cros_name = m + '.cros'
314 if (m in self.toolchain_lab_machines or
315 cros_name in self.toolchain_lab_machines):
Caroline Tice3f432712015-12-07 14:51:53 -0800316 name = m if m in self.toolchain_lab_machines else cros_name
cmticee5bc63b2015-05-27 16:59:37 -0700317 if not printed_hdr:
318 self.PrintStatusHeader(True)
319 printed_hdr = True
320 state = machine_states[m]
321 if state['locked']:
Caroline Ticea4486452015-12-08 13:43:23 -0800322 print('%s (%s)\tlocked by %s since %s' %
323 (name, state['board'], state['locked_by'], state['lock_time']))
cmticee5bc63b2015-05-27 16:59:37 -0700324 else:
Caroline Ticea4486452015-12-08 13:43:23 -0800325 print('%s (%s)\tunlocked' % (name, state['board']))
cmticee5bc63b2015-05-27 16:59:37 -0700326 else:
327 local_machines.append(m)
328
329 if local_machines:
330 self.PrintStatusHeader(False)
331 for m in local_machines:
332 state = machine_states[m]
333 if state['locked']:
Caroline Ticea4486452015-12-08 13:43:23 -0800334 print('%s (%s)\tlocked by %s since %s' %
335 (m, state['board'], state['locked_by'], state['lock_time']))
cmticee5bc63b2015-05-27 16:59:37 -0700336 else:
Caroline Ticea4486452015-12-08 13:43:23 -0800337 print('%s (%s)\tunlocked' % (m, state['board']))
cmticee5bc63b2015-05-27 16:59:37 -0700338
339
340 def UpdateLockInAFE(self, should_lock_machine, machine):
341 """Calls an AFE server to lock/unlock a machine.
342
343 Args:
344 should_lock_machine: Boolean indicating whether to lock the machine (True)
345 or unlock the machine (False).
346 machine: The machine to update.
347
348 Raises:
349 LockingError: An error occurred while attempting to update the machine
350 state.
351 """
352 action = 'lock'
353 if not should_lock_machine:
354 action = 'unlock'
355 kwargs = {'locked': should_lock_machine}
cmtice25c94f12015-07-24 11:37:34 -0700356 kwargs['lock_reason'] = 'toolchain user request (%s)' % self.user
cmticee5bc63b2015-05-27 16:59:37 -0700357
Caroline Tice3f432712015-12-07 14:51:53 -0800358 cros_name = machine + '.cros'
359 if cros_name in self.toolchain_lab_machines:
Caroline Ticea4486452015-12-08 13:43:23 -0800360 machine = cros_name
cmticee5bc63b2015-05-27 16:59:37 -0700361 if machine in self.toolchain_lab_machines:
362 m = machine.split('.')[0]
cmticee5bc63b2015-05-27 16:59:37 -0700363 afe_server = self.afe
364 else:
365 m = machine
366 afe_server = self.local_afe
367
368 try:
369 afe_server.run('modify_hosts',
Caroline Ticea4486452015-12-08 13:43:23 -0800370 host_filter_data={'hostname__in': [m]},
371 update_data=kwargs)
cmticee5bc63b2015-05-27 16:59:37 -0700372 except Exception as e:
373 traceback.print_exc()
374 raise LockingError('Unable to %s machine %s. %s' % (action, m, str(e)))
375
376 def UpdateMachines(self, lock_machines):
377 """Sets the locked state of the machines to the requested value.
378
379 The machines updated are the ones in self.machines (specified when the
380 class object was intialized).
381
382 Args:
Caroline Ticea4486452015-12-08 13:43:23 -0800383 lock_machines: Boolean indicating whether to lock the machines (True) or
cmticee5bc63b2015-05-27 16:59:37 -0700384 unlock the machines (False).
cmticef3eb8032015-07-27 13:55:52 -0700385
386 Returns:
387 A list of the machines whose state was successfully updated.
cmticee5bc63b2015-05-27 16:59:37 -0700388 """
cmticef3eb8032015-07-27 13:55:52 -0700389 updated_machines = []
cmticee5bc63b2015-05-27 16:59:37 -0700390 for m in self.machines:
391 self.UpdateLockInAFE(lock_machines, m)
cmticee5bc63b2015-05-27 16:59:37 -0700392 # Since we returned from self.UpdateLockInAFE we assume the request
393 # succeeded.
394 if lock_machines:
395 self.logger.LogOutput('Locked machine(s) %s.' % m)
396 else:
397 self.logger.LogOutput('Unlocked machine(s) %s.' % m)
cmticef3eb8032015-07-27 13:55:52 -0700398 updated_machines.append(m)
399
400 return updated_machines
401
402 def _InternalRemoveMachine(self, machine):
403 """Remove machine from internal list of machines.
404
405 Args:
406 machine: Name of machine to be removed from internal list.
407 """
408 # Check to see if machine is lab machine and if so, make sure it has
409 # ".cros" on the end.
410 cros_machine = machine
411 if machine.find('rack') > 0 and machine.find('row') > 0:
412 if machine.find('.cros') == -1:
413 cros_machine = cros_machine + '.cros'
414
415 self.machines = [m for m in self.machines if m != cros_machine and
416 m != machine]
cmticee5bc63b2015-05-27 16:59:37 -0700417
418 def CheckMachineLocks(self, machine_states, cmd):
419 """Check that every machine in requested list is in the proper state.
420
421 If the cmd is 'unlock' verify that every machine is locked by requestor.
422 If the cmd is 'lock' verify that every machine is currently unlocked.
423
424 Args:
425 machine_states: A dictionary of the current state of every machine in
426 the current AFELockManager's list of machines. Normally obtained by
427 calling AFELockManager::GetMachineStates.
Caroline Ticea4486452015-12-08 13:43:23 -0800428 cmd: The user-requested action for the machines: 'lock' or 'unlock'.
cmticee5bc63b2015-05-27 16:59:37 -0700429
430 Raises:
cmticee5bc63b2015-05-27 16:59:37 -0700431 DontOwnLock: The lock on a requested machine is owned by someone else.
432 """
433 for k, state in machine_states.iteritems():
434 if cmd == 'unlock':
435 if not state['locked']:
cmticef3eb8032015-07-27 13:55:52 -0700436 self.logger.LogWarning('Attempt to unlock already unlocked machine '
437 '(%s).' % k)
438 self._InternalRemoveMachine(k)
cmticee5bc63b2015-05-27 16:59:37 -0700439
cmticef3eb8032015-07-27 13:55:52 -0700440 if state['locked'] and state['locked_by'] != self.user:
cmticee5bc63b2015-05-27 16:59:37 -0700441 raise DontOwnLock('Attempt to unlock machine (%s) locked by someone '
442 'else (%s).' % (k, state['locked_by']))
443 elif cmd == 'lock':
444 if state['locked']:
Caroline Ticea4486452015-12-08 13:43:23 -0800445 self.logger.LogWarning('Attempt to lock already locked machine (%s)'
446 % k)
cmticef3eb8032015-07-27 13:55:52 -0700447 self._InternalRemoveMachine(k)
cmticee5bc63b2015-05-27 16:59:37 -0700448
449 def HasAFEServer(self, local):
450 """Verifies that the AFELockManager has appropriate AFE server.
451
452 Args:
453 local: Boolean indicating whether we are checking for the local server
454 (True) or for the global server (False).
455
456 Returns:
457 A boolean indicating if the AFELockManager has the requested AFE server.
458 """
459 if local:
460 return self.local_afe is not None
461 else:
462 return self.afe is not None
463
464 def GetMachineStates(self, cmd=''):
465 """Gets the current state of all the requested machines.
466
467 Gets the current state of all the requested machines, both from the HW lab
468 sever and from the local server. Stores the data in a dictionary keyed
469 by machine name.
470
471 Args:
472 cmd: The command for which we are getting the machine states. This is
473 important because if one of the requested machines is missing we raise
474 an exception, unless the requested command is 'add'.
475
476 Returns:
477 A dictionary of machine states for all the machines in the AFELockManager
478 object.
479
480 Raises:
481 NoAFEServer: Cannot find the HW Lab or local AFE server.
482 AFEAccessError: An error occurred when querying the server about a
483 machine.
484 """
485 if not self.HasAFEServer(False):
486 raise NoAFEServer('Error: Cannot connect to main AFE server.')
487
488 if self.local and not self.HasAFEServer(True):
489 raise NoAFEServer('Error: Cannot connect to local AFE server.')
490
Caroline Ticea4486452015-12-08 13:43:23 -0800491 machine_list = {}
cmticee5bc63b2015-05-27 16:59:37 -0700492 for m in self.machines:
493 host_info = None
Caroline Tice3f432712015-12-07 14:51:53 -0800494 cros_name = m + '.cros'
495 if (m in self.toolchain_lab_machines or
496 cros_name in self.toolchain_lab_machines):
cmticee5bc63b2015-05-27 16:59:37 -0700497 mod_host = m.split('.')[0]
498 host_info = self.afe.get_hosts(hostname=mod_host)
499 if not host_info:
500 raise AFEAccessError('Unable to get information about %s from main'
501 ' autotest server.' % m)
502 else:
503 host_info = self.local_afe.get_hosts(hostname=m)
504 if not host_info and cmd != 'add':
505 raise AFEAccessError('Unable to get information about %s from '
506 'local autotest server.' % m)
507 if host_info:
508 host_info = host_info[0]
509 name = host_info.hostname
510 values = {}
511 values['board'] = host_info.platform if host_info.platform else '??'
512 values['locked'] = host_info.locked
513 if host_info.locked:
Caroline Ticea4486452015-12-08 13:43:23 -0800514 values['locked_by'] = host_info.locked_by
515 values['lock_time'] = host_info.lock_time
cmticee5bc63b2015-05-27 16:59:37 -0700516 else:
Caroline Ticea4486452015-12-08 13:43:23 -0800517 values['locked_by'] = ''
518 values['lock_time'] = ''
519 machine_list[name] = values
cmticee5bc63b2015-05-27 16:59:37 -0700520 else:
Caroline Ticea4486452015-12-08 13:43:23 -0800521 machine_list[m] = {}
522 return machine_list
cmticee5bc63b2015-05-27 16:59:37 -0700523
524
525def Main(argv):
Caroline Ticea4486452015-12-08 13:43:23 -0800526 """Parse the options, initialize lock manager and dispatch proper method.
cmticee5bc63b2015-05-27 16:59:37 -0700527
Caroline Ticea4486452015-12-08 13:43:23 -0800528 Args:
529 argv: The options with which this script was invoked.
cmticee5bc63b2015-05-27 16:59:37 -0700530
Caroline Ticea4486452015-12-08 13:43:23 -0800531 Returns:
532 0 unless an exception is raised.
533 """
534 parser = argparse.ArgumentParser()
cmticee5bc63b2015-05-27 16:59:37 -0700535
Caroline Ticea4486452015-12-08 13:43:23 -0800536 parser.add_argument('--list', dest='cmd', action='store_const',
537 const='status',
538 help='List current status of all known machines.')
539 parser.add_argument('--lock', dest='cmd', action='store_const',
540 const='lock', help='Lock given machine(s).')
541 parser.add_argument('--unlock', dest='cmd', action='store_const',
542 const='unlock', help='Unlock given machine(s).')
543 parser.add_argument('--status', dest='cmd', action='store_const',
544 const='status',
545 help='List current status of given machine(s).')
546 parser.add_argument('--add_machine', dest='cmd', action='store_const',
547 const='add',
548 help='Add machine to local machine server.')
549 parser.add_argument('--remove_machine', dest='cmd',
550 action='store_const', const='remove',
551 help='Remove machine from the local machine server.')
552 parser.add_argument('--nolocal', dest='local',
553 action='store_false', default=True,
554 help='Do not try to use local machine server.')
555 parser.add_argument('--remote', dest='remote',
556 help='machines on which to operate')
557 parser.add_argument('--chromeos_root', dest='chromeos_root', required=True,
558 help='ChromeOS root to use for autotest scripts.')
559 parser.add_argument('--local_server', dest='local_server', default=None,
560 help='Alternate local autotest server to use.')
561 parser.add_argument('--force', dest='force', action='store_true',
562 default=False,
563 help='Force lock/unlock of machines, even if not'
564 ' current lock owner.')
cmticee5bc63b2015-05-27 16:59:37 -0700565
Caroline Ticea4486452015-12-08 13:43:23 -0800566 options = parser.parse_args(argv)
cmticee5bc63b2015-05-27 16:59:37 -0700567
Caroline Ticea4486452015-12-08 13:43:23 -0800568 if not options.remote and options.cmd != 'status':
569 parser.error('No machines specified for operation.')
cmticee5bc63b2015-05-27 16:59:37 -0700570
Caroline Ticea4486452015-12-08 13:43:23 -0800571 if not os.path.isdir(options.chromeos_root):
572 parser.error('Cannot find chromeos_root: %s.' % options.chromeos_root)
cmticee5bc63b2015-05-27 16:59:37 -0700573
Caroline Ticea4486452015-12-08 13:43:23 -0800574 if not options.cmd:
575 parser.error('No operation selected (--list, --status, --lock, --unlock,'
576 ' --add_machine, --remove_machine).')
cmticee5bc63b2015-05-27 16:59:37 -0700577
Caroline Ticea4486452015-12-08 13:43:23 -0800578 machine_list = []
579 if options.remote:
580 machine_list = options.remote.split()
cmticee5bc63b2015-05-27 16:59:37 -0700581
Caroline Ticea4486452015-12-08 13:43:23 -0800582 lock_manager = AFELockManager(machine_list, options.force,
583 options.chromeos_root, options.local_server,
584 options.local)
cmticee5bc63b2015-05-27 16:59:37 -0700585
Caroline Ticea4486452015-12-08 13:43:23 -0800586 machine_states = lock_manager.GetMachineStates(cmd=options.cmd)
587 cmd = options.cmd
cmticee5bc63b2015-05-27 16:59:37 -0700588
Caroline Ticea4486452015-12-08 13:43:23 -0800589 if cmd == 'status':
590 lock_manager.ListMachineStates(machine_states)
cmticee5bc63b2015-05-27 16:59:37 -0700591
Caroline Ticea4486452015-12-08 13:43:23 -0800592 elif cmd == 'lock':
593 if not lock_manager.force:
594 lock_manager.CheckMachineLocks(machine_states, cmd)
595 lock_manager.UpdateMachines(True)
cmticee5bc63b2015-05-27 16:59:37 -0700596
Caroline Ticea4486452015-12-08 13:43:23 -0800597 elif cmd == 'unlock':
598 if not lock_manager.force:
599 lock_manager.CheckMachineLocks(machine_states, cmd)
600 lock_manager.UpdateMachines(False)
cmticee5bc63b2015-05-27 16:59:37 -0700601
Caroline Ticea4486452015-12-08 13:43:23 -0800602 elif cmd == 'add':
603 lock_manager.AddMachinesToLocalServer()
cmticee5bc63b2015-05-27 16:59:37 -0700604
Caroline Ticea4486452015-12-08 13:43:23 -0800605 elif cmd == 'remove':
606 lock_manager.RemoveMachinesFromLocalServer()
cmticee5bc63b2015-05-27 16:59:37 -0700607
Caroline Ticea4486452015-12-08 13:43:23 -0800608 return 0
cmticee5bc63b2015-05-27 16:59:37 -0700609
610
611if __name__ == '__main__':
Caroline Ticea4486452015-12-08 13:43:23 -0800612 sys.exit(Main(sys.argv[1:]))