Compare commits

..

15 Commits

13 changed files with 166 additions and 74 deletions

View File

@ -1 +1 @@
0.9.66 0.9.70

View File

@ -1,5 +1,22 @@
## PVC Changelog ## PVC Changelog
###### [v0.9.70](https://github.com/parallelvirtualcluster/pvc/releases/tag/v0.9.70)
* [Node Daemon] Fixes several compatibility issues for Debian 12 "Bookworm"
###### [v0.9.69](https://github.com/parallelvirtualcluster/pvc/releases/tag/v0.9.69)
* [Node Daemon] Ensures that system load is always 2 decimal places on Bookworm
* [Node Daemon] Fixes bug blocking primary takeover at DNS Aggregator start if Patroni is down
###### [v0.9.68](https://github.com/parallelvirtualcluster/pvc/releases/tag/v0.9.68)
* [CLI] Fixes another bug with network info view
###### [v0.9.67](https://github.com/parallelvirtualcluster/pvc/releases/tag/v0.9.67)
* [CLI] Fixes several more bugs in the refactored CLI
###### [v0.9.66](https://github.com/parallelvirtualcluster/pvc/releases/tag/v0.9.66) ###### [v0.9.66](https://github.com/parallelvirtualcluster/pvc/releases/tag/v0.9.66)
* [CLI] Fixes a missing YAML import in CLI * [CLI] Fixes a missing YAML import in CLI

View File

@ -19,8 +19,7 @@
# #
############################################################################### ###############################################################################
from flask_migrate import Migrate, MigrateCommand from flask_migrate import Migrate, MigrateCommand, Manager
from flask_script import Manager
from pvcapid.flaskapi import app, db from pvcapid.flaskapi import app, db
from pvcapid.models import * # noqa F401,F403 from pvcapid.models import * # noqa F401,F403

View File

@ -27,7 +27,7 @@ from ssl import SSLContext, TLSVersion
from distutils.util import strtobool as dustrtobool from distutils.util import strtobool as dustrtobool
# Daemon version # Daemon version
version = "0.9.66" version = "0.9.70"
# API version # API version
API_VERSION = 1.0 API_VERSION = 1.0

View File

@ -19,6 +19,8 @@
# #
############################################################################### ###############################################################################
from colorama import Fore
from difflib import unified_diff
from functools import wraps from functools import wraps
from json import dump as jdump from json import dump as jdump
from json import dumps as jdumps from json import dumps as jdumps
@ -26,7 +28,7 @@ from json import loads as jloads
from os import environ, makedirs, path from os import environ, makedirs, path
from pkg_resources import get_distribution from pkg_resources import get_distribution
from lxml.etree import fromstring, tostring from lxml.etree import fromstring, tostring
from re import sub from re import sub, match
from yaml import load as yload from yaml import load as yload
from yaml import SafeLoader as SafeYAMLLoader from yaml import SafeLoader as SafeYAMLLoader
@ -1196,7 +1198,7 @@ def cli_vm_modify(
text=current_vm_cfgfile, require_save=True, extension=".xml" text=current_vm_cfgfile, require_save=True, extension=".xml"
) )
if new_vm_cfgfile is None: if new_vm_cfgfile is None:
echo("Aborting with no modifications.") echo(CLI_CONFIG, "Aborting with no modifications.")
exit(0) exit(0)
else: else:
new_vm_cfgfile = new_vm_cfgfile.strip() new_vm_cfgfile = new_vm_cfgfile.strip()
@ -1208,16 +1210,14 @@ def cli_vm_modify(
cfgfile.close() cfgfile.close()
echo( echo(
CLI_CONFIG,
'Replacing configuration of VM "{}" with file "{}".'.format( 'Replacing configuration of VM "{}" with file "{}".'.format(
dom_name, cfgfile.name dom_name, cfgfile.name
) ),
) )
# Show a diff and confirm
echo("Pending modifications:")
echo("")
diff = list( diff = list(
difflib.unified_diff( unified_diff(
current_vm_cfgfile.split("\n"), current_vm_cfgfile.split("\n"),
new_vm_cfgfile.split("\n"), new_vm_cfgfile.split("\n"),
fromfile="current", fromfile="current",
@ -1228,16 +1228,23 @@ def cli_vm_modify(
lineterm="", lineterm="",
) )
) )
if len(diff) < 1:
echo(CLI_CONFIG, "Aborting with no modifications.")
exit(0)
# Show a diff and confirm
echo(CLI_CONFIG, "Pending modifications:")
echo(CLI_CONFIG, "")
for line in diff: for line in diff:
if re.match(r"^\+", line) is not None: if match(r"^\+", line) is not None:
echo(colorama.Fore.GREEN + line + colorama.Fore.RESET) echo(CLI_CONFIG, Fore.GREEN + line + Fore.RESET)
elif re.match(r"^\-", line) is not None: elif match(r"^\-", line) is not None:
echo(colorama.Fore.RED + line + colorama.Fore.RESET) echo(CLI_CONFIG, Fore.RED + line + Fore.RESET)
elif re.match(r"^\^", line) is not None: elif match(r"^\^", line) is not None:
echo(colorama.Fore.BLUE + line + colorama.Fore.RESET) echo(CLI_CONFIG, Fore.BLUE + line + Fore.RESET)
else: else:
echo(line) echo(CLI_CONFIG, line)
echo("") echo(CLI_CONFIG, "")
# Verify our XML is sensible # Verify our XML is sensible
try: try:
@ -3594,7 +3601,7 @@ def cli_storage_volume_upload(pool, name, image_format, image_file):
""" """
if not os.path.exists(image_file): if not os.path.exists(image_file):
echo("ERROR: File '{}' does not exist!".format(image_file)) echo(CLI_CONFIG, "ERROR: File '{}' does not exist!".format(image_file))
exit(1) exit(1)
retcode, retmsg = pvc.lib.storage.ceph_volume_upload( retcode, retmsg = pvc.lib.storage.ceph_volume_upload(
@ -4432,7 +4439,8 @@ def cli_provisioner_template_storage_disk_add(
if source_volume and (size or filesystem or mountpoint): if source_volume and (size or filesystem or mountpoint):
echo( echo(
'The "--source-volume" option is not compatible with the "--size", "--filesystem", or "--mountpoint" options.' CLI_CONFIG,
'The "--source-volume" option is not compatible with the "--size", "--filesystem", or "--mountpoint" options.',
) )
exit(1) exit(1)
@ -4514,7 +4522,7 @@ def cli_provisioner_userdata_add(name, filename):
try: try:
yload(userdata, Loader=SafeYAMLLoader) yload(userdata, Loader=SafeYAMLLoader)
except Exception as e: except Exception as e:
echo("Error: Userdata document is malformed") echo(CLI_CONFIG, "Error: Userdata document is malformed")
cleanup(False, e) cleanup(False, e)
params = dict() params = dict()
@ -4551,7 +4559,7 @@ def cli_provisioner_userdata_modify(name, filename, editor):
# Grab the current config # Grab the current config
retcode, retdata = pvc.lib.provisioner.userdata_info(CLI_CONFIG, name) retcode, retdata = pvc.lib.provisioner.userdata_info(CLI_CONFIG, name)
if not retcode: if not retcode:
echo(retdata) echo(CLI_CONFIG, retdata)
exit(1) exit(1)
current_userdata = retdata["userdata"].strip() current_userdata = retdata["userdata"].strip()
@ -4559,16 +4567,14 @@ def cli_provisioner_userdata_modify(name, filename, editor):
text=current_userdata, require_save=True, extension=".yaml" text=current_userdata, require_save=True, extension=".yaml"
) )
if new_userdata is None: if new_userdata is None:
echo("Aborting with no modifications.") echo(CLI_CONFIG, "Aborting with no modifications.")
exit(0) exit(0)
else: else:
new_userdata = new_userdata.strip() new_userdata = new_userdata.strip()
# Show a diff and confirm # Show a diff and confirm
echo("Pending modifications:")
echo("")
diff = list( diff = list(
difflib.unified_diff( unified_diff(
current_userdata.split("\n"), current_userdata.split("\n"),
new_userdata.split("\n"), new_userdata.split("\n"),
fromfile="current", fromfile="current",
@ -4579,16 +4585,22 @@ def cli_provisioner_userdata_modify(name, filename, editor):
lineterm="", lineterm="",
) )
) )
if len(diff) < 1:
echo(CLI_CONFIG, "Aborting with no modifications.")
exit(0)
echo(CLI_CONFIG, "Pending modifications:")
echo(CLI_CONFIG, "")
for line in diff: for line in diff:
if re.match(r"^\+", line) is not None: if match(r"^\+", line) is not None:
echo(colorama.Fore.GREEN + line + colorama.Fore.RESET) echo(CLI_CONFIG, Fore.GREEN + line + Fore.RESET)
elif re.match(r"^\-", line) is not None: elif match(r"^\-", line) is not None:
echo(colorama.Fore.RED + line + colorama.Fore.RESET) echo(CLI_CONFIG, Fore.RED + line + Fore.RESET)
elif re.match(r"^\^", line) is not None: elif match(r"^\^", line) is not None:
echo(colorama.Fore.BLUE + line + colorama.Fore.RESET) echo(CLI_CONFIG, Fore.BLUE + line + Fore.RESET)
else: else:
echo(line) echo(CLI_CONFIG, line)
echo("") echo(CLI_CONFIG, "")
click.confirm("Write modifications to cluster?", abort=True) click.confirm("Write modifications to cluster?", abort=True)
@ -4603,7 +4615,7 @@ def cli_provisioner_userdata_modify(name, filename, editor):
try: try:
yload(userdata, Loader=SafeYAMLLoader) yload(userdata, Loader=SafeYAMLLoader)
except Exception as e: except Exception as e:
echo("Error: Userdata document is malformed") echo(CLI_CONFIG, "Error: Userdata document is malformed")
cleanup(False, e) cleanup(False, e)
params = dict() params = dict()
@ -4741,22 +4753,20 @@ def cli_provisioner_script_modify(name, filename, editor):
# Grab the current config # Grab the current config
retcode, retdata = pvc.lib.provisioner.script_info(CLI_CONFIG, name) retcode, retdata = pvc.lib.provisioner.script_info(CLI_CONFIG, name)
if not retcode: if not retcode:
echo(retdata) echo(CLI_CONFIG, retdata)
exit(1) exit(1)
current_script = retdata["script"].strip() current_script = retdata["script"].strip()
new_script = click.edit(text=current_script, require_save=True, extension=".py") new_script = click.edit(text=current_script, require_save=True, extension=".py")
if new_script is None: if new_script is None:
echo("Aborting with no modifications.") echo(CLI_CONFIG, "Aborting with no modifications.")
exit(0) exit(0)
else: else:
new_script = new_script.strip() new_script = new_script.strip()
# Show a diff and confirm # Show a diff and confirm
echo("Pending modifications:")
echo("")
diff = list( diff = list(
difflib.unified_diff( unified_diff(
current_script.split("\n"), current_script.split("\n"),
new_script.split("\n"), new_script.split("\n"),
fromfile="current", fromfile="current",
@ -4767,16 +4777,22 @@ def cli_provisioner_script_modify(name, filename, editor):
lineterm="", lineterm="",
) )
) )
if len(diff) < 1:
echo(CLI_CONFIG, "Aborting with no modifications.")
exit(0)
echo(CLI_CONFIG, "Pending modifications:")
echo(CLI_CONFIG, "")
for line in diff: for line in diff:
if re.match(r"^\+", line) is not None: if match(r"^\+", line) is not None:
echo(colorama.Fore.GREEN + line + colorama.Fore.RESET) echo(CLI_CONFIG, Fore.GREEN + line + Fore.RESET)
elif re.match(r"^\-", line) is not None: elif match(r"^\-", line) is not None:
echo(colorama.Fore.RED + line + colorama.Fore.RESET) echo(CLI_CONFIG, Fore.RED + line + Fore.RESET)
elif re.match(r"^\^", line) is not None: elif match(r"^\^", line) is not None:
echo(colorama.Fore.BLUE + line + colorama.Fore.RESET) echo(CLI_CONFIG, Fore.BLUE + line + Fore.RESET)
else: else:
echo(line) echo(CLI_CONFIG, line)
echo("") echo(CLI_CONFIG, "")
click.confirm("Write modifications to cluster?", abort=True) click.confirm("Write modifications to cluster?", abort=True)
@ -4897,7 +4913,7 @@ def cli_provisioner_ova_upload(name, filename, pool):
""" """
if not os.path.exists(filename): if not os.path.exists(filename):
echo("ERROR: File '{}' does not exist!".format(filename)) echo(CLI_CONFIG, "ERROR: File '{}' does not exist!".format(filename))
exit(1) exit(1)
params = dict() params = dict()

View File

@ -700,7 +700,7 @@ def format_info(config, network_information, long_output):
ainformation.append("") ainformation.append("")
if retcode: if retcode:
dhcp4_reservations_string = format_list_dhcp( dhcp4_reservations_string = format_list_dhcp(
dhcp4_reservations_list config, dhcp4_reservations_list
) )
for line in dhcp4_reservations_string.split("\n"): for line in dhcp4_reservations_string.split("\n"):
ainformation.append(line) ainformation.append(line)

View File

@ -2,7 +2,7 @@ from setuptools import setup
setup( setup(
name="pvc", name="pvc",
version="0.9.66", version="0.9.70",
packages=["pvc.cli", "pvc.lib"], packages=["pvc.cli", "pvc.lib"],
install_requires=[ install_requires=[
"Click", "Click",

25
debian/changelog vendored
View File

@ -1,3 +1,28 @@
pvc (0.9.70-0) unstable; urgency=high
* [Node Daemon] Fixes several compatibility issues for Debian 12 "Bookworm"
-- Joshua M. Boniface <joshua@boniface.me> Thu, 31 Aug 2023 14:15:54 -0400
pvc (0.9.69-0) unstable; urgency=high
* [Node Daemon] Ensures that system load is always 2 decimal places on Bookworm
* [Node Daemon] Fixes bug blocking primary takeover at DNS Aggregator start if Patroni is down
-- Joshua M. Boniface <joshua@boniface.me> Tue, 29 Aug 2023 22:01:22 -0400
pvc (0.9.68-0) unstable; urgency=high
* [CLI] Fixes another bug with network info view
-- Joshua M. Boniface <joshua@boniface.me> Sun, 27 Aug 2023 20:59:23 -0400
pvc (0.9.67-0) unstable; urgency=high
* [CLI] Fixes several more bugs in the refactored CLI
-- Joshua M. Boniface <joshua@boniface.me> Sun, 27 Aug 2023 14:47:20 -0400
pvc (0.9.66-0) unstable; urgency=high pvc (0.9.66-0) unstable; urgency=high
* [CLI] Fixes a missing YAML import in CLI * [CLI] Fixes a missing YAML import in CLI

2
debian/control vendored
View File

@ -16,7 +16,7 @@ Description: Parallel Virtual Cluster node daemon (Python 3)
Package: pvc-daemon-api Package: pvc-daemon-api
Architecture: all Architecture: all
Depends: systemd, pvc-daemon-common, python3-yaml, python3-flask, python3-flask-restful, python3-celery, python-celery-common, python3-distutils, redis, python3-redis, python3-lxml, python3-flask-migrate, python3-flask-script, fio Depends: systemd, pvc-daemon-common, python3-yaml, python3-flask, python3-flask-restful, python3-celery, python-celery-common, python3-distutils, redis, python3-redis, python3-lxml, python3-flask-migrate, fio
Description: Parallel Virtual Cluster API daemon (Python 3) Description: Parallel Virtual Cluster API daemon (Python 3)
A KVM/Zookeeper/Ceph-based VM and private cloud manager A KVM/Zookeeper/Ceph-based VM and private cloud manager
. .

View File

@ -49,7 +49,7 @@ import re
import json import json
# Daemon version # Daemon version
version = "0.9.66" version = "0.9.70"
########################################################## ##########################################################

View File

@ -77,7 +77,7 @@ def connect_zookeeper():
with open(pvcnoded_config_file, "r") as cfgfile: with open(pvcnoded_config_file, "r") as cfgfile:
try: try:
o_config = yaml.load(cfgfile) o_config = yaml.load(cfgfile, yaml.SafeLoader)
except Exception as e: except Exception as e:
print( print(
"ERROR: Failed to parse configuration file: {}".format(e), "ERROR: Failed to parse configuration file: {}".format(e),

View File

@ -620,9 +620,12 @@ class NodeInstance(object):
for network in self.d_network: for network in self.d_network:
self.d_network[network].startDHCPServer() self.d_network[network].startDHCPServer()
# 9. Start DNS aggregator; just continue if we fail # 9. Start DNS aggregator; just continue if we fail
if not patroni_failed: try:
self.dns_aggregator.start_aggregator() if not patroni_failed:
else: self.dns_aggregator.start_aggregator()
else:
raise
except Exception:
self.logger.out( self.logger.out(
"Not starting DNS aggregator due to Patroni failures", state="e" "Not starting DNS aggregator due to Patroni failures", state="e"
) )

View File

@ -338,8 +338,21 @@ def collect_ceph_stats(logger, config, zkhandler, this_node, queue):
line = re.sub(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))", "", line) line = re.sub(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))", "", line)
# Split it for parsing # Split it for parsing
line = line.split() line = line.split()
if len(line) > 1 and line[1].isdigit():
# This is an OSD line so parse it # Ceph 14 format:
# ['|', '0', '|', 'hv1.p.u.bonilan.net', '|', '318G', '|', '463G', '|', '213', '|', '1430k', '|', '22', '|', '124k', '|', 'exists,up', '|']
# Ceph 16 format:
# ['0', 'hv1.t.u.bonilan.net', '2489M', '236G', '0', '0', '0', '0', 'exists,up']
# Bypass obviously invalid lines
if len(line) < 1:
continue
elif line[0] == "+":
continue
# If line begins with | and second entry is a digit (i.e. OSD ID)
if line[0] == "|" and line[1].isdigit():
# Parse the line in Ceph 14 format
osd_id = line[1] osd_id = line[1]
node = line[3].split(".")[0] node = line[3].split(".")[0]
used = line[5] used = line[5]
@ -349,20 +362,39 @@ def collect_ceph_stats(logger, config, zkhandler, this_node, queue):
rd_ops = line[13] rd_ops = line[13]
rd_data = line[15] rd_data = line[15]
state = line[17] state = line[17]
osd_status.update( # If first entry is a digit (i.e. OSD ID)
{ elif line[0].isdigit():
str(osd_id): { # Parse the line in Ceph 16 format
"node": node, osd_id = line[0]
"used": used, node = line[1].split(".")[0]
"avail": avail, used = line[2]
"wr_ops": wr_ops, avail = line[3]
"wr_data": wr_data, wr_ops = line[4]
"rd_ops": rd_ops, wr_data = line[5]
"rd_data": rd_data, rd_ops = line[6]
"state": state, rd_data = line[7]
} state = line[8]
# Otherwise, it's the header line and is ignored
else:
continue
# I don't know why 2018 me used this construct instead of a normal
# dictionary update, but it works so not changing it.
# ref: bfbe9188ce830381f3f2fa1da11f1973f08eca8c
osd_status.update(
{
str(osd_id): {
"node": node,
"used": used,
"avail": avail,
"wr_ops": wr_ops,
"wr_data": wr_data,
"rd_ops": rd_ops,
"rd_data": rd_data,
"state": state,
} }
) }
)
# Merge them together into a single meaningful dict # Merge them together into a single meaningful dict
if debug: if debug:
@ -753,7 +785,7 @@ def node_keepalive(logger, config, zkhandler, this_node, monitoring_instance):
this_node.memtotal = int(psutil.virtual_memory().total / 1024 / 1024) this_node.memtotal = int(psutil.virtual_memory().total / 1024 / 1024)
this_node.memused = int(psutil.virtual_memory().used / 1024 / 1024) this_node.memused = int(psutil.virtual_memory().used / 1024 / 1024)
this_node.memfree = int(psutil.virtual_memory().free / 1024 / 1024) this_node.memfree = int(psutil.virtual_memory().free / 1024 / 1024)
this_node.cpuload = os.getloadavg()[0] this_node.cpuload = round(os.getloadavg()[0], 2)
# Join against running threads # Join against running threads
if config["enable_hypervisor"]: if config["enable_hypervisor"]: