Compare commits
3 Commits
3ed60ac1c1
...
bcabfa9d70
Author | SHA1 | Date | |
---|---|---|---|
bcabfa9d70 | |||
2dc2055cfa | |||
5bd2bd468a |
4
.flake8
4
.flake8
@@ -3,7 +3,9 @@
|
||||
# * W503 (line break before binary operator): Black moves these to new lines
|
||||
# * E501 (line too long): Long lines are a fact of life in comment blocks; Black handles active instances of this
|
||||
# * E203 (whitespace before ':'): Black recommends this as disabled
|
||||
ignore = W503, E501
|
||||
# * F403 (import * used; unable to detect undefined names): We use a wildcard for helpers
|
||||
# * F405 (possibly undefined name): We use a wildcard for helpers
|
||||
ignore = W503, E501, F403, F405
|
||||
extend-ignore = E203
|
||||
# We exclude the Debian, migrations, and provisioner examples
|
||||
exclude = debian,api-daemon/migrations/versions,api-daemon/provisioner/examples,node-daemon/monitoring
|
||||
|
@@ -1,116 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# cluster.py - PVC CLI client function library, cluster 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/>.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
import json
|
||||
|
||||
from pvc.lib.common import call_api
|
||||
|
||||
|
||||
def initialize(config, overwrite=False):
|
||||
"""
|
||||
Initialize the PVC cluster
|
||||
|
||||
API endpoint: GET /api/v1/initialize
|
||||
API arguments: overwrite, yes-i-really-mean-it
|
||||
API schema: {json_data_object}
|
||||
"""
|
||||
params = {"yes-i-really-mean-it": "yes", "overwrite": overwrite}
|
||||
response = call_api(config, "post", "/initialize", params=params)
|
||||
|
||||
if response.status_code == 200:
|
||||
retstatus = True
|
||||
else:
|
||||
retstatus = False
|
||||
|
||||
return retstatus, response.json().get("message", "")
|
||||
|
||||
|
||||
def backup(config):
|
||||
"""
|
||||
Get a JSON backup of the cluster
|
||||
|
||||
API endpoint: GET /api/v1/backup
|
||||
API arguments:
|
||||
API schema: {json_data_object}
|
||||
"""
|
||||
response = call_api(config, "get", "/backup")
|
||||
|
||||
if response.status_code == 200:
|
||||
return True, response.json()
|
||||
else:
|
||||
return False, response.json().get("message", "")
|
||||
|
||||
|
||||
def restore(config, cluster_data):
|
||||
"""
|
||||
Restore a JSON backup to the cluster
|
||||
|
||||
API endpoint: POST /api/v1/restore
|
||||
API arguments: yes-i-really-mean-it
|
||||
API schema: {json_data_object}
|
||||
"""
|
||||
cluster_data_json = json.dumps(cluster_data)
|
||||
|
||||
params = {"yes-i-really-mean-it": "yes"}
|
||||
data = {"cluster_data": cluster_data_json}
|
||||
response = call_api(config, "post", "/restore", params=params, data=data)
|
||||
|
||||
if response.status_code == 200:
|
||||
retstatus = True
|
||||
else:
|
||||
retstatus = False
|
||||
|
||||
return retstatus, response.json().get("message", "")
|
||||
|
||||
|
||||
def maintenance_mode(config, state):
|
||||
"""
|
||||
Enable or disable PVC cluster maintenance mode
|
||||
|
||||
API endpoint: POST /api/v1/status
|
||||
API arguments: {state}={state}
|
||||
API schema: {json_data_object}
|
||||
"""
|
||||
params = {"state": state}
|
||||
response = call_api(config, "post", "/status", params=params)
|
||||
|
||||
if response.status_code == 200:
|
||||
retstatus = True
|
||||
else:
|
||||
retstatus = False
|
||||
|
||||
return retstatus, response.json().get("message", "")
|
||||
|
||||
|
||||
def get_info(config):
|
||||
"""
|
||||
Get status of the PVC cluster
|
||||
|
||||
API endpoint: GET /api/v1/status
|
||||
API arguments:
|
||||
API schema: {json_data_object}
|
||||
"""
|
||||
response = call_api(config, "get", "/status")
|
||||
|
||||
if response.status_code == 200:
|
||||
return True, response.json()
|
||||
else:
|
||||
return False, response.json().get("message", "")
|
33
client-cli-old/pvc.py
Executable file
33
client-cli-old/pvc.py
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# pvc.py - PVC client command-line interface (stub testing interface)
|
||||
# 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/>.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
import pvc.pvc
|
||||
|
||||
|
||||
#
|
||||
# Main entry point
|
||||
#
|
||||
def main():
|
||||
return pvc.pvc.cli(obj={})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@@ -27,8 +27,8 @@ from requests_toolbelt.multipart.encoder import (
|
||||
MultipartEncoderMonitor,
|
||||
)
|
||||
|
||||
import pvc.cli_lib.ansiprint as ansiprint
|
||||
from pvc.cli_lib.common import UploadProgressBar, call_api
|
||||
import pvc.lib.ansiprint as ansiprint
|
||||
from pvc.lib.common import UploadProgressBar, call_api
|
||||
|
||||
#
|
||||
# Supplemental functions
|
313
client-cli-old/pvc/lib/cluster.py
Normal file
313
client-cli-old/pvc/lib/cluster.py
Normal file
@@ -0,0 +1,313 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# cluster.py - PVC CLI client function library, cluster 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/>.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
import json
|
||||
|
||||
import pvc.lib.ansiprint as ansiprint
|
||||
from pvc.lib.common import call_api
|
||||
|
||||
|
||||
def initialize(config, overwrite=False):
|
||||
"""
|
||||
Initialize the PVC cluster
|
||||
|
||||
API endpoint: GET /api/v1/initialize
|
||||
API arguments: overwrite, yes-i-really-mean-it
|
||||
API schema: {json_data_object}
|
||||
"""
|
||||
params = {"yes-i-really-mean-it": "yes", "overwrite": overwrite}
|
||||
response = call_api(config, "post", "/initialize", params=params)
|
||||
|
||||
if response.status_code == 200:
|
||||
retstatus = True
|
||||
else:
|
||||
retstatus = False
|
||||
|
||||
return retstatus, response.json().get("message", "")
|
||||
|
||||
|
||||
def backup(config):
|
||||
"""
|
||||
Get a JSON backup of the cluster
|
||||
|
||||
API endpoint: GET /api/v1/backup
|
||||
API arguments:
|
||||
API schema: {json_data_object}
|
||||
"""
|
||||
response = call_api(config, "get", "/backup")
|
||||
|
||||
if response.status_code == 200:
|
||||
return True, response.json()
|
||||
else:
|
||||
return False, response.json().get("message", "")
|
||||
|
||||
|
||||
def restore(config, cluster_data):
|
||||
"""
|
||||
Restore a JSON backup to the cluster
|
||||
|
||||
API endpoint: POST /api/v1/restore
|
||||
API arguments: yes-i-really-mean-it
|
||||
API schema: {json_data_object}
|
||||
"""
|
||||
cluster_data_json = json.dumps(cluster_data)
|
||||
|
||||
params = {"yes-i-really-mean-it": "yes"}
|
||||
data = {"cluster_data": cluster_data_json}
|
||||
response = call_api(config, "post", "/restore", params=params, data=data)
|
||||
|
||||
if response.status_code == 200:
|
||||
retstatus = True
|
||||
else:
|
||||
retstatus = False
|
||||
|
||||
return retstatus, response.json().get("message", "")
|
||||
|
||||
|
||||
def maintenance_mode(config, state):
|
||||
"""
|
||||
Enable or disable PVC cluster maintenance mode
|
||||
|
||||
API endpoint: POST /api/v1/status
|
||||
API arguments: {state}={state}
|
||||
API schema: {json_data_object}
|
||||
"""
|
||||
params = {"state": state}
|
||||
response = call_api(config, "post", "/status", params=params)
|
||||
|
||||
if response.status_code == 200:
|
||||
retstatus = True
|
||||
else:
|
||||
retstatus = False
|
||||
|
||||
return retstatus, response.json().get("message", "")
|
||||
|
||||
|
||||
def get_info(config):
|
||||
"""
|
||||
Get status of the PVC cluster
|
||||
|
||||
API endpoint: GET /api/v1/status
|
||||
API arguments:
|
||||
API schema: {json_data_object}
|
||||
"""
|
||||
response = call_api(config, "get", "/status")
|
||||
|
||||
if response.status_code == 200:
|
||||
return True, response.json()
|
||||
else:
|
||||
return False, response.json().get("message", "")
|
||||
|
||||
|
||||
def format_info(cluster_information, oformat):
|
||||
if oformat == "json":
|
||||
return json.dumps(cluster_information)
|
||||
|
||||
if oformat == "json-pretty":
|
||||
return json.dumps(cluster_information, indent=4)
|
||||
|
||||
# Plain formatting, i.e. human-readable
|
||||
if (
|
||||
cluster_information.get("maintenance") == "true"
|
||||
or cluster_information.get("cluster_health", {}).get("health", "N/A") == "N/A"
|
||||
):
|
||||
health_colour = ansiprint.blue()
|
||||
elif cluster_information.get("cluster_health", {}).get("health", 100) > 90:
|
||||
health_colour = ansiprint.green()
|
||||
elif cluster_information.get("cluster_health", {}).get("health", 100) > 50:
|
||||
health_colour = ansiprint.yellow()
|
||||
else:
|
||||
health_colour = ansiprint.red()
|
||||
|
||||
ainformation = []
|
||||
|
||||
ainformation.append(
|
||||
"{}PVC cluster status:{}".format(ansiprint.bold(), ansiprint.end())
|
||||
)
|
||||
ainformation.append("")
|
||||
|
||||
health_text = (
|
||||
f"{cluster_information.get('cluster_health', {}).get('health', 'N/A')}"
|
||||
)
|
||||
if health_text != "N/A":
|
||||
health_text += "%"
|
||||
if cluster_information.get("maintenance") == "true":
|
||||
health_text += " (maintenance on)"
|
||||
|
||||
ainformation.append(
|
||||
"{}Cluster health:{} {}{}{}".format(
|
||||
ansiprint.purple(),
|
||||
ansiprint.end(),
|
||||
health_colour,
|
||||
health_text,
|
||||
ansiprint.end(),
|
||||
)
|
||||
)
|
||||
if cluster_information.get("cluster_health", {}).get("messages"):
|
||||
health_messages = "\n > ".join(
|
||||
sorted(cluster_information["cluster_health"]["messages"])
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Health messages:{} > {}".format(
|
||||
ansiprint.purple(),
|
||||
ansiprint.end(),
|
||||
health_messages,
|
||||
)
|
||||
)
|
||||
else:
|
||||
ainformation.append(
|
||||
"{}Health messages:{} N/A".format(
|
||||
ansiprint.purple(),
|
||||
ansiprint.end(),
|
||||
)
|
||||
)
|
||||
|
||||
if oformat == "short":
|
||||
return "\n".join(ainformation)
|
||||
|
||||
ainformation.append("")
|
||||
ainformation.append(
|
||||
"{}Primary node:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["primary_node"]
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}PVC version:{} {}".format(
|
||||
ansiprint.purple(),
|
||||
ansiprint.end(),
|
||||
cluster_information.get("pvc_version", "N/A"),
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Cluster upstream IP:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["upstream_ip"]
|
||||
)
|
||||
)
|
||||
ainformation.append("")
|
||||
ainformation.append(
|
||||
"{}Total nodes:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["nodes"]["total"]
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Total VMs:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["vms"]["total"]
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Total networks:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["networks"]
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Total OSDs:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["osds"]["total"]
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Total pools:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["pools"]
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Total volumes:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["volumes"]
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Total snapshots:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["snapshots"]
|
||||
)
|
||||
)
|
||||
|
||||
nodes_string = "{}Nodes:{} {}/{} {}ready,run{}".format(
|
||||
ansiprint.purple(),
|
||||
ansiprint.end(),
|
||||
cluster_information["nodes"].get("run,ready", 0),
|
||||
cluster_information["nodes"].get("total", 0),
|
||||
ansiprint.green(),
|
||||
ansiprint.end(),
|
||||
)
|
||||
for state, count in cluster_information["nodes"].items():
|
||||
if state == "total" or state == "run,ready":
|
||||
continue
|
||||
|
||||
nodes_string += " {}/{} {}{}{}".format(
|
||||
count,
|
||||
cluster_information["nodes"]["total"],
|
||||
ansiprint.yellow(),
|
||||
state,
|
||||
ansiprint.end(),
|
||||
)
|
||||
|
||||
ainformation.append("")
|
||||
ainformation.append(nodes_string)
|
||||
|
||||
vms_string = "{}VMs:{} {}/{} {}start{}".format(
|
||||
ansiprint.purple(),
|
||||
ansiprint.end(),
|
||||
cluster_information["vms"].get("start", 0),
|
||||
cluster_information["vms"].get("total", 0),
|
||||
ansiprint.green(),
|
||||
ansiprint.end(),
|
||||
)
|
||||
for state, count in cluster_information["vms"].items():
|
||||
if state == "total" or state == "start":
|
||||
continue
|
||||
|
||||
if state in ["disable", "migrate", "unmigrate", "provision"]:
|
||||
colour = ansiprint.blue()
|
||||
else:
|
||||
colour = ansiprint.yellow()
|
||||
|
||||
vms_string += " {}/{} {}{}{}".format(
|
||||
count, cluster_information["vms"]["total"], colour, state, ansiprint.end()
|
||||
)
|
||||
|
||||
ainformation.append("")
|
||||
ainformation.append(vms_string)
|
||||
|
||||
if cluster_information["osds"]["total"] > 0:
|
||||
osds_string = "{}Ceph OSDs:{} {}/{} {}up,in{}".format(
|
||||
ansiprint.purple(),
|
||||
ansiprint.end(),
|
||||
cluster_information["osds"].get("up,in", 0),
|
||||
cluster_information["osds"].get("total", 0),
|
||||
ansiprint.green(),
|
||||
ansiprint.end(),
|
||||
)
|
||||
for state, count in cluster_information["osds"].items():
|
||||
if state == "total" or state == "up,in":
|
||||
continue
|
||||
|
||||
osds_string += " {}/{} {}{}{}".format(
|
||||
count,
|
||||
cluster_information["osds"]["total"],
|
||||
ansiprint.yellow(),
|
||||
state,
|
||||
ansiprint.end(),
|
||||
)
|
||||
|
||||
ainformation.append("")
|
||||
ainformation.append(osds_string)
|
||||
|
||||
ainformation.append("")
|
||||
return "\n".join(ainformation)
|
@@ -542,16 +542,11 @@ def net_sriov_vf_info(config, node, vf):
|
||||
return False, "VF not found."
|
||||
else:
|
||||
# Return a single instance if the response is a list
|
||||
data = dict()
|
||||
data["node"] = node
|
||||
if isinstance(response.json(), list):
|
||||
data = dict()
|
||||
data["vf_information"] = response.json()[0]
|
||||
return True, data
|
||||
return True, response.json()[0]
|
||||
# This shouldn't happen, but is here just in case
|
||||
else:
|
||||
data["vf_information"] = response.json()
|
||||
return True, data
|
||||
return True, response.json()
|
||||
else:
|
||||
return False, response.json().get("message", "")
|
||||
|
||||
@@ -719,7 +714,7 @@ def format_info(config, network_information, long_output):
|
||||
)
|
||||
ainformation.append("")
|
||||
if retcode:
|
||||
firewall_rules_string = format_list_acl(config, firewall_rules_list)
|
||||
firewall_rules_string = format_list_acl(firewall_rules_list)
|
||||
for line in firewall_rules_string.split("\n"):
|
||||
ainformation.append(line)
|
||||
else:
|
||||
@@ -893,7 +888,7 @@ def format_list(config, network_list):
|
||||
return "\n".join(network_list_output)
|
||||
|
||||
|
||||
def format_list_dhcp(config, dhcp_lease_list):
|
||||
def format_list_dhcp(dhcp_lease_list):
|
||||
dhcp_lease_list_output = []
|
||||
|
||||
# Determine optimal column widths
|
||||
@@ -992,7 +987,7 @@ def format_list_dhcp(config, dhcp_lease_list):
|
||||
return "\n".join(dhcp_lease_list_output)
|
||||
|
||||
|
||||
def format_list_acl(config, acl_list):
|
||||
def format_list_acl(acl_list):
|
||||
# Handle when we get an empty entry
|
||||
if not acl_list:
|
||||
acl_list = list()
|
||||
@@ -1091,7 +1086,7 @@ def format_list_acl(config, acl_list):
|
||||
return "\n".join(acl_list_output)
|
||||
|
||||
|
||||
def format_list_sriov_pf(config, pf_list):
|
||||
def format_list_sriov_pf(pf_list):
|
||||
# The maximum column width of the VFs column
|
||||
max_vfs_length = 70
|
||||
|
||||
@@ -1211,7 +1206,7 @@ def format_list_sriov_pf(config, pf_list):
|
||||
return "\n".join(pf_list_output)
|
||||
|
||||
|
||||
def format_list_sriov_vf(config, vf_list):
|
||||
def format_list_sriov_vf(vf_list):
|
||||
# Handle when we get an empty entry
|
||||
if not vf_list:
|
||||
vf_list = list()
|
||||
@@ -1343,13 +1338,10 @@ def format_list_sriov_vf(config, vf_list):
|
||||
return "\n".join(vf_list_output)
|
||||
|
||||
|
||||
def format_info_sriov_vf(config, data):
|
||||
if not data or not data["vf_information"]:
|
||||
def format_info_sriov_vf(config, vf_information, node):
|
||||
if not vf_information:
|
||||
return "No VF found"
|
||||
|
||||
node = data["node"]
|
||||
vf_information = data["vf_information"]
|
||||
|
||||
# Get information on the using VM if applicable
|
||||
if vf_information["usage"]["used"] == "True" and vf_information["usage"]["domain"]:
|
||||
vm_information = call_api(
|
@@ -52,7 +52,7 @@ def node_coordinator_state(config, node, action):
|
||||
return retstatus, response.json().get("message", "")
|
||||
|
||||
|
||||
def node_domain_state(config, node, action):
|
||||
def node_domain_state(config, node, action, wait):
|
||||
"""
|
||||
Set node domain state state (flush/ready)
|
||||
|
||||
@@ -60,7 +60,7 @@ def node_domain_state(config, node, action):
|
||||
API arguments: action={action}, wait={wait}
|
||||
API schema: {"message": "{data}"}
|
||||
"""
|
||||
params = {"state": action}
|
||||
params = {"state": action, "wait": str(wait).lower()}
|
||||
response = call_api(
|
||||
config, "post", "/node/{node}/domain-state".format(node=node), params=params
|
||||
)
|
||||
@@ -273,7 +273,7 @@ def getOutputColours(node_information):
|
||||
)
|
||||
|
||||
|
||||
def format_info(config, node_information, long_output):
|
||||
def format_info(node_information, long_output):
|
||||
(
|
||||
health_colour,
|
||||
daemon_state_colour,
|
||||
@@ -442,9 +442,12 @@ def format_info(config, node_information, long_output):
|
||||
return "\n".join(ainformation)
|
||||
|
||||
|
||||
def format_list(config, node_list):
|
||||
if node_list == "Node not found.":
|
||||
return node_list
|
||||
def format_list(node_list, raw):
|
||||
if raw:
|
||||
ainformation = list()
|
||||
for node in sorted(item["name"] for item in node_list):
|
||||
ainformation.append(node)
|
||||
return "\n".join(ainformation)
|
||||
|
||||
node_list_output = []
|
||||
|
@@ -750,11 +750,24 @@ def task_status(config, task_id=None, is_watching=False):
|
||||
if response.status_code == 200:
|
||||
retvalue = True
|
||||
respjson = response.json()
|
||||
|
||||
if is_watching:
|
||||
# Just return the raw JSON to the watching process instead of including value
|
||||
# Just return the raw JSON to the watching process instead of formatting it
|
||||
return respjson
|
||||
|
||||
job_state = respjson["state"]
|
||||
if job_state == "RUNNING":
|
||||
retdata = "Job state: RUNNING\nStage: {}/{}\nStatus: {}".format(
|
||||
respjson["current"], respjson["total"], respjson["status"]
|
||||
)
|
||||
elif job_state == "FAILED":
|
||||
retdata = "Job state: FAILED\nStatus: {}".format(respjson["status"])
|
||||
elif job_state == "COMPLETED":
|
||||
retdata = "Job state: COMPLETED\nStatus: {}".format(respjson["status"])
|
||||
else:
|
||||
return retvalue, respjson
|
||||
retdata = "Job state: {}\nStatus: {}".format(
|
||||
respjson["state"], respjson["status"]
|
||||
)
|
||||
else:
|
||||
retvalue = False
|
||||
retdata = response.json().get("message", "")
|
||||
@@ -801,7 +814,7 @@ def task_status(config, task_id=None, is_watching=False):
|
||||
#
|
||||
# Format functions
|
||||
#
|
||||
def format_list_template(config, template_data, template_type=None):
|
||||
def format_list_template(template_data, template_type=None):
|
||||
"""
|
||||
Format the returned template template
|
||||
|
||||
@@ -1317,12 +1330,7 @@ def format_list_template_storage(template_template):
|
||||
return "\n".join(template_list_output)
|
||||
|
||||
|
||||
def format_list_userdata(config, userdata_data):
|
||||
if not config.get("long_output"):
|
||||
lines = 4
|
||||
else:
|
||||
lines = None
|
||||
|
||||
def format_list_userdata(userdata_data, lines=None):
|
||||
if isinstance(userdata_data, dict):
|
||||
userdata_data = [userdata_data]
|
||||
|
||||
@@ -1424,12 +1432,7 @@ def format_list_userdata(config, userdata_data):
|
||||
return "\n".join(userdata_list_output)
|
||||
|
||||
|
||||
def format_list_script(config, script_data):
|
||||
if not config.get("long_output"):
|
||||
lines = 4
|
||||
else:
|
||||
lines = None
|
||||
|
||||
def format_list_script(script_data, lines=None):
|
||||
if isinstance(script_data, dict):
|
||||
script_data = [script_data]
|
||||
|
||||
@@ -1528,7 +1531,7 @@ def format_list_script(config, script_data):
|
||||
return "\n".join(script_list_output)
|
||||
|
||||
|
||||
def format_list_ova(config, ova_data):
|
||||
def format_list_ova(ova_data):
|
||||
if isinstance(ova_data, dict):
|
||||
ova_data = [ova_data]
|
||||
|
||||
@@ -1675,7 +1678,7 @@ def format_list_ova(config, ova_data):
|
||||
return "\n".join(ova_list_output)
|
||||
|
||||
|
||||
def format_list_profile(config, profile_data):
|
||||
def format_list_profile(profile_data):
|
||||
if isinstance(profile_data, dict):
|
||||
profile_data = [profile_data]
|
||||
|
||||
@@ -1864,23 +1867,7 @@ def format_list_profile(config, profile_data):
|
||||
return "\n".join(profile_list_output)
|
||||
|
||||
|
||||
def format_list_task(config, task_data):
|
||||
if not isinstance(task_data, list):
|
||||
job_state = task_data["state"]
|
||||
if job_state == "RUNNING":
|
||||
retdata = "Job state: RUNNING\nStage: {}/{}\nStatus: {}".format(
|
||||
task_data["current"], task_data["total"], task_data["status"]
|
||||
)
|
||||
elif job_state == "FAILED":
|
||||
retdata = "Job state: FAILED\nStatus: {}".format(task_data["status"])
|
||||
elif job_state == "COMPLETED":
|
||||
retdata = "Job state: COMPLETED\nStatus: {}".format(task_data["status"])
|
||||
else:
|
||||
retdata = "Job state: {}\nStatus: {}".format(
|
||||
task_data["state"], task_data["status"]
|
||||
)
|
||||
return retdata
|
||||
|
||||
def format_list_task(task_data):
|
||||
task_list_output = []
|
||||
|
||||
# Determine optimal column widths
|
@@ -286,18 +286,20 @@ def vm_tag_set(config, vm, action, tag, protected=False):
|
||||
return retstatus, response.json().get("message", "")
|
||||
|
||||
|
||||
def format_vm_tags(config, data):
|
||||
def format_vm_tags(config, name, tags):
|
||||
"""
|
||||
Format the output of a tags dictionary in a nice table
|
||||
"""
|
||||
|
||||
tags = data.get("tags", [])
|
||||
|
||||
if len(tags) < 1:
|
||||
return "No tags found."
|
||||
|
||||
output_list = []
|
||||
|
||||
name_length = 5
|
||||
_name_length = len(name) + 1
|
||||
if _name_length > name_length:
|
||||
name_length = _name_length
|
||||
|
||||
tags_name_length = 4
|
||||
tags_type_length = 5
|
||||
tags_protected_length = 10
|
||||
@@ -493,38 +495,44 @@ def vm_vcpus_get(config, vm):
|
||||
except Exception:
|
||||
return False, "ERROR: Failed to parse XML data."
|
||||
|
||||
data = dict()
|
||||
data["name"] = vm
|
||||
data["vcpus"] = int(parsed_xml.vcpu.text)
|
||||
data["sockets"] = parsed_xml.cpu.topology.attrib.get("sockets")
|
||||
data["cores"] = parsed_xml.cpu.topology.attrib.get("cores")
|
||||
data["threads"] = parsed_xml.cpu.topology.attrib.get("threads")
|
||||
vm_vcpus = int(parsed_xml.vcpu.text)
|
||||
vm_sockets = parsed_xml.cpu.topology.attrib.get("sockets")
|
||||
vm_cores = parsed_xml.cpu.topology.attrib.get("cores")
|
||||
vm_threads = parsed_xml.cpu.topology.attrib.get("threads")
|
||||
|
||||
return True, data
|
||||
return True, (vm_vcpus, (vm_sockets, vm_cores, vm_threads))
|
||||
|
||||
|
||||
def format_vm_vcpus(config, data):
|
||||
def format_vm_vcpus(config, name, vcpus):
|
||||
"""
|
||||
Format the output of a vCPU value in a nice table
|
||||
"""
|
||||
output_list = []
|
||||
|
||||
name_length = 5
|
||||
_name_length = len(name) + 1
|
||||
if _name_length > name_length:
|
||||
name_length = _name_length
|
||||
|
||||
vcpus_length = 6
|
||||
sockets_length = 8
|
||||
cores_length = 6
|
||||
threads_length = 8
|
||||
|
||||
output_list.append(
|
||||
"{bold}{vcpus: <{vcpus_length}} \
|
||||
"{bold}{name: <{name_length}} \
|
||||
{vcpus: <{vcpus_length}} \
|
||||
{sockets: <{sockets_length}} \
|
||||
{cores: <{cores_length}} \
|
||||
{threads: <{threads_length}}{end_bold}".format(
|
||||
name_length=name_length,
|
||||
vcpus_length=vcpus_length,
|
||||
sockets_length=sockets_length,
|
||||
cores_length=cores_length,
|
||||
threads_length=threads_length,
|
||||
bold=ansiprint.bold(),
|
||||
end_bold=ansiprint.end(),
|
||||
name="Name",
|
||||
vcpus="vCPUs",
|
||||
sockets="Sockets",
|
||||
cores="Cores",
|
||||
@@ -532,20 +540,23 @@ def format_vm_vcpus(config, data):
|
||||
)
|
||||
)
|
||||
output_list.append(
|
||||
"{bold}{vcpus: <{vcpus_length}} \
|
||||
"{bold}{name: <{name_length}} \
|
||||
{vcpus: <{vcpus_length}} \
|
||||
{sockets: <{sockets_length}} \
|
||||
{cores: <{cores_length}} \
|
||||
{threads: <{threads_length}}{end_bold}".format(
|
||||
name_length=name_length,
|
||||
vcpus_length=vcpus_length,
|
||||
sockets_length=sockets_length,
|
||||
cores_length=cores_length,
|
||||
threads_length=threads_length,
|
||||
bold="",
|
||||
end_bold="",
|
||||
vcpus=data["vcpus"],
|
||||
sockets=data["sockets"],
|
||||
cores=data["cores"],
|
||||
threads=data["threads"],
|
||||
name=name,
|
||||
vcpus=vcpus[0],
|
||||
sockets=vcpus[1][0],
|
||||
cores=vcpus[1][1],
|
||||
threads=vcpus[1][2],
|
||||
)
|
||||
)
|
||||
return "\n".join(output_list)
|
||||
@@ -608,35 +619,44 @@ def vm_memory_get(config, vm):
|
||||
except Exception:
|
||||
return False, "ERROR: Failed to parse XML data."
|
||||
|
||||
data = dict()
|
||||
data["name"] = vm
|
||||
data["memory"] = int(parsed_xml.memory.text)
|
||||
vm_memory = int(parsed_xml.memory.text)
|
||||
|
||||
return True, data
|
||||
return True, vm_memory
|
||||
|
||||
|
||||
def format_vm_memory(config, data):
|
||||
def format_vm_memory(config, name, memory):
|
||||
"""
|
||||
Format the output of a memory value in a nice table
|
||||
"""
|
||||
output_list = []
|
||||
|
||||
name_length = 5
|
||||
_name_length = len(name) + 1
|
||||
if _name_length > name_length:
|
||||
name_length = _name_length
|
||||
|
||||
memory_length = 6
|
||||
|
||||
output_list.append(
|
||||
"{bold}{memory: <{memory_length}}{end_bold}".format(
|
||||
"{bold}{name: <{name_length}} \
|
||||
{memory: <{memory_length}}{end_bold}".format(
|
||||
name_length=name_length,
|
||||
memory_length=memory_length,
|
||||
bold=ansiprint.bold(),
|
||||
end_bold=ansiprint.end(),
|
||||
name="Name",
|
||||
memory="RAM (M)",
|
||||
)
|
||||
)
|
||||
output_list.append(
|
||||
"{bold}{memory: <{memory_length}}{end_bold}".format(
|
||||
"{bold}{name: <{name_length}} \
|
||||
{memory: <{memory_length}}{end_bold}".format(
|
||||
name_length=name_length,
|
||||
memory_length=memory_length,
|
||||
bold="",
|
||||
end_bold="",
|
||||
memory=data["memory"],
|
||||
name=name,
|
||||
memory=memory,
|
||||
)
|
||||
)
|
||||
return "\n".join(output_list)
|
||||
@@ -926,9 +946,7 @@ def vm_networks_get(config, vm):
|
||||
except Exception:
|
||||
return False, "ERROR: Failed to parse XML data."
|
||||
|
||||
data = dict()
|
||||
data["name"] = vm
|
||||
data["networks"] = list()
|
||||
network_data = list()
|
||||
for interface in parsed_xml.devices.find("interface"):
|
||||
mac_address = interface.mac.attrib.get("address")
|
||||
model = interface.model.attrib.get("type")
|
||||
@@ -942,65 +960,76 @@ def vm_networks_get(config, vm):
|
||||
elif interface_type == "hostdev":
|
||||
network = "hostdev:{}".format(interface.source.attrib.get("dev"))
|
||||
|
||||
data["networks"].append(
|
||||
{"network": network, "mac_address": mac_address, "model": model}
|
||||
)
|
||||
network_data.append((network, mac_address, model))
|
||||
|
||||
return True, data
|
||||
return True, network_data
|
||||
|
||||
|
||||
def format_vm_networks(config, data):
|
||||
def format_vm_networks(config, name, networks):
|
||||
"""
|
||||
Format the output of a network list in a nice table
|
||||
"""
|
||||
output_list = []
|
||||
|
||||
network_length = 8
|
||||
name_length = 5
|
||||
vni_length = 8
|
||||
macaddr_length = 12
|
||||
model_length = 6
|
||||
|
||||
for network in data["networks"]:
|
||||
_network_length = len(network["network"]) + 1
|
||||
if _network_length > network_length:
|
||||
network_length = _network_length
|
||||
_name_length = len(name) + 1
|
||||
if _name_length > name_length:
|
||||
name_length = _name_length
|
||||
|
||||
_macaddr_length = len(network["mac_address"]) + 1
|
||||
for network in networks:
|
||||
_vni_length = len(network[0]) + 1
|
||||
if _vni_length > vni_length:
|
||||
vni_length = _vni_length
|
||||
|
||||
_macaddr_length = len(network[1]) + 1
|
||||
if _macaddr_length > macaddr_length:
|
||||
macaddr_length = _macaddr_length
|
||||
|
||||
_model_length = len(network["model"]) + 1
|
||||
_model_length = len(network[2]) + 1
|
||||
if _model_length > model_length:
|
||||
model_length = _model_length
|
||||
|
||||
output_list.append(
|
||||
"{bold}{network: <{network_length}} \
|
||||
"{bold}{name: <{name_length}} \
|
||||
{vni: <{vni_length}} \
|
||||
{macaddr: <{macaddr_length}} \
|
||||
{model: <{model_length}}{end_bold}".format(
|
||||
network_length=network_length,
|
||||
name_length=name_length,
|
||||
vni_length=vni_length,
|
||||
macaddr_length=macaddr_length,
|
||||
model_length=model_length,
|
||||
bold=ansiprint.bold(),
|
||||
end_bold=ansiprint.end(),
|
||||
network="Network",
|
||||
name="Name",
|
||||
vni="Network",
|
||||
macaddr="MAC Address",
|
||||
model="Model",
|
||||
)
|
||||
)
|
||||
count = 0
|
||||
for network in data["networks"]:
|
||||
for network in networks:
|
||||
if count > 0:
|
||||
name = ""
|
||||
count += 1
|
||||
output_list.append(
|
||||
"{bold}{network: <{network_length}} \
|
||||
"{bold}{name: <{name_length}} \
|
||||
{vni: <{vni_length}} \
|
||||
{macaddr: <{macaddr_length}} \
|
||||
{model: <{model_length}}{end_bold}".format(
|
||||
network_length=network_length,
|
||||
name_length=name_length,
|
||||
vni_length=vni_length,
|
||||
macaddr_length=macaddr_length,
|
||||
model_length=model_length,
|
||||
bold="",
|
||||
end_bold="",
|
||||
network=network["network"],
|
||||
macaddr=network["mac_address"],
|
||||
model=network["model"],
|
||||
name=name,
|
||||
vni=network[0],
|
||||
macaddr=network[1],
|
||||
model=network[2],
|
||||
)
|
||||
)
|
||||
return "\n".join(output_list)
|
||||
@@ -1241,9 +1270,7 @@ def vm_volumes_get(config, vm):
|
||||
except Exception:
|
||||
return False, "ERROR: Failed to parse XML data."
|
||||
|
||||
data = dict()
|
||||
data["name"] = vm
|
||||
data["volumes"] = list()
|
||||
volume_data = list()
|
||||
for disk in parsed_xml.devices.find("disk"):
|
||||
protocol = disk.attrib.get("type")
|
||||
disk_id = disk.target.attrib.get("dev")
|
||||
@@ -1258,52 +1285,58 @@ def vm_volumes_get(config, vm):
|
||||
protocol = "unknown"
|
||||
source = "unknown"
|
||||
|
||||
data["volumes"].append(
|
||||
{"volume": source, "disk_id": disk_id, "protocol": protocol, "bus": bus}
|
||||
)
|
||||
volume_data.append((source, disk_id, protocol, bus))
|
||||
|
||||
return True, data
|
||||
return True, volume_data
|
||||
|
||||
|
||||
def format_vm_volumes(config, data):
|
||||
def format_vm_volumes(config, name, volumes):
|
||||
"""
|
||||
Format the output of a volume value in a nice table
|
||||
"""
|
||||
output_list = []
|
||||
|
||||
name_length = 5
|
||||
volume_length = 7
|
||||
disk_id_length = 4
|
||||
protocol_length = 5
|
||||
bus_length = 4
|
||||
|
||||
for volume in data["volumes"]:
|
||||
_volume_length = len(volume["volume"]) + 1
|
||||
_name_length = len(name) + 1
|
||||
if _name_length > name_length:
|
||||
name_length = _name_length
|
||||
|
||||
for volume in volumes:
|
||||
_volume_length = len(volume[0]) + 1
|
||||
if _volume_length > volume_length:
|
||||
volume_length = _volume_length
|
||||
|
||||
_disk_id_length = len(volume["disk_id"]) + 1
|
||||
_disk_id_length = len(volume[1]) + 1
|
||||
if _disk_id_length > disk_id_length:
|
||||
disk_id_length = _disk_id_length
|
||||
|
||||
_protocol_length = len(volume["protocol"]) + 1
|
||||
_protocol_length = len(volume[2]) + 1
|
||||
if _protocol_length > protocol_length:
|
||||
protocol_length = _protocol_length
|
||||
|
||||
_bus_length = len(volume["bus"]) + 1
|
||||
_bus_length = len(volume[3]) + 1
|
||||
if _bus_length > bus_length:
|
||||
bus_length = _bus_length
|
||||
|
||||
output_list.append(
|
||||
"{bold}{volume: <{volume_length}} \
|
||||
"{bold}{name: <{name_length}} \
|
||||
{volume: <{volume_length}} \
|
||||
{disk_id: <{disk_id_length}} \
|
||||
{protocol: <{protocol_length}} \
|
||||
{bus: <{bus_length}}{end_bold}".format(
|
||||
name_length=name_length,
|
||||
volume_length=volume_length,
|
||||
disk_id_length=disk_id_length,
|
||||
protocol_length=protocol_length,
|
||||
bus_length=bus_length,
|
||||
bold=ansiprint.bold(),
|
||||
end_bold=ansiprint.end(),
|
||||
name="Name",
|
||||
volume="Volume",
|
||||
disk_id="Dev",
|
||||
protocol="Type",
|
||||
@@ -1311,23 +1344,28 @@ def format_vm_volumes(config, data):
|
||||
)
|
||||
)
|
||||
count = 0
|
||||
for volume in data["volumes"]:
|
||||
for volume in volumes:
|
||||
if count > 0:
|
||||
name = ""
|
||||
count += 1
|
||||
output_list.append(
|
||||
"{bold}{volume: <{volume_length}} \
|
||||
"{bold}{name: <{name_length}} \
|
||||
{volume: <{volume_length}} \
|
||||
{disk_id: <{disk_id_length}} \
|
||||
{protocol: <{protocol_length}} \
|
||||
{bus: <{bus_length}}{end_bold}".format(
|
||||
name_length=name_length,
|
||||
volume_length=volume_length,
|
||||
disk_id_length=disk_id_length,
|
||||
protocol_length=protocol_length,
|
||||
bus_length=bus_length,
|
||||
bold="",
|
||||
end_bold="",
|
||||
volume=volume["volume"],
|
||||
disk_id=volume["disk_id"],
|
||||
protocol=volume["protocol"],
|
||||
bus=volume["bus"],
|
||||
name=name,
|
||||
volume=volume[0],
|
||||
disk_id=volume[1],
|
||||
protocol=volume[2],
|
||||
bus=volume[3],
|
||||
)
|
||||
)
|
||||
return "\n".join(output_list)
|
||||
@@ -1831,7 +1869,7 @@ def format_info(config, domain_information, long_output):
|
||||
return "\n".join(ainformation)
|
||||
|
||||
|
||||
def format_list(config, vm_list):
|
||||
def format_list(config, vm_list, raw):
|
||||
# Function to strip the "br" off of nets and return a nicer list
|
||||
def getNiceNetID(domain_information):
|
||||
# Network list
|
||||
@@ -1850,6 +1888,13 @@ def format_list(config, vm_list):
|
||||
tag_list.append(tag["name"])
|
||||
return tag_list
|
||||
|
||||
# Handle raw mode since it just lists the names
|
||||
if raw:
|
||||
ainformation = list()
|
||||
for vm in sorted(item["name"] for item in vm_list):
|
||||
ainformation.append(vm)
|
||||
return "\n".join(ainformation)
|
||||
|
||||
vm_list_output = []
|
||||
|
||||
# Determine optimal column widths
|
@@ -21,8 +21,7 @@
|
||||
|
||||
import json
|
||||
|
||||
import pvc.cli_lib.ansiprint as ansiprint
|
||||
from pvc.cli_lib.common import call_api
|
||||
from pvc.lib.common import call_api
|
||||
|
||||
|
||||
def initialize(config, overwrite=False):
|
||||
@@ -115,199 +114,3 @@ def get_info(config):
|
||||
return True, response.json()
|
||||
else:
|
||||
return False, response.json().get("message", "")
|
||||
|
||||
|
||||
def format_info(cluster_information, oformat):
|
||||
if oformat == "json":
|
||||
return json.dumps(cluster_information)
|
||||
|
||||
if oformat == "json-pretty":
|
||||
return json.dumps(cluster_information, indent=4)
|
||||
|
||||
# Plain formatting, i.e. human-readable
|
||||
if (
|
||||
cluster_information.get("maintenance") == "true"
|
||||
or cluster_information.get("cluster_health", {}).get("health", "N/A") == "N/A"
|
||||
):
|
||||
health_colour = ansiprint.blue()
|
||||
elif cluster_information.get("cluster_health", {}).get("health", 100) > 90:
|
||||
health_colour = ansiprint.green()
|
||||
elif cluster_information.get("cluster_health", {}).get("health", 100) > 50:
|
||||
health_colour = ansiprint.yellow()
|
||||
else:
|
||||
health_colour = ansiprint.red()
|
||||
|
||||
ainformation = []
|
||||
|
||||
ainformation.append(
|
||||
"{}PVC cluster status:{}".format(ansiprint.bold(), ansiprint.end())
|
||||
)
|
||||
ainformation.append("")
|
||||
|
||||
health_text = (
|
||||
f"{cluster_information.get('cluster_health', {}).get('health', 'N/A')}"
|
||||
)
|
||||
if health_text != "N/A":
|
||||
health_text += "%"
|
||||
if cluster_information.get("maintenance") == "true":
|
||||
health_text += " (maintenance on)"
|
||||
|
||||
ainformation.append(
|
||||
"{}Cluster health:{} {}{}{}".format(
|
||||
ansiprint.purple(),
|
||||
ansiprint.end(),
|
||||
health_colour,
|
||||
health_text,
|
||||
ansiprint.end(),
|
||||
)
|
||||
)
|
||||
if cluster_information.get("cluster_health", {}).get("messages"):
|
||||
health_messages = "\n > ".join(
|
||||
sorted(cluster_information["cluster_health"]["messages"])
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Health messages:{} > {}".format(
|
||||
ansiprint.purple(),
|
||||
ansiprint.end(),
|
||||
health_messages,
|
||||
)
|
||||
)
|
||||
else:
|
||||
ainformation.append(
|
||||
"{}Health messages:{} N/A".format(
|
||||
ansiprint.purple(),
|
||||
ansiprint.end(),
|
||||
)
|
||||
)
|
||||
|
||||
if oformat == "short":
|
||||
return "\n".join(ainformation)
|
||||
|
||||
ainformation.append("")
|
||||
ainformation.append(
|
||||
"{}Primary node:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["primary_node"]
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}PVC version:{} {}".format(
|
||||
ansiprint.purple(),
|
||||
ansiprint.end(),
|
||||
cluster_information.get("pvc_version", "N/A"),
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Cluster upstream IP:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["upstream_ip"]
|
||||
)
|
||||
)
|
||||
ainformation.append("")
|
||||
ainformation.append(
|
||||
"{}Total nodes:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["nodes"]["total"]
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Total VMs:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["vms"]["total"]
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Total networks:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["networks"]
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Total OSDs:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["osds"]["total"]
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Total pools:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["pools"]
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Total volumes:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["volumes"]
|
||||
)
|
||||
)
|
||||
ainformation.append(
|
||||
"{}Total snapshots:{} {}".format(
|
||||
ansiprint.purple(), ansiprint.end(), cluster_information["snapshots"]
|
||||
)
|
||||
)
|
||||
|
||||
nodes_string = "{}Nodes:{} {}/{} {}ready,run{}".format(
|
||||
ansiprint.purple(),
|
||||
ansiprint.end(),
|
||||
cluster_information["nodes"].get("run,ready", 0),
|
||||
cluster_information["nodes"].get("total", 0),
|
||||
ansiprint.green(),
|
||||
ansiprint.end(),
|
||||
)
|
||||
for state, count in cluster_information["nodes"].items():
|
||||
if state == "total" or state == "run,ready":
|
||||
continue
|
||||
|
||||
nodes_string += " {}/{} {}{}{}".format(
|
||||
count,
|
||||
cluster_information["nodes"]["total"],
|
||||
ansiprint.yellow(),
|
||||
state,
|
||||
ansiprint.end(),
|
||||
)
|
||||
|
||||
ainformation.append("")
|
||||
ainformation.append(nodes_string)
|
||||
|
||||
vms_string = "{}VMs:{} {}/{} {}start{}".format(
|
||||
ansiprint.purple(),
|
||||
ansiprint.end(),
|
||||
cluster_information["vms"].get("start", 0),
|
||||
cluster_information["vms"].get("total", 0),
|
||||
ansiprint.green(),
|
||||
ansiprint.end(),
|
||||
)
|
||||
for state, count in cluster_information["vms"].items():
|
||||
if state == "total" or state == "start":
|
||||
continue
|
||||
|
||||
if state in ["disable", "migrate", "unmigrate", "provision"]:
|
||||
colour = ansiprint.blue()
|
||||
else:
|
||||
colour = ansiprint.yellow()
|
||||
|
||||
vms_string += " {}/{} {}{}{}".format(
|
||||
count, cluster_information["vms"]["total"], colour, state, ansiprint.end()
|
||||
)
|
||||
|
||||
ainformation.append("")
|
||||
ainformation.append(vms_string)
|
||||
|
||||
if cluster_information["osds"]["total"] > 0:
|
||||
osds_string = "{}Ceph OSDs:{} {}/{} {}up,in{}".format(
|
||||
ansiprint.purple(),
|
||||
ansiprint.end(),
|
||||
cluster_information["osds"].get("up,in", 0),
|
||||
cluster_information["osds"].get("total", 0),
|
||||
ansiprint.green(),
|
||||
ansiprint.end(),
|
||||
)
|
||||
for state, count in cluster_information["osds"].items():
|
||||
if state == "total" or state == "up,in":
|
||||
continue
|
||||
|
||||
osds_string += " {}/{} {}{}{}".format(
|
||||
count,
|
||||
cluster_information["osds"]["total"],
|
||||
ansiprint.yellow(),
|
||||
state,
|
||||
ansiprint.end(),
|
||||
)
|
||||
|
||||
ainformation.append("")
|
||||
ainformation.append(osds_string)
|
||||
|
||||
ainformation.append("")
|
||||
return "\n".join(ainformation)
|
||||
|
@@ -20,8 +20,8 @@
|
||||
###############################################################################
|
||||
|
||||
import re
|
||||
import pvc.cli_lib.ansiprint as ansiprint
|
||||
from pvc.cli_lib.common import call_api
|
||||
import pvc.lib.ansiprint as ansiprint
|
||||
from pvc.lib.common import call_api
|
||||
|
||||
|
||||
def isValidMAC(macaddr):
|
||||
@@ -542,11 +542,16 @@ def net_sriov_vf_info(config, node, vf):
|
||||
return False, "VF not found."
|
||||
else:
|
||||
# Return a single instance if the response is a list
|
||||
data = dict()
|
||||
data["node"] = node
|
||||
if isinstance(response.json(), list):
|
||||
return True, response.json()[0]
|
||||
data = dict()
|
||||
data["vf_information"] = response.json()[0]
|
||||
return True, data
|
||||
# This shouldn't happen, but is here just in case
|
||||
else:
|
||||
return True, response.json()
|
||||
data["vf_information"] = response.json()
|
||||
return True, data
|
||||
else:
|
||||
return False, response.json().get("message", "")
|
||||
|
||||
@@ -714,7 +719,7 @@ def format_info(config, network_information, long_output):
|
||||
)
|
||||
ainformation.append("")
|
||||
if retcode:
|
||||
firewall_rules_string = format_list_acl(firewall_rules_list)
|
||||
firewall_rules_string = format_list_acl(config, firewall_rules_list)
|
||||
for line in firewall_rules_string.split("\n"):
|
||||
ainformation.append(line)
|
||||
else:
|
||||
@@ -888,7 +893,7 @@ def format_list(config, network_list):
|
||||
return "\n".join(network_list_output)
|
||||
|
||||
|
||||
def format_list_dhcp(dhcp_lease_list):
|
||||
def format_list_dhcp(config, dhcp_lease_list):
|
||||
dhcp_lease_list_output = []
|
||||
|
||||
# Determine optimal column widths
|
||||
@@ -987,7 +992,7 @@ def format_list_dhcp(dhcp_lease_list):
|
||||
return "\n".join(dhcp_lease_list_output)
|
||||
|
||||
|
||||
def format_list_acl(acl_list):
|
||||
def format_list_acl(config, acl_list):
|
||||
# Handle when we get an empty entry
|
||||
if not acl_list:
|
||||
acl_list = list()
|
||||
@@ -1086,7 +1091,7 @@ def format_list_acl(acl_list):
|
||||
return "\n".join(acl_list_output)
|
||||
|
||||
|
||||
def format_list_sriov_pf(pf_list):
|
||||
def format_list_sriov_pf(config, pf_list):
|
||||
# The maximum column width of the VFs column
|
||||
max_vfs_length = 70
|
||||
|
||||
@@ -1206,7 +1211,7 @@ def format_list_sriov_pf(pf_list):
|
||||
return "\n".join(pf_list_output)
|
||||
|
||||
|
||||
def format_list_sriov_vf(vf_list):
|
||||
def format_list_sriov_vf(config, vf_list):
|
||||
# Handle when we get an empty entry
|
||||
if not vf_list:
|
||||
vf_list = list()
|
||||
@@ -1338,10 +1343,13 @@ def format_list_sriov_vf(vf_list):
|
||||
return "\n".join(vf_list_output)
|
||||
|
||||
|
||||
def format_info_sriov_vf(config, vf_information, node):
|
||||
if not vf_information:
|
||||
def format_info_sriov_vf(config, data):
|
||||
if not data or not data["vf_information"]:
|
||||
return "No VF found"
|
||||
|
||||
node = data["node"]
|
||||
vf_information = data["vf_information"]
|
||||
|
||||
# Get information on the using VM if applicable
|
||||
if vf_information["usage"]["used"] == "True" and vf_information["usage"]["domain"]:
|
||||
vm_information = call_api(
|
||||
|
@@ -21,8 +21,8 @@
|
||||
|
||||
import time
|
||||
|
||||
import pvc.cli_lib.ansiprint as ansiprint
|
||||
from pvc.cli_lib.common import call_api
|
||||
import pvc.lib.ansiprint as ansiprint
|
||||
from pvc.lib.common import call_api
|
||||
|
||||
|
||||
#
|
||||
@@ -52,7 +52,7 @@ def node_coordinator_state(config, node, action):
|
||||
return retstatus, response.json().get("message", "")
|
||||
|
||||
|
||||
def node_domain_state(config, node, action, wait):
|
||||
def node_domain_state(config, node, action):
|
||||
"""
|
||||
Set node domain state state (flush/ready)
|
||||
|
||||
@@ -60,7 +60,7 @@ def node_domain_state(config, node, action, wait):
|
||||
API arguments: action={action}, wait={wait}
|
||||
API schema: {"message": "{data}"}
|
||||
"""
|
||||
params = {"state": action, "wait": str(wait).lower()}
|
||||
params = {"state": action}
|
||||
response = call_api(
|
||||
config, "post", "/node/{node}/domain-state".format(node=node), params=params
|
||||
)
|
||||
@@ -273,7 +273,7 @@ def getOutputColours(node_information):
|
||||
)
|
||||
|
||||
|
||||
def format_info(node_information, long_output):
|
||||
def format_info(config, node_information, long_output):
|
||||
(
|
||||
health_colour,
|
||||
daemon_state_colour,
|
||||
@@ -442,12 +442,9 @@ def format_info(node_information, long_output):
|
||||
return "\n".join(ainformation)
|
||||
|
||||
|
||||
def format_list(node_list, raw):
|
||||
if raw:
|
||||
ainformation = list()
|
||||
for node in sorted(item["name"] for item in node_list):
|
||||
ainformation.append(node)
|
||||
return "\n".join(ainformation)
|
||||
def format_list(config, node_list):
|
||||
if node_list == "Node not found.":
|
||||
return node_list
|
||||
|
||||
node_list_output = []
|
||||
|
||||
|
@@ -24,8 +24,8 @@ from requests_toolbelt.multipart.encoder import (
|
||||
MultipartEncoderMonitor,
|
||||
)
|
||||
|
||||
import pvc.cli_lib.ansiprint as ansiprint
|
||||
from pvc.cli_lib.common import UploadProgressBar, call_api
|
||||
import pvc.lib.ansiprint as ansiprint
|
||||
from pvc.lib.common import UploadProgressBar, call_api
|
||||
from ast import literal_eval
|
||||
|
||||
|
||||
@@ -750,24 +750,11 @@ def task_status(config, task_id=None, is_watching=False):
|
||||
if response.status_code == 200:
|
||||
retvalue = True
|
||||
respjson = response.json()
|
||||
|
||||
if is_watching:
|
||||
# Just return the raw JSON to the watching process instead of formatting it
|
||||
# Just return the raw JSON to the watching process instead of including value
|
||||
return respjson
|
||||
|
||||
job_state = respjson["state"]
|
||||
if job_state == "RUNNING":
|
||||
retdata = "Job state: RUNNING\nStage: {}/{}\nStatus: {}".format(
|
||||
respjson["current"], respjson["total"], respjson["status"]
|
||||
)
|
||||
elif job_state == "FAILED":
|
||||
retdata = "Job state: FAILED\nStatus: {}".format(respjson["status"])
|
||||
elif job_state == "COMPLETED":
|
||||
retdata = "Job state: COMPLETED\nStatus: {}".format(respjson["status"])
|
||||
else:
|
||||
retdata = "Job state: {}\nStatus: {}".format(
|
||||
respjson["state"], respjson["status"]
|
||||
)
|
||||
return retvalue, respjson
|
||||
else:
|
||||
retvalue = False
|
||||
retdata = response.json().get("message", "")
|
||||
@@ -814,7 +801,7 @@ def task_status(config, task_id=None, is_watching=False):
|
||||
#
|
||||
# Format functions
|
||||
#
|
||||
def format_list_template(template_data, template_type=None):
|
||||
def format_list_template(config, template_data, template_type=None):
|
||||
"""
|
||||
Format the returned template template
|
||||
|
||||
@@ -1330,7 +1317,12 @@ def format_list_template_storage(template_template):
|
||||
return "\n".join(template_list_output)
|
||||
|
||||
|
||||
def format_list_userdata(userdata_data, lines=None):
|
||||
def format_list_userdata(config, userdata_data):
|
||||
if not config.get("long_output"):
|
||||
lines = 4
|
||||
else:
|
||||
lines = None
|
||||
|
||||
if isinstance(userdata_data, dict):
|
||||
userdata_data = [userdata_data]
|
||||
|
||||
@@ -1432,7 +1424,12 @@ def format_list_userdata(userdata_data, lines=None):
|
||||
return "\n".join(userdata_list_output)
|
||||
|
||||
|
||||
def format_list_script(script_data, lines=None):
|
||||
def format_list_script(config, script_data):
|
||||
if not config.get("long_output"):
|
||||
lines = 4
|
||||
else:
|
||||
lines = None
|
||||
|
||||
if isinstance(script_data, dict):
|
||||
script_data = [script_data]
|
||||
|
||||
@@ -1531,7 +1528,7 @@ def format_list_script(script_data, lines=None):
|
||||
return "\n".join(script_list_output)
|
||||
|
||||
|
||||
def format_list_ova(ova_data):
|
||||
def format_list_ova(config, ova_data):
|
||||
if isinstance(ova_data, dict):
|
||||
ova_data = [ova_data]
|
||||
|
||||
@@ -1678,7 +1675,7 @@ def format_list_ova(ova_data):
|
||||
return "\n".join(ova_list_output)
|
||||
|
||||
|
||||
def format_list_profile(profile_data):
|
||||
def format_list_profile(config, profile_data):
|
||||
if isinstance(profile_data, dict):
|
||||
profile_data = [profile_data]
|
||||
|
||||
@@ -1867,7 +1864,23 @@ def format_list_profile(profile_data):
|
||||
return "\n".join(profile_list_output)
|
||||
|
||||
|
||||
def format_list_task(task_data):
|
||||
def format_list_task(config, task_data):
|
||||
if not isinstance(task_data, list):
|
||||
job_state = task_data["state"]
|
||||
if job_state == "RUNNING":
|
||||
retdata = "Job state: RUNNING\nStage: {}/{}\nStatus: {}".format(
|
||||
task_data["current"], task_data["total"], task_data["status"]
|
||||
)
|
||||
elif job_state == "FAILED":
|
||||
retdata = "Job state: FAILED\nStatus: {}".format(task_data["status"])
|
||||
elif job_state == "COMPLETED":
|
||||
retdata = "Job state: COMPLETED\nStatus: {}".format(task_data["status"])
|
||||
else:
|
||||
retdata = "Job state: {}\nStatus: {}".format(
|
||||
task_data["state"], task_data["status"]
|
||||
)
|
||||
return retdata
|
||||
|
||||
task_list_output = []
|
||||
|
||||
# Determine optimal column widths
|
||||
|
@@ -22,8 +22,8 @@
|
||||
import time
|
||||
import re
|
||||
|
||||
import pvc.cli_lib.ansiprint as ansiprint
|
||||
from pvc.cli_lib.common import call_api, format_bytes, format_metric
|
||||
import pvc.lib.ansiprint as ansiprint
|
||||
from pvc.lib.common import call_api, format_bytes, format_metric
|
||||
|
||||
|
||||
#
|
||||
@@ -286,20 +286,18 @@ def vm_tag_set(config, vm, action, tag, protected=False):
|
||||
return retstatus, response.json().get("message", "")
|
||||
|
||||
|
||||
def format_vm_tags(config, name, tags):
|
||||
def format_vm_tags(config, data):
|
||||
"""
|
||||
Format the output of a tags dictionary in a nice table
|
||||
"""
|
||||
|
||||
tags = data.get("tags", [])
|
||||
|
||||
if len(tags) < 1:
|
||||
return "No tags found."
|
||||
|
||||
output_list = []
|
||||
|
||||
name_length = 5
|
||||
_name_length = len(name) + 1
|
||||
if _name_length > name_length:
|
||||
name_length = _name_length
|
||||
|
||||
tags_name_length = 4
|
||||
tags_type_length = 5
|
||||
tags_protected_length = 10
|
||||
@@ -495,44 +493,38 @@ def vm_vcpus_get(config, vm):
|
||||
except Exception:
|
||||
return False, "ERROR: Failed to parse XML data."
|
||||
|
||||
vm_vcpus = int(parsed_xml.vcpu.text)
|
||||
vm_sockets = parsed_xml.cpu.topology.attrib.get("sockets")
|
||||
vm_cores = parsed_xml.cpu.topology.attrib.get("cores")
|
||||
vm_threads = parsed_xml.cpu.topology.attrib.get("threads")
|
||||
data = dict()
|
||||
data["name"] = vm
|
||||
data["vcpus"] = int(parsed_xml.vcpu.text)
|
||||
data["sockets"] = parsed_xml.cpu.topology.attrib.get("sockets")
|
||||
data["cores"] = parsed_xml.cpu.topology.attrib.get("cores")
|
||||
data["threads"] = parsed_xml.cpu.topology.attrib.get("threads")
|
||||
|
||||
return True, (vm_vcpus, (vm_sockets, vm_cores, vm_threads))
|
||||
return True, data
|
||||
|
||||
|
||||
def format_vm_vcpus(config, name, vcpus):
|
||||
def format_vm_vcpus(config, data):
|
||||
"""
|
||||
Format the output of a vCPU value in a nice table
|
||||
"""
|
||||
output_list = []
|
||||
|
||||
name_length = 5
|
||||
_name_length = len(name) + 1
|
||||
if _name_length > name_length:
|
||||
name_length = _name_length
|
||||
|
||||
vcpus_length = 6
|
||||
sockets_length = 8
|
||||
cores_length = 6
|
||||
threads_length = 8
|
||||
|
||||
output_list.append(
|
||||
"{bold}{name: <{name_length}} \
|
||||
{vcpus: <{vcpus_length}} \
|
||||
"{bold}{vcpus: <{vcpus_length}} \
|
||||
{sockets: <{sockets_length}} \
|
||||
{cores: <{cores_length}} \
|
||||
{threads: <{threads_length}}{end_bold}".format(
|
||||
name_length=name_length,
|
||||
vcpus_length=vcpus_length,
|
||||
sockets_length=sockets_length,
|
||||
cores_length=cores_length,
|
||||
threads_length=threads_length,
|
||||
bold=ansiprint.bold(),
|
||||
end_bold=ansiprint.end(),
|
||||
name="Name",
|
||||
vcpus="vCPUs",
|
||||
sockets="Sockets",
|
||||
cores="Cores",
|
||||
@@ -540,23 +532,20 @@ def format_vm_vcpus(config, name, vcpus):
|
||||
)
|
||||
)
|
||||
output_list.append(
|
||||
"{bold}{name: <{name_length}} \
|
||||
{vcpus: <{vcpus_length}} \
|
||||
"{bold}{vcpus: <{vcpus_length}} \
|
||||
{sockets: <{sockets_length}} \
|
||||
{cores: <{cores_length}} \
|
||||
{threads: <{threads_length}}{end_bold}".format(
|
||||
name_length=name_length,
|
||||
vcpus_length=vcpus_length,
|
||||
sockets_length=sockets_length,
|
||||
cores_length=cores_length,
|
||||
threads_length=threads_length,
|
||||
bold="",
|
||||
end_bold="",
|
||||
name=name,
|
||||
vcpus=vcpus[0],
|
||||
sockets=vcpus[1][0],
|
||||
cores=vcpus[1][1],
|
||||
threads=vcpus[1][2],
|
||||
vcpus=data["vcpus"],
|
||||
sockets=data["sockets"],
|
||||
cores=data["cores"],
|
||||
threads=data["threads"],
|
||||
)
|
||||
)
|
||||
return "\n".join(output_list)
|
||||
@@ -619,44 +608,35 @@ def vm_memory_get(config, vm):
|
||||
except Exception:
|
||||
return False, "ERROR: Failed to parse XML data."
|
||||
|
||||
vm_memory = int(parsed_xml.memory.text)
|
||||
data = dict()
|
||||
data["name"] = vm
|
||||
data["memory"] = int(parsed_xml.memory.text)
|
||||
|
||||
return True, vm_memory
|
||||
return True, data
|
||||
|
||||
|
||||
def format_vm_memory(config, name, memory):
|
||||
def format_vm_memory(config, data):
|
||||
"""
|
||||
Format the output of a memory value in a nice table
|
||||
"""
|
||||
output_list = []
|
||||
|
||||
name_length = 5
|
||||
_name_length = len(name) + 1
|
||||
if _name_length > name_length:
|
||||
name_length = _name_length
|
||||
|
||||
memory_length = 6
|
||||
|
||||
output_list.append(
|
||||
"{bold}{name: <{name_length}} \
|
||||
{memory: <{memory_length}}{end_bold}".format(
|
||||
name_length=name_length,
|
||||
"{bold}{memory: <{memory_length}}{end_bold}".format(
|
||||
memory_length=memory_length,
|
||||
bold=ansiprint.bold(),
|
||||
end_bold=ansiprint.end(),
|
||||
name="Name",
|
||||
memory="RAM (M)",
|
||||
)
|
||||
)
|
||||
output_list.append(
|
||||
"{bold}{name: <{name_length}} \
|
||||
{memory: <{memory_length}}{end_bold}".format(
|
||||
name_length=name_length,
|
||||
"{bold}{memory: <{memory_length}}{end_bold}".format(
|
||||
memory_length=memory_length,
|
||||
bold="",
|
||||
end_bold="",
|
||||
name=name,
|
||||
memory=memory,
|
||||
memory=data["memory"],
|
||||
)
|
||||
)
|
||||
return "\n".join(output_list)
|
||||
@@ -677,7 +657,7 @@ def vm_networks_add(
|
||||
from lxml.objectify import fromstring
|
||||
from lxml.etree import tostring
|
||||
from random import randint
|
||||
import pvc.cli_lib.network as pvc_network
|
||||
import pvc.lib.network as pvc_network
|
||||
|
||||
network_exists, _ = pvc_network.net_info(config, network)
|
||||
if not network_exists:
|
||||
@@ -946,7 +926,9 @@ def vm_networks_get(config, vm):
|
||||
except Exception:
|
||||
return False, "ERROR: Failed to parse XML data."
|
||||
|
||||
network_data = list()
|
||||
data = dict()
|
||||
data["name"] = vm
|
||||
data["networks"] = list()
|
||||
for interface in parsed_xml.devices.find("interface"):
|
||||
mac_address = interface.mac.attrib.get("address")
|
||||
model = interface.model.attrib.get("type")
|
||||
@@ -960,76 +942,65 @@ def vm_networks_get(config, vm):
|
||||
elif interface_type == "hostdev":
|
||||
network = "hostdev:{}".format(interface.source.attrib.get("dev"))
|
||||
|
||||
network_data.append((network, mac_address, model))
|
||||
data["networks"].append(
|
||||
{"network": network, "mac_address": mac_address, "model": model}
|
||||
)
|
||||
|
||||
return True, network_data
|
||||
return True, data
|
||||
|
||||
|
||||
def format_vm_networks(config, name, networks):
|
||||
def format_vm_networks(config, data):
|
||||
"""
|
||||
Format the output of a network list in a nice table
|
||||
"""
|
||||
output_list = []
|
||||
|
||||
name_length = 5
|
||||
vni_length = 8
|
||||
network_length = 8
|
||||
macaddr_length = 12
|
||||
model_length = 6
|
||||
|
||||
_name_length = len(name) + 1
|
||||
if _name_length > name_length:
|
||||
name_length = _name_length
|
||||
for network in data["networks"]:
|
||||
_network_length = len(network["network"]) + 1
|
||||
if _network_length > network_length:
|
||||
network_length = _network_length
|
||||
|
||||
for network in networks:
|
||||
_vni_length = len(network[0]) + 1
|
||||
if _vni_length > vni_length:
|
||||
vni_length = _vni_length
|
||||
|
||||
_macaddr_length = len(network[1]) + 1
|
||||
_macaddr_length = len(network["mac_address"]) + 1
|
||||
if _macaddr_length > macaddr_length:
|
||||
macaddr_length = _macaddr_length
|
||||
|
||||
_model_length = len(network[2]) + 1
|
||||
_model_length = len(network["model"]) + 1
|
||||
if _model_length > model_length:
|
||||
model_length = _model_length
|
||||
|
||||
output_list.append(
|
||||
"{bold}{name: <{name_length}} \
|
||||
{vni: <{vni_length}} \
|
||||
"{bold}{network: <{network_length}} \
|
||||
{macaddr: <{macaddr_length}} \
|
||||
{model: <{model_length}}{end_bold}".format(
|
||||
name_length=name_length,
|
||||
vni_length=vni_length,
|
||||
network_length=network_length,
|
||||
macaddr_length=macaddr_length,
|
||||
model_length=model_length,
|
||||
bold=ansiprint.bold(),
|
||||
end_bold=ansiprint.end(),
|
||||
name="Name",
|
||||
vni="Network",
|
||||
network="Network",
|
||||
macaddr="MAC Address",
|
||||
model="Model",
|
||||
)
|
||||
)
|
||||
count = 0
|
||||
for network in networks:
|
||||
if count > 0:
|
||||
name = ""
|
||||
for network in data["networks"]:
|
||||
count += 1
|
||||
output_list.append(
|
||||
"{bold}{name: <{name_length}} \
|
||||
{vni: <{vni_length}} \
|
||||
"{bold}{network: <{network_length}} \
|
||||
{macaddr: <{macaddr_length}} \
|
||||
{model: <{model_length}}{end_bold}".format(
|
||||
name_length=name_length,
|
||||
vni_length=vni_length,
|
||||
network_length=network_length,
|
||||
macaddr_length=macaddr_length,
|
||||
model_length=model_length,
|
||||
bold="",
|
||||
end_bold="",
|
||||
name=name,
|
||||
vni=network[0],
|
||||
macaddr=network[1],
|
||||
model=network[2],
|
||||
network=network["network"],
|
||||
macaddr=network["mac_address"],
|
||||
model=network["model"],
|
||||
)
|
||||
)
|
||||
return "\n".join(output_list)
|
||||
@@ -1046,7 +1017,7 @@ def vm_volumes_add(config, vm, volume, disk_id, bus, disk_type, live, restart):
|
||||
from lxml.objectify import fromstring
|
||||
from lxml.etree import tostring
|
||||
from copy import deepcopy
|
||||
import pvc.cli_lib.ceph as pvc_ceph
|
||||
import pvc.lib.ceph as pvc_ceph
|
||||
|
||||
if disk_type == "rbd":
|
||||
# Verify that the provided volume is valid
|
||||
@@ -1270,7 +1241,9 @@ def vm_volumes_get(config, vm):
|
||||
except Exception:
|
||||
return False, "ERROR: Failed to parse XML data."
|
||||
|
||||
volume_data = list()
|
||||
data = dict()
|
||||
data["name"] = vm
|
||||
data["volumes"] = list()
|
||||
for disk in parsed_xml.devices.find("disk"):
|
||||
protocol = disk.attrib.get("type")
|
||||
disk_id = disk.target.attrib.get("dev")
|
||||
@@ -1285,58 +1258,52 @@ def vm_volumes_get(config, vm):
|
||||
protocol = "unknown"
|
||||
source = "unknown"
|
||||
|
||||
volume_data.append((source, disk_id, protocol, bus))
|
||||
data["volumes"].append(
|
||||
{"volume": source, "disk_id": disk_id, "protocol": protocol, "bus": bus}
|
||||
)
|
||||
|
||||
return True, volume_data
|
||||
return True, data
|
||||
|
||||
|
||||
def format_vm_volumes(config, name, volumes):
|
||||
def format_vm_volumes(config, data):
|
||||
"""
|
||||
Format the output of a volume value in a nice table
|
||||
"""
|
||||
output_list = []
|
||||
|
||||
name_length = 5
|
||||
volume_length = 7
|
||||
disk_id_length = 4
|
||||
protocol_length = 5
|
||||
bus_length = 4
|
||||
|
||||
_name_length = len(name) + 1
|
||||
if _name_length > name_length:
|
||||
name_length = _name_length
|
||||
|
||||
for volume in volumes:
|
||||
_volume_length = len(volume[0]) + 1
|
||||
for volume in data["volumes"]:
|
||||
_volume_length = len(volume["volume"]) + 1
|
||||
if _volume_length > volume_length:
|
||||
volume_length = _volume_length
|
||||
|
||||
_disk_id_length = len(volume[1]) + 1
|
||||
_disk_id_length = len(volume["disk_id"]) + 1
|
||||
if _disk_id_length > disk_id_length:
|
||||
disk_id_length = _disk_id_length
|
||||
|
||||
_protocol_length = len(volume[2]) + 1
|
||||
_protocol_length = len(volume["protocol"]) + 1
|
||||
if _protocol_length > protocol_length:
|
||||
protocol_length = _protocol_length
|
||||
|
||||
_bus_length = len(volume[3]) + 1
|
||||
_bus_length = len(volume["bus"]) + 1
|
||||
if _bus_length > bus_length:
|
||||
bus_length = _bus_length
|
||||
|
||||
output_list.append(
|
||||
"{bold}{name: <{name_length}} \
|
||||
{volume: <{volume_length}} \
|
||||
"{bold}{volume: <{volume_length}} \
|
||||
{disk_id: <{disk_id_length}} \
|
||||
{protocol: <{protocol_length}} \
|
||||
{bus: <{bus_length}}{end_bold}".format(
|
||||
name_length=name_length,
|
||||
volume_length=volume_length,
|
||||
disk_id_length=disk_id_length,
|
||||
protocol_length=protocol_length,
|
||||
bus_length=bus_length,
|
||||
bold=ansiprint.bold(),
|
||||
end_bold=ansiprint.end(),
|
||||
name="Name",
|
||||
volume="Volume",
|
||||
disk_id="Dev",
|
||||
protocol="Type",
|
||||
@@ -1344,28 +1311,23 @@ def format_vm_volumes(config, name, volumes):
|
||||
)
|
||||
)
|
||||
count = 0
|
||||
for volume in volumes:
|
||||
if count > 0:
|
||||
name = ""
|
||||
for volume in data["volumes"]:
|
||||
count += 1
|
||||
output_list.append(
|
||||
"{bold}{name: <{name_length}} \
|
||||
{volume: <{volume_length}} \
|
||||
"{bold}{volume: <{volume_length}} \
|
||||
{disk_id: <{disk_id_length}} \
|
||||
{protocol: <{protocol_length}} \
|
||||
{bus: <{bus_length}}{end_bold}".format(
|
||||
name_length=name_length,
|
||||
volume_length=volume_length,
|
||||
disk_id_length=disk_id_length,
|
||||
protocol_length=protocol_length,
|
||||
bus_length=bus_length,
|
||||
bold="",
|
||||
end_bold="",
|
||||
name=name,
|
||||
volume=volume[0],
|
||||
disk_id=volume[1],
|
||||
protocol=volume[2],
|
||||
bus=volume[3],
|
||||
volume=volume["volume"],
|
||||
disk_id=volume["disk_id"],
|
||||
protocol=volume["protocol"],
|
||||
bus=volume["bus"],
|
||||
)
|
||||
)
|
||||
return "\n".join(output_list)
|
||||
@@ -1869,7 +1831,7 @@ def format_info(config, domain_information, long_output):
|
||||
return "\n".join(ainformation)
|
||||
|
||||
|
||||
def format_list(config, vm_list, raw):
|
||||
def format_list(config, vm_list):
|
||||
# Function to strip the "br" off of nets and return a nicer list
|
||||
def getNiceNetID(domain_information):
|
||||
# Network list
|
||||
@@ -1888,13 +1850,6 @@ def format_list(config, vm_list, raw):
|
||||
tag_list.append(tag["name"])
|
||||
return tag_list
|
||||
|
||||
# Handle raw mode since it just lists the names
|
||||
if raw:
|
||||
ainformation = list()
|
||||
for vm in sorted(item["name"] for item in vm_list):
|
||||
ainformation.append(vm)
|
||||
return "\n".join(ainformation)
|
||||
|
||||
vm_list_output = []
|
||||
|
||||
# Determine optimal column widths
|
||||
|
Reference in New Issue
Block a user