Nornir рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ рдиреЗрдЯрд╡рд░реНрдХ рдЙрдкрдХрд░рдг рдХрдиреНрдлрд┐рдЧрд░реЗрд╕рди рддрддреНрд╡рд╣рд░реВрдХреЛ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рдЙрддреНрдкрд╛рджрди рд░ рднрд░рд┐рдиреЗ

Nornir рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ рдиреЗрдЯрд╡рд░реНрдХ рдЙрдкрдХрд░рдг рдХрдиреНрдлрд┐рдЧрд░реЗрд╕рди рддрддреНрд╡рд╣рд░реВрдХреЛ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рдЙрддреНрдкрд╛рджрди рд░ рднрд░рд┐рдиреЗ

рд╣реЗ рд╣рд╛рдмрд░!

рднрд░реНрдЦрд░реИ рдПрдЙрдЯрд╛ рд▓реЗрдЦ рдпрд╣рд╛рдБ рдкрдк рдЕрдк рднрдпреЛ Mikrotik рд░ Linuxред рджрд┐рдирдЪрд░реНрдпрд╛ рд░ рд╕реНрд╡рдЪрд╛рд▓рди рдЬрд╣рд╛рдБ рдПрдХ рд╕рдорд╛рди рд╕рдорд╕реНрдпрд╛ рдЬреАрд╡рд╛рд╢реНрдо рдорд╛рдзреНрдпрдо рдкреНрд░рдпреЛрдЧ рдЧрд░реА рд╣рд▓ рдЧрд░рд┐рдПрдХреЛ рдерд┐рдпреЛред рд░ рдпрджреНрдпрдкрд┐ рдХрд╛рд░реНрдп рдкреВрд░реНрдг рд░реВрдкрдорд╛ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдЫ, рддреНрдпрд╣рд╛рдБ Habr├й рдорд╛ рдпрд╕рдХреЛ рдмрд╛рд░реЗрдорд╛ рдХреЗрд╣рд┐ рд╕рдорд╛рди рдЫреИрдиред рдо рд╕рдореНрдорд╛рдирд┐рдд IT рд╕рдореБрджрд╛рдпрд▓рд╛рдИ рдореЗрд░реЛ рд╕рд╛рдЗрдХрд▓ рдкреНрд░рд╕реНрддрд╛рд╡ рдЧрд░реНрди рд╣рд┐рдореНрдордд рдЧрд░реНрдЫреБред

рдпрд╕реНрддреЛ рдХрд╛рд░реНрдпрдХреЛ рд▓рд╛рдЧрд┐ рдпреЛ рдкрд╣рд┐рд▓реЛ рдмрд╛рдЗрдХ рд╣реЛрдЗрдиред рдкрд╣рд┐рд▓реЛ рд╡рд┐рдХрд▓реНрдк рдХреЗрд╣реА рд╡рд░реНрд╖ рдкрд╣рд┐рд▓реЗ рд▓рд╛рдЧреВ рднрдПрдХреЛ рдерд┐рдпреЛ рдЙрддреНрддрд░рджрд╛рдпреА рд╕рдВрд╕реНрдХрд░рдг 1.x.x рд╕рд╛рдЗрдХрд▓ рд╡рд┐рд░рд▓реИ рдкреНрд░рдпреЛрдЧ рдЧрд░рд┐рдиреНрдереНрдпреЛ рд░ рддреНрдпрд╕реИрд▓реЗ рдирд┐рд░рдиреНрддрд░ рдЦрд┐рдпрд╛ рд▓рд╛рдЧреНрдереНрдпреЛред рдпрд╕ рдЕрд░реНрдердорд╛ рдХрд┐ рдХрд╛рд░реНрдп рдЖрдлреИрдВ рдЙрддреНрдкрдиреНрди рд╣реБрдБрджреИрди рдЬрддрд┐ рдкрдЯрдХ рд╕рдВрд╕реНрдХрд░рдгрд╣рд░реВ рдЕрдкрдбреЗрдЯ рд╣реБрдиреНрдЫрдиреН рдЙрддреНрддрд░рджрд╛рдпреАред рд░ рд╣рд░реЗрдХ рдкрдЯрдХ рддрдкрд╛рдИрд▓реЗ рдбреНрд░рд╛рдЗрдн рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдЫ, рдЪреЗрди рдЭрд░реНрдЫ рд╡рд╛ рдкрд╛рдЩреНрдЧреНрд░рд╛ рдЦрд╕реНрдЫред рдпрджреНрдпрдкрд┐, рдкрд╣рд┐рд▓реЛ рднрд╛рдЧ, рдХрдиреНрдлрд┐рдЧрд╣рд░реВ рдЙрддреНрдкрдиреНрди рдЧрд░реНрджреИ, рд╕рдзреИрдВ рдзреЗрд░реИ рд╕реНрдкрд╖реНрдЯ рд░реВрдкрдорд╛ рдХрд╛рдо рдЧрд░реНрджрдЫ, рд╕реМрднрд╛рдЧреНрдп рджреЗрдЦрд┐ jinja2 рдЗрдиреНрдЬрд┐рди рд▓рд╛рдореЛ рд╕рдордпрд╕рдореНрдо рд╕реНрдерд╛рдкрд┐рдд рдЫред рддрд░ рджреЛрд╕реНрд░реЛ рднрд╛рдЧ, рдХрдиреНрдлрд┐рдЧрд╣рд░реВ рд░реЛрд▓ рдЖрдЙрдЯ рдЧрд░реНрджреИ, рд╕рд╛рдорд╛рдиреНрдпрддрдпрд╛ рдЖрд╢реНрдЪрд░реНрдп рд▓реНрдпрд╛рдЗрдпреЛред рд░ рдореИрд▓реЗ рдХрдиреНрдлрд┐рдЧрд░реЗрд╕рдирд▓рд╛рдИ рдЯрд╛рдврд╛рдмрд╛рдЯ рдЖрдзрд╛ рд╕рдп рдпрдиреНрддреНрд░рд╣рд░реВрдорд╛ рд░реЛрд▓ рдЖрдЙрдЯ рдЧрд░реНрдиреБрдкрд░реНрдиреЗ рднрдПрдХреЛрд▓реЗ, рдЬрд╕рдордзреНрдпреЗ рдХреЗрд╣реА рд╣рдЬрд╛рд░реМрдВ рдХрд┐рд▓реЛрдорд┐рдЯрд░ рдЯрд╛рдврд╛ рдЕрд╡рд╕реНрдерд┐рдд рдЫрдиреН, рдпреЛ рдЙрдкрдХрд░рдг рдкреНрд░рдпреЛрдЧ рдЧрд░реНрдиреБ рдереЛрд░реИ рдмреЛрд░рд┐рдВрдЧ рдерд┐рдпреЛред

рдпрд╣рд╛рдБ рдореИрд▓реЗ рд╕реНрд╡реАрдХрд╛рд░ рдЧрд░реНрдиреБрдкрд░реНрдЫ рдХрд┐ рдореЗрд░реЛ рдЕрдирд┐рд╢реНрдЪрд┐рддрддрд╛ рдкреНрд░рд╛рдпрдГ рдореЗрд░реЛ рдкрд░рд┐рдЪрд┐рддрддрд╛рдХреЛ рдХрдореАрдорд╛ рдирд┐рд╣рд┐рдд рдЫ рдЙрддреНрддрд░рджрд╛рдпреАрдпрд╕рдХрд╛ рдХрдордЬреЛрд░реАрд╣рд░реВрдорд╛ рднрдиреНрджрд╛ред рд░ рдпреЛ, рдмрд╛рдЯреЛ рджреНрд╡рд╛рд░рд╛, рдПрдХ рдорд╣рддреНрддреНрд╡рдкреВрд░реНрдг рдмрд┐рдиреНрджреБ рд╣реЛред рдЙрддреНрддрд░рджрд╛рдпреА рдкреВрд░реНрдг рд░реВрдкрдорд╛ рдЫреБрдЯреНрдЯреИ рдЫ, рдпрд╕рдХреЛ рдЖрдлреНрдиреИ DSL (рдбреЛрдореЗрди рд╕реНрдкреЗрд╕рд┐рдлрд┐рдХ рд▓реНрдпрд╛рдЩреНрдЧреНрд╡реЗрдЬ) рд╕рдБрдЧрдХреЛ рдЬреНрдЮрд╛рдирдХреЛ рдЖрдлреНрдиреЛ рдХреНрд╖реЗрддреНрд░ рд╣реЛ, рдЬреБрди рдЖрддреНрдорд╡рд┐рд╢реНрд╡рд╛рд╕рдХреЛ рд╕реНрддрд░рдорд╛ рд░рд╛рдЦреНрдиреБрдкрд░реНрдЫред рдЦреИрд░, рддреНрдпреЛ рдХреНрд╖рдг рдЙрддреНрддрд░рджрд╛рдпреА рдпреЛ рдПрдХрджрдо рдЪрд╛рдБрдбреЛ рд╡рд┐рдХрд╛рд╕ рд╣реБрдБрджреИрдЫ, рд░ рдкрдЫрд╛рдбрд┐ рдЕрдиреБрдХреВрд▓рддрд╛рдХреЛ рд▓рд╛рдЧрд┐ рд╡рд┐рд╢реЗрд╖ рдзреНрдпрд╛рди рдмрд┐рдирд╛, рдпрд╕рд▓реЗ рдЖрддреНрдорд╡рд┐рд╢реНрд╡рд╛рд╕ рдердкреНрджреИрдиред

рддрд╕рд░реНрде, рдзреЗрд░реИ рд╕рдордп рдЕрдШрд┐ рд╕рд╛рдЗрдХрд▓ рдХреЛ рджреЛрд╕реНрд░реЛ рд╕рдВрд╕реНрдХрд░рдг рд▓рд╛рдЧреВ рднрдПрдХреЛ рдерд┐рдпреЛред рдпрд╕ рдкрдЯрдХ рдЕрдЬрдЧрд░, рд╡рд╛ рдмрд░реБ рдорд╛ рд▓реЗрдЦрд┐рдПрдХреЛ рдлреНрд░реЗрдорд╡рд░реНрдХ рдорд╛ рдЕрдЬрдЧрд░ рд░ рдХреЛ рд▓рд╛рдЧреА рдЕрдЬрдЧрд░ рднрдирд┐рдиреНрдЫ рдиреЛрд░реНрдирд┐рд░

рддрд░ - рдиреЛрд░реНрдирд┐рд░ рдорд╛ рд▓реЗрдЦрд┐рдПрдХреЛ рдПрдХ рдорд╛рдЗрдХреНрд░реЛрдлреНрд░реЗрдорд╡рд░реНрдХ рд╣реЛ рдЕрдЬрдЧрд░ рд░ рдХреЛ рд▓рд╛рдЧреА рдЕрдЬрдЧрд░ рд░ рд╕реНрд╡рдЪрд╛рд▓рдирдХреЛ рд▓рд╛рдЧрд┐ рдбрд┐рдЬрд╛рдЗрди рдЧрд░рд┐рдПрдХреЛред рдХреЛ рдорд╛рдорд▓рд╛ рдорд╛ рдЬрд╕реНрддреИ рдЙрддреНрддрд░рджрд╛рдпреА, рдпрд╣рд╛рдБ рд╕рдорд╕реНрдпрд╛рд╣рд░реВ рд╕рдорд╛рдзрд╛рди рдЧрд░реНрди, рд╕рдХреНрд╖рдо рдбрд╛рдЯрд╛ рддрдпрд╛рд░реА рдЖрд╡рд╢реНрдпрдХ рдЫ, рдЕрд░реНрдерд╛рддреНред рд╣реЛрд╕реНрдЯрд╣рд░реВ рд░ рддрд┐рдиреАрд╣рд░реВрдХрд╛ рдкреНрдпрд╛рд░рд╛рдорд┐рдЯрд░рд╣рд░реВрдХреЛ рд╕реВрдЪреА, рддрд░ рд▓рд┐рдкрд┐рд╣рд░реВ рдЫреБрдЯреНрдЯреИ DSL рдорд╛ рд▓реЗрдЦрд┐рдПрдХрд╛ рдЫреИрдирдиреН, рддрд░ рдзреЗрд░реИ рдкреБрд░рд╛рдиреЛ рд╣реЛрдЗрди, рддрд░ рдзреЗрд░реИ рд░рд╛рдореНрд░реЛ p[i|i] рдЯрдирдорд╛ рд▓реЗрдЦрд┐рдПрдХрд╛ рдЫрдиреНред

рдирд┐рдореНрди рдкреНрд░рддреНрдпрдХреНрд╖ рдЙрджрд╛рд╣рд░рдг рдкреНрд░рдпреЛрдЧ рдЧрд░реАрд░рд╣реЗрдХреЛ рдЫ рд╣реЗрд░реМрдВред

рдореЗрд░реЛ рджреЗрд╢рднрд░рд┐ рджрд░реНрдЬрдиреМрдВ рдХрд╛рд░реНрдпрд╛рд▓рдпрд╣рд░реВ рднрдПрдХреЛ рд╢рд╛рдЦрд╛ рд╕рдЮреНрдЬрд╛рд▓ рдЫред рдкреНрд░рддреНрдпреЗрдХ рдХрд╛рд░реНрдпрд╛рд▓рдпрдорд╛ WAN рд░рд╛рдЙрдЯрд░ рд╣реБрдиреНрдЫ рдЬрд╕рд▓реЗ рд╡рд┐рднрд┐рдиреНрди рдЕрдкрд░реЗрдЯрд░рд╣рд░реВрдмрд╛рдЯ рдзреЗрд░реИ рд╕рдЮреНрдЪрд╛рд░ рдЪреНрдпрд╛рдирд▓рд╣рд░реВ рд╕рдорд╛рдкреНрдд рдЧрд░реНрджрдЫред рд░реВрдЯрд┐рдЩ рдкреНрд░реЛрдЯреЛрдХрд▓ BGP рд╣реЛред WAN рд░рд╛рдЙрдЯрд░рд╣рд░реВ рджреБрдИ рдкреНрд░рдХрд╛рд░рдХрд╛ рд╣реБрдиреНрдЫрдиреН: рд╕рд┐рд╕реНрдХреЛ ISG рд╡рд╛ рдЬреБрдирд┐рдкрд░ SRXред

рдЕрдм рдХрд╛рд░реНрдп: рддрдкрд╛рдИрдВрд▓реЗ рд╢рд╛рдЦрд╛ рдиреЗрдЯрд╡рд░реНрдХрдХреЛ рд╕рдмреИ WAN рд░рд╛рдЙрдЯрд░рд╣рд░реВрдорд╛ рдЫреБрдЯреНрдЯреИ рдкреЛрд░реНрдЯрдорд╛ рднрд┐рдбрд┐рдпреЛ рдирд┐рдЧрд░рд╛рдиреАрдХреЛ рд▓рд╛рдЧрд┐ рд╕рдорд░реНрдкрд┐рдд рд╕рдмрдиреЗрдЯ рдХрдиреНрдлрд┐рдЧрд░ рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдЫ - BGP рдорд╛ рдпреЛ рд╕рдмрдиреЗрдЯ рд╡рд┐рдЬреНрдЮрд╛рдкрди рдЧрд░реНрдиреБрд╣реЛрд╕реН - рд╕рдорд░реНрдкрд┐рдд рдкреЛрд░реНрдЯрдХреЛ рдЧрддрд┐ рд╕реАрдорд╛ рдХрдиреНрдлрд┐рдЧрд░ рдЧрд░реНрдиреБрд╣реЛрд╕реНред

рдкрд╣рд┐рд▓реЗ, рд╣рд╛рдореАрд▓реЗ рдХреЗрд╣рд┐ рдЯреЗрдореНрдкреНрд▓реЗрдЯрд╣рд░реВ рддрдпрд╛рд░ рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдЫ, рдЬрд╕рдХреЛ рдЖрдзрд╛рд░рдорд╛ рд╕рд┐рд╕реНрдХреЛ рд░ рдЬреБрдирд┐рдкрд░рдХрд╛ рд▓рд╛рдЧрд┐ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдХрдиреНрдлрд┐рдЧрд░реЗрд╕рдирд╣рд░реВ рдЙрддреНрдкрдиреНрди рд╣реБрдиреЗрдЫрдиреНред рдкреНрд░рддреНрдпреЗрдХ рдмрд┐рдиреНрджреБ рд░ рдЬрдбрд╛рди рдкреНрдпрд╛рд░рд╛рдорд┐рдЯрд░рд╣рд░реВрдХреЛ рд▓рд╛рдЧрд┐ рдбрд╛рдЯрд╛ рддрдпрд╛рд░ рдЧрд░реНрди рдкрдирд┐ рдЖрд╡рд╢реНрдпрдХ рдЫ, рдЕрд░реНрдерд╛рддреНред рдПрдЙрдЯреИ рд╕реВрдЪреА рд╕рдЩреНрдХрд▓рди

рд╕рд┐рд╕реНрдХреЛрдХрд╛ рд▓рд╛рдЧрд┐ рддрдпрд╛рд░ рдЯреЗрдореНрдкреНрд▓реЗрдЯ:

$ 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, рд░ in 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

рдпрд╕реНрддреЛ рднрдпреЛ рд╕реВрдЪреА рд╣рд╛рдореНрд░реЛ рдХрд╛рд░реНрдпрдХреЛ рд▓рд╛рдЧрд┐ред рдкреНрд░рд╛рд░рдореНрднрд┐рдХ рд╕рдордпрдорд╛, рд╕реВрдЪреА рдлрд╛рдЗрд▓рд╣рд░реВрдмрд╛рдЯ рдкреНрдпрд╛рд░рд╛рдорд┐рдЯрд░рд╣рд░реВ рд╡рд╕реНрддреБ рдореЛрдбреЗрд▓рдорд╛ рдореНрдпрд╛рдк рдЧрд░рд┐рдиреНрдЫ рдЗрдиреНрднреЗрдиреНрдЯрд░реА рдПрд▓рд┐рдореЗрдиреНрдЯ.

рд╕реНрдкреЛрдЗрд▓рд░рдХреЛ рддрд▓ 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 рдорд╛ рдЪрд╛рд▓рдХ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрдирдорд╛ рдирд┐рд░реНрднрд░ рдЧрд░реНрджрдЫ) , рддрд░ рдирдпрд╛рдБ рдХрдиреНрдлрд┐рдЧрд░реЗрд╕рди рдкреНрд░рддреНрдпрдХреНрд╖ рд░реВрдкрдорд╛ рд▓рд╛рдЧреВ рдЧрд░рд┐рдПрдХреЛ рдЫреИрдиред рд▓рдбрд╛рдИ рдкреНрд░рдпреЛрдЧрдХреЛ рд▓рд╛рдЧрд┐, рддрдкрд╛рдИрдВрд▓реЗ рдкреНрдпрд╛рд░рд╛рдорд┐рдЯрд░ рд╣рдЯрд╛рдЙрдиреБ рдкрд░реНрдЫ рдбреНрд░рд╛рдИ_рд░рди рд╡рд╛ рдпрд╕рдХреЛ рдорд╛рди рдкрд░рд┐рд╡рд░реНрддрди рдЧрд░реНрдиреБрд╣реЛрд╕реН рдЭреВрдЯрд╛.

рдЬрдм рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╣реБрдиреНрдЫ, 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 рдорд╛ рдкрд╛рд╕рд╡рд░реНрдбрд╣рд░реВ рд▓реБрдХрд╛рдЙрдБрджреИ

рд▓реЗрдЦрдХреЛ рд╕реБрд░реБрдорд╛ рдо рдЕрд▓рд┐ рдУрднрд░рдмреЛрд░реНрдбрдорд╛ рдЧрдПрдБ рдЙрддреНрддрд░рджрд╛рдпреА, рддрд░ рдпреЛ рд╕рдмреИ рдЦрд░рд╛рдм рдЫреИрдиред рдо рддрд┐рдиреАрд╣рд░реВрд▓рд╛рдИ рд╕рд╛рдБрдЪреНрдЪреИ рдордирдкрд░реНрдЫ vault рдЬрд╕реНрддреИ, рдЬреБрди рд╕рдВрд╡реЗрджрдирд╢реАрд▓ рдЬрд╛рдирдХрд╛рд░реАрд▓рд╛рдИ рдирдЬрд░рдмрд╛рдЯ рд▓реБрдХрд╛рдЙрди рдбрд┐рдЬрд╛рдЗрди рдЧрд░рд┐рдПрдХреЛ рд╣реЛред рд░ рд╕рд╛рдпрдж рдзреЗрд░реИрд▓реЗ рдпрд╛рдж рдЧрд░реЗрдХрд╛ рдЫрдиреН рдХрд┐ рд╣рд╛рдореАрд╕рдБрдЧ рд╕рдмреИ рд▓рдбрд╛рдИ рд░рд╛рдЙрдЯрд░рд╣рд░реВрдХреЛ рд▓рд╛рдЧрд┐ рд╕рдмреИ рд▓рдЧрдЗрдирд╣рд░реВ/рдкрд╛рд╕рд╡рд░реНрдбрд╣рд░реВ рдлрд╛рдЗрд▓рдорд╛ рдЦреБрд▓рд╛ рд░реВрдкрдорд╛ рдЪрдореНрдХрд┐рд░рд╣реЗрдХрд╛ рдЫрдиреНред gorups.yamlред рдпреЛ рдкрдХреНрдХреИ рдкрдирд┐, рд╕реБрдиреНрджрд░ рдЫреИрдиред рдпрд╕ рдбрд╛рдЯрд╛рд▓рд╛рдИ рд╕реБрд░рдХреНрд╖рд┐рдд рдЧрд░реМрдВ vault.

group.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 рдХреЛ рдЫреЗрдЙрдорд╛ рдЕрд╡рд╕реНрдерд┐рдд рд╣реБрдиреБ рд╣реБрдБрджреИрдиред рддрд░ рдпреЛ рдЦреЗрд▓реНрдирдХреЛ рд▓рд╛рдЧрд┐ рдареАрдХ рдЫред

рдЕрд╣рд┐рд▓реЗрдХреЛ рд▓рд╛рдЧрд┐ рдпрддрд┐ рд╣реЛред рддреНрдпрд╣рд╛рдБ Cisco + Zabbix рдЖрдЙрдБрджреИ рдЧрд░реЗрдХреЛ рдмрд╛рд░реЗрдорд╛ рдХреЗрд╣рд┐ рдердк рд▓реЗрдЦрд╣рд░реВ рдЫрдиреН, рддрд░ рдпреЛ рд╕реНрд╡рдЪрд╛рд▓рдирдХреЛ рдмрд╛рд░реЗрдорд╛ рдХреЗрд╣реА рд╣реЛрдЗрдиред рд░ рдирд┐рдХрдЯ рднрд╡рд┐рд╖реНрдпрдорд╛ рдо рд╕рд┐рд╕реНрдХреЛрдорд╛ RESTCONF рдХреЛ рдмрд╛рд░реЗрдорд╛ рд▓реЗрдЦреНрдиреЗ рдпреЛрдЬрдирд╛ рдЧрд░реНрдЫреБред

рд╕реНрд░реЛрдд: www.habr.com

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдердкреНрди