Posledně jsme si nastavili naší virtuální OpenSwitch leaf-spine topologii a vybudovali jsme BGP L3 fabric. Dnes si ukážeme více z životního cyklu s použitím Ansible – otestování sítě, vrácení odchylek do požadovaného stavu sítě a rolování změn.
Jste líní číst? Mrkněte na video:
Playbook pro testování sítě
Ansible můžete použít i pro testování, že síť funguje. Můžete například iniciovat ping z rlzných prvků na jiné prvky a zjišťovat, jestli prochází. Můžete kontrolovat směrovací tabulky a ujišťovat se, že v nich není nějaký problém (třeba chybějící default route). My si pro studijní účely zkusíme něco jednoduché, ale účinného – budeme analyzovat BGP peering. V zásadě chci pouze vytisknout pro každý prvek informaci o tom, jestli všichni jeho peerové jsou estabilished nebo ne (a pokud ne, dát mi o tom vědět). Určitě by se to dalo dělat mnohem chytřeji, ale pro pochopení konceptu to asi stačí.
Prohlédněte si test-fabric.yml Playbook:
--- - name: Configure hostname and interfaces hosts: all remote_user: root gather_facts: no vars_files: - vars/topology.yml - vars/connections.yml tasks: - name: Get BGP peering information connection: local ops_facts: provider: "{{ ops_rest_provider }}" endpoints: - "/system/vrfs/vrf_default/bgp_routers/{{item.0.as_number}}/bgp_neighbors/{{item.1.address}}" register: rest_result with_subelements: - "{{switches[inventory_hostname].ops_vrfs.0.bgp_routers}}" - "neighbors" ignore_errors: True - name: Prepare output set_fact: peer: "Peering to {{item.item.1.address}} is {{item.ansible_facts.endpoints.0.status.status.bgp_peer_state}}\n" register: peers with_items: "{{rest_result.results}}" - name: Transform output set_fact: peer_set="{{ peers.results | map(attribute='ansible_facts.peer') | list }}" - name: Report switches with failed neighbors fail: msg="{{item}}" when: "'Established' not in item" with_items: "{{peer_set}}"
V zásadě všechno se odehrálo v jednom jediném tasku jménem „Get BGP peering information“. To ostatní je jen způsob jak to v nějaké rozumně čitelné formě zobrazit a vyhodnocovat. Jak to děláme? Používá se Ansible modul ops_facts, který si umí šáhnout do konfigurační databáze OpenSwitch, která je v jeho samém srdci. Tam totiž nenajdete jen informace konfiguračního charakteru, ale také state jako je směrovací tabulka nebo právě stav BGP peeringu. Stačí se tedy podívat na správné místo a je to.
Spousťte testovací Playbook a podívejte se na výsledek.
$ ansible-playbook -i hosts test-fabric.yml PLAY [Configure hostname and interfaces] *************************************** TASK [Get BGP peering information] ********************************************* ok: [leaf1] => (item=({u'router_id': u'192.168.0.1', u'as_number': 65001, u'networks': [u'192.168.0.1/32']}, {u'remote_as': 65101, u'address': u'10.1.0.2'})) ok: [leaf2] => (item=({u'router_id': u'192.168.0.2', u'as_number': 65002, u'networks': [u'192.168.0.2/32']}, {u'remote_as': 65101, u'address': u'10.1.1.2'})) ok: [spine2] => (item=({u'router_id': u'192.168.1.2', u'as_number': 65102, u'networks': [u'192.168.1.2/32']}, {u'remote_as': 65001, u'address': u'10.2.0.1'})) ok: [leaf3] => (item=({u'router_id': u'192.168.0.3', u'as_number': 65003, u'networks': [u'192.168.0.3/32']}, {u'remote_as': 65101, u'address': u'10.1.2.2'})) ok: [spine1] => (item=({u'router_id': u'192.168.1.1', u'as_number': 65101, u'networks': [u'192.168.1.1/32']}, {u'remote_as': 65001, u'address': u'10.1.0.1'})) ok: [leaf2] => (item=({u'router_id': u'192.168.0.2', u'as_number': 65002, u'networks': [u'192.168.0.2/32']}, {u'remote_as': 65102, u'address': u'10.2.1.2'})) ok: [leaf1] => (item=({u'router_id': u'192.168.0.1', u'as_number': 65001, u'networks': [u'192.168.0.1/32']}, {u'remote_as': 65102, u'address': u'10.2.0.2'})) ok: [leaf3] => (item=({u'router_id': u'192.168.0.3', u'as_number': 65003, u'networks': [u'192.168.0.3/32']}, {u'remote_as': 65102, u'address': u'10.2.2.2'})) ok: [spine2] => (item=({u'router_id': u'192.168.1.2', u'as_number': 65102, u'networks': [u'192.168.1.2/32']}, {u'remote_as': 65002, u'address': u'10.2.1.1'})) ok: [spine1] => (item=({u'router_id': u'192.168.1.1', u'as_number': 65101, u'networks': [u'192.168.1.1/32']}, {u'remote_as': 65002, u'address': u'10.1.1.1'})) ok: [spine1] => (item=({u'router_id': u'192.168.1.1', u'as_number': 65101, u'networks': [u'192.168.1.1/32']}, {u'remote_as': 65003, u'address': u'10.1.2.1'})) ok: [spine1] => (item=({u'router_id': u'192.168.1.1', u'as_number': 65101, u'networks': [u'192.168.1.1/32']}, {u'remote_as': 65004, u'address': u'10.1.3.1'})) ok: [leaf4] => (item=({u'router_id': u'192.168.0.4', u'as_number': 65004, u'networks': [u'192.168.0.4/32']}, {u'remote_as': 65101, u'address': u'10.1.3.2'})) ok: [spine2] => (item=({u'router_id': u'192.168.1.2', u'as_number': 65102, u'networks': [u'192.168.1.2/32']}, {u'remote_as': 65003, u'address': u'10.2.2.1'})) ok: [leaf4] => (item=({u'router_id': u'192.168.0.4', u'as_number': 65004, u'networks': [u'192.168.0.4/32']}, {u'remote_as': 65102, u'address': u'10.2.3.2'})) ok: [spine2] => (item=({u'router_id': u'192.168.1.2', u'as_number': 65102, u'networks': [u'192.168.1.2/32']}, {u'remote_as': 65004, u'address': u'10.2.3.1'})) TASK [Prepare output] ********************************************************** ... TASK [Transform output] ******************************************************** ok: [spine2] ok: [leaf2] ok: [spine1] ok: [leaf3] ok: [leaf1] ok: [leaf4] TASK [Report switches with failed neighbors] *********************************** skipping: [spine1] => (item=Peering to 10.1.0.1 is Established ) skipping: [spine2] => (item=Peering to 10.2.0.1 is Established ) skipping: [spine2] => (item=Peering to 10.2.1.1 is Established ) skipping: [spine1] => (item=Peering to 10.1.1.1 is Established ) skipping: [spine2] => (item=Peering to 10.2.2.1 is Established ) skipping: [spine2] => (item=Peering to 10.2.3.1 is Established ) skipping: [spine1] => (item=Peering to 10.1.2.1 is Established ) skipping: [leaf1] => (item=Peering to 10.1.0.2 is Established ) skipping: [leaf1] => (item=Peering to 10.2.0.2 is Established ) skipping: [spine1] => (item=Peering to 10.1.3.1 is Established ) skipping: [leaf2] => (item=Peering to 10.1.1.2 is Established ) skipping: [leaf2] => (item=Peering to 10.2.1.2 is Established ) skipping: [leaf3] => (item=Peering to 10.1.2.2 is Established ) skipping: [leaf3] => (item=Peering to 10.2.2.2 is Established ) skipping: [leaf4] => (item=Peering to 10.1.3.2 is Established ) skipping: [leaf4] => (item=Peering to 10.2.3.2 is Established ) PLAY RECAP ********************************************************************* leaf1 : ok=3 changed=0 unreachable=0 failed=0 leaf2 : ok=3 changed=0 unreachable=0 failed=0 leaf3 : ok=3 changed=0 unreachable=0 failed=0 leaf4 : ok=3 changed=0 unreachable=0 failed=0 spine1 : ok=3 changed=0 unreachable=0 failed=0 spine2 : ok=3 changed=0 unreachable=0 failed=0
Všechno je v pořádku.
Životní cyklus sítě
Pojďme teď do sítě neobratně zasáhnout – připojíme se do jednoho z prvků a něco zkazíme, konkrétně nastavíme špatně AS number u neighbor.
$ docker exec -ti leaf1 vtysh leaf1# conf t leaf1(config)# router bgp 65001 leaf1(config-router)# neighbor 10.1.0.2 remote-as 65999 leaf1(config-router)# end leaf1# exit
Otestujme znovu síť.
$ ansible-playbook -i hosts test-fabric.yml PLAY [Configure hostname and interfaces] *************************************** TASK [Report switches with failed neighbors] *********************************** skipping: [spine2] => (item=Peering to 10.2.0.1 is Established ) skipping: [spine2] => (item=Peering to 10.2.1.1 is Established ) skipping: [leaf2] => (item=Peering to 10.1.1.2 is Established ) skipping: [spine2] => (item=Peering to 10.2.2.1 is Established ) skipping: [leaf2] => (item=Peering to 10.2.1.2 is Established ) skipping: [spine2] => (item=Peering to 10.2.3.1 is Established ) skipping: [leaf3] => (item=Peering to 10.1.2.2 is Established ) failed: [spine1] (item=Peering to 10.1.0.1 is OpenSent ) => {"failed": true, "item": "Peering to 10.1.0.1 is OpenSent\n", "msg": "Peering to 10.1.0.1 is OpenSent\n"} skipping: [leaf3] => (item=Peering to 10.2.2.2 is Established ) skipping: [spine1] => (item=Peering to 10.1.1.1 is Established ) skipping: [spine1] => (item=Peering to 10.1.2.1 is Established ) skipping: [leaf4] => (item=Peering to 10.1.3.2 is Established ) skipping: [spine1] => (item=Peering to 10.1.3.1 is Established ) failed: [leaf1] (item=Peering to 10.1.0.2 is OpenSent ) => {"failed": true, "item": "Peering to 10.1.0.2 is OpenSent\n", "msg": "Peering to 10.1.0.2 is OpenSent\n"} skipping: [leaf4] => (item=Peering to 10.2.3.2 is Established ) skipping: [leaf1] => (item=Peering to 10.2.0.2 is Established ) NO MORE HOSTS LEFT ************************************************************* to retry, use: --limit @test-fabric.retry PLAY RECAP ********************************************************************* leaf1 : ok=3 changed=0 unreachable=0 failed=1 leaf2 : ok=3 changed=0 unreachable=0 failed=0 leaf3 : ok=3 changed=0 unreachable=0 failed=0 leaf4 : ok=3 changed=0 unreachable=0 failed=0 spine1 : ok=3 changed=0 unreachable=0 failed=1 spine2 : ok=3 changed=0 unreachable=0 failed=0
Hmm, to není dobré. Spusťme tedy znova Playbook na vybudování fabric. Ansible má vlastnost idempotency, takže ho může spouštět kolikrát chceme bez ohledu na to, jaký je aktuální stav sítě. Ansible inteligentně zajistí dosažení požadovaného stavu.
Šetřeme místo a ukažme si jen příkaz na spuštění Playbooku.
$ ansible-playbook -i hosts build-fabric.yml
Síť se nám opravila. Můžete znovu zkusit test-fabric.yml Playbook, dopadne dobře (jen BGPku dejte nějaký čas).