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

Platform Driver Development Framework (PDDF): Adding PDDF CLI utils #624

Merged
merged 3 commits into from
Dec 6, 2019
Merged
Show file tree
Hide file tree
Changes from 2 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/pddf_fanutil
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
_pddf_fanutil_completion() {
COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \
COMP_CWORD=$COMP_CWORD \
_PDDF_FANUTIL_COMPLETE=complete $1 ) )
return 0
}

complete -F _pddf_fanutil_completion -o default pddf_fanutil;
8 changes: 8 additions & 0 deletions data/etc/bash_completion.d/pddf_ledutil
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
_pddf_ledutil_completion() {
COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \
COMP_CWORD=$COMP_CWORD \
_PDDF_LEDUTIL_COMPLETE=complete $1 ) )
return 0
}

complete -F _pddf_ledutil_completion -o default pddf_ledutil;
8 changes: 8 additions & 0 deletions data/etc/bash_completion.d/pddf_psuutil
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
_pddf_psuutil_completion() {
COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \
COMP_CWORD=$COMP_CWORD \
_PDDF_PSUUTIL_COMPLETE=complete $1 ) )
return 0
}

complete -F _pddf_psuutil_completion -o default pddf_psuutil;
8 changes: 8 additions & 0 deletions data/etc/bash_completion.d/pddf_thermalutil
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
_pddf_thermalutil_completion() {
COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \
COMP_CWORD=$COMP_CWORD \
_PDDF_THERMALUTIL_COMPLETE=complete $1 ) )
return 0
}

complete -F _pddf_thermalutil_completion -o default pddf_thermalutil;
Empty file added pddf_fanutil/__init__.py
Empty file.
288 changes: 288 additions & 0 deletions pddf_fanutil/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
#!/usr/bin/env python
#
# main.py
#
# Command-line utility for interacting with FAN Controller in PDDF mode 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 = "fanutil"
PLATFORM_SPECIFIC_MODULE_NAME = "fanutil"
PLATFORM_SPECIFIC_CLASS_NAME = "FanUtil"

PLATFORM_ROOT_PATH = '/usr/share/sonic/device'
PLATFORM_ROOT_PATH_DOCKER = '/usr/share/sonic/platform'
SONIC_CFGGEN_PATH = '/usr/local/bin/sonic-cfggen'
HWSKU_KEY = 'DEVICE_METADATA.localhost.hwsku'
PLATFORM_KEY = 'DEVICE_METADATA.localhost.platform'
PDDF_FILE_PATH = '/usr/share/sonic/platform/pddf_support'
# Global platform-specific fanutil class instance
platform_fanutil = None
pddf_support = 0


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


def log_info(msg, also_print_to_console=False):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid redundant code here, same log_* functions defined in several files, maybe you can consider to use a common log class: https://github.com/Azure/sonic-buildimage/blob/master/src/sonic-daemon-base/sonic_daemon_base/daemon_base.py#L50

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making changes as per your comments. The daemon_base.py class you mentioned can not be used in sonic-utilities as it is not part of HOST. So I added similar class

syslog.openlog(SYSLOG_IDENTIFIER)
syslog.syslog(syslog.LOG_INFO, msg)
syslog.closelog()

if also_print_to_console:
click.echo(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:
click.echo(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:
click.echo(msg)


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

# Returns platform and HW SKU
def get_platform_and_hwsku():
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function also defined in several files, would like to suggest to find a way to reduce redundant code. for the below load_platform_*util functions, although they are loading different device utilities, I believe can define one common function and load different device util according to input.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made changes as per your comments

try:
proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-H', '-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, '-d', '-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 fanutil module from source
def load_platform_fanutil():
global platform_fanutil

# 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_fanutil_class = getattr(module, PLATFORM_SPECIFIC_CLASS_NAME)
platform_fanutil = platform_fanutil_class()
except AttributeError, e:
log_error("Failed to instantiate '%s' class: %s" % (PLATFORM_SPECIFIC_CLASS_NAME, str(e)), True)
return -2

return 0

def check_pddf_mode():
if os.path.exists(PDDF_FILE_PATH):
pddf_support = 1
return True
else:
pddf_support = 0
return False

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


# This is our main entrypoint - the main 'fanutil' command
@click.group()
def cli():
"""pddf_fanutil - Command line utility for providing FAN information"""

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

if not check_pddf_mode():
click.echo("PDDF mode should be supported and enabled for this platform for this operation")
sys.exit(1)

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

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

# 'numfans' subcommand
@cli.command()
def numfans():
"""Display number of FANs installed on device"""
click.echo(str(platform_fanutil.get_num_fans()))

# 'status' subcommand
@cli.command()
@click.option('-i', '--index', default=-1, type=int, help="the index of FAN")
def status(index):
"""Display FAN status"""
supported_fan = range(1, platform_fanutil.get_num_fans() + 1)
fan_ids = []
if (index < 0):
fan_ids = supported_fan
else:
fan_ids = [index]

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

for fan in fan_ids:
msg = ""
fan_name = "FAN {}".format(fan)
if fan not in supported_fan:
click.echo("Error! The {} is not available on the platform.\n" \
"Number of supported FAN - {}.".format(fan_name, platform_fanutil.get_num_fans()))
continue
presence = platform_fanutil.get_presence(fan)
if presence:
oper_status = platform_fanutil.get_status(fan)
msg = 'OK' if oper_status else "NOT OK"
else:
msg = 'NOT PRESENT'
status_table.append([fan_name, msg])

if status_table:
click.echo(tabulate(status_table, header, tablefmt="simple"))

# 'direction' subcommand
@cli.command()
@click.option('-i', '--index', default=-1, type=int, help="the index of FAN")
def direction(index):
"""Display FAN airflow direction"""
supported_fan = range(1, platform_fanutil.get_num_fans() + 1)
fan_ids = []
if (index < 0):
fan_ids = supported_fan
else:
fan_ids = [index]

header = ['FAN', 'Direction']
status_table = []

for fan in fan_ids:
msg = ""
fan_name = "FAN {}".format(fan)
if fan not in supported_fan:
click.echo("Error! The {} is not available on the platform.\n" \
"Number of supported FAN - {}.".format(fan_name, platform_fanutil.get_num_fans()))
continue
direction = platform_fanutil.get_direction(fan)
status_table.append([fan_name, direction])

if status_table:
click.echo(tabulate(status_table, header, tablefmt="simple"))

# 'speed' subcommand
@cli.command()
@click.option('-i', '--index', default=-1, type=int, help="the index of FAN")
def getspeed(index):
"""Display FAN speed in RPM"""
supported_fan = range(1, platform_fanutil.get_num_fans() + 1)
fan_ids = []
if (index < 0):
fan_ids = supported_fan
else:
fan_ids = [index]

header = ['FAN', 'Front Fan RPM', 'Rear Fan RPM']
status_table = []

for fan in fan_ids:
msg = ""
fan_name = "FAN {}".format(fan)
if fan not in supported_fan:
click.echo("Error! The {} is not available on the platform.\n" \
"Number of supported FAN - {}.".format(fan_name, platform_fanutil.get_num_fans()))
continue
front = platform_fanutil.get_speed(fan)
rear = platform_fanutil.get_speed_rear(fan)
status_table.append([fan_name, front, rear])

if status_table:
click.echo(tabulate(status_table, header, tablefmt="simple"))

# 'setspeed' subcommand
@cli.command()
@click.argument('speed', type=int)
def setspeed(speed):
"""Set FAN speed in percentage"""
if speed is None:
click.echo("speed value is required")
raise click.Abort()

status = platform_fanutil.set_speed(speed)
if status:
click.echo("Successful")
else:
click.echo("Failed")

@cli.group()
def debug():
"""pddf_fanutil debug commands"""
pass

@debug.command()
def dump_sysfs():
"""Dump all Fan related SysFS paths"""
status = platform_fanutil.dump_sysfs()

if status:
for i in status:
click.echo(i)



if __name__ == '__main__':
cli()
Empty file added pddf_ledutil/__init__.py
Empty file.
Loading