Skip to content

Commit

Permalink
[CLI]: Fix broken commands (sonic-net#28)
Browse files Browse the repository at this point in the history
Fixed the following commands, as well as some other cleanup:
- show version
- show platform
- show lldp table
- show techsupport
  • Loading branch information
jleveque authored Apr 6, 2017
1 parent 2ca359b commit 52cff2a
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 14 deletions.
2 changes: 1 addition & 1 deletion scripts/generate_dump
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ usage() {
cat <<EOF
$0 [-xnvh]
Create a sonic_suport dump for debugging. Requires root privileges.
Create a SONiC system dump for support/debugging. Requires root privileges.
OPTIONS
-x
Expand Down
124 changes: 124 additions & 0 deletions scripts/lldpshow
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#!/usr/bin/env python

""" Script to list LLDP neighbors in a summary view instead of default detailed view
Example output:
admin@sonic:~$ lldpshow
Capability codes: (R) Router, (B) Bridge, (O) Other
LocalPort RemoteDevice RemotePortID Capability RemotePortDescr
------------ --------------------- ---------------- ----------- ----------------------------------------
Ethernet0 <neighbor0_hostname> Ethernet1/51 BR <my_hostname>:fortyGigE0/0
Ethernet4 <neighbor1_hostname> Ethernet1/51 BR <my_hostname>:fortyGigE0/4
Ethernet8 <neighbor2_hostname> Ethernet1/51 BR <my_hostname>:fortyGigE0/8
Ethernet12 <neighbor3_hostname> Ethernet1/51 BR <my_hostname>:fortyGigE0/12
... ... ... ... ...
Ethernet124 <neighborN_hostname> Ethernet4/20/1 BR <my_hostname>:fortyGigE0/124
eth0 <mgmt_neighbor_name> Ethernet1/25 BR Ethernet1/25
-----------------------------------------------------
Total entries displayed: 33
"""

from __future__ import print_function
import subprocess
import re
import sys
import xml.etree.ElementTree as ET
from tabulate import tabulate

class Lldpshow(object):
def __init__(self):
self.lldpraw = None
self.lldpsum = {}
self.err = None
### So far only find Router and Bridge two capabilities in lldpctl, so any other capacility types will be read as Other
### if further capability type is supported like WLAN, can just add the tag definition here
self.ctags = {'Router': 'R', 'Bridge': 'B'}

def get_info(self):
"""
use 'lldpctl -f xml' command to gather local lldp detailed information
"""
lldp_cmd = 'lldpctl -f xml'
p = subprocess.Popen(lldp_cmd, stdout=subprocess.PIPE, shell=True)
(output, err) = p.communicate()
## Wait for end of command. Get return returncode ##
returncode = p.wait()
### if no error, get the lldpctl result
if returncode == 0:
self.lldpraw = output
else:
self.err = err

def parse_cap(self, capabs):
"""
capabilities that are turned on for each interface
"""
capability = ""
for cap in capabs:
if cap.attrib['enabled'] == 'on':
captype = cap.attrib['type']
if captype in self.ctags.keys():
capability += self.ctags[captype]
else:
capability += 'O'
return capability

def parse_info(self):
"""
Parse the lldp detailed infomation into dict
"""
if self.lldpraw is not None:
neis = ET.fromstring(self.lldpraw)
intfs = neis.findall('interface')
for intf in intfs:
l_intf = intf.attrib['name']
self.lldpsum[l_intf] = {}
chassis = intf.find('chassis')
capabs = chassis.findall('capability')
capab = self.parse_cap(capabs)
self.lldpsum[l_intf]['r_name'] = chassis.find('name').text
remote_port = intf.find('port')
self.lldpsum[l_intf]['r_portid'] = remote_port.find('id').text
rmt_desc = remote_port.find('descr')
if rmt_desc is not None:
self.lldpsum[l_intf]['r_portname'] = rmt_desc.text
else:
self.lldpsum[l_intf]['r_portname'] = ''
self.lldpsum[l_intf]['capability'] = capab

def sort_sum(self, summary):
""" Sort the summary information in the way that is expected(natural string)."""
alphanum_key = lambda key: [re.findall('[A-Za-z]+',key) + [int(port_num) for port_num in re.findall('\d+',key)]]
return sorted(summary, key=alphanum_key)


def display_sum(self):
"""
print out summary result of lldp neighbors
"""
if self.lldpraw is not None:
lldpstatus = []
print ('Capability codes: (R) Router, (B) Bridge, (O) Other')
header = ['LocalPort', 'RemoteDevice', 'RemotePortID', 'Capability', 'RemotePortDescr']
sortedsum = self.sort_sum(self.lldpsum)
for key in sortedsum:
lldpstatus.append([ key, self.lldpsum[key]['r_name'], self.lldpsum[key]['r_portid'], self.lldpsum[key]['capability'], self.lldpsum[key]['r_portname']])
print (tabulate(lldpstatus, header))
print ('-'.rjust(50, '-'))
print ('Total entries displayed: ', len(self.lldpsum))
elif self.err is not None:
print ('Error:',self.err)

def main():
try:
lldp = Lldpshow()
lldp.get_info()
lldp.parse_info()
lldp.display_sum()
except Exception as e:
print(e.message, file=sys.stderr)
sys.exit(1)

if __name__ == "__main__":
main()
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
'sonic_cli': ['aliases.ini']
},
scripts=[
'scripts/aclshow',
'scripts/boot_part',
'scripts/coredump-compress',
'scripts/decode-syseeprom',
'scripts/fast-reboot',
'scripts/generate_dump',
'scripts/lldpshow',
'scripts/portstat',
'scripts/sfputil',
'scripts/teamshow',
'scripts/aclshow',
],
data_files=[
('/etc/bash_completion.d', ['data/etc/bash_completion.d/show'])
Expand Down
49 changes: 37 additions & 12 deletions sonic_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ class AliasedGroup(DefaultGroup):
"""

def get_command(self, ctx, cmd_name):
global _config

# If we haven't instantiated our global config, do it now and load current config
if _config is None:
global _config
_config = Config()

# Load our config file
Expand Down Expand Up @@ -171,12 +171,12 @@ def portchannel():
# 'lldp' group ####
#

@cli.group(cls=AliasedGroup, default_if_no_args=True)
@cli.group(cls=AliasedGroup, default_if_no_args=False)
def lldp():
pass

# Default 'lldp' command (called if no subcommands or their aliases were passed)
@lldp.command(default=True)
@lldp.command()
@click.argument('interfacename', required=False)
def neighbors(interfacename):
"""Show LLDP neighbors"""
Expand Down Expand Up @@ -229,10 +229,28 @@ def summary():
# 'platform' group ####
#

@cli.group(invoke_without_command=True)
@cli.group(cls=AliasedGroup, default_if_no_args=False)
def platform():
pass

@platform.command()
def summary():
"""Show hardware platform information"""
run_command("platform-detect")
click.echo("")

PLATFORM_TEMPLATE_FILE = "/tmp/cli_platform.j2"
PLATFORM_TEMPLATE_CONTENTS = "Platform: {{ platform }}\n" \
"HwSKU: {{ minigraph_hwsku }}\n" \
"ASIC: {{ asic_type }}"

# Create a temporary Jinja2 template file to use with sonic-cfggen
f = open(PLATFORM_TEMPLATE_FILE, 'w')
f.write(PLATFORM_TEMPLATE_CONTENTS)
f.close()

command = "sonic-cfggen -m /etc/sonic/minigraph.xml -y /etc/sonic/sonic_version.yml -t {0}".format(PLATFORM_TEMPLATE_FILE)
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
click.echo(p.stdout.read())

# 'syseeprom' subcommand ####
@platform.command()
Expand Down Expand Up @@ -275,19 +293,26 @@ def default(process, tail, follow):
def version():
"""Show version information"""
click.echo("")
command = 'cat /etc/ssw/sysDescription'

VERSION_TEMPLATE_FILE = "/tmp/cli_version.j2"
VERSION_TEMPLATE_CONTENTS = "SONiC Software Version: SONiC.{{ build_version }}\n" \
"Distribution: Debian {{ debian_version }}\n" \
"Kernel: {{ kernel_version }}"

# Create a temporary Jinja2 template file to use with sonic-cfggen
f = open(VERSION_TEMPLATE_FILE, 'w')
f.write(VERSION_TEMPLATE_CONTENTS)
f.close()

command = "sonic-cfggen -y /etc/sonic/sonic_version.yml -t {0}".format(VERSION_TEMPLATE_FILE)
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
click.echo(p.stdout.read())
click.echo("")

click.echo("Docker images:")
command = 'docker images --format "table {{.Repository}}\\t{{.Tag}}\\t{{.ID}}\\t{{.Size}}"'
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
click.echo(p.stdout.read())

command = 'uptime -p'
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
click.echo("Kernel uptime " + p.stdout.read())


#
# 'environment' command ###
Expand Down Expand Up @@ -333,7 +358,7 @@ def users():
@cli.command()
def techsupport():
"""Gather information for troubleshooting"""
run_command('acs_support -v')
run_command('generate_dump -v')


#
Expand Down

0 comments on commit 52cff2a

Please sign in to comment.