Schmidsfeld / TelegrafFritzBox

Telegraf collector help file for the FritzBox by AVM
MIT License
51 stars 12 forks source link

errorCode: 401 - invalid Action #2

Closed itobey closed 4 years ago

itobey commented 4 years ago

Hi,

I somehow cannot get it to work, as it's always giving me an unauthorized error. The user and password is correct. I've also tried to leverage fritzconnection directly which gives me a valid output: fritzconnection -i 192.168.0.1 -S WANPPPConnection1

But when running the script I'm getting this:

Could not query WANPPPConnection1 with action GetInfo
Traceback (most recent call last):
  File "TelegrafFritzBox/telegrafFritzBox.py", line 48, in <module>
    connectionInfo = readfritz('WANPPPConnection1', 'GetInfo')
  File "TelegrafFritzBox/telegrafFritzBox.py", line 19, in readfritz
    answer= fc.call_action(module, action)
  File "/usr/local/lib/python3.8/dist-packages/fritzconnection/core/fritzconnection.py", line 218, in call_action
    return self.soaper.execute(service, action_name, arguments)
  File "/usr/local/lib/python3.8/dist-packages/fritzconnection/core/soaper.py", line 233, in execute
    return handle_response(response)
  File "/usr/local/lib/python3.8/dist-packages/fritzconnection/core/soaper.py", line 218, in handle_response
    raise_fritzconnection_error(response)
  File "/usr/local/lib/python3.8/dist-packages/fritzconnection/core/soaper.py", line 147, in raise_fritzconnection_error
    raise exception(message)
fritzconnection.core.exceptions.FritzActionError: UPnPError:
errorCode: 401
errorDescription: Invalid Action

TR-64 is enabled and I triple checked user and password.

FRITZ!Box 6591 Cable at http://192.168.0.1 FRITZ!OS: 7.13

Do you have any idea what could be going wrong here?

chevdor commented 4 years ago

This comes from an issue in the script. It is assuming that all fritzboxes have those capabilities. I have a 6490 (cable) and ran into the same issue. I think @Schmidsfeld already ran into that seeing:

# connectionInfo = readfritz('WANIPConn1', 'GetStatusInfo') 
connectionInfo = readfritz('WANPPPConnection1', 'GetInfo') 

I personally had to toggle those lines.

The following calls also fail on my router:

# dslInfo = readfritz('WANDSLInterfaceConfig1', 'GetInfo')
# dslError = readfritz('WANDSLInterfaceConfig1', 'GetStatisticsTotal')
# dslInfo = readfritz('WANDSLInterfaceConfig1', 'GetInfo')

I am pretty sure many Fritzbox work the same way but not all. I think a good solution would be to provide the fritbox model as input and extract the data the properly for each box.

I will be pushing a new version in a branch called "6490" in a few minutes at https://github.com/chevdor/TelegrafFritzBox

If that does not work, I suggest you comment out the lines that are failing. Most should work. You will just miss some stats in the dashboard until the issue is fixed.

chevdor commented 4 years ago

@itobey the command should be fritzconnection -i 192.168.0.1 -A WANPPPConnection1 GetInfo but as you said, this works.

What is confusing is:

errorCode: 401
errorDescription: Invalid Action

401 means there is an auth issue. The error however suggests an invalid action whereas the action, as you pointed out is valid.

chevdor commented 4 years ago

See https://github.com/kbr/fritzconnection/issues/65

Schmidsfeld commented 4 years ago

Thank you for your extensive input. I sere the root of the problem: My code was written and tested on a DSL Line. The TR-064 files WANPPPConnection1and WANDSLInterfaceConfig1 are obviously not available for non DSL lines...

I can guess that there is other kinds of statistics depending on the uplink. I will research the appropiate statements and put them there in a statement... Since I don't have a Cable box available I will rely on you guys to test it ;)

chevdor commented 4 years ago

@Schmidsfeld more than happy to help and assist in testing. Please read my comment in the issue I linked above. I think all the github issues in this repo are actually around the same problem.

In short, I queried my box and WANPPPConnection1 should be available according to TR-064:

$ fritzconnection -i 192.168.0.1 -s | grep WAN
                    WANCommonIFC1
                    WANDSLLinkC1
                    WANIPConn1
                    WANIPv6Firewall1
                    WANCommonInterfaceConfig1
                    WANDSLInterfaceConfig1                        <==== HERE
                    WANDSLLinkConfig1
                    WANEthernetLinkConfig1
                    WANPPPConnection1                               <===== HERE
                    WANIPConnection1

Atm, I suspect the issue to NOT be in this repo but related to kbr/fritzconnection#65

Schmidsfeld commented 4 years ago

well It is just our task to find out what part fails. It can have to do something with this strange parsing chain from SOAP

I am confident that there is an easy solution for cable, too, For DSL it took some trial and error, thus it should be also possible for other connections.

Here a test script I posted in the other issue tread, The output would be helpfull to determine if it is an upstream error or just wrong parsing (what I suspect...) @chevdor : Can you please post the output of: I Think we will be able to get a good functionality going soon :D

´´´

!/opt/bin/python3

from fritzconnection import FritzConnection import sys import os

FRITZBOX_IP = os.environ.get('FRITZ_IP', '192.168.178.1') FRITZBOX_USER = os.environ.get('FRITZ_USER', 'telegraf') FRITZBOX_PASSWORD = os.environ.get('Fritz_PASSWD' "SuperStrongPassword") FRITZBOX_ID = os.environ.get('FRITZ_ID', 'FritzBox')

try: fc = FritzConnection(address=FRITZBOX_IP, user=FRITZBOX_USER, password=FRITZBOX_PASSWORD, timeout=2.0) except BaseException: print("Cannot connect to fritzbox.") sys.exit(1)

def readfritz(module, action): try: answer = fc.call_action(module, action) except: answer = dict() #return an emty dict in case of failure return answer

connectionInfo = readfritz('WANPPPConnection1', 'GetInfo')

connectionStatus = readfritz('WANPPPConnection1', 'GetStatusInfo')

connectionRates = readfritz('WANPPPConnection1', 'GetLinkLayerMaxBitRates')

print(connectionInfo) print(connectionStatus) print(connectionRates)

´´´´

chevdor commented 4 years ago

There were issues in the script and realized you changed the ENV names :)

I used:

#!/opt/bin/python3

from fritzconnection import FritzConnection
import sys
import os

FRITZBOX_IP = os.environ.get('FRITZ_IP', '192.168.178.1')
FRITZBOX_USER = os.environ.get('FRITZ_USER', 'telegraf')
FRITZBOX_PASSWORD = os.environ.get('FRITZ_PASSWORD' "SuperStrongPassword")
FRITZBOX_ID = os.environ.get('FRITZ_ID', 'FritzBox')

try:
    print(f"connecting to {FRITZBOX_IP} as {FRITZBOX_USER}")
    fc = FritzConnection(address=FRITZBOX_IP, user=FRITZBOX_USER, password=FRITZBOX_PASSWORD, timeout=2.0)
except BaseException:
    print("Cannot connect to fritzbox.")
    sys.exit(1)

def readfritz(module, action):
    try:
        answer = fc.call_action(module, action)
    except:
        answer = dict() #return an emty dict in case of failure
        return answer

connectionInfo = readfritz('WANPPPConnection1', 'GetInfo')

connectionStatus = readfritz('WANPPPConnection1', 'GetStatusInfo')

connectionRates = readfritz('WANPPPConnection1', 'GetLinkLayerMaxBitRates')

print(connectionInfo)
print(connectionStatus)
print(connectionRates)

and the output is:

$ python3 ./test.py 
connecting to 192.168.0.1 as telegraf
{}
{}
{}

which is strange because:

$ fritzconnection -i 192.168.0.1 -A WANPPPConnection1 GetInfo

fritzconnection v1.3.4
FRITZ!Box 6490 Cable (lgi) at http://192.168.0.1
FRITZ!OS: 7.12

Service:            WANPPPConnection1
Action:             GetInfo
Parameters:

    Name                                  direction     data type

    NewEnable                                out ->     boolean
    NewConnectionStatus                      out ->     string
    NewPossibleConnectionTypes               out ->     string
    NewConnectionType                        out ->     string
    NewName                                  out ->     string
    NewUptime                                out ->     ui4
    NewUpstreamMaxBitRate                    out ->     ui4
    NewDownstreamMaxBitRate                  out ->     ui4
    NewLastConnectionError                   out ->     string
    NewIdleDisconnectTime                    out ->     ui4
    NewRSIPAvailable                         out ->     boolean
    NewUserName                              out ->     string
    NewNATEnabled                            out ->     boolean
    NewExternalIPAddress                     out ->     string
    NewDNSServers                            out ->     string
    NewMACAddress                            out ->     string
    NewConnectionTrigger                     out ->     string
    NewLastAuthErrorInfo                     out ->     string
    NewMaxCharsUsername                      out ->     ui2
    NewMinCharsUsername                      out ->     ui2
    NewAllowedCharsUsername                  out ->     string
    NewMaxCharsPassword                      out ->     ui2
    NewMinCharsPassword                      out ->     ui2
    NewAllowedCharsPassword                  out ->     string
    NewTransportType                         out ->     string
    NewRouteProtocolRx                       out ->     string
    NewPPPoEServiceName                      out ->     string
    NewRemoteIPAddress                       out ->     string
    NewPPPoEACName                           out ->     string
    NewDNSEnabled                            out ->     boolean
    NewDNSOverrideAllowed                    out ->     boolean
Schmidsfeld commented 4 years ago

I agree this is strange and should produce at least a bit of output besides empty opbjects. This also explains why some interesting stats are missing.

The good news is, the revised code now fails gracefully and returns empt code resulting in missing stats, but not an error message. The same should be true for the mainline code.

Now I have to think why it fails, at least the GetInfo should work!

My best guess right now is wrong credentials. Error 401 (forbidden) points to it. Most of the data is available for Users without a login (aka wrong permissions or password) - WANPPPConnection1 requires a login to work! You should double check, that the user telegraf is on your FB and has the TR64 permissions set. It is also possible (but a bit more dangerous) to try the default username 'admin' and default password of the fritzbox as used by the fritzconnection script.

chevdor commented 4 years ago

I did consider the credentials but I triple checked...

See PR https://github.com/Schmidsfeld/TelegrafFritzBox/pull/5. If this PR still works for you then we may be moving forward.

What will be missing is to populate the equivalent of the 'DSL' metrics.

chevdor commented 4 years ago

It sounds like the right time to move all those 'def' in a lib and add some unit tests :) I have no idea what the connectInfo info object should look like and pinning that into a unit test would ensure it works for everyone.

Schmidsfeld commented 4 years ago

Sorry I had to close your Pull request - it broke the code for DSL Lines in a bad way so all the specialized stats were missing. I eaven had to do a roll back on the code.

I will implement it myself if you can give me the output of

connectionType = readfritz('Layer3Forwarding', 'GetDefaultConnectionService')
print(connectionType)

and

cableStats = = readfritz('WAN??????Connection1', 'GetInfo')
print(cableStats)

and lastely:

print(connectionType)

from any working version of the script