Generasi otomatis lan ngisi unsur konfigurasi piranti jaringan nggunakake Nornir

Generasi otomatis lan ngisi unsur konfigurasi piranti jaringan nggunakake Nornir

Hey Habr!

Bubar ana artikel muncul ing kene Mikrotik lan Linux. Rutin lan otomatis ing ngendi masalah sing padha ditanggulangi kanthi nggunakake sarana fosil. Lan sanajan tugas kasebut pancen khas, ora ana sing padha karo Habré. Aku wani ngaturake sepedaku menyang komunitas IT sing dihormati.

Iki dudu mancal pisanan kanggo tugas kasebut. Pilihan pisanan wis dileksanakake sawetara taun kepungkur bali ing ansible versi 1.x.x. Sepedha kasebut arang digunakake lan mulane terus-terusan karat. Ing pangertèn sing tugas dhewe ora njedhul minangka asring versi dianyari ansible. Lan saben-saben sampeyan kudu nyopir, ranté tiba utawa setir tiba. Nanging, bagean pisanan, ngasilaken configs, tansah dianggo cetha banget, untunge jinja2 Mesin wis suwe diadegake. Nanging bagean kapindho - nggulung konfigurasi - biasane nggawa kejutan. Lan wiwit aku kudu muter metu config mbatalake kanggo setengah atus piranti, sawetara kang dumunung ewu kilometer adoh, nggunakake alat iki rada mboseni.

Ing kene aku kudu ngakoni manawa kahanan sing durung mesthi ana amarga ora ngerti ansibletinimbang kekurangane. Lan iki, kanthi cara, minangka titik penting. ansible minangka wilayah kawruh sing kapisah kanthi DSL (Basa Spesifik Domain), sing kudu dijaga kanthi yakin. Nah, wayahe iku ansible Iku berkembang cukup cepet, lan tanpa gati khusus kanggo kompatibilitas sakdurunge, iku ora nambah kapercayan.

Mulane, durung suwe, versi kapindho sepeda dileksanakake. Wektu iki ing python, utawa luwih tepat ing kerangka sing ditulis ing python lan kanggo python miturut jeneng Nornir

Dadi- Nornir yaiku microframework sing ditulis ing python lan kanggo python lan dirancang kanggo otomatisasi. Padha ing kasus karo ansible, kanggo ngatasi masalah ing kene, persiapan data sing kompeten dibutuhake, yaiku. persediaan sarwa dumadi lan paramèter sing, nanging Tulisan ditulis ora ing DSL kapisah, nanging ing padha ora lawas banget, nanging apik banget p[i|i]ton.

Ayo goleki apa sing nggunakake conto langsung ing ngisor iki.

Aku duwe jaringan cabang karo sawetara rolas kantor ing saindhenging negara. Saben kantor duwe router WAN sing mungkasi sawetara saluran komunikasi saka operator sing beda. Protokol routing yaiku BGP. Router WAN ana rong jinis: Cisco ISG utawa Juniper SRX.

Saiki tugas: sampeyan kudu ngatur subnet darmabakti kanggo Video Surveillance ing port kapisah ing kabeh router WAN saka jaringan cabang - Panggenan subnet iki ing BGP - ngatur watesan kacepetan port darmabakti.

Pisanan, kita kudu nyiyapake saperangan cithakan, ing basis kang konfigurasi bakal kui dhewe kanggo Cisco lan Juniper. Sampeyan uga perlu kanggo nyiapake data kanggo saben titik lan paramèter sambungan, i.e. ngumpulake persediaan padha

Cithakan siap kanggo Cisco:

$ cat templates/ios/base.j2 
class-map match-all VIDEO_SURV
 match access-group 111

policy-map VIDEO_SURV
 class VIDEO_SURV
    police 1500000 conform-action transmit  exceed-action drop

interface {{ host.task_data.ifname }}
  description VIDEOSURV
  ip address 10.10.{{ host.task_data.ipsuffix }}.254 255.255.255.0
  service-policy input VIDEO_SURV

router bgp {{ host.task_data.asn }}
  network 10.40.{{ host.task_data.ipsuffix }}.0 mask 255.255.255.0

access-list 11 permit 10.10.{{ host.task_data.ipsuffix }}.0 0.0.0.255
access-list 111 permit ip 10.10.{{ host.task_data.ipsuffix }}.0 0.0.0.255 any

Cithakan kanggo Juniper:

$ cat templates/junos/base.j2 
set interfaces {{ host.task_data.ifname }} unit 0 description "Video surveillance"
set interfaces {{ host.task_data.ifname }} unit 0 family inet filter input limit-in
set interfaces {{ host.task_data.ifname }} unit 0 family inet address 10.10.{{ host.task_data.ipsuffix }}.254/24
set policy-options policy-statement export2bgp term 1 from route-filter 10.10.{{ host.task_data.ipsuffix }}.0/24 exact
set security zones security-zone WAN interfaces {{ host.task_data.ifname }}
set firewall policer policer-1m if-exceeding bandwidth-limit 1m
set firewall policer policer-1m if-exceeding burst-size-limit 187k
set firewall policer policer-1m then discard
set firewall policer policer-1.5m if-exceeding bandwidth-limit 1500000
set firewall policer policer-1.5m if-exceeding burst-size-limit 280k
set firewall policer policer-1.5m then discard
set firewall filter limit-in term 1 then policer policer-1.5m
set firewall filter limit-in term 1 then count limiter

Cithakan, mesthi, ora metu saka hawa tipis. Iki pancen beda-beda ing antarane konfigurasi sing digunakake lan sawise ngrampungake tugas ing rong router tartamtu saka model sing beda.

Saka Cithakan kita waca sing kanggo ngatasi masalah, kita mung kudu loro paramèter Juniper lan 3 paramèter kanggo Cisco. punika:

  • yen jeneng
  • ipsufiks
  • asn

Saiki kita kudu nyetel paramèter kasebut kanggo saben piranti, i.e. nindakake perkara sing padha persediaan.

Kanggo persediaan Kita bakal strictly tindakake dokumentasi Initializing Nornir

yaiku, ayo nggawe kerangka file sing padha:

.
├── config.yaml
├── inventory
│   ├── defaults.yaml
│   ├── groups.yaml
│   └── hosts.yaml

File config.yaml minangka file konfigurasi nornir standar

$ cat config.yaml 
---
core:
    num_workers: 10

inventory:
    plugin: nornir.plugins.inventory.simple.SimpleInventory
    options:
        host_file: "inventory/hosts.yaml"
        group_file: "inventory/groups.yaml"
        defaults_file: "inventory/defaults.yaml"

Kita bakal nuduhake paramèter utama ing file kasebut sarwa dumadi.yaml, grup (ing kasusku iki logins / sandhi) ing kelompok.yaml, lan ing standar.yaml Kita ora bakal nuduhake apa-apa, nanging sampeyan kudu ngetik telung minus ing kana - nuduhake yen ana yaml file kosong sanadyan.

Iki kaya hosts.yaml:

---
srx-test:
    hostname: srx-test
    groups: 
        - juniper
    data:
        task_data:
            ifname: fe-0/0/2
            ipsuffix: 111

cisco-test:
    hostname: cisco-test
    groups: 
        - cisco
    data:
        task_data:
            ifname: GigabitEthernet0/1/1
            ipsuffix: 222
            asn: 65111

Lan iki grup.yaml:

---
cisco:
    platform: ios
    username: admin1
    password: cisco1

juniper:
    platform: junos
    username: admin2
    password: juniper2

Iki kedadeyan persediaan kanggo tugas kita. Sajrone initialization, paramèter saka file inventaris dipetakan menyang model obyek InventoryElement.

Ing ngisor spoiler ana diagram model InventoryElement

print(json.dumps(InventoryElement.schema(), indent=4))
{
    "title": "InventoryElement",
    "type": "object",
    "properties": {
        "hostname": {
            "title": "Hostname",
            "type": "string"
        },
        "port": {
            "title": "Port",
            "type": "integer"
        },
        "username": {
            "title": "Username",
            "type": "string"
        },
        "password": {
            "title": "Password",
            "type": "string"
        },
        "platform": {
            "title": "Platform",
            "type": "string"
        },
        "groups": {
            "title": "Groups",
            "default": [],
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "data": {
            "title": "Data",
            "default": {},
            "type": "object"
        },
        "connection_options": {
            "title": "Connection_Options",
            "default": {},
            "type": "object",
            "additionalProperties": {
                "$ref": "#/definitions/ConnectionOptions"
            }
        }
    },
    "definitions": {
        "ConnectionOptions": {
            "title": "ConnectionOptions",
            "type": "object",
            "properties": {
                "hostname": {
                    "title": "Hostname",
                    "type": "string"
                },
                "port": {
                    "title": "Port",
                    "type": "integer"
                },
                "username": {
                    "title": "Username",
                    "type": "string"
                },
                "password": {
                    "title": "Password",
                    "type": "string"
                },
                "platform": {
                    "title": "Platform",
                    "type": "string"
                },
                "extras": {
                    "title": "Extras",
                    "type": "object"
                }
            }
        }
    }
}

Model iki bisa katon rada mbingungake, utamane ing wiwitan. Kanggo ngerteni, mode interaktif ing python.

 $ ipython3
Python 3.6.9 (default, Nov  7 2019, 10:44:02) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.1.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from nornir import InitNornir                                                                           

In [2]: nr = InitNornir(config_file="config.yaml", dry_run=True)                                                

In [3]: nr.inventory.hosts                                                                                      
Out[3]: 
{'srx-test': Host: srx-test, 'cisco-test': Host: cisco-test}

In [4]: nr.inventory.hosts['srx-test'].data                                                                                    
Out[4]: {'task_data': {'ifname': 'fe-0/0/2', 'ipsuffix': 111}}

In [5]: nr.inventory.hosts['srx-test']['task_data']                                                     
Out[5]: {'ifname': 'fe-0/0/2', 'ipsuffix': 111}

In [6]: nr.inventory.hosts['srx-test'].platform                                                                                
Out[6]: 'junos'

Lan pungkasanipun, ayo pindhah menyang script dhewe. Aku ora duwe apa-apa utamané bangga kene. Aku mung njupuk conto siap-digawe saka tutorial lan digunakake meh ora owah. Iki minangka skrip kerja sing wis rampung:

from nornir import InitNornir
from nornir.plugins.tasks import networking, text
from nornir.plugins.functions.text import print_title, print_result

def config_and_deploy(task):
    # Transform inventory data to configuration via a template file
    r = task.run(task=text.template_file,
                 name="Base Configuration",
                 template="base.j2",
                 path=f"templates/{task.host.platform}")

    # Save the compiled configuration into a host variable
    task.host["config"] = r.result

    # Save the compiled configuration into a file
    with open(f"configs/{task.host.hostname}", "w") as f:
        f.write(r.result)

    # Deploy that configuration to the device using NAPALM
    task.run(task=networking.napalm_configure,
             name="Loading Configuration on the device",
             replace=False,
             configuration=task.host["config"])

nr = InitNornir(config_file="config.yaml", dry_run=True) # set dry_run=False, cross your fingers and run again

# run tasks
result = nr.run(task=config_and_deploy)
print_result(result)

Pay manungsa waé menyang parameter dry_run=Bener ing initialization obyek baris nr.
Ing kene padha karo ing ansible test run wis dipun ginakaken kang sambungan kanggo router digawe, konfigurasi modifikasi anyar wis disiapake, kang banjur divalidasi dening piranti (nanging iki ora tartamtu; iku gumantung ing support piranti lan implementasine driver ing NAPALM) , nanging konfigurasi anyar ora langsung ditrapake. Kanggo nggunakake pertempuran, sampeyan kudu mbusak parameter kasebut garing_run utawa ngganti regane kanggo palsu.

Nalika skrip dieksekusi, Nornir ngasilake log rinci menyang konsol.

Ing ngisor spoiler yaiku output saka pertempuran ing rong router tes:

config_and_deploy***************************************************************
* cisco-test ** changed : True *******************************************
vvvv config_and_deploy ** changed : True vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
---- Base Configuration ** changed : True ------------------------------------- INFO
class-map match-all VIDEO_SURV
 match access-group 111

policy-map VIDEO_SURV
 class VIDEO_SURV
    police 1500000 conform-action transmit  exceed-action drop

interface GigabitEthernet0/1/1
  description VIDEOSURV
  ip address 10.10.222.254 255.255.255.0
  service-policy input VIDEO_SURV

router bgp 65001
  network 10.10.222.0 mask 255.255.255.0

access-list 11 permit 10.10.222.0 0.0.0.255
access-list 111 permit ip 10.10.222.0 0.0.0.255 any
---- Loading Configuration on the device ** changed : True --------------------- INFO
+class-map match-all VIDEO_SURV
+ match access-group 111
+policy-map VIDEO_SURV
+ class VIDEO_SURV
+interface GigabitEthernet0/1/1
+  description VIDEOSURV
+  ip address 10.10.222.254 255.255.255.0
+  service-policy input VIDEO_SURV
+router bgp 65001
+  network 10.10.222.0 mask 255.255.255.0
+access-list 11 permit 10.10.222.0 0.0.0.255
+access-list 111 permit ip 10.10.222.0 0.0.0.255 any
^^^^ END config_and_deploy ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* srx-test ** changed : True *******************************************
vvvv config_and_deploy ** changed : True vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
---- Base Configuration ** changed : True ------------------------------------- INFO
set interfaces fe-0/0/2 unit 0 description "Video surveillance"
set interfaces fe-0/0/2 unit 0 family inet filter input limit-in
set interfaces fe-0/0/2 unit 0 family inet address 10.10.111.254/24
set policy-options policy-statement export2bgp term 1 from route-filter 10.10.111.0/24 exact
set security zones security-zone WAN interfaces fe-0/0/2
set firewall policer policer-1m if-exceeding bandwidth-limit 1m
set firewall policer policer-1m if-exceeding burst-size-limit 187k
set firewall policer policer-1m then discard
set firewall policer policer-1.5m if-exceeding bandwidth-limit 1500000
set firewall policer policer-1.5m if-exceeding burst-size-limit 280k
set firewall policer policer-1.5m then discard
set firewall filter limit-in term 1 then policer policer-1.5m
set firewall filter limit-in term 1 then count limiter
---- Loading Configuration on the device ** changed : True --------------------- INFO
[edit interfaces]
+   fe-0/0/2 {
+       unit 0 {
+           description "Video surveillance";
+           family inet {
+               filter {
+                   input limit-in;
+               }
+               address 10.10.111.254/24;
+           }
+       }
+   }
[edit]
+  policy-options {
+      policy-statement export2bgp {
+          term 1 {
+              from {
+                  route-filter 10.10.111.0/24 exact;
+              }
+          }
+      }
+  }
[edit security zones]
     security-zone test-vpn { ... }
+    security-zone WAN {
+        interfaces {
+            fe-0/0/2.0;
+        }
+    }
[edit]
+  firewall {
+      policer policer-1m {
+          if-exceeding {
+              bandwidth-limit 1m;
+              burst-size-limit 187k;
+          }
+          then discard;
+      }
+      policer policer-1.5m {
+          if-exceeding {
+              bandwidth-limit 1500000;
+              burst-size-limit 280k;
+          }
+          then discard;
+      }
+      filter limit-in {
+          term 1 {
+              then {
+                  policer policer-1.5m;
+                  count limiter;
+              }
+          }
+      }
+  }
^^^^ END config_and_deploy ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Ndhelikake sandhi ing ansible_vault

Ing wiwitan artikel, aku rada overboard ansible, nanging ora kabeh sing ala. Aku seneng banget kubah kaya, kang dirancang kanggo ndhelikake informasi sensitif metu saka ngarsane. Lan mbokmenawa akeh sing weruh yen kita duwe kabeh login / sandhi kanggo kabeh router tempur sing cerah ing wangun mbukak ing file. gorups.yaml. Ora ayu, mesthi. Ayo nglindhungi data iki karo kubah.

Ayo nransfer paramèter saka groups.yaml menyang creds.yaml, lan enkripsi nganggo AES256 nganggo sandhi 20 digit:

$ cd inventory
$ cat creds.yaml
---
cisco:
    username: admin1
    password: cisco1

juniper:
    username: admin2
    password: juniper2

$ pwgen 20 -N 1 > vault.passwd
ansible-vault encrypt creds.yaml --vault-password-file vault.passwd  
Encryption successful
$ cat creds.yaml 
$ANSIBLE_VAULT;1.1;AES256
39656463353437333337356361633737383464383231366233386636333965306662323534626131
3964396534396333363939373539393662623164373539620a346565373439646436356438653965
39643266333639356564663961303535353364383163633232366138643132313530346661316533
6236306435613132610a656163653065633866626639613537326233653765353661613337393839
62376662303061353963383330323164633162386336643832376263343634356230613562643533
30363436343465306638653932366166306562393061323636636163373164613630643965636361
34343936323066393763323633336366366566393236613737326530346234393735306261363239
35663430623934323632616161636330353134393435396632663530373932383532316161353963
31393434653165613432326636616636383665316465623036376631313162646435

Iku sing prasaja. Iku tetep kanggo mulang kita Nornir-script kanggo njupuk lan aplikasi data iki.
Kanggo nindakake iki, ing script kita sawise baris initialization nr = InitNornir(config_file=… tambahake kode ing ngisor iki:

...
nr = InitNornir(config_file="config.yaml", dry_run=True) # set dry_run=False, cross your fingers and run again

# enrich Inventory with the encrypted vault data
from ansible_vault import Vault
vault_password_file="inventory/vault.passwd"
vault_file="inventory/creds.yaml"
with open(vault_password_file, "r") as fp:
    password = fp.readline().strip()   
    vault = Vault(password)
    vaultdata = vault.load(open(vault_file).read())

for a in nr.inventory.hosts.keys():
    item = nr.inventory.hosts[a]
    item.username = vaultdata[item.groups[0]]['username']
    item.password = vaultdata[item.groups[0]]['password']
    #print("hostname={}, username={}, password={}n".format(item.hostname, item.username, item.password))

# run tasks
...

Mesthine, vault.passwd ora kudu ana ing jejere creds.yaml kaya ing contoku. Nanging ok kanggo muter.

Semono wae. Ana sawetara artikel liyane babagan Cisco + Zabbix teka, nanging iki dudu babagan otomatisasi. Lan ing mangsa cedhak aku arep nulis babagan RESTCONF ing Cisco.

Source: www.habr.com

Add a comment