paramiko - Python + SSH

Uma tarefa muito comum dos administradores de sistemas Linux, é executar o mesmo comando em vários servidores distintos. Isso, com o objetivo de aplicar um patch de segurança, instalar um novo pacote, efetuar alguma configuração e até mesmo padronizar configurações. Mas é possível também fazer estas configurações através do Python. Existe um módulo chamado paramiko, que foi criado justamente para fazer conexões via SSH. Então, neste artigo vou mostrar a vocês como usar este módulo.

[ Hits: 28.534 ]

Por: Alisson Machado em 29/09/2016


Introdução



Uma tarefa muito comum dos administradores de sistemas Linux, é executar o mesmo comando em vários servidores distintos. Isso, com o objetivo de aplicar um patchs de segurança, instalar um novo pacote, efetuar alguma configuração e até mesmo padronizar configurações.

Para isso, utilizamos ferramentas como:
  • Puppet
  • Ansible
  • Chef
  • Fabric

E entre outras, essas são as mais conhecidas.

Mas é possível também, fazer essas configurações através do Python. Existe um módulo chamado paramiko, que foi criado justamente para fazer conexões via SSH. Então, neste artigo vou mostrar a vocês como usar este módulo.

O primeiro passo, é instalar o paramiko:

# pip install paramiko

Agora, segue o script completo:

#!/usr/bin/python

from paramiko import SSHClient
import paramiko

class SSH:
    def __init__(self):
        self.ssh = SSHClient()
        self.ssh.load_system_host_keys()
        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.ssh.connect(hostname='127.0.0.1',username='root',password='SENHA_DE_ROOT')

    def exec_cmd(self,cmd):
        stdin,stdout,stderr = self.ssh.exec_command(cmd)
        if stderr.channel.recv_exit_status() != 0:
            print stderr.read()
        else:
            print stdout.read()

if __name__ == '__main__':
    ssh = SSH()
    ssh.exec_cmd("apt-get update")

Análise

Vamos entender o que faz esse script.

A primeira coisa a entender, é que foi criada uma classe chamada SSH, assim é possível facilitar algumas coisas, pois no método construtor, definido por "def __init__(self):", tem uma sequência de instruções para efetuar a conexão com um determinador servidor.

        self.ssh = SSHClient()

Essa linha faz a instância da classe SSHClient, que foi importada do módulo paramiko logo no topo do script.

from paramiko import SSHClient
import paramiko

Na sequência do construtor, foi definida a instrução:

        self.ssh.load_system_host_keys()

Essa instrução define que o paramiko vai ler todas as chaves cadastrados no arquivo "~/.ssh/known_hosts", assim, evitamos ter que ficar dando um "yes" ou "no" na hora de conectar em um servidor via SSH.

        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

Nessa linha, é definido o que fazer quando a chave de um servidor não é encontrada no "~/.ssh/known_hosts", então, foi definido o método "paramiko.AutoAddPolicy()", assim quando um servidor for acessado pela primeira vez o paramiko, automaticamente, vai aceitar a chave desse servidor e cadastrar no arquivo "known_hosts".

        self.ssh.connect(hostname='127.0.0.1',username='root',password='SENHA_DE_ROOT')

Nessa linha, definimos o servidor em que vamos conectar, o usuário e a senha, caso o seu acesso seja feito através de chaves, é possível omitir o username e o password, deixando somente a chave hostname com o IP do servidor que você quer conectar.

Por que todas essas instruções foram colocadas no método construtor? Dessa maneira, assim que for instanciado um objeto dessa classe SSH, a conexão já é feita automaticamente, então o método "exec_cmd" pode executar os comandos diretamente, sem a necessidade de ficar efetuando a conexão antes de executar qualquer comando.

Agora vamos analisar o método "exec_cmd".

O método "exec_cmd" tem apenas um parâmetro obrigatório, que é a variável "cmd", ela deve receber uma string com o comando que deve ser executado via SSH no servidor.

        stdin,stdout,stderr = self.ssh.exec_command(cmd)

A linha acima usa o próprio atributo ssh da classe SSH, que contém uma instância do SSHClient do paramiko que já está conectada ao servidor. Tudo isso foi realizado no construtor dessa classe, essa instância possui um método chamado "exec_command" que recebe uma string como parâmetro, que será o comando executado no servidor.

Quando o comando é executado, esse método retorna uma tupla com 3 valores:
  • Standard Input (Entrada padrão, normalmente uma entrada do teclado)
  • Standard Output (Saída padrão, o que aparece na tela)
  • Stander Error (Saída de Error, mensagem de erro mostrada na tela)

Todos foram abreviados como stdin, sdout, sdterr.

        if stderr.channel.recv_exit_status() != 0:
            print stderr.read()
        else:

Depois de recebidos os valores retornados pelo "exec_command", precisamos saber se o comando deu erro ou não, para isso foi feito esse if.

Na instrução "stderr.channel.recv_exit_status()", é verificado se o valor retornado da saída de erro é diferente de 0, se esse valor for diferente de zero, significa que um erro aconteceu. Por exemplo:
  • erro 127 (Command not found)
  • erro 1 (Erro ao executar o comando, pode ser um parâmetro invalido por exemplo)

Esses são os erros mais comuns.

Então, se for retornado um erro, a condição entra no primeiro bloco de instruções fazendo um print do erro do comando, caso contrário a saída padrão do comando será retornada na tela.

if __name__ == '__main__':
    ssh = SSH()
    ssh.exec_cmd("apt-get update")

Essas ultimas instruções são para testar o nosso script, a linha "if __name__ == '__main__':", diz que o bloco abaixo só será executado se o script for executado via linha de comando, caso você faça um import desse arquivo, essas instruções não serão executadas.

Na sequência, é instanciada a nossa classe SSH dentro da variável ssh, que acaba se tornando um objeto. Nesse momento, é feita a conexão com o servidor, uma vez que tudo foi definido no construtor da classe, então, logo abaixo em "ssh.exec_cmd("apt-get update")", é enviado o comando apt-get update para atualizar a base de dados do apt-get no servidor que quisermos, o script irá demorar um pouco e se o comando for executado com sucesso, na sua tela irá aparecer o resultado do comando executado.

Mais tutoriais estão em meu blog pessoal:
Obrigado \o

   

Páginas do artigo
   1. Introdução
Outros artigos deste autor

Vault: SSH com OneTimePassword

MongoDB Aggregation

Python - Threads

Python + ADB

Sockets em Python

Leitura recomendada

Redes definidas por Software com Mininet e POX - Criando meu primeiro Controlador

Como isolar seus projetos Python com virtualenv (ambiente virtual)

Threads - Importância dentro de um software

Python - Uma linguagem orientada a objetos

OAK: Câmera Open Source de Visão Computacional com AI

  
Comentários
[1] Comentário enviado por Arthur_Hoch em 01/10/2016 - 23:09h

Há um tempo atrás tinha feito uma coisa semelhante com Shell Script: https://www.vivaolinux.com.br/script/Manutencao-de-rede/

Mas acho que ninguém tinha entendido muito bem a utilização do script...

[2] Comentário enviado por LaisMD em 03/10/2016 - 11:44h

Ótimo artigo!

[3] Comentário enviado por mprandot em 04/10/2016 - 17:39h

Não desenvolvo em python, mas tenho que dizer : Que coisa linda!!
hauheu

[4] Comentário enviado por tonnytg em 06/10/2016 - 13:18h

Muito bom,
vou aplicar onde trabalho :D


Att,
Antonio Thomacelli Gomes
http://www.tonnytg.com.br
LPIC-2 Certified Linux Engineers


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts