Skip to content
This repository has been archived by the owner on Oct 12, 2020. It is now read-only.

17. junos_syslog engine and Salt's reactor system end to end demo

Khelil Sator edited this page Jul 17, 2017 · 29 revisions

Reactor:

Reactors files are executed when an event with a specific tag comes on the master event bus.

reactor files to events mapping:

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 files:

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

how to orchestrate ansible using Saltstack event driven capabilities:

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.

salt master configuration

salt master configuration file:

# 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

engines_dirs

copy junos_syslog.py in one of the engines_dirs directory

# ls /srv/engines/
junos_syslog.py  junos_syslog.pyc

reactor file:

# 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

Junos syslog configuration:

junos_syslog engine ip address (salt master):

# ifconfig ens33 | grep "inet addr"
          inet addr:192.168.233.17  Bcast:192.168.233.255  Mask:255.255.255.0

junos device syslog configuration:

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;

Check the setup:

Check minion from master:

# salt minion_1 test.ping
minion_1:
    True

Check proxy from master:

# salt "vq*" test.ping
vqfx01:
    True

ansible content on minion_1

ansible playbook on minion_1:

# 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

ansible variables on minion_1:

$ more /srv/ansible/junos_get_config/vars.yml 
---
DEVICE_PASSWORD: Poclab123
USERNAME: pytraining

ansible inventory file on minion_1:

$ 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

Run the demo

minion_1 doesnt have the vqfx01 configuration file:

# 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

run this command on the master to capture traffic:

sudo tcpdump -i ens33 port 516 -XX

run this command on the master to watch the event bus:

salt-run state.event pretty=True

commit a configuration change on junos device:

vagrant@vqfx01# commit 
configuration check succeeds
commit complete

This is what is going to happen:

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

minion_1 has now the vqfx01 new configuration file:

# 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 {
....

tcpdump output on master shows the syslog:

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

salt internal events:

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
}

end to end Saltstack event driven automation demo

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 ...)

master configuration:

master configuration file:

# 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

reactor file:

# 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

state file:

# more /srv/salt/junos.sls 
get-interface-information:
  junos:
    - rpc
    - dest: /tmp/rpc.log
    - interface_name: lo0

Run the demo

The master doesn't have the file /tmp/rpc.log:

# more /tmp/rpc.log
more: stat of /tmp/rpc.log failed: No such file or directory

commit a configuration change on junos device:

vagrant@vqfx01# commit 
configuration check succeeds
commit complete

This is what is going to happen:

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

the master has now the file generated by the reactor

# 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>
...

internal events:

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
}

Clone this wiki locally