blob: abbd89fdb295b1711f21cd863131bfe654909e2c [file] [log] [blame]
Ievgeniia Zadorozhna84023022021-12-30 13:00:41 +02001from io import StringIO
2import logging
3import select
4import utils
5import paramiko
6import time
7import os
8
9logger = logging.getLogger(__name__)
10
11# Suppress paramiko logging
12logging.getLogger("paramiko").setLevel(logging.WARNING)
13
14
15class SSHTransport(object):
16 def __init__(self, address, username, password=None,
17 private_key=None, look_for_keys=False, *args, **kwargs):
18
19 self.address = address
20 self.username = username
21 self.password = password
22 if private_key is not None:
23 self.private_key = paramiko.RSAKey.from_private_key(
24 StringIO(private_key))
25 else:
26 self.private_key = None
27
28 self.look_for_keys = look_for_keys
29 self.buf_size = 1024
30 self.channel_timeout = 10.0
31
32 def _get_ssh_connection(self):
33 ssh = paramiko.SSHClient()
34 ssh.set_missing_host_key_policy(
35 paramiko.AutoAddPolicy())
36 ssh.connect(self.address, username=self.username,
37 password=self.password, pkey=self.private_key,
38 timeout=self.channel_timeout)
39 logger.debug("Successfully connected to: {0}".format(self.address))
40 return ssh
41
42 def _get_sftp_connection(self):
43 transport = paramiko.Transport((self.address, 22))
44 transport.connect(username=self.username,
45 password=self.password,
46 pkey=self.private_key)
47
48 return paramiko.SFTPClient.from_transport(transport)
49
50 def exec_sync(self, cmd):
51 logger.debug("Executing {0} on host {1}".format(cmd, self.address))
52 ssh = self._get_ssh_connection()
53 transport = ssh.get_transport()
54 channel = transport.open_session()
55 channel.fileno()
56 channel.exec_command(cmd)
57 channel.shutdown_write()
58 out_data = []
59 err_data = []
60 poll = select.poll()
61 poll.register(channel, select.POLLIN)
62
63 while True:
64 ready = poll.poll(self.channel_timeout)
65 if not any(ready):
66 continue
67 if not ready[0]:
68 continue
69 out_chunk = err_chunk = None
70 if channel.recv_ready():
71 out_chunk = channel.recv(self.buf_size)
72 out_data += out_chunk,
73 if channel.recv_stderr_ready():
74 err_chunk = channel.recv_stderr(self.buf_size)
75 err_data += err_chunk,
76 if channel.closed and not err_chunk and not out_chunk:
77 break
78 exit_status = channel.recv_exit_status()
79 logger.debug("Command {0} executed with status: {1}"
80 .format(cmd, exit_status))
81 return (exit_status, b" ".join(out_data).strip(),
82 b" ".join(err_data).strip())
83
84 def exec_command(self, cmd):
85 exit_status, stdout, stderr = self.exec_sync(cmd)
86 return stdout
87
88 def check_call(self, command, error_info=None, expected=None,
89 raise_on_err=True):
90 """Execute command and check for return code
91 :type command: str
92 :type error_info: str
93 :type expected: list
94 :type raise_on_err: bool
95 :rtype: ExecResult
96 :raises: DevopsCalledProcessError
97 """
98 if expected is None:
99 expected = [0]
100 ret = self.exec_sync(command)
101 exit_code, stdout_str, stderr_str = ret
102 if exit_code not in expected:
103 message = (
104 "{append}Command '{cmd}' returned exit code {code} while "
105 "expected {expected}\n"
106 "\tSTDOUT:\n"
107 "{stdout}"
108 "\n\tSTDERR:\n"
109 "{stderr}".format(
110 append=error_info + '\n' if error_info else '',
111 cmd=command,
112 code=exit_code,
113 expected=expected,
114 stdout=stdout_str,
115 stderr=stderr_str
116 ))
117 logger.error(message)
118 if raise_on_err:
119 exit()
120 return ret
121
122 def put_file(self, source_path, destination_path):
123 sftp = self._get_sftp_connection()
124 sftp.put(source_path, destination_path)
125 sftp.close()
126
127 def put_iperf3_deb_packages_at_vms(self, source_directory,
128 destination_directory):
Ievgeniia Zadorozhna5ed74e22022-07-26 16:56:23 +0300129 required_packages = ['iperf', 'iperf3', 'libiperf0', 'libsctp1']
Ievgeniia Zadorozhnae1426742022-06-16 17:27:24 +0300130 iperf_deb_files = [pack for pack in os.listdir(source_directory) if
131 any(req in pack for req in required_packages) if
132 pack.endswith('.deb')]
Ievgeniia Zadorozhna84023022021-12-30 13:00:41 +0200133 if not iperf_deb_files:
134 raise BaseException(
Ievgeniia Zadorozhna5ed74e22022-07-26 16:56:23 +0300135 "iperf3 or iperf *.deb packages are not found locally at path"
136 " {}. Please recheck 'iperf_deb_package_dir_path' variable in "
Ievgeniia Zadorozhna84023022021-12-30 13:00:41 +0200137 "global_config.yaml and check *.deb packages are manually "
138 "copied there.".format(source_directory))
139 for f in iperf_deb_files:
140 source_abs_path = "{}/{}".format(source_directory, f)
141 dest_abs_path = "{}/{}".format(destination_directory, f)
142 self.put_file(source_abs_path, dest_abs_path)
143
144 def get_file(self, source_path, destination_path):
145 sftp = self._get_sftp_connection()
146 sftp.get(source_path, destination_path)
147 sftp.close()
148
149 def _is_timed_out(self, start_time, timeout):
150 return (time.time() - timeout) > start_time
151
152 def check_vm_is_reachable_ssh(self, floating_ip, timeout=500, sleep=5):
153 bsleep = sleep
154 ssh = paramiko.SSHClient()
155 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
156 _start_time = time.time()
157 attempts = 0
158 while True:
159 try:
160 ssh.connect(floating_ip, username=self.username,
161 password=self.password, pkey=self.private_key,
162 timeout=self.channel_timeout)
163 logger.info("VM with FIP {} is reachable via SSH. Success!"
164 "".format(floating_ip))
165 return True
166 except Exception as e:
167 ssh.close()
168 if self._is_timed_out(_start_time, timeout):
169 logger.info("VM with FIP {} is not reachable via SSH. "
170 "See details: {}".format(floating_ip, e))
171 raise TimeoutError(
172 "\nFailed to establish authenticated ssh connection "
173 "to {} after {} attempts during {} seconds.\n{}"
174 "".format(floating_ip, attempts, timeout, e))
175 attempts += 1
176 logger.info("Failed to establish authenticated ssh connection "
177 "to {}. Number attempts: {}. Retry after {} "
178 "seconds.".format(floating_ip, attempts, bsleep))
179 time.sleep(bsleep)
180
Ievgeniia Zadorozhna0facf3c2022-06-16 16:19:09 +0300181 def get_mtu_from_vm(self, floating_ip, user='ubuntu', password='password',
182 private_key=None):
183 transport = SSHTransport(floating_ip, user, password, private_key)
184 iface = (transport.exec_command(
185 'ls /sys/class/net | grep -v lo | head -n 1')).decode("utf-8")
186 mtu = transport.exec_command('cat /sys/class/net/{}/mtu'.format(iface))
187 return mtu.decode("utf-8")
Ievgeniia Zadorozhna84023022021-12-30 13:00:41 +0200188
Ievgeniia Zadorozhna5ed74e22022-07-26 16:56:23 +0300189
Ievgeniia Zadorozhna84023022021-12-30 13:00:41 +0200190class prepare_iperf(object):
191
192 def __init__(self, fip, user='ubuntu', password='password',
193 private_key=None):
194
195 transport = SSHTransport(fip, user, password, private_key)
196 config = utils.get_configuration()
197
Ievgeniia Zadorozhna5ed74e22022-07-26 16:56:23 +0300198 # Install iperf, iperf3 using apt or downloaded deb packages
Ievgeniia Zadorozhna84023022021-12-30 13:00:41 +0200199 internet_at_vms = utils.get_configuration().get("internet_at_vms")
Ievgeniia Zadorozhna2c6469d2022-08-10 17:21:10 +0300200 path_to_iperf_deb = (config.get('iperf_deb_package_dir_path') or
201 "/opt/packages/")
Ievgeniia Zadorozhna84023022021-12-30 13:00:41 +0200202 if internet_at_vms.lower() == 'false':
Ievgeniia Zadorozhna2c6469d2022-08-10 17:21:10 +0300203 logger.info("Copying offline iperf3 deb packages, installing...")
Ievgeniia Zadorozhna84023022021-12-30 13:00:41 +0200204 home_ubuntu = "/home/ubuntu/"
205 transport.put_iperf3_deb_packages_at_vms(path_to_iperf_deb,
206 home_ubuntu)
207 transport.exec_command('sudo dpkg -i {}*.deb'.format(home_ubuntu))
208 else:
Ievgeniia Zadorozhna5ed74e22022-07-26 16:56:23 +0300209 logger.info("Installing iperf, iperf3 using apt")
Ievgeniia Zadorozhna84023022021-12-30 13:00:41 +0200210 preparation_cmd = config.get('iperf_prep_string') or ['']
211 transport.exec_command(preparation_cmd)
212 transport.exec_command('sudo apt-get update;'
Ievgeniia Zadorozhna5ed74e22022-07-26 16:56:23 +0300213 'sudo apt-get install -y iperf3 iperf')
Ievgeniia Zadorozhna84023022021-12-30 13:00:41 +0200214
215 # Log whether iperf is installed with version
Ievgeniia Zadorozhna2c6469d2022-08-10 17:21:10 +0300216 check = transport.exec_command('dpkg -l | grep ii | grep iperf3')
Ievgeniia Zadorozhna5ed74e22022-07-26 16:56:23 +0300217 logger.info(check.decode('utf-8'))
Ievgeniia Zadorozhna2c6469d2022-08-10 17:21:10 +0300218 if not check:
219 if internet_at_vms.lower() == 'true':
220 info = "Please check the Internet access at VM."
221 else:
222 info = "Could not put offline iperf packages from {} to the " \
223 "VM.".format(path_to_iperf_deb)
224 raise BaseException("iperf3 is not installed at VM with FIP {}. {}"
225 "".format(fip, info))
Ievgeniia Zadorozhna84023022021-12-30 13:00:41 +0200226 # Staring iperf server
227 transport.exec_command('nohup iperf3 -s > file 2>&1 &')
Ievgeniia Zadorozhna5ed74e22022-07-26 16:56:23 +0300228 transport.exec_command('nohup iperf -s > file 2>&1 &')