技术教程 · 2026年5月24日 · VirtLab

网络自动化:使用 Python 和 Ansible 自动化网络运维

深入介绍网络自动化工具和方法,包括 Python Netmiko、Ansible、Nornir 等主流框架的实战应用。

网络自动化运维

网络自动化:使用 Python 和 Ansible 自动化网络运维

网络自动化是现代网络工程师的必备技能。本文将介绍如何使用 Python 和 Ansible 等工具实现网络设备的自动化配置和管理。

网络自动化概述

为什么需要网络自动化?

传统手动操作                              自动化操作
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

┌──────────────┐                    ┌──────────────┐
│  人工 SSH    │                    │  批量执行    │
│   逐台登录   │         ===>        │  并行处理    │
└──────┬───────┘                    └──────┬───────┘
       │                                   │
       ▼                                   ▼
┌──────────────┐                    ┌──────────────┐
│  手动配置    │                    │  配置模板化   │
│  易出错      │         ===>        │  一致性高    │
└──────┬───────┘                    └──────┬───────┘
       │                                   │
       ▼                                   ▼
┌──────────────┐                    ┌──────────────┐
│  文档记录    │                    │ 版本控制     │
│  不完整      │         ===>        │ 可追溯       │
└──────────────┘                    └──────────────┘

优势:效率提升 10x+,错误率降低 90%+

自动化工具生态

类别工具适用场景
配置管理Ansible, Chef, Puppet配置同步、批量修改
编程接口Netmiko, NAPALM, PyeAPI编程控制、定制化
编排平台AWX, Tower, Terraform工作流编排、基础设施
监控自动化Ansible Tower, RunDeck任务调度、审批流程

Python 网络自动化

Netmiko 基础

#!/usr/bin/env python3
"""
Netmiko 基础用法示例
"""
from netmiko import ConnectHandler
from datetime import datetime

def backup_device(device_info):
    """备份网络设备配置"""
    print(f"连接到设备: {device_info['host']}")
    
    # 建立连接
    connection = ConnectHandler(**device_info)
    
    # 获取主机名
    prompt = connection.find_prompt()
    hostname = prompt.replace('#', '').replace('>', '')
    
    # 获取配置
    print("正在获取配置...")
    config = connection.send_command("show running-config")
    
    # 保存到文件
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"backup_{hostname}_{timestamp}.cfg"
    
    with open(filename, 'w') as f:
        f.write(config)
    
    print(f"配置已保存到: {filename}")
    
    connection.disconnect()
    return filename

# 设备连接信息
device = {
    'device_type': 'cisco_ios',
    'host': '192.168.1.1',
    'username': 'admin',
    'password': 'cisco123',
    'secret': 'cisco123',  # 特权模式密码
}

# 执行备份
backup_device(device)

批量配置

#!/usr/bin/env python3
"""
批量配置网络设备
"""
from netmiko import ConnectHandler, exceptions
from concurrent.futures import ThreadPoolExecutor, as_completed
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def configure_device(device_info, commands):
    """配置单个设备"""
    try:
        logger.info(f"连接 {device_info['host']}...")
        connection = ConnectHandler(**device_info)
        
        # 进入特权模式
        connection.enable()
        
        # 批量发送命令
        output = connection.send_config_set(commands)
        
        # 保存配置
        connection.save_config()
        
        logger.info(f"{device_info['host']} 配置完成")
        
        connection.disconnect()
        return {"status": "success", "host": device_info['host']}
        
    except exceptions.NetmikoAuthenticationException:
        logger.error(f"{device_info['host']} 认证失败")
        return {"status": "auth_error", "host": device_info['host']}
    except Exception as e:
        logger.error(f"{device_info['host']} 配置失败: {e}")
        return {"status": "error", "host": device_info['host'], "msg": str(e)}

def batch_config(devices, commands, max_workers=10):
    """批量配置多台设备"""
    results = []
    
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = {
            executor.submit(configure_device, device, commands): device
            for device in devices
        }
        
        for future in as_completed(futures):
            result = future.result()
            results.append(result)
    
    return results

# 配置命令列表
config_commands = [
    'vlan 100',
    'name DATA_VLAN',
    'vlan 200',
    'name VOICE_VLAN',
    'interface range GigabitEthernet0/0 - 3',
    'switchport mode access',
    'switchport access vlan 100',
]

# 设备列表
devices = [
    {
        'device_type': 'cisco_ios',
        'host': '192.168.1.1',
        'username': 'admin',
        'password': 'cisco123',
        'secret': 'cisco123',
    },
    {
        'device_type': 'cisco_ios',
        'host': '192.168.1.2',
        'username': 'admin',
        'password': 'cisco123',
        'secret': 'cisco123',
    },
]

# 执行批量配置
results = batch_config(devices, config_commands)
print(results)

NAPALM 高级操作

#!/usr/bin/env python3
"""
使用 NAPALM 进行跨厂商网络自动化
"""
from napalm import get_network_driver

def get_device_info(host, driver_type):
    """获取设备信息"""
    drivers = {
        'cisco': 'ios',
        'huawei': 'vrp',
        'juniper': 'junos',
        'arista': 'eos',
    }
    
    driver_name = drivers.get(driver_type, 'ios')
    driver = get_network_driver(driver_name)
    
    device = driver(
        hostname=host,
        username='admin',
        password='cisco123',
        optional_args={'secret': 'cisco123'}
    )
    
    return device

def inventory_network(device_type, hosts):
    """批量获取网络设备信息"""
    results = []
    
    for host in hosts:
        try:
            device = get_device_info(host, device_type)
            device.open()
            
            # 获取信息
            facts = device.get_facts()
            interfaces = device.get_interfaces()
            arp_table = device.get_arp_table()
            
            results.append({
                'host': host,
                'status': 'success',
                'facts': facts,
                'interfaces_count': len(interfaces),
                'arp_entries': len(arp_table),
            })
            
            device.close()
            
        except Exception as e:
            results.append({
                'host': host,
                'status': 'error',
                'message': str(e),
            })
    
    return results

Ansible 网络自动化

Inventory 配置

# inventory.yml
all:
  children:
    cisco_devices:
      children:
        cisco_ios:
          hosts:
            core-sw-01:
              ansible_host: 192.168.1.1
              ansible_user: admin
              ansible_password: cisco123
            core-sw-02:
              ansible_host: 192.168.1.2
              ansible_user: admin
              ansible_password: cisco123
        cisco_nxos:
          hosts:
            nxos-switch:
              ansible_host: 192.168.2.1
              ansible_user: admin
              ansible_password: cisco123

    huawei_devices:
      hosts:
        huawei-sw-01:
          ansible_host: 192.168.3.1
          ansible_user: admin
          ansible_password: huawei123

Playbook 示例

# cisco_vlan_config.yml
---
- name: Configure VLANs on Cisco Devices
  hosts: cisco_ios
  gather_facts: no
  connection: local
  
  vars:
    vlans:
      - id: 10
        name: DATA
      - id: 20
        name: VOICE
      - id: 30
        name: WIRELESS
      - id: 99
        name: MGMT

  tasks:
    - name: Configure VLANs
      cisco.ios.ios_vlans:
        config: "{{ vlans }}"
        state: merged

    - name: Save configuration
      cisco.ios.ios_config:
        save_when: modified

角色结构

# Ansible 角色目录结构
roles/
└── network_vlan/
    ├── defaults/
   └── main.yml
    ├── tasks/
   └── main.yml
    ├── templates/
   └── vlan_config.j2
    └── handlers/
        └── main.yml
# roles/network_vlan/tasks/main.yml
---
- name: Deploy VLAN configuration
  cisco.ios.ios_vlans:
    config: "{{ vlans }}"
    state: "{{ vlans_state }}"

- name: Deploy port configurations
  cisco.ios.ios_l2_interfaces:
    config: "{{ port_configs }}"
    state: "{{ port_state }}"

Nornir 自动化框架

#!/usr/bin/env python3
"""
使用 Nornir 进行网络自动化
"""
from nornir import InitNornir
from nornir.core import Task
from nornir_utils.plugins.functions import print_result
from nornir_netmiko.tasks import netmiko_send_command, netmiko_send_config

def backup_config(task: Task) -> None:
    """备份配置"""
    task.run(
        task=netmiko_send_command,
        command_string="show running-config"
    )

def configure_vlan(task: Task, vlan_id: int, vlan_name: str) -> None:
    """配置 VLAN"""
    task.run(
        task=netmiko_send_config,
        config_commands=[
            f"vlan {vlan_id}",
            f"name {vlan_name}",
        ]
    )

# 初始化 Nornir
nr = InitNornir(config_file="nornir.yml")

# 备份所有设备配置
print("=== 备份配置 ===")
result = nr.run(task=backup_config)
print_result(result)

# 批量配置 VLAN
print("\n=== 配置 VLAN ===")
nr.run(
    task=configure_vlan,
    vlan_id=100,
    vlan_name="CORP_VLAN"
)

网络自动化最佳实践

配置模板化

{# templates/cisco_interface.j2 #}
interface {{ interface_name }}
 description {{ description }}
 {% if ip_address %}
 ip address {{ ip_address }} {{ subnet_mask }}
 {% endif %}
 {% if vlan %}
 switchport access vlan {{ vlan }}
 {% endif %}
 {% if switchport_mode %}
 switchport mode {{ switchport_mode }}
 {% endif %}
 no shutdown
!

版本控制

# Git 仓库结构
network-automation/
├── configs/
   ├── templates/
   └── interface.j2
   └── variables/
       └── site-a.yml
├── playbooks/
   ├── deploy.yml
   └── rollback.yml
├── roles/
   └── network_device/
├── inventory/
   ├── hosts.yml
   └── group_vars/
├── tests/
   └── test_configs.py
├── Jenkinsfile
└── README.md

CI/CD 流程

┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐
│  Code   │────▶│  Build  │────▶│  Test   │────▶│ Deploy  │
│ Commit  │     │  & Lint │     │ & Valid │     │         │
└─────────┘     └─────────┘     └─────────┘     └─────────┘


                             ┌─────────────┐
                             │  Rollback   │
                             │  if Failed  │
                             └─────────────┘

总结

网络自动化是网络工程师职业发展的必备技能。建议从 Python 基础开始,逐步学习 Ansible,最终建立完整的自动化运维体系。通过自动化,您可以大幅提升工作效率,减少人为错误,为职业发展增添竞争力。

#网络自动化 #Python #Ansible #Netmiko #DevOps