1 import cPickle
, os
, tempfile
, logging
3 from autotest_lib
.scheduler
import drone_utility
, email_manager
4 from autotest_lib
.client
.common_lib
import error
, global_config
7 AUTOTEST_INSTALL_DIR
= global_config
.global_config
.get_config_value('SCHEDULER',
8 'drone_installation_directory')
10 class DroneUnreachable(Exception):
11 """The drone is non-sshable."""
15 class _AbstractDrone(object):
18 * allowed_users: set of usernames allowed to use this drone. if None,
19 any user can use this drone.
25 self
.max_processes
= 0
26 self
.active_processes
= 0
27 self
.allowed_users
= None
34 def used_capacity(self
):
35 """Gets the capacity used by this drone
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.
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
45 if self
.max_processes
== 0:
47 return (float(self
.active_processes
) / self
.max_processes
,
51 def usable_by(self
, user
):
52 if self
.allowed_users
is None:
54 return user
in self
.allowed_users
57 def _execute_calls_impl(self
, calls
):
58 raise NotImplementedError
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
65 logging
.warn(subject
+ '\n' + warning
)
66 email_manager
.manager
.enqueue_notify_email(subject
, warning
)
67 return return_message
['results']
70 def call(self
, method
, *args
, **kwargs
):
71 return self
._execute
_calls
(
72 [drone_utility
.call(method
, *args
, **kwargs
)])
75 def queue_call(self
, method
, *args
, **kwargs
):
76 self
._calls
.append(drone_utility
.call(method
, *args
, **kwargs
))
78 def clear_call_queue(self
):
82 def execute_queued_calls(self
):
85 self
._execute
_calls
(self
._calls
)
86 self
.clear_call_queue()
89 def set_autotest_install_dir(self
, path
):
93 class _LocalDrone(_AbstractDrone
):
95 super(_LocalDrone
, self
).__init
__()
96 self
.hostname
= 'localhost'
97 self
._drone
_utility
= drone_utility
.DroneUtility()
100 def _execute_calls_impl(self
, calls
):
101 return self
._drone
_utility
.execute_calls(calls
)
104 def send_file_to(self
, drone
, source_path
, destination_path
,
106 if drone
.hostname
== self
.hostname
:
107 self
.queue_call('copy_file_or_directory', source_path
,
110 self
.queue_call('send_file_to', drone
.hostname
, source_path
,
111 destination_path
, can_fail
)
114 class _RemoteDrone(_AbstractDrone
):
115 def __init__(self
, hostname
):
116 super(_RemoteDrone
, self
).__init
__()
117 self
.hostname
= hostname
118 self
._host
= drone_utility
.create_host(hostname
)
119 if not self
._host
.is_up():
120 logging
.error('Drone %s is unpingable, kicking out', hostname
)
121 raise DroneUnreachable
122 self
._autotest
_install
_dir
= AUTOTEST_INSTALL_DIR
126 def _drone_utility_path(self
):
127 return os
.path
.join(self
._autotest
_install
_dir
,
128 'scheduler', 'drone_utility.py')
131 def set_autotest_install_dir(self
, path
):
132 self
._autotest
_install
_dir
= path
136 super(_RemoteDrone
, self
).shutdown()
140 def _execute_calls_impl(self
, calls
):
141 logging
.info("Running drone_utility on %s", self
.hostname
)
142 result
= self
._host
.run('python %s' % self
._drone
_utility
_path
,
143 stdin
=cPickle
.dumps(calls
), stdout_tee
=None,
146 return cPickle
.loads(result
.stdout
)
147 except Exception: # cPickle.loads can throw all kinds of exceptions
148 logging
.critical('Invalid response:\n---\n%s\n---', result
.stdout
)
152 def send_file_to(self
, drone
, source_path
, destination_path
,
154 if drone
.hostname
== self
.hostname
:
155 self
.queue_call('copy_file_or_directory', source_path
,
157 elif isinstance(drone
, _LocalDrone
):
158 drone
.queue_call('get_file_from', self
.hostname
, source_path
,
161 self
.queue_call('send_file_to', drone
.hostname
, source_path
,
162 destination_path
, can_fail
)
165 def get_drone(hostname
):
167 Use this factory method to get drone objects.
169 if hostname
== 'localhost':
172 return _RemoteDrone(hostname
)
173 except DroneUnreachable
: