blob: 44c8830d4792d8b23712f49837e7a2d4e4d80095 [file] [log] [blame]
Dennis Dmitriev6f59add2016-10-18 13:45:27 +03001# Copyright 2016 Mirantis, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
Tatyana Leontovichab47e162017-10-06 16:53:30 +030015import os
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030016import random
Artem Panchenkodb0a97f2017-06-27 19:09:13 +030017import StringIO
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030018
19from devops.helpers import helpers
20from devops.helpers import ssh_client
Dennis Dmitriev2dfb8ef2017-07-21 20:19:38 +030021from devops.helpers import subprocess_runner
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030022from paramiko import rsakey
Dennis Dmitriev99b26fe2017-04-26 12:34:44 +030023import yaml
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030024
25from tcp_tests import logger
Dennis Dmitrievd2604512018-06-04 05:34:44 +030026from tcp_tests import settings
Tatyana Leontovichab47e162017-10-06 16:53:30 +030027from tcp_tests.helpers import ext
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030028from tcp_tests.helpers import utils
29
30LOG = logger.logger
31
32
33class UnderlaySSHManager(object):
34 """Keep the list of SSH access credentials to Underlay nodes.
35
36 This object is initialized using config.underlay.ssh.
37
38 :param config_ssh: JSONList of SSH access credentials for nodes:
39 [
40 {
41 node_name: node1,
42 address_pool: 'public-pool01',
43 host: ,
44 port: ,
45 keys: [],
46 keys_source_host: None,
47 login: ,
48 password: ,
Dennis Dmitriev474e3f72016-10-21 16:46:09 +030049 roles: [],
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030050 },
51 {
52 node_name: node1,
53 address_pool: 'private-pool01',
54 host:
55 port:
56 keys: []
57 keys_source_host: None,
58 login:
59 password:
Dennis Dmitriev474e3f72016-10-21 16:46:09 +030060 roles: [],
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030061 },
62 {
63 node_name: node2,
64 address_pool: 'public-pool01',
65 keys_source_host: node1
66 ...
67 }
68 ,
69 ...
70 ]
71
72 self.node_names(): list of node names registered in underlay.
73 self.remote(): SSHClient object by a node name (w/wo address pool)
74 or by a hostname.
75 """
Dennis Dmitriev2a13a132016-11-04 00:56:23 +020076 __config = None
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030077 config_ssh = None
78 config_lvm = None
79
Dennis Dmitriev2a13a132016-11-04 00:56:23 +020080 def __init__(self, config):
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030081 """Read config.underlay.ssh object
82
83 :param config_ssh: dict
84 """
Dennis Dmitriev2a13a132016-11-04 00:56:23 +020085 self.__config = config
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030086 if self.config_ssh is None:
87 self.config_ssh = []
88
89 if self.config_lvm is None:
90 self.config_lvm = {}
91
Dennis Dmitriev2a13a132016-11-04 00:56:23 +020092 self.add_config_ssh(self.__config.underlay.ssh)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030093
94 def add_config_ssh(self, config_ssh):
95
96 if config_ssh is None:
97 config_ssh = []
98
99 for ssh in config_ssh:
100 ssh_data = {
101 # Required keys:
102 'node_name': ssh['node_name'],
103 'host': ssh['host'],
104 'login': ssh['login'],
105 'password': ssh['password'],
106 # Optional keys:
107 'address_pool': ssh.get('address_pool', None),
108 'port': ssh.get('port', None),
109 'keys': ssh.get('keys', []),
Dennis Dmitriev474e3f72016-10-21 16:46:09 +0300110 'roles': ssh.get('roles', []),
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300111 }
112
113 if 'keys_source_host' in ssh:
114 node_name = ssh['keys_source_host']
115 remote = self.remote(node_name)
116 keys = self.__get_keys(remote)
117 ssh_data['keys'].extend(keys)
118
119 self.config_ssh.append(ssh_data)
120
121 def remove_config_ssh(self, config_ssh):
122 if config_ssh is None:
123 config_ssh = []
124
125 for ssh in config_ssh:
126 ssh_data = {
127 # Required keys:
128 'node_name': ssh['node_name'],
129 'host': ssh['host'],
130 'login': ssh['login'],
131 'password': ssh['password'],
132 # Optional keys:
133 'address_pool': ssh.get('address_pool', None),
134 'port': ssh.get('port', None),
135 'keys': ssh.get('keys', []),
Dennis Dmitriev474e3f72016-10-21 16:46:09 +0300136 'roles': ssh.get('roles', []),
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300137 }
138 self.config_ssh.remove(ssh_data)
139
140 def __get_keys(self, remote):
141 keys = []
142 remote.execute('cd ~')
143 key_string = './.ssh/id_rsa'
144 if remote.exists(key_string):
145 with remote.open(key_string) as f:
146 keys.append(rsakey.RSAKey.from_private_key(f))
147 return keys
148
Tatyana Leontovichecd491d2017-09-13 13:51:12 +0300149 def __ssh_data(self, node_name=None, host=None, address_pool=None,
150 node_role=None):
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300151
152 ssh_data = None
153
154 if host is not None:
155 for ssh in self.config_ssh:
156 if host == ssh['host']:
157 ssh_data = ssh
158 break
159
160 elif node_name is not None:
161 for ssh in self.config_ssh:
162 if node_name == ssh['node_name']:
163 if address_pool is not None:
164 if address_pool == ssh['address_pool']:
165 ssh_data = ssh
166 break
167 else:
168 ssh_data = ssh
Tatyana Leontovichecd491d2017-09-13 13:51:12 +0300169 elif node_role is not None:
170 for ssh in self.config_ssh:
171 if node_role in ssh['roles']:
172 if address_pool is not None:
173 if address_pool == ssh['address_pool']:
174 ssh_data = ssh
175 break
176 else:
177 ssh_data = ssh
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300178 if ssh_data is None:
Dmitry Tyzhnenkob610afd2018-02-19 15:43:45 +0200179 LOG.debug("config_ssh - {}".format(self.config_ssh))
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300180 raise Exception('Auth data for node was not found using '
181 'node_name="{}" , host="{}" , address_pool="{}"'
182 .format(node_name, host, address_pool))
183 return ssh_data
184
185 def node_names(self):
186 """Get list of node names registered in config.underlay.ssh"""
187
188 names = [] # List is used to keep the original order of names
189 for ssh in self.config_ssh:
190 if ssh['node_name'] not in names:
191 names.append(ssh['node_name'])
192 return names
193
194 def enable_lvm(self, lvmconfig):
195 """Method for enabling lvm oh hosts in environment
196
197 :param lvmconfig: dict with ids or device' names of lvm storage
198 :raises: devops.error.DevopsCalledProcessError,
199 devops.error.TimeoutError, AssertionError, ValueError
200 """
201 def get_actions(lvm_id):
202 return [
203 "systemctl enable lvm2-lvmetad.service",
204 "systemctl enable lvm2-lvmetad.socket",
205 "systemctl start lvm2-lvmetad.service",
206 "systemctl start lvm2-lvmetad.socket",
207 "pvcreate {} && pvs".format(lvm_id),
208 "vgcreate default {} && vgs".format(lvm_id),
209 "lvcreate -L 1G -T default/pool && lvs",
210 ]
211 lvmpackages = ["lvm2", "liblvm2-dev", "thin-provisioning-tools"]
212 for node_name in self.node_names():
213 lvm = lvmconfig.get(node_name, None)
214 if not lvm:
215 continue
216 if 'id' in lvm:
217 lvmdevice = '/dev/disk/by-id/{}'.format(lvm['id'])
218 elif 'device' in lvm:
219 lvmdevice = '/dev/{}'.format(lvm['device'])
220 else:
221 raise ValueError("Unknown LVM device type")
222 if lvmdevice:
223 self.apt_install_package(
224 packages=lvmpackages, node_name=node_name, verbose=True)
225 for command in get_actions(lvmdevice):
226 self.sudo_check_call(command, node_name=node_name,
227 verbose=True)
228 self.config_lvm = dict(lvmconfig)
229
230 def host_by_node_name(self, node_name, address_pool=None):
231 ssh_data = self.__ssh_data(node_name=node_name,
232 address_pool=address_pool)
233 return ssh_data['host']
234
Tatyana Leontovichecd491d2017-09-13 13:51:12 +0300235 def host_by_node_role(self, node_role, address_pool=None):
236 ssh_data = self.__ssh_data(node_role=node_role,
237 address_pool=address_pool)
238 return ssh_data['host']
239
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200240 def remote(self, node_name=None, host=None, address_pool=None,
241 username=None):
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300242 """Get SSHClient by a node name or hostname.
243
244 One of the following arguments should be specified:
245 - host (str): IP address or hostname. If specified, 'node_name' is
246 ignored.
247 - node_name (str): Name of the node stored to config.underlay.ssh
248 - address_pool (str): optional for node_name.
249 If None, use the first matched node_name.
250 """
251 ssh_data = self.__ssh_data(node_name=node_name, host=host,
252 address_pool=address_pool)
Dmitry Tyzhnenko5a5d8da2017-12-14 14:14:42 +0200253 ssh_auth = ssh_client.SSHAuth(
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200254 username=username or ssh_data['login'],
Dmitry Tyzhnenko5a5d8da2017-12-14 14:14:42 +0200255 password=ssh_data['password'],
256 keys=[rsakey.RSAKey(file_obj=StringIO.StringIO(key))
257 for key in ssh_data['keys']])
258
Dennis Dmitrievd2604512018-06-04 05:34:44 +0300259 client = ssh_client.SSHClient(
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300260 host=ssh_data['host'],
261 port=ssh_data['port'] or 22,
Dmitry Tyzhnenko5a5d8da2017-12-14 14:14:42 +0200262 auth=ssh_auth)
Dennis Dmitrievd2604512018-06-04 05:34:44 +0300263 client._ssh.get_transport().set_keepalive(
264 settings.SSH_SERVER_ALIVE_INTERVAL)
265
266 return client
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300267
Dennis Dmitriev2dfb8ef2017-07-21 20:19:38 +0300268 def local(self):
269 """Get Subprocess instance for local operations like:
270
271 underlay.local.execute(command, verbose=False, timeout=None)
272 underlay.local.check_call(
273 command, verbose=False, timeout=None,
274 error_info=None, expected=None, raise_on_err=True)
275 underlay.local.check_stderr(
276 command, verbose=False, timeout=None,
277 error_info=None, raise_on_err=True)
278 """
279 return subprocess_runner.Subprocess()
280
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300281 def check_call(
282 self, cmd,
283 node_name=None, host=None, address_pool=None,
284 verbose=False, timeout=None,
285 error_info=None,
286 expected=None, raise_on_err=True):
287 """Execute command on the node_name/host and check for exit code
288
289 :type cmd: str
290 :type node_name: str
291 :type host: str
292 :type verbose: bool
293 :type timeout: int
294 :type error_info: str
295 :type expected: list
296 :type raise_on_err: bool
297 :rtype: list stdout
298 :raises: devops.error.DevopsCalledProcessError
299 """
300 remote = self.remote(node_name=node_name, host=host,
301 address_pool=address_pool)
302 return remote.check_call(
303 command=cmd, verbose=verbose, timeout=timeout,
304 error_info=error_info, expected=expected,
305 raise_on_err=raise_on_err)
306
307 def apt_install_package(self, packages=None, node_name=None, host=None,
308 **kwargs):
309 """Method to install packages on ubuntu nodes
310
311 :type packages: list
312 :type node_name: str
313 :type host: str
314 :raises: devops.error.DevopsCalledProcessError,
315 devops.error.TimeoutError, AssertionError, ValueError
316
317 Other params of check_call and sudo_check_call are allowed
318 """
319 expected = kwargs.pop('expected', None)
320 if not packages or not isinstance(packages, list):
321 raise ValueError("packages list should be provided!")
322 install = "apt-get install -y {}".format(" ".join(packages))
323 # Should wait until other 'apt' jobs are finished
324 pgrep_expected = [0, 1]
325 pgrep_command = "pgrep -a -f apt"
326 helpers.wait(
327 lambda: (self.check_call(
328 pgrep_command, expected=pgrep_expected, host=host,
329 node_name=node_name, **kwargs).exit_code == 1
330 ), interval=30, timeout=1200,
331 timeout_msg="Timeout reached while waiting for apt lock"
332 )
333 # Install packages
334 self.sudo_check_call("apt-get update", node_name=node_name, host=host,
335 **kwargs)
336 self.sudo_check_call(install, expected=expected, node_name=node_name,
337 host=host, **kwargs)
338
339 def sudo_check_call(
340 self, cmd,
341 node_name=None, host=None, address_pool=None,
342 verbose=False, timeout=None,
343 error_info=None,
344 expected=None, raise_on_err=True):
345 """Execute command with sudo on node_name/host and check for exit code
346
347 :type cmd: str
348 :type node_name: str
349 :type host: str
350 :type verbose: bool
351 :type timeout: int
352 :type error_info: str
353 :type expected: list
354 :type raise_on_err: bool
355 :rtype: list stdout
356 :raises: devops.error.DevopsCalledProcessError
357 """
358 remote = self.remote(node_name=node_name, host=host,
359 address_pool=address_pool)
360 with remote.get_sudo(remote):
361 return remote.check_call(
362 command=cmd, verbose=verbose, timeout=timeout,
363 error_info=error_info, expected=expected,
364 raise_on_err=raise_on_err)
365
366 def dir_upload(self, host, source, destination):
367 """Upload local directory content to remote host
368
369 :param host: str, remote node name
370 :param source: str, local directory path
371 :param destination: str, local directory path
372 """
373 with self.remote(node_name=host) as remote:
374 remote.upload(source, destination)
375
Dennis Dmitriev0f08d9a2017-12-19 02:27:59 +0200376 def get_random_node(self, node_names=None):
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300377 """Get random node name
378
Dennis Dmitriev0f08d9a2017-12-19 02:27:59 +0200379 :param node_names: list of strings
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300380 :return: str, name of node
381 """
Dennis Dmitriev0f08d9a2017-12-19 02:27:59 +0200382 return random.choice(node_names or self.node_names())
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300383
384 def yaml_editor(self, file_path, node_name=None, host=None,
385 address_pool=None):
386 """Returns an initialized YamlEditor instance for context manager
387
388 Usage (with 'underlay' fixture):
389
390 # Local YAML file
391 with underlay.yaml_editor('/path/to/file') as editor:
392 editor.content[key] = "value"
393
394 # Remote YAML file on TCP host
395 with underlay.yaml_editor('/path/to/file',
396 host=config.tcp.tcp_host) as editor:
397 editor.content[key] = "value"
398 """
399 # Local YAML file
400 if node_name is None and host is None:
401 return utils.YamlEditor(file_path=file_path)
402
403 # Remote YAML file
404 ssh_data = self.__ssh_data(node_name=node_name, host=host,
405 address_pool=address_pool)
406 return utils.YamlEditor(
407 file_path=file_path,
408 host=ssh_data['host'],
409 port=ssh_data['port'] or 22,
410 username=ssh_data['login'],
411 password=ssh_data['password'],
412 private_keys=ssh_data['keys'])
Dennis Dmitriev010f4cd2016-11-01 20:43:51 +0200413
Dennis Dmitriev99b26fe2017-04-26 12:34:44 +0300414 def read_template(self, file_path):
415 """Read yaml as a jinja template"""
416 options = {
417 'config': self.__config,
418 }
419 template = utils.render_template(file_path, options=options)
420 return yaml.load(template)
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300421
422 def get_logs(self, artifact_name,
423 node_role=ext.UNDERLAY_NODE_ROLES.salt_master):
Dennis Dmitriev21369672018-03-24 14:50:18 +0200424
425 # Prefix each '$' symbol with backslash '\' to disable
426 # early interpolation of environment variables on cfg01 node only
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200427 dump_commands = (
Dennis Dmitriev21369672018-03-24 14:50:18 +0200428 "mkdir /root/\$(hostname -f)/;"
429 "rsync -aruv /var/log/ /root/\$(hostname -f)/;"
430 "dpkg -l > /root/\$(hostname -f)/dump_dpkg_l.txt;"
431 "df -h > /root/\$(hostname -f)/dump_df.txt;"
432 "mount > /root/\$(hostname -f)/dump_mount.txt;"
433 "blkid -o list > /root/\$(hostname -f)/dump_blkid_o_list.txt;"
434 "iptables -t nat -S > /root/\$(hostname -f)/dump_iptables_nat.txt;"
435 "iptables -S > /root/\$(hostname -f)/dump_iptables.txt;"
436 "ps auxwwf > /root/\$(hostname -f)/dump_ps.txt;"
437 "docker images > /root/\$(hostname -f)/dump_docker_images.txt;"
438 "docker ps > /root/\$(hostname -f)/dump_docker_ps.txt;"
Mikhail Ivanov82da3392018-03-15 22:19:26 +0400439 "docker service ls > "
Dennis Dmitriev21369672018-03-24 14:50:18 +0200440 " /root/\$(hostname -f)/dump_docker_services_ls.txt;"
441 "for SERVICE in \$(docker service ls | awk '{ print $2 }'); "
442 " do docker service ps --no-trunc 2>&1 \$SERVICE >> "
443 " /root/\$(hostname -f)/dump_docker_service_ps.txt;"
Dennis Dmitriev01d5e372018-03-15 23:29:29 +0200444 " done;"
Dennis Dmitriev21369672018-03-24 14:50:18 +0200445 "for SERVICE in \$(docker service ls | awk '{ print $2 }'); "
446 " do docker service logs 2>&1 \$SERVICE > "
447 " /root/\$(hostname -f)/dump_docker_service_\${SERVICE}_logs;"
Dennis Dmitriev01d5e372018-03-15 23:29:29 +0200448 " done;"
Dennis Dmitriev21369672018-03-24 14:50:18 +0200449 "vgdisplay > /root/\$(hostname -f)/dump_vgdisplay.txt;"
450 "lvdisplay > /root/\$(hostname -f)/dump_lvdisplay.txt;"
451 "ip a > /root/\$(hostname -f)/dump_ip_a.txt;"
452 "ip r > /root/\$(hostname -f)/dump_ip_r.txt;"
453 "netstat -anp > /root/\$(hostname -f)/dump_netstat.txt;"
454 "brctl show > /root/\$(hostname -f)/dump_brctl_show.txt;"
455 "arp -an > /root/\$(hostname -f)/dump_arp.txt;"
456 "uname -a > /root/\$(hostname -f)/dump_uname_a.txt;"
457 "lsmod > /root/\$(hostname -f)/dump_lsmod.txt;"
458 "cat /proc/interrupts > /root/\$(hostname -f)/dump_interrupts.txt;"
459 "cat /etc/*-release > /root/\$(hostname -f)/dump_release.txt;"
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200460 # OpenStack specific, will fail on other nodes
Dennis Dmitriev21369672018-03-24 14:50:18 +0200461 # "rabbitmqctl report > "
462 # " /root/\$(hostname -f)/dump_rabbitmqctl.txt;"
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300463
Dennis Dmitriev21369672018-03-24 14:50:18 +0200464 # "ceph health > /root/\$(hostname -f)/dump_ceph_health.txt;"
465 # "ceph -s > /root/\$(hostname -f)/dump_ceph_s.txt;"
466 # "ceph osd tree > /root/\$(hostname -f)/dump_ceph_osd_tree.txt;"
Dennis Dmitriev0bc485b2017-12-13 12:49:54 +0200467
Dennis Dmitriev21369672018-03-24 14:50:18 +0200468 # "for ns in \$(ip netns list);"
469 # " do echo Namespace: \${ns}; ip netns exec \${ns} ip a;"
470 # "done > /root/\$(hostname -f)/dump_ip_a_ns.txt;"
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200471
Dennis Dmitriev21369672018-03-24 14:50:18 +0200472 # "for ns in \$(ip netns list);"
473 # " do echo Namespace: \${ns}; ip netns exec \${ns} ip r;"
474 # "done > /root/\$(hostname -f)/dump_ip_r_ns.txt;"
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200475
Dennis Dmitriev21369672018-03-24 14:50:18 +0200476 # "for ns in \$(ip netns list);"
477 # " do echo Namespace: \${ns}; ip netns exec \${ns} netstat -anp;"
478 # "done > /root/\$(hostname -f)/dump_netstat_ns.txt;"
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200479
480 "/usr/bin/haproxy-status.sh > "
Dennis Dmitriev21369672018-03-24 14:50:18 +0200481 " /root/\$(hostname -f)/dump_haproxy.txt;"
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200482
483 # Archive the files
484 "cd /root/; tar --absolute-names --warning=no-file-changed "
Dennis Dmitriev21369672018-03-24 14:50:18 +0200485 " -czf \$(hostname -f).tar.gz ./\$(hostname -f)/;"
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200486 )
487
488 master_host = self.__config.salt.salt_master_host
489 with self.remote(host=master_host) as master:
490 # dump files
491 LOG.info("Archive artifacts on all nodes")
Dennis Dmitrievc83b3d42018-03-16 00:59:18 +0200492 master.check_call('salt "*" cmd.run "{0}"'.format(dump_commands),
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200493 raise_on_err=False)
494
495 # create target dir for archives
496 master.check_call("mkdir /root/dump/")
497
498 # get archived artifacts to the master node
499 for node in self.config_ssh:
500 LOG.info("Getting archived artifacts from the node {0}"
Dennis Dmitriev0bc485b2017-12-13 12:49:54 +0200501 .format(node['node_name']))
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200502 master.check_call("rsync -aruv {0}:/root/*.tar.gz "
503 "/root/dump/".format(node['node_name']),
Dennis Dmitriev8feb2522018-03-28 19:17:04 +0300504 raise_on_err=False,
505 timeout=120)
Dennis Dmitriev0bc485b2017-12-13 12:49:54 +0200506
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200507 destination_name = '/root/{0}_dump.tar.gz'.format(artifact_name)
508 # Archive the artifacts from all nodes
509 master.check_call(
510 'cd /root/dump/;'
511 'tar --absolute-names --warning=no-file-changed -czf '
512 ' {0} ./'.format(destination_name))
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300513
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200514 # Download the artifact to the host
Dennis Dmitriev2d643bc2017-12-04 12:23:47 +0200515 LOG.info("Downloading the artifact {0}".format(destination_name))
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200516 master.download(destination=destination_name, target=os.getcwd())
Dennis Dmitriev2d643bc2017-12-04 12:23:47 +0200517
518 def delayed_call(
519 self, cmd,
520 node_name=None, host=None, address_pool=None,
521 verbose=True, timeout=5,
522 delay_min=None, delay_max=None):
523 """Delayed call of the specified command in background
524
525 :param delay_min: minimum delay in minutes before run
526 the command
527 :param delay_max: maximum delay in minutes before run
528 the command
529 The command will be started at random time in the range
530 from delay_min to delay_max in minutes from 'now'
531 using the command 'at'.
532
533 'now' is rounded to integer by 'at' command, i.e.:
534 now(28 min 59 sec) == 28 min 00 sec.
535
536 So, if delay_min=1 , the command may start in range from
537 1 sec to 60 sec.
538
539 If delay_min and delay_max are None, then the command will
540 be executed in the background right now.
541 """
542 time_min = delay_min or delay_max
543 time_max = delay_max or delay_min
544
545 delay = None
546 if time_min is not None and time_max is not None:
547 delay = random.randint(time_min, time_max)
548
549 delay_str = ''
550 if delay:
551 delay_str = " + {0} min".format(delay)
552
553 delay_cmd = "cat << EOF | at now {0}\n{1}\nEOF".format(delay_str, cmd)
554
555 self.check_call(delay_cmd, node_name=node_name, host=host,
556 address_pool=address_pool, verbose=verbose,
557 timeout=timeout)
558
559 def get_target_node_names(self, target='gtw01.'):
560 """Get all node names which names starts with <target>"""
561 return [node_name for node_name
562 in self.node_names()
563 if node_name.startswith(target)]