blob: e580556ed568deef5d5c0948cbe39198d1456ea1 [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
Simran Basiaf9b8e72012-10-12 15:02:36 -07004from autotest_lib.client.common_lib import error, global_config, utils
showard170873e2009-01-07 00:22:26 +00005
6
7AUTOTEST_INSTALL_DIR = global_config.global_config.get_config_value('SCHEDULER',
8 'drone_installation_directory')
9
Eric Li861b2d52011-02-04 14:50:35 -080010class DroneUnreachable(Exception):
11 """The drone is non-sshable."""
12 pass
13
14
Simran Basiaf9b8e72012-10-12 15:02:36 -070015class _BaseAbstractDrone(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
Simran Basiaf9b8e72012-10-12 15:02:36 -070093SiteDrone = utils.import_site_class(
94 __file__, 'autotest_lib.scheduler.site_drones',
95 '_SiteAbstractDrone', _BaseAbstractDrone)
96
97
98class _AbstractDrone(SiteDrone):
99 pass
100
101
showard170873e2009-01-07 00:22:26 +0000102class _LocalDrone(_AbstractDrone):
103 def __init__(self):
104 super(_LocalDrone, self).__init__()
105 self.hostname = 'localhost'
106 self._drone_utility = drone_utility.DroneUtility()
107
108
109 def _execute_calls_impl(self, calls):
110 return self._drone_utility.execute_calls(calls)
111
112
113 def send_file_to(self, drone, source_path, destination_path,
114 can_fail=False):
115 if drone.hostname == self.hostname:
showardde634ee2009-01-30 01:44:24 +0000116 self.queue_call('copy_file_or_directory', source_path,
117 destination_path)
showard170873e2009-01-07 00:22:26 +0000118 else:
119 self.queue_call('send_file_to', drone.hostname, source_path,
120 destination_path, can_fail)
121
122
123class _RemoteDrone(_AbstractDrone):
showard170873e2009-01-07 00:22:26 +0000124 def __init__(self, hostname):
125 super(_RemoteDrone, self).__init__()
126 self.hostname = hostname
127 self._host = drone_utility.create_host(hostname)
Eric Li861b2d52011-02-04 14:50:35 -0800128 if not self._host.is_up():
129 logging.error('Drone %s is unpingable, kicking out', hostname)
130 raise DroneUnreachable
showard202343e2009-10-14 16:20:24 +0000131 self._autotest_install_dir = AUTOTEST_INSTALL_DIR
showard170873e2009-01-07 00:22:26 +0000132
showard170873e2009-01-07 00:22:26 +0000133
Eric Lid656d562011-04-20 11:48:29 -0700134 @property
135 def _drone_utility_path(self):
136 return os.path.join(self._autotest_install_dir,
137 'scheduler', 'drone_utility.py')
138
139
showard202343e2009-10-14 16:20:24 +0000140 def set_autotest_install_dir(self, path):
141 self._autotest_install_dir = path
142
143
showard170873e2009-01-07 00:22:26 +0000144 def shutdown(self):
145 super(_RemoteDrone, self).shutdown()
146 self._host.close()
147
148
149 def _execute_calls_impl(self, calls):
jadmanski093a0682009-10-13 14:55:43 +0000150 logging.info("Running drone_utility on %s", self.hostname)
Eric Lid656d562011-04-20 11:48:29 -0700151 result = self._host.run('python %s' % self._drone_utility_path,
152 stdin=cPickle.dumps(calls), stdout_tee=None,
153 connect_timeout=300)
showard170873e2009-01-07 00:22:26 +0000154 try:
jadmanski093a0682009-10-13 14:55:43 +0000155 return cPickle.loads(result.stdout)
156 except Exception: # cPickle.loads can throw all kinds of exceptions
showardf098ebd2009-06-10 00:13:33 +0000157 logging.critical('Invalid response:\n---\n%s\n---', result.stdout)
showard170873e2009-01-07 00:22:26 +0000158 raise
159
160
161 def send_file_to(self, drone, source_path, destination_path,
162 can_fail=False):
163 if drone.hostname == self.hostname:
showardde634ee2009-01-30 01:44:24 +0000164 self.queue_call('copy_file_or_directory', source_path,
165 destination_path)
showard170873e2009-01-07 00:22:26 +0000166 elif isinstance(drone, _LocalDrone):
167 drone.queue_call('get_file_from', self.hostname, source_path,
168 destination_path)
169 else:
170 self.queue_call('send_file_to', drone.hostname, source_path,
171 destination_path, can_fail)
172
173
showard170873e2009-01-07 00:22:26 +0000174def get_drone(hostname):
175 """
176 Use this factory method to get drone objects.
177 """
178 if hostname == 'localhost':
179 return _LocalDrone()
Eric Li861b2d52011-02-04 14:50:35 -0800180 try:
181 return _RemoteDrone(hostname)
182 except DroneUnreachable:
183 return None