machine_manager_singleton.py revision 036c9233742004aa773a374df381b1cf137484f5
1import image_chromeos
2import lock_machine
3import sys
4import threading
5import time
6from utils import command_executer
7from utils import logger
8
9
10class CrosMachine(object):
11  def __init__(self, name):
12    self.name = name
13    self.image = None
14    self.checksum = None
15    self.locked = False
16    self.released_time = time.time()
17    self.autotest_run = None
18
19  def __str__(self):
20    l = []
21    l.append(self.name)
22    l.append(str(self.image))
23    l.append(str(self.checksum))
24    l.append(str(self.locked))
25    l.append(str(self.released_time))
26    return ", ".join(l)
27
28
29class MachineManagerSingleton(object):
30  _instance = None
31  _lock = threading.RLock()
32  _all_machines = []
33  _machines = []
34  image_lock = threading.Lock()
35  num_reimages = 0
36  chromeos_root = None
37  no_lock = False
38
39  def __new__(cls, *args, **kwargs):
40    with cls._lock:
41      if not cls._instance:
42        cls._instance = super(MachineManagerSingleton, cls).__new__(
43            cls, *args, **kwargs)
44      return cls._instance
45
46  def TryToLockMachine(self, cros_machine):
47    with self._lock:
48      assert cros_machine, "Machine can't be None"
49      for m in self._machines:
50        assert m.name != cros_machine.name, (
51            "Tried to double-lock %s" % cros_machine.name)
52      if self.no_lock:
53        locked = True
54      else:
55        locked = lock_machine.Machine(cros_machine.name).Lock(True, sys.argv[0])
56      if locked:
57        ce = command_executer.GetCommandExecuter()
58        command = "cat %s" % image_chromeos.checksum_file
59        ret, out, err = ce.CrosRunCommandWOutput(
60            command, chromeos_root=self.chromeos_root,
61            machine=cros_machine.name)
62        if ret == 0:
63          cros_machine.checksum = out.strip()
64        self._machines.append(cros_machine)
65      else:
66        logger.GetLogger().LogOutput("Warning: Couldn't lock: %s" %
67                                     cros_machine.name)
68
69  # This is called from single threaded mode.
70  def AddMachine(self, machine_name):
71    with self._lock:
72      for m in self._all_machines:
73        assert m.name != machine_name, "Tried to double-add %s" % machine_name
74      self._all_machines.append(CrosMachine(machine_name))
75
76  def AcquireMachine(self, image_checksum):
77    with self._lock:
78      # Lazily external lock machines
79      if not self._machines:
80        for m in self._all_machines:
81          self.TryToLockMachine(m)
82      assert self._machines, (
83          "Could not lock any machine in %s" % self._all_machines)
84
85###      for m in self._machines:
86###        if (m.locked and time.time() - m.released_time < 10 and
87###            m.checksum == image_checksum):
88###          return None
89      for m in [machine for machine in self._machines if not machine.locked]:
90        if m.checksum == image_checksum:
91          m.locked = True
92          m.autotest_run = threading.current_thread()
93          return m
94      for m in [machine for machine in self._machines if not machine.locked]:
95        if not m.checksum:
96          m.locked = True
97          m.autotest_run = threading.current_thread()
98          return m
99      for m in [machine for machine in self._machines if not machine.locked]:
100        if time.time() - m.released_time > 20:
101          m.locked = True
102          m.autotest_run = threading.current_thread()
103          return m
104    return None
105
106  def ReleaseMachine(self, machine):
107    with self._lock:
108      for m in self._machines:
109        if machine.name == m.name:
110          assert m.locked == True, "Tried to double-release %s" % m.name
111          m.released_time = time.time()
112          m.locked = False
113          m.status = "Available"
114          break
115
116  def __del__(self):
117    with self._lock:
118      # Unlock all machines.
119      for m in self._machines:
120        if not self.no_lock:
121          assert lock_machine.Machine(m.name).Unlock(True) == True, (
122              "Couldn't unlock machine: %s" % m.name)
123
124  def __str__(self):
125    with self._lock:
126      l = ["MachineManager Status:"]
127      for m in self._machines:
128        l.append(str(m))
129      return "\n".join(l)
130
131  def AsString(self):
132    with self._lock:
133      stringify_fmt = "%-30s %-10s %-4s %-25s %-32s"
134      header = stringify_fmt % ("Machine", "Thread", "Lock", "Status", "Checksum")
135      table = [header]
136      for m in self._machines:
137        if m.autotest_run:
138          autotest_name = m.autotest_run.name
139          autotest_status = m.autotest_run.status
140        else:
141          autotest_name = ""
142          autotest_status = ""
143
144        try:
145          machine_string = stringify_fmt % (m.name,
146                                            autotest_name,
147                                            m.locked,
148                                            autotest_status,
149                                            m.checksum)
150        except:
151          machine_string = ""
152        table.append(machine_string)
153      return "Machine Status:\n%s" % "\n".join(table)
154