ʻO ka hana maʻamau a me ka hoʻopiha ʻana i nā mea hoʻonohonoho hoʻonohonoho pūnaewele me Nornir

ʻO ka hana maʻamau a me ka hoʻopiha ʻana i nā mea hoʻonohonoho hoʻonohonoho pūnaewele me Nornir

E Habr!

I kēia manawa, ua puka mai kahi ʻatikala ma aneʻi ʻO Mikrotik a me Linux. ʻO ka hana maʻamau a me ka automation kahi i hoʻoponopono ʻia ai kekahi pilikia like me ka hoʻohana ʻana i nā ala fossil. A ʻoiai he ʻano maʻamau ka hana, ʻaʻohe mea like me ia ma Habré. ʻAʻa wau e hāʻawi i kaʻu paikikala i ke kaiāulu IT mahalo ʻia.

ʻAʻole kēia ka kaʻa kaʻa mua no ia hana. Ua hoʻokō ʻia ka koho mua i kekahi mau makahiki i hala aku nei Ansible mana 1.x.x. Kakaʻikahi ka hoʻohana ʻana i ke kaʻa kaʻa a no laila ʻeleʻele mau. I ka manaʻo ʻaʻole e kū mai ka hana ponoʻī e like me ka hoʻonui ʻia ʻana o nā mana Ansible. A i kēlā me kēia manawa e pono ai ʻoe e hoʻokele, hāʻule ke kaulahao a hāʻule paha ka huila. Eia nō naʻe, ʻo ka hapa mua, ka hoʻokumu ʻana i nā configs, e hana mau ana me ka maopopo loa, pōmaikaʻi jinja2 Ua hoʻokumu lōʻihi ka ʻenekini. Akā ʻo ka ʻāpana ʻelua - ʻo ka ʻōwili ʻana i nā configs - lawe pinepine mai i nā mea kupanaha. A no ka mea pono iaʻu e ʻōwili mamao i ka config i ka hapalua haneli mau mea hana, aia kekahi o ia mau tausani mau kilomita ka mamao, ʻo ka hoʻohana ʻana i kēia hāmeʻa he mea ʻoluʻolu iki.

Ma ʻaneʻi, pono iaʻu e ʻae aku ʻo koʻu kānalua ʻole aia i koʻu ʻike ʻole ʻana Ansiblema mua o kona mau hemahema. A ʻo kēia, ma ke ala, he mea nui. Ansible he kaʻawale loa ia, ʻo kāna wahi ʻike ponoʻī me kāna DSL (Domain Specific Language), pono e mālama ʻia ma kahi pae hilinaʻi. ʻAe, kēlā manawa kēlā Ansible Ke ulu wikiwiki nei ia, a me ka nānā ʻole ʻana i ka hoʻohālikelike hope, ʻaʻole ia e hoʻohui i ka hilinaʻi.

No laila, ʻaʻole i liʻuliʻu i hala aku nei ua hoʻokō ʻia kahi ʻano lua o ke kaʻa. ʻO kēia manawa ma python, a i ʻole ma kahi papa hana i kākau ʻia i loko python a mo python malalo o ka inoa Nornir

No laila - Nornir he microframework i kākau ʻia i loko python a mo python a hoʻolālā ʻia no ka automation. E like me ka hihia me Ansible, e hoʻoponopono i nā pilikia ma ʻaneʻi, pono ka hoʻomākaukau ʻana i ka ʻikepili mākaukau, ʻo ia. papa helu o nā pūʻali koa a me kā lākou mau palena, akā, ʻaʻole i kākau ʻia nā palapala i kahi DSL ʻokoʻa, akā i loko o ka like ʻaʻole kahiko loa, akā maikaʻi loa p[i|i]ton.

E nānā kākou i ka mea e hoʻohana nei i kēia hiʻohiʻona ola.

He lālā lālā kaʻu me nā keʻena he ʻumi mau keʻena ma ka ʻāina. Loaʻa i kēlā me kēia keʻena kahi alalai WAN e hoʻopau i nā ala kamaʻilio mai nā mea hana like ʻole. ʻO ka protocol routing ka BGP. Hele mai nā mea ala WAN i ʻelua ʻano: Cisco ISG a i ʻole Juniper SRX.

I kēia manawa ka hana: pono ʻoe e hoʻonohonoho i kahi subnet i hoʻolaʻa ʻia no ka Video Surveillance ma kahi awa ʻokoʻa ma nā mea hoʻokele WAN āpau o ka pūnaewele lālā - hoʻolaha i kēia subnet ma BGP - hoʻonohonoho i ka palena wikiwiki o ke awa i hoʻolaʻa ʻia.

ʻO ka mea mua, pono mākou e hoʻomākaukau i ʻelua mau hiʻohiʻona, ma ke kumu o ka hoʻonohonoho ʻana e hoʻokaʻawale ʻia no Cisco a me Juniper. Pono nō hoʻi e hoʻomākaukau i ka ʻikepili no kēlā me kēia kiko a me nā palena pili, ʻo ia hoʻi. e ohi i ka waihona like

Mākaukau no 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

Papahana no 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

ʻO nā templates, ʻoiaʻiʻo, ʻaʻole i puka mai i waho o ka ea lahilahi. He ʻokoʻa loa kēia ma waena o nā hoʻonohonoho hana i hana ʻia a ma hope o ka hoʻoponopono ʻana i ka hana ma nā mea ala ala ʻelua o nā hiʻohiʻona like ʻole.

Mai kā mākou mau hiʻohiʻona ʻike mākou e hoʻoponopono i ka pilikia, pono mākou i ʻelua mau palena no Juniper a me 3 mau palena no Cisco. eia lakou:

  • ifname
  • ipssuffix
  • asn

I kēia manawa pono mākou e hoʻonohonoho i kēia mau ʻāpana no kēlā me kēia hāmeʻa, i.e. e hana like mo'olako.

no ka mea, mo'olako E hahai pono mākou i nā palapala Hoʻomaka ʻia ʻo Nornir

ʻo ia hoʻi, e hana kāua i ka iwi waihona like:

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

ʻO ka faila config.yaml ka faila hoʻonohonoho nornir maʻamau

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

E hōʻike mākou i nā ʻāpana nui i ka faila hosts.yaml, pūʻulu (i koʻu hihia he mau logins / password kēia) ma pūʻulu.yaml, a in defaults.yaml ʻAʻole mākou e hōʻike i kekahi mea, akā pono ʻoe e komo i ʻekolu mau minus ma laila - e hōʻike ana ʻo ia ʻehu Ua nele nae ka waihona.

ʻO kēia ke ʻano o 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

A eia nā hui.yaml:

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

juniper:
    platform: junos
    username: admin2
    password: juniper2

ʻO kēia ka mea i hana ʻia mo'olako no kā mākou hana. I ka wā o ka hoʻomaka ʻana, hoʻopaʻa ʻia nā ʻāpana mai nā faila waihona i ke kumu hoʻohālike InventoryElement.

Aia ma lalo iho o ka mea hao ke kiʻi o ka InventoryElement model

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

Hiki i kēia kŘkohu ke nānā iki i ka huikau, ʻoi aku ka mua. I mea e noʻonoʻo ai, ke ʻano pāʻani i loko 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'

A ʻo ka hope, e neʻe kākou i ka palapala ponoʻī. ʻAʻohe aʻu mea e haʻaheo loa ai ma ʻaneʻi. Ua lawe wale au i kahi laʻana i hoʻomākaukau ʻia mai kumu aʻo a hoʻohana ʻaneʻane hoʻololi ʻole. Penei ke ano o ka palapala hana i pau:

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)

E hoʻolohe i ka ʻāpana dry_run=ʻOiaʻiʻo ma ka hoʻomaka ʻana o ka mea laina nr.
Eia ka like me ma Ansible ua hoʻokō ʻia kahi holo hoʻāʻo kahi i hana ʻia ai kahi pilina me ke alalai, ua hoʻomākaukau ʻia kahi hoʻonohonoho hoʻololi hou, a laila hōʻoia ʻia e ka hāmeʻa (akā ʻaʻole maopopo kēia; pili ia i ke kākoʻo o ka hāmeʻa a me ka hoʻokō ʻana o ka mea hoʻokele ma NAPALM) , akā, ʻaʻole pili pono ka hoʻonohonoho hou. No ka hoʻohana kaua, pono ʻoe e wehe i ke ʻano holo_maloo a hoololi paha i kona waiwai i wahahee.

Ke hoʻokō ʻia ka palapala, hoʻopuka ʻo Nornir i nā lāʻau kikoʻī i ka console.

Aia ma lalo o ka mea hao ka hopena o ka holo kaua ma nā mea hoʻokele hoʻāʻo ʻelua:

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

Hūnā ʻōlelo huna ma ansible_vault

I ka hoʻomaka ʻana o ka ʻatikala ua hele au i kahi liʻiliʻi Ansible, ʻaʻole naʻe he ʻino loa. Makemake au iā lākou ʻoiai like, i hoʻolālā ʻia e hūnā i ka ʻike koʻikoʻi mai ka ʻike ʻana. A ua ʻike paha ka poʻe he nui iā mākou nā logins / ʻōlelo huna no nā mea hoʻokele kaua āpau e ʻālohilohi i ka puka ākea i kahi faila. gorups.yaml. ʻAʻole nani, ʻoiaʻiʻo. E mālama kākou i kēia ʻikepili me ʻoiai.

E hoʻololi i nā ʻāpana mai groups.yaml i creds.yaml, a hoʻopili iā ia me AES256 me kahi huaʻōlelo 20-helu:

$ 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

He maʻalahi kēlā. Ke koe nei ke aʻo ʻana i kā mākou Nornir-script e kiʻi a hoʻohana i kēia ʻikepili.
No ka hana ʻana i kēia, i kā mākou palapala ma hope o ka laina hoʻomaka nr = InitNornir(config_file=… hoʻohui i kēia code:

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

ʻOiaʻiʻo, ʻaʻole pono e loaʻa ka vault.passwd ma kahi o creds.yaml e like me kaʻu laʻana. Akā maikaʻi no ka pāʻani.

ʻO ia wale nō i kēia manawa. Aia kekahi mau ʻatikala hou aʻe e pili ana iā Cisco + Zabbix e hele mai ana, akā ʻaʻole kēia e pili ana i ka automation. A i ka wā e hiki mai ana, hoʻolālā wau e kākau e pili ana iā RESTCONF ma Cisco.

Source: www.habr.com

Pākuʻi i ka manaʻo hoʻopuka