Add a new filesystem for image conversion
Create the new host_fs CLI commands and the APIs system host-fs-add system host-fs-delete These commands will be used only for adding/removing 'image-conversion' filesystem dedicated only for qcow2 image conversion. 'image-conversion' filesystem is optional. It is not allowed to add/remove any other filesystem. Change-Id: I87c876371e123ec1ba946170258401d220260e31 Partial-bug: 1819688 Depends-On: https://review.opendev.org/#/c/714936/ Signed-off-by: Elena Taivan <stefan.dinescu@windriver.com>
This commit is contained in:
parent
d10ddf1369
commit
8099bbbbcf
|
@ -9,6 +9,8 @@
|
|||
from cgtsclient.common import base
|
||||
from cgtsclient import exc
|
||||
|
||||
CREATION_ATTRIBUTES = ['name', 'ihost_uuid', 'size']
|
||||
|
||||
|
||||
class HostFs(base.Resource):
|
||||
def __repr__(self):
|
||||
|
@ -41,6 +43,21 @@ class HostFsManager(base.Manager):
|
|||
if body:
|
||||
return self.resource_class(self, body)
|
||||
|
||||
def delete(self, fs_id):
|
||||
path = '/v1/host_fs/%s' % fs_id
|
||||
return self._delete(path)
|
||||
|
||||
def create(self, **kwargs):
|
||||
path = '/v1/host_fs'
|
||||
valid_list = []
|
||||
new = {}
|
||||
for (key, value) in kwargs.items():
|
||||
if key in CREATION_ATTRIBUTES:
|
||||
new[key] = value
|
||||
else:
|
||||
raise exc.InvalidAttribute('%s' % key)
|
||||
return self._create(path, new)
|
||||
|
||||
|
||||
def _find_fs(cc, ihost, host_fs):
|
||||
if host_fs.isdigit():
|
||||
|
|
|
@ -96,3 +96,56 @@ def do_host_fs_modify(cc, args):
|
|||
raise exc.CommandError('Failed to modify filesystems')
|
||||
|
||||
_print_fs_list(cc, ihost.uuid)
|
||||
|
||||
|
||||
@utils.arg('hostnameorid',
|
||||
metavar='<hostname or id>',
|
||||
help="Name or ID of the host [REQUIRED]")
|
||||
@utils.arg('name',
|
||||
metavar='<fs name>',
|
||||
help="Name of the Filesystem [REQUIRED]")
|
||||
def do_host_fs_delete(cc, args):
|
||||
"""Delete a host filesystem."""
|
||||
|
||||
# Get the ihost object
|
||||
ihost = ihost_utils._find_ihost(cc, args.hostnameorid)
|
||||
host_fs = fs_utils._find_fs(cc, ihost, args.name)
|
||||
|
||||
try:
|
||||
cc.host_fs.delete(host_fs.uuid)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Filesystem delete failed: host %s: '
|
||||
'name %s' % (args.hostnameorid,
|
||||
args.name))
|
||||
|
||||
|
||||
@utils.arg('hostnameorid',
|
||||
metavar='<hostname or id>',
|
||||
help="Name or ID of the host [REQUIRED]")
|
||||
@utils.arg('name',
|
||||
metavar='<fs name=size>',
|
||||
nargs=1,
|
||||
action='append',
|
||||
help="Name of the Filesystem [REQUIRED]")
|
||||
def do_host_fs_add(cc, args):
|
||||
"""Add a host filesystem"""
|
||||
fields = {}
|
||||
# Get the ihost object
|
||||
ihost = ihost_utils._find_ihost(cc, args.hostnameorid)
|
||||
for attr in args.name[0]:
|
||||
try:
|
||||
fs_name, size = attr.split("=", 1)
|
||||
|
||||
fields['name'] = fs_name
|
||||
fields['size'] = size
|
||||
except ValueError:
|
||||
raise exc.CommandError('Filesystem creation attributes must be '
|
||||
'FS_NAME=SIZE not "%s"' % attr)
|
||||
try:
|
||||
fields['ihost_uuid'] = ihost.uuid
|
||||
fs = cc.host_fs.create(**fields)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Failed to create filesystem: host %s: fields %s' %
|
||||
(args.hostnameorid, fields))
|
||||
|
||||
_print_fs_show(fs)
|
||||
|
|
|
@ -332,11 +332,151 @@ class HostFsController(rest.RestController):
|
|||
|
||||
@wsme_pecan.wsexpose(None, types.uuid, status_code=204)
|
||||
def delete(self, host_fs_uuid):
|
||||
"""Delete a host_fs."""
|
||||
raise exception.OperationNotPermitted
|
||||
"""Delete a host filesystem."""
|
||||
|
||||
host_fs = objects.host_fs.get_by_uuid(pecan.request.context,
|
||||
host_fs_uuid).as_dict()
|
||||
ihost_uuid = host_fs['ihost_uuid']
|
||||
host = pecan.request.dbapi.ihost_get(ihost_uuid)
|
||||
_delete(host_fs)
|
||||
|
||||
try:
|
||||
# Host must be available to add/remove fs at runtime
|
||||
if host.availability in [constants.AVAILABILITY_AVAILABLE,
|
||||
constants.AVAILABILITY_DEGRADED]:
|
||||
# perform rpc to conductor to perform config apply
|
||||
pecan.request.rpcapi.update_host_filesystem_config(
|
||||
pecan.request.context,
|
||||
host=host,
|
||||
filesystem_list=[host_fs['name']],)
|
||||
|
||||
except Exception as e:
|
||||
msg = _("Failed to delete filesystem %s" % host_fs['name'])
|
||||
LOG.error("%s with exception %s" % (msg, e))
|
||||
pecan.request.dbapi.host_fs_create(host.id, host_fs)
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
@cutils.synchronized(LOCK_NAME)
|
||||
@wsme_pecan.wsexpose(HostFs, body=HostFs)
|
||||
def post(self, host_fs):
|
||||
"""Create a new host_fs."""
|
||||
raise exception.OperationNotPermitted
|
||||
"""Create a host filesystem."""
|
||||
|
||||
try:
|
||||
host_fs = host_fs.as_dict()
|
||||
host_fs = _create(host_fs)
|
||||
|
||||
ihost_uuid = host_fs['ihost_uuid']
|
||||
ihost_uuid.strip()
|
||||
|
||||
host = pecan.request.dbapi.ihost_get(ihost_uuid)
|
||||
except exception.SysinvException as e:
|
||||
LOG.exception(e)
|
||||
raise wsme.exc.ClientSideError(_("Invalid data: failed to create a"
|
||||
" filesystem"))
|
||||
try:
|
||||
# Host must be available to add/remove fs at runtime
|
||||
if host.availability in [constants.AVAILABILITY_AVAILABLE,
|
||||
constants.AVAILABILITY_DEGRADED]:
|
||||
# perform rpc to conductor to perform config apply
|
||||
pecan.request.rpcapi.update_host_filesystem_config(
|
||||
pecan.request.context,
|
||||
host=host,
|
||||
filesystem_list=[host_fs['name']],)
|
||||
|
||||
except Exception as e:
|
||||
msg = _("Failed to add filesystem name for %s" % host.hostname)
|
||||
LOG.error("%s with exception %s" % (msg, e))
|
||||
pecan.request.dbapi.host_fs_destroy(host_fs['id'])
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
return HostFs.convert_with_links(host_fs)
|
||||
|
||||
|
||||
def _check_host_fs(host_fs):
|
||||
"""Check host state"""
|
||||
|
||||
if host_fs['name'] not in constants.FS_CREATION_ALLOWED:
|
||||
raise wsme.exc.ClientSideError(
|
||||
_("Unsupported filesystem. Only the following filesystems are supported\
|
||||
for creation or deletion: %s" % str(constants.FS_CREATION_ALLOWED)))
|
||||
|
||||
ihost_uuid = host_fs['ihost_uuid']
|
||||
ihost_uuid.strip()
|
||||
|
||||
try:
|
||||
ihost = pecan.request.dbapi.ihost_get(ihost_uuid)
|
||||
except exception.ServerNotFound:
|
||||
raise wsme.exc.ClientSideError(_("Invalid ihost_uuid %s"
|
||||
% ihost_uuid))
|
||||
|
||||
if ihost.personality != constants.CONTROLLER:
|
||||
raise wsme.exc.ClientSideError(_("Filesystem can only be added "
|
||||
"on controller nodes"))
|
||||
|
||||
# Host must be online/available/degraded to add/remove
|
||||
# any filesystem specified in FS_CREATION_ALLOWED
|
||||
if ihost.availability not in [constants.AVAILABILITY_AVAILABLE,
|
||||
constants.AVAILABILITY_ONLINE,
|
||||
constants.AVAILABILITY_DEGRADED]:
|
||||
raise wsme.exc.ClientSideError(_("Filesystem can only be added when "
|
||||
"controller node is in available/online/degraded"))
|
||||
|
||||
|
||||
def _create(host_fs):
|
||||
"""Create a host filesystem"""
|
||||
|
||||
_check_host_fs(host_fs)
|
||||
|
||||
ihost_uuid = host_fs['ihost_uuid']
|
||||
ihost_uuid.strip()
|
||||
|
||||
ihost = pecan.request.dbapi.ihost_get(ihost_uuid)
|
||||
# See if this filesystem name already exists
|
||||
current_host_fs_list = pecan.request.dbapi.host_fs_get_by_ihost(ihost_uuid)
|
||||
for fs in current_host_fs_list:
|
||||
if fs['name'] == host_fs['name']:
|
||||
raise wsme.exc.ClientSideError(_("Filesystem name (%s) "
|
||||
"already present" %
|
||||
fs['name']))
|
||||
|
||||
requested_growth_gib = int(float(host_fs['size']))
|
||||
|
||||
LOG.info("Requested growth in GiB: %s for fs %s on host %s" %
|
||||
(requested_growth_gib, host_fs['name'], ihost_uuid))
|
||||
|
||||
cgtsvg_free_space_gib = utils.get_node_cgtsvg_limit(ihost)
|
||||
|
||||
if requested_growth_gib > cgtsvg_free_space_gib:
|
||||
msg = _("HostFs update failed: Not enough free space on %s. "
|
||||
"Current free space %s GiB, "
|
||||
"requested total increase %s GiB" %
|
||||
(constants.LVG_CGTS_VG, cgtsvg_free_space_gib, requested_growth_gib))
|
||||
LOG.warning(msg)
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
data = {
|
||||
'name': constants.FILESYSTEM_NAME_IMAGE_CONVERSION,
|
||||
'size': host_fs['size'],
|
||||
'logical_volume': constants.FILESYSTEM_LV_DICT[
|
||||
constants.FILESYSTEM_NAME_IMAGE_CONVERSION]
|
||||
}
|
||||
|
||||
forihostid = ihost['id']
|
||||
host_fs = pecan.request.dbapi.host_fs_create(forihostid, data)
|
||||
|
||||
return host_fs
|
||||
|
||||
|
||||
def _delete(host_fs):
|
||||
"""Delete a host filesystem."""
|
||||
|
||||
_check_host_fs(host_fs)
|
||||
|
||||
ihost = pecan.request.dbapi.ihost_get(host_fs['forihostid'])
|
||||
|
||||
try:
|
||||
pecan.request.dbapi.host_fs_destroy(host_fs['id'])
|
||||
except exception.HTTPNotFound:
|
||||
msg = _("Deleting Filesystem failed: host %s filesystem %s"
|
||||
% (ihost.hostname, host_fs['name']))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
|
|
@ -333,6 +333,7 @@ DEFAULT_SMALL_DISK_SIZE = 240
|
|||
MINIMUM_DISK_SIZE = 154
|
||||
|
||||
KUBERNETES_DOCKER_STOR_SIZE = 30
|
||||
IMAGE_CONVERSION_SIZE = 1
|
||||
DOCKER_DISTRIBUTION_STOR_SIZE = 16
|
||||
ETCD_STOR_SIZE = 5
|
||||
KUBELET_STOR_SIZE = 10
|
||||
|
@ -523,11 +524,13 @@ FILESYSTEM_NAME_EXTENSION = 'extension'
|
|||
FILESYSTEM_NAME_ETCD = 'etcd'
|
||||
FILESYSTEM_NAME_PATCH_VAULT = 'patch-vault'
|
||||
FILESYSTEM_NAME_KUBELET = 'kubelet'
|
||||
FILESYSTEM_NAME_IMAGE_CONVERSION = 'image-conversion'
|
||||
|
||||
FILESYSTEM_LV_DICT = {
|
||||
FILESYSTEM_NAME_PLATFORM: 'platform-lv',
|
||||
FILESYSTEM_NAME_BACKUP: 'backup-lv',
|
||||
FILESYSTEM_NAME_SCRATCH: 'scratch-lv',
|
||||
FILESYSTEM_NAME_IMAGE_CONVERSION: 'conversion-lv',
|
||||
FILESYSTEM_NAME_DOCKER: 'docker-lv',
|
||||
FILESYSTEM_NAME_DOCKER_DISTRIBUTION: 'dockerdistribution-lv',
|
||||
FILESYSTEM_NAME_DATABASE: 'pgsql-lv',
|
||||
|
@ -537,11 +540,13 @@ FILESYSTEM_LV_DICT = {
|
|||
FILESYSTEM_NAME_KUBELET: 'kubelet-lv',
|
||||
}
|
||||
|
||||
FS_CREATION_ALLOWED = [FILESYSTEM_NAME_IMAGE_CONVERSION]
|
||||
FILESYSTEM_CONTROLLER_SUPPORTED_LIST = [
|
||||
FILESYSTEM_NAME_SCRATCH,
|
||||
FILESYSTEM_NAME_BACKUP,
|
||||
FILESYSTEM_NAME_DOCKER,
|
||||
FILESYSTEM_NAME_KUBELET,
|
||||
FILESYSTEM_NAME_IMAGE_CONVERSION,
|
||||
]
|
||||
|
||||
FILESYSTEM_WORKER_SUPPORTED_LIST = [
|
||||
|
|
|
@ -5966,6 +5966,8 @@ class ConductorManager(service.PeriodicService):
|
|||
'platform::filesystem::docker::runtime',
|
||||
constants.FILESYSTEM_NAME_KUBELET:
|
||||
'platform::filesystem::kubelet::runtime',
|
||||
constants.FILESYSTEM_NAME_IMAGE_CONVERSION:
|
||||
'platform::filesystem::conversion::runtime',
|
||||
}
|
||||
|
||||
puppet_class = [classmap.get(fs) for fs in filesystem_list]
|
||||
|
|
|
@ -233,8 +233,27 @@ class StoragePuppet(base.BasePuppet):
|
|||
filters = ['"a|%s|"' % f for f in devices] + ['"r|.*|"']
|
||||
return '[ %s ]' % ', '.join(filters)
|
||||
|
||||
def _is_image_conversion_filesystem_enabled(self, host):
|
||||
filesystems = self.dbapi.host_fs_get_by_ihost(host.id)
|
||||
config = {}
|
||||
|
||||
for fs in filesystems:
|
||||
if fs.name == constants.FILESYSTEM_NAME_IMAGE_CONVERSION:
|
||||
config.update({
|
||||
'platform::filesystem::conversion::params::conversion_enabled': True,
|
||||
'platform::filesystem::conversion::params::lv_size': fs.size,
|
||||
})
|
||||
return config
|
||||
|
||||
config.update({
|
||||
'platform::filesystem::conversion::params::conversion_enabled': False,
|
||||
})
|
||||
return config
|
||||
|
||||
def _get_host_fs_config(self, host):
|
||||
config = {}
|
||||
conversion_config = self._is_image_conversion_filesystem_enabled(host)
|
||||
config.update(conversion_config)
|
||||
|
||||
filesystems = self.dbapi.host_fs_get_by_ihost(host.id)
|
||||
for fs in filesystems:
|
||||
|
|
|
@ -500,8 +500,9 @@ class ApiHostFSDeleteTestSuiteMixin(ApiHostFSTestCaseMixin):
|
|||
|
||||
# Verify appropriate exception is raised
|
||||
self.assertEqual(response.content_type, 'application/json')
|
||||
self.assertEqual(response.status_code, http_client.FORBIDDEN)
|
||||
self.assertIn("Operation not permitted", response.json['error_message'])
|
||||
self.assertEqual(response.status_code, http_client.BAD_REQUEST)
|
||||
self.assertIn("Unsupported filesystem",
|
||||
response.json['error_message'])
|
||||
|
||||
|
||||
class ApiHostFSPostTestSuiteMixin(ApiHostFSTestCaseMixin):
|
||||
|
@ -522,5 +523,6 @@ class ApiHostFSPostTestSuiteMixin(ApiHostFSTestCaseMixin):
|
|||
|
||||
# Verify appropriate exception is raised
|
||||
self.assertEqual(response.content_type, 'application/json')
|
||||
self.assertEqual(response.status_code, http_client.FORBIDDEN)
|
||||
self.assertIn("Operation not permitted", response.json['error_message'])
|
||||
self.assertEqual(response.status_code, http_client.BAD_REQUEST)
|
||||
self.assertIn("Unsupported filesystem",
|
||||
response.json['error_message'])
|
||||
|
|
Loading…
Reference in New Issue