import image_chromeos import lock_machine import sys import threading import time from cros_utils import command_executer from cros_utils import logger class CrosMachine(object): def __init__(self, name): self.name = name self.image = None self.checksum = None self.locked = False self.released_time = time.time() self.autotest_run = None def __str__(self): l = [] l.append(self.name) l.append(str(self.image)) l.append(str(self.checksum)) l.append(str(self.locked)) l.append(str(self.released_time)) return ', '.join(l) class MachineManagerSingleton(object): _instance = None _lock = threading.RLock() _all_machines = [] _machines = [] image_lock = threading.Lock() num_reimages = 0 chromeos_root = None no_lock = False def __new__(cls, *args, **kwargs): with cls._lock: if not cls._instance: cls._instance = super(MachineManagerSingleton, cls).__new__(cls, *args, **kwargs) return cls._instance def TryToLockMachine(self, cros_machine): with self._lock: assert cros_machine, "Machine can't be None" for m in self._machines: assert m.name != cros_machine.name, ('Tried to double-lock %s' % cros_machine.name) if self.no_lock: locked = True else: locked = lock_machine.Machine(cros_machine.name).Lock(True, sys.argv[0]) if locked: ce = command_executer.GetCommandExecuter() command = 'cat %s' % image_chromeos.checksum_file ret, out, err = ce.CrosRunCommandWOutput( command, chromeos_root=self.chromeos_root, machine=cros_machine.name) if ret == 0: cros_machine.checksum = out.strip() self._machines.append(cros_machine) else: logger.GetLogger().LogOutput("Warning: Couldn't lock: %s" % cros_machine.name) # This is called from single threaded mode. def AddMachine(self, machine_name): with self._lock: for m in self._all_machines: assert m.name != machine_name, 'Tried to double-add %s' % machine_name self._all_machines.append(CrosMachine(machine_name)) def AcquireMachine(self, image_checksum): with self._lock: # Lazily external lock machines if not self._machines: for m in self._all_machines: self.TryToLockMachine(m) assert self._machines, ('Could not lock any machine in %s' % self._all_machines) ### for m in self._machines: ### if (m.locked and time.time() - m.released_time < 10 and ### m.checksum == image_checksum): ### return None for m in [machine for machine in self._machines if not machine.locked]: if m.checksum == image_checksum: m.locked = True m.autotest_run = threading.current_thread() return m for m in [machine for machine in self._machines if not machine.locked]: if not m.checksum: m.locked = True m.autotest_run = threading.current_thread() return m for m in [machine for machine in self._machines if not machine.locked]: if time.time() - m.released_time > 20: m.locked = True m.autotest_run = threading.current_thread() return m return None def ReleaseMachine(self, machine): with self._lock: for m in self._machines: if machine.name == m.name: assert m.locked == True, 'Tried to double-release %s' % m.name m.released_time = time.time() m.locked = False m.status = 'Available' break def __del__(self): with self._lock: # Unlock all machines. for m in self._machines: if not self.no_lock: assert lock_machine.Machine(m.name).Unlock(True) == True, ( "Couldn't unlock machine: %s" % m.name) def __str__(self): with self._lock: l = ['MachineManager Status:'] for m in self._machines: l.append(str(m)) return '\n'.join(l) def AsString(self): with self._lock: stringify_fmt = '%-30s %-10s %-4s %-25s %-32s' header = stringify_fmt % ('Machine', 'Thread', 'Lock', 'Status', 'Checksum') table = [header] for m in self._machines: if m.autotest_run: autotest_name = m.autotest_run.name autotest_status = m.autotest_run.status else: autotest_name = '' autotest_status = '' try: machine_string = stringify_fmt % (m.name, autotest_name, m.locked, autotest_status, m.checksum) except: machine_string = '' table.append(machine_string) return 'Machine Status:\n%s' % '\n'.join(table)