class IpmiLibrary(Sdr, Sel, Fru, Bmc, Picmg, Hpm, Chassis, Lan): ROBOT_LIBRARY_VERSION = '0.0.1' ROBOT_LIBRARY_SCOPE = 'TEST SUITE' def __init__(self, timeout=3.0, poll_interval=1.0): self._cache = ConnectionCache() self._timeout = timeout self._poll_interval = poll_interval @property def _ipmi(self): """Currently active connection.""" return self._active_connection._ipmi @property def _cp(self): """Property storage per connection.""" return self._active_connection._properties def wait_until_rmcp_is_ready(self, timeout=45): """Waits until the host can handle RMCP packets. `timeout` is given in Robot Framework's time format (e.g. 1 minute 20 seconds) that is explained in the User Guide. """ timeout = robottime.timestr_to_secs(timeout) start_time = time.time() while time.time() < start_time + timeout: try: self._ipmi.session.rmcp_ping() return except TimeoutError: pass time.sleep(self._poll_interval) raise AssertionError('RMCP not ready in %s.' % (robottime.secs_to_timestr(timeout))) def open_ipmi_rmcp_connection(self, host, target_address, user='', password='', routing_information=None, port=623, alias=None): self.open_ipmi_lan_connection(host, target_address, user, password, routing_information, port, interface_type='rmcp', alias=alias) def open_ipmi_lan_connection(self, host, target_address, user='', password='', routing_information=None, port=623, interface_type='ipmitool', alias=None): """Opens a LAN connection to an IPMI shelf manager. `host` is the IP or hostname of the shelf manager. `target_address` the IPMI address to which the command should be sent. `user` and `password` are used to authenticate against the shelf manager. """ host = str(host) target_address = int_any_base(target_address) user = str(user) password = str(password) port = int_any_base(port) interface = pyipmi.interfaces.create_interface(interface_type) ipmi = pyipmi.create_connection(interface) ipmi.session.set_session_type_rmcp(host, port) ipmi.session.set_auth_type_user(user, password) self._info('Opening IPMI connection to %s:%d/%02Xh' % (host, port, target_address)) ipmi.session.establish() target = pyipmi.Target(target_address, routing_information) ipmi.target = target connection = IpmiConnection(ipmi, target) self._active_connection = connection return self._cache.register(connection, alias) def open_ipmi_aardvark_connection(self, port_or_serial, target_address, slave_address=0x20, routing_information=None, alias=None, enable_i2c_pullups=True): """Opens an Aardvark connection to the IPMB. `target_address` is the IPMB address to which the command should be sent. With the `serial_number` the aardvark device can be specified. If `None` is set the first is selected. """ target_address = int_any_base(target_address) slave_address = int_any_base(slave_address) if isinstance(port_or_serial, basestring) and '-' in port_or_serial: serial = port_or_serial port = None self._info('Opening Aardvark adapter with serial %s' % (port_or_serial, )) else: port = int(port_or_serial) serial = None self._info('Opening Aardvark adapter on port %d' % (port, )) interface = pyipmi.interfaces.create_interface( 'aardvark', slave_address=slave_address, port=port, serial_number=serial, enable_i2c_pullups=enable_i2c_pullups) ipmi = pyipmi.create_connection(interface) target = pyipmi.Target(target_address, routing_information) ipmi.target = target self._info('Opening IPMI aardvark connection to %02Xh' % target_address) connection = IpmiConnection(ipmi, target) self._active_connection = connection return self._cache.register(connection, alias) def switch_ipmi_connection(self, index_or_alias): """Switches between active connections using an index or alias. The index is got from `Open IPMI Connection` keyword, and an alias can be given to it. Returns the index of previously active conection. """ old_index = self._cache.current_index self._active_connection = self._cache.switch(index_or_alias) return old_index def currently_active_ipmi_connection(self): """Returns the index of the currently active IPMI connection.""" return self._cache.current_index def close_all_ipmi_connections(self): """Closes all open connections and empties the connection cache. After this keyword, new indexes got from the `Open Connection` keyword are reset to 1. This keyword should be used in a test or suite teardown to make sure all connections are closed. """ self._active_connection = self._cache.close_all() def close_ipmi_connection(self, loglevel=None): """Closes the current connection. """ self._active_connection.close() def wait_until_connection_is_ready(self): """*DEPRECATED*""" start_time = time.time() while time.time() < start_time + self._timeout: output, rc = self._ipmi.interface._run_ipmitool( self._ipmi.target, 'bmc info') if rc != 0: time.sleep(self._poll_interval) else: return def is_ipmc_accessible(self): return self._ipmi.is_ipmc_accessible() def _run_ipmitool_checked(self, cmd): """*DEPRECATED*""" output, rc = self._ipmi.interface._run_ipmitool(self._ipmi.target, cmd) if rc != 0: raise AssertionError('return code was %d' % rc) return output def set_timeout(self, timeout): """Sets the timeout used in `Wait Until X` keywords to the given value. `timeout` is given in Robot Framework's time format (e.g. 1 minute 20 seconds) that is explained in the User Guide. The old timeout is returned and can be used to restore it later. Example. | ${tout}= | Set Timeout | 2 minute 30 seconds | | Do Something | | Set Timeout | ${tout} | """ old = getattr(self, '_timeout', 3.0) self._timeout = robottime.timestr_to_secs(timeout) return robottime.secs_to_timestr(old) def set_poll_interval(self, poll_interval): """Sets the poll interval used in `Wait Until X` keywords to the given value. `poll_interval` is given in Robot Framework's time format. The old poll interval is returend. For more details see `Set Timeout`. """ old = getattr(self, '_poll_interval', 1.0) self._poll_interval = robottime.timestr_to_secs(poll_interval) return robottime.secs_to_timestr(old) def send_raw_command(self, *bytes): """Sends a raw IPMI command. `bytes` can either be a list or serveral scalar values. If a LUN other then zero is needed, it can be given with the first value of the list by prepending 'lun=', eg. lun=3. Example: | ${values}= | Create List | 0x06 | 0x01 | | | Send Raw Command | ${values} | | | | # BMC info command | Send Raw Command | 0x06 | 0x01 | | | # same as above | Send Raw Command | lun=3 | 0x3e | 0x62 | ... | # LUN other than zero """ if isinstance(bytes[0], list): bytes = bytes[0] lun = 0 if len(bytes) > 0 and bytes[0].startswith('lun='): lun = int_any_base(bytes[0][4:]) bytes = bytes[1:] if len(bytes) < 2: raise RuntimeError('netfn and/or cmdid missing') bytes = [int_any_base(b) for b in bytes] raw = ''.join([chr(b) for b in bytes[1:]]) rsp = self._ipmi.raw_command(lun, bytes[0], raw) return [ord(b) for b in rsp] def send_ipmi_message(self, message, expected_cc=0x00): expected_cc = int_any_base(expected_cc) rsp = self._ipmi.send_message(message) cc = rsp.completion_code msg = 'Command returned with return completion code 0x%02x, ' \ 'but should be 0x%02x' % (cc, expected_cc) asserts.assert_equal(expected_cc, cc, msg, values=False) return rsp def create_message_request(self, name): return pyipmi.msgs.create_request_by_name(name) def _warn(self, msg): self._log(msg, 'WARN') def _info(self, msg): self._log(msg, 'INFO') def _debug(self, msg): self._log(msg, 'DEBUG') def _trace(self, msg): self._log(msg, 'TRACE') def _log(self, msg, level=None): self._is_valid_log_level(level, raise_if_invalid=True) msg = msg.strip() if level is None: level = self._default_log_level if msg != '': print '*%s* %s' % (level.upper(), msg) def _is_valid_log_level(self, level, raise_if_invalid=False): if level is None: return True if isinstance(level, basestring) and \ level.upper() in ['TRACE', 'DEBUG', 'INFO', 'WARN', 'HTML']: return True if not raise_if_invalid: return False raise AssertionError("Invalid log level '%s'" % level)
def __init__(self, timeout=3.0, poll_interval=1.0): self._cache = ConnectionCache() self._timeout = timeout self._poll_interval = poll_interval
class IpmiLibrary(Sdr, Sel, Fru, Bmc, Picmg, Hpm, Chassis, Lan): ROBOT_LIBRARY_VERSION = '0.0.1' ROBOT_LIBRARY_SCOPE = 'TEST SUITE' def __init__(self, timeout=3.0, poll_interval=1.0): self._cache = ConnectionCache() self._timeout = timeout self._poll_interval = poll_interval @property def _ipmi(self): """Currently active connection.""" return self._active_connection._ipmi @property def _cp(self): """Property storage per connection.""" return self._active_connection._properties def wait_until_rmcp_is_ready(self, timeout=45): """Waits until the host can handle RMCP packets. `timeout` is given in Robot Framework's time format (e.g. 1 minute 20 seconds) that is explained in the User Guide. """ timeout = robottime.timestr_to_secs(timeout) start_time = time.time() while time.time() < start_time + timeout: try: self._ipmi.session.rmcp_ping() return except TimeoutError: pass time.sleep(self._poll_interval) raise AssertionError('RMCP not ready in %s.' % (robottime.secs_to_timestr(timeout))) def open_ipmi_rmcp_connection(self, host, target_address, user='', password='', routing_information=None, port=623, alias=None): self.open_ipmi_lan_connection(host, target_address, user, password, routing_information, port, interface_type='rmcp', alias=alias) def open_ipmi_lan_connection(self, host, target_address, user='', password='', routing_information=None, port=623, interface_type='ipmitool', alias=None): """Opens a LAN connection to an IPMI shelf manager. `host` is the IP or hostname of the shelf manager. `target_address` the IPMI address to which the command should be sent. `user` and `password` are used to authenticate against the shelf manager. """ host = str(host) target_address = int_any_base(target_address) user = str(user) password = str(password) port = int_any_base(port) interface = pyipmi.interfaces.create_interface(interface_type) ipmi = pyipmi.create_connection(interface) ipmi.session.set_session_type_rmcp(host, port) ipmi.session.set_auth_type_user(user, password) self._info('Opening IPMI connection to %s:%d/%02Xh' % (host, port, target_address)) ipmi.session.establish() target = pyipmi.Target(target_address, routing_information) ipmi.target = target connection = IpmiConnection(ipmi, target) self._active_connection = connection return self._cache.register(connection, alias) def open_ipmi_aardvark_connection(self, port_or_serial, target_address, slave_address=0x20, routing_information=None, alias=None, enable_i2c_pullups=True): """Opens an Aardvark connection to the IPMB. `target_address` is the IPMB address to which the command should be sent. With the `serial_number` the aardvark device can be specified. If `None` is set the first is selected. """ target_address = int_any_base(target_address) slave_address = int_any_base(slave_address) if isinstance(port_or_serial, basestring) and '-' in port_or_serial: serial = port_or_serial port = None self._info('Opening Aardvark adapter with serial %s' % (port_or_serial,)) else: port = int(port_or_serial) serial = None self._info('Opening Aardvark adapter on port %d' % (port,)) interface = pyipmi.interfaces.create_interface('aardvark', slave_address=slave_address, port=port, serial_number=serial, enable_i2c_pullups=enable_i2c_pullups) ipmi = pyipmi.create_connection(interface) target = pyipmi.Target(target_address, routing_information) ipmi.target = target self._info('Opening IPMI aardvark connection to %02Xh' % target_address) connection = IpmiConnection(ipmi, target) self._active_connection = connection return self._cache.register(connection, alias) def switch_ipmi_connection(self, index_or_alias): """Switches between active connections using an index or alias. The index is got from `Open IPMI Connection` keyword, and an alias can be given to it. Returns the index of previously active conection. """ old_index = self._cache.current_index self._active_connection = self._cache.switch(index_or_alias) return old_index def currently_active_ipmi_connection(self): """Returns the index of the currently active IPMI connection.""" return self._cache.current_index def close_all_ipmi_connections(self): """Closes all open connections and empties the connection cache. After this keyword, new indexes got from the `Open Connection` keyword are reset to 1. This keyword should be used in a test or suite teardown to make sure all connections are closed. """ self._active_connection = self._cache.close_all() def close_ipmi_connection(self, loglevel=None): """Closes the current connection. """ self._active_connection.close() def wait_until_connection_is_ready(self): """*DEPRECATED*""" start_time = time.time() while time.time() < start_time + self._timeout: output, rc = self._ipmi.interface._run_ipmitool( self._ipmi.target, 'bmc info') if rc != 0: time.sleep(self._poll_interval) else: return def is_ipmc_accessible(self): return self._ipmi.is_ipmc_accessible() def _run_ipmitool_checked(self, cmd): """*DEPRECATED*""" output, rc = self._ipmi.interface._run_ipmitool( self._ipmi.target, cmd) if rc != 0: raise AssertionError('return code was %d' % rc) return output def set_timeout(self, timeout): """Sets the timeout used in `Wait Until X` keywords to the given value. `timeout` is given in Robot Framework's time format (e.g. 1 minute 20 seconds) that is explained in the User Guide. The old timeout is returned and can be used to restore it later. Example. | ${tout}= | Set Timeout | 2 minute 30 seconds | | Do Something | | Set Timeout | ${tout} | """ old = getattr(self, '_timeout', 3.0) self._timeout = robottime.timestr_to_secs(timeout) return robottime.secs_to_timestr(old) def set_poll_interval(self, poll_interval): """Sets the poll interval used in `Wait Until X` keywords to the given value. `poll_interval` is given in Robot Framework's time format. The old poll interval is returend. For more details see `Set Timeout`. """ old = getattr(self, '_poll_interval', 1.0) self._poll_interval = robottime.timestr_to_secs(poll_interval) return robottime.secs_to_timestr(old) def send_raw_command(self, *bytes): """Sends a raw IPMI command. `bytes` can either be a list or serveral scalar values. If a LUN other then zero is needed, it can be given with the first value of the list by prepending 'lun=', eg. lun=3. Example: | ${values}= | Create List | 0x06 | 0x01 | | | Send Raw Command | ${values} | | | | # BMC info command | Send Raw Command | 0x06 | 0x01 | | | # same as above | Send Raw Command | lun=3 | 0x3e | 0x62 | ... | # LUN other than zero """ if isinstance(bytes[0], list): bytes = bytes[0] lun = 0 if len(bytes) > 0 and bytes[0].startswith('lun='): lun = int_any_base(bytes[0][4:]) bytes = bytes[1:] if len(bytes) < 2: raise RuntimeError('netfn and/or cmdid missing') bytes = [ int_any_base(b) for b in bytes ] raw = ''.join([chr(b) for b in bytes[1:]]) rsp = self._ipmi.raw_command(lun, bytes[0], raw) return [ord(b) for b in rsp] def send_ipmi_message(self, message, expected_cc=0x00): expected_cc = int_any_base(expected_cc) rsp = self._ipmi.send_message(message) cc = rsp.completion_code msg = 'Command returned with return completion code 0x%02x, ' \ 'but should be 0x%02x' % (cc, expected_cc) asserts.assert_equal(expected_cc, cc, msg, values=False) return rsp def create_message_request(self, name): return pyipmi.msgs.create_request_by_name(name) def _warn(self, msg): self._log(msg, 'WARN') def _info(self, msg): self._log(msg, 'INFO') def _debug(self, msg): self._log(msg, 'DEBUG') def _trace(self, msg): self._log(msg, 'TRACE') def _log(self, msg, level=None): self._is_valid_log_level(level, raise_if_invalid=True) msg = msg.strip() if level is None: level = self._default_log_level if msg != '': print '*%s* %s' % (level.upper(), msg) def _is_valid_log_level(self, level, raise_if_invalid=False): if level is None: return True if isinstance(level, basestring) and \ level.upper() in ['TRACE', 'DEBUG', 'INFO', 'WARN', 'HTML']: return True if not raise_if_invalid: return False raise AssertionError("Invalid log level '%s'" % level)
class SnmpLibrary(_Traps): AGENT_NAME = "robotframework agent" ROBOT_LIBRARY_VERSION = __version__ ROBOT_LIBRARY_SCOPE = "TEST SUITE" def __init__(self): _Traps.__init__(self) self._active_connection = None self._cache = ConnectionCache() def open_snmp_v2c_connection(self, host, community_string=None, port=161, timeout=1.0, retries=5, alias=None): """Opens a new SNMP v2c connection to the given host. Set `community_string` that is used for this connection. If no `port` is given, the default port 161 is used. The connection `timeout` and `retries` can be configured. The optional `alias` is a name for the connection and it can be used for switching between connections, similarly as the index. See `Switch Connection` for more details about that. """ host = str(host) port = int(port) timeout = float(timeout) retries = int(retries) if alias: alias = str(alias) authentication_data = cmdgen.CommunityData(self.AGENT_NAME, community_string) transport_target = cmdgen.UdpTransportTarget((host, port), timeout, retries) connection = _SnmpConnection(authentication_data, transport_target) self._active_connection = connection return self._cache.register(self._active_connection, alias) # backwards compatibility, will be removed soon open_snmp_connection = open_snmp_v2c_connection def open_snmp_v3_connection( self, host, user, password="", encryption_password=None, authentication_protocol=None, encryption_protocol=None, port=161, timeout=1.0, retries=5, alias=None, ): """Opens a new SNMP v3 Connection to the given host. If no `port` is given, the default port 161 is used. Valid values for `authentication_protocol` are `MD5`, `SHA`, and None. Valid values for `encryption_protocol` are `DES`,`3DES`, `AES128`, `AES192`, `AES256` and None. The optional `alias` is a name for the connection and it can be used for switching between connections, similarly as the index. See `Switch Connection` for more details about that. """ host = str(host) port = int(port) user = str(user) timeout = float(timeout) retries = int(retries) if password is not None: password = str(password) if encryption_password is not None: encryption_password = str(encryption_password) if alias: alias = str(alias) if authentication_protocol is not None: authentication_protocol = authentication_protocol.upper() try: authentication_protocol = { None: cmdgen.usmNoAuthProtocol, "MD5": cmdgen.usmHMACMD5AuthProtocol, "SHA": cmdgen.usmHMACSHAAuthProtocol, }[authentication_protocol] except KeyError: raise RuntimeError("Invalid authentication protocol %s" % authentication_protocol) if encryption_protocol is not None: encryption_protocol = encryption_protocol.upper() try: encryption_protocol = { None: cmdgen.usmNoPrivProtocol, "DES": cmdgen.usmDESPrivProtocol, "3DES": cmdgen.usm3DESEDEPrivProtocol, "AES128": cmdgen.usmAesCfb128Protocol, "AES192": cmdgen.usmAesCfb192Protocol, "AES256": cmdgen.usmAesCfb256Protocol, }[encryption_protocol] except KeyError: raise RuntimeError("Invalid encryption protocol %s" % encryption_protocol) authentication_data = cmdgen.UsmUserData( user, password, encryption_password, authentication_protocol, encryption_protocol ) transport_target = cmdgen.UdpTransportTarget((host, port), timeout, retries) conn = _SnmpConnection(authentication_data, transport_target) self._active_connection = conn return self._cache.register(self._active_connection, alias) def close_snmp_connection(self): """Closes the current connection. """ self._active_connection.close() self._active_connection = None def close_all_snmp_connections(self): """Closes all open connections and empties the connection cache. After this keyword, new indexes got from the `Open Connection` keyword are reset to 1. This keyword should be used in a test or suite teardown to make sure all connections are closed. """ self._active_connection = self._cache.close_all() self._active_connection = None def switch_snmp_connection(self, index_or_alias): """Switches between active connections using an index or alias. The index is got from `Open Connection` keyword, and an alias can be given to it. Returns the index of previously active connection. """ old_index = self._cache.current_index self._active_connection = self._cache.switch(index_or_alias) return old_index def add_mib_search_path(self, path): """Adds a path to the MIB search path. Example: | Add MIB Search Path | /usr/share/mibs/ | """ self._info("Adding MIB path %s" % (path,)) if not os.path.exists(path): raise RuntimeError('Path "%s" does not exist' % path) paths = self._active_connection.builder.getMibPath() paths += (path,) self._debug("New paths: %s" % " ".join(paths)) self._active_connection.builder.setMibPath(*paths) def preload_mibs(self, *names): """Preloads MIBs. Preloading MIBs can be useful in cases where the `Get`- or `Set`-keyword should be executed as fast as possible. `names` can either be a list of MIB names or can be empty in which case all available MIBs are preloaded. This keyword should be used within the test setup. Note: Preloading all MIBs take a long time. """ if len(names): self._info("Preloading MIBs %s" % " ".join(list(names))) else: self._info("Preloading all available MIBs") self._active_connection.builder.loadModules(*names) def _get(self, oid, idx=(0,), expect_display_string=False): if self._active_connection is None: raise RuntimeError("No transport host set") idx = utils.parse_idx(idx) oid = utils.parse_oid(oid) + idx error_indication, error, _, var = self._active_connection.cmd_gen.getCmd( self._active_connection.authentication_data, self._active_connection.transport_target, oid ) if error_indication is not None: raise RuntimeError("SNMP GET failed: %s" % error_indication) if error != 0: raise RuntimeError("SNMP GET failed: %s" % error.prettyPrint()) oid, obj = var[0] if isinstance(obj, rfc1905.NoSuchInstance): raise RuntimeError("Object with OID %s not found" % utils.format_oid(oid)) if expect_display_string: if not univ.OctetString().isSuperTypeOf(obj): raise RuntimeError("Returned value is not an octetstring") value = obj.prettyOut(obj) elif univ.OctetString().isSuperTypeOf(obj): value = obj.asNumbers() else: value = obj.prettyOut(obj) self._info("OID %s has value %s" % (utils.format_oid(oid), value)) return value def get(self, oid, idx=(0,)): """Does a SNMP GET request for the specified 'oid'. The optional `idx` can be specified as tuple or as a string and defaults to `.0`. Examples: | ${value}= | Get | SNMPv2-MIB::sysDescr | | | ${value}= | Get | .1.3.6.1.2.1.1.1 | | | ${value}= | Get | .iso.org.6.internet.2.1.1.1 | | | ${value}= | Get | sysDescr | | | ${value}= | Get | ifDescr | 2 | """ return self._get(oid, idx) def get_display_string(self, oid, idx=(0,)): """Does a SNMP GET request for the specified 'oid' and convert it to display string. For more information and an example see `Get`. """ return self._get(oid, idx, expect_display_string=True) def _set(self, *oid_values): for oid, value in oid_values: self._info("Setting OID %s to %s" % (utils.format_oid(oid), value)) error_indication, error, _, var = self._active_connection.cmd_gen.setCmd( self._active_connection.authentication_data, self._active_connection.transport_target, *oid_values ) if error_indication is not None: raise RuntimeError("SNMP SET failed: %s" % error_indication) if error != 0: raise RuntimeError("SNMP SET failed: %s" % error.prettyPrint()) def set(self, oid, value, idx=(0,)): """Does a SNMP SET request. See `Get` for more information on possible OID notations. Automatic converting to the SNMP type expected by the remote system is only supported for OIDs for which there is a MIB describing it. If you want to use an OID which is not described by a MIB, you'll have to use the `Set XXX`-keyword or `Convert To XXX`-keyword. The optional `idx` is appended to the OID (defaults to `.0`). Example: | Set | SNMPv2::sysDescr | New System Description | """ if self._active_connection is None: raise RuntimeError("No transport host set") idx = utils.parse_idx(idx) oid = utils.parse_oid(oid) + idx self._set((oid, value)) def set_many(self, *oid_value_pairs): """ Does a SNMP SET request with multiple values. Set one or more values simultaneously. See `Set` for more information about the OID and possible value. After each value, you can give an index (`idx`, see example below) which is appended to the OIDs. By default the index is `.0`. Examples: | Set Many | SNMPv2::sysDescr | New System Description | | | ... | SNMPv2-MIB::sysName | New System Name | | | Set Many | IF-MIB::ifDescr | IF1 Description | idx=1 | | ... | IF-MIB::ifDescr | IF2 Description | idx=2 | """ if self._active_connection is None: raise RuntimeError("No transport host set") args = list(oid_value_pairs) oid_values = list() try: while len(args): oid = args.pop(0) value = args.pop(0) possible_idx = args[0] if len(args) > 0 else "" if possible_idx.startswith("idx="): idx = args.pop(0)[4:] else: idx = (0,) idx = utils.parse_idx(idx) oid = utils.parse_oid(oid) + idx oid_values.append((oid, value)) except IndexError: raise RuntimeError("Invalid OID/value(/index) format") if len(oid_values) < 1: raise RuntimeError("You must specify at least one OID/value pair") self._set(*oid_values) def walk(self, oid): """Does a SNMP WALK request and returns the result as OID list.""" if self._active_connection is None: raise RuntimeError("No transport host set") self._info("Walk starts at OID %s" % (oid,)) oid = utils.parse_oid(oid) error_indication, error, _, var_bind_table = self._active_connection.cmd_gen.nextCmd( self._active_connection.authentication_data, self._active_connection.transport_target, oid ) if error_indication: raise RuntimeError("SNMP WALK failed: %s" % error_indication) if error != 0: raise RuntimeError("SNMP WALK failed: %s" % error.prettyPrint()) oids = list() for var_bind_table_row in var_bind_table: oid, obj = var_bind_table_row[0] oid = "".join((".", str(oid))) if obj.isSuperTypeOf(rfc1902.ObjectIdentifier()): obj = "".join((".", str(obj))) else: obj = obj.prettyOut(obj) self._info("%s: %s" % (oid, obj)) oids.append((oid, obj)) return oids def prefetch_oid_table(self, oid): """Prefetch the walk result of the given oid. Subsequent calls to the `Find OID By Value` keyword will use the prefetched result. """ oids = self.walk(oid) self._active_connection.prefetched_table[oid] = oids def find_oid_by_value(self, oid, value, strip=False): """Return the first OID that matches a value in a list.""" if self._active_connection.prefetched_table.has_key(oid): oids = self._active_connection.prefetched_table[oid] else: oids = self.walk(oid) for oid in oids: s = str(oid[1]) if strip is True: s = s.strip() if s == str(value): return oid[0] raise RuntimeError('Value "%s" not found.' % value) def find_index(self, index_length, *args): """Searches an index in given datasets. There are some SNMP tables where the index is an arbitrary one. In this case you have to walk through the table and find a row where some columns match your values. For example consider the following table: | =a= | =b= | =c= | =d= | =e= | | 2 | 2 | 2 | 3 | 3 | | 2 | 3 | 2 | 5 | 6 | You want to know the value of d where a is 2 and b is 3. | ${a}= | Walk ${oidOfA} | | | | | | | ${b}= | Walk ${oidOfB} | | | | | | | ${idx}= | Find Index | 1 | ${a} | 2 | ${b} | 3 | | ${valueOfD}= | Get | ${oidOfD} | index=${idx} | | | | The `index_length` parameter is the length of the part of the OID which denotes the index. Eg. if you have an OID .1.3.6.1.4.1234.1.2.3 and `index_length` is 2, the index would be (2,3). """ if len(args) % 2 != 0: raise RuntimeError("Called with an invalid amount of arguments") data = islice(args, 0, None, 2) match = islice(args, 1, None, 2) l = list() for e in izip(data, match): # match our desired value d = filter(lambda x: x[1] == e[1], e[0]) # we only need the index part of the oid d = map(lambda x: (utils.parse_oid(x[0])[-int(index_length) :]), d) # now convert the list of indices to a set d = set(d) l.append(d) # intersect all sets s = set(l[0]).intersection(*l[1:]) if len(s) == 0: raise RuntimeError("No index found for the given matches") if len(s) > 1: raise RuntimeError("Ambiguous match. Found %d matching indices" % len(s)) return s.pop() def get_index_from_oid(self, oid, length=1): """Return last part of oid. If length is 1 an integer is returned. Otherwise a tuple of integers is returened. Example: | ${val}= | Get Index From OID | 1.3.6.1.2.1.2.2.1.2.10102 | 1 | """ length = int(length) oid = utils.parse_oid(oid) if length == 1: return oid[-1] else: return oid[-length:] def convert_to_octetstring(self, value): """Converts a value to a SNMP OctetString object.""" return rfc1902.OctetString(value) def convert_to_integer(self, value): """Converts a value to a SNMP Integer object.""" return rfc1902.Integer(value) def convert_to_integer32(self, value): """Converts a value to a SNMP Integer32 object.""" return rfc1902.Integer32(value) def convert_to_counter32(self, value): """Converts a value to a SNMP Counter32 object.""" return rfc1902.Counter32(value) def convert_to_counter64(self, value): """Converts a value to a SNMP Counter64 object.""" return rfc1902.Counter64(value) def convert_to_gauge32(self, value): """Converts a value to a SNMP Gauge32 object.""" return rfc1902.Gauge32(value) def convert_to_unsigned32(self, value): """Converts a value to a SNMP Unsigned32 object.""" return rfc1902.Unsigned32(value) def convert_to_timeticks(self, value): """Converts a value to a SNMP TimeTicks object.""" return rfc1902.TimeTicks(value) def convert_to_ip_address(self, value): """Converts a value to a SNMP IpAddress object. See `Set IP Address` for formats which are accepted for value. """ # Unfortunately, pysnmp does not support unicode strings if isinstance(value, basestring): value = str(value) return rfc1902.IpAddress(value) def set_octetstring(self, oid, value, idx=(0,)): """Does a SNMP SET request after converting the value to an OctetString SNMP Object. This is a convenient keyword, it does the same as a `Convert To OctetString` followed by a `Set`. """ value = self.convert_to_octetstring(value) self.set(oid, value, idx) def set_integer(self, oid, value, idx=(0,)): """Does a SNMP SET request after converting the value to an Integer SNMP Object. This is a convenient keyword, it does the same as a `Convert To Integer` followed by a `Set`. """ value = self.convert_to_integer(value) self.set(oid, value, idx) def set_integer32(self, oid, value, idx=(0,)): """Does a SNMP SET request after converting the value to an Integer32 SNMP Object. See also `Set Integer`. """ value = self.convert_to_integer32(value) self.set(oid, value, idx) def set_counter32(self, oid, value, idx=(0,)): """Does a SNMP SET request after converting the value to a Counter32 SNMP Object. See also `Set Integer`. """ value = self.convert_to_counter32(value) self.set(oid, value, idx) def set_counter64(self, oid, value, idx=(0,)): """Does a SNMP SET request after converting the value to a Counter64 SNMP Object. See also `Set Integer`. """ value = self.convert_to_counter64(value) self.set(oid, value, idx) def set_gauge32(self, oid, value, idx=(0,)): """Does a SNMP SET request after converting the value to a Gauge32 SNMP Object. See also `Set Integer`. """ value = self.convert_to_gauge32(value) self.set(oid, value, idx) def set_unsigned32(self, oid, value, idx=(0,)): """Does a SNMP SET request after converting the value to a Unsigned32 SNMP Object. See also `Set Integer`. """ value = self.convert_to_unsigned32(value) self.set(oid, value, idx) def set_timeticks(self, oid, value, idx=(0,)): """Does a SNMP SET request after converting the value to a TimeTicks SNMP Object. See also `Set Integer`. """ value = self.convert_to_timeticks(value) self.set(oid, value, idx) def set_ip_address(self, oid, value, idx=(0,)): """Does a SNMP SET request after converting the value to an IpAddress SNMP Object. The `value` can either be a string (dotted IPv4 address) or an iterable. See also `Set Integer`. Examples: | Set IP Address | ${oid} | 172.16.0.1 | | | | | ${myIp}= | Create List | ${172} | ${16} | ${0} | ${1} | | Set IP Address | ${oid} | ${myIp} | | | | """ value = self.convert_to_ip_address(value) self.set(oid, value, idx) def _warn(self, msg): self._log(msg, "WARN") def _info(self, msg): self._log(msg, "INFO") def _debug(self, msg): self._log(msg, "DEBUG") def _log(self, msg, level=None): self._is_valid_log_level(level, raise_if_invalid=True) msg = msg.strip() if level is None: level = self._default_log_level if msg != "": print "*%s* %s" % (level.upper(), msg) def _is_valid_log_level(self, level, raise_if_invalid=False): if level is None: return True if isinstance(level, basestring) and level.upper() in ["TRACE", "DEBUG", "INFO", "WARN", "HTML"]: return True if not raise_if_invalid: return False raise RuntimeError("Invalid log level '%s'" % level)
def __init__(self): _Traps.__init__(self) self._active_connection = None self._cache = ConnectionCache()
class AardvarkLibrary: """Robot Framework test library for the Totalphase Aardvark host adapter. """ ROBOT_LIBRARY_SCOPE = 'GLOBAL' ROBOT_LIBRARY_VERSION = __version__ def __init__(self, i2c_bitrate=100, spi_bitrate=100): self._cache = ConnectionCache() self._i2c_bitrate = i2c_bitrate self._spi_bitrate = spi_bitrate self._device = None def open_aardvark_adapter(self, port_or_serial=0, alias=None): """Opens a new Aardvark host adapter. The adapter to be used is identified by the port or by a serial number. By default the port 0 is used, which is sufficient if there is only one host adapter. If there are multiple adapters connected, you have to provide either the port or a serial number. The serial number must be given in the form NNNN-NNNNNN, otherwise the argument is interpreted as the port number. Possible already opened adapters are cached and it is possible to switch back to them using the `Switch Aardvark Adapter` keyword. It is possible to switch either using explicitly given `alias` or using the index returned by this keyword. Indexing start from 1 and is reset back to it by the `Close All Connections` keyword. """ port = None serial = None if isinstance(port_or_serial, basestring) and '-' in port_or_serial: logger.info('Opening Aardvark adapter with serial %s' % (port_or_serial,)) serial = port_or_serial else: port = int(port_or_serial) logger.info('Opening Aardvark adapter on port %d' % (port,)) device = pyaardvark.open(port=port, serial_number=serial) device.i2c_bitrate = self._i2c_bitrate device.spi_bitrate = self._spi_bitrate device.enable_i2c = True device.enable_spi = True device.spi_configure_mode(pyaardvark.SPI_MODE_3) self._device = device return self._cache.register(self._device, alias) def switch_aardvark_adapter(self, index_or_alias): """Switches between active Aardvark host adapters using an index or alias. Aliases can be given to `Open Aardvark Adapter` keyword wich also always returns the connection index. This keyword retruns the index of previous active connection. """ old_index = self._cache_current_index self._device = self._cache.switch(index_or_alias) return old_index def close_all_aardvark_adapters(self): """Closes all open Aardvark host adapters and empties the open adapters cache. If multiple adapters are opened, this keyword should be used in a test or suite teardown to make sure that all adapters are closed. After this keyword, new indexes returned by the `Open Conection` keyword are reset to 1. """ self._device = self._cache.close_all() def close_adapter(self): """Closes the current Aardvark host adapter. Use `Close All Aardvark Adapters` if you want to make sure all opened host adapters are closed. """ self._device.close() def set_i2c_bitrate(self, bitrate): """Sets the bitrate used during I2C transfers. The `bitrate` is given in kHz. This changes only the bitrate for the current adapter. The default value can be set when importing the library. """ self._device.i2c_bitrate(bitrate) def set_spi_bitrate(self, bitrate): """Sets the bitrate used during SPI transfers. The `bitrate` is given in kHz. This changes only the bitrate for the current adapter. The default value can be set when importing the library. """ self._device.spi_bitrate(bitrate) def enable_i2c_pullups(self, enable=True): """Enable (or disable) the I2C pullup resistors.""" if enable: logger.info('Enabling I2C pullup resistors.') else: logger.info('Disabling I2C pullup resistors.') self._device.i2c_pullups = enable def enable_traget_power(self, enable=True): """Enable (or disable) the target power.""" if enable: logger.info('Enabling target power.') else: logger.info('Disabling target power.') self._device.target_power = enable def i2c_master_read(self, address, length=1): """Perform an I2C master read access. Read `length` bytes from a slave device with the address given in the `address` argument. """ address = int_any_base(address) length = int_any_base(length) data = self._device.i2c_master_read(address, length) data = array.array('B', data) logger.info('Read %d bytes from %02xh: %s', length, address, ' '.join('%02x' % d for d in data)) return data def i2c_master_write(self, address, *data): """Perform an I2C master write access. Writes the given `data` to a slave device with address given in the `address` argument. The `data` argument can either be list of bytes, a whitespace separated list of bytes or a single byte. See the examples below. Both the `address` and `data` can be given either as strings or integers. Strings are parsed accoding to their prefix. Eg. `0x` denotes a hexadecimal number. Examples: | I2C Master Write | 0xa4 | 0x10 | | I2C Master Write | 0xa4 | 0x10 0x12 0x13 | | I2C Master Write | 0xa4 | 0x10 | 0x12 | 0x13 | """ address = int_any_base(address) if len(data) == 1: data = list_any_input(data[0]) else: data = [int_any_base(c) for c in data] logger.info('Writing %d bytes to %02xh: %s' % (len(data), address, ' '.join('%02x' % d for d in data))) data = ''.join('%c' % chr(c) for c in data) self._device.i2c_master_write(address, data) def i2c_master_write_read(self, address, length, *data): """Perform an I2C master write read access. First write the given `data` to a slave device, then read `length` bytes from it. For more information see the `I2C Master Read` and `I2C Master Write` keywords. Examples: | I2C Master Write Read | 0xa4 | 1 | 0x10 | | I2C Master Write Read | 0xa4 | 1 | 0x10 0x12 0x13 | | I2C Master Write Read | 0xa4 | 1 | 0x10 | 0x12 | 0x13 | """ address = int_any_base(address) length = int_any_base(length) if len(data) == 1: data = list_any_input(data[0]) else: data = [int_any_base(c) for c in data] logger.info('Writing %d bytes to %02xh: %s' % (len(data), address, ' '.join('%02x' % d for d in data))) data = ''.join('%c' % chr(c) for c in data) data = self._device.i2c_master_write_read(address, data, length) data = array.array('B', data) logger.info('Read %d bytes from %02xh: %s' % (length, address, ' '.join('%02x' % d for d in data))) return data def spi_transfer(self, *data): """Performs a SPI access. Writes a stream of bytes (given in `data`) on the SPI interface while reading back the same amount of bytes. The read back has the same length as the input data. If you want to read more data than writing on the bus you have to send dummy bytes. Examples: | SPI Write | 0x10 | | SPI Write | 0x10 0x12 0x13 | | SPI Write | 0x10 | 0x12 | 0x13 | | ${ret}= | SPI Write | 0x10 | 0x12 | 0x13 | # ${ret} is an array of 3 bytes | """ if len(data) == 1: data = list_any_input(data[0]) else: data = [int_any_base(c) for c in data] logger.info('Writing %d bytes: %s' % (len(data), ' '.join('%02x' % d for d in data))) data = ''.join('%c' % chr(c) for c in data) data = self._device.spi_write(data) data = array.array('B', data) logger.info('Read %d bytes: %s' % (len(data), ' '.join('%02x' % d for d in data))) return data
def __init__(self, i2c_bitrate=100, spi_bitrate=100): self._cache = ConnectionCache() self._i2c_bitrate = i2c_bitrate self._spi_bitrate = spi_bitrate self._device = None
class SnmpLibrary(_Traps): AGENT_NAME = 'robotframework agent' ROBOT_LIBRARY_VERSION = __version__ ROBOT_LIBRARY_SCOPE = 'TEST SUITE' def __init__(self): _Traps.__init__(self) self._active_connection = None self._cache = ConnectionCache() def open_snmp_v2c_connection(self, host, community_string=None, port=161, timeout=1.0, retries=5, alias=None): """Opens a new SNMP v2c connection to the given host. Set `community_string` that is used for this connection. If no `port` is given, the default port 161 is used. The connection `timeout` and `retries` can be configured. The optional `alias` is a name for the connection and it can be used for switching between connections, similarly as the index. See `Switch Connection` for more details about that. """ host = str(host) port = int(port) timeout = float(timeout) retries = int(retries) if alias: alias = str(alias) authentication_data = cmdgen.CommunityData(self.AGENT_NAME, community_string) transport_target = cmdgen.UdpTransportTarget((host, port), timeout, retries) connection = _SnmpConnection(authentication_data, transport_target) self._active_connection = connection return self._cache.register(self._active_connection, alias) # backwards compatibility, will be removed soon open_snmp_connection = open_snmp_v2c_connection def open_snmp_v3_connection(self, host, user, password='', encryption_password=None, authentication_protocol=None, encryption_protocol=None, port=161, timeout=1.0, retries=5, alias=None): """Opens a new SNMP v3 Connection to the given host. If no `port` is given, the default port 161 is used. Valid values for `authentication_protocol` are `MD5`, `SHA`, and None. Valid values for `encryption_protocol` are `DES`,`3DES`, `AES128`, `AES192`, `AES256` and None. The optional `alias` is a name for the connection and it can be used for switching between connections, similarly as the index. See `Switch Connection` for more details about that. """ host = str(host) port = int(port) user = str(user) timeout = float(timeout) retries = int(retries) if password is not None: password = str(password) if encryption_password is not None: encryption_password = str(encryption_password) if alias: alias = str(alias) if authentication_protocol is not None: authentication_protocol = authentication_protocol.upper() try: authentication_protocol = { None: cmdgen.usmNoAuthProtocol, 'MD5': cmdgen.usmHMACMD5AuthProtocol, 'SHA': cmdgen.usmHMACSHAAuthProtocol }[authentication_protocol] except KeyError: raise RuntimeError('Invalid authentication protocol %s' % authentication_protocol) if encryption_protocol is not None: encryption_protocol = encryption_protocol.upper() try: encryption_protocol = { None: cmdgen.usmNoPrivProtocol, 'DES': cmdgen.usmDESPrivProtocol, '3DES': cmdgen.usm3DESEDEPrivProtocol, 'AES128': cmdgen.usmAesCfb128Protocol, 'AES192': cmdgen.usmAesCfb192Protocol, 'AES256': cmdgen.usmAesCfb256Protocol, }[encryption_protocol] except KeyError: raise RuntimeError('Invalid encryption protocol %s' % encryption_protocol) authentication_data = cmdgen.UsmUserData(user, password, encryption_password, authentication_protocol, encryption_protocol) transport_target = cmdgen.UdpTransportTarget((host, port), timeout, retries) conn = _SnmpConnection(authentication_data, transport_target) self._active_connection = conn return self._cache.register(self._active_connection, alias) def close_snmp_connection(self): """Closes the current connection. """ self._active_connection.close() self._active_connection = None def close_all_snmp_connections(self): """Closes all open connections and empties the connection cache. After this keyword, new indexes got from the `Open Connection` keyword are reset to 1. This keyword should be used in a test or suite teardown to make sure all connections are closed. """ self._active_connection = self._cache.close_all() self._active_connection = None def switch_snmp_connection(self, index_or_alias): """Switches between active connections using an index or alias. The index is got from `Open Connection` keyword, and an alias can be given to it. Returns the index of previously active connection. """ old_index = self._cache.current_index self._active_connection = self._cache.switch(index_or_alias) return old_index def add_mib_search_path(self, path): """Adds a path to the MIB search path. Example: | Add MIB Search Path | /usr/share/mibs/ | """ self._info('Adding MIB path %s' % (path, )) if not os.path.exists(path): raise RuntimeError('Path "%s" does not exist' % path) paths = self._active_connection.builder.getMibPath() paths += (path, ) self._debug('New paths: %s' % ' '.join(paths)) self._active_connection.builder.setMibPath(*paths) def preload_mibs(self, *names): """Preloads MIBs. Preloading MIBs can be useful in cases where the `Get`- or `Set`-keyword should be executed as fast as possible. `names` can either be a list of MIB names or can be empty in which case all available MIBs are preloaded. This keyword should be used within the test setup. Note: Preloading all MIBs take a long time. """ if len(names): self._info('Preloading MIBs %s' % ' '.join(list(names))) else: self._info('Preloading all available MIBs') self._active_connection.builder.loadModules(*names) def _get(self, oid, idx=(0, ), expect_display_string=False): if self._active_connection is None: raise RuntimeError('No transport host set') idx = utils.parse_idx(idx) oid = utils.parse_oid(oid) + idx error_indication, error, _, var = \ self._active_connection.cmd_gen.getCmd( self._active_connection.authentication_data, self._active_connection.transport_target, oid ) if error_indication is not None: raise RuntimeError('SNMP GET failed: %s' % error_indication) if error != 0: raise RuntimeError('SNMP GET failed: %s' % error.prettyPrint()) oid, obj = var[0] if isinstance(obj, rfc1905.NoSuchInstance): raise RuntimeError('Object with OID %s not found' % utils.format_oid(oid)) if expect_display_string: if not univ.OctetString().isSuperTypeOf(obj): raise RuntimeError('Returned value is not an octetstring') value = obj.prettyOut(obj) elif univ.OctetString().isSuperTypeOf(obj): value = obj.asNumbers() else: value = obj.prettyOut(obj) self._info('OID %s has value %s' % (utils.format_oid(oid), value)) return value def get(self, oid, idx=(0, )): """Does a SNMP GET request for the specified 'oid'. The optional `idx` can be specified as tuple or as a string and defaults to `.0`. Examples: | ${value}= | Get | SNMPv2-MIB::sysDescr | | | ${value}= | Get | .1.3.6.1.2.1.1.1 | | | ${value}= | Get | .iso.org.6.internet.2.1.1.1 | | | ${value}= | Get | sysDescr | | | ${value}= | Get | ifDescr | 2 | """ return self._get(oid, idx) def get_display_string(self, oid, idx=(0, )): """Does a SNMP GET request for the specified 'oid' and convert it to display string. For more information and an example see `Get`. """ return self._get(oid, idx, expect_display_string=True) def _set(self, *oid_values): for oid, value in oid_values: self._info('Setting OID %s to %s' % (utils.format_oid(oid), value)) error_indication, error, _, var = \ self._active_connection.cmd_gen.setCmd( self._active_connection.authentication_data, self._active_connection.transport_target, *oid_values ) if error_indication is not None: raise RuntimeError('SNMP SET failed: %s' % error_indication) if error != 0: raise RuntimeError('SNMP SET failed: %s' % error.prettyPrint()) def set(self, oid, value, idx=(0, )): """Does a SNMP SET request. See `Get` for more information on possible OID notations. Automatic converting to the SNMP type expected by the remote system is only supported for OIDs for which there is a MIB describing it. If you want to use an OID which is not described by a MIB, you'll have to use the `Set XXX`-keyword or `Convert To XXX`-keyword. The optional `idx` is appended to the OID (defaults to `.0`). Example: | Set | SNMPv2::sysDescr | New System Description | """ if self._active_connection is None: raise RuntimeError('No transport host set') idx = utils.parse_idx(idx) oid = utils.parse_oid(oid) + idx self._set((oid, value)) def set_many(self, *oid_value_pairs): """ Does a SNMP SET request with multiple values. Set one or more values simultaneously. See `Set` for more information about the OID and possible value. After each value, you can give an index (`idx`, see example below) which is appended to the OIDs. By default the index is `.0`. Examples: | Set Many | SNMPv2::sysDescr | New System Description | | | ... | SNMPv2-MIB::sysName | New System Name | | | Set Many | IF-MIB::ifDescr | IF1 Description | idx=1 | | ... | IF-MIB::ifDescr | IF2 Description | idx=2 | """ if self._active_connection is None: raise RuntimeError('No transport host set') args = list(oid_value_pairs) oid_values = list() try: while len(args): oid = args.pop(0) value = args.pop(0) possible_idx = args[0] if len(args) > 0 else '' if possible_idx.startswith('idx='): idx = args.pop(0)[4:] else: idx = (0, ) idx = utils.parse_idx(idx) oid = utils.parse_oid(oid) + idx oid_values.append((oid, value)) except IndexError: raise RuntimeError('Invalid OID/value(/index) format') if len(oid_values) < 1: raise RuntimeError('You must specify at least one OID/value pair') self._set(*oid_values) def walk(self, oid): """Does a SNMP WALK request and returns the result as OID list.""" if self._active_connection is None: raise RuntimeError('No transport host set') self._info('Walk starts at OID %s' % (oid, )) oid = utils.parse_oid(oid) error_indication, error, _, var_bind_table = \ self._active_connection.cmd_gen.nextCmd( self._active_connection.authentication_data, self._active_connection.transport_target, oid ) if error_indication: raise RuntimeError('SNMP WALK failed: %s' % error_indication) if error != 0: raise RuntimeError('SNMP WALK failed: %s' % error.prettyPrint()) oids = list() for var_bind_table_row in var_bind_table: oid, obj = var_bind_table_row[0] oid = ''.join(('.', str(oid))) if obj.isSuperTypeOf(rfc1902.ObjectIdentifier()): obj = ''.join(('.', str(obj))) else: obj = obj.prettyOut(obj) self._info('%s: %s' % (oid, obj)) oids.append((oid, obj)) return oids def prefetch_oid_table(self, oid): """Prefetch the walk result of the given oid. Subsequent calls to the `Find OID By Value` keyword will use the prefetched result. """ oids = self.walk(oid) self._active_connection.prefetched_table[oid] = oids def find_oid_by_value(self, oid, value, strip=False): """Return the first OID that matches a value in a list.""" if self._active_connection.prefetched_table.has_key(oid): oids = self._active_connection.prefetched_table[oid] else: oids = self.walk(oid) for oid in oids: s = str(oid[1]) if strip is True: s = s.strip() if s == str(value): return oid[0] raise RuntimeError('Value "%s" not found.' % value) def find_index(self, index_length, *args): """Searches an index in given datasets. There are some SNMP tables where the index is an arbitrary one. In this case you have to walk through the table and find a row where some columns match your values. For example consider the following table: | =a= | =b= | =c= | =d= | =e= | | 2 | 2 | 2 | 3 | 3 | | 2 | 3 | 2 | 5 | 6 | You want to know the value of d where a is 2 and b is 3. | ${a}= | Walk ${oidOfA} | | | | | | | ${b}= | Walk ${oidOfB} | | | | | | | ${idx}= | Find Index | 1 | ${a} | 2 | ${b} | 3 | | ${valueOfD}= | Get | ${oidOfD} | index=${idx} | | | | The `index_length` parameter is the length of the part of the OID which denotes the index. Eg. if you have an OID .1.3.6.1.4.1234.1.2.3 and `index_length` is 2, the index would be (2,3). """ if len(args) % 2 != 0: raise RuntimeError('Called with an invalid amount of arguments') data = islice(args, 0, None, 2) match = islice(args, 1, None, 2) l = list() for e in izip(data, match): # match our desired value d = filter(lambda x: x[1] == e[1], e[0]) # we only need the index part of the oid d = map(lambda x: (utils.parse_oid(x[0])[-int(index_length):]), d) # now convert the list of indices to a set d = set(d) l.append(d) # intersect all sets s = set(l[0]).intersection(*l[1:]) if len(s) == 0: raise RuntimeError('No index found for the given matches') if len(s) > 1: raise RuntimeError('Ambiguous match. Found %d matching indices' % len(s)) return s.pop() def get_index_from_oid(self, oid, length=1): """Return last part of oid. If length is 1 an integer is returned. Otherwise a tuple of integers is returened. Example: | ${val}= | Get Index From OID | 1.3.6.1.2.1.2.2.1.2.10102 | 1 | """ length = int(length) oid = utils.parse_oid(oid) if length == 1: return oid[-1] else: return oid[-length:] def convert_to_octetstring(self, value): """Converts a value to a SNMP OctetString object.""" return rfc1902.OctetString(value) def convert_to_integer(self, value): """Converts a value to a SNMP Integer object.""" return rfc1902.Integer(value) def convert_to_integer32(self, value): """Converts a value to a SNMP Integer32 object.""" return rfc1902.Integer32(value) def convert_to_counter32(self, value): """Converts a value to a SNMP Counter32 object.""" return rfc1902.Counter32(value) def convert_to_counter64(self, value): """Converts a value to a SNMP Counter64 object.""" return rfc1902.Counter64(value) def convert_to_gauge32(self, value): """Converts a value to a SNMP Gauge32 object.""" return rfc1902.Gauge32(value) def convert_to_unsigned32(self, value): """Converts a value to a SNMP Unsigned32 object.""" return rfc1902.Unsigned32(value) def convert_to_timeticks(self, value): """Converts a value to a SNMP TimeTicks object.""" return rfc1902.TimeTicks(value) def convert_to_ip_address(self, value): """Converts a value to a SNMP IpAddress object. See `Set IP Address` for formats which are accepted for value. """ # Unfortunately, pysnmp does not support unicode strings if isinstance(value, basestring): value = str(value) return rfc1902.IpAddress(value) def set_octetstring(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to an OctetString SNMP Object. This is a convenient keyword, it does the same as a `Convert To OctetString` followed by a `Set`. """ value = self.convert_to_octetstring(value) self.set(oid, value, idx) def set_integer(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to an Integer SNMP Object. This is a convenient keyword, it does the same as a `Convert To Integer` followed by a `Set`. """ value = self.convert_to_integer(value) self.set(oid, value, idx) def set_integer32(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to an Integer32 SNMP Object. See also `Set Integer`. """ value = self.convert_to_integer32(value) self.set(oid, value, idx) def set_counter32(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to a Counter32 SNMP Object. See also `Set Integer`. """ value = self.convert_to_counter32(value) self.set(oid, value, idx) def set_counter64(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to a Counter64 SNMP Object. See also `Set Integer`. """ value = self.convert_to_counter64(value) self.set(oid, value, idx) def set_gauge32(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to a Gauge32 SNMP Object. See also `Set Integer`. """ value = self.convert_to_gauge32(value) self.set(oid, value, idx) def set_unsigned32(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to a Unsigned32 SNMP Object. See also `Set Integer`. """ value = self.convert_to_unsigned32(value) self.set(oid, value, idx) def set_timeticks(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to a TimeTicks SNMP Object. See also `Set Integer`. """ value = self.convert_to_timeticks(value) self.set(oid, value, idx) def set_ip_address(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to an IpAddress SNMP Object. The `value` can either be a string (dotted IPv4 address) or an iterable. See also `Set Integer`. Examples: | Set IP Address | ${oid} | 172.16.0.1 | | | | | ${myIp}= | Create List | ${172} | ${16} | ${0} | ${1} | | Set IP Address | ${oid} | ${myIp} | | | | """ value = self.convert_to_ip_address(value) self.set(oid, value, idx) def _warn(self, msg): self._log(msg, 'WARN') def _info(self, msg): self._log(msg, 'INFO') def _debug(self, msg): self._log(msg, 'DEBUG') def _log(self, msg, level=None): self._is_valid_log_level(level, raise_if_invalid=True) msg = msg.strip() if level is None: level = self._default_log_level if msg != '': print '*%s* %s' % (level.upper(), msg) def _is_valid_log_level(self, level, raise_if_invalid=False): if level is None: return True if isinstance(level, basestring) and \ level.upper() in ['TRACE', 'DEBUG', 'INFO', 'WARN', 'HTML']: return True if not raise_if_invalid: return False raise RuntimeError("Invalid log level '%s'" % level)
class SnmpLibrary(_Traps): AGENT_NAME = 'robotframework agent' ROBOT_LIBRARY_VERSION = __version__ ROBOT_LIBRARY_SCOPE = 'TEST SUITE' DTM_MIB_LIB_NAME = 'DTM-TD-LTE-ENODEB-ENBMIB' def __init__(self): _Traps.__init__(self) self._active_connection = None self._cache = ConnectionCache() def open_snmp_v2c_connection(self, host, community_string=None, port=161, timeout=2.0, retries=5, alias=None): """Opens a new SNMP v2c connection to the given host. Set `community_string` that is used for this connection. If no `port` is given, the default port 161 is used. The connection `timeout` and `retries` can be configured. The optional `alias` is a name for the connection and it can be used for switching between connections, similarly as the index. See `Switch Connection` for more details about that. """ host = str(host) port = int(port) timeout = float(timeout) retries = int(retries) if alias: alias = str(alias) authentication_data = cmdgen.CommunityData(self.AGENT_NAME, community_string) transport_target = cmdgen.UdpTransportTarget((host, port), timeout, retries) connection = _SnmpConnection(authentication_data, transport_target) self._active_connection = connection return self._cache.register(self._active_connection, alias) # backwards compatibility, will be removed soon open_snmp_connection = open_snmp_v2c_connection def open_snmp_v3_connection(self, host, user, password='', encryption_password=None, authentication_protocol=None, encryption_protocol=None, port=161, timeout=1.0, retries=5, alias=None): """Opens a new SNMP v3 Connection to the given host. If no `port` is given, the default port 161 is used. Valid values for `authentication_protocol` are `MD5`, `SHA`, and None. Valid values for `encryption_protocol` are `DES`,`3DES`, `AES128`, `AES192`, `AES256` and None. The optional `alias` is a name for the connection and it can be used for switching between connections, similarly as the index. See `Switch Connection` for more details about that. """ host = str(host) port = int(port) user = str(user) timeout = float(timeout) retries = int(retries) if password is not None: password = str(password) if encryption_password is not None: encryption_password = str(encryption_password) if alias: alias = str(alias) if authentication_protocol is not None: authentication_protocol = authentication_protocol.upper() try: authentication_protocol = { None: cmdgen.usmNoAuthProtocol, 'MD5': cmdgen.usmHMACMD5AuthProtocol, 'SHA': cmdgen.usmHMACSHAAuthProtocol }[authentication_protocol] except KeyError: raise RuntimeError('Invalid authentication protocol %s' % authentication_protocol) if encryption_protocol is not None: encryption_protocol = encryption_protocol.upper() try: encryption_protocol = { None: cmdgen.usmNoPrivProtocol, 'DES': cmdgen.usmDESPrivProtocol, '3DES': cmdgen.usm3DESEDEPrivProtocol, 'AES128': cmdgen.usmAesCfb128Protocol, 'AES192': cmdgen.usmAesCfb192Protocol, 'AES256': cmdgen.usmAesCfb256Protocol, }[encryption_protocol] except KeyError: raise RuntimeError('Invalid encryption protocol %s' % encryption_protocol) authentication_data = cmdgen.UsmUserData(user, password, encryption_password, authentication_protocol, encryption_protocol) transport_target = cmdgen.UdpTransportTarget((host, port), timeout, retries) conn = _SnmpConnection(authentication_data, transport_target) self._active_connection = conn return self._cache.register(self._active_connection, alias) def close_snmp_connection(self): """Closes the current connection. """ self._active_connection.close() self._active_connection = None def close_all_snmp_connections(self): """Closes all open connections and empties the connection cache. After this keyword, new indexes got from the `Open Connection` keyword are reset to 1. This keyword should be used in a test or suite teardown to make sure all connections are closed. """ self._active_connection = self._cache.close_all() self._active_connection = None def switch_snmp_connection(self, index_or_alias): """Switches between active connections using an index or alias. The index is got from `Open Connection` keyword, and an alias can be given to it. Returns the index of previously active connection. """ old_index = self._cache.current_index self._active_connection = self._cache.switch(index_or_alias) return old_index def add_mib_search_path(self, path): """Adds a path to the MIB search path. Example: | Add MIB Search Path | /usr/share/mibs/ | """ self._info('Adding MIB path %s' % (path, )) if not os.path.exists(path): raise RuntimeError('Path "%s" does not exist' % path) paths = self._active_connection.builder.getMibPath() paths += (path, ) self._debug('New paths: %s' % ' '.join(paths)) self._active_connection.builder.setMibPath(*paths) def preload_mibs(self, *names): """Preloads MIBs. Preloading MIBs can be useful in cases where the `Get`- or `Set`-keyword should be executed as fast as possible. `names` can either be a list of MIB names or can be empty in which case all available MIBs are preloaded. This keyword should be used within the test setup. Note: Preloading all MIBs take a long time. """ if len(names): self._info('Preloading MIBs %s' % ' '.join(list(names))) else: self._info('Preloading all available MIBs') self._active_connection.builder.loadModules(*names) def load_mib(self, *names): """loads MIBs. Preloading MIBs can be useful in cases where the `Get`- or `Set`-keyword should be executed as fast as possible. `names` can either be a list of MIB names or can be empty in which case all available MIBs are preloaded. This keyword should be used within the test setup. Note: Preloading all MIBs take a long time. """ if len(names): self._info('loading MIB %s' % ' '.join(list(names))) self._active_connection.builder.loadModule(*names) else: self._info('Preloading no MIBs') def get_oid_by_name(self, *names): """get oid from custom names. """ if len(names): self._info('get_oid_by_name %s' % ' '.join(list(names))) mibViewCtl = view.MibViewController( self._active_connection.builder) objIdentity = ObjectIdentity(*names) objIdentity.resolveWithMib(mibViewCtl) return '.' + str(objIdentity.getOid()) def get_value_by_name(self, *names): """get value from custom names direcly. """ if len(names): self._info('get_oid_by_name %s' % ' '.join(list(names))) mibViewCtl = view.MibViewController( self._active_connection.builder) objIdentity = ObjectIdentity(*names) objIdentity.resolveWithMib(mibViewCtl) oid = '.' + str(objIdentity.getOid()) return self._get(oid) def get_value_by_name2(self, idx, *names): """get value from custom names direcly. """ if len(names): self._info('get_oid_by_name %s' % ' '.join(list(names))) mibViewCtl = view.MibViewController( self._active_connection.builder) objIdentity = ObjectIdentity(*names) objIdentity.resolveWithMib(mibViewCtl) oid = '.' + str(idx) + '.' + str(objIdentity.getOid()) return self._get(oid) def _get(self, oid, idx=(0, ), expect_display_string=False): if self._active_connection is None: raise RuntimeError('No transport host set') idx = utils.parse_idx(idx) oid = utils.parse_oid(oid) + idx error_indication, error, _, var = \ self._active_connection.cmd_gen.getCmd( self._active_connection.authentication_data, self._active_connection.transport_target, oid ) if error_indication is not None: raise RuntimeError('SNMP GET failed: %s' % error_indication) if error != 0: raise RuntimeError('SNMP GET failed: %s' % error.prettyPrint()) oid, obj = var[0] if isinstance(obj, rfc1905.NoSuchInstance): raise RuntimeError('Object with OID %s not found' % utils.format_oid(oid)) if expect_display_string: if not univ.OctetString().isSuperTypeOf(obj): raise RuntimeError('Returned value is not an octetstring') value = obj.prettyOut(obj) elif univ.OctetString().isSuperTypeOf(obj): value = obj.asNumbers() else: value = obj.prettyOut(obj) self._info('OID %s has value %s' % (utils.format_oid(oid), value)) return value #majingwei 2018/10/12 # def getNext(self, oid, idx=(0,), expect_display_string=False): # if self._active_connection is None: # raise RuntimeError('No transport host set') # idx = utils.parse_idx(idx) # oid = utils.parse_oid(oid) + idx # error_indication, error, _, var = \ # self._active_connection.cmd_gen.nextCmd( # self._active_connection.authentication_data, # self._active_connection.transport_target, # oid # ) # self._info(str(type(var))) # for oid, obj in var: # self._info(str(oid)) # self._info(str(obj)) # if error_indication is not None: # raise RuntimeError('SNMP GET failed: %s' % error_indication) # if error == 342: # return 342 # if error != 0: # raise RuntimeError('SNMP GET failed: %s' % error.prettyPrint()) # value_list = list() # #oid, obj = var[0] # for oid, obj in var: # if isinstance(obj, rfc1905.NoSuchInstance): # raise RuntimeError('Object with OID %s not found' % # utils.format_oid(oid)) # if expect_display_string: # if not univ.OctetString().isSuperTypeOf(obj): # raise RuntimeError('Returned value is not an octetstring') # value = obj.prettyOut(obj) # elif univ.OctetString().isSuperTypeOf(obj): # value = obj.asNumbers() # else: # value = obj.prettyOut(obj) # self._info('OID %s has value %s' % (utils.format_oid(oid), value)) # value_list.append(value) # return value_list #majingwei 2018/10/12 def _get_with_error_status(self, oid, idx=(0, ), expect_display_string=False): if self._active_connection is None: raise RuntimeError('No transport host set') idx = utils.parse_idx(idx) oid = utils.parse_oid(oid) + idx error_indication, error, _, var = \ self._active_connection.cmd_gen.getCmd( self._active_connection.authentication_data, self._active_connection.transport_target, oid ) if error_indication is not None: raise RuntimeError('SNMP GET failed: %s' % error_indication) # if error != 0: # raise RuntimeError('SNMP GET failed: %s' % error.prettyPrint()) oid, obj = var[0] if isinstance(obj, rfc1905.NoSuchInstance): raise RuntimeError('Object with OID %s not found' % utils.format_oid(oid)) if expect_display_string: if not univ.OctetString().isSuperTypeOf(obj): raise RuntimeError('Returned value is not an octetstring') value = obj.prettyOut(obj) elif univ.OctetString().isSuperTypeOf(obj): value = obj.asNumbers() else: value = obj.prettyOut(obj) self._info('OID %s has value %s' % (utils.format_oid(oid), value)) return value, error def get_with_error_by_name(self, obj_name, idx=(0, )): """Does a SNMP GET request for the specified 'oid'. The optional `idx` can be specified as tuple or as a string and defaults to `.0`. Examples: | ${value}= | Get | SNMPv2-MIB::sysDescr | | | ${value}= | Get | .1.3.6.1.2.1.1.1 | | | ${value}= | Get | .iso.org.6.internet.2.1.1.1 | | | ${value}= | Get | sysDescr | | | ${value}= | Get | ifDescr | 2 | """ mibViewCtl = view.MibViewController(self._active_connection.builder) objIdentity = ObjectIdentity(self.DTM_MIB_LIB_NAME, obj_name) objIdentity.resolveWithMib(mibViewCtl) oid = '.' + str(objIdentity.getOid()) return self._get_with_error_status(oid, idx) def get(self, oid, idx=(0, )): """Does a SNMP GET request for the specified 'oid'. The optional `idx` can be specified as tuple or as a string and defaults to `.0`. Examples: | ${value}= | Get | SNMPv2-MIB::sysDescr | | | ${value}= | Get | .1.3.6.1.2.1.1.1 | | | ${value}= | Get | .iso.org.6.internet.2.1.1.1 | | | ${value}= | Get | sysDescr | | | ${value}= | Get | ifDescr | 2 | """ return self._get(oid, idx) def get_by_name(self, obj_name, idx=(0, )): """Does a SNMP GET request for the specified 'obj_name'. The optional `idx` can be specified as tuple or as a string and defaults to `.0`. Examples: | ${value}= | Get | SNMPv2-MIB::sysDescr | | | ${value}= | Get | .1.3.6.1.2.1.1.1 | | | ${value}= | Get | .iso.org.6.internet.2.1.1.1 | | | ${value}= | Get | sysDescr | | | ${value}= | Get | ifDescr | 2 | """ mibViewCtl = view.MibViewController(self._active_connection.builder) objIdentity = ObjectIdentity(self.DTM_MIB_LIB_NAME, obj_name) objIdentity.resolveWithMib(mibViewCtl) oid = '.' + str(objIdentity.getOid()) return self._get(oid, idx) def get_display_string(self, oid, idx=(0, )): """Does a SNMP GET request for the specified 'oid' and convert it to display string. For more information and an example see `Get`. """ return self._get(oid, idx, expect_display_string=True) def _set(self, *oid_values): for oid, value in oid_values: self._info('Setting OID %s to %s' % (utils.format_oid(oid), value)) error_indication, error, _, var = \ self._active_connection.cmd_gen.setCmd( self._active_connection.authentication_data, self._active_connection.transport_target, *oid_values ) if error_indication is not None: raise RuntimeError('SNMP SET failed: %s' % error_indication) if error != 0: raise RuntimeError('SNMP SET failed: %s' % error.prettyPrint()) def set(self, oid, value, idx=(0, )): """Does a SNMP SET request. See `Get` for more information on possible OID notations. Automatic converting to the SNMP type expected by the remote system is only supported for OIDs for which there is a MIB describing it. If you want to use an OID which is not described by a MIB, you'll have to use the `Set XXX`-keyword or `Convert To XXX`-keyword. The optional `idx` is appended to the OID (defaults to `.0`). Example: | Set | SNMPv2::sysDescr | New System Description | """ if self._active_connection is None: raise RuntimeError('No transport host set') idx = utils.parse_idx(idx) oid = utils.parse_oid(oid) + idx self._set((oid, value)) def set_by_name(self, obj_name, value, idx=(0, )): """Does a SNMP SET request. See `Get` for more information on possible OID notations. Automatic converting to the SNMP type expected by the remote system is only supported for OIDs for which there is a MIB describing it. If you want to use an OID which is not described by a MIB, you'll have to use the `Set XXX`-keyword or `Convert To XXX`-keyword. The optional `idx` is appended to the OID (defaults to `.0`). Example: | Set | SNMPv2::sysDescr | New System Description | """ if self._active_connection is None: raise RuntimeError('No transport host set') mibViewCtl = view.MibViewController(self._active_connection.builder) objIdentity = ObjectIdentity(self.DTM_MIB_LIB_NAME, obj_name) objIdentity.resolveWithMib(mibViewCtl) oid = '.' + str(objIdentity.getOid()) idx = utils.parse_idx(idx) oid = utils.parse_oid(oid) + idx self._set((oid, value)) def set_many(self, *oid_value_pairs): """ Does a SNMP SET request with multiple values. Set one or more values simultaneously. See `Set` for more information about the OID and possible value. After each value, you can give an index (`idx`, see example below) which is appended to the OIDs. By default the index is `.0`. Examples: | Set Many | SNMPv2::sysDescr | New System Description | | | ... | SNMPv2-MIB::sysName | New System Name | | | Set Many | IF-MIB::ifDescr | IF1 Description | idx=1 | | ... | IF-MIB::ifDescr | IF2 Description | idx=2 | """ if self._active_connection is None: raise RuntimeError('No transport host set') args = list(oid_value_pairs) oid_values = list() try: while len(args): oid = args.pop(0) value = args.pop(0) possible_idx = args[0] if len(args) > 0 else '' if possible_idx.startswith('idx='): idx = args.pop(0)[4:] else: idx = (0, ) idx = utils.parse_idx(idx) oid = utils.parse_oid(oid) + idx oid_values.append((oid, value)) except IndexError: raise RuntimeError('Invalid OID/value(/index) format') if len(oid_values) < 1: raise RuntimeError('You must specify at least one OID/value pair') self._set(*oid_values) def set_many_by_name(self, *name_value_pairs): """ Does a SNMP SET request with multiple values. Set one or more values simultaneously. See `Set` for more information about the OID and possible value. After each value, you can give an index (`idx`, see example below) which is appended to the OIDs. By default the index is `.0`. Examples: | Set Many | SNMPv2::sysDescr | New System Description | | | ... | SNMPv2-MIB::sysName | New System Name | | | Set Many | IF-MIB::ifDescr | IF1 Description | idx=1 | | ... | IF-MIB::ifDescr | IF2 Description | idx=2 | """ if self._active_connection is None: raise RuntimeError('No transport host set') args = list(name_value_pairs) oid_values = list() try: while len(args): obj_name = args.pop(0) mibViewCtl = view.MibViewController( self._active_connection.builder) objIdentity = ObjectIdentity(self.DTM_MIB_LIB_NAME, obj_name) objIdentity.resolveWithMib(mibViewCtl) oid = '.' + str(objIdentity.getOid()) value = args.pop(0) possible_idx = args[0] if len(args) > 0 else '' if possible_idx.startswith('idx='): idx = args.pop(0)[4:] else: idx = (0, ) idx = utils.parse_idx(idx) oid = utils.parse_oid(oid) + idx oid_values.append((oid, value)) except IndexError: raise RuntimeError('Invalid Object Name/value(/index) format') if len(oid_values) < 1: raise RuntimeError( 'You must specify at least one Object Name/value pair') self._set(*oid_values) def walk(self, oid): """Does a SNMP WALK request and returns the result as OID list.""" if self._active_connection is None: raise RuntimeError('No transport host set') self._info('Walk starts at OID %s' % (oid, )) oid = utils.parse_oid(oid) error_indication, error, _, var_bind_table = \ self._active_connection.cmd_gen.nextCmd( self._active_connection.authentication_data, self._active_connection.transport_target, oid ) if error_indication: raise RuntimeError('SNMP WALK failed: %s' % error_indication) if error != 0: raise RuntimeError('SNMP WALK failed: %s' % error.prettyPrint()) oids = list() for var_bind_table_row in var_bind_table: oid, obj = var_bind_table_row[0] oid = ''.join(('.', str(oid))) if obj.isSuperTypeOf(rfc1902.ObjectIdentifier()): obj = ''.join(('.', str(obj))) else: obj = obj.prettyOut(obj) self._info('%s: %s' % (oid, obj)) oids.append((oid, obj)) self._info(oids) return oids def prefetch_oid_table(self, oid): """Prefetch the walk result of the given oid. Subsequent calls to the `Find OID By Value` keyword will use the prefetched result. """ oids = self.walk(oid) self._active_connection.prefetched_table[oid] = oids def find_oid_by_value(self, oid, value, strip=False): """Return the first OID that matches a value in a list.""" if self._active_connection.prefetched_table.has_key(oid): oids = self._active_connection.prefetched_table[oid] else: oids = self.walk(oid) for oid in oids: s = str(oid[1]) if strip is True: s = s.strip() if s == str(value): return oid[0] raise RuntimeError('Value "%s" not found.' % value) def find_index(self, index_length, *args): """Searches an index in given datasets. There are some SNMP tables where the index is an arbitrary one. In this case you have to walk through the table and find a row where some columns match your values. For example consider the following table: | =a= | =b= | =c= | =d= | =e= | | 2 | 2 | 2 | 3 | 3 | | 2 | 3 | 2 | 5 | 6 | You want to know the value of d where a is 2 and b is 3. | ${a}= | Walk ${oidOfA} | | | | | | | ${b}= | Walk ${oidOfB} | | | | | | | ${idx}= | Find Index | 1 | ${a} | 2 | ${b} | 3 | | ${valueOfD}= | Get | ${oidOfD} | index=${idx} | | | | The `index_length` parameter is the length of the part of the OID which denotes the index. Eg. if you have an OID .1.3.6.1.4.1234.1.2.3 and `index_length` is 2, the index would be (2,3). """ if len(args) % 2 != 0: raise RuntimeError('Called with an invalid amount of arguments') data = islice(args, 0, None, 2) match = islice(args, 1, None, 2) l = list() for e in izip(data, match): # match our desired value d = filter(lambda x: x[1] == e[1], e[0]) # we only need the index part of the oid d = map(lambda x: (utils.parse_oid(x[0])[-int(index_length):]), d) # now convert the list of indices to a set d = set(d) l.append(d) # intersect all sets s = set(l[0]).intersection(*l[1:]) if len(s) == 0: raise RuntimeError('No index found for the given matches') if len(s) > 1: raise RuntimeError('Ambiguous match. Found %d matching indices' % len(s)) return s.pop() def get_index_from_oid(self, oid, length=1): """Return last part of oid. If length is 1 an integer is returned. Otherwise a tuple of integers is returened. Example: | ${val}= | Get Index From OID | 1.3.6.1.2.1.2.2.1.2.10102 | 1 | """ length = int(length) oid = utils.parse_oid(oid) if length == 1: return oid[-1] else: return oid[-length:] def convert_to_octetstring(self, value): """Converts a value to a SNMP OctetString object.""" return rfc1902.OctetString(value) def convert_to_integer(self, value): """Converts a value to a SNMP Integer object.""" return rfc1902.Integer(value) def convert_to_integer32(self, value): """Converts a value to a SNMP Integer32 object.""" return rfc1902.Integer32(value) def convert_to_counter32(self, value): """Converts a value to a SNMP Counter32 object.""" return rfc1902.Counter32(value) def convert_to_counter64(self, value): """Converts a value to a SNMP Counter64 object.""" return rfc1902.Counter64(value) def convert_to_gauge32(self, value): """Converts a value to a SNMP Gauge32 object.""" return rfc1902.Gauge32(value) def convert_to_unsigned32(self, value): """Converts a value to a SNMP Unsigned32 object.""" return rfc1902.Unsigned32(value) def convert_to_timeticks(self, value): """Converts a value to a SNMP TimeTicks object.""" return rfc1902.TimeTicks(value) # def convert_to_ip_address(self, value): # """Converts a value to a SNMP IpAddress object. # See `Set IP Address` for formats which are accepted for value. # """ # # Unfortunately, pysnmp does not support unicode strings # if isinstance(value, str): # value = str(value) # return rfc1902.IpAddress(value) def set_octetstring(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to an OctetString SNMP Object. This is a convenient keyword, it does the same as a `Convert To OctetString` followed by a `Set`. """ value = self.convert_to_octetstring(value) self.set(oid, value, idx) def set_integer(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to an Integer SNMP Object. This is a convenient keyword, it does the same as a `Convert To Integer` followed by a `Set`. """ value = self.convert_to_integer(value) self.set(oid, value, idx) def set_integer32(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to an Integer32 SNMP Object. See also `Set Integer`. """ value = self.convert_to_integer32(value) self.set(oid, value, idx) def set_counter32(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to a Counter32 SNMP Object. See also `Set Integer`. """ value = self.convert_to_counter32(value) self.set(oid, value, idx) def set_counter64(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to a Counter64 SNMP Object. See also `Set Integer`. """ value = self.convert_to_counter64(value) self.set(oid, value, idx) def set_gauge32(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to a Gauge32 SNMP Object. See also `Set Integer`. """ value = self.convert_to_gauge32(value) self.set(oid, value, idx) def set_unsigned32(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to a Unsigned32 SNMP Object. See also `Set Integer`. """ value = self.convert_to_unsigned32(value) self.set(oid, value, idx) def set_timeticks(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to a TimeTicks SNMP Object. See also `Set Integer`. """ value = self.convert_to_timeticks(value) self.set(oid, value, idx) def set_ip_address(self, oid, value, idx=(0, )): """Does a SNMP SET request after converting the value to an IpAddress SNMP Object. The `value` can either be a string (dotted IPv4 address) or an iterable. See also `Set Integer`. Examples: | Set IP Address | ${oid} | 172.16.0.1 | | | | | ${myIp}= | Create List | ${172} | ${16} | ${0} | ${1} | | Set IP Address | ${oid} | ${myIp} | | | | """ value = self.convert_to_ip_address(value) self.set(oid, value, idx) def _warn(self, msg): self._log(msg, 'WARN') def _info(self, msg): self._log(msg, 'INFO') def _debug(self, msg): self._log(msg, 'DEBUG') def _log(self, msg, level=None): self._is_valid_log_level(level, raise_if_invalid=True) msg = msg.strip() if level is None: level = self._default_log_level if msg != '': print('*%s* %s' % (level.upper(), msg)) def _is_valid_log_level(self, level, raise_if_invalid=False): if level is None: return True if isinstance(level, str) and \ level.upper() in ['TRACE', 'DEBUG', 'INFO', 'WARN', 'HTML']: return True if not raise_if_invalid: return False raise RuntimeError("Invalid log level '%s'" % level) def get_board_slotNo(self, rowstatus_oid, maxSlotNo): row_oid_list = list() slotNo_list = list() for loop in range(0, int(maxSlotNo) + 1): error = self.get_with_errcode(str(rowstatus_oid), idx=(0, 0, int(loop))) if 342 != error: slotNo_list.append(str(loop)) return slotNo_list def get_board_of_aom_or_som(self, aom_or_som, rowstatus_oid, maxSlotNo): slotNo_list = self.get_board_slotNo(rowstatus_oid, maxSlotNo) aom_slotNo_list = list() som_slotNo_list = list() self._info('debug..........') for slotNo in slotNo_list: if int(slotNo) in (0, 1): aom_slotNo_list.append(str(slotNo)) if int(slotNo) in (6, 7, 8, 9, 10, 11): som_slotNo_list.append(str(slotNo)) if aom_or_som == 'aom': return aom_slotNo_list elif aom_or_som == 'som': return som_slotNo_list return 'error' def get_Rru_No(self, rowstatus_oid, maxRruNo): row_oid_list = list() rruNo_list = list() for loop in range(0, int(maxRruNo) + 1): error = self.get_with_errcode(str(rowstatus_oid), idx=(loop)) if 342 != error: rruNo_list.append(str(loop)) return rruNo_list def get_topoRRUNo_Instance(self, rowstatus_oid, maxRruNo): rruNo_list = self.get_Rru_No(rowstatus_oid, maxRruNo) rru_slotNo_list = list() self._info('debug..........') for rruNo in rruNo_list: if int(rruNo) in (range(maxRruNo)): rru_slotNo_list.append(str(rruNo)) return rru_slotNo_list def get_Path_No(self, rowstatus_oid, RruNo, maxPathNo): rruNo_list = list() pathNo_list = list() for loop_pathNo in range(1, int(maxPathNo) + 1): error = self.get_with_errcode(str(rowstatus_oid), idx=(int(RruNo), int(loop_pathNo))) if 342 != error: pathNo_list.append(str(loop_pathNo)) return pathNo_list def get_rruPathInfo(self, oid_name, rowstatus_oid, RruNo, maxPathNo): ret_rruPathInfo_list = list() get_PahtNo_list = list() #获取的所有RRU No的list集合 get_pathNo_No_list = list() get_PahtNo_list = self.get_Path_No(rowstatus_oid, RruNo, maxPathNo) for pathNo in get_PahtNo_list: if int(pathNo) in (range(maxPathNo + 1)): get_pathNo_No_list.append(str(pathNo)) for pathNo in get_pathNo_No_list: ret_rruPathTxInfo = self.get_by_name(oid_name, idx=((int)(RruNo), (int)(pathNo))) ret_rruPathInfo_list.append(ret_rruPathTxInfo) return ret_rruPathInfo_list def get_rruPathStatusByThreshold(self, oid_name, rowstatus_oid, RruNo, maxPathNo, min_value, max_value): rru_PathInfo_List = list() ret_Info_list = list() is_wrong_flag = 0 tmp_RruNo = RruNo #获取‘射频单元编号’RruNo节点下的所有有效‘射频单元天线通道编号’ ret_Info_list = ">>>> The rruPathRRUNo[%s], " % (int)(RruNo) rru_PathInfo_List = self.get_rruPathInfo(oid_name, rowstatus_oid, RruNo, maxPathNo) #天线通道收发方向信息 if (oid_name == 'rruPathTxStatus' or oid_name == 'rruPathRxStatus'): ret_Info_list += "Following 'rruPathNo' status are FAILURE: \n" for index in range(1, maxPathNo): temp_Value = (str)(rru_PathInfo_List[index]) if temp_Value == 'failure': is_wrong_flag = 1 ret_Info_list += ('[' + (str)(index) + '] ') if (0 == index % 10 and index != 0): ret_Info_list += '\n' #需要根据门限值判断:天线驻波比,发送方向输出功率, 单通道上的温度 elif (oid_name == 'rruPathTemperature' or oid_name == 'rruPathTxPower' or oid_name == 'rruPathVSWR'): ret_Info_list += "Following 'rruPathNo' status are OUT OF RANGE: \n" for index in range(1, maxPathNo): temp_Value = (int)(rru_PathInfo_List[index]) if (temp_Value > max_value) or (temp_Value < min_value): is_wrong_flag = 1 ret_Info_list += ' rruPathNo%d,Value is %d\n' % ( index, temp_Value) else: is_wrong_flag = 2 ret_Info_list += "Input OID Name Wrong...\n" if (0 == is_wrong_flag): ret_Info = ">>> ALL 'rruPathNo' Status are RIGHT <<<\n" ret_Info_list += ret_Info print(ret_Info) return ret_Info_list #----------------rruPathTable Start---------------- def get_rruPathRRUNo_Instence_En(self, rowstatus_oid, rruPathRRUNo_Max, rruPathNo_Max, switch): row_oid_list = list() rruPathRRUNo_list = list() rruPathNo_list = list() loop_times = 0 rruPathNoValue_list = list() ret_rruRRUNo = dict() for loop_PathRRUNo in range(0, int(rruPathRRUNo_Max) + 1): #rruPathNo,射频单元天线通道编号 limited 1..64 for loop_PathNo in range(1, int(rruPathNo_Max) + 1): error = self.get_with_errcode(str(rowstatus_oid), idx=(loop_PathRRUNo, loop_PathNo)) if 342 != error: loop_times += 1 rruPathNo_list.append(loop_PathNo) if loop_times == 1: rruPathRRUNo_list.append(loop_PathRRUNo) loop_times = 0 if "rruPathRRUNo" == switch: return rruPathRRUNo_list elif "rruPathNo" == switch: return rruPathNo_list else: return rruPathRRUNo_list, rruPathNo_list def Get_rruPathTable_Value_By_Name(self, oid, rruPathRRUNo_Max, rruPathNo_Max): rruPathRRUNo_En = list() rruPathNo_En = list() rruPathValue = list() rruPathRRUNo_En, rruPathNo_En = self.get_rruPathRRUNo_Instence_En( oid, rruPathRRUNo_Max, rruPathNo_Max, 0) for loop_PathRRUNo in rruPathRRUNo_En: for loop_rruPathNo in rruPathNo_En: value = self.get(str(oid), idx=(loop_PathRRUNo, loop_rruPathNo)) rruPathValue.append(value) return rruPathValue def Set_rruPathTable_Value_By_Name(self, oid, value_base, value_new, rruPathRRUNo=list(), rruPathNo=list()): for loop_PathRRUNo in rruPathRRUNo: for loop_PathNo in rruPathNo: ret = Get_rruPathTable_Value_Each(oid, loop_PathRRUNo, loop_PathNo) if ret == value_base: self.Set_rruPathTable_Value_Each(oid, value_new, loop_PathRRUNo, loop_PathNo) def Get_rruPathTable_Value_Each(self, oid, rruPathRRUNo, rruPathNo): value = self.get(str(oid), idx=(rruPathRRUNo, rruPathNo)) return value def Set_rruPathTable_Value_Each(self, oid, value, rruPathRRUNo, rruPathNo): self.set(str(oid), value, idx=(rruPathRRUNo, rruPathNo)) #----------------rruPathTable End---------------- def get_with_errcode(self, oid, idx=(0, ), expect_display_string=False): if self._active_connection is None: raise RuntimeError('No transport host set') idx = utils.parse_idx(idx) oid = utils.parse_oid(oid) + idx self._info(str(oid)) error_indication, error, _, var = \ self._active_connection.cmd_gen.getCmd( self._active_connection.authentication_data, self._active_connection.transport_target, oid ) self._info(str(oid)) self._info(str(var)) self._info(str(error)) if error_indication is not None: raise RuntimeError('SNMP GET failed: %s' % error_indication) if error == 342: return 342 if error != 0: raise RuntimeError('SNMP GET failed: %s' % error.prettyPrint()) oid, obj = var[0] if isinstance(obj, rfc1905.NoSuchInstance): raise RuntimeError('Object with OID %s not found' % utils.format_oid(oid)) if expect_display_string: if not univ.OctetString().isSuperTypeOf(obj): raise RuntimeError('Returned value is not an octetstring') value = obj.prettyOut(obj) elif univ.OctetString().isSuperTypeOf(obj): value = obj.asNumbers() else: value = obj.prettyOut(obj) self._info('OID %s has value %s' % (utils.format_oid(oid), value)) return value def get_singleIdx_in_gnb(self, entry_oid, maxSingleIdxNo): '''return single index list''' singleIdx_list = list() for idx_loop in range(0, int(maxSingleIdxNo)): #self._info(str(self.walk(str(entry_oid) + '.1'))) rowstatus_oid = str(entry_oid) + '.6' self._info(rowstatus_oid) error = self.get_with_errcode( str(rowstatus_oid)) #idx = (int(idx_loop), if 342 != error: singleIdx_list.append(str(idx_loop)) return singleIdx_list def get_until_createGo(self, oid, idx=(0, )): seconds_counter = 0 while True: try: if 342 == self.get_with_errcode(oid, idx, False): time.sleep(1) seconds_counter = seconds_counter + 1 else: self._info('createGo time:' + str(seconds_counter) + 'S') return self.get_with_errcode(oid, idx, False) except RuntimeError: time.sleep(1) seconds_counter = seconds_counter + 1 def get_until_enabled(self, oid, idx=(0, )): seconds_counter = 0 while True: try: if 342 == self.get_with_errcode(oid, idx, False): time.sleep(1) seconds_counter = seconds_counter + 1 elif 'enabled' == self.get_with_errcode(oid, idx, False): self._info('enabled time:' + str(seconds_counter) + 'S') return self.get_with_errcode(oid, idx, False) else: time.sleep(1) seconds_counter = seconds_counter + 1 except RuntimeError: time.sleep(1) seconds_counter = seconds_counter + 1 #add by zhaobaoxin@20181119 for RRU Access Phase def get_until_rru_available(self, oid, idx=(0, )): seconds_counter = 0 while True: try: if 342 == self.get_with_errcode(oid, idx, False): time.sleep(1) seconds_counter = seconds_counter + 1 elif 'available' == self.get_with_errcode(oid, idx, False): self._info('enabled time:' + str(seconds_counter) + 'S') return self.get_with_errcode(oid, idx, False) else: time.sleep(1) seconds_counter = seconds_counter + 1 except RuntimeError: time.sleep(1) seconds_counter = seconds_counter + 1 def get_CellIDList(self, rowstatus_oid, maxCellID): cellId_list = list() for loop in range(0, int(maxCellID) + 1): error = self.get_with_errcode(str(rowstatus_oid), idx=int(loop)) if 342 != error: cellId_list.append(str(loop)) return cellId_list #plp0 is disabled when no clk mode def is_fpga_plp0(idx): sub_idx = idx.split('.') if 4 != len(sub_idx): self._info(idx) raise RuntimeError('SNMP GET failed: %s' % error.prettyPrint()) else: if '1' == sub_idx[-1] and 5 < int(sub_idx[-2]): return True return False def get_until_enabled_for_processors(self, oid, idx=(0, )): seconds_counter = 0 while True: try: if 342 == self.get_with_errcode(oid, idx, False): time.sleep(1) seconds_counter = seconds_counter + 1 elif 'disabled' == self.get_with_errcode(oid, idx, False) and \ True == self.is_fpga_plp0(str(idx)): self._info('disabled time:' + str(seconds_counter) + 'S') return self.get_with_errcode(oid, idx, False) elif 'enabled' == self.get_with_errcode(oid, idx, False): self._info('enabled time:' + str(seconds_counter) + 'S') return self.get_with_errcode(oid, idx, False) else: time.sleep(1) seconds_counter = seconds_counter + 1 except RuntimeError: time.sleep(1) seconds_counter = seconds_counter + 1 def create_folder_by_current_time(self, basePath, timeStr): timeStr = ''.join(timeStr.split('-')) timeStr = ''.join(timeStr.split()) timeStr = ''.join(timeStr.split(':')) timeStr = ''.join(timeStr.split('.')) folderpath = str(basePath) + '\\' + timeStr.strip(':') isExists = os.makedirs(folderpath) print(isExists) return folderpath #Merge by zhaobaoxin@20181127 from dev_python36 Created by FYF def get_next(self, oid): """Does a SNMP GET-NEXT request and returns the result as OID list.""" if self._active_connection is None: raise RuntimeError('No transport host set') self._info('GET-NEXT starts at OID %s' % (oid, )) oid = utils.parse_oid(oid) self._info("This is a test after oid = utils.parse_oid(oid)") error_indication, error, _, var_bind_table = \ self._active_connection.cmd_gen.nextOneCmd( self._active_connection.authentication_data, self._active_connection.transport_target, oid ) if error_indication: raise RuntimeError('SNMP GET-NEXT 1 failed: %s' % error_indication) if error != 0: if error == 13: return () else: raise RuntimeError('SNMP GET-NEXT 2 failed: %s' % error.prettyPrint()) vbTuple = () for var_bind_table_row in var_bind_table: oid, obj = var_bind_table_row[0] oid = ''.join(('.', str(oid))) if obj.isSuperTypeOf(rfc1902.ObjectIdentifier()): obj = ''.join(('.', str(obj))) else: obj = obj.prettyOut(obj) self._info('%s: %s' % (oid, obj)) vbTuple = (oid, obj) return vbTuple
def __init__(self, timeout=10.0, poll_interval=1.0): PerConnectionStorage.__init__(self, '_active_session') self._cache = ConnectionCache() self._active_session = None self._timeout = timeout self._poll_interval = poll_interval
class HpiLibrary(Logging, PerConnectionStorage): def __init__(self, timeout=10.0, poll_interval=1.0): PerConnectionStorage.__init__(self, '_active_session') self._cache = ConnectionCache() self._active_session = None self._timeout = timeout self._poll_interval = poll_interval def set_timeout(self, timeout): """Sets the timeout used in `Wait Until X` keywords to the given value. `timeout` is given in Robot Framework's time format (e.g. 1 minute 20 seconds) that is explained in the User Guide. The old timeout is returned and can be used to restore it later. Example. | ${tout}= | Set Timeout | 2 minute 30 seconds | | Do Something | | Set Timeout | ${tout} | """ old = getattr(self, '_timeout', 3.0) self._timeout = timestr_to_secs(timeout) return secs_to_timestr(old) @property def _s(self): return self._active_session def open_hpi_connection(self, host, port=4743, alias=None): """Opens an HPI session. `host` specifies the hostname or IP address to connect to. `port` is the port number the HPI daemon listens on. """ port = int(port) self._info('Opening connection to %s:%d' % (host, port)) os.environ["OPENHPI_DAEMON_HOST"] = str(host) os.environ["OPENHPI_DAEMON_PORT"] = str(port) session = Session() session.open() session.attach_event_listener() self._active_session = session return self._cache.register(session, alias) def switch_hpi_connection(self, index_or_alias): """Switches between opened HPI session usigg an index or alias. The index is got from `Open HPI Connection` keyword, and an alias can be given to it. Returns the index of previously active connection. """ old_index = self._cache.current_index self._active_device = self._cache.switch(index_or_alias) return old_index def close_hpi_connection(self, loglevel=None): """Closes the current HPI session. """ self._active_session.close() def close_all_hpi_connections(self): """Closes all open HPI sessions and empties the connection cache. After this keyword, new indexes got from the `Open HPI Connection` keyword are reset to 1. This keyword should be used in a test or suite teardown to make sure all connections to devices are closed. """ self._active_session = self._cache.close_all() def set_entity_path(self, ep): """Sets the entity path all further keywords operates on.""" try: ep = EntityPath().from_string(ep) except ValueError: raise RuntimeError('Invalid entity path "%s"' % ep) self._info('Setting entity path to %s' % (ep,)) self._cp['entity_path'] = ep def _selected_resource(self): path = self._cp['entity_path'] res = self._s.get_resources_by_entity_path(path) if len(res) != 1: raise RuntimeError('More than one resources were retrieved using ' 'the entity path (%s)' % (path,)) return res[0] def _find_rdr(self, rdr_type, id): res = self._selected_resource() for rdr in res.rdrs(): self._debug('Found RDR type "%d" id "%s"' % (rdr.rdr_type, rdr.id_string)) if isinstance(rdr, rdr_type) and rdr.id_string == id: self._debug('Found match') return rdr return None def _rdr_should_exist(self, rdr_type, id): rdr = self._find_rdr(rdr_type, id) if rdr is None: raise AssertionError('No FUMI RDR with id "%s" found.' % (id,)) self._cp['selected_rdr'] = rdr def _selected_rdr(self): return self._cp['selected_rdr'] ### # General ### def entity_path_should_exist(self, ep): ep = EntityPath().from_string(ep) for res in self._s.resources(): self._debug('%s' % res.rpt) if res.rpt.entity_path == ep: break else: raise AssertionError('An RPT with entity path %s does not exist' % (ep,)) def product_id_of_selected_resource_should_be(self, expected_pid, msg=None, values=True): expected_pid = int_any_base(expected_pid) info = self._selected_resource().rpt.resource_info actual_pid = info.product_id asserts.assert_equal(expected_pid, actual_pid, msg, values) def manufacturer_id_of_selected_resource_should_be(self, expected_mid, msg=None, values=True): expected_mid = int_any_base(expected_mid) info = self._selected_resource().rpt.resource_info actual_mid = info.manufacturer_id asserts.assert_equal(expected_mid, actual_mid, msg, values) ### # Events ### def clear_event_queue(self): listener = self._s.event_listener while True: event = listener.get(timeout=0) if event is None: return def wait_until_event_queue_contains_event_type(self, event_type, may_fail=False): listener = self._s.event_listener event_type = find_event_type(event_type) start_time = time.time() end_time = start_time + self._timeout timeout = end_time - time.time() while timeout > 0: try: event = listener.get(timeout) self._debug('Got event %s from queue' % event_type_str(event.event_type)) except SaHpiError: if may_fail: time.sleep(self._poll_interval) continue else: raise if event.event_type == event_type: self._cp['selected_event'] = event return raise AssertionError('No event with type %s in queue for %s' % (event_type_str(event_type), secs_to_timestr(self._timeout))) def _selected_event(self): return self._cp['selected_event'] def upgrade_state_of_fumi_event_should_be(self, expected_state, msg=None, values=True): if self._selected_event().event_type != SAHPI_ET_FUMI: raise RuntimeError('Event is not of type FUMI') expected_state = find_fumi_upgrade_state(expected_state) actual_state = self._selected_event().status asserts.assert_equal(expected_state, actual_state, msg, values) def test_status_of_dimi_event_should_be(self, expected_status, msg=None, values=True): if self._selected_event().event_type != SAHPI_ET_DIMI: raise RuntimeError('Event is not of type DIMI') expected_status = find_dimi_test_status(expected_status) actual_status = self._selected_event().run_status asserts.assert_equal(expected_status, actual_status, msg, values) ### # FUMI ### def set_fumi_number(self, number): """Sets the FUMI number for all further FUMI keywords.""" self._cp['fumi_number'] = number def select_logical_bank(self): res = self._selected_resource() rdr = self._selected_rdr() fumi = res.fumi_handler_by_rdr(rdr) bank = fumi.logical_bank() self._cp['selected_fumi_bank'] = bank def select_bank_number(self, number): number = int(number) res = self._selected_resource() rdr = self._selected_rdr() fumi = res.fumi_handler_by_rdr(rdr) bank = fumi.bank(number) self._cp['selected_fumi_bank'] = bank def _selected_fumi_bank(self): return self._cp['selected_fumi_bank'] def fumi_rdr_should_exist(self, id): """Fails unless the specified FUMI RDR exist. `id` is the ID string of the resource descriptor record. If the RDR is found, it will be automatically selected. """ self._rdr_should_exist(FumiRdr, id) def select_fumi_rdr(self, id): """This is just a convenient keyword. It does the same as the `FUMI RDR Should Exist` keyword. """ self.fumi_rdr_should_exist(id) def fumi_number_of_selected_rdr_should_be(self, expected_num, msg=None, values=True): res = self._selected_resource() rdr = self._selected_rdr() expected_num = int(expected_num) asserts.assert_equal(expected_num, rdr.fumi_num, msg, values) def access_protocol_of_selected_rdr_should_be(self, expected_protocol, msg=None, values=True): rdr = self._selected_rdr() expected_protocol = find_fumi_access_protocol(expected_protocol) asserts.assert_equal(expected_protocol, rdr.access_protocol, msg, values) def capabilities_of_selected_rdr_should_be(self, expected_capabilities, msg=None, values=True): rdr = self._selected_rdr() expected_capabilities = find_fumi_capabilities(expected_capabilities) asserts.assert_equal(expected_capabilities, rdr.capability, msg, values) def number_of_banks_of_selected_rdr_should_be(self, expected_number, msg=None, values=True): rdr = self._selected_rdr() expected_number = int(expected_number) asserts.assert_equal(expected_number, rdr.num_banks, msg, values) def size_of_selected_bank_should_be(self, expected_size, msg=None, values=True): info = self._selected_fumi_bank().bank_info() expected_size = int(expected_size) asserts.assert_equal(expected_size, info.size, msg, values) def identifier_of_selected_bank_should_be(self, expected_id, msg=None, values=True): info = self._selected_fumi_bank().bank_info() asserts.assert_equal(expected_id, str(info.identifier), msg, values) def description_of_selected_bank_should_be(self, expected_description, msg=None, values=True): info = self._selected_fumi_bank().bank_info() asserts.assert_equal(expected_description, str(info.description), msg, values) def datetime_of_selected_bank_should_be(self, expected_datetime, msg=None, values=True): info = self._selected_fumi_bank().bank_info() asserts.assert_equal(expected_datetime, str(info.date_time), msg, values) def version_of_selected_bank_should_be(self, expected_major, expected_minor, expected_aux, msg=None, values=True): info = self._selected_fumi_bank().bank_info() expected_major = int(expected_major) expected_minor = int(expected_minor) expected_aux = int(expected_aux) asserts.assert_equal( (expected_major, expected_minor, expected_aux), (info.major_version, info.minor_version, info.aux_version), msg, values) def set_source(self, uri): self._selected_fumi_bank().set_source(uri) def start_validation(self): self._selected_fumi_bank().start_validation() def start_installation(self): self._selected_fumi_bank().start_installation() def start_rollback(self): res = self._selected_resource() rdr = self._selected_rdr() fumi = res.fumi_handler_by_rdr(rdr) fumi.start_rollback() def start_activation(self): res = self._selected_resource() rdr = self._selected_rdr() fumi = res.fumi_handler_by_rdr(rdr) fumi.start_activation() def cancel_upgrade(self): self._selected_fumi_bank().cancel() def cleanup(self): self._selected_fumi_bank().cleanup() def upgrade_state_should_be(self, expected_state, msg=None, values=True): expected_state = find_fumi_upgrade_state(expected_state) state = self._selected_fumi_bank().status() asserts.assert_equal(expected_state, state, msg, values) def wait_until_upgrade_state_is(self, state, may_fail=False): state = find_fumi_upgrade_state(state) bank = self._selected_fumi_bank() start_time = time.time() while time.time() < start_time + self._timeout: try: _state = bank.status() except SaHpiError: if may_fail: time.sleep(self._poll_interval) continue else: raise self._debug('Current upgrade state is %s' % fumi_upgrade_status_str(_state)) if _state == state: return time.sleep(self._poll_interval) raise AssertionError('Upgrade state %s not reached %s.' % (fumi_upgrade_status_str(state), secs_to_timestr(self._timeout))) def source_status_should_be(self, expected_status, msg=None, values=True): expected_status = find_fumi_source_status(expected_status) info = self._selected_fumi_bank().source_info() asserts.assert_equal(expected_status, info.source_status.value, msg, values) ### # DIMI ### def set_dimi_number(self, number): """Sets the DIMI number for all further DIMI keywords.""" self._cp['dimi_number'] = number def dimi_rdr_should_exist(self, id): """Fails unless the specified DIMI RDR exist. A found RDR will be automatically selected. See also `FUMI RDR Should Exist` keyword. """ self._rdr_should_exist(DimiRdr, id) def select_dimi_rdr(self, id): """This is just a convenient keyword. It does the same as the `DIMI RDR Should Exist` keyword. """ self.dimi_rdr_should_exist(id) def select_test(self, number): number = int(number) res = self._selected_resource() rdr = self._cp['selected_rdr'] dimi = res.dimi_handler_by_rdr(rdr) test = dimi.get_test_by_num(number) self._cp['selected_dimi_test'] = test def dimi_number_of_selected_rdr_should_be(self, expected_num, msg=None, values=True): rdr = self._cp['selected_rdr'] expected_num = int(expected_num) asserts.assert_equal(expected_num, rdr.dimi_num, msg, values) def name_of_selected_test_should_be(self, expected_name, msg=None, values=True): test = self._cp['selected_dimi_test'] asserts.assert_equal(expected_name, test.name, msg, values) def service_impact_of_selected_test_should_be(self, expected_impact, msg=None, values=True): expected_impact = find_dimi_test_service_impact(expected_impact) test = self._cp['selected_dimi_test'] asserts.assert_equal(expected_impact, test.service_impact, msg, values) def capabilities_of_selected_test_should_be(self, expected_capabilities, msg=None, values=True): expected_capabilities = \ find_dimi_test_capabilities(expected_capabilities) test = self._cp['selected_dimi_test'] asserts.assert_equal(expected_capabilities, test.capabilities, msg, values) def selected_test_should_have_parameter(self, expected_parameter, msg=None, values=True): test = self._cp['selected_dimi_test'] parameters = [p.name for p in test.parameters] asserts.assert_true(expected_parameter in parameters, msg) def default_value_of_parameter_of_selected_test_should_be(self, parameter_name, expected_default_value, msg=None, values=True): test = self._cp['selected_dimi_test'] try: parameter = filter( lambda parameter: parameter.name == parameter_name, test.parameters)[0] except IndexError: raise RuntimeError('Parameter with name "%s" not found.', parameter_name) asserts.assert_equal(expected_default_value, parameter.default, msg, values) def start_test(self, *parameters): _parameters = [ p.split('=', 1) for p in parameters ] for p in _parameters: if len(p) != 2: raise RuntimeError('Parameters has to be in form of ' '"name=value"') test = self._cp['selected_dimi_test'] test.start(_parameters or None) def cancel_test(self): test = self._cp['selected_dimi_test'] test.cancel() def test_status_should_be(self, expected_status, msg=None, values=True): expected_status = find_dimi_test_status(expected_status) test = self._cp['selected_dimi_test'] status = test.status()[0] asserts.assert_equal(expected_status, status, msg, values) def wait_until_test_status_is(self, status): status = find_dimi_test_status(status) test = self._cp['selected_dimi_test'] start_time = time.time() while time.time() < start_time + self._timeout: _status = test.status()[0] self._debug('Current test status is %s' % dimi_test_status_str(_status)) if _status == status: return time.sleep(self._poll_interval) raise AssertionError('Test status %s not reached in %s.' % (dimi_test_status_str(status), secs_to_timestr(self._timeout))) def error_status_of_test_result_should_be(self, expected_status, msg=None, values=True): expected_status = find_dimi_test_status_error(expected_status) test = self._cp['selected_dimi_test'] result = test.results() asserts.assert_equal(expected_status, result.error_code, msg, values) def test_run_status_of_test_result_should_be(self, expected_status, msg=None, values=True): expected_status = find_dimi_test_status(expected_status) test = self._cp['selected_dimi_test'] result = test.results() asserts.assert_equal(expected_status, result.last_run_status, msg, values) def result_string_of_test_result_should_be(self, expected_string, msg=None, values=True): test = self._cp['selected_dimi_test'] result = test.results() asserts.assert_equal(expected_string, result.result, msg, values)