diff options
Diffstat (limited to 'resources')
-rw-r--r-- | resources/libraries/python/Classify.py | 30 | ||||
-rw-r--r-- | resources/libraries/python/DUTSetup.py | 14 | ||||
-rw-r--r-- | resources/libraries/python/IPv4Setup.py | 39 | ||||
-rw-r--r-- | resources/libraries/python/IPv4Util.py | 35 | ||||
-rw-r--r-- | resources/libraries/python/IPv6Util.py | 8 | ||||
-rw-r--r-- | resources/libraries/python/LispSetup.py | 8 | ||||
-rw-r--r-- | resources/libraries/python/Namespaces.py | 14 | ||||
-rw-r--r-- | resources/libraries/python/PacketVerifier.py | 17 | ||||
-rw-r--r-- | resources/libraries/python/QemuUtils.py | 42 | ||||
-rw-r--r-- | resources/libraries/python/Routing.py | 18 | ||||
-rw-r--r-- | resources/libraries/python/SetupFramework.py | 6 | ||||
-rw-r--r-- | resources/libraries/python/VatExecutor.py | 21 | ||||
-rw-r--r-- | resources/libraries/python/VatJsonUtil.py | 2 | ||||
-rw-r--r-- | resources/libraries/python/ssh.py | 37 | ||||
-rw-r--r-- | resources/libraries/python/topology.py | 2 | ||||
-rwxr-xr-x | resources/tools/topology/update_topology.py | 2 |
16 files changed, 206 insertions, 89 deletions
diff --git a/resources/libraries/python/Classify.py b/resources/libraries/python/Classify.py index 8dbe3fb25f..c781bbbd14 100644 --- a/resources/libraries/python/Classify.py +++ b/resources/libraries/python/Classify.py @@ -11,12 +11,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Classify utilities library.""" from robot.api import logger from resources.libraries.python.VatExecutor import VatExecutor, VatTerminal +# pylint: disable=too-many-arguments, invalid-name + class Classify(object): """Classify utilities.""" @@ -117,9 +120,9 @@ class Classify(object): return table_index, skip_n, match_n @staticmethod - def vpp_configures_classify_session_l3(node, acl_method, table_index, skip_n, - match_n, ip_version, direction, - address): + def vpp_configures_classify_session_l3(node, acl_method, table_index, + skip_n, match_n, ip_version, + direction, address): """Configuration of classify session for IP address filtering. :param node: VPP node to setup classify session. @@ -150,8 +153,8 @@ class Classify(object): address=address) @staticmethod - def vpp_configures_classify_session_l2(node, acl_method, table_index, skip_n, - match_n, direction, address): + def vpp_configures_classify_session_l2(node, acl_method, table_index, + skip_n, match_n, direction, address): """Configuration of classify session for MAC address filtering. :param node: VPP node to setup classify session. @@ -170,17 +173,18 @@ class Classify(object): :type address: str """ with VatTerminal(node) as vat: - vat.vat_terminal_exec_cmd_from_template("classify_add_session_l2.vat", - acl_method=acl_method, - table_index=table_index, - skip_n=skip_n, - match_n=match_n, - direction=direction, - address=address) + vat.vat_terminal_exec_cmd_from_template( + "classify_add_session_l2.vat", + acl_method=acl_method, + table_index=table_index, + skip_n=skip_n, + match_n=match_n, + direction=direction, + address=address) @staticmethod def vpp_configures_classify_session_hex(node, acl_method, table_index, - skip_n, match_n, hex_value): + skip_n, match_n, hex_value): """Configuration of classify session with hex value. :param node: VPP node to setup classify session. diff --git a/resources/libraries/python/DUTSetup.py b/resources/libraries/python/DUTSetup.py index e2d183fe4e..e176dd704a 100644 --- a/resources/libraries/python/DUTSetup.py +++ b/resources/libraries/python/DUTSetup.py @@ -11,6 +11,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""DUT setup library.""" + from robot.api import logger from resources.libraries.python.topology import NodeType @@ -20,6 +22,7 @@ from resources.libraries.python.VatExecutor import VatExecutor class DUTSetup(object): + """Contains methods for setting up DUTs.""" @staticmethod def start_vpp_service_on_all_duts(nodes): """Start up the VPP service on all nodes.""" @@ -29,7 +32,7 @@ class DUTSetup(object): ssh.connect(node) (ret_code, stdout, stderr) = \ ssh.exec_command_sudo('service vpp restart') - if 0 != int(ret_code): + if int(ret_code) != 0: logger.debug('stdout: {0}'.format(stdout)) logger.debug('stderr: {0}'.format(stderr)) raise Exception('DUT {0} failed to start VPP service'. @@ -74,6 +77,13 @@ class DUTSetup(object): @staticmethod def setup_dut(node): + """Run script over SSH to setup the DUT node. + + :param node: DUT node to set up. + :type node: dict + + :raises Exception: If the DUT setup fails. + """ ssh = SSH() ssh.connect(node) @@ -82,7 +92,7 @@ class DUTSetup(object): Constants.REMOTE_FW_DIR, Constants.RESOURCES_LIB_SH)) logger.trace(stdout) logger.trace(stderr) - if 0 != int(ret_code): + if int(ret_code) != 0: logger.debug('DUT {0} setup script failed: "{1}"'. format(node['host'], stdout + stderr)) raise Exception('DUT test setup script failed at node {}'. diff --git a/resources/libraries/python/IPv4Setup.py b/resources/libraries/python/IPv4Setup.py index d4be4c602a..07089025b0 100644 --- a/resources/libraries/python/IPv4Setup.py +++ b/resources/libraries/python/IPv4Setup.py @@ -34,6 +34,15 @@ class IPv4Node(object): @staticmethod def _get_netmask(prefix_length): + """Convert IPv4 network prefix length into IPV4 network mask. + + :param prefix_length: Length of network prefix. + :type prefix_length: int + + :return: Network mask. + :rtype: str + """ + bits = 0xffffffff ^ (1 << 32 - prefix_length) - 1 return inet_ntoa(pack('>I', bits)) @@ -112,9 +121,25 @@ class Tg(IPv4Node): super(Tg, self).__init__(node_info) def _execute(self, cmd): + """Executes the specified command on TG using SSH. + + :param cmd: Command to be executed. + :type cmd: str + + :return: Content of stdout and stderr returned by command. + :rtype: tuple + """ return exec_cmd_no_error(self.node_info, cmd) def _sudo_execute(self, cmd): + """Executes the specified command with sudo on TG using SSH. + + :param cmd: Command to be executed. + :type cmd: str + + :return: Content of stdout and stderr returned by command. + :rtype: tuple + """ return exec_cmd_no_error(self.node_info, cmd, sudo=True) def set_ip(self, interface, address, prefix_length): @@ -135,6 +160,13 @@ class Tg(IPv4Node): format(network, prefix_length)) def arp_ping(self, destination_address, source_interface): + """Execute 'arping' command to send one ARP packet from the TG node. + + :param destination_address: Destination IP address for the ARP packet. + :param source_interface: Name of an interface to send ARP packet from. + :type destination_address: str + :type source_interface: str + """ self._sudo_execute('arping -c 1 -I {} {}'.format(source_interface, destination_address)) @@ -207,6 +239,7 @@ class Dut(IPv4Node): sw_if_index=self.get_sw_if_index(interface)) def arp_ping(self, destination_address, source_interface): + """Does nothing.""" pass def flush_ip_addresses(self, interface): @@ -286,9 +319,9 @@ class IPv4Setup(object): host = port.get('node') dev = port.get('if') if host == node['host'] and dev == interface: - ip = port.get('addr') - if ip is not None: - return ip + ip_addr = port.get('addr') + if ip_addr is not None: + return ip_addr else: raise Exception( 'Node {n} port {p} IPv4 address is not set'.format( diff --git a/resources/libraries/python/IPv4Util.py b/resources/libraries/python/IPv4Util.py index ca5a1b571f..96572f5e1d 100644 --- a/resources/libraries/python/IPv4Util.py +++ b/resources/libraries/python/IPv4Util.py @@ -28,6 +28,15 @@ class IPv4Util(object): @keyword('From node "${node}" interface "${port}" ARP-ping ' 'IPv4 address "${ip_address}"') def arp_ping(node, interface, ip_address): + """Send an ARP ping from the specified node. + + :param node: Node in topology. + :param ip_address: Destination IP address for the ARP packet. + :param interface: Name of an interface to send the ARP packet from. + :type node: dict + :type ip_address: str + :type interface: str + """ log.debug('From node {} interface {} ARP-ping IPv4 address {}'. format(Topology.get_node_hostname(node), interface, ip_address)) @@ -89,8 +98,8 @@ class IPv4Util(object): :rtype: int """ for net in nodes_addr.values(): - for p in net['ports'].values(): - if p['node'] == node['host'] and p['if'] == port: + for net_port in net['ports'].values(): + if net_port['node'] == node['host'] and net_port['if'] == port: return net['prefix'] raise Exception('Subnet not found for node {n} port {p}'. @@ -112,8 +121,8 @@ class IPv4Util(object): :rtype: str """ for net in nodes_addr.values(): - for p in net['ports'].values(): - if p['node'] == node['host'] and p['if'] == port: + for net_port in net['ports'].values(): + if net_port['node'] == node['host'] and net_port['if'] == port: return net['net_addr'] raise Exception('Subnet not found for node {n} port {p}'. @@ -189,32 +198,32 @@ class IPv4Util(object): interface, ping_count, destination) else: cmd = 'ping -c{0} {1}'.format(ping_count, destination) - rc, stdout, stderr = exec_cmd(node, cmd, sudo=True) - if rc != 0: + ret_code, _, _ = exec_cmd(node, cmd, sudo=True) + if ret_code != 0: raise RuntimeError("Ping Not Successful") @staticmethod - def set_linux_interface_arp(node, interface, ip, mac, namespace=None): + def set_linux_interface_arp(node, interface, ip_addr, mac, namespace=None): """Set arp on interface in linux. :param node: Node where to execute command. :param interface: Interface in namespace. - :param ip: IP for arp. + :param ip_addr: IP address for ARP entry. :param mac: MAC address. :param namespace: Execute command in namespace. Optional :type node: dict :type interface: str - :type ip: str + :type ip_addr: str :type mac: str :type namespace: str :raises RuntimeError: Could not set ARP properly. """ if namespace is not None: cmd = 'ip netns exec {} arp -i {} -s {} {}'.format( - namespace, interface, ip, mac) + namespace, interface, ip_addr, mac) else: - cmd = 'arp -i {} -s {} {}'.format(interface, ip, mac) - rc, _, stderr = exec_cmd(node, cmd, sudo=True) - if rc != 0: + cmd = 'arp -i {} -s {} {}'.format(interface, ip_addr, mac) + ret_code, _, stderr = exec_cmd(node, cmd, sudo=True) + if ret_code != 0: raise RuntimeError("Arp set not successful, reason:{}". format(stderr)) diff --git a/resources/libraries/python/IPv6Util.py b/resources/libraries/python/IPv6Util.py index 54196ebf52..2a4704dff2 100644 --- a/resources/libraries/python/IPv6Util.py +++ b/resources/libraries/python/IPv6Util.py @@ -46,7 +46,7 @@ class IPv6Util(object): cmd = "ping6 -c {c} -s {s} -W {W} {dst}".format(c=count, s=data_size, W=timeout, dst=dst_addr) - (ret_code, stdout, _) = ssh.exec_command(cmd) + (_, stdout, _) = ssh.exec_command(cmd) regex = re.compile(r'(\d+) packets transmitted, (\d+) received') match = regex.search(stdout) @@ -99,9 +99,9 @@ class IPv6Util(object): host = port.get('node') dev = port.get('if') if host == node['host'] and dev == interface: - ip = port.get('addr') - if ip is not None: - return ip + ip_addr = port.get('addr') + if ip_addr is not None: + return ip_addr else: raise Exception( 'Node {n} port {p} IPv6 address is not set'.format( diff --git a/resources/libraries/python/LispSetup.py b/resources/libraries/python/LispSetup.py index ee2ae5d8a3..99e3de8f74 100644 --- a/resources/libraries/python/LispSetup.py +++ b/resources/libraries/python/LispSetup.py @@ -103,6 +103,8 @@ class LispRemoteMapping(object): seid=seid, seid_prefix=seid_prefix, rloc=rloc) + + class LispAdjacency(object): """Class for lisp adjacency API.""" @@ -117,7 +119,7 @@ class LispAdjacency(object): :param node: VPP node. :param vni: Vni. :param deid: Destination eid address. - :param deid_predix: Destination eid address prefix_len. + :param deid_prefix: Destination eid address prefix_len. :param seid: Source eid address. :param seid_prefix: Source eid address prefix_len. :type node: dict @@ -144,7 +146,7 @@ class LispAdjacency(object): :param node: VPP node. :param vni: Vni. :param deid: Destination eid address. - :param deid_predix: Destination eid address prefix_len. + :param deid_prefix: Destination eid address prefix_len. :param seid: Source eid address. :param seid_prefix: Source eid address prefix_len. :type node: dict @@ -216,11 +218,13 @@ class LispGpeForwardEntry(object): @staticmethod def add_lisp_gpe_forward_entry(node, *args): + """Not implemented""" # TODO: Implement when VPP-334 is fixed. pass @staticmethod def del_lisp_gpe_forward_entry(node, *args): + """Not implemented""" # TODO: Implement when VPP-334 is fixed. pass diff --git a/resources/libraries/python/Namespaces.py b/resources/libraries/python/Namespaces.py index d92dfd7e26..00d615350e 100644 --- a/resources/libraries/python/Namespaces.py +++ b/resources/libraries/python/Namespaces.py @@ -13,7 +13,7 @@ """Linux namespace utilities library.""" -from resources.libraries.python.ssh import exec_cmd_no_error, exec_cmd, SSH +from resources.libraries.python.ssh import exec_cmd_no_error, exec_cmd class Namespaces(object): @@ -46,14 +46,14 @@ class Namespaces(object): :raises RuntimeError: Interface could not be attached. """ cmd = 'ip link set {0} netns {1}'.format(interface, namespace) - (rc, _, stderr) = exec_cmd(node, cmd, timeout=5, sudo=True) - if rc != 0: + (ret_code, _, stderr) = exec_cmd(node, cmd, timeout=5, sudo=True) + if ret_code != 0: raise RuntimeError( 'Could not attach interface, reason:{}'.format(stderr)) cmd = 'ip netns exec {} ip link set {} up'.format( namespace, interface) - (rc, _, stderr) = exec_cmd(node, cmd, timeout=5, sudo=True) - if rc != 0: + (ret_code, _, stderr) = exec_cmd(node, cmd, timeout=5, sudo=True) + if ret_code != 0: raise RuntimeError( 'Could not set interface state, reason:{}'.format(stderr)) @@ -91,6 +91,6 @@ class Namespaces(object): for namespace in self._namespaces: print "Cleaning namespace {}".format(namespace) cmd = 'ip netns delete {}'.format(namespace) - (rc, stdout, stderr) = exec_cmd(node, cmd, timeout=5, sudo=True) - if rc != 0: + (ret_code, _, _) = exec_cmd(node, cmd, timeout=5, sudo=True) + if ret_code != 0: raise RuntimeError('Could not delete namespace') diff --git a/resources/libraries/python/PacketVerifier.py b/resources/libraries/python/PacketVerifier.py index 59ea2db5a3..bbfdfe8688 100644 --- a/resources/libraries/python/PacketVerifier.py +++ b/resources/libraries/python/PacketVerifier.py @@ -68,7 +68,7 @@ import socket import select from scapy.all import ETH_P_IP, ETH_P_IPV6, ETH_P_ALL, ETH_P_ARP -from scapy.all import Ether, ARP, Packet +from scapy.all import Ether, ARP from scapy.layers.inet6 import IPv6 __all__ = ['RxQueue', 'TxQueue', 'Interface', 'create_gratuitous_arp_request', @@ -276,17 +276,32 @@ class TxQueue(PacketVerifier): class Interface(object): + """Class for network interfaces. Contains methods for sending and receiving + packets.""" def __init__(self, if_name): + """Initialize the interface class. + + :param if_name: Name of the interface. + :type if_name: str + """ self.if_name = if_name self.sent_packets = [] self.rxq = RxQueue(if_name) self.txq = TxQueue(if_name) def send_pkt(self, pkt): + """Send the provided packet out the interface.""" self.sent_packets.append(pkt) self.txq.send(pkt) def recv_pkt(self, timeout=3): + """Read one packet from the interface's receive queue. + + :param timeout: Timeout value in seconds. + :type timeout: int + :return: Ether() initialized object from packet data. + :rtype: scapy.Ether + """ return self.rxq.recv(timeout, self.sent_packets) diff --git a/resources/libraries/python/QemuUtils.py b/resources/libraries/python/QemuUtils.py index bc854350dd..aa149da185 100644 --- a/resources/libraries/python/QemuUtils.py +++ b/resources/libraries/python/QemuUtils.py @@ -215,10 +215,10 @@ class QemuUtils(object): '{ \\"execute\\": \\"' + cmd + '\\" }" | sudo -S nc -U ' + \ self.__QMP_SOCK (ret_code, stdout, stderr) = self._ssh.exec_command(qmp_cmd) - if 0 != int(ret_code): + if int(ret_code) != 0: logger.debug('QMP execute failed {0}'.format(stderr)) - raise RuntimeError('QMP execute "{0}" failed on {1}'.format(cmd, - self._node['host'])) + raise RuntimeError('QMP execute "{0}"' + ' failed on {1}'.format(cmd, self._node['host'])) logger.trace(stdout) # Skip capabilities negotiation messages. out_list = stdout.splitlines() @@ -233,10 +233,10 @@ class QemuUtils(object): qga_cmd = 'printf "\xFF" | sudo -S nc ' \ '-q 1 -U ' + self.__QGA_SOCK (ret_code, stdout, stderr) = self._ssh.exec_command(qga_cmd) - if 0 != int(ret_code): + if int(ret_code) != 0: logger.debug('QGA execute failed {0}'.format(stderr)) - raise RuntimeError('QGA execute "{0}" failed on {1}'.format(cmd, - self._node['host'])) + raise RuntimeError('QGA execute "{0}" ' + 'failed on {1}'.format(cmd, self._node['host'])) logger.trace(stdout) if not stdout: return {} @@ -253,10 +253,10 @@ class QemuUtils(object): qga_cmd = 'echo "{ \\"execute\\": \\"' + cmd + '\\" }" | sudo -S nc ' \ '-q 1 -U ' + self.__QGA_SOCK (ret_code, stdout, stderr) = self._ssh.exec_command(qga_cmd) - if 0 != int(ret_code): + if int(ret_code) != 0: logger.debug('QGA execute failed {0}'.format(stderr)) - raise RuntimeError('QGA execute "{0}" failed on {1}'.format(cmd, - self._node['host'])) + raise RuntimeError('QGA execute "{0}"' + ' failed on {1}'.format(cmd, self._node['host'])) logger.trace(stdout) if not stdout: return {} @@ -353,8 +353,10 @@ class QemuUtils(object): self._node['host'])) # If we do not want to allocate dynamicaly end with error else: - raise RuntimeError('Not enough free huge pages: {0}, ' - '{1} MB'.format(huge_free, huge_free * huge_size)) + raise RuntimeError( + 'Not enough free huge pages: {0}, ' + '{1} MB'.format(huge_free, huge_free * huge_size) + ) # Check if huge pages mount point exist has_huge_mnt = False (_, output, _) = self._ssh.exec_command('cat /proc/mounts') @@ -395,7 +397,7 @@ class QemuUtils(object): # Memory and huge pages mem = '-object memory-backend-file,id=mem,size={0}M,mem-path={1},' \ 'share=on -m {0} -numa node,memdev=mem'.format( - self._qemu_opt.get('mem_size'), self._qemu_opt.get('huge_mnt')) + self._qemu_opt.get('mem_size'), self._qemu_opt.get('huge_mnt')) # By default check only if hugepages are availbale. # If 'huge_allocate' is set to true try to allocate as well. @@ -406,7 +408,7 @@ class QemuUtils(object): # Setup serial console serial = '-chardev socket,host=127.0.0.1,port={0},id=gnc0,server,' \ 'nowait -device isa-serial,chardev=gnc0'.format( - self._qemu_opt.get('serial_port')) + self._qemu_opt.get('serial_port')) # Setup QGA via chardev (unix socket) and isa-serial channel qga = '-chardev socket,path=/tmp/qga.sock,server,nowait,id=qga0 ' \ '-device isa-serial,chardev=qga0' @@ -443,15 +445,18 @@ class QemuUtils(object): out = self._qemu_qmp_exec('system_powerdown') err = out.get('error') if err is not None: - raise RuntimeError('QEMU system powerdown failed on {0}, ' - 'error: {1}'.format(self._node['host'], json.dumps(err))) + raise RuntimeError( + 'QEMU system powerdown failed on {0}, ' + 'error: {1}'.format(self._node['host'], json.dumps(err)) + ) def qemu_system_reset(self): """Reset the system.""" out = self._qemu_qmp_exec('system_reset') err = out.get('error') if err is not None: - raise RuntimeError('QEMU system reset failed on {0}, ' + raise RuntimeError( + 'QEMU system reset failed on {0}, ' 'error: {1}'.format(self._node['host'], json.dumps(err))) def qemu_kill(self): @@ -501,7 +506,8 @@ class QemuUtils(object): return ret.get('status') else: err = out.get('error') - raise RuntimeError('QEMU query-status failed on {0}, ' + raise RuntimeError( + 'QEMU query-status failed on {0}, ' 'error: {1}'.format(self._node['host'], json.dumps(err))) @staticmethod @@ -518,6 +524,6 @@ class QemuUtils(object): ssh.exec_command('sudo -Sn bash {0}/{1}/qemu_build.sh'.format( Constants.REMOTE_FW_DIR, Constants.RESOURCES_LIB_SH), 1000) logger.trace(stdout) - if 0 != int(ret_code): + if int(ret_code) != 0: logger.debug('QEMU build failed {0}'.format(stderr)) raise RuntimeError('QEMU build failed on {0}'.format(node['host'])) diff --git a/resources/libraries/python/Routing.py b/resources/libraries/python/Routing.py index fbe7b60183..ff675f39a9 100644 --- a/resources/libraries/python/Routing.py +++ b/resources/libraries/python/Routing.py @@ -17,10 +17,12 @@ from resources.libraries.python.VatExecutor import VatTerminal from resources.libraries.python.topology import Topology from resources.libraries.python.ssh import exec_cmd_no_error + class Routing(object): """Routing utilities.""" + # pylint: disable=too-many-arguments @staticmethod def vpp_route_add(node, network, prefix_len, gateway=None, interface=None, use_sw_index=True, resolve_attempts=10, @@ -101,23 +103,23 @@ class Routing(object): where=place) @staticmethod - def add_route(node, ip, prefix, gw, namespace=None): + def add_route(node, ip_addr, prefix, gateway, namespace=None): """Add route in namespace. :param node: Node where to execute command. - :param ip: Route destination IP. + :param ip_addr: Route destination IP address. :param prefix: IP prefix. - :param namespace: Execute command in namespace. Optional - :param gw: Gateway. + :param namespace: Execute command in namespace. Optional. + :param gateway: Gateway address. :type node: dict - :type ip: str + :type ip_addr: str :type prefix: int - :type gw: str + :type gateway: str :type namespace: str """ if namespace is not None: cmd = 'ip netns exec {} ip route add {}/{} via {}'.format( - namespace, ip, prefix, gw) + namespace, ip_addr, prefix, gateway) else: - cmd = 'ip route add {}/{} via {}'.format(ip, prefix, gw) + cmd = 'ip route add {}/{} via {}'.format(ip_addr, prefix, gateway) exec_cmd_no_error(node, cmd, sudo=True) diff --git a/resources/libraries/python/SetupFramework.py b/resources/libraries/python/SetupFramework.py index 1a1e991b3b..d0059ebed8 100644 --- a/resources/libraries/python/SetupFramework.py +++ b/resources/libraries/python/SetupFramework.py @@ -58,7 +58,7 @@ def pack_framework_dir(): logger.debug(stderr) return_code = proc.wait() - if 0 != return_code: + if return_code != 0: raise Exception("Could not pack testing framework.") return file_name @@ -99,7 +99,7 @@ def extract_tarball_at_node(tarball, node): cmd = 'sudo rm -rf {1}; mkdir {1} ; tar -zxf {0} -C {1}; ' \ 'rm -f {0}'.format(tarball, con.REMOTE_FW_DIR) (ret_code, _, stderr) = ssh.exec_command(cmd, timeout=30) - if 0 != ret_code: + if ret_code != 0: logger.error('Unpack error: {0}'.format(stderr)) raise Exception('Failed to unpack {0} at node {1}'.format( tarball, node['host'])) @@ -116,7 +116,7 @@ def create_env_directory_at_node(node): '. env/bin/activate && ' 'pip install -r requirements.txt' .format(con.REMOTE_FW_DIR), timeout=100) - if 0 != ret_code: + if ret_code != 0: logger.error('Virtualenv creation error: {0}'.format(stdout + stderr)) raise Exception('Virtualenv setup failed') else: diff --git a/resources/libraries/python/VatExecutor.py b/resources/libraries/python/VatExecutor.py index a0f9634d8c..03b7321020 100644 --- a/resources/libraries/python/VatExecutor.py +++ b/resources/libraries/python/VatExecutor.py @@ -11,6 +11,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""VAT executor library.""" + import json from robot.api import logger @@ -39,6 +41,7 @@ def cleanup_vat_json_output(json_output): class VatExecutor(object): + """Contains methods for executing VAT commands on DUTs.""" def __init__(self): self._stdout = None self._stderr = None @@ -62,7 +65,7 @@ class VatExecutor(object): Constants.RESOURCES_TPL_VAT, vat_name) # TODO this overwrites the output if the vat script has been used twice - remote_file_out = remote_file_path + ".out" + # remote_file_out = remote_file_path + ".out" cmd = "sudo -S {vat} {json} < {input}".format( vat=Constants.VAT_BIN_NAME, @@ -81,17 +84,29 @@ class VatExecutor(object): # self._delete_files(node, remote_file_path, remote_file_out) def execute_script_json_out(self, vat_name, node, timeout=10): + """Pass all arguments to 'execute_script' method, then cleanup returned + json output.""" self.execute_script(vat_name, node, timeout, json_out=True) self._stdout = cleanup_vat_json_output(self._stdout) @staticmethod def _delete_files(node, *files): + """Use SSH to delete the specified files on node. + + :param node: Node in topology. + :param files: Files to delete. + :type node: dict + :type files: iterable + """ + ssh = SSH() ssh.connect(node) files = " ".join([str(x) for x in files]) ssh.exec_command("rm {0}".format(files)) def script_should_have_failed(self): + """Read return code from last executed script and raise exception if the + script didn't fail.""" if self._ret_code is None: raise Exception("First execute the script!") if self._ret_code == 0: @@ -99,6 +114,8 @@ class VatExecutor(object): "Script execution passed, but failure was expected") def script_should_have_passed(self): + """Read return code from last executed script and raise exception if the + script failed.""" if self._ret_code is None: raise Exception("First execute the script!") if self._ret_code != 0: @@ -106,9 +123,11 @@ class VatExecutor(object): "Script execution failed, but success was expected") def get_script_stdout(self): + """Returns value of stdout from last executed script.""" return self._stdout def get_script_stderr(self): + """Returns value of stderr from last executed script.""" return self._stderr @staticmethod diff --git a/resources/libraries/python/VatJsonUtil.py b/resources/libraries/python/VatJsonUtil.py index 1cafff0d27..6445d8c387 100644 --- a/resources/libraries/python/VatJsonUtil.py +++ b/resources/libraries/python/VatJsonUtil.py @@ -173,7 +173,7 @@ class VatJsonUtil(object): :type err_msg: str :raises RuntimeError: If VAT command return value is incorrect. """ - if type(vat_out) is dict: + if isinstance(vat_out, dict): retval = vat_out.get('retval') if retval is not None: if retval != exp_retval: diff --git a/resources/libraries/python/ssh.py b/resources/libraries/python/ssh.py index b470e86ccc..287ad31d65 100644 --- a/resources/libraries/python/ssh.py +++ b/resources/libraries/python/ssh.py @@ -11,6 +11,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Library for SSH connection management.""" + import StringIO from time import time, sleep @@ -19,7 +21,7 @@ import paramiko from paramiko import RSAKey from paramiko.ssh_exception import SSHException from scp import SCPClient -from interruptingcow import timeout +from interruptingcow import timeout as icTimeout from robot.api import logger from robot.utils.asserts import assert_equal @@ -29,6 +31,7 @@ __all__ = ["exec_cmd", "exec_cmd_no_error"] class SSH(object): + """Contains methods for managing and using SSH connections.""" __MAX_RECV_BUF = 10*1024*1024 __existing_connections = {} @@ -39,6 +42,14 @@ class SSH(object): @staticmethod def _node_hash(node): + """Get IP address and port hash from node dictionary. + + :param node: Node in topology. + :type node: dict + :return: IP address and port for the specified node. + :rtype: int + """ + return hash(frozenset([node['host'], node['port']])) def connect(self, node): @@ -56,7 +67,7 @@ class SSH(object): pkey = None if 'priv_key' in node: pkey = RSAKey.from_private_key( - StringIO.StringIO(node['priv_key'])) + StringIO.StringIO(node['priv_key'])) self._ssh = paramiko.SSHClient() self._ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) @@ -90,6 +101,8 @@ class SSH(object): ssh.close() def _reconnect(self): + """Close the SSH connection and open it again.""" + node = self._node self.disconnect(node) self.connect(node) @@ -100,8 +113,8 @@ class SSH(object): """Execute SSH command on a new channel on the connected Node. :param cmd: Command to run on the Node. - :param timeout: Maximal time in seconds to wait while the command is - done. If is None then wait forever. + :param timeout: Maximal time in seconds to wait until the command is + done. If set to None then wait forever. :type cmd: str :type timeout: int :return return_code, stdout, stderr @@ -203,7 +216,7 @@ class SSH(object): buf = '' try: - with timeout(time_out, exception=RuntimeError): + with icTimeout(time_out, exception=RuntimeError): while not buf.endswith(':~$ '): if chan.recv_ready(): buf = chan.recv(4096) @@ -235,7 +248,7 @@ class SSH(object): chan.sendall('{c}\n'.format(c=cmd)) buf = '' try: - with timeout(time_out, exception=RuntimeError): + with icTimeout(time_out, exception=RuntimeError): while not buf.endswith(prompt): if chan.recv_ready(): buf += chan.recv(4096) @@ -283,8 +296,8 @@ def exec_cmd(node, cmd, timeout=600, sudo=False): ssh = SSH() try: ssh.connect(node) - except Exception, e: - logger.error("Failed to connect to node" + str(e)) + except Exception as err: + logger.error("Failed to connect to node" + str(err)) return None, None, None try: @@ -293,8 +306,8 @@ def exec_cmd(node, cmd, timeout=600, sudo=False): else: (ret_code, stdout, stderr) = ssh.exec_command_sudo(cmd, timeout=timeout) - except Exception, e: - logger.error(e) + except Exception as err: + logger.error(err) return None, None, None return ret_code, stdout, stderr @@ -307,7 +320,7 @@ def exec_cmd_no_error(node, cmd, timeout=600, sudo=False): Returns (stdout, stderr). """ - (rc, stdout, stderr) = exec_cmd(node, cmd, timeout=timeout, sudo=sudo) - assert_equal(rc, 0, 'Command execution failed: "{}"\n{}'. + (ret_code, stdout, stderr) = exec_cmd(node, cmd, timeout=timeout, sudo=sudo) + assert_equal(ret_code, 0, 'Command execution failed: "{}"\n{}'. format(cmd, stderr)) return stdout, stderr diff --git a/resources/libraries/python/topology.py b/resources/libraries/python/topology.py index c02991fbde..02f326a1df 100644 --- a/resources/libraries/python/topology.py +++ b/resources/libraries/python/topology.py @@ -34,6 +34,7 @@ def load_topo_from_yaml(): with open(topo_path) as work_file: return load(work_file.read())['nodes'] + # pylint: disable=invalid-name class NodeType(object): """Defines node types used in topology dictionaries.""" @@ -46,6 +47,7 @@ class NodeType(object): class NodeSubTypeTG(object): + """Defines node sub-type TG - traffic generator.""" # T-Rex traffic generator TREX = 'TREX' # Moongen diff --git a/resources/tools/topology/update_topology.py b/resources/tools/topology/update_topology.py index a5711d0922..f60fdf1653 100755 --- a/resources/tools/topology/update_topology.py +++ b/resources/tools/topology/update_topology.py @@ -59,7 +59,7 @@ def ssh_no_error(ssh, cmd): :rtype: str """ ret, stdo, stde = ssh.exec_command(cmd) - if 0 != ret: + if ret != 0: print 'Command execution failed: "{}"'.format(cmd) print 'stdout: {0}'.format(stdo) print 'stderr: {0}'.format(stde) |