0

I've a function for connecting to network devices(Netmiko). I'm catching AuthenticationException from netmiko, changing user creds and trying again.

Main function:

for round, device in zip(range(1, num_devices), devices):

    dev_class = d2c(device)
    print(f"Device {dev_class.Hostname}: {dev_class.MgmtIp}, {round} of {len(devices)}")
    devParsed = connectionMethods.Prep(dev_class, playVars)    
    services = connectionMethods.TestSSH(devParsed, user)

    print(services)


def TestSSH(device, user, notes_in = None, num_tries=0, timeout = 10):
    try:
        dict_out = {}
        notes = {'notes' : []}
        if notes_in:
            notes['notes'].append(notes_in)
            notes['notes'].append(f"Number of tries = {num_tries}")

        print(f"""************************************************\n"""
              f"""Connecting to device : {device.device.Hostname}\n"""
              f"""ip address : {device.device.MgmtIp}\n"""
              f"""Tried to connect before {num_tries} times.\n"""
              f"""Using {user.username} to connect\n"""
              f"""************************************************\n""")

        logging.basicConfig(filename='debug.log', level=logging.DEBUG)
        logger = logging.getLogger(f"netmiko")
        
        
        Conn = {'device_type':device.device.driver,
                 'ip': device.device.MgmtIp,
                 'username': user.username, 
                 'password': user.password,
                 'conn_timeout' : timeout,
        }

        pattern = re.compile(r'(server\s+)(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})')
        net_connect = ConnectHandler(**Conn)
        if not net_connect.check_enable_mode():
            net_connect.enable()
        
        for command in device.questions:
            output_list = []
            list_out = []
            for k, v in command.items():
                output = net_connect.send_command(v)
                print(f"""Output from device : 
                      {output}""")
                for line in output.splitlines():
                   result = pattern.search(line)
                   if result:
                      output_list.append(result.group(2))

                num_servers = len(output_list)
                print(f"Number of lines returned {num_servers}")
                if num_servers > 0:
                    for round, line in zip(range(1,num_servers + 1), output_list):
                        list_out.append(f"server{round} : {line}")
                    list_parsed = ','.join(list_out)
                    print(list_parsed)
                else: 
                    list_parsed = "No servers configured"
                
                dict_out.update({k : list_parsed})

    except NetmikoAuthenticationException as e:
        exception_type = type(e).__name__
        print(f"Error type = {exception_type}")
        if num_tries == 0:
            if input("Retry with a new token?:").lower() == "y": 
                retry_user = User()
                retry_user.GetToken()
                TestSSH(device, retry_user, notes_in= "admin login",num_tries=1, timeout=10)
            else:
                notes['notes'].append(f"{exception_type}, retries : {num_tries}")
                notes['notes'] = ','.join(notes.get('notes'))
                dict_out.update(notes)
                print(f""" Error from device {device.device.Hostname}:
                        {dict_out.get('notes')}""")
                
                return dict_out, 400
        else:
                notes['notes'].append(f"{exception_type}, retries : {num_tries}")
                notes['notes'] = ','.join(notes.get('notes'))
                dict_out.update(notes)
                print(dict_out)
                print(f""" Error from device {device.device.Hostname}:
                        {dict_out.get('notes')}""")
                print(type(dict_out))
                return dict_out, 401

What has me scratching my head is this: If num_tries == 0 and I choose not to try another account, return is as I would expect:

Retry with a new token?:n
 Error from device xxx.xxx.xxx:
                        NetmikoAuthenticationException, retries : 0
({'notes': 'NetmikoAuthenticationException, retries : 0'}, 400)

If num_tries is gt == 0: 

Error type = NetmikoAuthenticationException
{'notes': 'admin login,Number of tries = 1,NetmikoAuthenticationException, retries : 1'}
 Error from device xxx.xxx.xxx:
                        admin login,Number of tries = 1,NetmikoAuthenticationException, retries : 1
<class 'dict'>
None

I can't figure out the returned dictionary is None when it clearly is not when I print it before returning it.

Any suggestion how to troubleshoot the issue are welcome. Python version Python 3.9.6

Br, Heikki

I would hope to produce:

({'notes': 'NetmikoAuthenticationException, retries : 1'}, 401)
Hlavaste
  • 1
  • 1
  • 1
    Why does your output show `None`? There doesn't seem to be a `print()` which corresponds to that line of output? How do you call this function? – quamrana Dec 29 '22 at 13:07
  • 1
    Please update your question with this code from the main function. Also, why are you calling this function recursively? – quamrana Dec 29 '22 at 13:11
  • The answers to this [question](https://stackoverflow.com/questions/17778372/why-does-my-recursive-function-return-none) should help. – quamrana Dec 29 '22 at 13:13

1 Answers1

0

The reason you get None printed here: print(services) is because you also call TestSSH() from within itself and from there the function exits the function by default. That is, its like python has written a return None for you at the end of the function.

You could add a return yourself, but I don't know if this will help:

return TestSSH(device, retry_user, notes_in= "admin login",num_tries=1, timeout=10)
quamrana
  • 37,849
  • 12
  • 53
  • 71