Compare commits

..

5 Commits

Author SHA1 Message Date
c6c44bf775 Bump version to 0.9.78 2023-09-30 12:57:55 -04:00
bbb940da65 Remove spurious comments 2023-09-30 12:37:58 -04:00
a0b45a2bcd Always create RBDs with bytes value
Converting into human results in imprecise values when specifying bytes
directly, which in turn breaks VMDK image uploads. Instead, just use the
raw bytes value when creating the volume instead of converting it back.
2023-09-30 12:37:43 -04:00
35e27f79ef Fix uploading of non-raw image files
Adds a new API query parameter to define the file size, which is then
used for the temporary image. This is required for, at least VMDK, files
to work properly in qemu-img convert.
2023-09-29 16:19:22 -04:00
ad2e7750ff Fix output path and print message 2023-09-21 02:32:53 -04:00
12 changed files with 70 additions and 19 deletions

View File

@ -1 +1 @@
0.9.77 0.9.78

View File

@ -1,5 +1,10 @@
## PVC Changelog ## PVC Changelog
###### [v0.9.78](https://github.com/parallelvirtualcluster/pvc/releases/tag/v0.9.78)
* [API, Client CLI] Fixes several bugs around image uploads; adds a new query parameter for non-raw images
* [API] Ensures RBD images are created with a raw bytes value to avoid rounding errors
###### [v0.9.77](https://github.com/parallelvirtualcluster/pvc/releases/tag/v0.9.77) ###### [v0.9.77](https://github.com/parallelvirtualcluster/pvc/releases/tag/v0.9.77)
* [Client CLI] Fixes a bug from a bad library import * [Client CLI] Fixes a bug from a bad library import

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.77" version = "0.9.78"
# API version # API version
API_VERSION = 1.0 API_VERSION = 1.0

View File

@ -5088,7 +5088,12 @@ class API_Storage_Ceph_Volume_Element_Upload(Resource):
"required": True, "required": True,
"location": ["args"], "location": ["args"],
"helptext": "A source image format must be specified.", "helptext": "A source image format must be specified.",
} },
{
"name": "file_size",
"required": False,
"location": ["args"],
},
] ]
) )
@Authenticator @Authenticator
@ -5113,6 +5118,11 @@ class API_Storage_Ceph_Volume_Element_Upload(Resource):
- qed - qed
- vdi - vdi
- vpc - vpc
- in: query
name: file_size
type: integer
required: false
description: The size of the image file, if {image_format} is not "raw"
responses: responses:
200: 200:
description: OK description: OK
@ -5131,7 +5141,10 @@ class API_Storage_Ceph_Volume_Element_Upload(Resource):
id: Message id: Message
""" """
return api_helper.ceph_volume_upload( return api_helper.ceph_volume_upload(
pool, volume, reqargs.get("image_format", None) pool,
volume,
reqargs.get("image_format", None),
reqargs.get("file_size", None),
) )

View File

@ -1584,7 +1584,7 @@ def ceph_volume_remove(zkhandler, pool, name):
@ZKConnection(config) @ZKConnection(config)
def ceph_volume_upload(zkhandler, pool, volume, img_type): def ceph_volume_upload(zkhandler, pool, volume, img_type, file_size=None):
""" """
Upload a raw file via HTTP post to a PVC Ceph volume Upload a raw file via HTTP post to a PVC Ceph volume
""" """
@ -1605,7 +1605,17 @@ def ceph_volume_upload(zkhandler, pool, volume, img_type):
} }
retcode = 400 retcode = 400
return output, retcode return output, retcode
dev_size = retdata[0]["stats"]["size"]
try:
dev_size = retdata[0]["stats"]["size"]
except Exception:
output = {
"message": "Target volume '{}' does not exist in pool '{}'.".format(
volume, pool
)
}
retcode = 400
return output, retcode
def cleanup_maps_and_volumes(): def cleanup_maps_and_volumes():
# Unmap the target blockdev # Unmap the target blockdev
@ -1619,8 +1629,14 @@ def ceph_volume_upload(zkhandler, pool, volume, img_type):
zkhandler, pool, "{}_tmp".format(volume) zkhandler, pool, "{}_tmp".format(volume)
) )
# Create a temporary block device to store non-raw images
if img_type == "raw": if img_type == "raw":
if file_size != dev_size:
output = {
"message": f"Image file size {file_size} does not match volume size {dev_size}"
}
retcode = 400
return output, retcode
# Map the target blockdev # Map the target blockdev
retflag, retdata = pvc_ceph.map_volume(zkhandler, pool, volume) retflag, retdata = pvc_ceph.map_volume(zkhandler, pool, volume)
if not retflag: if not retflag:
@ -1659,11 +1675,15 @@ def ceph_volume_upload(zkhandler, pool, volume, img_type):
cleanup_maps_and_volumes() cleanup_maps_and_volumes()
return output, retcode return output, retcode
# Write the image directly to the blockdev
else: else:
if file_size is None:
output = {"message": "A file size must be specified"}
retcode = 400
return output, retcode
# Create a temporary blockdev # Create a temporary blockdev
retflag, retdata = pvc_ceph.add_volume( retflag, retdata = pvc_ceph.add_volume(
zkhandler, pool, "{}_tmp".format(volume), dev_size zkhandler, pool, "{}_tmp".format(volume), file_size
) )
if not retflag: if not retflag:
output = {"message": retdata.replace('"', "'")} output = {"message": retdata.replace('"', "'")}

View File

@ -3598,7 +3598,7 @@ def cli_storage_volume_upload(pool, name, image_format, image_file):
If the image format is "raw", the image is uploaded directly to the target volume without modification. Otherwise, it will be converted into raw format by "qemu-img convert" on the remote side before writing using a temporary volume. The image format must be a valid format recognized by "qemu-img", such as "vmdk" or "qcow2". If the image format is "raw", the image is uploaded directly to the target volume without modification. Otherwise, it will be converted into raw format by "qemu-img convert" on the remote side before writing using a temporary volume. The image format must be a valid format recognized by "qemu-img", such as "vmdk" or "qcow2".
""" """
if not os.path.exists(image_file): if not path.exists(image_file):
echo(CLI_CONFIG, "ERROR: File '{}' does not exist!".format(image_file)) echo(CLI_CONFIG, "ERROR: File '{}' does not exist!".format(image_file))
exit(1) exit(1)
@ -4910,13 +4910,13 @@ def cli_provisioner_ova_upload(name, filename, pool):
Storage templates, provisioning scripts, and arguments for OVA-type profiles will be ignored and should not be set. Storage templates, provisioning scripts, and arguments for OVA-type profiles will be ignored and should not be set.
""" """
if not os.path.exists(filename): if not path.exists(filename):
echo(CLI_CONFIG, "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()
params["pool"] = pool params["pool"] = pool
params["ova_size"] = os.path.getsize(filename) params["ova_size"] = path.getsize(filename)
retcode, retdata = pvc.lib.provisioner.ova_upload( retcode, retdata = pvc.lib.provisioner.ova_upload(
CLI_CONFIG, name, filename, params CLI_CONFIG, name, filename, params

View File

@ -21,6 +21,7 @@
import math import math
from os import path
from json import loads from json import loads
from requests_toolbelt.multipart.encoder import ( from requests_toolbelt.multipart.encoder import (
MultipartEncoder, MultipartEncoder,
@ -1209,6 +1210,11 @@ def ceph_volume_upload(config, pool, volume, image_format, image_file):
""" """
import click import click
if image_format != "raw":
file_size = path.getsize(image_file)
else:
file_size = None
bar = UploadProgressBar( bar = UploadProgressBar(
image_file, end_message="Parsing file on remote side...", end_nl=False image_file, end_message="Parsing file on remote side...", end_nl=False
) )
@ -1220,7 +1226,7 @@ def ceph_volume_upload(config, pool, volume, image_format, image_file):
upload_monitor = MultipartEncoderMonitor(upload_data, bar.update) upload_monitor = MultipartEncoderMonitor(upload_data, bar.update)
headers = {"Content-Type": upload_monitor.content_type} headers = {"Content-Type": upload_monitor.content_type}
params = {"image_format": image_format} params = {"image_format": image_format, "file_size": file_size}
response = call_api( response = call_api(
config, config,

View File

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

View File

@ -763,9 +763,7 @@ def add_volume(zkhandler, pool, name, size):
# 2. Create the volume # 2. Create the volume
retcode, stdout, stderr = common.run_os_command( retcode, stdout, stderr = common.run_os_command(
"rbd create --size {} {}/{}".format( "rbd create --size {}B {}/{}".format(size_bytes, pool, name)
format_bytes_tohuman(size_bytes), pool, name
)
) )
if retcode: if retcode:
return False, 'ERROR: Failed to create RBD volume "{}": {}'.format(name, stderr) return False, 'ERROR: Failed to create RBD volume "{}": {}'.format(name, stderr)

7
debian/changelog vendored
View File

@ -1,3 +1,10 @@
pvc (0.9.78-0) unstable; urgency=high
* [API, Client CLI] Fixes several bugs around image uploads; adds a new query parameter for non-raw images
* [API] Ensures RBD images are created with a raw bytes value to avoid rounding errors
-- Joshua M. Boniface <joshua@boniface.me> Sat, 30 Sep 2023 12:57:55 -0400
pvc (0.9.77-0) unstable; urgency=high pvc (0.9.77-0) unstable; urgency=high
* [Client CLI] Fixes a bug from a bad library import * [Client CLI] Fixes a bug from a bad library import

View File

@ -14,7 +14,7 @@ sys.path.append('api-daemon')
import pvcapid.flaskapi as pvc_api import pvcapid.flaskapi as pvc_api
swagger_file = "docs/manuals/swagger.json" swagger_file = "swagger.json"
swagger_data = swagger(pvc_api.app) swagger_data = swagger(pvc_api.app)
swagger_data['info']['version'] = "1.0" swagger_data['info']['version'] = "1.0"
swagger_data['info']['title'] = "PVC Client and Provisioner API" swagger_data['info']['title'] = "PVC Client and Provisioner API"
@ -22,3 +22,5 @@ swagger_data['host'] = "pvc.local:7370"
with open(swagger_file, 'w') as fd: with open(swagger_file, 'w') as fd:
fd.write(json.dumps(swagger_data, sort_keys=True, indent=4)) fd.write(json.dumps(swagger_data, sort_keys=True, indent=4))
print(f"Swagger file output to {swagger_file}; add it to the PVC documentation repo.")

View File

@ -49,7 +49,7 @@ import re
import json import json
# Daemon version # Daemon version
version = "0.9.77" version = "0.9.78"
########################################################## ##########################################################