Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added PSU CLI #152

Merged
merged 3 commits into from
Nov 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions data/etc/bash_completion.d/psuutil
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
_psuutil_completion() {
COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \
COMP_CWORD=$COMP_CWORD \
_PSUUTIL_COMPLETE=complete $1 ) )
return 0
}

complete -F _psuutil_completion -o default psuutil;
Empty file added psuutil/__init__.py
Empty file.
192 changes: 192 additions & 0 deletions psuutil/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#!/usr/bin/env python
#
# main.py
#
# Command-line utility for interacting with PSU in SONiC
#

try:
import sys
import os
import subprocess
import click
import imp
import syslog
import types
import traceback
from tabulate import tabulate
except ImportError as e:
raise ImportError("%s - required module not found" % str(e))

VERSION = '1.0'

SYSLOG_IDENTIFIER = "psuutil"
PLATFORM_SPECIFIC_MODULE_NAME = "psuutil"
PLATFORM_SPECIFIC_CLASS_NAME = "PsuUtil"

PLATFORM_ROOT_PATH = '/usr/share/sonic/device'
PLATFORM_ROOT_PATH_DOCKER = '/usr/share/sonic/platform'
SONIC_CFGGEN_PATH = '/usr/local/bin/sonic-cfggen'
MINIGRAPH_PATH = '/etc/sonic/minigraph.xml'
HWSKU_KEY = "DEVICE_METADATA['localhost']['hwsku']"
PLATFORM_KEY = 'platform'

# Global platform-specific psuutil class instance
platform_psuutil = None


# ========================== Syslog wrappers ==========================


def log_info(msg, also_print_to_console=False):
syslog.openlog(SYSLOG_IDENTIFIER)
syslog.syslog(syslog.LOG_INFO, msg)
syslog.closelog()

if also_print_to_console:
print msg


def log_warning(msg, also_print_to_console=False):
syslog.openlog(SYSLOG_IDENTIFIER)
syslog.syslog(syslog.LOG_WARNING, msg)
syslog.closelog()

if also_print_to_console:
print msg


def log_error(msg, also_print_to_console=False):
syslog.openlog(SYSLOG_IDENTIFIER)
syslog.syslog(syslog.LOG_ERR, msg)
syslog.closelog()

if also_print_to_console:
print msg


# ==================== Methods for initialization ====================

# Returns platform and HW SKU
def get_platform_and_hwsku():
try:
proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-v', PLATFORM_KEY],
stdout=subprocess.PIPE,
shell=False,
stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
platform = stdout.rstrip('\n')

proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-m', MINIGRAPH_PATH, '-v', HWSKU_KEY],
stdout=subprocess.PIPE,
shell=False,
stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
hwsku = stdout.rstrip('\n')
except OSError, e:
raise OSError("Cannot detect platform")

return (platform, hwsku)


# Loads platform specific psuutil module from source
def load_platform_psuutil():
global platform_psuutil

# Get platform and hwsku
(platform, hwsku) = get_platform_and_hwsku()

# Load platform module from source
platform_path = ''
if len(platform) != 0:
platform_path = "/".join([PLATFORM_ROOT_PATH, platform])
else:
platform_path = PLATFORM_ROOT_PATH_DOCKER
hwsku_path = "/".join([platform_path, hwsku])

try:
module_file = "/".join([platform_path, "plugins", PLATFORM_SPECIFIC_MODULE_NAME + ".py"])
module = imp.load_source(PLATFORM_SPECIFIC_MODULE_NAME, module_file)
except IOError, e:
log_error("Failed to load platform module '%s': %s" % (PLATFORM_SPECIFIC_MODULE_NAME, str(e)), True)
return -1

try:
platform_psuutil_class = getattr(module, PLATFORM_SPECIFIC_CLASS_NAME)
platform_psuutil = platform_psuutil_class()
except AttributeError, e:
log_error("Failed to instantiate '%s' class: %s" % (PLATFORM_SPECIFIC_CLASS_NAME, str(e)), True)
return -2

return 0


# ==================== CLI commands and groups ====================


# This is our main entrypoint - the main 'psuutil' command
@click.group()
def cli():
"""psuutil - Command line utility for providing PSU status"""

if os.geteuid() != 0:
print "Root privileges are required for this operation"
sys.exit(1)

# Load platform-specific psuutil class
err = load_platform_psuutil()
if err != 0:
sys.exit(2)

# 'version' subcommand
@cli.command()
def version():
"""Display version info"""
click.echo("psuutil version {0}".format(VERSION))

# 'numpsus' subcommand
@cli.command()
def numpsus():
"Display the number of supported PSU in the device"
print(str(platform_psuutil.get_num_psus()))

# 'status' subcommand
@cli.command()
@click.option('-i', '--index', default=-1, type=int, help="the index of PSU")
@click.option('--textonly', is_flag=True, help="show the PSU status in a simple text format instead of table")
def status(index, textonly):
"""Display PSU status"""
supported_psu = range(1, platform_psuutil.get_num_psus() + 1)
psu_ids = []
if (index < 0):
psu_ids = supported_psu
else:
psu_ids = [index]

header = ['PSU', 'Status']
status_table = []

for psu in psu_ids:
msg = ""
psu_name = "PSU {}".format(psu)
if psu not in supported_psu:
status_table.append([psu_name, "NOT SUPPORTED"])
continue
presence = platform_psuutil.get_psu_presence(psu)
if presence:
oper_status = platform_psuutil.get_psu_status(psu)
msg = 'OK' if oper_status else "NOT OK"
else:
msg = 'NOT PRESENT'
status_table.append([psu_name, msg])

if textonly:
for status in status_table:
print status[0] + ':' + status[1]
else:
print tabulate(status_table, header, tablefmt="grid")

if __name__ == '__main__':
cli()
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def get_test_suite():
'debug',
'pfcwd',
'sfputil',
'psuutil',
'show',
'sonic_eeprom',
'sonic_installer',
Expand Down Expand Up @@ -61,6 +62,7 @@ def get_test_suite():
'debug = debug.main:cli',
'pfcwd = pfcwd.main:cli',
'sfputil = sfputil.main:cli',
'psuutil = psuutil.main:cli',
'show = show.main:cli',
'sonic-clear = clear.main:cli',
'sonic_installer = sonic_installer.main:cli',
Expand Down
11 changes: 11 additions & 0 deletions show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,17 @@ def syseeprom():
"""Show system EEPROM information"""
run_command("sudo decode-syseeprom")

# 'psustatus' subcommand ("show platform psustatus")
@platform.command()
@click.option('-i', '--index', default=-1, type=int, help="the index of PSU")
def psustatus(index):
"""Show PSU status information"""
command = "sudo psuutil status"

if index >= 0:
command += " -i {}".format(index)

run_command(command)

#
# 'logging' command ("show logging")
Expand Down
26 changes: 20 additions & 6 deletions sonic_psu/psu_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,33 @@ class PsuBase(object):
@abc.abstractmethod
def get_num_psus(self):
"""
Retrieves the number of PSUs available on the device
Retrieves the number of PSUs supported on the device
:return: An integer, the number of PSUs available on the device
:return: An integer, the number of PSUs supported on the device
"""
return 0

@abc.abstractmethod
def get_psu_status(self, index):
"""
Retrieves the oprational status of power supply unit (PSU) defined
by index <index>
Retrieves the operational status of power supply unit (PSU) defined
by index 1-based <index>
:param index: An integer, index of the PSU of which to query status
:return: Boolean, True if PSU is operating properly, False if PSU is faulty
:param index: An integer, 1-based index of the PSU of which to query status
:return: Boolean,
- True if PSU is operating properly: PSU is inserted and powered in the device
- False if PSU is faulty: PSU is inserted in the device but not powered
"""
return False

@abc.abstractmethod
def get_psu_presence(self, index):
"""
Retrieves the presence status of power supply unit (PSU) defined
by 1-based index <index>
:param index: An integer, 1-based index of the PSU of which to query status
:return: Boolean, True if PSU is plugged, False if not
"""
return False