3
\$\begingroup\$

The following piece of code does ssh do different servers and fetches the system info and display it to the user:

import paramiko

#list variables : ["IP_ADDR", "USERNAME", "PASSWD", "ROOT_PASSWD"]
ip_list = [
    ["192.168.11.44", "root", "****", "****"],
    ["192.168.11.8", "root", "****", "****"],
    ["192.168.11.30", "root", "****", "****"],
    ["192.168.11.6", "****", "****", "****"]
]
os_check_list = ["DISTRIB_DESCRIPTION"]
hard_disks = [
             'sda', 'sdb', 'sdc', 'sdd', 'sde', 'sdf', 'sdg',
             'sdh', 'sdi', 'sdj', 'sdk', 'sdl', 'sdm', 'sdn',
             'sdo', 'sdp', 'sdq', 'sdr', 'sds'
             ]
os = None

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

for ip in ip_list:
    ssh.connect(ip[0], username = ip[1], password = ip[2])

    print("\n \n   ************************************************************ \
           \n   IP ADDR = {}                SYSTEM INFO                           \
          \n  ******************************************************************". format(ip[0]))
    stdin, stdout, stderr = ssh.exec_command("cat /etc/*release")
    stdin.write(ip[3]+"\n")
    for line in stdout.readlines():
       if any(x in line for x in os_check_list):
           os_dist = line.split("=")
           os = os_dist[1]
           print(" Operating System is {}" .format(os))
    if not os:
           stdin, stdout, stderr = ssh.exec_command("cat /etc/system-release")
           for line in stdout.readlines():
               os = line
               print(" Operating System is {}" .format(os))

    os = None

    stdin, stdout, stderr = ssh.exec_command("sudo -k udisksctl status", get_pty = True)
    stdin.write(ip[3]+"\n")
    for line in stdout.readlines():
        if any(x in line for x in hard_disks):
            print(line)
        elif "command not found" in line:
            print("udisksctl not installed on target server")

    stdin, stdout, stderr = ssh.exec_command("sudo dmidecode -t 0 | grep -i version", get_pty = True)
    stdin.write(ip[3]+"\n")
    for line in stdout.readlines():
        if "Version" in line:
            print(line)
        elif "command not found" in line:
            print("dmidecode not installed on target server")

Even though this is working, I felt like I am running too many for loops for fetching the information. How to minimize these many loops?

\$\endgroup\$
  • 1
    \$\begingroup\$ You probably want to use ssh certificates instead of having the password in clear text in your script (you can find quite a few tutorials on how to do that) \$\endgroup\$ – ChatterOne Apr 24 '17 at 13:46
4
\$\begingroup\$

I would start by making your ip_list easier to understand. You could write a class for this, but using a collectins.namedtuple is a lot easier here:

from collections import namedtuple

Client = namedtuple("Client", "ip username passwd root_passwd")

clients = [Client("192.168.11.44", "root", "****", "****"),
           Client("192.168.11.8", "root", "****", "****"),
           Client("192.168.11.30", "root", "****", "****"),
           Client("192.168.11.6", "****", "****", "****")]

For the hard_disks, you could use the string module for all lowercase letters:

import string

HARD_DISKS = ['sd{}'.format(c) for c in string.ascii_lowercase[:19]]

For your repeated stuff, you could write a function:

def ssh_cmd(cmd, *args, root_passwd=None, **kwargs):
    stdin, stdout, stderr = ssh.exec_command(cmd, *args, **kwargs)
    if root_passwd is not None:
        stdin.write(root_passwd + "\n")
    return stdout.readlines()

This makes the rest slightly nicer to read. However, I would go further and put the other stuff in dedicated functions as well, which can then be put into a utils.py:

import string
from collections import namedtuple

Client = namedtuple("Client", "ip username passwd root_passwd")

HARD_DISKS = ['sd{}'.format(c) for c in string.ascii_lowercase[:19]]

def get_os(client):
    for line in ssh_cmd("cat /etc/*release", root_passwd=client.root_passwd):
       if any(x in line for x in os_check_list):
           # check for the obvious stuff first
           return line.split("=")[1]
    for os in ssh_cmd("cat /etc/system-release"):
       return os


def print_hard_disks(client, hard_disks=HARD_DISKS):
    for line in ssh_cmd("sudo -k udisksctl status", get_pty=True, root_passwd=client.root_passwd):
        if any(hd in line for hd in hard_disks):
            print(line)
        elif "command not found" in line:
            print("udisksctl not installed on target server")


def print_dmi_version(client):
    for line in ssh_cmd("sudo dmidecode -t 0 | grep -i version", get_pty=True, root_passwd=client.root_passwd):
        if "Version" in line:
            print(line)
        elif "command not found" in line:
            print("dmidecode not installed on target server")

It can then be imported in your main script:

import paramiko

from utils import Client, get_os, print_hard_disks, print_dmi_version

BANNER = """

******************************************************************

   IP ADDR = {}                SYSTEM INFO                           

******************************************************************"""

CLIENTS = [Client("192.168.11.44", "root", "****", "****"),
           Client("192.168.11.8", "root", "****", "****"),
           Client("192.168.11.30", "root", "****", "****"),
           Client("192.168.11.6", "****", "****", "****")]


def main(clients):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    for client in clients:
        ssh.connect(client.ip, username=client.username, password=client.passwd)

        print(BANNER.format(client.ip))
        print(" Operating System is {}" .format(get_os(client)))
        print_hard_disks(client)
        print_dmi_version(client)


if __name__ == "__main__":
    main(CLIENTS)
\$\endgroup\$
  • \$\begingroup\$ loved the way you have modified , especially HARD_DISKS, but I didn't get what advantage we are getting when we use namedtuple in our case. It looks a normal list and namedtuple are similar. \$\endgroup\$ – Here_2_learn Apr 25 '17 at 5:01
  • \$\begingroup\$ @Raghavendra It is exactly like a normal tuple (so also immutable). What you gain is that client.root_passwd is a lot easier to understand than ip[3]. \$\endgroup\$ – Graipher Apr 25 '17 at 5:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.