Initial commit

This commit is contained in:
Neill Cox 2023-09-11 20:22:22 +10:00
commit 1b51716d1b
72 changed files with 8204 additions and 0 deletions

View file

@ -0,0 +1,308 @@
"""
This module containts all the entry points for the redfish cli tools.
It uses argparse to add subparses.
All of this is developed and tested against Dell iDRACs at the moment. At some
point in its future there will be a lot of refactoring to deal with other OEMs
I expect.
"""
import json
import logging
import sys
# PYTHON_ARGCOMPLETE_OK
import argcomplete
from redfish_cli import api
from redfish_cli.api import storage
import redfish_cli.cli.jobs
from .parsers import parse_args
from .utils import error, table, write_output
def chassis(args):
"""Entry point: get the list of chassis"""
details = api.chassis(args.server, args.username, args.password)
write_output(details, output_format=args.format)
def chassis_details(args):
"""Entry point: get the details of a chassis"""
details = api.chassis_details(
args.server, args.username, args.password, args.chassis
)
write_output(details, output_format=args.format)
def create_logical_volume(args):
"""Stub for the create logical volume command"""
try:
result = storage.create_logical_volume(args)
write_output(result)
except api.exceptions.InvalidRaidLevel as exc:
error(exc.msg)
except api.exceptions.LogicalVolumeCreationFailure as exc:
error(json.dumps(exc.json, indent=2))
def delete_logical_volume(args):
"""Delete a logical volume"""
logging.debug(args)
try:
result = api.storage.delete_logical_volume(args)
write_output(
result,
output_format=args.format,
)
# except api.exceptions.FailedToGetJobID as exc:
# details = exc.msg
# error_msg = (
# "An error has occured. The usual cause is that the disk "
# f"specified ({args.disk}) does not exist\n"
# f"{details}"
# )
# error(error_msg)
except api.exceptions.ResponseNotOK as exc:
details = exc.response.text
error_msg = (
"An error has occured. The usual cause is that the disk "
f"specified ({args.disk}) does not exist\n"
f"{details}"
)
error(error_msg)
def configure_logging(args):
"""Configure logging for the cli"""
log_format = (
"%(asctime)s - %(pathname)s:%(funcName)s - %(levelname)s - %(message)s"
)
logging.basicConfig(
filename="redfish.log",
level=getattr(logging, args.log_level.upper()),
format=log_format,
)
def get(args):
"""
Write the results of an arbitrary get on the specified url to stdout (using
print).
Currently it always dumps the results as json, but it should respect the
the output format in the args.
It will also just exit with an error message if a url requires
authentication and no username and password is supplied. This could be
handled better.
"""
url = args.url
if url is None:
url = ""
if args.expand:
url += "?$expand=.($levels=1)"
try:
result = api.utils.get(
args.server,
url,
username=args.username,
password=args.password,
)
write_output(
result,
output_format=args.format,
)
except api.exceptions.Unauthorized:
error("This url requires authentication\n")
except api.exceptions.ResponseNotOK as exc:
error(json.dumps(json.loads(exc.response.text), indent=2))
def logical_volume(args):
"""
Entry point: get the details of a logical volume.
By default the first logical drive in the first storage controller of the
first system.
"""
logging.debug(
"server: %s username: %s system: %s controller: %s drive: %s",
args.server,
args.username,
args.system,
args.controller,
args.drive,
)
try:
write_output(
storage.logical_volume_details(args),
output_format=args.format,
)
except api.exceptions.ResponseNotOK as exc:
error(json.dumps(json.loads(exc.response.text), indent=2))
def logical_volumes(args):
"""
Entry point: get the list of logical volumes attached to a storage
controller (which in turn is part of a system)
"""
try:
result = storage.list_logical_volumes(args)
write_output(
result,
output_format=args.format,
)
except api.exceptions.ResponseNotOK as exc:
error(json.dumps(json.loads(exc.response.text), indent=2))
def physical_volume(args):
"""
Entry point: get the list of physical volumes attached to a storage
controller (which in turn is part of a system)
"""
data = storage.physical_volume(args)
# if args.format == "text":
# result = "\n".join([ f"{x['system']} {x['drive']}" for x in data])
# elif args.format == "json":
# result = data
# elif args.format == "table":
# if args.verbose:
# table = [ [x['system'], x['drive'], x['url']] for x in data]
# headers=["System", "Drive", "URL"]
# else:
# table = [ [x['system'], x['drive']] for x in data]
# headers=["System", "Drive"]
# result = tabulate(table, headers=headers, tablefmt="outline")
result = data
write_output(result, output_format=args.format)
def physical_volumes(args):
"""
Entry point: get the list of physical volumes attached to a storage
controller (which in turn is part of a system)
"""
data = storage.list_physical_volumes(args)
if args.format == "text":
result = "\n".join([f"{x['system']} {x['drive']}" for x in data])
elif args.format == "json":
result = data
elif args.format == "table":
if args.verbose:
data = [[x["system"], x["drive"], x["url"]] for x in data]
headers = ["System", "Drive", "URL"]
else:
data = [[x["system"], x["drive"]] for x in data]
headers = ["System", "Drive"]
result = table(data, headers=headers)
write_output(result, output_format=args.format)
def product(args):
"""Entry point: get the product name of a server"""
details = api.product(args.server)
write_output(details, output_format=args.format)
def redfish(args=None):
"""
Entrypoint: Base command, gets args from stdin unless they are passed in as
a parameter (which probably means we are being called from a test)
"""
if args is None:
args = parse_args(sys.argv[1:])
if args.debug:
args.log_level = "DEBUG"
configure_logging(args)
if "func" in args:
args.func(args)
else:
write_output(api.utils.get(args.server, ""), output_format=args.format)
def service_tag(args):
"""Entry point: get the service tag of a Dell server"""
try:
write_output(api.service_tag(args.server), output_format=args.format)
except api.exceptions.DellOnly:
error("Service tags are only available for Dell iDRACs")
def storage_controller(args):
"""Entry point: get the details of a storage controllers in a server"""
try:
result = storage.storage_controller_details(args)
write_output(
result,
output_format=args.format,
)
except api.exceptions.ResponseNotOK as exc:
error(json.dumps(json.loads(exc.response.text), indent=2))
def storage_controllers(args):
"""Entry point: get the list of storage controllers in a server"""
result = storage.list_storage_controllers(args)
if not args.verbose:
controller_list = []
for i in result["Members"]:
controller_list.append(i["@odata.id"].split("/")[-1])
result = controller_list
if args.format == "text":
result = "\n".join(result)
elif args.format == "table":
data = []
for row in result:
data.append([row])
result = table(data, headers=["Controller"])
write_output(
result,
output_format=args.format,
)
def system_details(args):
"""Entry point: get the details of a system in a server"""
details = api.power.system_details(
args.server, args.username, args.password, args.system
)
write_output(details, output_format=args.format)
def version(args):
"""Entry point: get the verion of redfish implemented by the BMC"""
redfish_version = api.redfish_version(args.server)
write_output(redfish_version, output_format=args.format)