Nornir рд╡рд╛рдкрд░реВрди рдиреЗрдЯрд╡рд░реНрдХ рдбрд┐рд╡реНрд╣рд╛рдЗрд╕ рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рди рдШрдЯрдХрд╛рдВрдЪреА рд╕реНрд╡рдпрдВрдЪрд▓рд┐рдд рдирд┐рд░реНрдорд┐рддреА рдЖрдгрд┐ рднрд░рдгреЗ

Nornir рд╡рд╛рдкрд░реВрди рдиреЗрдЯрд╡рд░реНрдХ рдбрд┐рд╡реНрд╣рд╛рдЗрд╕ рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рди рдШрдЯрдХрд╛рдВрдЪреА рд╕реНрд╡рдпрдВрдЪрд▓рд┐рдд рдирд┐рд░реНрдорд┐рддреА рдЖрдгрд┐ рднрд░рдгреЗ

рдЕрд╣реЛ рд╣рд╛рдмреНрд░!

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

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

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

рдореНрд╣рдгреВрдирдЪ, рдлрд╛рд░ рдкреВрд░реНрд╡реА рд╕рд╛рдпрдХрд▓рдЪреА рджреБрд╕рд░реА рдЖрд╡реГрддреНрддреА рд▓рд╛рдЧреВ рдХреЗрд▓реА рдЧреЗрд▓реА рдирд╛рд╣реА. рдпрд╛ рд╡реЗрд│реА рдЕрдЬрдЧрд░, рдХрд┐рдВрд╡рд╛ рддреНрдпрд╛рдРрд╡рдЬреА рдордзреНрдпреЗ рд▓рд┐рд╣рд┐рд▓реЗрд▓реНрдпрд╛ рдлреНрд░реЗрдорд╡рд░реНрдХрд╡рд░ рдЕрдЬрдЧрд░ рдЖрдгрд┐ рд╕рд╛рдареА рдЕрдЬрдЧрд░ рдореНрд╣рдгрддрд╛рдд рдиреЙрд░рдирд┐рд░

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

рдЦрд╛рд▓реАрд▓ рдереЗрдЯ рдЙрджрд╛рд╣рд░рдгрд╛рдЪрд╛ рд╡рд╛рдкрд░ рдХрд░реВрди рддреЗ рдХрд╛рдп рдЖрд╣реЗ рддреЗ рдкрд╛рд╣реВ.

рдорд╛рдЭреНрдпрд╛рдХрдбреЗ рджреЗрд╢рднрд░рд╛рдд рдЕрдиреЗрдХ рдбрдЭрди рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдВрд╕рд╣ рд╢рд╛рдЦрд╛ рдиреЗрдЯрд╡рд░реНрдХ рдЖрд╣реЗ. рдкреНрд░рддреНрдпреЗрдХ рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдд рдПрдХ WAN рд░рд╛рдЙрдЯрд░ рдЕрд╕рддреЛ рдЬреЛ рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ рдСрдкрд░реЗрдЯрд░рдХрдбреВрди рдЕрдиреЗрдХ рд╕рдВрдкреНрд░реЗрд╖рдг рдЪреЕрдиреЗрд▓ рдмрдВрдж рдХрд░рддреЛ. рд░реВрдЯрд┐рдВрдЧ рдкреНрд░реЛрдЯреЛрдХреЙрд▓ BGP рдЖрд╣реЗ. WAN рд░рд╛рдЙрдЯрд░ рджреЛрди рдкреНрд░рдХрд╛рд░рд╛рдд рдпреЗрддрд╛рдд: Cisco ISG рдХрд┐рдВрд╡рд╛ Juniper 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, рдЖрдгрд┐ рдордзреНрдпреЗ 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 рдордзреНрдпреЗ рдкрд╛рд╕рд╡рд░реНрдб рд▓рдкрд╡рдд рдЖрд╣реЗ

рд▓реЗрдЦрд╛рдЪреНрдпрд╛ рд╕реБрд░реБрд╡рд╛рддреАрд▓рд╛ рдореА рдереЛрдбрдВ рдУрд╡реНрд╣рд░рдмреЛрд░реНрдб рдЧреЗрд▓реЛ рдЙрддреНрддрд░рджрд╛рдпреА, рдкрд░рдВрддреБ рд╣реЗ рд╕рд░реНрд╡ рдЗрддрдХреЗ рд╡рд╛рдИрдЯ рдирд╛рд╣реА. рдорд▓рд╛ рддреЗ рдЦрд░реЛрдЦрд░ рдЖрд╡рдбрддрд╛рдд рддрд┐рдЬреЛрд░реА рдЬрд╕реЗ рдХреА, рд╕рдВрд╡реЗрджрдирд╢реАрд▓ рдорд╛рд╣рд┐рддреА рджреГрд╖реНрдЯреАрдмрд╛рд╣реЗрд░ рд▓рдкрд╡рдгреНрдпрд╛рд╕рд╛рдареА рдбрд┐рдЭрд╛рдЗрди рдХреЗрд▓реЗрд▓реА рдЖрд╣реЗ. рдЖрдгрд┐ рдХрджрд╛рдЪрд┐рдд рдЕрдиреЗрдХрд╛рдВрдЪреНрдпрд╛ рд▓рдХреНрд╖рд╛рдд рдЖрд▓реЗ рдЕрд╕реЗрд▓ рдХреА рдЖрдордЪреНрдпрд╛рдХрдбреЗ рд╕рд░реНрд╡ рд▓рдврд╛рдК рд░рд╛рдЙрдЯрд░рд╕рд╛рдареА рд╕рд░реНрд╡ рд▓реЙрдЧрд┐рди/рдкрд╛рд╕рд╡рд░реНрдбреНрд╕ рдПрдХрд╛ рдлрд╛рдИрд▓рдордзреНрдпреЗ рдЙрдШрдбреНрдпрд╛ рд╕реНрд╡рд░реВрдкрд╛рдд рдЪрдордХрдд рдЖрд╣реЗрдд. gorups.yaml. рд╣реЗ рдирдХреНрдХреАрдЪ рд╕реБрдВрджрд░ рдирд╛рд╣реА. рдпрд╛ рдбреЗрдЯрд╛рдЪреЗ рд╕рдВрд░рдХреНрд╖рдг рдХрд░реВрдпрд╛ рддрд┐рдЬреЛрд░реА.

рдЪрд▓рд╛ 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

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдЬреЛрдбрд╛