Topic: tech juniper jaut prev next

tech juniper jaut > Module 05: Ansible Intermediate

Module 05: Ansible Intermediate

Ansible uses SSH to connect to remote devices and copy and run Python code.

The Juniper implementation requires all tasks to run locally on the management server. Tasks connect to remote devices by NETCONF.

Ansible modules are written in Python. They are more like Python functions, not Python modules. In Ansible, the result is JSON. Modules are idempotent; they only make changes if required.

Ansible server files:

Juniper Ansible modules in the Ansible Module Library use the ncclient library to communicate over NETCONF. Previously, Juniper modules were only available in Ansible Galaxy. The new naming convention is ‘juniperjunos*’.

pip install junos-eznc
pip install jxmlease
ansible-galaxy install Juniper.junos

Playbooks reference ‘Juniper.junos’.

Ansible configuration file preference

It is possible to use Ansible without a configuration file as the defaults may be enough.

The inventory file lists hosts. Devices are in groups. Devices can be part of multiple groups. The group ‘all’ is always defined. The default location is ‘/etc/ansible/hosts’ and can be overridden by the configuration file or the ‘-i’ key.

[routers]
vmx-1

[spine]
spine[1:4].example.com

[leaf]
leaf[1:24].example.com

The ‘switches’ group includes all hosts that are part of the ‘spine’ and ‘leaf’ groups.

[switches:children]
spine
leaf

Ad-hoc commands can be run from the command line without a playbook.

ansible -m junos_facts -a "username=lab password=lab123" -c local <hostname>

Playbooks automate tasks on multiple devices. Here is an example using the Galaxy module. The ‘Juniper.junos’ role imports the Galaxy module. Use ‘connection: local’ to prevent Ansible attempting to copy the modules to the remote host.

---
- name: play1
  connection: local
  roles:
   - Juniper.junos
  vars_prompt:
   - name: USERNAME
     prompt: Username
     private: no
   - name: PASSWORD
     prompt: Password
     private: yes
  hosts: <list of hosts>
  tasks:
   - name: Get Junos device information
     juniper_junos_facts:
       user: "{{ USERNAME }}"
       passwd: "{{ PASSWORD }}"
     register: junos_facts
   - name: Print Junos facts
     debug:
       msg: "{{ junos_facts }}"

Execute a playbook with the ‘ansible-playbook’ utility, giving the playbook name as the argument.

If using the Ansible library module, omit the ‘roles’ section and change the task to the following;

  tasks:
   - name: Get Junos device information
     juniper_junos_facts:
       provider:
         username: "{{ USERNAME }}"
         password: "{{ PASSWORD }}"
     register: junos_facts

Use ‘–list-hosts’ to only list the hosts that will run each play (without running the play).

Use ‘–limit’ to limit execution of a playbook to just one device. This is useful for making a ‘canary’ deployment.

If execution fails, the hosts on which it failed are written to ‘.retry’. The playbook can be run again with ‘ansible-playbook book.yml –limit book.retry’.

Use ‘–check’ to do a dry run, without making changes. Not all Ansible modules support all arguments; it is best to try them in a test environment first.

Ansible Vault is used to store sensitive information. Once SSH keys have been stored, username and password information does not need to be stored in the plaintext playbook.

ansible-vault encrypt hello.yml
# prompt for password
ansible-vault view hello.yml

ansible-playbook hello.yml --ask-vault-pass
# or configure in ansible.cfg

Ansible provides a ‘when’ statement, which operates like ‘if’.

   - name: Check Version
     fail: msg="Incorrect Junos version"
     when: (junos_facts.facts.junos_info.re0.text != "...")

Similarly, use the ‘loop’ option to run a task multiple times.

   ...
     juniper_junos_command:
       command: "show interfaces {{item}} terse"
       ...
     loop:
      - ge-0/0/0
      - ge-0/0/1

‘until’ style loops are also supported. This approach is common for playbooks that provision large services.

     until: (condition)
     retries: 20
     delay: 5

Handlers are similar to tasks. They are referenced by tasks using the ‘notify’ key and only if a task makes a change. Handlers are executed in the order they are defined and are executed at most once per playbook run.

For example, a ‘commit’ handler might be notified if a task makes a config change, so that the change can be committed.

     tasks:
      - name: ...
        juniper_junos_config:
          ...
        notify: confirm commit
     handlers:
      - name: confirm commit
        juniper_junos_config:
          provider: "{{ provider }}"
          comment: "Confirming previous commit"
          commit: yes

Variables can be used to deal with differences between hosts. The order of precedence is groupvar/all, groupvars/, host_vars/, vars:, varsprompt:, varsfiles:, command line variables (‘-e’ option).

Playbooks use Jinja2 syntax for variable substitution, including filters. Use of advanced constructs is not recommended.

Ansible provides a number of special variables, including ‘inventory_hostname’ (used as the ‘host’ argument), ‘hostvars’ and ‘groups’.

Variables can be passed to modules in YAML using various syntax;

user: "{{ USERNAME }}"
user={{ USERNAME }}

The ‘assemble’ module can be used to assemble (by concatenation) multiple files.

To debug a playbook, use the ‘fail’ module to stop execution after a config file has been generated, but before it is pushed to a device.

- fail: msg="Fail"