blob: 85a5ee297e4c95ad1df05b41a2dea924a7eb6d85 [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
10class _AbstractDrone(object):
showard9bb960b2009-11-19 01:02:11 +000011 """
12 Attributes:
13 * allowed_users: set of usernames allowed to use this drone. if None,
14 any user can use this drone.
15 """
showard170873e2009-01-07 00:22:26 +000016 def __init__(self):
17 self._calls = []
18 self.hostname = None
showardc5afc462009-01-13 00:09:39 +000019 self.enabled = True
showard324bf812009-01-20 23:23:38 +000020 self.max_processes = 0
21 self.active_processes = 0
showard9bb960b2009-11-19 01:02:11 +000022 self.allowed_users = None
showard170873e2009-01-07 00:22:26 +000023
24
25 def shutdown(self):
26 pass
27
28
showard324bf812009-01-20 23:23:38 +000029 def used_capacity(self):
jamesren37b50452010-03-25 20:38:56 +000030 """Gets the capacity used by this drone
31
32 Returns a tuple of (percentage_full, -max_capacity). This is to aid
33 direct comparisons, so that a 0/10 drone is considered less heavily
34 loaded than a 0/2 drone.
35
36 This value should never be used directly. It should only be used in
37 direct comparisons using the basic comparison operators, or using the
38 cmp() function.
39 """
showard324bf812009-01-20 23:23:38 +000040 if self.max_processes == 0:
jamesren37b50452010-03-25 20:38:56 +000041 return (1.0, 0)
42 return (float(self.active_processes) / self.max_processes,
43 -self.max_processes)
showard324bf812009-01-20 23:23:38 +000044
45
showard9bb960b2009-11-19 01:02:11 +000046 def usable_by(self, user):
47 if self.allowed_users is None:
48 return True
49 return user in self.allowed_users
50
51
showard170873e2009-01-07 00:22:26 +000052 def _execute_calls_impl(self, calls):
53 raise NotImplementedError
54
55
56 def _execute_calls(self, calls):
57 return_message = self._execute_calls_impl(calls)
58 for warning in return_message['warnings']:
59 subject = 'Warning from drone %s' % self.hostname
showardf098ebd2009-06-10 00:13:33 +000060 logging.warn(subject + '\n' + warning)
showard170873e2009-01-07 00:22:26 +000061 email_manager.manager.enqueue_notify_email(subject, warning)
62 return return_message['results']
63
64
65 def call(self, method, *args, **kwargs):
66 return self._execute_calls(
67 [drone_utility.call(method, *args, **kwargs)])
68
69
70 def queue_call(self, method, *args, **kwargs):
71 self._calls.append(drone_utility.call(method, *args, **kwargs))
72
73 def clear_call_queue(self):
74 self._calls = []
75
76
77 def execute_queued_calls(self):
78 if not self._calls:
79 return
80 self._execute_calls(self._calls)
81 self.clear_call_queue()
82
83
showardac5b0002009-10-19 18:34:00 +000084 def set_autotest_install_dir(self, path):
85 pass
86
87
showard170873e2009-01-07 00:22:26 +000088class _LocalDrone(_AbstractDrone):
89 def __init__(self):
90 super(_LocalDrone, self).__init__()
91 self.hostname = 'localhost'
92 self._drone_utility = drone_utility.DroneUtility()
93
94
95 def _execute_calls_impl(self, calls):
96 return self._drone_utility.execute_calls(calls)
97
98
99 def send_file_to(self, drone, source_path, destination_path,
100 can_fail=False):
101 if drone.hostname == self.hostname:
showardde634ee2009-01-30 01:44:24 +0000102 self.queue_call('copy_file_or_directory', source_path,
103 destination_path)
showard170873e2009-01-07 00:22:26 +0000104 else:
105 self.queue_call('send_file_to', drone.hostname, source_path,
106 destination_path, can_fail)
107
108
109class _RemoteDrone(_AbstractDrone):
showard170873e2009-01-07 00:22:26 +0000110 def __init__(self, hostname):
111 super(_RemoteDrone, self).__init__()
112 self.hostname = hostname
113 self._host = drone_utility.create_host(hostname)
showard202343e2009-10-14 16:20:24 +0000114 self._autotest_install_dir = AUTOTEST_INSTALL_DIR
showard170873e2009-01-07 00:22:26 +0000115
showard170873e2009-01-07 00:22:26 +0000116
showard202343e2009-10-14 16:20:24 +0000117 def set_autotest_install_dir(self, path):
118 self._autotest_install_dir = path
119
120
showard170873e2009-01-07 00:22:26 +0000121 def shutdown(self):
122 super(_RemoteDrone, self).shutdown()
123 self._host.close()
124
125
126 def _execute_calls_impl(self, calls):
jadmanski093a0682009-10-13 14:55:43 +0000127 logging.info("Running drone_utility on %s", self.hostname)
showard202343e2009-10-14 16:20:24 +0000128 drone_utility_path = os.path.join(self._autotest_install_dir,
129 'scheduler', 'drone_utility.py')
130 result = self._host.run('python %s' % drone_utility_path,
131 stdin=cPickle.dumps(calls), connect_timeout=300)
showard170873e2009-01-07 00:22:26 +0000132
133 try:
jadmanski093a0682009-10-13 14:55:43 +0000134 return cPickle.loads(result.stdout)
135 except Exception: # cPickle.loads can throw all kinds of exceptions
showardf098ebd2009-06-10 00:13:33 +0000136 logging.critical('Invalid response:\n---\n%s\n---', result.stdout)
showard170873e2009-01-07 00:22:26 +0000137 raise
138
139
140 def send_file_to(self, drone, source_path, destination_path,
141 can_fail=False):
142 if drone.hostname == self.hostname:
showardde634ee2009-01-30 01:44:24 +0000143 self.queue_call('copy_file_or_directory', source_path,
144 destination_path)
showard170873e2009-01-07 00:22:26 +0000145 elif isinstance(drone, _LocalDrone):
146 drone.queue_call('get_file_from', self.hostname, source_path,
147 destination_path)
148 else:
149 self.queue_call('send_file_to', drone.hostname, source_path,
150 destination_path, can_fail)
151
152
showard170873e2009-01-07 00:22:26 +0000153def get_drone(hostname):
154 """
155 Use this factory method to get drone objects.
156 """
157 if hostname == 'localhost':
158 return _LocalDrone()
159 return _RemoteDrone(hostname)