ktbyers / netmiko

Multi-vendor library to simplify Paramiko SSH connections to network devices
MIT License
3.59k stars 1.31k forks source link

send command "copy running-config tftp:\n %s\n\r" % Tftp_server" #528

Closed bill831231 closed 5 years ago

bill831231 commented 7 years ago

Dear all,

I used the following lines to backup a configuration from cisco sw to a tftp server

        net_connect = ConnectHandler(**LoginInfo)
        net_connect.enable()
        command_ConfSave="copy running-config tftp:\n %s\n\r" % Tftp_server
        result=net_connect.send_command_expect(command_ConfSave)

it works with Catalyst switch, but when with Nexus, always facing an error as following Exception in thread Thread-2:

Traceback (most recent call last):
  File "C:\Program Files (x86)\Python36-32\lib\threading.py", line 916, in _bootstrap_inner
    self.run()
  File "C:\Program Files (x86)\Python36-32\lib\threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Temp\workspace\Production\SWlogin\ExcuteCommandOnSW.py", line 61, in BBakC
    result=net_connect.send_command_expect(command_ConfSave)
  File "C:\Program Files (x86)\Python36-32\lib\site-packages\netmiko\base_connection.py", line 823, in send_command_expect
    return self.send_command(*args, **kwargs)
  File "C:\Program Files (x86)\Python36-32\lib\site-packages\netmiko\base_connection.py", line 815, in send_command
    search_pattern))

seems the interaction between the netmiko and switch had an error, can not "#" from switch

Catalyst version 15.0(1)SE3 Nexus version 6.0(2)N2(2) Netmiko 1.4.1

My question: is there some way can help to see the realtime output from this ssh session? it may help to do some more trouble shooting on it

thanks a lot

ktbyers commented 7 years ago

@bill831231 Are you using a device_type=cisco_nxos.

Also a better pattern for handling commands with additional prompting is to do something like the following (using the send_command_timing() method).

        cmd1 = "copy running-config tftp:"
        result = net_connect.send_command_timing(cmd1)
        if 'Address or name of remote host' in result:
            result += net_connect.send_command_timing(Tftp_server)
        if 'Destination filename' in result: 
            # switch back to send_command() here as this might be slow
            result += net_connect.send_command()
bill831231 commented 7 years ago

@ktbyers ,

Hi Kirk,

Thanks a lot for the feedback, I have repeat the test, here are some more details: I have just tried your new commands, it works, and I have tried my code again with multi-thread and without multi-thread. in the single Thread mode, the way with one all command in one line also works
command_ConfSave="copy running-config tftp:\n\r\n%s\n\r" % Tftp_server result=net_connect.send_command_expect(command_ConfSave) in the multi-thread mode, both of the following methods got the same error: method 1: command_ConfSave="copy running-config tftp:\n\r\n%s\n\r" % Tftp_server result=net_connect.send_command_expect(command_ConfSave) method 2: cmd1 = "copy running-config tftp:" result = net_connect.send_command_timing(cmd1) if 'Address or name of remote host' in result: result += net_connect.send_command_timing(Tftp_server) if 'Destination filename' in result:

switch back to send_command() here as this might be slow

        result += net_connect.send_command()

Do you have some suggestion for running in multi-thread or multi-process?

My multi-thread code, may be there are some error. and the behavior is different from Catalyst and Nexus

class CommWithDevice(object):

def __init__(self):
    self.tftp="10.139.27.38"
def NBuildUp(self,line):
    LoginInfo=line.split(",")
    CiscoLogin={}
    if int(LoginInfo[-1])==0:
        CiscoLogin['device_type']=LoginInfo[0]
        CiscoLogin['ip']=LoginInfo[1]
        CiscoLogin['username']=LoginInfo[2]
        CiscoLogin['password']=LoginInfo[3]

        CiscoLogin['secret']=LoginInfo[4]
        CiscoLogin['verbose']=LoginInfo[5]

    return CiscoLogin

def ShowVersion(self):
    net_connect = ConnectHandler(**self.CiscoIOS)
    net_connect.enable()
    command_ConfSave="show version"

    result=net_connect.send_command_expect(command_ConfSave)

    print (result)
    net_connect.disconnect()

def BBakC(self,q):
    line=q.get()
    LoginInfo=self.NBuildUp(line)       
    net_connect = ConnectHandler(**LoginInfo)
    net_connect.enable()
    Tftp_server=self.tftp
    command_ConfSave="copy running-config tftp:\n %s\n\r" % Tftp_server
    result=net_connect.send_command_expect(command_ConfSave)
    print (result)
    if '%' in result:
        with open('hostbakerr.txt','at') as f:
            for ii in result:
                f.write(ii)
    net_connect.disconnect()
    q.task_done()
def BBakCt(self,q):
    line=q.get()
    LoginInfo=line.split(",")
    CiscoLogin={}
    if int(LoginInfo[-1])==0:
        CiscoLogin['device_type']=LoginInfo[0]
        CiscoLogin['ip']=LoginInfo[1]
        CiscoLogin['username']=LoginInfo[2]
        CiscoLogin['password']=LoginInfo[3]

        CiscoLogin['secret']=LoginInfo[4]
        CiscoLogin['verbose']=LoginInfo[5]

    net_connect = ConnectHandler(**CiscoLogin)
    net_connect.enable()
    Tftp_server=self.tftp
    command_ConfSave="copy running-config tftp:\n %s\n\r" % Tftp_server
    result=net_connect.send_command_expect(command_ConfSave)
    print (result)
    if '%' in result:
        with open('hostbakerr.txt','at') as f:
            for ii in result:
                f.write(ii)
    net_connect.disconnect()
    q.task_done()

def BBakN(self,q):
    line=q.get()
    LoginInfo=self.NBuildUp(line)       
    net_connect = ConnectHandler(**LoginInfo)
    net_connect.enable()
    Tftp_server=self.tftp
    cmd1 = "copy running-config tftp:"
    result = net_connect.send_command_timing(cmd1)
    if 'Address or name of remote host' in result:
        result += net_connect.send_command_timing(Tftp_server)
        print(result)
    if 'Destination filename' in result: 
        # switch back to send_command() here as this might be slow
        result += net_connect.send_command_timing('\n')
        print(result)

    print (result)
    net_connect.disconnect()
    q.task_done()

def ABC(self, file):
    q=Queue()
    MaxThreadNo=150
    tstart=time.time() 
    for i in range(MaxThreadNo):
        t=threading.Thread(target=self.BBakCt,args=(q,))
        t.setDaemon(True)
        t.start()

    with open(file,'r') as testlist:
        for line in testlist:   
            q.put(line)
        q.join()   
    tend=time.time()
    print("finished, totally takes %s s" %(tend-tstart) )

def ABN(self, file):
    q=Queue()
    MaxThreadNo=50
    tstart=time.time() 
    for i in range(MaxThreadNo):
        t=threading.Thread(target=self.BBakN,args=(q,))
        t.setDaemon(True)
        t.start()

    with open(file,'r') as testlist:
        for line in testlist:   
            q.put(line)
        q.join()   
    tend=time.time()
    print("finished, totally takes %s s" %(tend-tstart) )          

if name=="main":

LoginInstance= CommWithDevice()
LoginInstance.ABC('DeviceInfo.txt')       
LoginInstance.ABC('Ntest.txt') 
ktbyers commented 7 years ago

@bill831231 What are you trying to do at a high-level? Is it just backup configurations? If so, don't use TFTP just use straight SSH to do this.

bill831231 commented 7 years ago

Hi Kirk,

Thanks for the reply, backup configuration is the first function need this kind of interactive method :), then I found it is very hard to do the trouble shooting, since the output cannot be obtained with some easy way. I have tried to give the parameter for pattern search, seems also easy to write something with interaction, any suggestion for it?

thanks again, and nice evening

Mit freundlichen Grüßen Wu,Peng

2017-08-01 19:50 GMT+02:00 Kirk Byers notifications@github.com:

@bill831231 https://github.com/bill831231 What are you trying to do at a high-level? Is it just backup configurations? If so, don't use TFTP just use straight SSH to do this.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ktbyers/netmiko/issues/528#issuecomment-319445875, or mute the thread https://github.com/notifications/unsubscribe-auth/AUXaqJj4ccDWeqog16fbkzrDshjreuKwks5sT2VjgaJpZM4OeQ5E .

ktbyers commented 7 years ago

@bill831231 So if single thread, you should be able to just do this (as you referenced above):

cmd1 = "copy running-config tftp:"
result = net_connect.send_command_timing(cmd1)
if 'Address or name of remote host' in result:
    result += net_connect.send_command_timing(Tftp_server)
if 'Destination filename' in result:
    # switch back to send_command() here as this might be slow
    result += net_connect.send_command()

Not sure I understand your difficulty getting output statement? Are you talking in a multi-process/multi-thread context?

If running multithreads/multiprocess I would just make a function out of my backup process (note, I wouldn't use TFTP; I would do the backup straight using SSH i.e. just grab 'show run' from the SSH channel).

Here is an example using a queue. Threading code would be almost identical.

https://github.com/ktbyers/pynet-ons-jul17/blob/master/threads_procs/proc_w_queue.py

Note, you can get into a deadlock situation if you try to pass too much data through a queue.

bill831231 commented 7 years ago

Hi Kirk,

Seems I have a special case, I have test the code for nexus with single thread. it is working with the interaction way (send something, check the result, then send something). then just change to a multi-thread way, it is not working, then I want to check out what exactly is the going wrong(ssh link, input, output or some other things), then faced some difficulties there. and strange thing is change the code for from single thread to multi thread, seems no impact for catalyst at all :) Now trying to separate the code for nexus out from the calalyst.

thanks a lot for your support have a nice day

Mit freundlichen Grüßen Wu,Peng

2017-08-02 19:17 GMT+02:00 Kirk Byers notifications@github.com:

@bill831231 https://github.com/bill831231 So if single thread, you should be able to just do this (as you referenced above):

cmd1 = "copy running-config tftp:" result = net_connect.send_command_timing(cmd1) if 'Address or name of remote host' in result: result += net_connect.send_command_timing(Tftp_server) if 'Destination filename' in result:

switch back to send_command() here as this might be slow

result += net_connect.send_command()

Not sure I understand your difficulty getting output statement? Are you talking in a multi-process/multi-thread context?

If running multithreads/multiprocess I would just make a function out of my backup process (note, I wouldn't use TFTP; I would do the backup straight using SSH i.e. just grab 'show run' from the SSH channel).

Here is an example using a queue. Threading code would be almost identical.

https://github.com/ktbyers/pynet-ons-jul17/blob/master/ threads_procs/proc_w_queue.py

Note, you can get into a deadlock situation if you try to pass too much data through a queue.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ktbyers/netmiko/issues/528#issuecomment-319738849, or mute the thread https://github.com/notifications/unsubscribe-auth/AUXaqKXhoKTFsvy85jf9AinaMl7z6ftiks5sUK8fgaJpZM4OeQ5E .

bosspuj commented 6 years ago

@ktbyers Hi Kirk,

I am trying to use multiprocess and ftp to copy ios image from ftp server. I tried send_command and send_command_timing, but received the following error: IOError: Search pattern never detected in send_command_expect: Accessing\ ftp\:\/\/*****\:*****\@...\/example-img-file.bin...

Upon looking at the source code, I observed that the send_command method looks for the device hostname prompt, but it is failing as it is timing out with the predefined delay factor and max loops. Is there a way to change these predefined values? Also can I change the method to look for a different prompt?

Thanks in advance! Appreciate your awesome work!

ktbyers commented 6 years ago

@bosspuj Can you post your code?

bosspuj commented 6 years ago

@ktbyers

#!/usr/bin/env python

from __future__ import absolute_import,  division
from tabulate import tabulate
from pprint import pprint as pp
from progressbar import ProgressBar
from datetime import date
from mytools import get_credentials
import netmiko
import json
import re
import signal
import sys
import os
import difflib
import multiprocessing as mp
import time

signal.signal(signal.SIGPIPE, signal.SIG_DFL) # IOError: Broken pipe
signal.signal(signal.SIGINT, signal.SIG_DFL) # KeyboardInterrupt: Ctrl-C 

netmiko_exceptions = (netmiko.ssh_exception.NetMikoTimeoutException, 
                      netmiko.ssh_exception.NetMikoAuthenticationException)

username, password = get_credentials()
scriptdir = os.path.dirname(os.path.realpath('__file__'))
ips_file = os.path.join(scriptdir, 'hosts/tst_swdist_ips.txt')
with open(ips_file) as in_file:
    ips = in_file.read().splitlines()

def connect_device(device_ip):
    connect = netmiko.ConnectHandler(ip = device_ip, device_type = 'cisco_ios', username = username, password = password)
    return connect

def copy_sw(ip):
    connection = connect_device(ip)
    print('\n')
    print('*'*79)
    ngear = connection.base_prompt
    print('Connecting to Device %s - %s' %(ngear, ip))
    version = connection.send_command('show version')
    ver = re.search('(\/(.*)\/)*\w{1,}([.-]\w{1,})+\.bin', version)
    run_img = ver.group(2)
    if run_img is None:
        run_img = ver.group(0)
    else:
        pass
    print('~'*60)
    print('\nRunning Image Version: ')
    print(run_img)
    dir = connection.send_command('dir | exclude .lic|.pcap|.pkg|.cfg|.tar|.dat|.shtml|.cis|EST')
    directory = re.search('(\w{5,9})\:\/', dir)
    directory = directory.group(1)
    img_repository = []
    for d in os.listdir('/home/netftp'):
        img_repository.append(d)
    print('~'*60)
    print('\nAvailable Images in Repository...')
    for d in img_repository:
        print(d)
    suitable_img_files = difflib.get_close_matches(run_img, img_repository, 1)
    suitable_img_file = suitable_img_files[0]
    print('~'*60)
    print('\nBest Match:')
    print(suitable_img_file)
    print('\nFile copy in progress...\n')
    print('copy ftp://username:pass@internal_ip/%s %s:/...'%(suitable_img_file, directory))
    connection.send_command_timing('copy ftp://username:pass@internal_ip/%s %s:/'%(suitable_img_file, directory))
    connection.send_command_timing('\n')
    time.sleep(300)
    connection.disconnect()

def mp_handler():
    p = mp.Pool(5)
    p.map(copy_sw, ips)

try:
    mp_handler()

except netmiko_exceptions as e:
    print('Failed to ', ip, e)

except AttributeError as e:
    print('Failed to ', ip, e)

I am looking for a way to avoid that time.sleep(300)... make it dynamic (based on file copy time), instead of a static predefined value....

Thanks.

ktbyers commented 6 years ago

Probably try:

    connection.send_command('copy ftp://username:pass@internal_ip/%s %s:/'%(suitable_img_file, directory), delay_factor=4)

Note, by default send_command will wait 100 seconds, so delay_factor=4 will wait 400 seconds. If you need longer than this, you can increase it to a bigger value.

bosspuj commented 6 years ago

@ktbyers Thanks for the help! Much appreciated!