Initiate SNMP GET query over SNMPv1/v2c.

    Based on passed parameters, prepares SNMP GET packet
    (:RFC:`1905#section-4.2.1`) and schedules its transmission by
    I/O framework at a later point of time.

    Parameters
    ----------
    snmpDispatcher: :py:class:`~pysnmp.hlapi.v1arch.asyncore.SnmpDispatcher`
        Class instance representing asyncore-based asynchronous event loop and
        associated state information.

    authData: :py:class:`~pysnmp.hlapi.v1arch.CommunityData`
        Class instance representing SNMPv1/v2c credentials.

    transportTarget: :py:class:`~pysnmp.hlapi.v1arch.asyncore.UdpTransportTarget` or
        :py:class:`~pysnmp.hlapi.v1arch.asyncore.Udp6TransportTarget` Class instance representing
        transport type along with SNMP peer address.

    \*varBinds: :class:`tuple` of OID-value pairs or :py:class:`~pysnmp.smi.rfc1902.ObjectType`
        One or more class instances representing MIB variables to place
        into SNMP request.

    Other Parameters
    ----------------
    \*\*options:
        Request options:

        * `lookupMib` - load MIB and resolve response MIB variables at
          the cost of slightly reduced performance. Default is `False`.
        * `cbFun` (callable) - user-supplied callable that is invoked
          to pass SNMP response data or error to user at a later point
          of time. Default is `None`.
        * `cbCtx` (object) - user-supplied object passing additional
          parameters to/from `cbFun`. Default is `None`.

    Note
    ----
    The `SnmpDispatcher` object may be expensive to create, therefore it is
    advised to maintain it for the lifecycle of the application/thread for
    as long as possible.

    Note
    ----
    User-supplied `cbFun` callable must have the following call
    signature:

    * snmpDispatcher: :py:class:`~pysnmp.hlapi.v1arch.asyncore.SnmpDispatcher`
      Class instance representing asyncore-based asynchronous event loop and
      associated state information.
    * stateHandle (int): Unique request identifier. Can be used
      for matching multiple ongoing requests with received responses.
    * errorIndication (str): evaluates to `True` to indicate SNMP dispatcher
      error.
    * errorStatus (int): evaluates to `True` to indicate SNMP PDU error.
    * errorIndex (int): Non-zero value refers to `varBinds[errorIndex-1]`
    * varBinds (tuple): A sequence of
      :py:class:`~pysnmp.smi.rfc1902.ObjectType` class instances
      representing MIB variables returned in SNMP response in exactly
      the same order as `varBinds` in request.
    * `cbCtx` (object): Original user-supplied object.

    Returns
    -------
    stateHandle: int
        Unique request identifier. Can be used for matching received
        responses with ongoing requests.

    Raises
    ------
    PySnmpError
        Or its derivative indicating that an error occurred while
        performing SNMP operation.

    Examples
    --------
    >>> from pysnmp.hlapi.v1arch.asyncore import *
    >>>
    >>> def cbFun(snmpDispatcher, stateHandle, errorIndication,
    >>>           errorStatus, errorIndex, varBinds, cbCtx):
    >>>     print(errorIndication, errorStatus, errorIndex, varBinds)
    >>>
    >>> snmpDispatcher = SnmpDispatcher()
    >>>
    >>> stateHandle = getCmd(
    >>>     snmpDispatcher,
    >>>     CommunityData('public'),
    >>>     UdpTransportTarget(('demo.snmplabs.com', 161)),
    >>>     ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
    >>>     cbFun=cbFun
    >>> )
    >>>
    >>> snmpDispatcher.transportDispatcher.runDispatcher()

def getCmd(snmpDispatcher, authData, transportTarget, *varBinds, **options):
    """Initiate SNMP GET query over SNMPv1/v2c.

    Based on passed parameters, prepares SNMP GET packet
    (:RFC:`1905#section-4.2.1`) and schedules its transmission by
    I/O framework at a later point of time.

    Parameters
    ----------
    snmpDispatcher: :py:class:`~pysnmp.hlapi.v1arch.asyncore.SnmpDispatcher`
        Class instance representing asyncore-based asynchronous event loop and
        associated state information.

    authData: :py:class:`~pysnmp.hlapi.v1arch.CommunityData`
        Class instance representing SNMPv1/v2c credentials.

    transportTarget: :py:class:`~pysnmp.hlapi.v1arch.asyncore.UdpTransportTarget` or
        :py:class:`~pysnmp.hlapi.v1arch.asyncore.Udp6TransportTarget` Class instance representing
        transport type along with SNMP peer address.

    \*varBinds: :class:`tuple` of OID-value pairs or :py:class:`~pysnmp.smi.rfc1902.ObjectType`
        One or more class instances representing MIB variables to place
        into SNMP request.

    Other Parameters
    ----------------
    \*\*options:
        Request options:

        * `lookupMib` - load MIB and resolve response MIB variables at
          the cost of slightly reduced performance. Default is `False`.
        * `cbFun` (callable) - user-supplied callable that is invoked
          to pass SNMP response data or error to user at a later point
          of time. Default is `None`.
        * `cbCtx` (object) - user-supplied object passing additional
          parameters to/from `cbFun`. Default is `None`.

    Note
    ----
    The `SnmpDispatcher` object may be expensive to create, therefore it is
    advised to maintain it for the lifecycle of the application/thread for
    as long as possible.

    Note
    ----
    User-supplied `cbFun` callable must have the following call
    signature:

    * snmpDispatcher: :py:class:`~pysnmp.hlapi.v1arch.asyncore.SnmpDispatcher`
      Class instance representing asyncore-based asynchronous event loop and
      associated state information.
    * stateHandle (int): Unique request identifier. Can be used
      for matching multiple ongoing requests with received responses.
    * errorIndication (str): evaluates to `True` to indicate SNMP dispatcher
      error.
    * errorStatus (int): evaluates to `True` to indicate SNMP PDU error.
    * errorIndex (int): Non-zero value refers to `varBinds[errorIndex-1]`
    * varBinds (tuple): A sequence of
      :py:class:`~pysnmp.smi.rfc1902.ObjectType` class instances
      representing MIB variables returned in SNMP response in exactly
      the same order as `varBinds` in request.
    * `cbCtx` (object): Original user-supplied object.

    Returns
    -------
    stateHandle: int
        Unique request identifier. Can be used for matching received
        responses with ongoing requests.

    Raises
    ------
    PySnmpError
        Or its derivative indicating that an error occurred while
        performing SNMP operation.

    Examples
    --------
    >>> from pysnmp.hlapi.v1arch.asyncore import *
    >>>
    >>> def cbFun(snmpDispatcher, stateHandle, errorIndication,
    >>>           errorStatus, errorIndex, varBinds, cbCtx):
    >>>     print(errorIndication, errorStatus, errorIndex, varBinds)
    >>>
    >>> snmpDispatcher = SnmpDispatcher()
    >>>
    >>> stateHandle = getCmd(
    >>>     snmpDispatcher,
    >>>     CommunityData('public'),
    >>>     UdpTransportTarget(('demo.snmplabs.com', 161)),
    >>>     ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
    >>>     cbFun=cbFun
    >>> )
    >>>
    >>> snmpDispatcher.transportDispatcher.runDispatcher()
    """

    def _cbFun(snmpDispatcher, stateHandle, errorIndication, rspPdu, _cbCtx):
        if not cbFun:
            return

        if errorIndication:
            cbFun(errorIndication, pMod.Integer(0), pMod.Integer(0), None,
                  cbCtx=cbCtx, snmpDispatcher=snmpDispatcher, stateHandle=stateHandle)
            return

        errorStatus = pMod.apiPDU.getErrorStatus(rspPdu)
        errorIndex = pMod.apiPDU.getErrorIndex(rspPdu)

        varBinds = pMod.apiPDU.getVarBinds(rspPdu)

        if lookupMib:
            varBinds = VB_PROCESSOR.unmakeVarBinds(snmpDispatcher.cache, varBinds)

        nextStateHandle = pMod.getNextRequestID()

        nextVarBinds = cbFun(errorIndication, errorStatus, errorIndex, varBinds,
                             cbCtx=cbCtx,
                             snmpDispatcher=snmpDispatcher,
                             stateHandle=stateHandle,
                             nextStateHandle=nextStateHandle)

        if not nextVarBinds:
            return

        pMod.apiPDU.setRequestID(reqPdu, nextStateHandle)
        pMod.apiPDU.setVarBinds(reqPdu, nextVarBinds)

        return snmpDispatcher.sendPdu(authData, transportTarget, reqPdu, cbFun=_cbFun)

    lookupMib, cbFun, cbCtx = [options.get(x) for x in ('lookupMib', 'cbFun', 'cbCtx')]

    if lookupMib:
        varBinds = VB_PROCESSOR.makeVarBinds(snmpDispatcher.cache, varBinds)

    pMod = api.PROTOCOL_MODULES[authData.mpModel]

    reqPdu = pMod.GetRequestPDU()
    pMod.apiPDU.setDefaults(reqPdu)
    pMod.apiPDU.setVarBinds(reqPdu, varBinds)

    return snmpDispatcher.sendPdu(authData, transportTarget, reqPdu, cbFun=_cbFun)