
مرحبا يا هبر!
في الآونة الأخيرة ، تراجع مقال لمكروتيك ولينكس هنا . الروتينية والأتمتة حيث تم حل مشكلة مماثلة بالوسائل الأحفورية. على الرغم من أن المهمة نموذجية تمامًا ، إلا أنه لا يوجد شيء مماثل في حبري حول هذا الموضوع. أجرؤ على تقديم دراجتي لمجتمع تكنولوجيا المعلومات ذي السمعة الطيبة.
ليست هذه هي الدراجة الأولى لهذه المهمة. تم تنفيذ الخيار الأول قبل عدة سنوات على الإصدار ansible 1.x.x. نادرا ما كانت تستخدم الدراجة ، وبالتالي الصدأ باستمرار. بمعنى أن المهمة نفسها لا تحدث في كثير من الأحيان مثل الإصدارات المحدثة من ansible . وفي كل مرة تحتاج إلى الذهاب ، سوف تسقط السلسلة ، وسوف تسقط العجلة. ومع ذلك ، فإن الجزء الأول ، وهو تكوين التكوينات - يعمل دائمًا بشكل واضح للغاية ، حيث تم إنشاء المحرك لفترة طويلة من أجل jinja2 . لكن الجزء الثاني - طرح التكوينات ، كقاعدة جلب المفاجآت. وبما أنه لا بد لي من طرح التكوين عن بعد على أرضية مئات الأجهزة ، بعضها على بعد آلاف الكيلومترات ، فقد كان استخدام هذه الأداة أمرًا مزعجًا بعض الشيء.
هنا يجب أن أعترف أن انعدام الأمن لدي ، يكمن في عدم وجود إلفة مع أنسبل لي أكثر من عيوبه. وهذه ، بالمناسبة ، هي نقطة مهمة. ansible هو مجال معرفة منفصل تمامًا عن طريق DSL (لغة خاصة بالمجال) ، والتي يجب الحفاظ عليها بمستوى موثوق. حسنًا ، اللحظة التي يتطور فيها ansible بسرعة كبيرة ، ودون أي اعتبار خاص للتوافق مع الإصدارات السابقة ، لا تضيف الثقة.
لذلك ، منذ وقت ليس ببعيد ، تم تنفيذ الإصدار الثاني من الدراجة. هذه المرة في الثعبان ، أو بالأحرى ، في إطار مكتوب في الثعبان ولثعبان يسمى نورنير
So - Nornir عبارة عن عمل مصغر مكتوب بلغة الثعبان ولثعبان المعدة للتشغيل الآلي. كما هو الحال في ansible ، لحل المشكلات ، يلزم إعداد البيانات المختصة هنا ، أي جرد المضيفين ومعلماتهم ، ولكن لا يتم كتابة البرامج النصية على DSL منفصل ، ولكن كل شيء على نفس نغمة [و | آه] ليست قديمة جدًا ، ولكن جيدة جدًا.
دعونا نلقي نظرة على ما هو عليه في المثال الحي التالي.
لديّ شبكة فروع لها عشرات المكاتب في جميع أنحاء البلاد. في كل مكتب ، يوجد جهاز توجيه 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 كافية. ها هم:
نحتاج الآن إلى تعيين هذه المعلمات لكل جهاز ، أي تفعل نفس المخزون .
بالنسبة للمخزون ، سنتبع بوضوح وثائق تهيئة 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 ، المجموعة (في حالتي هذه أسماء المستخدمين / كلمات المرور) في groups.yaml ، ولن نحدد أي شيء في defaults.yaml ، لكن يجب عليك إدخال ثلاثة سلبيات هناك - للإشارة إلى أن هذا ملف 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
وهنا المجموعات. 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):
لاحظ dry_run = True المعلمة في سطر التهيئة للكائن nr .
هنا ، تمامًا كما هو الحال في ansible ، يتم تنفيذ التشغيل التجريبي الذي يتم من خلاله إجراء الاتصال بالموجه ، وإعداد التهيئة المتغيرة ، والذي يتم التحقق من صحته من قبل الجهاز (لكن هذا ليس دقيقًا ؛ هذا يعتمد على دعم الجهاز وتنفيذ برنامج التشغيل في NAPALM) ، لكن التكوين الجديد لا يتم تطبيقه مباشرةً . للاستخدام القتالي ، يجب إزالة المعلمة dry_run أو تغيير قيمتها إلى False .
عند تشغيل البرنامج النصي ، يعرض 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
في بداية المقال ، واجهت بعض الشيء ، لكن ليس كل شيء سيء للغاية هناك. أحب حقًا قبوهم المصمم لإخفاء المعلومات الحساسة عن الأنظار. وربما لاحظ الكثيرون أن لدينا جميع أسماء المستخدمين / كلمات المرور لجميع أجهزة توجيه المعركة تتألق في النموذج المفتوح في ملف groups.yaml . انها قبيحة ، بالطبع. دعونا حماية هذه البيانات مع قبو .
ننقل المعلمات من groups.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
بسيط جدا يبقى لتعليم السيناريو Nornir لدينا للحصول على واستخدام هذه البيانات.
للقيام بذلك ، في البرنامج النصي لدينا ، بعد سطر التهيئة nr = InitNornir (config_file = ... أضف الكود التالي:
... nr = InitNornir(config_file="config.yaml", dry_run=True)
بالطبع ، لا ينبغي أن يقع vault.passwd بجوار creds.yaml كما في المثال الخاص بي. ولكن للعب سوف تفعل.
هذا كل شيء الآن. تم طرح مقالتين حول Cisco + Zabbix ، لكن هذا ليس قليلاً عن الأتمتة. وفي المستقبل القريب ، أخطط للكتابة عن RESTCONF في Cisco.