Test Paramiko to configure the switch

  • The topology

Configure the SSH Server, SSH login user, and password on SW1

  • Creating a Key Pair

    [SW1]rsa local-key-pair create 
    The key name will be: SW1_Host
    The range of public key size is (512 ~ 2048). 
    NOTES: If the key modulus is greater than 512, 
           it will take a few minutes.
    Input the bits inthe modulus[default = 512]:2048 Generating keys... . + + + + + + + + + + + +... + + + + + + + + + + + +. + + + + + + + +... ++++++++Copy the code

    Ubuntu 20.04 is a relatively new version and does not support keys shorter than 1023 bits. Therefore, you can directly configure keys of 1024 or 2048 bits

  • The Stelnet server was enabled

    stelnet server enable
    Copy the code
  • Configure SSH

    • Configure the SSH user login page

      user-interface vty 0 4
       authentication-mode aaa
       user privilege level 15
       protocol inbound ssh
      #
      Copy the code
    • Configuring SSH Users

      ssh user sht
      ssh user sht authentication-type password
      ssh user sht service-type stelnet
      #
      Copy the code

      If use SSH user password authentication, the authentication – type = “| rsa | password – rsa), then only need to generate local rsa key in SSH server. If SSH users use RSA authentication, local RSA key pairs need to be generated on both the server and client, and the public keys of the server and client need to be configured locally.

    • Configure password authentication for SSH users

      aaa
       local-user sht password cipher Huawei
       local-user sht privilege level 15
       local-user sht service-type ssh
      #
      Copy the code
  • Configuring management vlans

    • Create manage VLAN100

      #
      vlan 100
       management-vlan
      #Interface Vlanif100 IP address 192.168.50.1 255.255.255.0#
      Copy the code
    • Configuring Hybrid interfaces (For details on why Hybrid interfaces are configured, see Network Basics review [1])

      #
      interface Ethernet0/0/1
       port hybrid pvid vlan 100
       port hybrid untagged vlan 100
      #
      Copy the code

Ubuntu 20.04 Use SSH to remotely log in to SW1 and check whether the SSH configuration of SW1 takes effect

  • SSH [email protected] on Ubuntu 20.04

    $SSH [email protected] **Unable to negotiate with** 192.168.50.1 port 22: **no matching key exchange method found.** **Their offer: diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1**Copy the code
    • Problem Description:

      If the client and server cannot agree on a common set of parameters, the connection will fail. OpenSSH (7.0 and later) produces an error message similar to this. Ubuntu20.04 uses OpenSSH 8.2. In this case, the client and server cannot agree on the key exchange algorithm, and the server only provides (in this case, SW1) two methods, Diffie-Hellman-group1-SHA1 and Diffie-Hellman-group-exchange-sha1. OpenSSH supports both key exchange methods, but they are not enabled by default because they are older, weak and fall within the theoretical scope of the so-called Logjam attack. So we need to enable weaker methods on the client side.

      $SSH -v openssh_8.2p1 Ubuntu-4Ubuntu0.1, OpenSSL 1.1.1f 31 Mar 2020Copy the code
    • Solution:

      Source:

      OpenSSH: Legacy Options

      $SSH - oKexAlgorithms = + diffie-hellman group1 -- sha1 [email protected]Copy the code
      • +– Add the switching algorithm to the client default set rather than replace the default algorithm
      • -oOptions –
      • -KexAlgorithms– Key exchange method – used to generate the key for each connection;
  • After using ssh-okexalgorithms =+ Diffie-Hellman-group1-sha1 [email protected], SW1 is connected successfully

    $ ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 [email protected]
    The authenticity of host '192.168.50.1 (192.168.50.1)' can't be established.
    RSA key fingerprint is SHA256:BqJmFBxfyFuobgWlC7MmLlpjfu1UjsyhnLbdd/GIVc8.
    Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
    Warning: Permanently added '192.168.50.1'(RSA) to the list of known hosts. [email protected]'s password: 
    
    Info: The max number of VTY users is 5, and the number
          of current VTY users on line is 1.
          The current login time is 2021-03-02 19:12:58.
    <SW1>sys
    Enter system view, return user view with Ctrl+Z.
    [SW1]
    Copy the code

configurationparamiko_test.pyConfigure SW1

  • Write the script paramiko_test.py

    import paramiko
    import time
    
    ip_address = "192.168.50.1"
    username = "sht"
    password = "Huawei"
    
    ssh_client = paramiko.SSHClient()
    ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # not recommended for production but fine for labs
    ssh_client.connect(hostname = ip_address, username = username, password = password)
    
    print("Successful connection", ip_address)
    
    remote_connection = ssh_client.invoke_shell()
    remote_connection.send("system\n")
    # creation of vlans
    for n inRange (2, 21) :print("Creating VLAN " + str(n))
        remote_connection.send("vlan " + str(n) +  "\n")
        remote_connection.send("description VLAN " + str(n) +  "\n") time. Sleep (0.5)# wait half of second
    remote_connection.send("return\n")
    
    time.sleep(1)
    output = remote_connection.recv(65535)
    print(output.decode('ascii'))
    
    ssh_client.close
    Copy the code

    paramiko_test.pyThe script to explain

    • The first part

      ip_address = "192.168.50.1"
      username = "sht"
      password = "Huawei"
      
      ssh_client = paramiko.SSHClient()
      ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # not recommended for production but fine for labs
      ssh_client.connect(hostname = ip_address, username = username, password = password)
      Copy the code
      • Paramiko official document given Paramiko. Client. SSHClient class description as follows:

        High-level representation of an SSH session with an SSH server. This class encapsulates the Transport, Channel, and SFTPClient classes to handle authentication and open channels.

        The typical usage of SSHClient is as follows:

        client = SSHClient()
        client.load_system_host_keys()
        client.connect('ssh.example.com')
        Exec_command ('ls -l') exec_command('ls -l'
        Copy the code

        Then call load_system_host_keys(filename = None) to read the host key. Filename defaults to None. Attempts to read the key from the user’s local “Known hosts” file; Call connect() to establish a connection with the server whose hostname is ssh.example.com. The server’s host key is checked against the system host key (load_system_host_keys) and any local host key (load_host_keys). If the host name of the server is not found in either set of host keys, the missing host key policy (set_missing_host_KEY_policy) is used. The default policy is to reject the connection and report an error SSHException.

      • set_missing_host_key_policy(paramiko.AutoAddPolicy( ))

        • set_missing_host_key_policy(policy)Set the connection toUnknown Host KeyThe policy to be used when using the server;policyThe policy class to be set can be RejectPolicy(default), AutoAddPolicy, or WarningPolicy.If the policy is not configured, the connection to the server is denied by default.
        • willparamiko.AutoAddPolicy()Set to a parameter forAutomatically adds the host name and the new host key to the local HostKeys objectAnd save the pair information. This method is called by SSHClient.
        • It not only accepts the public key provided by the server with the unknown host key and establishes the connection, but also saves the host name and the corresponding key into the HostKeys object, and then connects to the server for useload_system_host_keysorload_host_keysMethods the
    • The second part

      remote_connection = ssh_client.invoke_shell()
      remote_connection.send("system\n")
      # creation of vlans
      for n in range (2.21) :print("Creating VLAN " + str(n))
          remote_connection.send("vlan " + str(n) +  "\n")
          remote_connection.send("description VLAN " + str(n) +  "\n")
          time.sleep(0.5) # wait half of second
      remote_connection.send("return\n")
      Copy the code
      • remote_connection = ssh_client.invoke_shell()
        • Since the SSHClient class encapsulates the Channel class, similar to socket, useinvoke_shell()After that, the client starts an interactive shell session on the SSH server, using the channel class (ChannelCreate a channel and connect it to a dummy terminal according to the required terminal type and size
        • ssh_clientSSHClient is an object of the SSHClient class that calls methods of SSHClientinvoke_shell()After creatingremote_connectionThe Channel of this Channel class
      • remote_connection.send()
        • In the Channel classsend()Used to transmit data
        • Here remote_connection is a channel, calledsend()Method to send data to the SSH server
    • The third part

      time.sleep(1)
      output = remote_connection.recv(65535)
      print(output.decode('ascii'))
      
      ssh_client.close
      Copy the code
      • time.sleep(1)

      • Output = remote_connection.recv(65535) the remote_connection channel uses recv(65535) to set the maximum number of Bytes read. If recv() returns a length of 0 bytes, the channel stream is closed;

      • Output.decode (‘ ASCII ‘), recv() returns a byte string, assigns the return value to the output variable, and then uses decode(‘ ASCII ‘) in Python 3 to parse the byte string into an ASCII encoding to help output beautiful content;

        If not applicabledecode('ascii'), the output is as follows:

      • Ssh_client. close Closes ssh_client. Close Closes the SSHClient class

Push the configuration to the device

  • Upload to SW1

  • Check whether the script configuration takes effect on SW1

    The script successfully configured the vlans on SW1 remotely.

Configure the MSTP scenario

Topology planning and basic device configuration

  • Topology and Planning

    • In MST1, SwitchA acts as the root and blocks the Ethernet0/0/2 interface on SwitchD
    • In MST2, SwitchB acts as the root and blocks the Ethernet0/0/2 interface of SwitchC
  • Basic Switch Configuration

    rsa local-key-pair create 
    **2048**
    #
    stelnet server enable
    #
    user-interface vty 0 4
     authentication-mode aaa
     user privilege level 15
     protocol inbound ssh
    #
    aaa
     local-user sht password cipher Huawei
     local-user sht privilege level 15
     local-user sht service-type ssh
    #
    ssh user sht
    ssh user sht authentication-type password
    ssh user sht service-type stelnet
    #
    vlan 100
     management-vlan
    #Interface Vlanif100 IP address 192.168.50.x 255.255.255.0Copy the code
    • SwitchA | SwitchB

      #
      interface Ethernet0/0/1
       port link-type trunk
       port trunk allow-pass vlan 100
      #
      interface Ethernet0/0/2
       port link-type trunk
       port trunk allow-pass vlan 100
      #
      interface Ethernet0/0/3
       port link-type trunk
       port trunk allow-pass vlan 100
      #
      Copy the code
    • SwitchC | SwitchD

      #
      interface Ethernet0/0/1
       port link-type trunk
       port trunk allow-pass vlan 100
      #
      interface Ethernet0/0/2
       port link-type trunk
       port trunk allow-pass vlan 100
      #
      Copy the code
  • Verify the connectivity between Ubuntu and switches

    $ ssh-keygen -f "/home/sht/.ssh/known_hosts" -R "192.168.50.100"
    # $SSH -keygen -f "/home/ SHT /. SSH /known_hosts" -r "192.168.50.101"
    # $SSH -keygen -f "/home/ SHT /. SSH /known_hosts" -r "192.168.50.102"
    # $SSH -keygen -f "/home/ SHT /. SSH /known_hosts" -r "192.168.50.103"
    Copy the code

    If the login is successful, the connection between Ubuntu and the switch is good and remote login can be performed.

Write the script and upload it to the device

Script overview

  • Configuration overview

  • Script overview

    #! /usr/bin/env python3
    
    import paramiko
    import time
    
    SwitchA = {
        'ip': '192.168.50.100'.'username': 'sht'.'password': 'Huawei'.'is_root': True.'primary_instances': ['1'].'secondary_instances': ['2'].'cost_change_interfaces': [].'trunk_interfaces': ['e0/0/1'.'e0/0/2'].'access_interfaces': [].'root_interfaces': ['e0/0/2'],
    }
    
    SwitchB = {
        'ip': '192.168.50.101'.'username': 'sht'.'password': 'Huawei'.'is_root': True.'primary_instances': ['2'].'secondary_instances': ['1'].'cost_change_interfaces': [].'trunk_interfaces': ['e0/0/1'.'e0/0/2'].'access_interfaces': [].'root_interfaces': ['e0/0/2'],
    }
    
    SwitchC = {
        'ip': '192.168.50.102'.'username': 'sht'.'password': 'Huawei'.'is_root': False.'primary_instances': ['1'].'secondary_instances': ['2'].'cost_change_interfaces': ['e0/0/2'].'trunk_interfaces': ['e0/0/1'.'e0/0/2'].'access_interfaces': ['e0/0/3'].'root_interfaces':[],
    }
    
    SwitchD = {
        'ip': '192.168.50.103'.'username': 'sht'.'password': 'Huawei'.'is_root': False.'primary_instances': ['2'].'secondary_instances': ['1'].'cost_change_interfaces': ['e0/0/2'].'trunk_interfaces': ['e0/0/1'.'e0/0/2'].'access_interfaces': ['e0/0/3'].'root_interfaces':[],
    }
    
    def paramiko_SSH_Command(switch) :
    	''' Start '''
    	ssh_client = paramiko.SSHClient()
    	ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # not recommended for production but fine for labs
    	ssh_client.connect(hostname = switch['ip'], username = switch['username'], password = switch['password'])
    
    	print(""*3)
    	print("= = = = = = = = = = = = = = ="*3)
    	print(""*3)
    	print("Successful connection", switch['ip'])
    	remote_connection = ssh_client.invoke_shell()
    
    	remote_connection.send("system\n")
    
    	''' Some Configuration Functions '''
    
    	common_Configuration(remote_connection, switch['cost_change_interfaces'], switch['secondary_instances'])
    	if switch['is_root']:
    		root_PriSec(remote_connection, switch['primary_instances'], switch['secondary_instances'])
    	edged_port_bpdu_protection(remote_connection, switch['access_interfaces'])
    	if switch['root_interfaces']:
    		root_protection(remote_connection, switch['root_interfaces'])
    	conf_trunk_interfaces(remote_connection, switch['trunk_interfaces'])
    	if switch == SwitchC:
    		conf_access_interfaces(remote_connection, switch['access_interfaces'].2)
    	elif switch == SwitchD:
    		conf_access_interfaces(remote_connection, switch['access_interfaces'].11)
    
    	''' End '''
    	remote_connection.send("return\n")
    	remote_connection.send("save\n")
    	remote_connection.send("y\n")
    	remote_connection.send("\n")
    
    	time.sleep(5)
    	output = remote_connection.recv(65535)
    	print(output.decode('ascii'))
    
    	ssh_client.close
    
    def common_Configuration(remote_connection, cost_change_interfaces, secondary_instances) :
    	# Stp region_configuration
    	print("Configuring stp region_configuration ...")
    	region_configurations = ['stp region-configuration\n'.'region-name RG1\n'.'instance 1 vlan 2 to 10\n'.'instance 2 vlan 11 to 20\n'.'active region-configuration\n'.'quit\n']
    	for region_configuration in region_configurations:
    		remote_connection.send(region_configuration)
    
    	# Stp cost computing methods
    	print("Configuring stp pathcost-standard legacy ...")
    	remote_connection.send("stp pathcost-standard legacy\n")
    	# Stp interface cost
    	print("Configuring interface cost ...")
    	if cost_change_interfaces:
    		for secondary_instance in secondary_instances:
    			for cost_change_interface in cost_change_interfaces:
    				remote_connection.send("interface " + cost_change_interface + "\n")
    				remote_connection.send("stp instance " + secondary_instance +" cost 20000\n")
    				remote_connection.send("quit\n")
    
    	# STP enable
    	print("Creating VLAN and Enabling STP ...")
    	remote_connection.send("vlan batch 2 to 20\n")
    	remote_connection.send("stp enable\n")
    
    def conf_trunk_interfaces(remote_connection, trunk_interfaces) :
    	print("Configuring trunk interfaces ...")
    	for trunk_interface in trunk_interfaces:
    		remote_connection.send("interface " + trunk_interface + "\n")
    		remote_connection.send("port link-type trunk\n")
    		remote_connection.send("port trunk allow-pass vlan 2 to 20\n")
    		remote_connection.send("quit\n")
    
    def conf_access_interfaces(remote_connection, access_interfaces, access_vlan) :
    	print("Configuring access interfaces ...")
    	for access_interface in access_interfaces:
    		remote_connection.send("interface " + access_interface + "\n")
    		remote_connection.send("port link-type access\n")
    		remote_connection.send("port default vlan " + str(access_vlan) + "\n")
    		remote_connection.send("quit\n")
    
    def root_protection(remote_connection, interfaces) :
    	print("Configuring stp protection ...")
    	for interface in interfaces:
    		remote_connection.send("interface " + interface + "\n")
    		remote_connection.send("stp root-protection\n")
    		remote_connection.send("quit\n")
    
    def edged_port_bpdu_protection(remote_connection, interfaces) :
    	print("Configuring edged-port and bpdu protection ...")
    	if interfaces:
    		for interface in interfaces:
    			remote_connection.send("interface " + interface + "\n")
    			remote_connection.send("stp edged-port enable\n")
    			remote_connection.send("quit\n")
    		remote_connection.send("stp bpdu-protection\n")
    
    def root_PriSec(remote_connection, primary_instances, secondary_instances) :
    	# Stp primary root
    	print("Configuring primary root ...")
    	if primary_instances:
    		for primary_instance in primary_instances:
    			remote_connection.send("stp instance " + primary_instance + " root primary\n")
    	else:
    		print("No primary root configuration.")
    	# Stp secondary root
    	print("Configuring secondary root ...")
    	if secondary_instances:
    		for secondary_instance in secondary_instances:
    			remote_connection.send("stp instance " + secondary_instance + " root secondary\n")
    	else:
    		print("No secondary root configuration.")
    
    if __name__=="__main__":
    	paramiko_SSH_Command(SwitchA)
    	paramiko_SSH_Command(SwitchB)
    	paramiko_SSH_Command(SwitchC)
    	paramiko_SSH_Command(SwitchD)
    Copy the code

Push configuration to the device

Script parsing

  • Use dict to regulate switch configuration parameters
    • The access layer differs from the aggregation layer in some configurations, and the keys in the dictionary are the same
    • For the purpose of calling different methods to configure different configurations, keys that do not have corresponding configurations are set with null values. When the function is called, it checks whether the value is null. If it is empty, no specific function is configured. If the value is not empty, the value is invoked according to the key to configure the corresponding function.
  • useparamiko_SSH_Command(switch)Call the remaining methods that correspond to various configurations as a general method
    • The STP region-configuraion configuration of the four switches is the same. The same VLAN needs to be created and STP(the default MSTP mode) needs to be enabled on the four switches. In addition, STP costs are calculated using huawei’s unique method. Use a functioncommon_ConfigurationEncapsulation;
    • When configuring the Access interface, SwitchA and SwitchB at the aggregation layer do not need to be configuredconf_access_interfaces(remote_connection, access_interfaces, access_vlan), check whether the switch object is SwitchC or SwitchD. For example, SwitchC is responsible for hosts on even-numbered vlans, and SwitchD is responsible for hosts on odd-numbered vlans. Configure different Pvids by matching SwitchC or SwitchD.
    • Similar to the configuration of edge portsedged_port_bpdu_protectionFrom some special conditions (such as different Switch configuration PVID), only need to configure the interface is different on each of the switches, can make the edge port command without the participation of other parameters, you can first determine whether need to configure the edge of the port list is empty, empty is not configured, no empty, the for loop calls for configuration interface list, Each interface is configured one by one. Similarly, MSTP root and standby root are configured only on SwitchA and SwitchB. To avoid configuration on SwitchC and SwitchD, the MSTP root and standby root are invoked only on the aggregation layer according to the instance fields in the device dictionaryroot_PriSec.

Verify the configuration

  • View the port status and port protection type
    • SwitchA

      [SwitchA]display stp brief 
       MSTID  Port                        Role  STP State     Protection
      ...
         1    Ethernet0/0/1               DESI  FORWARDING      NONE
         1    Ethernet0/0/2               DESI  FORWARDING      ROOT
         2    Ethernet0/0/1               ROOT  FORWARDING      NONE
         2    Ethernet0/0/2               DESI  FORWARDING      ROOT
      Copy the code
      • In MSTI1, SwitchA is the root bridge, and Ethernet0/0/1 and Ethernet0/0/2 are the specified ports.
      • In MSTI2, Ethernet0/0/1 of SwitchA becomes the specified port, and Ethernet0/0/2 becomes the root port.
    • SwitchB

      [SwitchB]display stp brief 
       MSTID  Port                        Role  STP State     Protection
      ...
         1    Ethernet0/0/1               ROOT  FORWARDING      NONE
         1    Ethernet0/0/2               DESI  FORWARDING      ROOT
         2    Ethernet0/0/1               DESI  FORWARDING      NONE
         2    Ethernet0/0/2               DESI  FORWARDING      ROOT
      Copy the code
      • In MSTI2, SwitchB is the root bridge, and Ethernet0/0/1 and Ethernet0/0/2 are the specified ports.
      • In MSTI1, Ethernet0/0/2 of SwitchB becomes the specified port, and Ethernet0/0/1 becomes the root port.
    • SwitchC

      <SwitchC>display stp interface Ethernet 0/0/1 brief 
       MSTID  Port                        Role  STP State     Protection
      ..
         1    Ethernet0/0/1               ROOT  FORWARDING      NONE
         2    Ethernet0/0/1               ROOT  FORWARDING      NONE
      <SwitchC>display stp interface Ethernet 0/0/2 brief
       MSTID  Port                        Role  STP State     Protection
      ...
         1    Ethernet0/0/2               DESI  FORWARDING      NONE
         2    Ethernet0/0/2               ALTE  DISCARDING      NONE
      Copy the code
      • In MSTI1 and MSTI2, Ethernet0/0/1 is the root port.
      • In MST1, Ethernet0/0/2 is the specified port. In MST2, Ethernet0/0/1 is blocked;
    • SwitchD

      <SwitchD>display stp interface e0/0/1 brief 
       MSTID  Port                        Role  STP State     Protection
      ...
         1    Ethernet0/0/1               ROOT  FORWARDING      NONE
         2    Ethernet0/0/1               ROOT  FORWARDING      NONE
      <SwitchD>display stp interface e0/0/2 brief
       MSTID  Port                        Role  STP State     Protection
      ...
         1    Ethernet0/0/2               ALTE  DISCARDING      NONE
         2    Ethernet0/0/2               DESI  FORWARDING      NONE
      Copy the code
      • In MSTI1 and MSTI2, Ethernet0/0/1 is the root port.
      • In MST2, Ethernet0/0/2 is the specified port. In MST1, Ethernet0/0/1 is blocked;

reference

  • Paramiko documentation
  • Huawei S3700 configuration documents

Write in the last

  • This paper mainly records my learning process of Paramiko, aiming to improve my understanding of paramiko and programming ability. It is just a way of exploration and may not be a reliable reference for learning.
  • There are a lot of content in the article is translation and their own thoughts, there may be many mistakes, but also please everyone can point out the mistakes and shortcomings.

Content of the past

[1] Huawei simulator eNSP was used to build network automation scenarios

Network Basics review

[1] Switch data processing process