Source code for taf.testlib.switch_ons

# Copyright (c) 2011 - 2017, Intel Corporation.
#
# 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.

"""``switch_ons.py``

`ONS switches-specific functionality`

"""
from os.path import isfile as os_path_isfile
from os.path import join as os_path_join
import shutil
from subprocess import Popen, PIPE
import time

import paramiko
import pexpect
import pytest

from . import environment
from . import loggers
from . import sshtun
from .switch_general import SwitchGeneral, SwitchReal
from .xmlrpc_proxy import TimeoutServerProxy as xmlrpcProxy
from .custom_exceptions import SwitchException


[docs]class SwitchONSGeneralMixin(object): def __init__(self, *args, **kwargs): super(SwitchONSGeneralMixin, self).__init__(*args, **kwargs) self.xmlproxy = xmlrpcProxy("http://%s:%s/RPC2" % (self.ipaddr, self.port), timeout=180) self.sshtun = None self._use_sshtun = False
[docs] def _get_port_for_probe(self): """Get port ID. Returns: int: ssh tunnel ports ID """ # In case using sshtun check device by ssh port. if self._use_sshtun: return self._sshtun_port else: return int(self.port)
[docs] def getprop(self, table, param, row_id, dst="nb"): """Return switchpp property. Args: table(str): Name of table where necessary parameter is stored param(str): Name of necessary parameter row_id(int): Row index in switch table dst(str): Querry destination. E.g. nb, system, onsps Returns: str, int: Parameter value Notes: This is just wrapper for xmlrpc nb call. Examples:: switch_instance.getprop("Ports", "operationalStatus", 5) env.switch[1].getprop("SpanningTree", "mode", 1) """ return getattr(self.xmlproxy, "%s.%s.get.%s" % (dst, table, param))(row_id)
[docs] def getprop_row(self, table, row_id, dst="nb"): """Return switchpp table row. Args: table(str): Name of table row_id(int): Row index in switch table dst(str): Querry destination. E.g. nb, system, onsps Returns: dict: Table row Notes: This is just wrapper for xmlrpc nb call. Examples:: switch_instance.getprop("Ports", 5) env.switch[1].getprop("Platform", 1) """ return getattr(self.xmlproxy, "%s.%s.getRow" % (dst, table))(row_id)
[docs] def getprop_table(self, table, dst="nb"): """Return switchpp table. Args: table(str): Name of table dst(str): Querry destination. E.g. nb, system, onsps Returns: list[dict]: Table Notes: This is just wrapper for xmlrpc nb call. Examples:: switch_instance.getprop("SpanningTree") env.switch[1].getprop("RSTPPorts") """ table_size = self.getprop_size(table, dst=dst) if table_size <= 1000: return getattr(self.xmlproxy, "%s.%s.getTable" % (dst, table))() else: table_content = [] subset_size = 200 # Create table list: start_point = 0 subset = getattr(self.xmlproxy, "%s.%s.getTableSubset" % (dst, table))(start_point, subset_size) table_content.extend(subset) while len(subset) == 200: start_point = subset[-1]["rowId"] subset = getattr(self.xmlproxy, "%s.%s.getTableSubset" % (dst, table))(start_point, subset_size) table_content.extend(subset) return table_content
[docs] def getprop_size(self, table, dst="nb"): """Return switchpp table length. Args: table(str): Name of table dst(str): Querry destination. E.g. nb, system, onsps Returns: int: Table size Notes: This is just wrapper for xmlrpc nb call. Examples:: switch_instance.getprop_size("SpanningTree") env.switch[1].getprop_size("RSTPPorts") """ return getattr(self.xmlproxy, "%s.%s.size" % (dst, table))()
[docs] def getprop_table_info(self, table, dst="nb"): """Return switchpp table info. Args: table(str): Name of table dst(str): Querry destination. E.g. nb, system, onsps Returns: dict: Table info Notes: This is just wrapper for xmlrpc nb call. Examples:: switch_instance.getprop_table_info("SpanningTree") env.switch[1].getprop_table_info("RSTPPorts") """ return getattr(self.xmlproxy, "%s.%s.getInfo" % (dst, table))()
[docs] def getprop_field_info(self, table, field, dst="nb"): """Return switchpp table field info. Args: table(str): Name of table dst(str): Querry destination. E.g. nb, system, onsps field(str): Name of field Returns: dict: Field info Notes: This is just wrapper for xmlrpc nb call. Examples:: switch_instance.getprop_field_info("Vlans", "vlanId") env.switch[1].getprop_field_info("Vlans", "name") """ return getattr(self.xmlproxy, "%s.%s.getInfo.%s" % (dst, table, field))()
[docs] def getprop_method_help(self, method): """Return switchpp table info. Args: method(str): xmlrpc method Returns: str: Method help information Notes: This is just wrapper for xmlrpc call. Examples:: switch_instance.getprop_method_help("nb.StaticARP.addRow") env.switch[1].getprop_method_help("nb.StaticARP.addRow") """ return getattr(self.xmlproxy, "system.methodHelp")(method)
[docs] def setprop(self, table, param, values, dst="nb"): """Set switchpp property. Args: table(str): Name of table where necessary parameter is stored param(str): Name of necessary parameter values(list): List on necessary set parameters dst(str): Querry destination. E.g. nb, system, onsps Raises: xmlrpclib.Fault Returns: int: Set operation status (int or xmlrpclib.Fault exception) Notes: This is just wrapper for xmlrpc nb call. Examples:: switch_instance.setprop("Ports", "adminMode", [10, "Up"]) env.switch[1].setprop("SpanningTree", "mode", [1, "MSTP"]) """ return getattr(self.xmlproxy, "%s.%s.set.%s" % (dst, table, param))(*values)
[docs] def setprop_row(self, table, values, dst="nb"): """Add row to switchpp table. Args: table(str): Name of table values(list): List on necessary addRow parameters dst(str): Querry destination. E.g. nb, system, onsps Raises: xmlrpclib.Fault Returns: int: addRow operation status (int or xmlrpclib.Fault exception) Notes: This is just wrapper for xmlrpc nb call. Examples:: switch_instance.setprop_row("Vlans", [7, "TestVlan"]) port_id = 1 vlan_id = 7 env.switch[1].setprop_row("Ports2Vlans", [port_id, vlan_id, "Tagged"]) """ return getattr(self.xmlproxy, "%s.%s.addRow" % (dst, table))(*values)
[docs] def unsetprop(self, table, param, values, dst="nb"): """Unset switchpp property. Args: table(str): Name of table where necessary parameter is stored param(str): Name of necessary parameter values(list): List on necessary set parameters dst(str): Query destination. E.g. nb, system, onsps Raises: xmlrpclib.Fault Returns: int: Unset operation status (int or xmlrpclib.Fault exception) Notes: This is just wrapper for xmlrpc nb call. Examples:: switch_instance.setprop("Ports", "adminMode", [10, "Up"]) env.switch[1].unsetprop("SpanningTree", "mode", [1, "MSTP"]) """ return getattr(self.xmlproxy, "%s.%s.unset.%s" % (dst, table, param))(*values)
[docs] def delprop_row(self, table, row_id, dst="nb"): """Delete row from switchpp table. Args: table(str): Name of table row_id(int): Row ID in switch table dst(str): Querry destination. E.g. nb, system, onsps Raises: xmlrpclib.Fault Returns: int: delRow operation status (int or xmlrpclib.Fault exception) Notes: This is just wrapper for xmlrpc nb call. Examples:: switch_instance.delprop_row("Vlans", 8) port_id = 1 vlan_id = 7 env.switch[1].delprop_row("Ports2Vlans", 4) """ return getattr(self.xmlproxy, "%s.%s.delRow" % (dst, table))(row_id)
[docs] def findprop(self, table, values, dst="nb"): """Find switchpp property id. Args: table(str): Name of table where necessary parameter is stored values(list): List on necessary find parameters dst(str): Querry destination. E.g. nb, system, onsps Raises: xmlrpclib.Fault Returns: int: Query reply (row id). Notes: This is just wrapper for xmlrpc nb call. Examples:: switch_instance.findprop("Vlans", [7, ]) env.switch[1].findprop("Applications", [1, 1, 'ONSNameServer']) """ return getattr(self.xmlproxy, "%s.%s.find" % (dst, table))(*values)
[docs] def existsprop(self, table, values, dst="nb"): """Check switchpp property existence. Args: table(str): Name of table where necessary parameter is stored values(str): List on necessary find parameters dst(str): Querry destination. E.g. nb, system, onsps Returns: bool: Query reply. Notes: This is just wrapper for xmlrpc nb call. Examples:: switch_instance.existsprop("StaticARP", ["10.10.10.10", 0]) """ return getattr(self.xmlproxy, "%s.%s.exists" % (dst, table))(*values)
[docs] def multicall(self, calls_list): """Execute switchpp multicall. Args: calls_list(list(dict)): List of dictionaries for necessary XML-RPC calls Raises: SwitchException: incorrect parameters Returns: List of executed operations statuses and return values Notes: This is just wrapper for xmlrpc system call. Examples:: env.switch[1].multicall([{'methodName': 'nb.Vlans.addRow', 'params': [(10, 'Vlan_10'), (20, 'Vlan_20'), (30, 'Vlan_30'), (40, 'Vlan_40'), ]}, ]) env.switch[1].multicall([{'methodName': 'nb.Vlans.addRow', 'params': [(100, 'Vlan_100'), ]}, {'methodName': 'nb.Ports2Vlans.addRow', 'params': [(1, 100, "Untagged"), ]}, {'methodName': 'nb.Ports.set.pvid', 'params': [(1, 100), ]}]) env.switch[1].multicall([{'methodName': 'nb.Ports.get.operationalStatus', 'params': [(1, ), (2, ), (3, ), (4, ), (5, )]}, ]) """ multicalls_list = [] for row in calls_list: try: methodname = row['methodName'] # use extend with an iterables instead of appending each one multicalls_list.extend( {'methodName': methodname, 'params': params} for params in row['params']) except KeyError as err: raise SwitchException("Incorrect key is transmitted in calls_list row dictionary: %s" % err) return_values = getattr(self.xmlproxy, "system.multicall")(multicalls_list) assert len(return_values) == len(multicalls_list), "Return values list has different length than multicall list" i = 0 while i < len(return_values): if isinstance(return_values[i], list): multicalls_list[i]["result"] = str(return_values[i][0]) else: multicalls_list[i]["result"] = str(return_values[i]) i += 1 return multicalls_list
[docs] def set_app_log_level(self, loglevel="Notice"): """Set application log level for switch. Args: loglevel(str): value of set log level """ for i in self.ui.get_table_applications(): self.ui.configure_application(i['name'], loglevel)
[docs] def check_app_table(self): """Check if Application table contains all expected items in admin Up state. Returns: bool: True or False """ # TODO update application list expapp_list = {'ONSCoreServer', 'ONSApplicationServer', 'ONSNorthboundServer'} expapp_list.union(self.SWITCH_APPS) app_table = self.getprop_table("Applications") app_list = set([_x['name'] for _x in app_table]) app_state = set([(x['adminState'] == x['operationalState']) for x in app_table]) app_no_ready = set(x['name'] for x in app_table if x['adminState'] != x['operationalState']) # Verify that app_list contains all members from expapp_list if expapp_list.issubset(app_list): if len(app_state) == 1 and app_state.pop(): return True else: self.class_logger.debug("Applications that aren't ready: %s" % (app_no_ready, )) return False else: self.class_logger.debug("Applications that aren't registered yet: %s" % (expapp_list - app_list, )) return False
[docs]class SwitchONS(SwitchONSGeneralMixin, SwitchReal): SWITCH_APPS = {"FulcrumApp", "onsps"} UI_RESTART_TIMEOUT = 25 def __init__(self, config, opts): self.build_path = None self.cli_img_path = None self.cli_delay = None self.xmlrpcport = None super(SwitchONS, self).__init__(config, opts) self.xmlproxy = xmlrpcProxy("http://%s:%s/RPC2" % (self.ipaddr, self.port), timeout=180) self.local_xmlrpc_port = None if "use_sshtun" in self.config and self.config['use_sshtun'] > 0: self.class_logger.info("Using secure xmlrpc connection.") self._use_sshtun = True # devices are booted via netboot if parameter is True in config self.netboot = config.get("netboot", False) def _get_port_for_probe(self): return SwitchONSGeneralMixin._get_port_for_probe(self)
[docs] def start(self, wait_on=True): """Power on switch or perform power cycle if it is already On. Args: wait_on(bool): Check if switch boot successfully Raises: SwitchException: unknown device status """ self.class_logger.info("Starting Real switch device %s(%s) ..." % (self.name, self.ipaddr)) self.class_logger.debug("Checking device status on powerboard...") status = self.powerboard.get_status(self.pwboard, self.pwport, self.pwboard_snmp_rw_community_string) self.class_logger.debug("Current status %s." % status) if status == "On": # Turn Off Seacliff with halt. if "halt" in self.config and self.config["halt"]: self.halt() self.powerboard.do_action(self.pwboard, self.pwport, self.pwboard_snmp_rw_community_string, self.powerboard.commands["Off"]) time.sleep(1) self.powerboard.do_action(self.pwboard, self.pwport, self.pwboard_snmp_rw_community_string, self.powerboard.commands["On"]) elif status == "Off": self.powerboard.do_action(self.pwboard, self.pwport, self.pwboard_snmp_rw_community_string, self.powerboard.commands["On"]) else: raise SwitchException("Cannot determine device status.") if self.netboot: self.exec_netboot() # After snmp command is sent APC could restart switch # in few seconds. Terefore it's good to wait a little # to prevent fail login prompt detection. time.sleep(1) if self._use_serial: # Check if mgmt interface already configured. # Let's wait until device is up and running: # For real devices we have to check boot status on telnet. self.class_logger.debug("Waiting for telnet prompt and login...") self.get_serial(timeout=100, with_login=True, wait_login=30) self.close_serial() else: self.class_logger.debug("Waiting 10 seconds to allow device HW load...") time.sleep(10) # The switch loads really very long time. # Wait to reduce number of unsuccessful queries. # Also this could help to avoid entering boot shell after unexpected device reboot self.class_logger.debug("Waiting 15 seconds to allow device load all services...") time.sleep(15) if self.db_corruption and self._use_serial: self.class_logger.warning("Switch configuration DB is damaged. Performing emergency DB reset.") self.forced_clearconfig() elif self.db_corruption and not self._use_serial: self.class_logger.warning("Switch configuration DB is damaged. DB reset is skipped since serial access is disabled.") if self._use_serial: # Configure management interface before any further operations. self.get_serial(timeout=100, with_login=True, wait_login=0.1) if not self.check_mgmt_iface(): self.setup_mgmt_iface() self.close_serial() if wait_on: # Set huge timeout because of issue ONS-3170 self.waiton(timeout=self.startup_time) # Set status to On(True) self.status = True # Perform syslog configuration if one exists. self.setup_syslog() # Set initial ports speed self.speed_preconfig()
[docs] def clearconfig(self): """Perform clearConfig query on switch using telnet. And try to configure management interface. """ # If serial is disabled using default clearconfig function. if not self._use_serial: if self.db_corruption: self.class_logger.warning("Switch configuration DB is damaged. DB reset is skipped since serial access is disabled.") super(SwitchONS, self).clearconfig() return if self.db_corruption: self.class_logger.warning("Switch configuration DB is damaged. Performing emergency DB reset.") self.forced_clearconfig(wait_on=True) # Set initial ports speed self.speed_preconfig() self.setup_syslog() return self.class_logger.debug("Performing clearConfig on real switch.") self.class_logger.debug("Wait telnet prompt and login.") self.get_serial(timeout=240, with_login=None, wait_login=0.1) # Check that console is OK output, err, _ = self.telnet.exec_command(" ") try: self.console_clear_config() if not self.check_mgmt_iface(): self.setup_mgmt_iface() self.setup_syslog() # Set initial ports speed self.speed_preconfig() finally: self.close_serial()
[docs] def exec_netboot(self): """Method to execute netboot on device start. """ trm = pexpect.spawn('telnet %s %d' % (self.config["portserv_host"], self.config["portserv_port"])) trm.expect_exact('Hit a key to start the shell...', 150) trm.sendline('') trm.expect_exact('shell> ', 10) trm.sendline('netboot\n') trm.close() trm.kill(0)
[docs] def open_sshtun(self): """Establish ssh tunnel. """ if self.sshtun is None: self.class_logger.debug("Creating sshtun instance ...") self.sshtun = sshtun.SSHTunnel((self.ipaddr, self._sshtun_port), self._sshtun_user, self._sshtun_pass, ("127.0.0.1", int(self.port))) if not self.sshtun.check(): self.class_logger.debug("Establishing ssh tunnel ...") self.local_xmlrpc_port = self.sshtun.establish() self.xmlproxy = xmlrpcProxy("http://%s:%s/RPC2" % ("127.0.0.1", self.local_xmlrpc_port), timeout=180)
[docs] def close_sstun(self): """Close ssh tunnel. """ if self.sshtun is not None and self.sshtun.check(): self.class_logger.debug("Closing ssh tunnel ...") self.sshtun.close()
[docs] def get_env_prop(self, param): """Read properties from all devices. """ if getattr(getattr(self, "rag", None), "role", "") == "slave": return "Slave_%s" % param else: if param == 'chipName': return getattr(self, 'jira_platform_name', self.instance_prop[param]) return self.instance_prop[param]
[docs] def get_processes(self, tc_name, skip_prcheck=None): """Procedure of getting processes on switch. Args: tc_name(str): test case name skip_prcheck(list[str]): list of processes to skip PID verification """ return self.supervisorctl(tc_name, cmd="status", ssh=self.ssh, skip_prcheck=skip_prcheck)
[docs] def supervisorctl(self, tc_name, cmd="status", ssh=None, skip_prcheck=None): """Procedure of calling supervisorctl tool on switch. Args: tc_name(str): test case name cmd(str): supervisorctl command ssh(CLISSH): ssh object skip_prcheck(list[str]): list of processes to skip PID verification """ self.class_logger.debug("Supervisor procedure of getting processes is on.") p2pid = {} # Make dictionary of process to pid command = "sudo supervisorctl {0}".format(cmd) alternatives = [("Password:", "admin", False, False), ("password for", "admin", False, False), ] output, err = ssh.shell_command(command, alternatives=alternatives, timeout=25, ret_code=True, quiet=True) processes = [line for line in output.split("\n") if "pid" in line and "platform:syslogd" not in line] for line in processes: pr_check = True if skip_prcheck: for pr in skip_prcheck: if pr in line: pr_check = False if pr_check: p2pid[line.split(" ")[0]] = line.split("pid ")[1].split(",")[0] return p2pid
[docs] def check_mgmt_iface(self): """Check if management interface is configured on switch. Raises: SwitchException: error on command execution Returns: bool: True or False """ self.class_logger.debug("Check if management interface is configured.") command = "ip addr show dev %s | grep 'inet ' --color=never" % self.mgmt_iface output, err, _ = self.telnet.exec_command(command.encode("ascii"), sudo=True) if err: message = "Cannot check management interface status. Command '%s'.\nStdOut: %s\nStdErr: %s" % (command, output, err) self.class_logger.error(message) raise SwitchException(message) if output is not None and "inet " in output: try: found_ip = output.split("inet ")[1].split("/")[0].strip() except IndexError: found_ip = "<cannot recognize>" self.class_logger.debug("Switch has already configured management interface %s with IP:%s" % (self.mgmt_iface, found_ip, )) if found_ip != self.ipaddr: self.class_logger.debug("Switch management interface IP doesn't answer setup configuration and will be reconfigured.") return False else: return True else: self.class_logger.debug("Switch doesn't have configured management interface %s" % (self.mgmt_iface, )) return False
[docs] def setup_mgmt_iface(self): """Configure management interface on switch. Raises: SwitchException: error on command execution, timeout exceeded """ self.class_logger.debug("Configure management interface %s." % self.mgmt_iface) # Wait until switch starts listening on port 8081 command = "netstat -tnl | grep %s" % (self.port, ) end_time = time.time() + 20 wait_flag = True while time.time() <= end_time and wait_flag: err, output = None, None output, err, _ = self.telnet.exec_command(command.encode("ascii")) self.class_logger.debug("TelnetCMD StdOut:\n%s" % (output, )) if err: raise SwitchException("Cannot execute command '%s'. StdErr: %s" % (command, err)) if output is not None and "LISTEN" in output: wait_flag = False else: time.sleep(0.5) # In case time is elapsed but wait_flag isn't set raise Timeout Exception if wait_flag: raise SwitchException("TimeOut exceeded. Switch isn't listening port %s on localhost." % (self.port, )) # Stabilization interval # Without this nb server cannot handle eth0 up status. time.sleep(10) cmd = [ "import xmlrpclib", "rc = 'FAILED'", "s = xmlrpclib.ServerProxy('http://127.0.0.1:%s')" % (self.port, ), "rc = s.nb.MgmtPort.set.mode(1, 'Static')", "rc = s.nb.MgmtPort.set.address(1, '%s')" % ( self._netmsk_to_cidr(self.ipaddr, self._net_mask), ), "rc = s.nb.MgmtPort.set.gateway(1, '%s')" % (self._default_gw, ), "rc = s.nb.Methods.applyMgmtPortConfig()", "rc = s.nb.MgmtPort.set.adminstate(1, 'Down')", "rc = s.nb.Methods.applyMgmtPortConfig()", "rc = s.nb.MgmtPort.set.adminstate(1, 'Up')", "rc = s.nb.Methods.applyMgmtPortConfig()", "print 'eth0 setup returnCode={0}'.format(rc)"] command = "python -c \"" + "; ".join(cmd) + "\"" err, output = None, None output, err, _ = self.telnet.exec_command(command.encode("ascii")) self.class_logger.debug("Eth0 setup console output:\n%s" % (output, )) if err or "returnCode=FAILED" in output: message = "Cannot configure management interface. StdErr: %s, StdOut: %s" % (err, output, ) self.class_logger.error(message) raise SwitchException(message)
[docs] def rm_configdb(self, close_serial=True): """Remove configuration database. Args: close_serial(bool): Close telnet session in the end. Returns: None """ self.get_serial(timeout=15, with_login=True, wait_login=1) self.class_logger.info("Removing configuration data base of %s(%s)." % (self.name, self.ipaddr)) output_r, err = self.telnet.shell_command("cd /persistent", sudo=False) self.class_logger.debug("Database removing output, error %s, output: %s" % (err, output_r)) output_r, err = self.telnet.shell_command("ls | grep -v \"default-cfg\" | xargs -i -t sudo rm -rf {}", sudo=False) self.class_logger.debug("Database removing output, error %s, output: %s" % (err, output_r)) output_r, err = self.telnet.shell_command("ls -la /persistent/", sudo=True) self.class_logger.debug("DB directory after rm, error %s, output: %s" % (err, output_r)) if close_serial: self.close_serial() self.db_corruption = False
[docs] def console_clear_config(self): """Clear device configuration using console connection """ cmd = [ "from xmlrpclib import ServerProxy", "rc = -1", "rc = ServerProxy('http://127.0.0.1:8081/RPC2').nb.clearConfig()", "print 'clearConfig() returnCode={0}.'.format(rc)"] command = "python -c \"" + "; ".join(cmd) + "\"" output, err, _ = self.telnet.exec_command(command.encode("ascii")) if err: message = "Cannot perform clearConfig.\nCommand: %s.\nStdout: %s\nStdErr: %s" % (command, output, err) self.class_logger.error(message) self.db_corruption = True pytest.fail(message) else: if "returnCode=0." in output: self.class_logger.debug("ClearConfig finished. StdOut:\n%s" % (output, )) else: message = "ClearConfig failed. StdOut:\n%s" % (output, ) self.class_logger.error(message) pytest.fail(message)
[docs] def forced_clearconfig(self, wait_on=False, timeout=60): """Remove DB and restart necessary processes. Args: wait_on(bool): Wait untill necessary apps are in run state after processes restart. timeout(int): Time to wait until necessary apps are in run state. """ # Removing configuration data base. self.rm_configdb(close_serial=False) self.class_logger.info("Stopping switchpp processes...") self.telnet.exec_command("supervisorctl stop all", sudo=True) self.class_logger.info("Starting switchpp processes...") self.telnet.exec_command("supervisorctl start all", sudo=True) self.close_serial() if wait_on: self.waiton(timeout=timeout) # Set initial ports speed self.speed_preconfig()
[docs]class SwitchSimulated(SwitchONSGeneralMixin, SwitchGeneral): """Simulated Switch in LXC containers class. """ class_logger = loggers.ClassLogger()
[docs] def __init__(self, config, opts): """Initialize SwitchSimulated class. Args: config(dict): Configuration information. opts(OptionParser): py.test config.option object which contains all py.test cli options. Raises: SwitchException: incorrect switch path """ self.build_path = environment.get_absolute_build_path(opts.build_path) if self.build_path is None: raise SwitchException("Could not find path to switch binaries - %s." % (opts.build_path, )) self.class_logger.info("SimSwitch binaries path: %s." % self.build_path) self.cli_img_path = config['cli_img_path'] self.xmlrpcport = config['ip_port'] self.cli_delay = 0.5 super(SwitchSimulated, self).__init__(config, opts) for device_id in list(config['related_conf'].keys()): if config['related_conf'][device_id]['instance_type'] == "vlab": self.vlab_ip = config['related_conf'][device_id]['ip_host'] self.vlab_iface = config['related_conf'][device_id]['ip_iface'] break self.waiton_err_message = "LXC container is started but switch does not response on xmlrpc queries." self.popen_logfile = "switchpp%s.output.log" % (self.id, ) # SSH connection credentials: self.ssh_user = config['cli_user'] self.ssh_user_pass = config['cli_user_passw'] self.ssh_user_prompt = config['cli_user_prompt'] self.startup_time = 90
def _get_port_for_probe(self): return SwitchONSGeneralMixin._get_port_for_probe(self)
[docs] def start(self, wait_on=True): """Create and launch LXC container with switchpp. Args: wait_on(bool): Indicates if wait for device status """ self.class_logger.info("Starting LXC for switch with ip:%s port:%s..." % (self.ipaddr, self.port)) # Check if it is an altamodel. if os_path_isfile(os_path_join(self.build_path, "bin", "ons-fulcrum")): self.class_logger.info("AltaModel is found.") self.__class__.SWITCH_APP = {"FulcrumApp"} log_wrap_out, log_wrap_err = loggers.pipe_loggers("switchpp%s" % (self.id, ), self.popen_logfile) # sudo env LD_LIBRARY_PATH=$PWD/lib ./bin/ons-lxc -n 1 -i br0 -a 10.0.5.101/24 -p 52 lxc_id = str(int(self.port) - 8080) command = ["./ons-ctl", "start", "-n", lxc_id, "-i", self.vlab_iface, "-a", "%s/24" % self.ipaddr, "-p", str(self.ports_count)] self.class_logger.debug("LXC start command: %s" % (" ".join(command))) process = Popen(command, stdout=log_wrap_out, stderr=log_wrap_err, close_fds=True, cwd=os_path_join(self.build_path, "bin")) process = Popen(['lxc-wait', '-n', lxc_id, '-s', 'RUNNING'], stdout=log_wrap_out, stderr=log_wrap_err, close_fds=True) process.wait() # let's wait until device is up and running: if wait_on: time.sleep(5) self.waiton(timeout=self.startup_time) # Set On(True) status self.status = True return self.xmlproxy
[docs] def stop(self): """Terminate LXC container. """ lxc_id = str(int(self.port) - 8080) process = Popen(["lxc-stop", "-n", lxc_id], stdout=PIPE, close_fds=True) process.wait() process = Popen(['lxc-wait', '-n', lxc_id, '-s', 'STOPPED'], stdout=PIPE, close_fds=True) process.wait() # try to remove LXC containers files in case lxc-wait did not do this for some reason try: lxc_env = os_path_join(self.build_path, "lxc", str(self.id)) shutil.rmtree(lxc_env) except Exception: pass # try to restore tty settings try: Popen(["stty", "sane"]) except Exception: pass self.waitoff(timeout=60) self.status = False return True
[docs] def restart(self, wait_on=True, mode='powercycle'): """Restart LXC container. Args: wait_on(bool): Indicates if wait for device status mode(bool): restart mode. powercycle|ui """ self.stop() return self.start(wait_on) self.ui.connect()
[docs] def rm_configdb(self): """Remove configuration database. Raises: SwitchException: not implemented """ # TODO implement this for Simulated switch message = "This methods is not implemented for Simulated switch" raise SwitchException(message)
[docs] def get_processes(self, tc_name, skip_prcheck=None): """Gets procecces-to-PID dictionary. Args: tc_name(str): test case name skip_prcheck(list[str]): list of processes to skip PID verification """ p2pid = {} fpath = self.build_path + "/bin" # Create process-to-PID dictionary. output = list(self.execute_ssh_command("ps -aux")) processes = [line for line in output[0].split("\n") if fpath in line] for process in processes: clear_proc = [] for proc_elem in process.split(' '): if proc_elem is not '': clear_proc.append(proc_elem) for pname in clear_proc: if fpath in pname: p2pid[pname.split('/')[-1]] = clear_proc[1] return p2pid
[docs] def execute_ssh_command(self, command): """Executes command on switch. Args: command(str): ssh command to execute """ client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # Make connection and create shell. client.connect(self.ipaddr, self._sshtun_port, self.ssh_user, self.ssh_user_pass) shell = client.invoke_shell() # Execute command and get results. _, stdout, stderr = client.exec_command(command) data = self._read_command_output(stdout, stderr, 'both') # Close connection. shell.close() client.close() return data
[docs] def _read_command_output(self, stdout, stderr, ret_mode): """Read result of not-interactive command execution. Args: stdout(str): StdOut info stderr(str): StdErr info ret_mode(str): return mode. both|stderr|stdout """ if ret_mode.lower() == 'both': return stdout.read(), stderr.read() elif ret_mode.lower() == 'stderr': return stderr.read() else: return stdout.read()