Automação de rede com Ansible: módulo de comando

Falando em cenários típicos de automação de rede, não se pode prescindir de um conjunto de módulos de comando. Graças a esses módulos, o Ansible permite executar comandos em equipamentos de rede como se você os estivesse inserindo diretamente no console. Ao mesmo tempo, a saída dos comandos não apenas desliza na janela do terminal para afundar no esquecimento, mas pode ser salva e usada no futuro. Ele pode ser gravado em variáveis, analisado para uso em tarefas subseqüentes ou salvo para o futuro em variáveis ​​de host.



O objetivo desta publicação é mostrar que qualquer tarefa repetitiva de gerenciamento de rede pode ser automatizada e que o Ansible não apenas permite gerenciar configurações, mas também ajuda a se livrar da rotina e economizar tempo.

Vamos analisar as maneiras básicas de usar os módulos de comando de rede, incluindo salvar a saída de comandos usando o parâmetro register. Também consideraremos como dimensionar para vários dispositivos de rede usando hostvars e como organizar a execução condicional usando o parâmetro wait_for e três outros parâmetros relacionados: intervalo, novas tentativas e correspondência.

Diferentes plataformas de rede têm seus próprios módulos de comando, todos suportados no nível de extensão do Red Hat Ansible Engine Networking Add-on:

Plataformas de redeMódulos * os_command
Arista eoseos_command
Cisco IOS / IOS-XEios_command
Cisco IOS-XRiosxr_command
Cisco NX-OSnxos_command
Juniper Junosjunos_command
Vyosvyos_command

Noções básicas do módulo de comando


Considere um manual que simplesmente execute o comando show version usando o módulo eos_command:

--- - name: COMMAND MODULE PLAYBOOK hosts: eos connection: network_cli tasks: - name: EXECUTE ARISTA EOS COMMAND eos_command: commands: show version register: output - name: PRINT OUT THE OUTPUT VARIABLE debug: var: output 

Aqui temos duas tarefas e a primeira usa o módulo eos_command com um único parâmetro de comandos. Como executamos apenas um comando - show version - ele pode ser especificado na mesma linha que o próprio parâmetro de comandos. Se houver duas ou mais equipes, cada uma delas deverá ser colocada em uma linha separada após os comandos:. Neste exemplo, usamos a palavra - chave register para salvar a saída do comando show version. O parâmetro register (pode ser usado em qualquer tarefa Ansible) define a variável em que a saída da nossa tarefa será salva, para que possa ser usada posteriormente. No nosso exemplo, essa variável é chamada de saída.

A segunda tarefa em nosso exemplo usa o módulo de depuração para exibir o conteúdo da variável de saída recém-criada. Ou seja, são os mesmos dados que você veria na interface da linha de comandos no dispositivo EOS se você inserisse "show version" lá. A diferença é que nosso manual irá mostrá-los na janela do terminal em que você o inicia. Como você pode ver, o módulo de depuração facilita a verificação de variáveis ​​Ansible.

Aqui está o resultado do nosso manual:

 PLAY [eos] ************************************************************************* TASK [execute Arista eos command] ************************************************** ok: [eos] TASK [print out the output variable] *********************************************** ok: [eos] => { "output": { "changed": false, "failed": false, "stdout": [ "Arista vEOS\nHardware version: \nSerial number: \nSystem MAC address: 0800.27ec.005e\n\nSoftware image version: 4.20.1F\nArchitecture: i386\nInternal build version: 4.20.1F-6820520.4201F\nInternal build ID: 790a11e8-5aaf-4be7-a11a-e61795d05b91\n\nUptime: 1 day, 3 hours and 23 minutes\nTotal memory: 2017324 kB\nFree memory: 1111848 kB" ], "stdout_lines": [ [ "Arista vEOS", "Hardware version: ", "Serial number: ", "System MAC address: 0800.27ec.005e", "", "Software image version: 4.20.1F", "Architecture: i386", "Internal build version: 4.20.1F-6820520.4201F", "Internal build ID: 790a11e8-5aaf-4be7-a11a-e61795d05b91", "", "Uptime: 1 day, 3 hours and 23 minutes", "Total memory: 2017324 kB", "Free memory: 1111848 kB" ] ] } } PLAY RECAP ************************************************************************* eos : ok=2 changed=0 unreachable=0 failed=0 

Como pode ser visto na captura de tela, nossas tarefas foram bem-sucedidas. Como a primeira tarefa usa o nível de detalhe padrão das mensagens, simplesmente diz que o host eos concluiu a tarefa com o resultado ok, destacando o sucesso da execução em verde. A segunda tarefa, com o módulo debug, retorna a saída do comando executado, exibindo as mesmas informações em dois formatos:

  • stdout
  • stdout_lines

A seção stdout mostra a mesma coisa que você veria na interface da linha de comandos do dispositivo, mas na forma de uma linha longa. E a seção stdout_lines divide essa saída em linhas para facilitar a leitura. Cada item desta lista é uma linha separada na saída do comando.

Compare a saída do comando no dispositivo e no Ansible:

Saída da equipe no Arista EOSstdout_lines no Ansible
eos> show vers
Arista vEOS
Versão do hardware:
Número de série:
Endereço MAC do sistema: 0800.27ec.005e

Versão da imagem do software: 4.20.1F
Arquitetura: i386
Versão de compilação interna: 4.20.1F-6820520.4201F
ID da compilação interna: 790a11e8-5aaf-4be7-a11a-e61795d05b91

Tempo de atividade: 1 dia, 3 horas e 56 minutos
Memória total: 2017324 kB
Memória livre: 1116624 kB
"Stdout_lines": [
[
"Arista vEOS",
"Versão do hardware:",
"Número de série:",
"Endereço MAC do sistema: 0800.27ec.005e",
"",
"Versão da imagem do software: 4.20.1F",
"Arquitetura: i386",
"Versão de compilação interna:
4.20.1F-6820520.4201F ",
"ID da versão interna:
790a11e8-5aaf-4be7-a11a-e61795d05b91 ",
"",
"Tempo de atividade: 1 dia, 3 horas e 23 minutos",
"Memória total: 2017324 kB",
"Memória livre: 1111848 kB"
]

Se você conhece JSON e YAML, provavelmente já prestou atenção em uma singularidade: stdout_lines começa com dois colchetes de abertura:

 "stdout_lines": [ [ 

Dois colchetes de abertura indicam que stdout_lines realmente retorna uma lista de listas de linhas. Se você modificar ligeiramente nossa tarefa de depuração, esse chip poderá ser usado para exibir seletivamente os resultados do comando. Como existe apenas uma lista de linhas em nossa lista, essa lista é chamada zero (na verdade, é a primeira, mas a contagem vai do zero). Agora vamos ver como extrair uma linha separada dela, por exemplo, Endereço MAC do Sistema. Na saída do comando, essa linha é a quarta de uma linha, mas, como contamos do zero, precisamos da linha 3 da lista 0, ou seja: output.stdout_lines [0] [3].

  - name: print out a single line of the output variable debug: var: output.stdout_lines[0][3]   debug-   : TASK [print out a single line of the output variable] ****************************** ok: [eos] => { "output.stdout_lines[0][3]": "System MAC address: 0800.27ec.005e" } 

Qual é o objetivo da numeração da lista e por que é necessário? O fato é que, dentro da mesma tarefa, você pode executar várias equipes, por exemplo, assim (aqui temos três equipes):

 --- - hosts: eos connection: network_cli tasks: - name: execute Arista eos command eos_command: commands: - show version - show ip int br - show int status register: output - name: print out command debug: var: output.stdout_lines 

Aqui está a aparência da saída:

  "output.stdout_lines": [ [ "Arista vEOS", "Hardware version: ", "Serial number: ", "System MAC address: 0800.27ec.005e", "", "Software image version: 4.20.1F", "Architecture: i386", "Internal build version: 4.20.1F-6820520.4201F", "Internal build ID: 790a11e8-5aaf-4be7-a11a-e61795d05b91", "", "Uptime: 1 day, 4 hours and 20 minutes", "Total memory: 2017324 kB", "Free memory: 1111104 kB" ], [ "Interface IP Address Status Protocol MTU", "Ethernet1 172.16.1.1/24 up up 1500", "Management1 192.168.2.10/24 up up 1500" ], [ "Port Name Status Vlan Duplex Speed Type Flags", "Et1 connected routed full unconf EbraTestPhyPort ", "Et2 connected 1 full unconf EbraTestPhyPort ", "Et3 connected 1 full unconf EbraTestPhyPort ", "Ma1 connected routed a-full a-1G 10/100/1000" ] ] 

Aqui, o número da lista zero é a saída do comando show version, a lista número um é a saída show ip int br, a lista número dois é a saída show int status. Ou seja, o número da lista é determinado pela ordem em que os comandos são executados.

Equipes Arista EOSListas de saída correspondentes
mostrar versãooutput.stdout_lines [0]
mostre o IP int broutput.stdout_lines [1]
mostre o status intoutput.stdout_lines [2]

Escala do módulo de comando: variáveis ​​do host


E o que acontece se você executar o manual em vários dispositivos ao mesmo tempo?



Para ser exclusiva, a variável de saída é armazenada como uma variável de host para cada host no inventário. Se tivermos três switches e executarmos nosso manual sobre eles, obteremos a variável de saída para cada host único. Suponha que precisamos do endereço IP do comando show ip int br para a porta Ethernet1 no switch03. Como show ip int br é o segundo comando executado como parte da tarefa e os dados Ethernet1 estão contidos na segunda linha de sua saída, precisaremos escrever stdout_lines [1] [1]. Para acessar as variáveis ​​de um host específico, usamos a palavra-chave hostvars e pesquisamos o host que precisamos pelo nome.

Veja como fazê-lo:

  - name: debug hostvar debug: var: hostvars["switch03"].output.stdout_lines[1][1] 

Como resultado, a saída contém exatamente o que precisamos:

 TASK [debug hostvar] *************************************************************** ok: [switch03] => { "hostvars[\"switch03\"].output.stdout_lines[1][1]": "Ethernet1 172.16.1.3/24 up up 1500" } 

Por padrão, a tarefa usa as variáveis ​​do host atual, mas o hostvars permite acessar diretamente as variáveis ​​de outro host.

Condições em tarefas com módulos de comando: parâmetro wait_for


O parâmetro wait_for permite implementar uma verificação de condição imediatamente após a execução do comando. Por exemplo, para que a tarefa seja considerada concluída com êxito apenas se a saída do comando de verificação de status contiver determinado texto. Por padrão, o parâmetro wait_for não é usado, portanto, a tarefa é executada apenas uma vez, como nos exemplos acima. Porém, se você a definir explicitamente, a tarefa será reiniciada até que a condição seja atendida ou o limite de tentativas seja excedido (há 10 por padrão). Se você habilitar o log de comandos, poderá ver que, no manual abaixo (especialmente escrito para que a condição nunca ocorra), tudo acontece exatamente assim.

 --- - hosts: eos connection: network_cli tasks: - name: execute Arista eos command eos_command: commands: - show int status wait_for: - result[0] contains DURHAM 

Este manual executará o comando show int status 10 vezes, porque sua saída nunca conterá uma linha DURHAM.

Você pode verificar isso usando o comando show logging:

 Mar 24 20:33:52 eos Aaa: %ACCOUNTING-6-CMD: admin vty6 192.168.2.1 stop task_id=17 start_time=1521923632.5 timezone=UTC service=shell priv-lvl=15 cmd=show interfaces status Mar 24 20:33:53 eos Aaa: %ACCOUNTING-6-CMD: admin vty6 192.168.2.1 stop task_id=18 start_time=1521923633.71 timezone=UTC service=shell priv-lvl=15 cmd=show interfaces status Mar 24 20:33:54 eos Aaa: %ACCOUNTING-6-CMD: admin vty6 192.168.2.1 stop task_id=19 start_time=1521923634.81 timezone=UTC service=shell priv-lvl=15 cmd=show interfaces status Mar 24 20:33:55 eos Aaa: %ACCOUNTING-6-CMD: admin vty6 192.168.2.1 stop task_id=20 start_time=1521923635.92 timezone=UTC service=shell priv-lvl=15 cmd=show interfaces status Mar 24 20:33:56 eos Aaa: %ACCOUNTING-6-CMD: admin vty6 192.168.2.1 stop task_id=21 start_time=1521923636.99 timezone=UTC service=shell priv-lvl=15 cmd=show interfaces status Mar 24 20:33:58 eos Aaa: %ACCOUNTING-6-CMD: admin vty6 192.168.2.1 stop task_id=22 start_time=1521923638.07 timezone=UTC service=shell priv-lvl=15 cmd=show interfaces status Mar 24 20:33:59 eos Aaa: %ACCOUNTING-6-CMD: admin vty6 192.168.2.1 stop task_id=23 start_time=1521923639.22 timezone=UTC service=shell priv-lvl=15 cmd=show interfaces status Mar 24 20:34:00 eos Aaa: %ACCOUNTING-6-CMD: admin vty6 192.168.2.1 stop task_id=24 start_time=1521923640.32 timezone=UTC service=shell priv-lvl=15 cmd=show interfaces status Mar 24 20:34:01 eos Aaa: %ACCOUNTING-6-CMD: admin vty6 192.168.2.1 stop task_id=25 start_time=1521923641.4 timezone=UTC service=shell priv-lvl=15 cmd=show interfaces status Mar 24 20:34:02 eos Aaa: %ACCOUNTING-6-CMD: admin vty6 192.168.2.1 stop task_id=26 start_time=1521923642.47 timezone=UTC service=shell priv-lvl=15 cmd=show interfaces status 

Agora, vejamos um exemplo de um manual real, no qual tudo está configurado para estabelecer uma vizinhança OSPF (adjacência) com um dispositivo que não seja o comando ip ospf area. Aplicamos esse comando e, em seguida, usamos o parâmetro wait_for para verificar a presença da palavra FULL na saída: se estiver lá, a vizinhança foi estabelecida com sucesso. Se CHEIO não aparecer em 10 tentativas, a tarefa falhará.

 --- - hosts: eos connection: network_cli tasks: - name: turn on OSPF for interface Ethernet1 eos_config: lines: - ip ospf area 0.0.0.0 parents: interface Ethernet1 - name: execute Arista eos command eos_command: commands: - show ip ospf neigh wait_for: - result[0] contains FULL 

Execute este manual usando o comando ansible-playbook:

 ➜ ansible-playbook ospf.yml PLAY [eos] ********************************************************************************************* TASK [turn on OSPF for interface Ethernet1] ******************************************************* changed: [eos] TASK [execute Arista eos command] **************************************************************** ok: [eos] PLAY RECAP ****************************************************************************************** eos : ok=2 changed=1 unreachable=0 failed=0 

Observamos a linha de comando e vemos que o manual foi bem-sucedido:

 eos#show ip ospf neigh Neighbor ID VRF Pri State Dead Time Address Interface 2.2.2.2 default 1 FULL/DR 00:00:33 172.16.1.2 Ethernet1 

Além de contém, você pode usar os seguintes operadores de comparação:

  • eq: - é igual
  • neq: - não é igual
  • gt: - mais
  • ge: - maior ou igual a
  • lt: - menos
  • le: - menor ou igual a

Além disso, junto com wait_for, você pode usar três parâmetros adicionais (descritos em detalhes na documentação dos módulos):

ParâmetroDescrição do produto
intervaloTempo entre repetições de uma equipe.
tentativasMáx. o número de repetições antes que a tarefa seja concluída com um erro ou a condição seja atendida.
combinarA coincidência de todas as condições ou pelo menos uma.

Vamos nos aprofundar mais detalhadamente no parâmetro match:

  - name: execute Arista eos command eos_command: commands: - show ip ospf neigh match: any wait_for: - result[0] contains FULL - result[0] contains 172.16.1.2 

Quando match: any for especificado, a tarefa será considerada bem-sucedida se o resultado contiver FULL ou 172.16.1.2. Se match: all for especificado, o resultado deverá conter FULL e 172.16.1.2. Por padrão, match: all é usado, porque se você prescreve várias condições, provavelmente deseja que elas sejam executadas todas, e não pelo menos uma.

Quando pode corresponder: algum é útil? Suponha que você precise verificar se o centro de dados possui uma conexão bidirecional com a Internet. E o data center está conectado a cinco provedores de Internet diferentes, cada um com sua própria conexão BGP. Um manual pode verificar todas essas cinco conexões e, se pelo menos uma delas funcionar, e não as cinco, informar que tudo está em ordem. Lembre-se de que any é um OR lógico e tudo é um AND lógico.

ParâmetroDescrição do produto
match: any"OR" lógico
É necessária pelo menos uma condição
match: all"E" lógico
Todas as condições necessárias

Condições negativas: construindo a lógica inversa


Às vezes, é importante não o que está na conclusão, mas o que não está lá. Aqui, é claro, é sempre tentador usar o operador de comparação neq, mas para alguns cenários com condições negativas, existem opções melhores. Por exemplo, se você precisar inverter a instrução contains (do tipo "a saída do comando não deve conter tal e tal"), você pode usar a palavra-chave register para salvar a saída e processá-la na próxima tarefa usando a expressão when . Ou, por exemplo, quando precisar interromper o manual, se as condições não forem atendidas, basta usar os módulos de falha ou declaração para sair especificamente com o erro. Quanto ao operador de comparação neq, é útil apenas quando você pode extrair o valor exato da saída (por exemplo, de um par de valores-chave ou de JSON), e não apenas uma sequência ou uma lista de sequências. Caso contrário, será realizada a comparação caractere por seqüência de caracteres.

O que vem a seguir


Leia a documentação sobre como trabalhar com a saída de comandos nos módulos de rede. Ele fornece exemplos úteis do uso de ge, le e outras condições ao trabalhar com saída no formato JSON em plataformas de rede específicas.

Source: https://habr.com/ru/post/pt438312/


All Articles