Насли худкор ва пур кардани унсурҳои конфигуратсияи дастгоҳи шабакавӣ бо истифода аз Nornir

Насли худкор ва пур кардани унсурҳои конфигуратсияи дастгоҳи шабакавӣ бо истифода аз Nornir

Эй Ҳабр!

Ба наздикӣ дар ин ҷо як мақола пайдо шуд Mikrotik ва Linux. Реҷа ва автоматизатсия ки дар он чо хамин гуна проблема бо истифода аз воситахои фосилавй хал карда мешуд. Ва гарчанде ки вазифа комилан маъмул аст, аммо дар Ҳабре ҳеҷ чизи шабеҳе вуҷуд надорад. Ман ҷуръат мекунам, ки велосипедамро ба ҷомеаи мӯҳтарами IT пешниҳод кунам.

Ин аввалин велосипед барои чунин вазифа нест. Варианти аввал чанд сол пеш дар бозгашт амалӣ карда шуд иҷрошаванда версияи 1.x.x. Велосипед кам истифода мешуд ва аз ин рӯ пайваста занг мезад. Ба он маъно, ки худи вазифа на он қадар зуд-зуд нав карда мешавад иҷрошаванда. Ва хар дафъае, ки ба шумо мошин рондан лозим меояд, занчир меафтад ё чарх меафтад. Аммо, қисми аввал, тавлиди конфигуратсияҳо, хушбахтона, ҳамеша хеле равшан кор мекунад ҷинҷа 2 Мотор кайҳо боз таъсис ёфтааст. Аммо қисми дуюм, ки конфигуратсияҳоро паҳн мекунад, одатан ногаҳонӣ овард. Ва азбаски ман бояд конфигуратсияро ба таври фосилавӣ ба нимсад дастгоҳ, ки баъзе аз онҳо ҳазорҳо километр дур ҷойгиранд, паҳн кунам, истифодаи ин асбоб каме дилгиркунанда буд.

Дар ин ҷо ман бояд иқрор шавам, ки номуайянии ман эҳтимолан дар набудани ошноии ман аст иҷрошаванданазар ба камбудихои худ. Ва ин, дар омади гап, як нуктаи муҳим аст. иҷрошаванда як минтақаи комилан алоҳида, соҳаи дониши худ бо DSL (Забони мушаххаси домен), ки бояд дар сатҳи боварӣ нигоҳ дошта шавад. Бале, ҳамон лаҳза иҷрошаванда Он хеле зуд инкишоф меёбад ва бидуни таваҷҷӯҳи махсус ба мутобиқати ақиб, он эътимодро илова намекунад.

Аз ин рӯ, чанде пеш версияи дуюми велосипед амалӣ карда шуд. Ин дафъа дар python, ё дурусттар дар чаҳорчӯбае, ки дар он навишта шудааст python ва барои python дар зери ном Норнир

Пас - Норнир микрочаҳорчӯба аст, ки дар он навишта шудааст python ва барои python ва барои автоматикунонӣ пешбинӣ шудааст. Ҳамон тавре ки дар мавриди бо иҷрошаванда, барои ҳалли мушкилот дар ин ҷо, омодасозии маълумоти салоҳиятдор талаб карда мешавад, яъне. инвентаризатсияи ҳостҳо ва параметрҳои онҳо, аммо скриптҳо на дар DSL-и алоҳида, балки дар ҳамон p[i|i]ton на он қадар кӯҳна, балки хеле хуб навишта шудаанд.

Биёед бубинем, ки он мисоли зиндаи зеринро истифода мебарад.

Ман як шабакаи филиал дорам, ки дар саросари кишвар даҳҳо офисҳо доранд. Ҳар як идора дорои роутери WAN мебошад, ки якчанд каналҳои алоқаро аз операторҳои гуногун қатъ мекунад. Протоколи масир BGP аст. Роутерҳои WAN дар ду намуд меоянд: Cisco ISG ё Juniper SRX.

Акнун вазифа: ба шумо лозим аст, ки зершабакаи махсусро барои назорати видеоӣ дар бандари алоҳида дар ҳама роутерҳои WAN-и шабакаи филиал танзим кунед - ин зершабакаро дар BGP таблиғ кунед - маҳдудияти суръати порти махсусро танзим кунед.

Аввалан, мо бояд якчанд шаблон омода кунем, ки дар асоси онҳо конфигуратсияҳо барои Cisco ва Juniper алоҳида тавлид карда мешаванд. Инчунин барои ҳар як нуқта ва параметрҳои пайвастшавӣ маълумот омода кардан лозим аст, яъне. ҳамон инвентаризатсияро ҷамъ кунед

Шаблони омода барои 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

Шаблон барои арча:

$ 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

Шаблонҳо, албатта, аз ҳаво берун намеоянд. Инҳо аслан фарқияти байни конфигуратсияҳои корӣ мебошанд, ки пас аз ҳалли вазифа дар ду роутери мушаххаси моделҳои гуногун буданд ва буданд.

Аз қолабҳои мо мебинем, ки барои ҳалли мушкилот ба мо танҳо ду параметр барои Juniper ва 3 параметр барои Cisco лозим аст. инҳоянд:

  • ifname
  • ipsuffix
  • асн

Акнун мо бояд ин параметрҳоро барои ҳар як дастгоҳ муқаррар кунем, яъне. ҳамон корро кунед инвентаризатсияи.

барои инвентаризатсияи Мо ҳуҷҷатҳоро қатъиян риоя хоҳем кард Оғози Nornir

яъне биёед ҳамон скелети файлро созем:

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

Файли config.yaml файли конфигуратсияи стандартии nornir аст

$ 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"

Мо параметрҳои асосиро дар файл нишон медиҳем hosts.yaml, гурӯҳ (дар ҳолати ман инҳо логинҳо/паролҳо мебошанд) дар гуруххо.ямл, дар ҳоле ки дар defaults.yaml Мо чизеро нишон намедиҳем, аммо шумо бояд дар он ҷо се минус ворид кунед - нишон медиҳад, ки ин аст ямл ҳарчанд файл холӣ аст.

Ин аст он чизе ки 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

Ва ин аст group.yaml:

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

juniper:
    platform: junos
    username: admin2
    password: juniper2

Ин вокеа руй дод инвентаризатсияи барои вазифаи мо. Ҳангоми оғозёбӣ, параметрҳо аз файлҳои инвентаризатсия ба модели объект харита карда мешаванд InventoryElement.

Дар поёни спойлер диаграммаи модели 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"
                }
            }
        }
    }
}

Ин модел метавонад каме печида бошад, махсусан дар аввал. Барои фаҳмидани он, режими интерактивӣ дар ipython.

 $ 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'

Ва ниҳоят, биёед ба худи скрипт гузарем. Дар ин ҷо ман чизе надорам, ки аз он фахр кунам. Ман танҳо як мисоли тайёрро аз он гирифтам дастур ва онро кариб бетагйир истифода бурданд. Ин аст скрипти кории анҷомшуда чунин менамояд:

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)

Ба параметр диққат диҳед dry_run=Дуруст дар оғозкунии объекти сатр nr.
Дар ин ҷо ҳамон тавре ки дар иҷрошаванда озмоиши санҷишӣ амалӣ карда шуд, ки дар он пайвастшавӣ ба роутер анҷом дода мешавад, конфигуратсияи нави тағирёфта омода карда мешавад, ки баъд аз он аз ҷониби дастгоҳ тасдиқ карда мешавад (аммо ин аниқ нест; он аз дастгирии дастгоҳ ва татбиқи драйвер дар NAPALM вобаста аст) , аммо конфигуратсияи нав мустақиман татбиқ карда намешавад. Барои истифодаи ҷангӣ, шумо бояд параметрро хориҷ кунед хушк_дав ё арзиши онро ба тағйир диҳед дуруц.

Вақте ки скрипт иҷро мешавад, Nornir гузоришҳои муфассалро ба консол мебарорад.

Дар поёни спойлер баромади мубориза дар ду роутери санҷишӣ мавҷуд аст:

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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Пинҳон кардани паролҳо дар ansible_vault

Дар аввали мақола ман каме аз ҳад гузаштам иҷрошаванда, аммо ин ҳама он қадар бад нест. Ман дар ҳақиқат онҳоро дӯст медорам толор монанди, ки барои пинҳон кардани маълумоти ҳассос аз чашмҳо пешбинӣ шудааст. Ва шояд бисёриҳо пай бурданд, ки мо ҳама логинҳо/паролҳоро барои ҳама роутерҳои ҷангӣ дар шакли кушода дар файл дорем. gorups.yaml. Зебо нест, албатта. Биёед ин маълумотро бо толор.

Биёед параметрҳоро аз group.yaml ба creds.yaml интиқол диҳем ва онро бо AES256 бо пароли 20-рақама рамзгузорӣ кунем:

$ 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

Ин хеле оддӣ аст. Ба мо таълим додан боқӣ мемонад Норнир-скрипт барои дарёфт ва татбиқи ин маълумот.
Барои ин, дар скрипти мо пас аз сатри оғозёбӣ nr = InitNornir (config_file =… рамзи зеринро илова кунед:

...
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
...

Албатта, vault.passwd набояд мисли мисоли ман дар паҳлӯи creds.yaml ҷойгир бошад. Аммо барои бозӣ хуб аст.

Ҳамааш ҳамин аст. Якчанд мақолаи дигар дар бораи омадани Cisco + Zabbix вуҷуд дорад, аммо ин каме дар бораи автоматизатсия нест. Ва дар ояндаи наздик ман ният дорам дар бораи RESTCONF дар Cisco нависам.

Манбаъ: will.com

Илова Эзоҳ