class StcApp(TgnApp): """ TestCenter driver. Equivalent to TestCenter Application. """ def __init__(self, logger, api_wrapper): """ Set all kinds of application level objects - logger, api, etc. :param logger: python logger (e.g. logging.getLogger('log')) :param api_wrapper: api wrapper object inheriting and implementing StcApi base class. """ super(StcApp, self).__init__(logger, api_wrapper) StcObject.logger = self.logger StcObject.str_2_class = TYPE_2_OBJECT self.system = StcObject(objType='system', objRef='system1', parent=None) self.system.api = self.api self.system.logger = self.logger self.system.project = None def connect(self, lab_server=None): """ Create object and (optionally) connect to lab server. :param lab_server: optional lab server address. """ self.lab_server = lab_server if self.lab_server: self.api.perform('CSTestSessionConnect', Host=self.lab_server, CreateNewTestSession=True) # Every object creation/retrieval must come AFTER we connect to lab server (if needed). self.hw = self.system.get_child('PhysicalChassisManager') self.project = StcProject(parent=self.system) self.project.project = self.project def disconnect(self, terminate=True): """ Disconnect from lab server (if used) and reset configuration. :param terminate: True - terminate session, False - leave session on server. """ self.reset_config() if type(self.api) == StcRestWrapper: self.api.disconnect(terminate) if self.lab_server: self.api.perform('CSTestSessionDisconnect', Terminate=terminate) def load_config(self, config_file_name): """ Load configuration file from tcc or xml. Configuration file type is extracted from the file suffix - xml or tcc. :param config_file_name: full path to the configuration file. """ if type(self.api) == StcRestWrapper: self.api.ls.upload(config_file_name) config_file_name = path.basename(config_file_name) ext = path.splitext(config_file_name)[-1].lower() if ext == '.tcc': self.api.perform( 'LoadFromDatabase', DatabaseConnectionString=path.normpath(config_file_name)) elif ext == '.xml': self.api.perform('LoadFromXml', FileName=path.normpath(config_file_name)) else: raise ValueError( 'Configuration file type {} not supported.'.format(ext)) self.project.objects = {} self.project.get_children('port') def reset_config(self): self.api.perform('ResetConfig', config='system1') def save_config(self, config_file_name, server_folder='c:\\temp'): """ Save configuration file as tcc or xml. Configuration file type is extracted from the file suffix - xml or tcc. :param config_file_name: full path to the configuration file. :param server_temp_folder: folder on the server where the system will save the files before download. """ if type(self.api) == StcRestWrapper: config_file_name_full_path = config_file_name config_file_name = server_folder + '\\' + path.basename( config_file_name) ext = path.splitext(config_file_name)[-1].lower() if ext == '.tcc': rc = self.api.perform('SaveToTcc', FileName=path.normpath(config_file_name)) elif ext == '.xml': rc = self.api.perform('SaveAsXml', FileName=path.normpath(config_file_name)) else: raise ValueError( 'Configuration file type {0} not supported.'.format(ext)) if type(self.api) == StcRestWrapper: self.api.ls.download(rc['FileName'], config_file_name_full_path) def clear_results(self): self.project.clear_results() # # Online commands. # All commands assume that all ports are reserved and port objects exist under project. # def send_arp_ns(self): StcObject.send_arp_ns( *self.project.get_objects_or_children_by_type('port')) def get_arp_cache(self): return StcObject.get_arp_cache( *self.project.get_objects_or_children_by_type('port')) # # Devices commands. # def start_devices(self): """ Start all devices. It is the test responsibility to wait for devices to reach required state. """ self._command_devices('DeviceStart') def stop_devices(self): self._command_devices('DeviceStop') def _command_devices(self, command): self.project.command_devices(command, 4) self.project.test_command_rc('Status') time.sleep(4) # # Traffic commands. # def start_traffic(self, blocking=False): self.project.start_ports(blocking) def stop_traffic(self): self.project.stop_ports() def wait_traffic(self): self.project.wait_traffic() # # Sequencer commands. # def sequencer_command(self, command): """ Perform sequencer command. :param command: sequencer command. :type command: testcenter.stc_app.StcSequencerOperation """ if command != StcSequencerOperation.wait: self.project.command(command.value) else: self.project.wait()
class StcStats: """Represents statistics view. The statistics dictionary represents a table: Statistics Name | Object 1 Value | Object 2 Value | ... object | | | parents | | | topLevelName | | | Stat 1 | | | ... For example, generatorportresults statistics for two ports might look like the following: Statistics Name | Object 1 Value | Object 2 Value object | analyzerportresults1 | analyzerportresults2 parents | project1/port1/analyzer1 | project1/port2/analyzer2 topLevelName | Port 1 | Port 2 GeneratorFrameCount | 1000 | 2000 ... """ def __init__(self, view: str) -> None: """Subscribe to view with default configuration type as defined by config_2_type. :param view: statistics view to subscribe to. If view is None it is the test responsibility to subscribe with specific config_type. """ self.rds = None self.statistics = TgnObjectsDict() if view: self.subscribe(view) def subscribe(self, view: str, config_type: Optional[str] = None) -> None: """Subscribe to statistics view. :param view: statistics view to subscribe to. :param config_type: configuration type to subscribe to. """ if view.lower() in view_2_config_type: if not config_type: config_type = view_2_config_type[view.lower()] rds = StcObject.project.api.subscribe( Parent=StcObject.project.ref, ResultParent=StcObject.project.ref, ConfigType=config_type, ResultType=view) self.rds = StcObject(objType="ResultDataSet", parent=StcObject.project, objRef=rds) else: StcObject.project.get_children("DynamicResultView") drv = StcObject.project.get_object_by_name(view) rc = StcObject.project.command("SubscribeDynamicResultView", DynamicResultView=drv.ref) self.rds = StcObject(objType="DynamicResultView", parent=StcObject.project, objRef=rc["DynamicResultView"]) def unsubscribe(self) -> None: """UnSubscribe from statistics view.""" StcObject.project.api.unsubscribe(self.rds.ref) def read_stats( self, obj_id_stat: Optional[str] = "topLevelName") -> TgnObjectsDict: """Reads the statistics view from STC and saves it in statistics dictionary. :param obj_id_stat: which statistics name to use as object ID, sometimes topLevelName is not meaningful and it is better to use other unique identifier like stream ID. """ self.statistics = TgnObjectsDict() if self.rds.type == "dynamicresultview": self._read_custom_view() else: self._read_view(obj_id_stat) return self.statistics def get_column_stats(self, name: str) -> TgnObjectsDict: """Returns all statistics values for the requested statistics. N/A for custom views. :param name: requested statistic name. """ column_statistics = TgnObjectsDict() for obj, obj_values in self.statistics.items(): column_statistics[obj] = obj_values[name] return column_statistics # # Private methods. # def _read_custom_view(self): StcObject.project.command("RefreshResultView", ResultDataSet=self.rds.ref) StcObject.project.command("UpdateDynamicResultViewCommand", DynamicResultView=self.rds.ref) presentationResultQuery = self.rds.get_child("PresentationResultQuery") selectedProperties = presentationResultQuery.get_list_attribute( "SelectProperties") self.objs_stats = [] for rvd in presentationResultQuery.get_children("ResultViewData"): self.objs_stats.append( rvd.get_list_attribute("ResultData")[:len(selectedProperties)]) self._get_result_data(rvd, len(selectedProperties)) self.statistics = dict(zip(selectedProperties, zip(*self.objs_stats))) def _get_result_data(self, rvd, num_columns): StcObject.project.command("ExpandResultViewDataCommand", ResultViewData=rvd.ref) for child_rvd in rvd.get_children("ResultViewData"): if is_false(child_rvd.get_attribute("IsDummy")): self.objs_stats.append( child_rvd.get_list_attribute("ResultData")[:num_columns]) self._get_result_data(child_rvd, num_columns) def _read_view(self, obj_id_stat: Optional[str] = "topLevelName") -> None: StcObject.project.command("RefreshResultView", ResultDataSet=self.rds.ref) for page_number in range( 1, int(self.rds.get_attribute("TotalPageCount")) + 1): self.rds.set_attributes(PageNumber=page_number) for results in self.rds.get_objects_from_attribute( "ResultHandleList"): parent = results.get_object_from_attribute("parent") parents = parent.ref name = "" while parent != StcObject.project: if not name and parent.obj_type().lower() in ( "port", "emulateddevice", "streamblock"): name = parent.get_name() parent = parent.get_object_from_attribute("parent") parents = parent.ref + "/" + parents obj_stats = { "object": results.ref, "parents": parents, "topLevelName": name } obj_stats.update(results.get_attributes()) obj_stats.pop("parent", None) obj_stats.pop("Name", None) obj_stats.pop("resultchild-Sources", None) for stat in obj_stats: try: obj_stats[stat] = int(obj_stats[stat]) except ValueError: pass self.statistics[StcObject.project.get_object_by_name( obj_stats[obj_id_stat])] = obj_stats
class StcStats(object): """ Represents statistics view. The statistics dictionary represents a table: Statistics Name | Object 1 Value | Object 2 Value | ... object | | | parents | | | topLevelName | | | Stat 1 | | | ... For example, generatorportresults statistics for two ports might look like the following: Statistics Name | Object 1 Value | Object 2 Value object | analyzerportresults1 | analyzerportresults2 parents | project1/port1/analyzer1 | project1/port2/analyzer2 topLevelName | Port 1 | Port 2 GeneratorFrameCount | 1000 | 2000 ... """ statistics = {} def __init__(self, project, view): """ Subscribe to view with default configuration type as defined by config_2_type. :param project: parent project object for all statistics. :param view: statistics view to subscribe to. If view is None it is the test responsibility to subscribe with specific config_type. """ super(StcStats, self).__init__() self.project = project if view: self.subscribe(view) self.statistics = {} def subscribe(self, view, config_type=None): """ Subscribe to statistics view. :parama view: statistics view to subscribe to. :parama config_type: configuration type to subscribe to. """ if view.lower() in view_2_config_type: if not config_type: config_type = view_2_config_type[view.lower()] rds = self.project.api.subscribe(Parent=self.project.obj_ref(), ResultParent=self.project.obj_ref(), ConfigType=config_type, ResultType=view) self.rds = StcObject(objType='ResultDataSet', parent=self.project, objRef=rds) else: self.project.get_children('DynamicResultView') drv = self.project.get_object_by_name(view) rc = self.project.command('SubscribeDynamicResultView', DynamicResultView=drv.ref) self.rds = StcObject(objType='DynamicResultView', parent=self.project, objRef=rc['DynamicResultView']) def unsubscribe(self): """ UnSubscribe from statistics view. """ self.project.api.unsubscribe(self.rds.obj_ref()) def read_stats(self, *stats): """ Reads the statistics view from STC and saves it in statistics dictionary. :param stats: list of statistics names to read, empty list will read all statistics. Relevant for system (not dynamic) result views only. :todo: add support for user statistics. """ self.statistics = {} if self.rds.type == 'dynamicresultview': self._read_custom_view() else: self._read_view(*stats) def get_stats(self, row='topLevelName'): """ :param row: requested row (== statistic name) :returns: all statistics values for the requested row. """ return self.statistics[row] def get_object_stats(self, obj_id, obj_id_stat='topLevelName'): """ :param obj_id: requested object ID. :param obj_id_stat: which statistics name to use as object ID, sometimes topLevelName is not meaningful and it is better to use other unique identifier like stream ID. :returns: all statistics values for the requested object ID. """ obj_statistics = {} for counter in self.statistics.keys(): if self.statistics[counter]: obj_statistics[counter] = self.get_stat(obj_id, counter, obj_id_stat) return obj_statistics def get_stat(self, obj_id, counter, obj_id_stat='topLevelName'): """ :param obj_id: requested object id. :param counter: requested statistics (note that some statistics are not counters). :param obj_id_stat: which statistics name to use as object ID, sometimes topLevelName is not meaningful and it is better to use other unique identifier like stream ID. :returns: the value of the requested counter for the requested object ID. """ obj_index = self.statistics[obj_id_stat].index(obj_id) return self.statistics[counter][obj_index] def get_counter(self, obj_id, counter, obj_id_stat='topLevelName'): """ :param obj_id: requested object ID. :param counter: requested statistics (note that some statistics are not counters). :param obj_id_stat: which statistics name to use as object ID, sometimes topLevelName is not meaningful and it is better to use other unique identifier like stream ID. :returns: the int value of the requested counter for the requested object ID. """ return int(self.get_stat(obj_id, counter, obj_id_stat)) def get_all_stats(self, obj_id_stat='topLevelName'): all_statistics = OrderedDict() for obj_name in self.statistics[obj_id_stat]: all_statistics[obj_name] = self.get_object_stats(obj_name) return all_statistics def _read_custom_view(self): self.project.command('RefreshResultView', ResultDataSet=self.rds.ref) self.project.command('UpdateDynamicResultViewCommand', DynamicResultView=self.rds.ref) presentationResultQuery = self.rds.get_child('PresentationResultQuery') selectedProperties = presentationResultQuery.get_list_attribute('SelectProperties') self.objs_stats = [] for rvd in presentationResultQuery.get_children('ResultViewData'): self.objs_stats.append(rvd.get_list_attribute('ResultData')[:len(selectedProperties)]) self._get_result_data(rvd, len(selectedProperties)) self.statistics = dict(zip(selectedProperties, zip(*self.objs_stats))) def _get_result_data(self, rvd, num_columns): self.project.command('ExpandResultViewDataCommand', ResultViewData=rvd.ref) for child_rvd in rvd.get_children('ResultViewData'): if is_false(child_rvd.get_attribute('IsDummy')): self.objs_stats.append(child_rvd.get_list_attribute('ResultData')[:num_columns]) self._get_result_data(child_rvd, num_columns) def _read_view(self, *stats): objs_stats = [] self.project.command('RefreshResultView', ResultDataSet=self.rds.obj_ref()) for page_number in range(1, int(self.rds.get_attribute('TotalPageCount')) + 1): self.rds.set_attributes(PageNumber=page_number) for results in self.rds.get_objects_from_attribute('ResultHandleList'): parent = results.get_object_from_attribute('parent') parents = parent.obj_ref() name = '' while parent != self.project: if not name and parent.obj_type().lower() in ('port', 'emulateddevice', 'streamblock'): name = parent.get_name() parent = parent.get_object_from_attribute('parent') parents = parent.obj_ref() + '/' + parents obj_stats = ({'object': results.obj_ref(), 'parents': parents, 'topLevelName': name}) obj_stats.update(results.get_attributes(*stats)) obj_stats.pop('parent', None) obj_stats.pop('Name', None) obj_stats.pop('resultchild-Sources', None) objs_stats.append(obj_stats.values()) keys = obj_stats.keys() if objs_stats: self.statistics = dict(zip(keys, zip(*objs_stats)))
class StcApp(TgnApp): """TestCenter driver. Equivalent to TestCenter Application.""" def __init__(self, logger: logging.Logger, api_wrapper: Union[StcRestWrapper, StcTclWrapper]) -> None: """Set all kinds of application level objects - logger, api, etc. :param logger: python logger (e.g. logging.getLogger('log')) :param api_wrapper: api wrapper object inheriting and implementing StcApi base class. """ super().__init__(logger, api_wrapper) StcObject.logger = self.logger StcObject.str_2_class = TYPE_2_OBJECT self.system = StcObject(parent=None, objType="system", objRef="system1") self.system.api = self.api self.system.logger = self.logger self.lab_server = None self.project: StcProject = None self.hw = None def connect(self, lab_server=None) -> None: """Create object and (optionally) connect to lab server. :param lab_server: optional lab server address. """ self.lab_server = lab_server if self.lab_server: self.api.perform("CSTestSessionConnect", Host=self.lab_server, CreateNewTestSession=True) # Every object creation/retrieval must come AFTER we connect to lab server (if needed). self.project = StcProject(parent=self.system) StcObject.project = self.project self.hw = self.system.get_child("PhysicalChassisManager") def disconnect(self, terminate=True) -> None: """Disconnect from lab server (if used) and reset configuration. :param terminate: True - terminate session, False - leave session on server. """ self.reset_config() if type(self.api) == StcRestWrapper: self.api.disconnect(terminate) if self.lab_server: self.api.perform("CSTestSessionDisconnect", Terminate=terminate) def load_config(self, config_file_name: str) -> None: """Load configuration file from tcc or xml. Configuration file type is extracted from the file suffix - xml or tcc. :param config_file_name: full path to the configuration file. """ if type(self.api) == StcRestWrapper: self.api.client.upload(config_file_name) config_file_name = path.basename(config_file_name) ext = path.splitext(config_file_name)[-1].lower() if ext == ".tcc": self.api.perform( "LoadFromDatabase", DatabaseConnectionString=path.normpath(config_file_name)) elif ext == ".xml": self.api.perform("LoadFromXml", FileName=path.normpath(config_file_name)) else: raise ValueError(f"Configuration file type {ext} not supported.") self.project.objects = {} self.project.get_children("port") def reset_config(self): self.api.perform("ResetConfig", config="system1") def save_config(self, config_file_name: str, server_folder: Optional[str] = "c:\\temp") -> None: """Save configuration file as tcc or xml. Configuration file type is extracted from the file suffix - xml or tcc. :param config_file_name: full path to the configuration file. :param server_folder: folder on the server where the system will save the files before download. """ if type(self.api) == StcRestWrapper: config_file_name_full_path = config_file_name config_file_name = server_folder + "\\" + path.basename( config_file_name) ext = path.splitext(config_file_name)[-1].lower() if ext == ".tcc": rc = self.api.perform("SaveToTcc", FileName=path.normpath(config_file_name)) elif ext == ".xml": rc = self.api.perform("SaveAsXml", FileName=path.normpath(config_file_name)) else: raise ValueError( "Configuration file type {0} not supported.".format(ext)) if type(self.api) == StcRestWrapper: self.api.client.download(rc["FileName"], config_file_name_full_path) def clear_results(self) -> None: self.project.clear_results() # # Online commands. # All commands assume that all ports are reserved and port objects exist under project. # def send_arp_ns(self) -> None: """Run ARP on all ports.""" StcObject.send_arp_ns(*self.project.ports.values()) def get_arp_cache(self): return StcObject.get_arp_cache( *self.project.get_objects_or_children_by_type("port")) # # Devices commands. # def start_devices(self) -> None: """Start all devices. It is the test responsibility to wait for devices to reach required state. """ self._command_devices("DeviceStart") def stop_devices(self) -> None: """Stop all devices.""" self._command_devices("DeviceStop") def _command_devices(self, command) -> None: self.project.command_devices(command, 4) self.project.test_command_rc("Status") time.sleep(4) # # Traffic commands. # def start_traffic(self, blocking: Optional[bool] = False) -> None: """Start traffic on all ports.""" self.project.start_ports(blocking) def stop_traffic(self) -> None: """Stop traffic on all ports.""" self.project.stop_ports() def wait_traffic(self) -> None: """Wait for traffic to end on all ports.""" self.project.wait_traffic() # # Sequencer commands. # def sequencer_command(self, command: StcSequencerOperation) -> None: """Perform sequencer command. :param command: sequencer command. """ if command != StcSequencerOperation.wait: self.project.command(command.value) else: self.project.wait()