ANSIBLE - AUTOMAÇÃO DE BACKUPS
Tags: ansible, python, network, reintegris
Published: 2021-06-21 00:04:29

#### **Sobre Ansible**
> O Ansible é uma ferramenta de automação sem agente que gerencia hosts por meio de módulos através do protocolo SSH (por padrão). Uma vez instalado, o Ansible pode gerenciar remotamente, uma grande quantidade de máquinas, a partir de um servidor, ou seja, após instalar o Ansible, o servidor irá utilizar SSH para se comunicar com os hosts definidos em sua configuração.

> Ansible pode ser instalado e executado em qualquer máquina com Sistema Operacional baseado em UNIX (Red Hat, Debian, CentOS, macOS e qualquer um dos BSDs), não sendo possível utilizá-lo em Windows. É necessária a utilização do Python (Python 2 (versão 2.7) ou Python 3 (versão 3.5 e superior)).

#### **Sobre o projeto**
> Neste post iremos utilizar o Ansible para rotinas de Backup de ativos de rede (Network Automation), podemos utilizá-lo em uma rede com equipamentos de diversas marcas e modelos.

Neste cenário utilizaremos:
Host Ansible:
`Máquina Windows, com WSL (Windows Subsystem for Linux), utilizando "Ubuntu 20.04.1 LTS"`

![Ubuntu](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3aaeow8xv5ovsp9m97kc.PNG)

Hosts para backup:
`Switch CISCO L3 (Layer 3 - Camada 3)`
`Switch HUAWEI L2 (Layer 2 - camada 2)`
`Roteador MIKROTIK`
`Roteador HUAWEI`

#### **Topologia Utilizada**
![Topologia](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lk6xvmyib8m3uwuf66w2.png)
#### **Instalando Ansible**
Para instalar o Ansible no Ubuntu, iremos atualizar o sistema. 
```
$ sudo apt update
```
Instalar Python e Ansible
```
$ sudo apt install python3 python3-pip python-utils ansible -y
```

> Também podemos instalar através do gerenciador de pacotes do Python, o pip (como iremos trabalhar com o Python3, instalaremos e utilizaremos pip3)

```
$ sudo apt install python3 python3-pip python-utils  -y
$ pip3 install ansible
```

> Obs.: durante o processo de criação e testes, tivemos um problema com a versão do Ansible. 
No ambiente Ubuntu após o início dos testes, utilizamos a versão "ansible 2.9.6" e após a conclusão, tudo funcionou corretamente.
Posteriormente em novos testes em novas máquinas Ubuntu e CentOS, após a instalação do Ansible, a versão instalada foi a "ansible 2.9.18", e o mesmo código não estava funcionando, após diversas pesquisas, encontramos um problema de versão após o "ansible 2.9.7" para huawei (ce_config) e juniper (junos_facts e junos_config), para corrigir este problema, realizamos o downgrade da versão do ansible.

Checando versão do python, pip e ansible
```
$ python -V ou python --version
$ python3 -V ou python3 --version
$ ansible --version
```

Caso precise saber qual python ou ansible está utilizando
```
$ which python
$ which python3
$ which ansible
```

Downgrade do ansible
```
$ pip install ansible==[version]
$ pip3 install ansible==[version]
```

Exemplo:
```
$ pip install ansible==2.9.6
$ pip3 install ansible==2.9.6
```

Após a instalação e checagem das versões, iremos então para a utilização do Ansible em Network Automation, em rotinas de Backup.

Para este projeto utilizamos apenas dois arquivos: hosts (arquivo de inventário que contém os hosts e suas variáveis) e main.yml (playbook utilizado para realizar as tarefas).


#### **Criando local de trabalho**

Iniciando, criamos um diretório no home para o Ansible e dentro do diretório os arquivos:

Criaremos um diretório para trabalhar com o Ansible.
```
$ mkdir ansible
```

Entraremos no diretório criado.
```
$ cd ansible
```

Para checagem em qual diretório estamos.
```
$ pwd 
```

Criando o arquivo hosts e o main.yml
```
$ touch hosts main.yml
```

Listamos os arquivos dentro do diretório atual
```
$ ls
```

#### **Arquivo - hosts do Ansible**

Primeiro iremos tratar o arquivo hosts, neste arquivo iremos declarar os hosts que serão realizados os backups de forma automática.

O arquivo hosts possui o formato INI ou YAML, neste projeto iremos utilizar o formato INI.

Dentro do arquivo hosts iremos trabalhar com grupos, identificados através de colchetes “[]” e com variáveis que irão diferenciar características de cada grupo (caso necessário).

[all:vars] - criamos um grupo de "variáveis globais" que serão aplicadas em todos os hosts que não possuírem variáveis definidas exclusivamente. 
Este grupo não é obrigatório, as variáveis podem ficar “soltas” no topo do arquivo, mas preferimos utilizar este formato para organizar o arquivo.

As variáveis utilizadas neste grupo são:
```javascript
[all:vars]
ansible_user=USER_HERE // Usuário
ansible_password=USER_PASSWORD_HERE // Senha
ansible_become=yes // Permite receber privilégios de "enable"
ansible_become_password=USER_ROOT_PASSWORD_HERE // senha de "enable"
ansible_become_method=enable // para informar ao sistema operacional o privilégio de “enable”, para checagem de opções de become: “ansible-doc -t become -l”
ansible_port=22 // Porta de conexão SSH aos hosts
ansible_connection=network_cli // Plugin para acesso SSH aos hosts
gather_facts=false // Coletar fatos sobre o host remoto 
```

Os demais grupos foram criados para cada vendor/marca de equipamentos, os nomes foram escolhidos para melhor identificação e agrupamento de hosts: [hosts_cisco], [hosts_huawei],[hosts_mikrotik].

Podemos declarar os nomes dos hosts de três formas distintas, como mostradas abaixo, em nosso arquivo iremos utilizar a forma 1:

1- HOSTNAME ansible_host=HOST_IP_ADDRESS
```javascript
[hosts_cisco]
SW_CISCO_01 ansible_host=10.0.0.1
[hosts_mikrotik]
RT_MIKROTIK_01 ansible_host=10.0.0.4
```

2- HOST_IP_ADDRESS
```javascript
[hosts_huawei]
10.0.0.2 // SW_HUAWEI_01
```
3- HOSTNAME (esta opção precisamos declarar o IP e HOSTNAME no arquivo /etc/hosts)

Editando arquivo /etc/hosts (escolha seu editor vi/vim/nano/pico)
```
$ sudo vim /etc/hosts
```
Inserindo os dados dentro do arquivo /etc/hosts
```javascript
# ANSIBLE HOSTS
# IP_ADDRESS     HOSTNAME
10.0.0.3         RT_HUAWEI_01
```

Editando o arquivo hosts Ansible (escolha seu editor vi/vim/nano/pico)
```
$ vim hosts
```
```javascript
[hosts_huawei]
10.0.0.2 // SW_HUAWEI_01
RT_HUAWEI_01
```

> Após informados os endereços IPs e/ou Hostnames iremos configurar as variáveis locais para cada tipo de host (caso necessário).

> Neste caso utilizamos apenas a "variável local" `ansible_network_os`, que será utilizada para informar ao Ansible o SO (Sistema Operacional) do host, pois são equipamentos de marcas diferentes, neste caso utilizamos hosts Huawei, Cisco e Mikrotik.

`HUAWEI => ansible_network_os=ce`
`CISCO => ansible_network_os=ios`
`MIKROTIK => ansible_network_os=routeros`

> Caso possuam usuários, senhas e portas de acesso diferentes para cada equipamento, será necessário inserir as linhas de `ansible_user`, `ansible_password`, `ansible_become_password` e `ansible_port` em cada grupo, para que possam utilizar as credenciais corretas.

Exemplo do arquivo hosts finalizado:

```javascript
[all:vars]
ansible_user=admin
ansible_password=Password123
ansible_become_password=RootPassword123
ansible_port=22
ansible_connection=network_cli
ansible_become=yes
ansible_become_method=enable
gather_facts=false

[hosts_cisco]
SW_CISCO_01 ansible_host= 10.0.0.1

[hosts_cisco:vars]
ansible_network_os=ios

[hosts_huawei]
SW_HUAWEI_01 ansible_host= 10.0.0.2
RT_HUAWEI_01 ansible_host= 10.0.0.3

[hosts_huawei:vars]
ansible_network_os=ce

[hosts_mikrotik]
RT_MIKROTIK_01 ansible_host= 10.0.0.4

[hosts_mikrotik:vars]
ansible_network_os=routeros
```

#### **PLAY BOOK**
O arquivo playbook do Ansible é um arquivo YAML, este tipo de arquivo é iniciado por “---" e precisa de indentação (dois espaços para cada nível) para sua configuração, assim como precisa da extensão .yml no final do nome do arquivo.

Por padrão definimos um nome (`name`) para o grupo de atividades que queremos realizar, em seguida definimos os hosts (`hosts`) que farão parte daquele grupo de atividades, informamos para o Ansible que não queremos que ele colete fatos (`gather_facts`) sobre o host remoto e `ignore_errors` definimos para que caso haja algum erro o Ansible siga para a próxima tarefa, entre outras possíveis variáveis que podem ser inseridas nesta seção, em seguida iremos realizar as tarefas (tasks).

**Tarefas (tasks):**
Nesta seção onde serão executados os módulos e enviados comandos para os hosts remotos afim de realizar diversos tipos de ações, em nosso caso, iremos utilizar os módulos de rede para cada equipamento (`ios_config` para cisco, `ce_config` para huawei, `routeros_command` para mikrotik), realizaremos os backups dos hosts e salvaremos no diretório escolhido, renomeando da forma que acharmos melhor.

> Com o módulo de rede, seja ele `ios_config` ou `ce_config` podemos utilizar a função de backup (backup), sem que precisemos utilizar comandos da cli do vendor, para isso utilizamos esta função e suas opções (backup_options), neste caso apenas dir_path.

##### OBSERVAÇÕES IMPORTANTES SOBRE MIKROTIK
* O Mikrotik não possui um módulo Ansible de Redes para realização dos backups, como nos outros vendors, então iremos realizar os backups executando os comandos necessários no roteador via CLI e enviando para nosso servidor, via **FTP**.
* Os comandos (`commands`) executados no Mikrotik são próprios da CLI do vendor.
* As variáveis `vars` (`ftp_server_var`, `ftp_user_var` e `ftp_pass_var`) possuem os dados de autenticação do servidor `FTP`, a variável `date` será utilizada para renomear o arquivo salvo e a variável `caminho_arquivo` corresponde ao caminho para o arquivo desejado.
* Serão realizados dois backups, o arquivo.backup e arquivo.src

O diretório é definido através da opção dir_path, sendo este o local onde ficará salvo o arquivo no servidor e caso o diretório desejado não exista, o mesmo será criado.

O nome do arquivo por padrão tem o formato:
`HOSTNAME_config.YYYY-MM-DD@HH:MM:SS`

Para renomearmos os arquivos de backup, utilizamos as variáveis especiais do Ansible:
inventory_hostname - coleta o hostname do host
ansible_host - coleta o hostname e endereço IP do host


#### **PLAY BOOK - main.yml**

```yml
---
- name: Rotina de backup de hosts Cisco
  hosts: hosts_cisco
  gather_facts: no
  ignore_errors: yes

  tasks:
    - name: Backup host config - Cisco
      ios_config:
        backup: yes
        backup_options:
          dir_path: "/home/ansible/{{ inventory_hostname }}"

- name: Rotina de backup de hosts Huawei
  hosts: hosts_huawei
  gather_facts: no
  ignore_errors: yes

  tasks:
    - name: Backup host config - Huawei
      ce_config:
        backup: yes
        backup_options:
          dir_path: "/home/ansible/{{ ansible_host }}"

- name: Rotina de Backup de hosts Mikrotik
  hosts: hosts_mikrotik
  gather_facts: no
  ignore_errors: yes

  vars:
    ftp_server_var: "ftpipaddress"
    ftp_user_var: "ftpuser"
    ftp_pass_var: "ftppassword"
    date: "{{ lookup('pipe', 'date +%Y-%m-%d@%H:%M:%S') }}"
    caminho_arquivo: /home/ansible/{{ inventory_hostname }}
  tasks:
     - name: Create a directory if it does not exist
       ansible.builtin.file:
         path: "{{ caminho_arquivo }}"
         state: directory
         mode: '0755'

     - name: Backup host config (file.backup) - Mikrotik
       routeros_command:
         commands:
           - /system backup save name "{{ inventory_hostname }}"
           - /tool fetch address={{ ftp_server_var }} src-path={{ inventory_hostname }}.backup user={{ ftp_user_var }} password={{ ftp_pass_var }} upload=yes port=21 mode=ftp dst-path={{ caminho_arquivo }}/{{ inventory_hostname }}_config.{{ date }}.backup
           - :delay 5
           - /file remove "{{ inventory_hostname }}.backup"

     - name: Backup host config (file.src) - Mikrotik
       routeros_command:
         commands:
           - /export file={{ inventory_hostname }}
           - /tool fetch address={{ ftp_server_var }} src-path={{ inventory_hostname }}.rsc user={{ ftp_user_var }} password={{ ftp_pass_var }} upload=yes port=21 mode=ftp dst-path={{ caminho_arquivo }}/{{ inventory_hostname }}_config.{{ date }}.rsc
           - :delay 5
           - /file remove "{{ inventory_hostname }}.rsc"

```

### **Executando Ansible**

Neste tópico iremos executar o Ansible para que realize a rotina de backup dos hosts cadastrados.
O comando ansible-playbook irá utilizar arquivo hosts como inventário e utilizará o arquivo main.yml como playbook.

```
$ ansible-playbook -i hosts main.yml
```

Caso precisemos fazer troubleshooting ao executar o comando acima, podemos utilizar `-vvv`, para que sejam mostrados mais informações de saída.

```
$ ansible-playbook -i hosts main.yml -vvv
```

### **Saída do Ansible**
```javascript
-> ansible-playbook -i hosts main.yml

PLAY [Rotina de backup de hosts Cisco] *********************************************************************************
TASK [Backup host config - Cisco] **************************************************************************************
changed: [SW_CISCO_01]

PLAY [Rotina de backup de hosts Huawei] ********************************************************************************
TASK [Backup host config - Huawei] *************************************************************************************
changed: [SW_HUAWEI_01]
changed: [RT_HUAWEI_01]

PLAY [Rotina de Backup de hosts Mikrotik] ******************************************************************************
TASK [Create a directory if it does not exist] *************************************************************************
changed: [RT_MIKROTIK_01]

TASK [Backup host config (file.backup) - Mikrotik] *********************************************************************
ok: [RT_MIKROTIK_01]

TASK [Backup host config (file.src) - Mikrotik] ************************************************************************
ok: [RT_MIKROTIK_01]

PLAY RECAP *************************************************************************************************************
SW_CISCO_01              : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
SW_HUAWEI_01             : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
RT_HUAWEI_01             : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
RT_MIKROTIK_01           : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```


### **Agendamento dos Backups**

Neste tópico agendaremos a execução do Ansible via Cron, para que a rotina de backup, seja realizada toda sexta-feira as 23:59.
Para abrir editar, utilizamos o comando:
```
$ crontab -e
```
Dentro do arquivo, inserimos as informações abaixo:
```javascript
# AGENDAMENTO CRON MANUAL - 23:59 TODA SEXTA FEIRA (5)
59 23 * * 5 /bin/ansible-playbook -i /home/ansible/hosts /home/ansible/main.yml > /home/ansible/log_ansible.log 2>&1
```
Para listarmos os agendamentos que existem no nosso sistema.
```
$ crontab -l
```

## **Referências**
* ##### **Ansible**
https://docs.ansible.com/ansible/3/user_guide/windows_faq.html#:~:text=Can%20Ansible%20run%20on%20Windows,Subsystem%20for%20Linux%20(WSL).&text=The%20Windows%20Subsystem%20for%20Linux%20is%20not%20supported%20by%20Microsoft,be%20used%20for%20production%20systems.

* ##### Inventório Ansible (HOSTS FILE)
https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html

* ##### Variáveis Ansible
https://docs.ansible.com/ansible/latest/reference_appendices/special_variables.html

* ##### Instalar outra coleção do Ansible
https://github.com/ansible-collections/community.network

* ##### Problema de versão do Ansible
https://github.com/ansible/ansible/issues/69036

* ##### Downgrade de versão do Ansible
https://www.unixarena.com/2019/06/downgrading-ansible-engine-on-centos-7-rhel-7.html/

* ##### SSH ignorar key checking
https://stackoverflow.com/questions/32297456/how-to-ignore-ansible-ssh-authenticity-checking

* ##### Plugin Network_cli
https://docs.ansible.com/ansible/latest/collections/ansible/netcommon/network_cli_connection.html

* ##### Gather Facts
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/gather_facts_module.html


* #### **YAML**
https://www.youtube.com/watch?v=JOtIVGy1SgE
* #### **CRON**
https://www.digitalocean.com/community/tutorials/how-to-use-cron-to-automate-tasks-centos-8-pt
https://crontab.guru/every-week
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/cron_module.html