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 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)
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
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)
示例#6
0
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
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)