Implement interfaces to faults

This commit is contained in:
2023-12-04 01:37:54 -05:00
parent b59f743690
commit 672e58133f
6 changed files with 651 additions and 35 deletions

View File

@ -37,6 +37,7 @@ from pvc.cli.parsers import *
from pvc.cli.formatters import *
import pvc.lib.cluster
import pvc.lib.faults
import pvc.lib.node
import pvc.lib.vm
import pvc.lib.network
@ -347,40 +348,6 @@ def cli_cluster():
pass
###############################################################################
# > pvc cluster status
###############################################################################
@click.command(
name="status",
short_help="Show cluster status.",
)
@connection_req
@format_opt(
{
"pretty": cli_cluster_status_format_pretty,
"short": cli_cluster_status_format_short,
"json": lambda d: jdumps(d),
"json-pretty": lambda d: jdumps(d, indent=2),
}
)
def cli_cluster_status(
format_function,
):
"""
Show information and health about a PVC cluster.
\b
Format options:
"pretty": Output all details in a nice colourful format.
"short" Output only details about cluster health in a nice colourful format.
"json": Output in unformatted JSON.
"json-pretty": Output in formatted JSON.
"""
retcode, retdata = pvc.lib.cluster.get_info(CLI_CONFIG)
finish(retcode, retdata, format_function)
###############################################################################
# > pvc cluster init
###############################################################################
@ -485,6 +452,120 @@ def cli_cluster_restore(
"""
###############################################################################
# > pvc cluster status
###############################################################################
@click.command(
name="status",
short_help="Show cluster status.",
)
@connection_req
@format_opt(
{
"pretty": cli_cluster_status_format_pretty,
"short": cli_cluster_status_format_short,
"json": lambda d: jdumps(d),
"json-pretty": lambda d: jdumps(d, indent=2),
}
)
def cli_cluster_status(
format_function,
):
"""
Show information and health about a PVC cluster.
\b
Format options:
"pretty": Output all details in a nice colourful format.
"short" Output only details about cluster health in a nice colourful format.
"json": Output in unformatted JSON.
"json-pretty": Output in formatted JSON.
"""
retcode, retdata = pvc.lib.cluster.get_info(CLI_CONFIG)
finish(retcode, retdata, format_function)
###############################################################################
# > pvc cluster fault
###############################################################################
@click.group(
name="fault",
short_help="Manage PVC cluster faults.",
context_settings=CONTEXT_SETTINGS,
)
def cli_cluster_fault():
"""
Manage faults in the PVC cluster.
"""
pass
###############################################################################
# > pvc cluster fault list
###############################################################################
@click.command(
name="list",
short_help="List all cluster faults.",
)
@click.argument("limit", default=None, required=False)
@format_opt(
{
"pretty": cli_cluster_fault_list_format_pretty,
# "short": cli_cluster_status_format_short,
"json": lambda d: jdumps(d),
"json-pretty": lambda d: jdumps(d, indent=2),
}
)
@connection_req
def cli_cluster_fault_list(limit, format_function):
"""
List all faults in the PVC cluster, optionally limited to fault ID LIMIT.
"""
retcode, retdata = pvc.lib.faults.get_list(
CLI_CONFIG,
limit=limit,
)
finish(retcode, retdata, format_function)
###############################################################################
# > pvc cluster fault ack
###############################################################################
@click.command(
name="ack",
short_help="Acknowledge a cluster fault.",
)
@click.argument("fault_id")
@connection_req
def cli_cluster_fault_acknowledge(fault_id):
"""
Acknowledge the cluster fault FAULT_ID.
"""
retcode, retdata = pvc.lib.faults.acknowledge(CLI_CONFIG, fault_id)
finish(retcode, retdata)
###############################################################################
# > pvc cluster fault delete
###############################################################################
@click.command(
name="delete",
short_help="Delete a cluster fault.",
)
@click.argument("fault_id")
@connection_req
def cli_cluster_fault_delete(fault_id):
"""
Delete the cluster fault FAULT_ID.
"""
retcode, retdata = pvc.lib.faults.delete(CLI_CONFIG, fault_id)
finish(retcode, retdata)
###############################################################################
# > pvc cluster maintenance
###############################################################################
@ -6170,10 +6251,14 @@ cli_provisioner_profile.add_command(cli_provisioner_profile_list)
cli_provisioner.add_command(cli_provisioner_profile)
cli_provisioner.add_command(cli_provisioner_create)
cli.add_command(cli_provisioner)
cli_cluster.add_command(cli_cluster_status)
cli_cluster.add_command(cli_cluster_init)
cli_cluster.add_command(cli_cluster_backup)
cli_cluster.add_command(cli_cluster_restore)
cli_cluster.add_command(cli_cluster_status)
cli_cluster_fault.add_command(cli_cluster_fault_list)
cli_cluster_fault.add_command(cli_cluster_fault_acknowledge)
cli_cluster_fault.add_command(cli_cluster_fault_delete)
cli_cluster.add_command(cli_cluster_fault)
cli_cluster_maintenance.add_command(cli_cluster_maintenance_on)
cli_cluster_maintenance.add_command(cli_cluster_maintenance_off)
cli_cluster.add_command(cli_cluster_maintenance)

View File

@ -261,6 +261,127 @@ def cli_cluster_status_format_short(CLI_CONFIG, data):
return "\n".join(output)
def cli_cluster_fault_list_format_pretty(CLI_CONFIG, fault_data):
"""
Pretty format the output of cli_cluster_fault_list
"""
fault_list_output = []
# Determine optimal column widths
fault_id_length = 3 # "ID"
fault_status_length = 7 # "Status"
fault_health_delta_length = 7 # "Health"
fault_acknowledged_at_length = 6 # "Ack'd"
fault_last_reported_length = 5 # "Last"
fault_first_reported_length = 6 # "First"
# Message goes on its own line
for fault in fault_data:
# fault_id column
_fault_id_length = len(str(fault["id"])) + 1
if _fault_id_length > fault_id_length:
fault_id_length = _fault_id_length
# status column
_fault_status_length = len(str(fault["status"])) + 1
if _fault_status_length > fault_status_length:
fault_status_length = _fault_status_length
# health_delta column
_fault_health_delta_length = len(str(fault["health_delta"])) + 1
if _fault_health_delta_length > fault_health_delta_length:
fault_health_delta_length = _fault_health_delta_length
# acknowledged_at column
_fault_acknowledged_at_length = len(str(fault["acknowledged_at"])) + 1
if _fault_acknowledged_at_length > fault_acknowledged_at_length:
fault_acknowledged_at_length = _fault_acknowledged_at_length
# last_reported column
_fault_last_reported_length = len(str(fault["last_reported"])) + 1
if _fault_last_reported_length > fault_last_reported_length:
fault_last_reported_length = _fault_last_reported_length
# first_reported column
_fault_first_reported_length = len(str(fault["first_reported"])) + 1
if _fault_first_reported_length > fault_first_reported_length:
fault_first_reported_length = _fault_first_reported_length
# Format the string (header)
fault_list_output.append(
"{bold}{fault_id: <{fault_id_length}} {fault_status: <{fault_status_length}} {fault_health_delta: <{fault_health_delta_length}} {fault_acknowledged_at: <{fault_acknowledged_at_length}} {fault_last_reported: <{fault_last_reported_length}} {fault_first_reported: <{fault_first_reported_length}}{end_bold}".format(
bold=ansii["bold"],
end_bold=ansii["end"],
fault_id_length=fault_id_length,
fault_status_length=fault_status_length,
fault_health_delta_length=fault_health_delta_length,
fault_acknowledged_at_length=fault_acknowledged_at_length,
fault_last_reported_length=fault_last_reported_length,
fault_first_reported_length=fault_first_reported_length,
fault_id="ID",
fault_status="Status",
fault_health_delta="Health",
fault_acknowledged_at="Ack'd",
fault_last_reported="Last",
fault_first_reported="First",
)
)
fault_list_output.append(
"{bold}> {fault_message}{end_bold}".format(
bold=ansii["bold"],
end_bold=ansii["end"],
fault_message="Message",
)
)
for fault in sorted(
fault_data,
key=lambda x: (x["status"], x["health_delta"], x["last_reported"]),
reverse=True,
):
health_delta = fault["health_delta"]
if fault["acknowledged_at"] != "":
health_colour = ansii["blue"]
elif health_delta >= 50:
health_colour = ansii["red"]
elif health_delta >= 10:
health_colour = ansii["yellow"]
else:
health_colour = ansii["green"]
fault_list_output.append("")
fault_list_output.append(
"{bold}{fault_id: <{fault_id_length}} {health_colour}{fault_status: <{fault_status_length}} {fault_health_delta: <{fault_health_delta_length}}{end_colour} {fault_acknowledged_at: <{fault_acknowledged_at_length}} {fault_last_reported: <{fault_last_reported_length}} {fault_first_reported: <{fault_first_reported_length}}{end_bold}".format(
bold="",
end_bold="",
health_colour=health_colour,
end_colour=ansii["end"],
fault_id_length=fault_id_length,
fault_status_length=fault_status_length,
fault_health_delta_length=fault_health_delta_length,
fault_acknowledged_at_length=fault_acknowledged_at_length,
fault_last_reported_length=fault_last_reported_length,
fault_first_reported_length=fault_first_reported_length,
fault_id=fault["id"],
fault_status=fault["status"].title(),
fault_health_delta=f"-{fault['health_delta']}%",
fault_acknowledged_at=fault["acknowledged_at"]
if fault["acknowledged_at"] != ""
else "N/A",
fault_last_reported=fault["last_reported"],
fault_first_reported=fault["first_reported"],
)
)
fault_list_output.append(
"> {fault_message}".format(
fault_message=fault["message"],
)
)
return "\n".join(fault_list_output)
def cli_cluster_task_format_pretty(CLI_CONFIG, task_data):
"""
Pretty format the output of cli_cluster_task

View File

@ -0,0 +1,78 @@
#!/usr/bin/env python3
# faults.py - PVC CLI client function library, faults management
# Part of the Parallel Virtual Cluster (PVC) system
#
# Copyright (C) 2018-2022 Joshua M. Boniface <joshua@boniface.me>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
###############################################################################
from pvc.lib.common import call_api
def get_list(config, limit=None, sort_key="last_reported"):
"""
Get list of PVC faults
API endpoint: GET /api/v1/faults
API arguments: sort_key={sort_key}
API schema: {json_data_object}
"""
if limit is not None:
params = {}
endpoint = f"/faults/{limit}"
else:
params = {"sort_key": sort_key}
endpoint = "/faults"
response = call_api(config, "get", endpoint, params=params)
if response.status_code == 200:
return True, response.json()
else:
return False, response.json().get("message", "")
def acknowledge(config, fault_id):
"""
Acknowledge a PVC fault
API endpoint: PUT /api/v1/faults/<fault_id>
API arguments:
API schema: {json_message}
"""
response = call_api(config, "put", f"/faults/{fault_id}")
print(response.json())
if response.status_code == 200:
return True, response.json().get("message", "")
else:
return False, response.json().get("message", "")
def delete(config, fault_id):
"""
Delete a PVC fault
API endpoint: DELETE /api/v1/faults/<fault_id>
API arguments:
API schema: {json_message}
"""
response = call_api(config, "delete", f"/faults/{fault_id}")
if response.status_code == 200:
return True, response.json().get("message", "")
else:
return False, response.json().get("message", "")