159 lines
5.6 KiB
Python
159 lines
5.6 KiB
Python
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import contextlib
|
|
import time
|
|
|
|
from hamcrest import assert_that, is_, greater_than, has_value
|
|
from stepler.third_party import ping
|
|
from stepler.third_party import tcpdump
|
|
from stepler.third_party import waiter
|
|
|
|
CONNECTED = 'CONNECTED'
|
|
|
|
|
|
def check_connection_status(ip,
|
|
remote,
|
|
port=22,
|
|
udp=False,
|
|
answer=CONNECTED,
|
|
must_available=True,
|
|
timeout=0):
|
|
"""Check TCP/UDP port connection availability (or not).
|
|
|
|
Note:
|
|
This method doesn't work on ubuntu-based servers due another `nc`
|
|
variant.
|
|
UDP checking requires an server on remote side. Simple server example:
|
|
```while true; do nc -u -l -p 1235 -e echo Reply; done```
|
|
|
|
Args:
|
|
ip (str): IP address to check connectivity to.
|
|
remote (obj): Connected instance of
|
|
'stepler.third_party.ssh.SshClient'.
|
|
port (int, optional): Port to check.
|
|
udp (bool, optional): Check UDP instead of TCP
|
|
must_available (bool, optional): Flag whether port should be available
|
|
or not.
|
|
timeout (int, optional): Time in seconds to wait expected port
|
|
condition.
|
|
"""
|
|
|
|
proto_flag = ''
|
|
if udp:
|
|
proto_flag = '-u'
|
|
|
|
def _predicate():
|
|
result = remote.execute(
|
|
'echo "" | nc {proto} -w1 {ip} {port} | grep {answer}'.format(
|
|
ip=ip, port=port, proto=proto_flag, answer=answer))
|
|
msg = ("Expected that {proto} connection for {port}"
|
|
" will be {status}").format(
|
|
proto='UDP' if udp else 'TCP',
|
|
port=port,
|
|
status='available' if must_available else 'unavailable')
|
|
return waiter.expect_that(result.exit_code == 0,
|
|
is_(must_available), msg)
|
|
|
|
return waiter.wait(_predicate, timeout_seconds=timeout, sleep_seconds=2)
|
|
|
|
|
|
def check_icmp_connection_status(ip, remote, must_available=True, timeout=0):
|
|
"""Check that icmp connection to ip is `must_available`."""
|
|
|
|
def predicate():
|
|
ping_result = ping.Pinger(ip, remote=remote).ping(count=3)
|
|
if must_available:
|
|
value = ping_result.loss
|
|
else:
|
|
value = ping_result.received
|
|
return waiter.expect_that(value, is_(0))
|
|
|
|
return waiter.wait(predicate, timeout_seconds=timeout)
|
|
|
|
|
|
def start_port_listener(server_ssh,
|
|
port,
|
|
udp=False,
|
|
answer=CONNECTED,
|
|
socat=False):
|
|
"""Start background netcat listener on remote server.
|
|
|
|
Note:
|
|
Netcat call syntax is valid only for cirros. For ubuntu use socat.
|
|
"""
|
|
if socat:
|
|
if not server_ssh.execute('which socat').is_ok:
|
|
raise RuntimeError('socat is not installed on remote server')
|
|
proto = 'UDP4' if udp else 'TCP4'
|
|
cmd = "socat {proto}-LISTEN:{port},fork EXEC:'echo {answer}'".format(
|
|
proto=proto, port=port, answer=answer)
|
|
else:
|
|
proto = '-u' if udp else ''
|
|
listener_cmd = 'nc {proto} -l -p {port} -e echo "{answer}"'.format(
|
|
proto=proto, port=port, answer=answer)
|
|
|
|
cmd = "while true; do {}; done".format(listener_cmd)
|
|
|
|
server_ssh.background_call(cmd)
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def calc_packets_count(os_faults_steps, nodes, iface, filters,
|
|
max_packets=10000):
|
|
"""CM to calc packages count on nodes' iface.
|
|
|
|
Returns dict: fqdn -> captured packets count.
|
|
"""
|
|
tcpdump_base_path = os_faults_steps.start_tcpdump(
|
|
nodes, '-i {0} {1} -c {2}'.format(iface, filters, max_packets))
|
|
result = {node.fqdn: 0 for node in nodes}
|
|
yield result
|
|
os_faults_steps.stop_tcpdump(nodes, tcpdump_base_path)
|
|
|
|
for fqdn, pcap in os_faults_steps.download_tcpdump_results(
|
|
nodes, tcpdump_base_path).items():
|
|
packets = list(tcpdump.read_pcap(pcap))
|
|
result[fqdn] = len(packets)
|
|
|
|
|
|
def start_iperf_pair(client_ssh, server_ssh, ip, port, udp=False, timeout=10):
|
|
"""Start iperf client/server."""
|
|
if not server_ssh.execute('which iperf').is_ok:
|
|
raise RuntimeError('iperf is not installed on server')
|
|
if not client_ssh.execute('which iperf').is_ok:
|
|
raise RuntimeError('iperf is not installed on server')
|
|
|
|
proto = '-u' if udp else ''
|
|
server_cmd = "iperf {proto} -s -p {port}"
|
|
client_cmd = "iperf {proto} -c {ip} -p {port} -t {time}"
|
|
|
|
server_ssh.background_call(server_cmd.format(proto=proto, port=port))
|
|
|
|
# if not udp:
|
|
time.sleep(10)
|
|
|
|
client_ssh.background_call(
|
|
client_cmd.format(proto=proto, ip=ip, port=port, time=timeout))
|
|
|
|
|
|
def check_packets_on_iface(os_faults_steps, node, iface, filters,
|
|
should_be=True):
|
|
with calc_packets_count(os_faults_steps, node, iface,
|
|
filters) as tcp_counts:
|
|
time.sleep(1)
|
|
if should_be:
|
|
matcher = greater_than(0)
|
|
else:
|
|
matcher = is_(0)
|
|
assert_that(tcp_counts, has_value(matcher), 'Wrong packets count')
|