Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
58d57d7037 | |||
00d2c67c41 | |||
67131de4f6 | |||
abc23ebb18 | |||
9f122e916f | |||
3ce4d90693 | |||
6ccd19e636 | |||
d8689e6eaa | |||
bc49b5eca2 | |||
8470dfaa29 |
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,5 +1,17 @@
|
||||
## PVC Changelog
|
||||
|
||||
###### [v0.9.46](https://github.com/parallelvirtualcluster/pvc/releases/tag/v0.9.46)
|
||||
|
||||
* [API] Fixes bugs with legacy benchmark display
|
||||
* [API] Fixes a bug around cloned image sizes
|
||||
* [API] Removes extraneous message text in provisioner create command
|
||||
* [API] Corrects bugs around fuzzy matching
|
||||
* [CLI] Adds auditing for PVC CLI to local syslog
|
||||
* [CLI] Adds --yes bypass for benchmark command
|
||||
* [Node Daemon/API/CLI] Adds support for "detect" strings when specifying OSD or OSDDB devices
|
||||
* [Node Daemon] Fixes a bug when removing OSDs
|
||||
* [Node Daemon] Fixes a single-node cluster shutdown deadlock
|
||||
|
||||
###### [v0.9.45](https://github.com/parallelvirtualcluster/pvc/releases/tag/v0.9.45)
|
||||
|
||||
* [Node Daemon] Fixes an ordering issue with pvcnoded.service
|
||||
|
@ -25,7 +25,7 @@ import yaml
|
||||
from distutils.util import strtobool as dustrtobool
|
||||
|
||||
# Daemon version
|
||||
version = "0.9.45"
|
||||
version = "0.9.46"
|
||||
|
||||
# API version
|
||||
API_VERSION = 1.0
|
||||
|
@ -3849,7 +3849,7 @@ class API_Storage_Ceph_OSDDB_Root(Resource):
|
||||
{
|
||||
"name": "device",
|
||||
"required": True,
|
||||
"helptext": "A valid device must be specified.",
|
||||
"helptext": "A valid device or detect string must be specified.",
|
||||
},
|
||||
]
|
||||
)
|
||||
@ -3871,7 +3871,7 @@ class API_Storage_Ceph_OSDDB_Root(Resource):
|
||||
name: device
|
||||
type: string
|
||||
required: true
|
||||
description: The block device (e.g. "/dev/sdb", "/dev/disk/by-path/...", etc.) to create the OSD DB volume group on
|
||||
description: The block device (e.g. "/dev/sdb", "/dev/disk/by-path/...", etc.) or detect string ("detect:NAME:SIZE:ID") to create the OSD DB volume group on
|
||||
responses:
|
||||
200:
|
||||
description: OK
|
||||
@ -4003,7 +4003,7 @@ class API_Storage_Ceph_OSD_Root(Resource):
|
||||
{
|
||||
"name": "device",
|
||||
"required": True,
|
||||
"helptext": "A valid device must be specified.",
|
||||
"helptext": "A valid device or detect string must be specified.",
|
||||
},
|
||||
{
|
||||
"name": "weight",
|
||||
@ -4040,7 +4040,7 @@ class API_Storage_Ceph_OSD_Root(Resource):
|
||||
name: device
|
||||
type: string
|
||||
required: true
|
||||
description: The block device (e.g. "/dev/sdb", "/dev/disk/by-path/...", etc.) to create the OSD on
|
||||
description: The block device (e.g. "/dev/sdb", "/dev/disk/by-path/...", etc.) or detect string ("detect:NAME:SIZE:ID") to create the OSD on
|
||||
- in: query
|
||||
name: weight
|
||||
type: number
|
||||
|
@ -1442,11 +1442,17 @@ def create_vm(
|
||||
)
|
||||
if not volume["pool"] in pools:
|
||||
pools[volume["pool"]] = int(
|
||||
volume_data["stats"]["size"].replace("G", "")
|
||||
pvc_ceph.format_bytes_fromhuman(volume_data["stats"]["size"])
|
||||
/ 1024
|
||||
/ 1024
|
||||
/ 1024
|
||||
)
|
||||
else:
|
||||
pools[volume["pool"]] += int(
|
||||
volume_data["stats"]["size"].replace("G", "")
|
||||
pvc_ceph.format_bytes_fromhuman(volume_data["stats"]["size"])
|
||||
/ 1024
|
||||
/ 1024
|
||||
/ 1024
|
||||
)
|
||||
else:
|
||||
if not volume["pool"] in pools:
|
||||
@ -2080,7 +2086,7 @@ def create_vm(
|
||||
del zkhandler
|
||||
|
||||
return {
|
||||
"status": 'VM "{}" with profile "{}" has been provisioned and started successfully'.format(
|
||||
"status": 'VM "{}" with profile "{}" has been provisioned successfully'.format(
|
||||
vm_name, vm_profile
|
||||
),
|
||||
"current": 10,
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
import math
|
||||
|
||||
from json import dumps
|
||||
from json import dumps, loads
|
||||
from requests_toolbelt.multipart.encoder import (
|
||||
MultipartEncoder,
|
||||
MultipartEncoderMonitor,
|
||||
@ -1648,6 +1648,8 @@ def ceph_benchmark_list(config, job):
|
||||
|
||||
|
||||
def get_benchmark_list_results_legacy(benchmark_data):
|
||||
if isinstance(benchmark_data, str):
|
||||
benchmark_data = loads(benchmark_data)
|
||||
benchmark_bandwidth = dict()
|
||||
benchmark_iops = dict()
|
||||
for test in ["seq_read", "seq_write", "rand_read_4K", "rand_write_4K"]:
|
||||
@ -1732,7 +1734,7 @@ def format_list_benchmark(config, benchmark_information):
|
||||
|
||||
for benchmark in benchmark_information:
|
||||
benchmark_job = benchmark["job"]
|
||||
benchmark_format = benchmark["test_format"] # noqa: F841
|
||||
benchmark_format = benchmark.get("test_format", 0) # noqa: F841
|
||||
|
||||
_benchmark_job_length = len(benchmark_job)
|
||||
if _benchmark_job_length > benchmark_job_length:
|
||||
@ -1837,7 +1839,7 @@ def format_list_benchmark(config, benchmark_information):
|
||||
|
||||
for benchmark in benchmark_information:
|
||||
benchmark_job = benchmark["job"]
|
||||
benchmark_format = benchmark["test_format"] # noqa: F841
|
||||
benchmark_format = benchmark.get("test_format", 0) # noqa: F841
|
||||
|
||||
if benchmark["benchmark_result"] == "Running":
|
||||
seq_benchmark_bandwidth = "Running"
|
||||
|
@ -28,8 +28,11 @@ import time
|
||||
import colorama
|
||||
import yaml
|
||||
import json
|
||||
import syslog
|
||||
import lxml.etree as etree
|
||||
|
||||
from sys import argv
|
||||
|
||||
from distutils.util import strtobool
|
||||
|
||||
from functools import wraps
|
||||
@ -51,6 +54,22 @@ default_store_data = {"cfgfile": "/etc/pvc/pvcapid.yaml"}
|
||||
config = dict()
|
||||
|
||||
|
||||
#
|
||||
# Audit function
|
||||
#
|
||||
def audit():
|
||||
args = argv
|
||||
args[0] = "pvc"
|
||||
syslog.openlog(facility=syslog.LOG_AUTH)
|
||||
syslog.syslog(
|
||||
'client audit: command "{}" by user "{}"'.format(
|
||||
" ".join(args),
|
||||
os.environ.get("USER", None),
|
||||
)
|
||||
)
|
||||
syslog.closelog()
|
||||
|
||||
|
||||
#
|
||||
# Version function
|
||||
#
|
||||
@ -3159,20 +3178,29 @@ def ceph_benchmark():
|
||||
# pvc storage benchmark run
|
||||
###############################################################################
|
||||
@click.command(name="run", short_help="Run a storage benchmark.")
|
||||
@click.option(
|
||||
"-y",
|
||||
"--yes",
|
||||
"confirm_flag",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help="Confirm the run",
|
||||
)
|
||||
@click.argument("pool")
|
||||
@cluster_req
|
||||
def ceph_benchmark_run(pool):
|
||||
def ceph_benchmark_run(confirm_flag, pool):
|
||||
"""
|
||||
Run a storage benchmark on POOL in the background.
|
||||
"""
|
||||
try:
|
||||
click.confirm(
|
||||
"NOTE: Storage benchmarks take approximately 10 minutes to run and generate significant load on the cluster; they should be run sparingly. Continue",
|
||||
prompt_suffix="? ",
|
||||
abort=True,
|
||||
)
|
||||
except Exception:
|
||||
exit(0)
|
||||
if not confirm_flag and not config["unsafe"]:
|
||||
try:
|
||||
click.confirm(
|
||||
"NOTE: Storage benchmarks take approximately 10 minutes to run and generate significant load on the cluster; they should be run sparingly. Continue",
|
||||
prompt_suffix="? ",
|
||||
abort=True,
|
||||
)
|
||||
except Exception:
|
||||
exit(0)
|
||||
|
||||
retcode, retmsg = pvc_ceph.ceph_benchmark_run(config, pool)
|
||||
cleanup(retcode, retmsg)
|
||||
@ -3253,7 +3281,9 @@ def ceph_osd():
|
||||
@cluster_req
|
||||
def ceph_osd_create_db_vg(node, device, confirm_flag):
|
||||
"""
|
||||
Create a new Ceph OSD database volume group on node NODE with block device DEVICE. DEVICE must be a valid raw block device, one of e.g. '/dev/sda', '/dev/nvme0n1', '/dev/disk/by-path/...', '/dev/disk/by-id/...', etc. Using partitions is not supported.
|
||||
Create a new Ceph OSD database volume group on node NODE with block device DEVICE. DEVICE must be a valid raw block device (e.g. '/dev/nvme0n1', '/dev/disk/by-path/...') or a "detect" string. Using partitions is not supported.
|
||||
|
||||
A "detect" string is a string in the form "detect:<NAME>:<HUMAN-SIZE>:<ID>". Detect strings allow for automatic determination of Linux block device paths from known basic information about disks by leveraging "lsscsi" on the target host. The "NAME" should be some descriptive identifier, for instance the manufacturer (e.g. "INTEL"), the "HUMAN-SIZE" should be the labeled human-readable size of the device (e.g. "480GB", "1.92TB"), and "ID" specifies the Nth 0-indexed device which matches the "NAME" and "HUMAN-SIZE" values (e.g. "2" would match the third device with the corresponding "NAME" and "HUMAN-SIZE"). When matching against sizes, there is +/- 3% flexibility to account for base-1000 vs. base-1024 differences and rounding errors. The "NAME" may contain whitespace but if so the entire detect string should be quoted, and is case-insensitive. More information about detect strings can be found in the pvcbootstrapd manual.
|
||||
|
||||
This volume group will be used for Ceph OSD database and WAL functionality if the '--ext-db' flag is passed to newly-created OSDs during 'pvc storage osd add'. DEVICE should be an extremely fast SSD device (NVMe, Intel Optane, etc.) which is significantly faster than the normal OSD disks and with very high write endurance. Only one OSD database volume group on a single physical device is supported per node, so it must be fast and large enough to act as an effective OSD database device for all OSDs on the node. Attempting to add additional database volume groups after the first will fail.
|
||||
"""
|
||||
@ -3315,7 +3345,9 @@ def ceph_osd_create_db_vg(node, device, confirm_flag):
|
||||
@cluster_req
|
||||
def ceph_osd_add(node, device, weight, ext_db_flag, ext_db_ratio, confirm_flag):
|
||||
"""
|
||||
Add a new Ceph OSD on node NODE with block device DEVICE. DEVICE must be a valid raw block device, one of e.g. '/dev/sda', '/dev/nvme0n1', '/dev/disk/by-path/...', '/dev/disk/by-id/...', etc. Using partitions is not supported.
|
||||
Add a new Ceph OSD on node NODE with block device DEVICE. DEVICE must be a valid raw block device (e.g. '/dev/sda', '/dev/nvme0n1', '/dev/disk/by-path/...', '/dev/disk/by-id/...') or a "detect" string. Using partitions is not supported.
|
||||
|
||||
A "detect" string is a string in the form "detect:<NAME>:<HUMAN-SIZE>:<ID>". Detect strings allow for automatic determination of Linux block device paths from known basic information about disks by leveraging "lsscsi" on the target host. The "NAME" should be some descriptive identifier, for instance the manufacturer (e.g. "INTEL"), the "HUMAN-SIZE" should be the labeled human-readable size of the device (e.g. "480GB", "1.92TB"), and "ID" specifies the Nth 0-indexed device which matches the "NAME" and "HUMAN-SIZE" values (e.g. "2" would match the third device with the corresponding "NAME" and "HUMAN-SIZE"). When matching against sizes, there is +/- 3% flexibility to account for base-1000 vs. base-1024 differences and rounding errors. The "NAME" may contain whitespace but if so the entire detect string should be quoted, and is case-insensitive. More information about detect strings can be found in the pvcbootstrapd manual.
|
||||
|
||||
The weight of an OSD should reflect the ratio of the OSD to other OSDs in the storage cluster. For example, if all OSDs are the same size as recommended for PVC, 1 (the default) is a valid weight so that all are treated identically. If a new OSD is added later which is 4x the size of the existing OSDs, the new OSD's weight should then be 4 to tell the cluster that 4x the data can be stored on the OSD. Weights can also be tweaked for performance reasons, since OSDs with more data will incur more I/O load. For more information about CRUSH weights, please see the Ceph documentation.
|
||||
|
||||
@ -5702,6 +5734,8 @@ def cli(_cluster, _debug, _quiet, _unsafe, _colour):
|
||||
)
|
||||
echo("", err=True)
|
||||
|
||||
audit()
|
||||
|
||||
|
||||
#
|
||||
# Click command tree
|
||||
|
@ -2,7 +2,7 @@ from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="pvc",
|
||||
version="0.9.45",
|
||||
version="0.9.46",
|
||||
packages=["pvc", "pvc.cli_lib"],
|
||||
install_requires=[
|
||||
"Click",
|
||||
|
@ -375,7 +375,7 @@ def get_list_osd(zkhandler, limit, is_fuzzy=True):
|
||||
for osd in full_osd_list:
|
||||
if limit:
|
||||
try:
|
||||
if re.match(limit, osd):
|
||||
if re.fullmatch(limit, osd):
|
||||
osd_list.append(getOSDInformation(zkhandler, osd))
|
||||
except Exception as e:
|
||||
return False, "Regex Error: {}".format(e)
|
||||
@ -496,16 +496,19 @@ def remove_pool(zkhandler, name):
|
||||
def get_list_pool(zkhandler, limit, is_fuzzy=True):
|
||||
full_pool_list = zkhandler.children("base.pool")
|
||||
|
||||
if limit:
|
||||
if not is_fuzzy:
|
||||
limit = "^" + limit + "$"
|
||||
if is_fuzzy and limit:
|
||||
# Implicitly assume fuzzy limits
|
||||
if not re.match(r"\^.*", limit):
|
||||
limit = ".*" + limit
|
||||
if not re.match(r".*\$", limit):
|
||||
limit = limit + ".*"
|
||||
|
||||
get_pool_info = dict()
|
||||
for pool in full_pool_list:
|
||||
is_limit_match = False
|
||||
if limit:
|
||||
try:
|
||||
if re.match(limit, pool):
|
||||
if re.fullmatch(limit, pool):
|
||||
is_limit_match = True
|
||||
except Exception as e:
|
||||
return False, "Regex Error: {}".format(e)
|
||||
@ -848,15 +851,12 @@ def get_list_volume(zkhandler, pool, limit, is_fuzzy=True):
|
||||
|
||||
full_volume_list = getCephVolumes(zkhandler, pool)
|
||||
|
||||
if limit:
|
||||
if not is_fuzzy:
|
||||
limit = "^" + limit + "$"
|
||||
else:
|
||||
# Implicitly assume fuzzy limits
|
||||
if not re.match(r"\^.*", limit):
|
||||
limit = ".*" + limit
|
||||
if not re.match(r".*\$", limit):
|
||||
limit = limit + ".*"
|
||||
if is_fuzzy and limit:
|
||||
# Implicitly assume fuzzy limits
|
||||
if not re.match(r"\^.*", limit):
|
||||
limit = ".*" + limit
|
||||
if not re.match(r".*\$", limit):
|
||||
limit = limit + ".*"
|
||||
|
||||
get_volume_info = dict()
|
||||
for volume in full_volume_list:
|
||||
@ -867,7 +867,7 @@ def get_list_volume(zkhandler, pool, limit, is_fuzzy=True):
|
||||
if limit:
|
||||
# Try to match the limit against the volume name
|
||||
try:
|
||||
if re.match(limit, volume_name):
|
||||
if re.fullmatch(limit, volume_name):
|
||||
is_limit_match = True
|
||||
except Exception as e:
|
||||
return False, "Regex Error: {}".format(e)
|
||||
@ -1073,7 +1073,7 @@ def get_list_snapshot(zkhandler, pool, volume, limit, is_fuzzy=True):
|
||||
pool_name, volume_name = volume.split("/")
|
||||
if limit:
|
||||
try:
|
||||
if re.match(limit, snapshot_name):
|
||||
if re.fullmatch(limit, snapshot_name):
|
||||
snapshot_list.append(
|
||||
{
|
||||
"pool": pool_name,
|
||||
|
@ -665,16 +665,20 @@ def get_list(zkhandler, limit, is_fuzzy=True):
|
||||
net_list = []
|
||||
full_net_list = zkhandler.children("base.network")
|
||||
|
||||
if is_fuzzy and limit:
|
||||
# Implicitly assume fuzzy limits
|
||||
if not re.match(r"\^.*", limit):
|
||||
limit = ".*" + limit
|
||||
if not re.match(r".*\$", limit):
|
||||
limit = limit + ".*"
|
||||
|
||||
for net in full_net_list:
|
||||
description = zkhandler.read(("network", net))
|
||||
if limit:
|
||||
try:
|
||||
if not is_fuzzy:
|
||||
limit = "^" + limit + "$"
|
||||
|
||||
if re.match(limit, net):
|
||||
if re.fullmatch(limit, net):
|
||||
net_list.append(getNetworkInformation(zkhandler, net))
|
||||
if re.match(limit, description):
|
||||
if re.fullmatch(limit, description):
|
||||
net_list.append(getNetworkInformation(zkhandler, net))
|
||||
except Exception as e:
|
||||
return False, "Regex Error: {}".format(e)
|
||||
@ -700,25 +704,19 @@ def get_list_dhcp(zkhandler, network, limit, only_static=False, is_fuzzy=True):
|
||||
full_dhcp_list = getNetworkDHCPReservations(zkhandler, net_vni)
|
||||
full_dhcp_list += getNetworkDHCPLeases(zkhandler, net_vni)
|
||||
|
||||
if limit:
|
||||
try:
|
||||
if not is_fuzzy:
|
||||
limit = "^" + limit + "$"
|
||||
|
||||
# Implcitly assume fuzzy limits
|
||||
if not re.match(r"\^.*", limit):
|
||||
limit = ".*" + limit
|
||||
if not re.match(r".*\$", limit):
|
||||
limit = limit + ".*"
|
||||
except Exception as e:
|
||||
return False, "Regex Error: {}".format(e)
|
||||
if is_fuzzy and limit:
|
||||
# Implicitly assume fuzzy limits
|
||||
if not re.match(r"\^.*", limit):
|
||||
limit = ".*" + limit
|
||||
if not re.match(r".*\$", limit):
|
||||
limit = limit + ".*"
|
||||
|
||||
for lease in full_dhcp_list:
|
||||
valid_lease = False
|
||||
if limit:
|
||||
if re.match(limit, lease):
|
||||
if re.fullmatch(limit, lease):
|
||||
valid_lease = True
|
||||
if re.match(limit, lease):
|
||||
if re.fullmatch(limit, lease):
|
||||
valid_lease = True
|
||||
else:
|
||||
valid_lease = True
|
||||
@ -748,23 +746,17 @@ def get_list_acl(zkhandler, network, limit, direction, is_fuzzy=True):
|
||||
acl_list = []
|
||||
full_acl_list = getNetworkACLs(zkhandler, net_vni, direction)
|
||||
|
||||
if limit:
|
||||
try:
|
||||
if not is_fuzzy:
|
||||
limit = "^" + limit + "$"
|
||||
|
||||
# Implcitly assume fuzzy limits
|
||||
if not re.match(r"\^.*", limit):
|
||||
limit = ".*" + limit
|
||||
if not re.match(r".*\$", limit):
|
||||
limit = limit + ".*"
|
||||
except Exception as e:
|
||||
return False, "Regex Error: {}".format(e)
|
||||
if is_fuzzy and limit:
|
||||
# Implicitly assume fuzzy limits
|
||||
if not re.match(r"\^.*", limit):
|
||||
limit = ".*" + limit
|
||||
if not re.match(r".*\$", limit):
|
||||
limit = limit + ".*"
|
||||
|
||||
for acl in full_acl_list:
|
||||
valid_acl = False
|
||||
if limit:
|
||||
if re.match(limit, acl["description"]):
|
||||
if re.fullmatch(limit, acl["description"]):
|
||||
valid_acl = True
|
||||
else:
|
||||
valid_acl = True
|
||||
|
@ -237,13 +237,17 @@ def get_list(
|
||||
node_list = []
|
||||
full_node_list = zkhandler.children("base.node")
|
||||
|
||||
if is_fuzzy and limit:
|
||||
# Implicitly assume fuzzy limits
|
||||
if not re.match(r"\^.*", limit):
|
||||
limit = ".*" + limit
|
||||
if not re.match(r".*\$", limit):
|
||||
limit = limit + ".*"
|
||||
|
||||
for node in full_node_list:
|
||||
if limit:
|
||||
try:
|
||||
if not is_fuzzy:
|
||||
limit = "^" + limit + "$"
|
||||
|
||||
if re.match(limit, node):
|
||||
if re.fullmatch(limit, node):
|
||||
node_list.append(getNodeInformation(zkhandler, node))
|
||||
except Exception as e:
|
||||
return False, "Regex Error: {}".format(e)
|
||||
|
@ -1227,9 +1227,9 @@ def get_list(zkhandler, node, state, tag, limit, is_fuzzy=True, negate=False):
|
||||
if limit:
|
||||
# Try to match the limit against the UUID (if applicable) and name
|
||||
try:
|
||||
if is_limit_uuid and re.match(limit, vm):
|
||||
if is_limit_uuid and re.fullmatch(limit, vm):
|
||||
is_limit_match = True
|
||||
if re.match(limit, name):
|
||||
if re.fullmatch(limit, name):
|
||||
is_limit_match = True
|
||||
except Exception as e:
|
||||
return False, "Regex Error: {}".format(e)
|
||||
|
14
debian/changelog
vendored
14
debian/changelog
vendored
@ -1,3 +1,17 @@
|
||||
pvc (0.9.46-0) unstable; urgency=high
|
||||
|
||||
* [API] Fixes bugs with legacy benchmark display
|
||||
* [API] Fixes a bug around cloned image sizes
|
||||
* [API] Removes extraneous message text in provisioner create command
|
||||
* [API] Corrects bugs around fuzzy matching
|
||||
* [CLI] Adds auditing for PVC CLI to local syslog
|
||||
* [CLI] Adds --yes bypass for benchmark command
|
||||
* [Node Daemon/API/CLI] Adds support for "detect" strings when specifying OSD or OSDDB devices
|
||||
* [Node Daemon] Fixes a bug when removing OSDs
|
||||
* [Node Daemon] Fixes a single-node cluster shutdown deadlock
|
||||
|
||||
-- Joshua M. Boniface <joshua@boniface.me> Tue, 28 Dec 2021 15:02:14 -0500
|
||||
|
||||
pvc (0.9.45-0) unstable; urgency=high
|
||||
|
||||
* [Node Daemon] Fixes an ordering issue with pvcnoded.service
|
||||
|
@ -5034,7 +5034,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "The block device (e.g. \"/dev/sdb\", \"/dev/disk/by-path/...\", etc.) to create the OSD on",
|
||||
"description": "The block device (e.g. \"/dev/sdb\", \"/dev/disk/by-path/...\", etc.) or detect string (\"detect:NAME:SIZE:ID\") to create the OSD on",
|
||||
"in": "query",
|
||||
"name": "device",
|
||||
"required": true,
|
||||
@ -5194,7 +5194,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "The block device (e.g. \"/dev/sdb\", \"/dev/disk/by-path/...\", etc.) to create the OSD DB volume group on",
|
||||
"description": "The block device (e.g. \"/dev/sdb\", \"/dev/disk/by-path/...\", etc.) or detect string (\"detect:NAME:SIZE:ID\") to create the OSD DB volume group on",
|
||||
"in": "query",
|
||||
"name": "device",
|
||||
"required": true,
|
||||
|
@ -48,7 +48,7 @@ import re
|
||||
import json
|
||||
|
||||
# Daemon version
|
||||
version = "0.9.45"
|
||||
version = "0.9.46"
|
||||
|
||||
|
||||
##########################################################
|
||||
@ -233,11 +233,14 @@ def entrypoint():
|
||||
|
||||
# Force into secondary coordinator state if needed
|
||||
try:
|
||||
if this_node.router_state == "primary":
|
||||
if this_node.router_state == "primary" and len(d_node) > 1:
|
||||
zkhandler.write([("base.config.primary_node", "none")])
|
||||
logger.out("Waiting for primary migration", state="s")
|
||||
while this_node.router_state != "secondary":
|
||||
timeout = 240
|
||||
count = 0
|
||||
while this_node.router_state != "secondary" and count < timeout:
|
||||
sleep(0.5)
|
||||
count += 1
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
@ -26,7 +26,76 @@ import psutil
|
||||
import daemon_lib.common as common
|
||||
|
||||
from distutils.util import strtobool
|
||||
from re import search
|
||||
from re import search, match, sub
|
||||
|
||||
|
||||
def get_detect_device(detect_string):
|
||||
"""
|
||||
Parses a "detect:" string into a normalized block device path using lsscsi.
|
||||
|
||||
A detect string is formatted "detect:<NAME>:<SIZE>:<ID>", where
|
||||
NAME is some unique identifier in lsscsi, SIZE is a human-readable
|
||||
size value to within +/- 3% of the real size of the device, and
|
||||
ID is the Nth (0-indexed) matching entry of that NAME and SIZE.
|
||||
"""
|
||||
_, name, size, idd = detect_string.split(":")
|
||||
if _ != "detect":
|
||||
return None
|
||||
|
||||
retcode, stdout, stderr = common.run_os_command("lsscsi -s")
|
||||
if retcode:
|
||||
print(f"Failed to run lsscsi: {stderr}")
|
||||
return None
|
||||
|
||||
# Get valid lines
|
||||
lsscsi_lines_raw = stdout.split("\n")
|
||||
lsscsi_lines = list()
|
||||
for line in lsscsi_lines_raw:
|
||||
if not line:
|
||||
continue
|
||||
split_line = line.split()
|
||||
if split_line[1] != "disk":
|
||||
continue
|
||||
lsscsi_lines.append(line)
|
||||
|
||||
# Handle size determination (+/- 3%)
|
||||
lsscsi_sizes = set()
|
||||
for line in lsscsi_lines:
|
||||
lsscsi_sizes.add(split_line[-1])
|
||||
for l_size in lsscsi_sizes:
|
||||
b_size = float(sub(r"\D.", "", size))
|
||||
t_size = float(sub(r"\D.", "", l_size))
|
||||
|
||||
plusthreepct = t_size * 1.03
|
||||
minusthreepct = t_size * 0.97
|
||||
|
||||
if b_size > minusthreepct and b_size < plusthreepct:
|
||||
size = l_size
|
||||
break
|
||||
|
||||
blockdev = None
|
||||
matches = list()
|
||||
for idx, line in enumerate(lsscsi_lines):
|
||||
# Skip non-disk entries
|
||||
if line.split()[1] != "disk":
|
||||
continue
|
||||
# Skip if name is not contained in the line (case-insensitive)
|
||||
if name.lower() not in line.lower():
|
||||
continue
|
||||
# Skip if the size does not match
|
||||
if size != line.split()[-1]:
|
||||
continue
|
||||
# Get our blockdev and append to the list
|
||||
matches.append(line.split()[-2])
|
||||
|
||||
blockdev = None
|
||||
# Find the blockdev at index {idd}
|
||||
for idx, _blockdev in enumerate(matches):
|
||||
if int(idx) == int(idd):
|
||||
blockdev = _blockdev
|
||||
break
|
||||
|
||||
return blockdev
|
||||
|
||||
|
||||
class CephOSDInstance(object):
|
||||
@ -76,6 +145,22 @@ class CephOSDInstance(object):
|
||||
def add_osd(
|
||||
zkhandler, logger, node, device, weight, ext_db_flag=False, ext_db_ratio=0.05
|
||||
):
|
||||
# Handle a detect device if that is passed
|
||||
if match(r"detect:", device):
|
||||
ddevice = get_detect_device(device)
|
||||
if ddevice is None:
|
||||
logger.out(
|
||||
f"Failed to determine block device from detect string {device}",
|
||||
state="e",
|
||||
)
|
||||
return False
|
||||
else:
|
||||
logger.out(
|
||||
f"Determined block device {ddevice} from detect string {device}",
|
||||
state="i",
|
||||
)
|
||||
device = ddevice
|
||||
|
||||
# We are ready to create a new OSD on this node
|
||||
logger.out("Creating new OSD disk on block device {}".format(device), state="i")
|
||||
try:
|
||||
@ -228,7 +313,7 @@ class CephOSDInstance(object):
|
||||
def remove_osd(zkhandler, logger, osd_id, osd_obj):
|
||||
logger.out("Removing OSD disk {}".format(osd_id), state="i")
|
||||
try:
|
||||
# 1. Verify the OSD is present
|
||||
# Verify the OSD is present
|
||||
retcode, stdout, stderr = common.run_os_command("ceph osd ls")
|
||||
osd_list = stdout.split("\n")
|
||||
if osd_id not in osd_list:
|
||||
@ -237,7 +322,17 @@ class CephOSDInstance(object):
|
||||
)
|
||||
return True
|
||||
|
||||
# 1. Set the OSD out so it will flush
|
||||
# 1. Set the OSD down and out so it will flush
|
||||
logger.out("Setting down OSD disk with ID {}".format(osd_id), state="i")
|
||||
retcode, stdout, stderr = common.run_os_command(
|
||||
"ceph osd down {}".format(osd_id)
|
||||
)
|
||||
if retcode:
|
||||
print("ceph osd down")
|
||||
print(stdout)
|
||||
print(stderr)
|
||||
raise Exception
|
||||
|
||||
logger.out("Setting out OSD disk with ID {}".format(osd_id), state="i")
|
||||
retcode, stdout, stderr = common.run_os_command(
|
||||
"ceph osd out {}".format(osd_id)
|
||||
@ -354,17 +449,33 @@ class CephOSDInstance(object):
|
||||
|
||||
@staticmethod
|
||||
def add_db_vg(zkhandler, logger, device):
|
||||
# Check if an existsing volume group exists
|
||||
retcode, stdout, stderr = common.run_os_command("vgdisplay osd-db")
|
||||
if retcode != 5:
|
||||
logger.out('Ceph OSD database VG "osd-db" already exists', state="e")
|
||||
return False
|
||||
|
||||
# Handle a detect device if that is passed
|
||||
if match(r"detect:", device):
|
||||
ddevice = get_detect_device(device)
|
||||
if ddevice is None:
|
||||
logger.out(
|
||||
f"Failed to determine block device from detect string {device}",
|
||||
state="e",
|
||||
)
|
||||
return False
|
||||
else:
|
||||
logger.out(
|
||||
f"Determined block device {ddevice} from detect string {device}",
|
||||
state="i",
|
||||
)
|
||||
device = ddevice
|
||||
|
||||
logger.out(
|
||||
"Creating new OSD database volume group on block device {}".format(device),
|
||||
state="i",
|
||||
)
|
||||
try:
|
||||
# 0. Check if an existsing volume group exists
|
||||
retcode, stdout, stderr = common.run_os_command("vgdisplay osd-db")
|
||||
if retcode != 5:
|
||||
logger.out('Ceph OSD database VG "osd-db" already exists', state="e")
|
||||
return False
|
||||
|
||||
# 1. Create an empty partition table
|
||||
logger.out(
|
||||
"Creating partitions on block device {}".format(device), state="i"
|
||||
|
Reference in New Issue
Block a user