-
Notifications
You must be signed in to change notification settings - Fork 0
17. junos_syslog engine and Salt's reactor system end to end demo
Reactors files are executed when an event with a specific tag comes on the master event bus.
This mapping between an event tag and the reactor file to execute is present in the reactor
section of the master config file.
Example:
reactor:
- 'jnpr/syslog/*/UI_COMMIT_COMPLETED':
- /srv/reactor/on_commit.sls
Reactor sls files should be placed in the /srv/reactor/
directory for consistency between environments, but this is not currently enforced by Salt.
Reactor sls files follow a similar format to other sls files in Salt.
They are written in YAML and can be templated using Jinja.
Example:
reactor:
- 'jnpr/syslog/*/UI_COMMIT_COMPLETED':
- /srv/reactor/on_commit.sls
This demo is about using SaltStack as an event driven orchestrator to fire ansible playbooks based Juniper syslog messages (i.e based on Salt events from junos syslog engine and reactors).
The file How_to_orchestrate_Ansible_using_SaltStack.pdf shows how to orchestrate Ansible using SaltStack.
# more /etc/salt/master
file_roots:
base:
- /srv/salt
pillar_roots:
base:
- /srv/pillar
engines_dirs:
- /srv/engines
engines:
- junos_syslog:
port: 516
reactor:
- 'jnpr/syslog/*/UI_COMMIT_COMPLETED':
- /srv/reactor/on_commit.sls
copy junos_syslog.py in one of the engines_dirs
directory
# ls /srv/engines/
junos_syslog.py junos_syslog.pyc
# more /srv/reactor/on_commit.sls
Run ansible playbook:
local.cmd.run:
- tgt: minion_1
- arg:
- ansible-playbook /srv/ansible/junos_get_config/pb.2.yml --extra-vars target={{ data['hostname'] }} -i /srv/ansible/hosts
# ifconfig ens33 | grep "inet addr"
inet addr:192.168.233.17 Bcast:192.168.233.255 Mask:255.255.255.0
For junos_syslog engine to receive events, syslog must be set on the junos device. The ip address is the one of the device running the syslog engine and port is the port where the engine is listening for events.
vagrant@vqfx01> show configuration system syslog host 192.168.233.17
any any;
match UI_COMMIT_COMPLETED;
port 516;
# salt minion_1 test.ping
minion_1:
True
# salt "vq*" test.ping
vqfx01:
True
# more /srv/ansible/junos_get_config/pb.2.yml
---
- name: create a directory
hosts: localhost
gather_facts: no
tasks:
- name: create a directory
file:
path: "{{playbook_dir}}/configs"
state: directory
- name: Retrieve configuration from junos devices
hosts: "{{ target }}"
roles:
- Juniper.junos
connection: local
gather_facts: no
tasks:
- name: remove old files from the configs directory
file:
path: "{{playbook_dir}}/configs/{{inventory_hostname}}.conf"
state: absent
- name: include vars
include_vars: "{{playbook_dir}}/vars.yml"
- name: Retrieve interfaces configuration from junos devices
junos_get_config:
host: "{{ junos_host }}"
port: "{{ junos_port }}"
user: "{{ USERNAME }}"
passwd: "{{ DEVICE_PASSWORD }}"
dest: "{{playbook_dir}}/configs/{{ inventory_hostname }}.conf"
format: text
logfile: "{{playbook_dir}}/junos_get_config.log"
filter: interfaces
$ more /srv/ansible/junos_get_config/vars.yml
---
DEVICE_PASSWORD: Poclab123
USERNAME: pytraining
$ more /srv/ansible/hosts
[EX4200]
ex4200-7 junos_host=172.30.179.107
[VQFX]
vqfx01 junos_host=127.0.0.1 junos_port=8331
[EX4300]
ex4300-9 junos_host=172.30.179.95
ex4300-18 junos_host=172.30.179.74
ex4300-17 junos_host=172.30.179.73
# more /srv/ansible/junos_get_config/configs/vqfx01.conf
/srv/ansible/junos_get_config/configs/vqfx01.conf: No such file or directory
# ls -l /srv/ansible/junos_get_config/configs
ls: cannot access /srv/ansible/junos_get_config/configs: No such file or directory
### date
Tue Jun 27 14:02:39 PDT 2017
sudo tcpdump -i ens33 port 516 -XX
salt-run state.event pretty=True
vagrant@vqfx01# commit
configuration check succeeds
commit complete
The junos device will send a syslog message UI_COMMIT_COMPLETED
to the junos_syslog Salt engine.
The engine will then generate and send this message on SaltStack bus:
jnpr/syslog/vqfx01/UI_COMMIT_COMPLETED
The reactor file will be executed:
So the master will run:
salt minion_1 cmd.run "ansible-playbook /srv/ansible/junos_get_config/pb.2.yml --extra-vars target=vqfx01 -i /srv/ansible/hosts"
So the minion_1 will run:
ansible-playbook /srv/ansible/junos_get_config/pb.2.yml --extra-vars target=vqfx01 -i /srv/ansible/hosts
# ls -l /srv/ansible/junos_get_config/configs/vqfx01.conf
-rw-r--r-- 1 root root 75633 Jun 27 14:03 /srv/ansible/junos_get_config/configs/vqfx01.conf
# ls -l /srv/ansible/junos_get_config/configs
-rw-r--r-- 1 root root 75633 Jun 27 14:03 vqfx01.conf
# more /srv/ansible/junos_get_config/configs/vqfx01.conf
interfaces {
et-0/0/0 {
unit 0 {
family inet {
....
22:48:53.957045 IP 192.168.233.158.59781 > 192.168.233.17.516: UDP, length 75
0x0000: 000c 2911 2ecd 000c 2943 2de4 0800 4500 ..).....)C-...E.
0x0010: 0067 cef6 0000 3f11 588e c0a8 e99e c0a8 .g....?.X.......
0x0020: e911 e985 0204 0053 8ea6 3c31 3838 3e4a .......S..<188>J
0x0030: 756e 2032 3720 3230 3a34 383a 3431 2076 un.27.20:48:41.v
0x0040: 7166 7830 3120 6d67 645b 3137 3132 5d3a qfx01.mgd[1712]:
0x0050: 2055 495f 434f 4d4d 4954 5f43 4f4d 504c .UI_COMMIT_COMPL
0x0060: 4554 4544 3a20 636f 6d6d 6974 2063 6f6d ETED:.commit.com
0x0070: 706c 6574 65 plete
jnpr/syslog/vqfx01/UI_COMMIT_COMPLETED {
"_stamp": "2017-06-27T20:58:09.034524",
"daemon": "mgd",
"event": "UI_COMMIT_COMPLETED",
"facility": 23,
"hostip": "192.168.233.158",
"hostname": "vqfx01",
"message": "commit complete",
"pid": "2945",
"priority": 188,
"raw": "<188>Jun 27 20:57:55 vqfx01 mgd[2945]: UI_COMMIT_COMPLETED: commit complete",
"severity": 4,
"timestamp": "2017-06-27 22:58:09"
}
20170627225809048834 {
"_stamp": "2017-06-27T20:58:09.049214",
"minions": [
"minion_1"
]
}
salt/job/20170627225809048834/new {
"_stamp": "2017-06-27T20:58:09.051006",
"arg": [
"ansible-playbook /srv/ansible/junos_get_config/pb.2.yml --extra-vars target=vqfx01 -i /srv/ansible/hosts"
],
"fun": "cmd.run",
"jid": "20170627225809048834",
"minions": [
"minion_1"
],
"tgt": "minion_1",
"tgt_type": "glob",
"user": "sudo_ksator"
}
salt/job/20170627225809048834/ret/minion_1 {
"_stamp": "2017-06-27T20:58:11.730328",
"cmd": "_return",
"fun": "cmd.run",
"fun_args": [
"ansible-playbook /srv/ansible/junos_get_config/pb.2.yml --extra-vars target=vqfx01 -i /srv/ansible/hosts"
],
"id": "minion_1",
"jid": "20170627225809048834",
"retcode": 0,
"return": "\nPLAY [create a directory] ******************************************************\n\nTASK [create a directory] ******************************************************\nok: [localhost]\n\nPLAY [Retrieve configuration from junos devices] *******************************\n\nTASK [remove old files from the configs directory for each host] ***************\nchanged: [vqfx01]\n\nTASK [include vars] ************************************************************\nok: [vqfx01]\n\nTASK [Retrieve configuration from junos devices] *******************************\nchanged: [vqfx01]\n\nPLAY RECAP *********************************************************************\nlocalhost : ok=1 changed=0 unreachable=0 failed=0 \nvqfx01 : ok=3 changed=2 unreachable=0 failed=0",
"success": true
}
This is an Event driven Junos automation demo, using only SaltStack (i.e not having SaltStack to fire third party automation content like ansible/jsnapy ...)
# more /etc/salt/master
file_roots:
base:
- /srv/salt
pillar_roots:
base:
- /srv/pillar
engines_dirs:
- /srv/engines
engines:
- junos_syslog:
port: 516
reactor:
- 'jnpr/syslog/*/UI_COMMIT_COMPLETED':
- /srv/reactor/on_commit1.sls
# more /srv/reactor/on_commit1.sls
Run interface sls:
local.state.apply:
- tgt: {{ data['hostname'] }}
- arg:
- junos
This is equivalent to running this command on the master, but dynamically (triggered by an event):
salt xxx state.apply junos
# more /srv/salt/junos.sls
get-interface-information:
junos:
- rpc
- dest: /tmp/rpc.log
- interface_name: lo0
# more /tmp/rpc.log
more: stat of /tmp/rpc.log failed: No such file or directory
vagrant@vqfx01# commit
configuration check succeeds
commit complete
The junos device will send a syslog message UI_COMMIT_COMPLETED
to the junos_syslog Salt engine.
The engine will then generate and send this message on SaltStack bus jnpr/syslog/vqfx01/UI_COMMIT_COMPLETED
The reactor file will be executed.
So the master will run salt vqfx01 state.apply junos
# ls -l /tmp/rpc.log
-rw-r--r-- 1 root root 2623 juin 29 12:51 /tmp/rpc.log
# more /tmp/rpc.log
<interface-information style="normal">
<physical-interface>
<name>
lo0
</name>
<admin-status format="Enabled">
up
</admin-status>
<oper-status>
...
jnpr/syslog/vqfx01/UI_COMMIT_COMPLETED {
"_stamp": "2017-06-29T10:51:19.649033",
"daemon": "mgd",
"event": "UI_COMMIT_COMPLETED",
"facility": 23,
"hostip": "192.168.233.158",
"hostname": "vqfx01",
"message": "commit complete",
"pid": "1712",
"priority": 188,
"raw": "<188>Jun 28 17:14:28 vqfx01 mgd[1712]: UI_COMMIT_COMPLETED: commit complete",
"severity": 4,
"timestamp": "2017-06-29 12:51:19"
}
20170629125119753660 {
"_stamp": "2017-06-29T10:51:19.757053",
"minions": [
"vqfx01"
]
}
salt/job/20170629125119753660/new {
"_stamp": "2017-06-29T10:51:19.757738",
"arg": [
"junos"
],
"fun": "state.apply",
"jid": "20170629125119753660",
"minions": [
"vqfx01"
],
"tgt": "vqfx01",
"tgt_type": "glob",
"user": "sudo_ksator"
}
salt/auth {
"_stamp": "2017-06-29T10:51:19.829913",
"act": "accept",
"id": "ex4200-7",
"pub": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAybUpTGSHXaMYMHvcutLF\n3zAwuCXJtw/FvL5Kriy+MUY0o/zg12uS8HcMMe/dDpA8U64UX/XM4CIQHebkgzQE\n0YZAcXWmOkM2BfVk35iin7QrORJn0vhIOjfexNpmSa3ouZ45YfGrNdUYQOtXP0qE\ngqCGvy3qSv7ZnkAlIoX72BfTnHGyuj25qpY7BMnVwdmqSPzV6adWL7q4Ag5IUWOc\nTVrciACpqr8dchxbUgDYSka0FvFg7bAS0ROMCPjK5DPqXLOxbtTcrQ+7JBvczHxw\n67gNQpqtSL9ma8WTl2D5vw9ehnQ7UH7fKK5O0AJPMvGSeIh6rqQ6wI9wHb9Z4l4A\nIQIDAQAB\n-----END PUBLIC KEY-----",
"result": true
}
salt/auth {
"_stamp": "2017-06-29T10:51:19.835285",
"act": "accept",
"id": "minion_1",
"pub": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoCaDFZK9PWBwi33/ovjq\n5PErSOWQu+j0ltmfC/NXq78rlH1NXMjdMC/c4JFHYRt32z6sDf1fK0VqYZ/DCp44\nSNcfHOeHGVolnoEVNONvsOCnjV80xlpQmVKOvn1Hy4oZKmbBsk8jHzVsaxfOS0QG\nUK+yqmZlbVW91C8HA0uA+pqa79qoXR1JhIGNdXUly/hhrdwl/JbQ3ylffR0pXn7q\noJTSgZUNnm4jz42+lubQM7GXrPbNHSY9X7bGUJFjjwr7G++QcAzzhG+kbNdAXnWN\nLC26x5Jjo2XUmxWaq2tuj4BUO0g96I7PKCfeQzsAPu5Gzi7Ul8DVvTRAxTj+4j/m\nnwIDAQAB\n-----END PUBLIC KEY-----",
"result": true
}
salt/auth {
"_stamp": "2017-06-29T10:51:19.891070",
"act": "accept",
"id": "vqfx01",
"pub": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAybUpTGSHXaMYMHvcutLF\n3zAwuCXJtw/FvL5Kriy+MUY0o/zg12uS8HcMMe/dDpA8U64UX/XM4CIQHebkgzQE\n0YZAcXWmOkM2BfVk35iin7QrORJn0vhIOjfexNpmSa3ouZ45YfGrNdUYQOtXP0qE\ngqCGvy3qSv7ZnkAlIoX72BfTnHGyuj25qpY7BMnVwdmqSPzV6adWL7q4Ag5IUWOc\nTVrciACpqr8dchxbUgDYSka0FvFg7bAS0ROMCPjK5DPqXLOxbtTcrQ+7JBvczHxw\n67gNQpqtSL9ma8WTl2D5vw9ehnQ7UH7fKK5O0AJPMvGSeIh6rqQ6wI9wHb9Z4l4A\nIQIDAQAB\n-----END PUBLIC KEY-----",
"result": true
}
minion/refresh/vqfx01 {
"Minion data cache refresh": "vqfx01",
"_stamp": "2017-06-29T10:51:20.286848"
}
salt/job/20170629125119753660/ret/vqfx01 {
"_stamp": "2017-06-29T10:51:20.660185",
"cmd": "_return",
"fun": "state.apply",
"fun_args": [
"junos"
],
"id": "vqfx01",
"jid": "20170629125119753660",
"out": "highstate",
"retcode": 0,
"return": {
"junos_|-get-interface-information_|-get-interface-information_|-rpc": {
"__id__": "get-interface-information",
"__run_num__": 0,
"__sls__": "junos",
"changes": {
"out": true,
"rpc_reply": {
"interface-information": {
"physical-interface": {
"admin-status": "up",
"if-config-flags": {
"iff-snmp-traps": ""
},
"if-device-flags": {
"ifdf-loopback": "",
"ifdf-present": "",
"ifdf-running": ""
},
"if-media-flags": {
"ifmf-none": ""
},
"if-type": "Loopback",
"ifd-specific-config-flags": "",
"interface-flapped": "Never",
"local-index": "6",
"logical-interface": [
{
"address-family": [
{
"address-family-flags": {
"ifff-sendbcast-pkt-to-re": ""
},
"address-family-name": "inet",
"mtu": "Unlimited"
},
{
"address-family-flags": {
"ifff-none": ""
},
"address-family-name": "inet6",
"interface-address": {
"ifa-flags": {
"internal-flags": "0x800"
},
"ifa-local": "fe80::200:f:fc00:0",
"interface-address": {
"in6-addr-flags": {
"ifaf-none": ""
}
}
},
"intf-curr-cnt": "0",
"intf-dropcnt": "0",
"intf-unresolved-cnt": "0",
"max-local-cache": "0",
"mtu": "Unlimited",
"new-hold-limit": "0"
}
],
"encapsulation": "Unspecified",
"filter-information": "",
"if-config-flags": {
"iff-down": "",
"iff-snmp-traps": ""
},
"local-index": "547",
"name": "lo0.0",
"policer-overhead": "",
"snmp-index": "16",
"traffic-statistics": {
"input-packets": "0",
"output-packets": "0"
}
},
{
"address-family": {
"address-family-flags": {
"ifff-none": ""
},
"address-family-name": "inet",
"mtu": "Unlimited"
},
"encapsulation": "Unspecified",
"filter-information": "",
"if-config-flags": {
"iff-down": "",
"iff-snmp-traps": ""
},
"local-index": "548",
"name": "lo0.16385",
"policer-overhead": "",
"snmp-index": "22",
"traffic-statistics": {
"input-packets": "30648",
"output-packets": "30648"
}
}
],
"mtu": "Unlimited",
"name": "lo0",
"oper-status": "up",
"snmp-index": "6",
"traffic-statistics": {
"input-packets": "30740",
"output-packets": "30740"
}
}
}
}
},
"comment": "",
"duration": 216.283,
"name": "get-interface-information",
"result": true,
"start_time": "12:51:20.431987"
}
},
"success": true
}