Π₯Π΅ΠΉ Π₯Π°Π±Ρ!
ΠΠ°ΡΠΊΠΎΡΠΎ ΡΡΠΊ ΡΠ΅ ΠΏΠΎΡΠ²ΠΈ Π΅Π΄Π½Π° ΡΡΠ°ΡΠΈΡ
Π’ΠΎΠ²Π° Π½Π΅ Π΅ ΠΏΡΡΠ²ΠΈΡΡ ΠΌΠΎΡΠΎΡ Π·Π° ΠΏΠΎΠ΄ΠΎΠ±Π½Π° Π·Π°Π΄Π°ΡΠ°. ΠΡΡΠ²ΠΈΡΡ Π²Π°ΡΠΈΠ°Π½Ρ Π±Π΅ΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ ΠΏΡΠ΅Π΄ΠΈ Π½ΡΠΊΠΎΠ»ΠΊΠΎ Π³ΠΎΠ΄ΠΈΠ½ΠΈ Π² ansible Π²Π΅ΡΡΠΈΡ 1.x.x. ΠΠ΅Π»ΠΎΡΠΈΠΏΠ΅Π΄ΡΡ Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½ ΡΡΠ΄ΠΊΠΎ ΠΈ ΠΏΠΎΡΠ°Π΄ΠΈ ΡΠΎΠ²Π° ΠΏΠΎΡΡΠΎΡΠ½Π½ΠΎ ΡΡΠΆΠ΄ΡΡΠ²Π°Π». Π ΡΠΌΠΈΡΡΠ», ΡΠ΅ ΡΠ°ΠΌΠ°ΡΠ° Π·Π°Π΄Π°ΡΠ° Π½Π΅ Π²ΡΠ·Π½ΠΈΠΊΠ²Π° ΡΠΎΠ»ΠΊΠΎΠ²Π° ΡΠ΅ΡΡΠΎ, ΠΊΠΎΠ»ΠΊΠΎΡΠΎ Π²Π΅ΡΡΠΈΠΈΡΠ΅ ΡΠ΅ Π°ΠΊΡΡΠ°Π»ΠΈΠ·ΠΈΡΠ°Ρ ansible. Π Π²ΡΠ΅ΠΊΠΈ ΠΏΡΡ, ΠΊΠΎΠ³Π°ΡΠΎ ΡΡΡΠ±Π²Π° Π΄Π° ΠΊΠ°ΡΠ°ΡΠ΅, Π²Π΅ΡΠΈΠ³Π°ΡΠ° ΠΏΠ°Π΄Π° ΠΈΠ»ΠΈ ΠΊΠΎΠ»Π΅Π»ΠΎΡΠΎ ΠΏΠ°Π΄Π°. ΠΡΠΏΡΠ΅ΠΊΠΈ ΡΠΎΠ²Π°, ΠΏΡΡΠ²Π°ΡΠ° ΡΠ°ΡΡ, Π³Π΅Π½Π΅ΡΠΈΡΠ°ΡΠ° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ, Π²ΠΈΠ½Π°Π³ΠΈ ΡΠ°Π±ΠΎΡΠΈ ΠΌΠ½ΠΎΠ³ΠΎ ΡΡΠ½ΠΎ, Π·Π° ΡΠ°ΡΡΠΈΠ΅ Π΄ΠΆΠΈΠ½Π΄ΠΆΠ°2 ΠΠ²ΠΈΠ³Π°ΡΠ΅Π»ΡΡ Π΅ ΠΎΡΠ΄Π°Π²Π½Π° ΠΌΠΎΠ½ΡΠΈΡΠ°Π½. ΠΠΎ Π²ΡΠΎΡΠ°ΡΠ° ΡΠ°ΡΡ - ΠΏΡΡΠΊΠ°Π½Π΅ΡΠΎ Π½Π° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ - ΠΎΠ±ΠΈΠΊΠ½ΠΎΠ²Π΅Π½ΠΎ Π½ΠΎΡΠ΅ΡΠ΅ ΠΈΠ·Π½Π΅Π½Π°Π΄ΠΈ. Π ΡΡΠΉ ΠΊΠ°ΡΠΎ ΡΡΡΠ±Π²Π° Π΄Π° ΠΏΡΡΠ½Π° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡΡΠ° Π΄ΠΈΡΡΠ°Π½ΡΠΈΠΎΠ½Π½ΠΎ Π½Π° ΠΏΠΎΠ»ΠΎΠ²ΠΈΠ½ ΡΡΠΎ ΡΡΡΡΠΎΠΉΡΡΠ²Π°, Π½ΡΠΊΠΎΠΈ ΠΎΡ ΠΊΠΎΠΈΡΠΎ ΡΠ° ΡΠ°Π·ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈ Π½Π° Ρ ΠΈΠ»ΡΠ΄ΠΈ ΠΊΠΈΠ»ΠΎΠΌΠ΅ΡΡΠΈ, ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅ΡΠΎ Π½Π° ΡΠΎΠ·ΠΈ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½Ρ Π±Π΅ΡΠ΅ ΠΌΠ°Π»ΠΊΠΎ ΡΠΊΡΡΠ½ΠΎ.
Π’ΡΠΊ ΡΡΡΠ±Π²Π° Π΄Π° ΠΏΡΠΈΠ·Π½Π°Ρ, ΡΠ΅ ΠΌΠΎΡΡΠ° Π½Π΅ΡΠΈΠ³ΡΡΠ½ΠΎΡΡ Π½Π°ΠΉ-Π²Π΅ΡΠΎΡΡΠ½ΠΎ ΡΠ΅ ΠΊΡΠΈΠ΅ Π² Π»ΠΈΠΏΡΠ°ΡΠ° ΠΌΠΈ Π½Π° Π·Π°ΠΏΠΎΠ·Π½Π°Π½ΡΡΠ²Π° Ρ ansibleΠΎΡΠΊΠΎΠ»ΠΊΠΎΡΠΎ Π² ΡΠ²ΠΎΠΈΡΠ΅ Π½Π΅Π΄ΠΎΡΡΠ°ΡΡΡΠΈ. Π ΡΠΎΠ²Π°, ΠΌΠ΅ΠΆΠ΄Ρ Π΄ΡΡΠ³ΠΎΡΠΎ, Π΅ Π²Π°ΠΆΠ΅Π½ ΠΌΠΎΠΌΠ΅Π½Ρ. ansible Π΅ Π½Π°ΠΏΡΠ»Π½ΠΎ ΠΎΡΠ΄Π΅Π»Π½Π°, ΡΠΎΠ±ΡΡΠ²Π΅Π½Π° ΠΎΠ±Π»Π°ΡΡ Π½Π° ΠΏΠΎΠ·Π½Π°Π½ΠΈΠ΅ ΡΡΡ ΡΠΎΠ±ΡΡΠ²Π΅Π½ DSL (ΡΠΏΠ΅ΡΠΈΡΠΈΡΠ΅Π½ Π·Π° Π΄ΠΎΠΌΠ΅ΠΉΠ½ Π΅Π·ΠΈΠΊ), ΠΊΠΎΠΉΡΠΎ ΡΡΡΠ±Π²Π° Π΄Π° ΡΠ΅ ΠΏΠΎΠ΄Π΄ΡΡΠΆΠ° Π½Π° ΡΠ²Π΅ΡΠ΅Π½ΠΎ Π½ΠΈΠ²ΠΎ. Π, ΡΠΎΠ·ΠΈ ΠΌΠΎΠΌΠ΅Π½Ρ, ΠΊΠΎΠΉΡΠΎ ansible Π Π°Π·Π²ΠΈΠ²Π° ΡΠ΅ Π΄ΠΎΡΡΠ° Π±ΡΡΠ·ΠΎ ΠΈ Π±Π΅Π· ΡΠΏΠ΅ΡΠΈΠ°Π»Π½ΠΎ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ Π½Π° ΠΎΠ±ΡΠ°ΡΠ½Π°ΡΠ° ΡΡΠ²ΠΌΠ΅ΡΡΠΈΠΌΠΎΡΡ Π½Π΅ Π΄ΠΎΠ±Π°Π²Ρ ΡΠ²Π΅ΡΠ΅Π½ΠΎΡΡ.
Π‘Π»Π΅Π΄ΠΎΠ²Π°ΡΠ΅Π»Π½ΠΎ Π½Π΅ ΡΠΎΠ»ΠΊΠΎΠ²Π° ΠΎΡΠ΄Π°Π²Π½Π° Π±Π΅ΡΠ΅ ΡΠ΅Π°Π»ΠΈΠ·ΠΈΡΠ°Π½Π° Π²ΡΠΎΡΠ° Π²Π΅ΡΡΠΈΡ Π½Π° Π²Π΅Π»ΠΎΡΠΈΠΏΠ΅Π΄Π°. Π’ΠΎΠ·ΠΈ ΠΏΡΡ Π½Π° ΠΏΠΈΡΠΎΠ½, ΠΈΠ»ΠΈ ΠΏΠΎ-ΡΠΊΠΎΡΠΎ Π½Π° ΡΠ°ΠΌΠΊΠ°, Π½Π°ΠΏΠΈΡΠ°Π½Π° Π² ΠΏΠΈΡΠΎΠ½ ΠΈ Π·Π° ΠΏΠΈΡΠΎΠ½ Π½Π°ΡΠΈΡΠ°
Π’Π°ΠΊΠ° - ΠΠΎΡΠ½ΠΈΡ Π΅ ΠΌΠΈΠΊΡΠΎΡΠ°ΠΌΠΊΠ°, Π½Π°ΠΏΠΈΡΠ°Π½Π° Π½Π° ΠΏΠΈΡΠΎΠ½ ΠΈ Π·Π° ΠΏΠΈΡΠΎΠ½ ΠΈ ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ΅Π½ΠΈ Π·Π° Π°Π²ΡΠΎΠΌΠ°ΡΠΈΠ·Π°ΡΠΈΡ. Π‘ΡΡΠΎΡΠΎ ΠΊΠ°ΡΠΎ Π² ΡΠ»ΡΡΠ°Ρ Ρ ansible, Π·Π° ΡΠ΅ΡΠ°Π²Π°Π½Π΅ Π½Π° ΠΏΡΠΎΠ±Π»Π΅ΠΌΠΈ ΡΡΠΊ Π΅ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠ° ΠΊΠΎΠΌΠΏΠ΅ΡΠ΅Π½ΡΠ½Π° ΠΏΠΎΠ΄Π³ΠΎΡΠΎΠ²ΠΊΠ° Π½Π° Π΄Π°Π½Π½ΠΈ, Ρ.Π΅. ΠΎΠΏΠΈΡ Π½Π° Ρ ΠΎΡΡΠΎΠ²Π΅ ΠΈ ΡΠ΅Ρ Π½ΠΈΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΈ, Π½ΠΎ ΡΠΊΡΠΈΠΏΡΠΎΠ²Π΅ΡΠ΅ Π½Π΅ ΡΠ° Π½Π°ΠΏΠΈΡΠ°Π½ΠΈ Π² ΠΎΡΠ΄Π΅Π»Π΅Π½ 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
Π¨Π°Π±Π»ΠΎΠ½ Π·Π° 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
Π¨Π°Π±Π»ΠΎΠ½ΠΈΡΠ΅, ΡΠ°Π·Π±ΠΈΡΠ° ΡΠ΅, Π½Π΅ ΠΈΠ·Π»ΠΈΠ·Π°Ρ ΠΎΡ Π½ΠΈΡΠΎΡΠΎ. Π’ΠΎΠ²Π° ΡΠ° ΠΏΠΎ ΡΡΡΠ΅ΡΡΠ²ΠΎ ΡΠ°Π·Π»ΠΈΠΊΠΈ ΠΌΠ΅ΠΆΠ΄Ρ ΡΠ°Π±ΠΎΡΠ½ΠΈΡΠ΅ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ, ΠΊΠΎΠΈΡΠΎ Π±ΡΡ Π° ΠΈ Π±ΡΡ Π° ΡΠ»Π΅Π΄ ΡΠ΅ΡΠ°Π²Π°Π½Π΅ΡΠΎ Π½Π° Π·Π°Π΄Π°ΡΠ°ΡΠ° Π½Π° Π΄Π²Π° ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΠΈ ΡΡΡΠ΅ΡΠ° ΠΎΡ ΡΠ°Π·Π»ΠΈΡΠ½ΠΈ ΠΌΠΎΠ΄Π΅Π»ΠΈ.
ΠΡ Π½Π°ΡΠΈΡΠ΅ ΡΠ°Π±Π»ΠΎΠ½ΠΈ Π²ΠΈΠΆΠ΄Π°ΠΌΠ΅, ΡΠ΅ Π·Π° Π΄Π° ΡΠ°Π·ΡΠ΅ΡΠΈΠΌ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ°, ΠΈΠΌΠ°ΠΌΠ΅ Π½ΡΠΆΠ΄Π° ΡΠ°ΠΌΠΎ ΠΎΡ Π΄Π²Π° ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡΠ° Π·Π° Juniper ΠΈ 3 ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡΠ° Π·Π° Cisco. ΠΡΠΎ Π³ΠΈ ΠΈ ΡΡΡ :
- ifname
- ΠΈΠΏΡΡΡΠΈΠΊΡ
- Π°ΡΠ½
Π‘Π΅Π³Π° ΡΡΡΠ±Π²Π° Π΄Π° Π·Π°Π΄Π°Π΄Π΅ΠΌ ΡΠ΅Π·ΠΈ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΈ Π·Π° Π²ΡΡΠΊΠΎ ΡΡΡΡΠΎΠΉΡΡΠ²ΠΎ, Ρ.Π΅. Π½Π°ΠΏΡΠ°Π²ΠΈ ΡΡΡΠΎΡΠΎ ΠΎΠΏΠΈΡ.
ΠΠ° ΠΎΠΏΠΈΡ Π‘ΡΡΠΈΠΊΡΠ½ΠΎ ΡΠ΅ ΡΠΏΠ°Π·Π²Π°ΠΌΠ΅ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡΡΠ°
ΡΠΎΠ΅ΡΡ, Π½Π΅ΠΊΠ° ΡΡΠ·Π΄Π°Π΄Π΅ΠΌ ΡΡΡΠΈΡ ΡΠ°ΠΉΠ»ΠΎΠ² ΡΠΊΠ΅Π»Π΅Ρ:
.
βββ 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, Π³ΡΡΠΏΠ° (Π² ΠΌΠΎΡ ΡΠ»ΡΡΠ°ΠΉ ΡΠΎΠ²Π° ΡΠ° ΠΈΠΌΠ΅Π½Π°/ΠΏΠ°ΡΠΎΠ»ΠΈ) Π² Π³ΡΡΠΏΠΈ.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
Π Π΅ΡΠΎ ΠΈ groups.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.
Π’ΡΠΊ ΡΡΡΠΎΡΠΎ ΠΊΠ°ΡΠΎ Π² ansible ΡΠ΅Π°Π»ΠΈΠ·ΠΈΡΠ°Π½ΠΎ Π΅ ΡΠ΅ΡΡΠΎΠ²ΠΎ ΠΈΠ·ΠΏΡΠ»Π½Π΅Π½ΠΈΠ΅, ΠΏΡΠΈ ΠΊΠΎΠ΅ΡΠΎ ΡΠ΅ ΠΎΡΡΡΠ΅ΡΡΠ²ΡΠ²Π° Π²ΡΡΠ·ΠΊΠ° Ρ ΡΡΡΠ΅ΡΠ°, ΠΏΠΎΠ΄Π³ΠΎΡΠ²Ρ ΡΠ΅ Π½ΠΎΠ²Π° ΠΌΠΎΠ΄ΠΈΡΠΈΡΠΈΡΠ°Π½Π° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ, ΠΊΠΎΡΡΠΎ ΡΠ»Π΅Π΄ ΡΠΎΠ²Π° ΡΠ΅ Π²Π°Π»ΠΈΠ΄ΠΈΡΠ° ΠΎΡ ΡΡΡΡΠΎΠΉΡΡΠ²ΠΎΡΠΎ (Π½ΠΎ ΡΠΎΠ²Π° Π½Π΅ Π΅ ΡΠΈΠ³ΡΡΠ½ΠΎ; Π·Π°Π²ΠΈΡΠΈ ΠΎΡ ΠΏΠΎΠ΄Π΄ΡΡΠΆΠΊΠ°ΡΠ° Π½Π° ΡΡΡΡΠΎΠΉΡΡΠ²ΠΎΡΠΎ ΠΈ Π²Π½Π΅Π΄ΡΡΠ²Π°Π½Π΅ΡΠΎ Π½Π° Π΄ΡΠ°ΠΉΠ²Π΅ΡΠ° Π² 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
Π Π½Π°ΡΠ°Π»ΠΎΡΠΎ Π½Π° ΡΡΠ°ΡΠΈΡΡΠ° ΠΌΠ°Π»ΠΊΠΎ ΠΏΡΠ΅ΠΊΠ°Π»ΠΈΡ ansible, Π½ΠΎ Π½Π΅ Π²ΡΠΈΡΠΊΠΎ Π΅ ΡΠΎΠ»ΠΊΠΎΠ²Π° Π»ΠΎΡΠΎ. ΠΠ½ΠΎΠ³ΠΎ Π³ΠΈ Ρ Π°ΡΠ΅ΡΠ²Π°ΠΌ ΡΠ²ΠΎΠ΄ like, ΠΊΠΎΠΉΡΠΎ Π΅ ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ΅Π½ Π΄Π° ΡΠΊΡΠΈΠ΅ ΡΡΠ²ΡΡΠ²ΠΈΡΠ΅Π»Π½Π° ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΈΠ·Π²ΡΠ½ ΠΏΠΎΠ»Π΅Π·ΡΠ΅Π½ΠΈΠ΅ΡΠΎ. Π Π²Π΅ΡΠΎΡΡΠ½ΠΎ ΠΌΠ½ΠΎΠ·ΠΈΠ½Π° ΡΠ° Π·Π°Π±Π΅Π»ΡΠ·Π°Π»ΠΈ, ΡΠ΅ ΠΈΠΌΠ°ΠΌΠ΅ Π²ΡΠΈΡΠΊΠΈ Π΄Π°Π½Π½ΠΈ Π·Π° Π²Π»ΠΈΠ·Π°Π½Π΅/ΠΏΠ°ΡΠΎΠ»ΠΈ Π·Π° Π²ΡΠΈΡΠΊΠΈ Π±ΠΎΠΉΠ½ΠΈ ΡΡΡΠ΅ΡΠΈ, ΠΈΡΠΊΡΡΡΠΈ Π² ΠΎΡΠ²ΠΎΡΠ΅Π½Π° ΡΠΎΡΠΌΠ° Π²ΡΠ² ΡΠ°ΠΉΠ» gorups.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
Π’ΠΎΠ»ΠΊΠΎΠ²Π° Π΅ ΠΏΡΠΎΡΡΠΎ. ΠΡΡΠ°Π²Π° Π΄Π° Π½Π°ΡΡΠΈΠΌ Π½Π°ΡΠΈΡΠ΅ ΠΠΎΡΠ½ΠΈΡ-ΡΠΊΡΠΈΠΏΡ Π·Π° ΠΈΠ·Π²Π»ΠΈΡΠ°Π½Π΅ ΠΈ ΠΏΡΠΈΠ»Π°Π³Π°Π½Π΅ Π½Π° ΡΠ΅Π·ΠΈ Π΄Π°Π½Π½ΠΈ.
ΠΠ° Π΄Π° Π½Π°ΠΏΡΠ°Π²ΠΈΡΠ΅ ΡΠΎΠ²Π°, Π² Π½Π°ΡΠΈΡ ΡΠΊΡΠΈΠΏΡ ΡΠ»Π΅Π΄ ΡΠ΅Π΄Π° Π·Π° ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ 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.
ΠΠ·ΡΠΎΡΠ½ΠΈΠΊ: www.habr.com