This addresses feedback in D356#6972.
Details
Details
Diff Detail
Diff Detail
- Repository
- rLTRN libtaskotron
- Branch
- feature/D356-fixes
- Lint
Lint OK - Unit
No Unit Test Coverage
Lint OK |
No Unit Test Coverage |
Path | Packages | |||
---|---|---|---|---|
M | conf/taskotron.yaml.example (2 lines) | |||
M | libtaskotron.spec (2 lines) | |||
M | libtaskotron/config_defaults.py (2 lines) | |||
M | libtaskotron/remote_exec.py (10 lines) | |||
M | libtaskotron/runner.py (54 lines) | |||
M | testing/test_remote_exec.py (6 lines) |
Commit | Tree | Parents | Author | Summary | Date |
---|---|---|---|---|---|
32a4ed574223 | 2495f16deab8 | 7344502fb1c1 | Martin Krizek | D356 post-mortem review fixes (Show More…) | Jun 7 2015, 8:05 AM |
Show First 20 Lines • Show All 85 Lines • ▼ Show 20 Line(s) | |||||
86 | 86 | | |||
87 | 87 | | |||
88 | ## ==== PATHS section ==== | 88 | ## ==== PATHS section ==== | ||
89 | ## Location of various pieces of the project. | 89 | ## Location of various pieces of the project. | ||
90 | 90 | | |||
91 | ## The location of log files for Taskotron | 91 | ## The location of log files for Taskotron | ||
92 | #logdir: /var/log/taskotron | 92 | #logdir: /var/log/taskotron | ||
93 | 93 | | |||
94 | ## The location of task file when running in disposable clients mode | 94 | ## The location of task files (git checkout) when running in disposable clients mode | ||
95 | #client_taskdir: /var/tmp/taskotron/taskdir | 95 | #client_taskdir: /var/tmp/taskotron/taskdir | ||
96 | 96 | | |||
97 | ## The location of temporary files for Taskotron | 97 | ## The location of temporary files for Taskotron | ||
98 | #tmpdir: /var/tmp/taskotron | 98 | #tmpdir: /var/tmp/taskotron | ||
99 | 99 | | |||
100 | ## The location of artifacts produced by checks | 100 | ## The location of artifacts produced by checks | ||
101 | #artifactsdir: /var/lib/taskotron/artifacts | 101 | #artifactsdir: /var/lib/taskotron/artifacts | ||
102 | 102 | | |||
Show All 29 Lines |
Show All 17 Lines | |||||
18 | Requires: libtaskotron-config | 18 | Requires: libtaskotron-config | ||
19 | Requires: mash | 19 | Requires: mash | ||
20 | Requires: pyOpenSSL | 20 | Requires: pyOpenSSL | ||
21 | Requires: pytap13 >= 0.3.0 | 21 | Requires: pytap13 >= 0.3.0 | ||
22 | Requires: python-bayeux >= 0.9 | 22 | Requires: python-bayeux >= 0.9 | ||
23 | Requires: python-bunch >= 1.0.1 | 23 | Requires: python-bunch >= 1.0.1 | ||
24 | Requires: python-fedora >= 0.3.36 | 24 | Requires: python-fedora >= 0.3.36 | ||
25 | Requires: python-hawkey >= 0.4.13-1 | 25 | Requires: python-hawkey >= 0.4.13-1 | ||
26 | Requires: python-paramiko | 26 | Requires: python-paramiko >= 1.15.1 | ||
27 | Requires: python-pycurl | 27 | Requires: python-pycurl | ||
28 | Requires: python-setuptools | 28 | Requires: python-setuptools | ||
29 | Requires: python-urlgrabber | 29 | Requires: python-urlgrabber | ||
30 | Requires: python-yamlish >= 0.18 | 30 | Requires: python-yamlish >= 0.18 | ||
31 | Requires: PyYAML >= 3.11 | 31 | Requires: PyYAML >= 3.11 | ||
32 | Requires: resultsdb_api >= 1.2.1 | 32 | Requires: resultsdb_api >= 1.2.1 | ||
33 | Requires: rpm-python | 33 | Requires: rpm-python | ||
34 | BuildRequires: koji | 34 | BuildRequires: koji | ||
▲ Show 20 Lines • Show All 181 Lines • Show Last 20 Lines |
Show First 20 Lines • Show All 60 Lines • ▼ Show 20 Line(s) | 23 | class Config(object): | |||
---|---|---|---|---|---|
61 | 61 | | |||
62 | bodhi_posting_comments_span = 4320 #: | 62 | bodhi_posting_comments_span = 4320 #: | ||
63 | # 3 days (3*24*60 = 4320) | 63 | # 3 days (3*24*60 = 4320) | ||
64 | bodhi_request_max_retries = 3 #: | 64 | bodhi_request_max_retries = 3 #: | ||
65 | 65 | | |||
66 | tmpdir = '/var/tmp/taskotron' #: | 66 | tmpdir = '/var/tmp/taskotron' #: | ||
67 | logdir = '/var/log/taskotron' #: | 67 | logdir = '/var/log/taskotron' #: | ||
68 | client_taskdir = '/var/tmp/taskotron/taskdir' #: | 68 | client_taskdir = '/var/tmp/taskotron/taskdir' #: | ||
69 | remote_log_name = 'taskotron-initiator.log' | 69 | initiator_log_name = 'taskotron-initiator.log' | ||
70 | '''name of a log file when running in disposable clients mode | 70 | '''name of a log file when running in disposable clients mode | ||
71 | in :attr:`artifacts_dir`/<uuid>''' | 71 | in :attr:`artifacts_dir`/<uuid>''' | ||
72 | remote_stdio_name = 'stdio.log' | 72 | remote_stdio_name = 'stdio.log' | ||
73 | '''name of a file containing stdio of disposable client | 73 | '''name of a file containing stdio of disposable client | ||
74 | in :attr:`artifacts_dir`/<uuid>''' | 74 | in :attr:`artifacts_dir`/<uuid>''' | ||
75 | log_name = 'taskotron.log' | 75 | log_name = 'taskotron.log' | ||
76 | '''name of the main log file in :attr:`logdir`''' | 76 | '''name of the main log file in :attr:`logdir`''' | ||
77 | artifactsdir = '/var/lib/taskotron/artifacts' #: | 77 | artifactsdir = '/var/lib/taskotron/artifacts' #: | ||
▲ Show 20 Lines • Show All 45 Lines • Show Last 20 Lines |
Show All 23 Lines | 22 | class Tee(object): | |||
---|---|---|---|---|---|
24 | 24 | | |||
25 | def __init__(self, *files): | 25 | def __init__(self, *files): | ||
26 | self._files = list(files) | 26 | self._files = list(files) | ||
27 | 27 | | |||
28 | def add(self, file_): | 28 | def add(self, file_): | ||
29 | if file_.mode.startswith('w'): | 29 | if file_.mode.startswith('w'): | ||
30 | self._files.append(file_) | 30 | self._files.append(file_) | ||
31 | else: | 31 | else: | ||
32 | log.warning('File not opened for writing. Not adding.') | 32 | name = file_.name if hasattr(file_, 'name') else '<unnamed file>' | ||
33 | log.warning('File %s not opened for writing. Not adding.', name) | ||||
33 | 34 | | |||
34 | def write(self, data): | 35 | def write(self, data): | ||
35 | for f in self._files: | 36 | for f in self._files: | ||
36 | f.write(data) | 37 | f.write(data) | ||
37 | 38 | | |||
38 | def close(self): | 39 | def close(self): | ||
39 | for f in self._files: | 40 | for f in self._files: | ||
40 | if f is not sys.stdout: | 41 | if f is not sys.stdout: | ||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Line(s) | 78 | try: | |||
82 | 83 | | |||
83 | self.sftp = self.ssh.open_sftp() | 84 | self.sftp = self.ssh.open_sftp() | ||
84 | 85 | | |||
85 | if self.stdio_filename is not None: | 86 | if self.stdio_filename is not None: | ||
86 | try: | 87 | try: | ||
87 | f = open(self.stdio_filename, 'w') | 88 | f = open(self.stdio_filename, 'w') | ||
88 | self.outstream.add(f) | 89 | self.outstream.add(f) | ||
89 | except IOError, e: | 90 | except IOError, e: | ||
90 | log.warning('Could not open %s. Falling back to writing vm\'s output to stdout only.') | 91 | log.warning('Could not open %s. Falling back to writing vm\'s output to stdout only.', | ||
92 | self.stdio_filename) | ||||
91 | 93 | | |||
92 | except paramiko.BadHostKeyException, e: | 94 | except paramiko.BadHostKeyException, e: | ||
93 | raise exc.TaskotronRemoteError('Server\'s (%s@%s:%s) hostkey could not be verified: %s' % | 95 | raise exc.TaskotronRemoteError('Server\'s (%s@%s:%s) hostkey could not be verified: %s' % | ||
94 | (self.username, self.hostname, self.port, str(e))) | 96 | (self.username, self.hostname, self.port, str(e))) | ||
95 | except paramiko.AuthenticationException, e: | 97 | except paramiko.AuthenticationException, e: | ||
96 | raise exc.TaskotronRemoteError('Authentication to %s@%s:%s failed: %s' % | 98 | raise exc.TaskotronRemoteError('Authentication to %s@%s:%s failed: %s' % | ||
97 | (self.username, self.hostname, self.port, str(e))) | 99 | (self.username, self.hostname, self.port, str(e))) | ||
98 | except (paramiko.SSHException, socket.error), e: | 100 | except (paramiko.SSHException, socket.error), e: | ||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Line(s) | |||||
222 | 224 | | |||
223 | def _remote_isdir(self, remote_path): | 225 | def _remote_isdir(self, remote_path): | ||
224 | try: | 226 | try: | ||
225 | return S_ISDIR(self.sftp.stat(remote_path).st_mode) | 227 | return S_ISDIR(self.sftp.stat(remote_path).st_mode) | ||
226 | except IOError: | 228 | except IOError: | ||
227 | return False | 229 | return False | ||
228 | 230 | | |||
229 | def mkdir(self, name, mode=777): | 231 | def mkdir(self, name, mode=777): | ||
230 | '''Get a directory from a remote path. | 232 | '''Create a directory on a remote machine. | ||
231 | 233 | | |||
232 | :param str name: A name of the directory | 234 | :param str name: A name of the directory | ||
233 | :param int mode: Permissions (posix-style, decimal) of the directory, | 235 | :param int mode: Permissions (posix-style, decimal) of the directory, | ||
234 | default is 777 | 236 | default is 777 | ||
235 | 237 | | |||
236 | :raise TaskotronRemoteError: If the directory could not be created | 238 | :raise TaskotronRemoteError: If the directory could not be created | ||
237 | ''' | 239 | ''' | ||
238 | 240 | | |||
239 | self.cmd('mkdir -p -m %d %s' % (mode, name)) | 241 | self.cmd('mkdir -p -m %d %s' % (mode, name)) | ||
240 | 242 | | |||
241 | def put_dir(self, local_path, remote_path, overwrite=True): | 243 | def put_dir(self, local_path, remote_path, overwrite=True): | ||
242 | '''Get a directory from a remote path. | 244 | '''Copy a directory to a remote path. | ||
243 | 245 | | |||
244 | :param str remote_path: A path to the remote directory | 246 | :param str remote_path: A path to the remote directory | ||
245 | :param str local_path: A path to the local directory | 247 | :param str local_path: A path to the local directory | ||
246 | :param bool overwrite: Whether to overwrite remote path. Default is True. | 248 | :param bool overwrite: Whether to overwrite remote path. Default is True. | ||
247 | 249 | | |||
248 | :raise TaskotronRemoteError: If the directory could not be uploaded | 250 | :raise TaskotronRemoteError: If the directory could not be uploaded | ||
249 | ''' | 251 | ''' | ||
250 | 252 | | |||
▲ Show 20 Lines • Show All 45 Lines • Show Last 20 Lines |
Show All 22 Lines | |||||
23 | from libtaskotron import remote_exec | 23 | from libtaskotron import remote_exec | ||
24 | from libtaskotron.logger import log | 24 | from libtaskotron.logger import log | ||
25 | import libtaskotron.exceptions as exc | 25 | import libtaskotron.exceptions as exc | ||
26 | 26 | | |||
27 | 27 | | |||
28 | # The list of accepted item types on the command line (--type option) | 28 | # The list of accepted item types on the command line (--type option) | ||
29 | _ITEM_TYPES = ["bodhi_id", "koji_build", "koji_tag", "compose"] | 29 | _ITEM_TYPES = ["bodhi_id", "koji_build", "koji_tag", "compose"] | ||
30 | 30 | | |||
31 | CONF = config.get_config() | | |||
32 | 31 | | |||
33 | def _create_uuid_dir(uuid): | 32 | def _create_artifacts_dir(uuid): | ||
34 | uuid_dir = os.path.join(CONF.artifactsdir, uuid) | 33 | artifacts_dir = os.path.join(config.get_config().artifactsdir, uuid) | ||
35 | 34 | | |||
36 | try: | 35 | try: | ||
37 | file_utils.makedirs(uuid_dir) | 36 | file_utils.makedirs(artifacts_dir) | ||
38 | log.info("Task artifacts will be saved in: %s", uuid_dir) | 37 | log.info("Task artifacts will be saved in: %s", artifacts_dir) | ||
39 | except OSError, e: | 38 | except OSError, e: | ||
40 | log.error("Can't create artifacts directory %s", uuid_dir) | 39 | log.error("Can't create artifacts directory %s", artifacts_dir) | ||
41 | raise exc.TaskotronError(e) | 40 | raise exc.TaskotronError(e) | ||
42 | 41 | | |||
43 | 42 | | |||
44 | class Runner(object): | 43 | class Runner(object): | ||
45 | def __init__(self, taskdata, argdata, workdir=None): | 44 | def __init__(self, taskdata, argdata, workdir=None): | ||
46 | self.taskdata = taskdata | 45 | self.taskdata = taskdata | ||
47 | self.envdata = argdata | 46 | self.envdata = argdata | ||
48 | self.working_data = {} | 47 | self.working_data = {} | ||
49 | self.directives = {} | 48 | self.directives = {} | ||
50 | self.workdir = workdir | 49 | self.workdir = workdir | ||
51 | 50 | | |||
52 | def run(self): | 51 | def run(self): | ||
53 | self._validate_input() | 52 | self._validate_input() | ||
54 | 53 | | |||
55 | if not self.workdir: # create temporary workdir if needed | 54 | if not self.workdir: # create temporary workdir if needed | ||
56 | self.workdir = tempfile.mkdtemp(prefix="task-", | 55 | self.workdir = tempfile.mkdtemp(prefix="task-", | ||
57 | dir=CONF.tmpdir) | 56 | dir=config.get_config().tmpdir) | ||
58 | log.debug("Current workdir: %s", self.workdir) | 57 | log.debug("Current workdir: %s", self.workdir) | ||
59 | self.envdata['workdir'] = self.workdir | 58 | self.envdata['workdir'] = self.workdir | ||
60 | self.envdata['checkname'] = self.taskdata['name'] | 59 | self.envdata['checkname'] = self.taskdata['name'] | ||
61 | self.envdata['artifactsdir'] = os.path.join(CONF.artifactsdir, self.envdata['uuid']) | 60 | self.envdata['artifactsdir'] = os.path.join(config.get_config().artifactsdir, | ||
61 | self.envdata['uuid']) | ||||
62 | 62 | | |||
63 | # override variable values | 63 | # override variable values | ||
64 | for var, val in self.envdata['override']: | 64 | for var, val in self.envdata['override']: | ||
65 | log.debug("Overriding variable %s, new value: %s", var, val) | 65 | log.debug("Overriding variable %s, new value: %s", var, val) | ||
66 | self.envdata[var] = eval(val, {}, {}) | 66 | self.envdata[var] = eval(val, {}, {}) | ||
67 | 67 | | |||
68 | self.do_actions() | 68 | self.do_actions() | ||
69 | 69 | | |||
▲ Show 20 Lines • Show All 107 Lines • ▼ Show 20 Line(s) | 176 | raise NotImplementedError("Environment validation is not" | |||
177 | " yet implemented") | 177 | " yet implemented") | ||
178 | 178 | | |||
179 | 179 | | |||
180 | class RemoteRunner(object): | 180 | class RemoteRunner(object): | ||
181 | def __init__(self, ssh, task_data, arg_data): | 181 | def __init__(self, ssh, task_data, arg_data): | ||
182 | self.ssh = ssh | 182 | self.ssh = ssh | ||
183 | self.task_data = task_data | 183 | self.task_data = task_data | ||
184 | self.arg_data = arg_data | 184 | self.arg_data = arg_data | ||
185 | self.taskdir = CONF.client_taskdir | 185 | self.taskdir = config.get_config().client_taskdir | ||
186 | 186 | | |||
187 | def prepare_task(self): | 187 | def prepare_task(self): | ||
188 | # FIXME remove this once the vm code is done | 188 | # FIXME remove this once the vm code is done | ||
189 | self.ssh.install_pkgs('wget') | 189 | self.ssh.install_pkgs('wget') | ||
190 | self.ssh.cmd('wget -O /etc/yum.repos.d/tflink-taskotron-fedora-f21.repo http://copr-fe.cloud.fedoraproject.org/coprs/tflink/taskotron/repo/fedora-21/tflink-taskotron-fedora-21.repo') | 190 | self.ssh.cmd('wget -O /etc/yum.repos.d/tflink-taskotron-fedora-f21.repo ' | ||
191 | 'http://copr-fe.cloud.fedoraproject.org/coprs/tflink/taskotron/' | ||||
192 | 'repo/fedora-21/tflink-taskotron-fedora-21.repo') | ||||
191 | self.ssh.install_pkgs('libtaskotron') | 193 | self.ssh.install_pkgs('libtaskotron') | ||
192 | # | 194 | # | ||
193 | 195 | | |||
194 | # install required packages | 196 | # install required packages | ||
195 | rpms = self.task_data['environment']['rpm'] | 197 | rpms = self.task_data['environment']['rpm'] | ||
196 | self.ssh.install_pkgs(rpms) | 198 | self.ssh.install_pkgs(rpms) | ||
197 | 199 | | |||
198 | # configure libtaskotron | 200 | # configure libtaskotron | ||
199 | if CONF.config_filename: | 201 | if config.get_config().config_filename: | ||
200 | self.ssh.put_file(CONF.config_filename, '/etc/taskotron/taskotron.yaml') | 202 | self.ssh.put_file(config.get_config().config_filename, '/etc/taskotron/taskotron.yaml') | ||
201 | 203 | | |||
202 | # patch remote libtaskotron if needed (for dev, shouldn't be used in production) | 204 | # patch remote libtaskotron if needed (for dev, shouldn't be used in production) | ||
203 | if self.arg_data['patch'] is not None: | 205 | if self.arg_data['patch'] is not None: | ||
204 | self.ssh.put_file(os.path.abspath(self.arg_data['patch']), '%s/%s' % (self.taskdir, self.arg_data['patch'])) | 206 | self.ssh.put_file(os.path.abspath(self.arg_data['patch']), '%s/%s' % (self.taskdir, self.arg_data['patch'])) | ||
205 | self.ssh.install_pkgs('patch') | 207 | self.ssh.install_pkgs('patch') | ||
206 | self.ssh.cmd('patch -d /usr/lib/python2.7/site-packages/ -p1 -i %s/%s' % (self.taskdir, self.arg_data['patch'])) | 208 | self.ssh.cmd('patch -d /usr/lib/python2.7/site-packages/ -p1 -i %s/%s' % (self.taskdir, self.arg_data['patch'])) | ||
207 | 209 | | |||
208 | # create needed dirs | 210 | # create needed dirs | ||
Show All 17 Lines | 223 | task_cmd = ['cd %s;' % self.taskdir, | |||
226 | self.arg_data['item'], # XXX what if item is "; rm -rf /;"? | 228 | self.arg_data['item'], # XXX what if item is "; rm -rf /;"? | ||
227 | '--uuid', | 229 | '--uuid', | ||
228 | self.arg_data['uuid'], | 230 | self.arg_data['uuid'], | ||
229 | os.path.basename(self.arg_data['taskfile'])] | 231 | os.path.basename(self.arg_data['taskfile'])] | ||
230 | 232 | | |||
231 | self.ssh.cmd(' '.join(task_cmd)) | 233 | self.ssh.cmd(' '.join(task_cmd)) | ||
232 | 234 | | |||
233 | def get_output(self): | 235 | def get_output(self): | ||
234 | artifacts_dir = os.path.join(CONF.artifactsdir, self.arg_data['uuid']) | 236 | conf = config.get_config() | ||
235 | logfile = os.path.join(CONF.logdir, CONF.log_name) | 237 | artifacts_dir = os.path.join(conf.artifactsdir, self.arg_data['uuid']) | ||
236 | local_logfile = os.path.join(artifacts_dir, CONF.log_name) | 238 | logfile = os.path.join(conf.logdir, conf.log_name) | ||
239 | local_logfile = os.path.join(artifacts_dir, conf.log_name) | ||||
237 | 240 | | |||
238 | # copy taskotron.log from vm | 241 | # copy artifacts from vm | ||
239 | try: | 242 | try: | ||
240 | self.ssh.get_file(logfile, local_logfile) | 243 | self.ssh.get_dir(artifacts_dir, artifacts_dir) | ||
241 | except exc.TaskotronRemoteError, e: | 244 | except exc.TaskotronRemoteError, e: | ||
242 | log.info(e) | 245 | log.info(e) | ||
243 | 246 | | |||
244 | # copy artifacts from vm | 247 | # copy taskotron.log from vm | ||
245 | try: | 248 | try: | ||
246 | self.ssh.get_dir(artifacts_dir, artifacts_dir) | 249 | self.ssh.get_file(logfile, local_logfile) | ||
247 | except exc.TaskotronRemoteError, e: | 250 | except exc.TaskotronRemoteError, e: | ||
248 | log.info(e) | 251 | log.info(e) | ||
249 | 252 | | |||
250 | def get_argparser(): | 253 | def get_argparser(): | ||
251 | parser = argparse.ArgumentParser() | 254 | parser = argparse.ArgumentParser() | ||
252 | parser.add_argument("task", nargs=1, help="task to run") | 255 | parser.add_argument("task", nargs=1, help="task to run") | ||
253 | parser.add_argument("-a", "--arch", | 256 | parser.add_argument("-a", "--arch", | ||
254 | choices=["i386", "x86_64", "armhfp", "noarch"], | 257 | choices=["i386", "x86_64", "armhfp", "noarch"], | ||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Line(s) | 315 | def main(): | |||
330 | 333 | | |||
331 | # parse task formula | 334 | # parse task formula | ||
332 | task_data = taskformula.parse_yaml_from_file(arg_data['taskfile']) | 335 | task_data = taskformula.parse_yaml_from_file(arg_data['taskfile']) | ||
333 | if not task_data: | 336 | if not task_data: | ||
334 | raise exc.TaskotronYamlError('Input file should not be empty') | 337 | raise exc.TaskotronYamlError('Input file should not be empty') | ||
335 | 338 | | |||
336 | # decide whether to run on disposable client | 339 | # decide whether to run on disposable client | ||
337 | try: | 340 | try: | ||
338 | remote = task_data['environment']['machine'] is not None | 341 | run_remotely = task_data['environment']['machine'] is not None | ||
339 | except KeyError: | 342 | except KeyError: | ||
340 | remote = False | 343 | run_remotely = False | ||
341 | 344 | | |||
342 | _create_uuid_dir(arg_data['uuid']) | 345 | _create_artifacts_dir(arg_data['uuid']) | ||
343 | 346 | | |||
344 | level_stream = logging.DEBUG if args.debug else None | 347 | level_stream = logging.DEBUG if args.debug else None | ||
345 | 348 | | |||
346 | if remote: | 349 | if run_remotely: | ||
350 | conf = config.get_config() | ||||
347 | logger.init(level_stream=level_stream, filelog=True, | 351 | logger.init(level_stream=level_stream, filelog=True, | ||
348 | filelog_path=os.path.join(CONF.artifactsdir, arg_data['uuid'], CONF.remote_log_name)) | 352 | filelog_path=os.path.join(conf.artifactsdir, arg_data['uuid'], conf.initiator_log_name)) | ||
349 | 353 | | |||
350 | log.info("Running Task in VM") | 354 | log.info("Running Task in VM") | ||
351 | # FIXME remove hardcoded ip when the vm code is done | 355 | # FIXME remove hardcoded ip when the vm code is done | ||
352 | stdio_filename = os.path.join(CONF.artifactsdir, arg_data['uuid'], CONF.remote_stdio_name) | 356 | stdio_filename = os.path.join(conf.artifactsdir, arg_data['uuid'], conf.remote_stdio_name) | ||
353 | with remote_exec.RemoteExec('192.168.122.99', 22, 'root', 'conf/id_taskotron', stdio_filename) as ssh: | 357 | with remote_exec.RemoteExec('192.168.122.99', 22, 'root', 'conf/id_taskotron', stdio_filename) as ssh: | ||
354 | task_runner = RemoteRunner(ssh, task_data, arg_data) | 358 | task_runner = RemoteRunner(ssh, task_data, arg_data) | ||
355 | try: | 359 | try: | ||
356 | task_runner.prepare_task() | 360 | task_runner.prepare_task() | ||
357 | task_runner.run() | 361 | task_runner.run() | ||
358 | except exc.TaskotronRemoteError, e: | 362 | except exc.TaskotronRemoteError, e: | ||
359 | log.exception(e) | 363 | log.exception(e) | ||
360 | task_runner.get_output() | 364 | task_runner.get_output() | ||
Show All 9 Lines |
Show All 14 Lines | |||||
15 | 15 | | |||
16 | from libtaskotron import remote_exec | 16 | from libtaskotron import remote_exec | ||
17 | from libtaskotron.exceptions import TaskotronRemoteError | 17 | from libtaskotron.exceptions import TaskotronRemoteError | ||
18 | 18 | | |||
19 | 19 | | |||
20 | STUB_DIR = 'dirname' | 20 | STUB_DIR = 'dirname' | ||
21 | STUB_FILE = 'filename' | 21 | STUB_FILE = 'filename' | ||
22 | STUB_NONEXISTING_FILE = '' | 22 | STUB_NONEXISTING_FILE = '' | ||
23 | # stub for a file that is too large (no space left on a device) | ||||
23 | STUB_ENOSPC = 1 | 24 | STUB_ENOSPC = 1 | ||
25 | # file mode | ||||
24 | STUB_REF_MODE = None | 26 | STUB_REF_MODE = None | ||
25 | STUB_NONEXISTING_HOST1 = 'ip1' | 27 | STUB_NONEXISTING_HOST1 = 'ip1' | ||
26 | STUB_NONEXISTING_HOST2 = 'ip2' | 28 | STUB_NONEXISTING_HOST2 = 'ip2' | ||
27 | STUB_NONEXISTING_USER = 'foo' | 29 | STUB_NONEXISTING_USER = 'foo' | ||
28 | STUB_NONWRITABLE_FILE = 'permissiondenied' | 30 | STUB_NONWRITABLE_FILE = 'permissiondenied' | ||
29 | STUB_CMD_TRUE = 0 | 31 | STUB_CMD_TRUE = 0 | ||
30 | STUB_CMD_FALSE = 1 | 32 | STUB_CMD_FALSE = 1 | ||
31 | 33 | | |||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Line(s) | 82 | if localpath == STUB_ENOSPC: | |||
81 | raise IOError | 83 | raise IOError | ||
82 | 84 | | |||
83 | return None | 85 | return None | ||
84 | 86 | | |||
85 | def stat(self, path): | 87 | def stat(self, path): | ||
86 | attrs = paramiko.SFTPAttributes() | 88 | attrs = paramiko.SFTPAttributes() | ||
87 | 89 | | |||
88 | if path == STUB_DIR: | 90 | if path == STUB_DIR: | ||
89 | attrs.st_mode = 16893 | 91 | attrs.st_mode = 16893 # mode of a directory | ||
90 | elif path == STUB_FILE: | 92 | elif path == STUB_FILE: | ||
91 | attrs.st_mode = 33204 | 93 | attrs.st_mode = 33204 # mode of a file | ||
92 | elif path == STUB_NONEXISTING_FILE: | 94 | elif path == STUB_NONEXISTING_FILE: | ||
93 | raise IOError(errno.ENOENT, 'No such file') | 95 | raise IOError(errno.ENOENT, 'No such file') | ||
94 | 96 | | |||
95 | return attrs | 97 | return attrs | ||
96 | 98 | | |||
97 | def listdir(self, path): | 99 | def listdir(self, path): | ||
98 | if not path == STUB_DIR: | 100 | if not path == STUB_DIR: | ||
99 | raise IOError | 101 | raise IOError | ||
▲ Show 20 Lines • Show All 147 Lines • Show Last 20 Lines |