Эй Хабр!
Жакында бул жерде бир макала пайда болду
Бул мындай тапшырма үчүн биринчи велосипед эмес. Биринчи параметр кайра бир нече жыл мурун ишке ашырылган түшүнүктүү версия 1.x.x. Велосипед сейрек колдонулгандыктан, дайыма дат басып калган. Бул тапшырманын өзү версиялар жаңыртылган сайын пайда болбойт деген мааниде түшүнүктүү. Ал эми айдоо керек болгон сайын чынжыр же дөңгөлөк түшүп калат. Бирок, конфигурацияларды жаратуучу биринчи бөлүк, бактыга жараша, ар дайым абдан так иштейт jinja2 Мотор узак убакыттан бери орнотулган. Бирок экинчи бөлүк - конфигурацияларды жайылтуу - адатта күтүлбөгөн нерселерди алып келди. Мен конфигурацияны алыстан жарым жүздөй түзмөккө жайылтышым керек болгондуктан, алардын айрымдары миңдеген километр алыстыкта жайгашкан, бул куралды колдонуу бир аз кызыксыз болду.
Бул жерде менин белгисиздигим, балким, тааныштыгымдын жоктугунан экенин моюнга алышым керек түшүнүктүүкемчиликтерине караганда. Жана бул, демек, маанилүү жагдай. түшүнүктүү толугу менен өзүнчө, өзүнүн DSL (домендик тил) менен өз билим чөйрөсү болуп саналат, ал ишенимдүү деңгээлде сакталышы керек. Ооба, ошол учур түшүнүктүү Ал тездик менен өнүгүп жатат жана артта калган шайкештикке өзгөчө маани бербестен, ишенимди арттырбайт.
Ошондуктан, жакында эле велосипеддин экинчи версиясы ишке киргизилген. Бул жолу код, же тагыраак айтканда, жазылган алкак боюнча код жана код Атындагы
Ошентип - Nornir ичинде жазылган микрофремворк болуп саналат код жана код жана автоматташтыруу учун арналган. менен болгон учурда эле түшүнүктүү, бул жерде көйгөйлөрдү чечүү үчүн, компетенттүү маалыматтарды даярдоо талап кылынат, б.а. хосттордун жана алардын параметрлеринин инвентаризациясы, бирок сценарийлер өзүнчө 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
Арча үчүн шаблон:
$ 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 үчүн эки гана параметр жана Cisco үчүн 3 параметр керек экенин көрөбүз. бул жерде алар:
- ifname
- ipsuffix
- asn
Эми биз ар бир түзмөк үчүн бул параметрлерди орнотуу керек, б.а. ошол эле нерсени кыл жолу.
үчүн жолу Документти так сактайбыз
башкача айтканда, ошол эле файлдын скелетин түзөлү:
.
├── 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.
Бул жерде ошол эле түшүнүктүү тестирлөө ишке ашырылды, мында роутерге туташуу жасалган, жаңы модификацияланган конфигурация даярдалат, ал андан кийин аппарат тарабынан текшерилет (бирок бул так эмес; бул түзмөктүн колдоосуна жана 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 ичинде сырсөздөрдү жашыруу
Макаланын башында мен бир аз чектен чыгып кеттим түшүнүктүү, бирок баары жаман эмес. Мен аларды абдан жакшы көрөм жыйнак сыяктуу сезимтал маалыматты жашыруу үчүн иштелип чыккан. Балким, көптөр бизде файлда ачык формада жаркыраган бардык согуштук роутерлердин бардык логиндери/паролдору бар экенин байкашкан. 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
Бул жөнөкөй. Бизге үйрөтүү калды Nornir-скрипт бул маалыматтарды алуу жана колдонуу үчүн.
Бул үчүн, инициализация сызыгынан кийин биздин скриптте 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 жөнүндө дагы бир нече макалалар бар, бирок бул автоматташтыруу жөнүндө эмес. Ал эми жакынкы келечекте Ciscoдо RESTCONF жөнүндө жазууну пландап жатам.
Source: www.habr.com