blob: b742b55bcd8917b9cf1481636aa37f51331f135d [file] [log] [blame]
jadmanski093a0682009-10-13 14:55:43 +00001import cPickle, os, tempfile, logging
showard170873e2009-01-07 00:22:26 +00002import common
3from autotest_lib.scheduler import drone_utility, email_manager
showard170873e2009-01-07 00:22:26 +00004from autotest_lib.client.common_lib import error, global_config
5
6
7AUTOTEST_INSTALL_DIR = global_config.global_config.get_config_value('SCHEDULER',
8 'drone_installation_directory')
9
Eric Li7edb3042011-01-06 17:57:17 -080010class DroneUnreachable(Exception):
11 """The drone is non-sshable."""
12 pass
13
14
showard170873e2009-01-07 00:22:26 +000015class _AbstractDrone(object):
showard9bb960b2009-11-19 01:02:11 +000016 """
17 Attributes:
18 * allowed_users: set of usernames allowed to use this drone. if None,
19 any user can use this drone.
20 """
showard170873e2009-01-07 00:22:26 +000021 def __init__(self):
22 self._calls = []
23 self.hostname = None
showardc5afc462009-01-13 00:09:39 +000024 self.enabled = True
showard324bf812009-01-20 23:23:38 +000025 self.max_processes = 0
26 self.active_processes = 0
showard9bb960b2009-11-19 01:02:11 +000027 self.allowed_users = None
showard170873e2009-01-07 00:22:26 +000028
29
30 def shutdown(self):
31 pass
32
33
showard324bf812009-01-20 23:23:38 +000034 def used_capacity(self):
jamesren37b50452010-03-25 20:38:56 +000035 """Gets the capacity used by this drone
36
37 Returns a tuple of (percentage_full, -max_capacity). This is to aid
38 direct comparisons, so that a 0/10 drone is considered less heavily
39 loaded than a 0/2 drone.
40
41 This value should never be used directly. It should only be used in
42 direct comparisons using the basic comparison operators, or using the
43 cmp() function.
44 """
showard324bf812009-01-20 23:23:38 +000045 if self.max_processes == 0:
jamesren37b50452010-03-25 20:38:56 +000046 return (1.0, 0)
47 return (float(self.active_processes) / self.max_processes,
48 -self.max_processes)
showard324bf812009-01-20 23:23:38 +000049
50
showard9bb960b2009-11-19 01:02:11 +000051 def usable_by(self, user):
52 if self.allowed_users is None:
53 return True
54 return user in self.allowed_users
55
56
showard170873e2009-01-07 00:22:26 +000057 def _execute_calls_impl(self, calls):
58 raise NotImplementedError
59
60
61 def _execute_calls(self, calls):
62 return_message = self._execute_calls_impl(calls)
63 for warning in return_message['warnings']:
64 subject = 'Warning from drone %s' % self.hostname
showardf098ebd2009-06-10 00:13:33 +000065 logging.warn(subject + '\n' + warning)
showard170873e2009-01-07 00:22:26 +000066 email_manager.manager.enqueue_notify_email(subject, warning)
67 return return_message['results']
68
69
70 def call(self, method, *args, **kwargs):
71 return self._execute_calls(
72 [drone_utility.call(method, *args, **kwargs)])
73
74
75 def queue_call(self, method, *args, **kwargs):
76 self._calls.append(drone_utility.call(method, *args, **kwargs))
77
78 def clear_call_queue(self):
79 self._calls = []
80
81
82 def execute_queued_calls(self):
83 if not self._calls:
84 return
85 self._execute_calls(self._calls)
86 self.clear_call_queue()
87
88
showardac5b0002009-10-19 18:34:00 +000089 def set_autotest_install_dir(self, path):
90 pass
91
92
showard170873e2009-01-07 00:22:26 +000093class _LocalDrone(_AbstractDrone):
94 def __init__(self):
95 super(_LocalDrone, self).__init__()
96 self.hostname = 'localhost'
97 self._drone_utility = drone_utility.DroneUtility()
98
99
100 def _execute_calls_impl(self, calls):
101 return self._drone_utility.execute_calls(calls)
102
103
104 def send_file_to(self, drone, source_path, destination_path,
105 can_fail=False):
106 if drone.hostname == self.hostname:
showardde634ee2009-01-30 01:44:24 +0000107 self.queue_call('copy_file_or_directory', source_path,
108 destination_path)
showard170873e2009-01-07 00:22:26 +0000109 else:
110 self.queue_call('send_file_to', drone.hostname, source_path,
111 destination_path, can_fail)
112
113
114class _RemoteDrone(_AbstractDrone):
showard170873e2009-01-07 00:22:26 +0000115 def __init__(self, hostname):
116 super(_RemoteDrone, self).__init__()
117 self.hostname = hostname
118 self._host = drone_utility.create_host(hostname)
Eric Li7edb3042011-01-06 17:57:17 -0800119 if not self._host.is_up():
120 logging.error('Drone %s is unpingable, kicking out', hostname)
121 raise DroneUnreachable
showard202343e2009-10-14 16:20:24 +0000122 self._autotest_install_dir = AUTOTEST_INSTALL_DIR
showard170873e2009-01-07 00:22:26 +0000123
showard170873e2009-01-07 00:22:26 +0000124
showard202343e2009-10-14 16:20:24 +0000125 def set_autotest_install_dir(self, path):
126 self._autotest_install_dir = path
127
128
showard170873e2009-01-07 00:22:26 +0000129 def shutdown(self):
130 super(_RemoteDrone, self).shutdown()
131 self._host.close()
132
133
134 def _execute_calls_impl(self, calls):
jadmanski093a0682009-10-13 14:55:43 +0000135 logging.info("Running drone_utility on %s", self.hostname)
showard202343e2009-10-14 16:20:24 +0000136 drone_utility_path = os.path.join(self._autotest_install_dir,
137 'scheduler', 'drone_utility.py')
138 result = self._host.run('python %s' % drone_utility_path,
139 stdin=cPickle.dumps(calls), connect_timeout=300)
showard170873e2009-01-07 00:22:26 +0000140
141 try:
jadmanski093a0682009-10-13 14:55:43 +0000142 return cPickle.loads(result.stdout)
143 except Exception: # cPickle.loads can throw all kinds of exceptions
showardf098ebd2009-06-10 00:13:33 +0000144 logging.critical('Invalid response:\n---\n%s\n---', result.stdout)
showard170873e2009-01-07 00:22:26 +0000145 raise
146
147
148 def send_file_to(self, drone, source_path, destination_path,
149 can_fail=False):
150 if drone.hostname == self.hostname:
showardde634ee2009-01-30 01:44:24 +0000151 self.queue_call('copy_file_or_directory', source_path,
152 destination_path)
showard170873e2009-01-07 00:22:26 +0000153 elif isinstance(drone, _LocalDrone):
154 drone.queue_call('get_file_from', self.hostname, source_path,
155 destination_path)
156 else:
157 self.queue_call('send_file_to', drone.hostname, source_path,
158 destination_path, can_fail)
159
160
showard170873e2009-01-07 00:22:26 +0000161def get_drone(hostname):
162 """
163 Use this factory method to get drone objects.
164 """
165 if hostname == 'localhost':
166 return _LocalDrone()
Eric Li7edb3042011-01-06 17:57:17 -0800167 try:
168 return _RemoteDrone(hostname)
169 except DroneUnreachable:
170 return None