# SPDX-License-Identifier: LicenseRef-CSSL-1.0 #--------------------------------------------------------------------- # # Required Python Version # Python 3.x # #--------------------------------------------------------------------- #to use isfile import logging #time.sleep import time import re import yaml import cls_cmd from cls_ci_helper import archiveArtifact class Module_UE: def __init__(self, module_name, node=None, filename="ci_infra.yaml"): with open(filename, 'r') as f: all_ues = yaml.load(f, Loader=yaml.FullLoader) m = all_ues.get(module_name) if m is None: raise Exception(f'no such module name "{module_name}" in "{filename}"') self.module_name = module_name self.host = m['Host'] if m['Host'] != "%%current_host%%" else None if node is None and self.host is None: raise Exception(f'node not provided when needed') elif node is not None and self.host is None: self.host = node self.cmd_dict = { "attach": m.get('AttachScript'), "detach": m.get('DetachScript'), "initialize": m.get('InitScript'), "terminate": m.get('TermScript'), "getNetwork": m.get('NetworkScript'), "check": m.get('CheckStatusScript'), "dataEnable": m.get('DataEnableScript'), "dataDisable": m.get('DataDisableScript'), } self.interface = m.get('IF') self.MTU = m.get('MTU') self.cmd_prefix = m.get('CmdPrefix') logging.info(f'initialized UE {self} from {filename}') t = m.get('Tracing') self.trace = t is not None if self.trace: if t.get('Start') is None or t.get('Stop') is None or t.get('Collect')is None : raise ValueError("need to have Start/Stop/Collect for tracing") self.cmd_dict["traceStart"] = t.get('Start') self.cmd_dict["traceStop"] = t.get('Stop') self._logCollect = t.get('Collect') if "%%log_dir%%" not in self._logCollect: raise ValueError(f"(At least one) LogCollect expression for {module_name} must contain \"%%log_dir%%\"") def __str__(self): return f"{self.module_name}@{self.host}" def __repr__(self): return self.__str__() def _command(self, cmd, silent=False, reportNonZero=True): if cmd is None: raise Exception("no command provided") with cls_cmd.getConnection(self.host) as c: response = c.run(cmd, silent=silent, reportNonZero=reportNonZero) return response #-----------------$ #PUBLIC Methods$ #-----------------$ def initialize(self): # we first terminate to make sure the UE has been stopped if self.cmd_dict["detach"]: self._command(self.cmd_dict["detach"], silent=True) self._command(self.cmd_dict["terminate"], silent=True) ret = self._command(self.cmd_dict["initialize"]) logging.info(f'For command: {ret.args} | return output: {ret.stdout} | Code: {ret.returncode}') if self.trace: self._enableTrace() # Here each UE returns differently for the successful initialization, requires check based on UE return ret.returncode == 0 def terminate(self, ctx=None): self._command(self.cmd_dict["terminate"]) if self.trace and ctx is not None: self._disableTrace() return self._collectTrace(ctx) return None def attach(self, attach_tries = 3, attach_timeout = 40): ip = None while attach_tries > 0: self._command(self.cmd_dict["attach"]) timeout = attach_timeout logging.debug("Waiting for IP address to be assigned") ip = self.getIP(silent=False, reportNonZero=True) while timeout > 0 and not ip: time.sleep(1) timeout -= 1 ip = self.getIP(silent=True, reportNonZero=False) if ip: break logging.warning(f"UE did not receive IP address after {attach_timeout} s, detaching") attach_timeout += 20 attach_tries -= 1 self._command(self.cmd_dict["detach"]) time.sleep(5) if ip: logging.debug(f'\u001B[1mUE IP Address for UE {self.module_name} is {ip}\u001B[0m') else: logging.debug(f'\u001B[1;37;41mUE IP Address for UE {self.module_name} Not Found!\u001B[0m') return ip def detach(self): self._command(self.cmd_dict["detach"]) def check(self): cmd = self.cmd_dict["check"] if cmd: return self._command(cmd).stdout else: logging.warning(f"requested status check of UE {self.getName()}, but operation is not supported") return f"UE {self.getName()} does not support status checking" def dataEnable(self): cmd = self.cmd_dict["dataEnable"] if cmd: self._command(cmd) return True else: message = f"requested enabling data of UE {self.getName()}, but operation is not supported" logging.error(message) return False def dataDisable(self): cmd = self.cmd_dict["dataDisable"] if cmd: self._command(cmd) return True else: message = f"requested disabling data of UE {self.getName()}, but operation is not supported" logging.error(message) return False def getIP(self, silent=True, reportNonZero=True): output = self._command(self.cmd_dict["getNetwork"], silent=silent, reportNonZero=reportNonZero) result = re.search(r'inet (?P[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)', output.stdout) if result and result.group('ip'): ip = result.group('ip') return ip return None def checkMTU(self): output = self._command(self.cmd_dict["getNetwork"], silent=True) result = re.search(r'mtu (?P[0-9]+)', output.stdout) if result and result.group('mtu') and int(result.group('mtu')) == self.MTU: logging.debug(f'\u001B[1mUE Module {self.module_name} NIC MTU is {self.MTU} as expected\u001B[0m') return True else: logging.debug(f'\u001B[1;37;41m UE module {self.module_name} has incorrect Module NIC MTU or MTU not found! Expected: {self.MTU} \u001B[0m') return False def getName(self): return self.module_name def getIFName(self): return self.interface def getHost(self): return self.host def getCmdPrefix(self): return self.cmd_prefix if self.cmd_prefix else "" def _enableTrace(self): logging.info(f'UE {self}: start UE tracing') self._command(self.cmd_dict["traceStart"]) def _disableTrace(self): logging.info(f'UE {self}: stop UE tracing') self._command(self.cmd_dict["traceStop"]) def _collectTrace(self, ctx): remote_dir = "/tmp/ue-trace-logs" with cls_cmd.getConnection(self.host) as c: # create a directory for log collection c.run(f'rm -rf {remote_dir}') ret = c.run(f'mkdir {remote_dir}') if ret.returncode != 0: logging.error("cannot create directory for log collection") return [] log_cmd = self._logCollect.replace('%%log_dir%%', remote_dir) self._command(log_cmd) # enumerate collected files ret = c.run(f'ls {remote_dir}/*') if ret.returncode != 0: logging.error("cannot enumerate log files") return [] log_files = [] # copy them to the executor one by one, and store in log_dir for f in ret.stdout.split("\n"): name = archiveArtifact(c, ctx, f) log_files.append(name) c.run(f'rm -rf {remote_dir}') return log_files