Support uploading disk images to volumes in API

Addresses #68
This commit is contained in:
Joshua Boniface 2020-02-09 13:43:48 -05:00
parent 92df125a77
commit 49e5ce1176
4 changed files with 165 additions and 1 deletions

View File

@ -3334,6 +3334,53 @@ class API_Storage_Ceph_Volume_Element_Clone(Resource):
) )
api.add_resource(API_Storage_Ceph_Volume_Element_Clone, '/storage/ceph/volume/<pool>/<volume>/clone') api.add_resource(API_Storage_Ceph_Volume_Element_Clone, '/storage/ceph/volume/<pool>/<volume>/clone')
# /storage/ceph/volume/<pool>/<volume>/upload
class API_Storage_Ceph_Volume_Element_Upload(Resource):
@Authenticator
def post(self, pool, volume):
"""
Upload a disk image to Ceph volume {volume} in pool {pool}
---
tags:
- storage / ceph
parameters:
- in: query
name: file
type: binary
required: true
description: The raw binary contents of the file
responses:
200:
description: OK
schema:
type: object
id: Message
404:
description: Not found
schema:
type: object
id: Message
400:
description: Bad request
schema:
type: object
id: Message
"""
from flask_restful import reqparse
from werkzeug.datastructures import FileStorage
parser = reqparse.RequestParser()
parser.add_argument('file', type=FileStorage, location='files')
data = parser.parse_args()
image_data = data.get('file', None)
if not image_data:
return {'message':'An image file contents must be specified'}, 400
return api_helper.ceph_volume_upload(
pool,
volume,
image_data
)
api.add_resource(API_Storage_Ceph_Volume_Element_Upload, '/storage/ceph/volume/<pool>/<volume>/upload')
# /storage/ceph/snapshot # /storage/ceph/snapshot
class API_Storage_Ceph_Snapshot_Root(Resource): class API_Storage_Ceph_Snapshot_Root(Resource):
@RequestParser([ @RequestParser([

View File

@ -1327,6 +1327,49 @@ def ceph_volume_remove(pool, name):
} }
return output, retcode return output, retcode
def ceph_volume_upload(pool, volume, data):
"""
Upload a raw file via HTTP post to a PVC Ceph volume
"""
# Map the target blockdev
zk_conn = pvc_common.startZKConnection(config['coordinators'])
retflag, retdata = pvc_ceph.map_volume(zk_conn, pool, volume)
pvc_common.stopZKConnection(zk_conn)
if not retflag:
output = {
'message': retdata.replace('\"', '\'')
}
retcode = 400
return output, retcode
blockdev = retdata
output = {
'message': "Wrote uploaded file to volume '{}' in pool '{}'.".format(volume, pool)
}
retcode = 200
# Save the data to the blockdev
try:
data.save(blockdev)
except:
output = {
'message': "ERROR: Failed to write image file to volume."
}
retcode = 400
# Unmap the target blockdev
zk_conn = pvc_common.startZKConnection(config['coordinators'])
retflag, retdata = pvc_ceph.unmap_volume(zk_conn, pool, volume)
pvc_common.stopZKConnection(zk_conn)
if not retflag:
output = {
'message': retdata.replace('\"', '\'')
}
retcode = 400
return output, retcode
return output, retcode
def ceph_volume_snapshot_list(pool=None, volume=None, limit=None, is_fuzzy=True): def ceph_volume_snapshot_list(pool=None, volume=None, limit=None, is_fuzzy=True):
""" """
Get the list of RBD volume snapshots in the Ceph storage cluster. Get the list of RBD volume snapshots in the Ceph storage cluster.

View File

@ -20,6 +20,7 @@
# #
############################################################################### ###############################################################################
import os
import re import re
import click import click
import json import json
@ -903,7 +904,7 @@ def add_volume(zk_conn, pool, name, size):
'/ceph/snapshots/{}/{}'.format(pool, name): '', '/ceph/snapshots/{}/{}'.format(pool, name): '',
}) })
return True, 'Created RBD volume "{}/{}" ({})'.format(pool, name, size) return True, 'Created RBD volume "{}/{}" ({}).'.format(pool, name, size)
def clone_volume(zk_conn, pool, name_src, name_new): def clone_volume(zk_conn, pool, name_src, name_new):
if not verifyVolume(zk_conn, pool, name_src): if not verifyVolume(zk_conn, pool, name_src):
@ -994,6 +995,41 @@ def remove_volume(zk_conn, pool, name):
return True, 'Removed RBD volume "{}" in pool "{}".'.format(name, pool) return True, 'Removed RBD volume "{}" in pool "{}".'.format(name, pool)
def map_volume(zk_conn, pool, name):
if not verifyVolume(zk_conn, pool, name):
return False, 'ERROR: No volume with name "{}" is present in pool "{}".'.format(name, pool)
# 1. Map the volume onto the local system
retcode, stdout, stderr = common.run_os_command('rbd map {}/{}'.format(pool, name))
if retcode:
return False, 'ERROR: Failed to map RBD volume "{}" in pool "{}": {}'.format(name, pool, stderr)
# 2. Calculate the absolute path to the mapped volume
mapped_volume = '/dev/rbd/{}/{}'.format(pool, name)
# 3. Ensure the volume exists
if not os.path.exists(mapped_volume):
return False, 'ERROR: Mapped volume not found at expected location "{}".'.format(mapped_volume)
return True, mapped_volume
def unmap_volume(zk_conn, pool, name):
if not verifyVolume(zk_conn, pool, name):
return False, 'ERROR: No volume with name "{}" is present in pool "{}".'.format(name, pool)
mapped_volume = '/dev/rbd/{}/{}'.format(pool, name)
# 1. Ensure the volume exists
if not os.path.exists(mapped_volume):
return False, 'ERROR: Mapped volume not found at expected location "{}".'.format(mapped_volume)
# 2. Unap the volume
retcode, stdout, stderr = common.run_os_command('rbd unmap {}'.format(mapped_volume))
if retcode:
return False, 'ERROR: Failed to unmap RBD volume at "{}": {}'.format(mapped_volume, stderr)
return True, 'Unmapped RBD volume at "{}".'.format(mapped_volume)
def get_list_volume(zk_conn, pool, limit, is_fuzzy=True): def get_list_volume(zk_conn, pool, limit, is_fuzzy=True):
volume_list = [] volume_list = []
if pool and not verifyPool(zk_conn, pool): if pool and not verifyPool(zk_conn, pool):

View File

@ -4691,6 +4691,44 @@
] ]
} }
}, },
"/api/v1/storage/ceph/volume/{pool}/{volume}/upload": {
"post": {
"description": "",
"parameters": [
{
"description": "The raw binary contents of the file",
"in": "query",
"name": "file",
"required": true,
"type": "binary"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Message"
}
},
"400": {
"description": "Bad request",
"schema": {
"$ref": "#/definitions/Message"
}
},
"404": {
"description": "Not found",
"schema": {
"$ref": "#/definitions/Message"
}
}
},
"summary": "Upload a disk image to Ceph volume {volume} in pool {pool}",
"tags": [
"storage / ceph"
]
}
},
"/api/v1/vm": { "/api/v1/vm": {
"get": { "get": {
"description": "", "description": "",