Nornir استعمال ڪندي نيٽ ورڪ ڊيوائس جي ترتيب واري عناصر جي خودڪار نسل ۽ ڀرڻ

Nornir استعمال ڪندي نيٽ ورڪ ڊيوائس جي ترتيب واري عناصر جي خودڪار نسل ۽ ڀرڻ

اي حبر!

تازو هڪ مضمون هتي پاپ اپ ڪيو ميڪروٽڪ ۽ لينڪس. معمول ۽ خودڪار جتي ساڳيو مسئلو فوسل ذريعي حل ڪيو ويو. ۽ جيتوڻيڪ اهو ڪم مڪمل طور تي عام آهي، ان جي باري ۾ ڪا به شيء ناهي Habré تي. مان پنهنجي سائيڪل کي معزز آئي ٽي ڪميونٽي کي پيش ڪرڻ جي جرئت ڪريان ٿو.

اهڙي ڪم لاءِ هي پهرين سائيڪل ناهي. پهريون اختيار ڪيترائي سال اڳ ۾ لاڳو ڪيو ويو ناھي نسخو 1.x.x. سائيڪل تمام گھٽ استعمال ڪئي وئي ۽ ان ڪري مسلسل زنگ لڳل. ان معنى ۾ ته اهو ڪم پاڻ ئي نه ٿو پيدا ٿئي جيترو اڪثر نسخن کي اپڊيٽ ڪيو ويندو آهي ناھي. ۽ هر دفعي توهان کي ڊرائيو ڪرڻ جي ضرورت آهي، زنجير بند ٿي وڃي ٿو يا ڦيٿي بند ٿي وڃي ٿي. جڏهن ته، پهريون حصو، ٺاهي ترتيب ڏيڻ، هميشه تمام واضح طور تي ڪم ڪري ٿو، خوش قسمت جنجا 2 انجڻ ڊگهي قائم آهي. پر ٻيو حصو - رولنگ آئوٽ ترتيب - عام طور تي حيران ٿي ويا. ۽ جيئن ته مون کي ريموٽ طور تي اڌ سؤ ڊوائيسز تائين ترتيب ڏيڻو آهي، جن مان ڪجهه هزارين ڪلوميٽر پري واقع آهن، هن اوزار کي استعمال ڪرڻ ٿورو بورنگ هو.

هتي مون کي اهو تسليم ڪرڻ گهرجي ته منهنجي غير يقيني صورتحال گهڻو ڪري منهنجي واقفيت جي گهٽتائي ۾ آهي ناھيان جي گهٽتائي جي ڀيٽ ۾. ۽ هي، رستي ۾، هڪ اهم نقطو آهي. ناھي هڪ مڪمل طور تي الڳ آهي، ان جي پنهنجي DSL (ڊومين جي مخصوص ٻولي) سان علم جو پنهنجو علائقو، جنهن کي اعتماد جي سطح تي برقرار رکڻ گهرجي. خير، اهو لمحو ناھي اهو ڪافي تيزيء سان ترقي ڪري رهيو آهي، ۽ پسمانده مطابقت لاء خاص خيال کان سواء، اهو اعتماد ۾ اضافو نٿو ڪري.

تنهن ڪري، گهڻو وقت اڳ نه سائيڪل جو ٻيو نسخو لاڳو ڪيو ويو. هن ڀيري سٿان، يا بلڪه هڪ فريم ورڪ ۾ لکيل آهي سٿان ۽ لاء سٿان نالو هيٺ نورنير

سو - نورنير هڪ microframework ۾ لکيل آهي سٿان ۽ لاء سٿان ۽ خودڪار لاء ٺهيل. ساڳيءَ طرح سان ڪيس ۾ ناھي، هتي مسئلا حل ڪرڻ لاءِ، قابل ڊيٽا تيار ڪرڻ جي ضرورت آهي، يعني. ميزبانن جي فهرست ۽ انهن جا پيرا ميٽرس، پر اسڪرپٽ هڪ الڳ DSL ۾ نه لکيا ويا آهن، پر ساڳئي طرح تمام پراڻي نه، پر تمام سٺو p[i|i]ton.

اچو ته ڏسو ته اهو هيٺ ڏنل زنده مثال استعمال ڪري رهيو آهي.

مون وٽ هڪ شاخ نيٽ ورڪ آهي جنهن سان سڄي ملڪ ۾ ڪيترن ئي درجنين آفيسون آهن. هر آفيس ۾ هڪ WAN روٽر آهي جيڪو مختلف آپريٽرن کان ڪيترن ئي ڪميونيڪيشن چينلز کي ختم ڪري ٿو. روٽنگ پروٽوڪول BGP آهي. وان روٽر ٻن قسمن ۾ اچن ٿا: سسڪو ISG يا Juniper SRX.

هاڻي ڪم: توهان کي برانچ نيٽ ورڪ جي سڀني وان روٽرز تي هڪ الڳ پورٽ تي وڊيو نگراني لاءِ وقف ڪيل سب نيٽ ترتيب ڏيڻ جي ضرورت آهي - هن سب نيٽ کي BGP ۾ اشتهار ڏيو - وقف ٿيل بندرگاهه جي رفتار جي حد کي ترتيب ڏيو.

پهرين، اسان کي ڪجهه ٽيمپليٽس تيار ڪرڻ جي ضرورت آهي، جن جي بنياد تي ترتيبون ٺاهيا ويندا الڳ الڳ سسڪو ۽ جونيپر لاء. اهو پڻ ضروري آهي ته هر نقطي ۽ ڪنيڪشن جي پيٽرولن لاء ڊيٽا تيار ڪرڻ، يعني. ساڳئي لسٽ گڏ ڪريو

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

ٽيمپليٽ، يقينا، پتلي هوا مان ٻاهر نه اچن. اهي بنيادي طور تي ڪم ڪندڙ ترتيبن جي وچ ۾ فرق آهن جيڪي مختلف ماڊلن جي ٻن مخصوص رستن تي ڪم کي حل ڪرڻ کان پوءِ هئا.

اسان جي ٽيمپليٽس مان اسان ڏسون ٿا ته مسئلو حل ڪرڻ لاءِ، اسان کي صرف جونيئر لاءِ ٻه پيرا ميٽرز ۽ سسڪو لاءِ 3 پيرا ميٽرز جي ضرورت آهي. هتي اهي آهن:

  • ifname
  • ipsuffix
  • asn

هاڻي اسان کي هر ڊوائيس لاء اهي پيٽرولر مقرر ڪرڻ جي ضرورت آهي، يعني. ساڳيو ڪم ڪريو شفقت.

لاء شفقت اسان سختي سان دستاويز جي پيروي ڪنداسين 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, گروپ (منهنجي صورت ۾ اهي لاگ ان/پاسورڊ آهن) ۾ group.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

ائين ئي ٿيو شفقت اسان جي ڪم لاء. شروعات جي دوران، انوینٽري فائلن مان پيٽرولر کي اعتراض ماڊل ڏانهن نقشو ڪيو ويو آهي Inventory Element.

خراب ڪندڙ جي هيٺان 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 ۾ ڊرائيور جي عمل تي) ، پر نئين تشڪيل سڌي طرح لاڳو نه ڪئي وئي آهي. جنگي استعمال لاء، توهان کي پيٽرولر کي هٽائڻ گهرجي dry_run يا ان جي قيمت کي تبديل ڪريو ڪوڙي.

جڏهن اسڪرپٽ تي عمل ڪيو ويندو آهي، 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. اهو خوبصورت ناهي، يقينا. اچو ته هن ڊيٽا جي حفاظت ڪريون وقفي.

اچو ته گروپس.yaml کان creds.yaml ۾ پيرا ميٽرز کي منتقل ڪريون، ۽ ان کي 256 عددي پاسورڊ سان AES20 سان انڪريپ ڪريو:

$ 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 جي اڳيان واقع نه هجڻ گهرجي جيئن منهنجي مثال ۾. پر اهو راند ڪرڻ لاء ٺيڪ آهي.

اهو سڀ ڪجهه هاڻي لاءِ آهي. سسڪو + زيبڪس اچڻ بابت ڪجھ وڌيڪ مضمون آھن، پر اھو ڪجھھ نه آھي آٽوميشن بابت. ۽ ويجهي مستقبل ۾ آئون سسڪو ۾ RESTCONF بابت لکڻ جو ارادو ڪريان ٿو.

جو ذريعو: www.habr.com

تبصرو شامل ڪريو