Skip to content

Commit

Permalink
[devutils] add utilities to handle devices (#2645)
Browse files Browse the repository at this point in the history
Summary:
Add utilities for device related oeprations.

Approach
What is the motivation for this PR?
Finding DUT information for access is a chore. Checking device status is not easy especially when comes to multiple devices.

How did you do it?
list hosts defined in an inventory.
ping hosts defined in an inventory.
ssh to host(s) define din an inventory.
(best executed with limited to one host)
console connect to host(s).
(not implemetned, for future expansion)
Signed-off-by: Ying Xie [email protected]

How did you verify/test it?
yinxi@str-serv-acs-14:~/src/sonic-mgmt/ansible$ ./devutils -h
usage: devutils [-h] [-6] [-a {list,ping,ssh,console}] [-c CATEGORY]
[-i INVENTORY] [-l LIMIT] [-u USER]

Device utilities

optional arguments:
-h, --help show this help message and exit
-6, --ipv6 Include IPv6
-a {list,ping,ssh,console}, --action {list,ping,ssh,console}
Action towards host(s): list, ping, ssh, console,
default list
-c CATEGORY, --category CATEGORY
Categories: all, sonic, ptf, pdu, default all
-i INVENTORY, --inventory INVENTORY
Categories: lab, etc, default lab
-l LIMIT, --limit LIMIT
Host: limit to a single dut host name, default all
-u USER, --user USER User: user account to login to host with, default
admin
  • Loading branch information
yxieca authored Dec 10, 2020
1 parent 08271c7 commit 4f5abea
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 0 deletions.
98 changes: 98 additions & 0 deletions ansible/devutils
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/usr/bin/env python

import argparse
import os
import subprocess
from inv_helpers import get_host_list


def run_cmd(cmd):
'''
@summary: Utility that runs a command in a subprocess
@param cmd: Command to be run
@return: stdout of the command run
@return: stderr of the command run
'''
out = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
stdout, stderr = out.communicate()
return out.returncode, stdout, stderr


def action_list(kind, host, details, parameters):
print('type {} host {} details {}'.format(kind, host, details))


def action_ping(kind, host, details, parameters):
if not details or type(details) != dict:
return

if 'ansible_host' in details:
addr = details['ansible_host']
rc, _, _ = run_cmd('ping -q -c 1 -w 1 {}'.format(addr))
if rc != 0:
print('host {} (type {}) is unreachable at address {}'.format(host, kind, addr))
if parameters['ipv6'] and 'ansible_hostv6' in details:
addr = details['ansible_hostv6']
rc, _, _ = run_cmd('ping -6 -q -c 1 -w 1 {}'.format(addr))
if rc != 0:
print('host {} (type {}) is unreachable at address {}'.format(host, kind, addr))


def action_ssh(kind, host, details, parameters):
if not details or type(details) != dict:
return

if 'ansible_host' in details:
addr = details['ansible_host']
os.system('ssh {}@{}'.format(parameters['user'], addr))


def action_console(kind, host, details, parameters):
print('Action console is not implemented')


def hosts_walker(parameters):
hosts = parameters['hosts']
limit = parameters['limit']
for key, val in hosts.items():
for host, details in val.items():
if limit == 'all' or limit == host:
parameters['action'](key, host, details, parameters)


def main():
parser = argparse.ArgumentParser(description='Device utilities')
parser.add_argument('-6', '--ipv6', help='Include IPv6', action='store_true',
required=False, default=False)
parser.add_argument('-a', '--action',
help='Action towards host(s): list, ping, ssh, console, default list',
type=str, required=False, default='list',
choices=['list', 'ping', 'ssh', 'console'])
parser.add_argument('-c', '--category', help='Categories: all, sonic, ptf, pdu, default all',
type=str, required=False, default='all')
parser.add_argument('-i', '--inventory', help='Categories: lab, etc, default lab',
type=str, required=False, default='lab')
parser.add_argument('-l', '--limit', help='Host: limit to a single dut host name, default all',
type=str, required=False, default='all')
parser.add_argument('-u', '--user', help='User: user account to login to host with, default admin',
type=str, required=False, default='admin')

args = parser.parse_args()

hosts = get_host_list(args.inventory, args.category)
actions = { 'list' : action_list,
'ping' : action_ping,
'ssh' : action_ssh,
'console' : action_console,
}
parameters = { 'hosts' : hosts,
'limit' : args.limit,
'action' : actions[args.action],
'user' : args.user,
'ipv6' : args.ipv6,
}
hosts_walker(parameters)


if __name__ == '__main__':
main()
27 changes: 27 additions & 0 deletions ansible/inv_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import yaml


def get_all_hosts(inventory):
hosts = {}
for key, val in inventory.items():
vtype = type(val)
if vtype == dict:
if 'hosts' in val:
hosts.update({ key : val['hosts'] })
else:
hosts.update(get_all_hosts(val))
return hosts


def get_host_list(inventory, category):
with open(inventory, 'r') as file:
inv = yaml.safe_load(file)

all_hosts = get_all_hosts(inv)
hosts = {}
for key, val in all_hosts.items():
if category == 'all' or category in key:
hosts.update({key : val})

return hosts

0 comments on commit 4f5abea

Please sign in to comment.