config/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/controller_fs.py

801 lines
32 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 UnitedStack Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Copyright (c) 2013-2018 Wind River Systems, Inc.
#
import jsonpatch
import pecan
from pecan import rest
import wsme
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan
from sysinv.api.controllers.v1 import base
from sysinv.api.controllers.v1 import collection
from sysinv.api.controllers.v1 import link
from sysinv.api.controllers.v1 import types
from sysinv.api.controllers.v1 import utils
from sysinv.common import constants
from sysinv.common import exception
from sysinv.common import health
from sysinv.common import utils as cutils
from sysinv import objects
from sysinv.openstack.common import log
from sysinv.openstack.common.gettextutils import _
from fm_api import constants as fm_constants
from sysinv.common.storage_backend_conf import StorageBackendConfig
LOG = log.getLogger(__name__)
class ControllerFsPatchType(types.JsonPatchType):
@staticmethod
def mandatory_attrs():
return []
class ControllerFs(base.APIBase):
"""API representation of a controller_fs.
This class enforces type checking and value constraints, and converts
between the internal object model and the API representation of
a ControllerFs.
The database GiB of controller_fs - maps to
/var/lib/postgresql (pgsql-lv)
The image GiB of controller_fs - maps to
/opt/cgcs (cgcs-lv)
The image conversion GiB of controller_fs - maps to
/opt/img-conversions (img-conversions-lv)
The backup GiB of controller_fs - maps to
/opt/backups (backup-lv)
The scratch GiB of controller_fs - maps to
/scratch (scratch-lv)
The extension GiB of controller_fs - maps to
/opt/extension (extension-lv)
The gnocchi GiB of controller_fs - maps to
/opt/gnocchi (gnocchi-lv)
"""
uuid = types.uuid
"Unique UUID for this controller_fs"
name = wsme.wsattr(wtypes.text, mandatory=True)
size = int
logical_volume = wsme.wsattr(wtypes.text)
replicated = bool
state = wtypes.text
"The state of controller_fs indicates a drbd file system resize operation"
forisystemid = int
"The isystemid that this controller_fs belongs to"
isystem_uuid = types.uuid
"The UUID of the system this controller_fs belongs to"
action = wtypes.text
"Represent the action on the controller_fs"
links = [link.Link]
"A list containing a self link and associated controller_fs links"
created_at = wtypes.datetime.datetime
updated_at = wtypes.datetime.datetime
def __init__(self, **kwargs):
self.fields = list(objects.controller_fs.fields.keys())
for k in self.fields:
setattr(self, k, kwargs.get(k))
# API-only attribute)
self.fields.append('action')
setattr(self, 'action', kwargs.get('action', None))
@classmethod
def convert_with_links(cls, rpc_controller_fs, expand=True):
controller_fs = ControllerFs(**rpc_controller_fs.as_dict())
if not expand:
controller_fs.unset_fields_except(['created_at',
'updated_at',
'uuid',
'name',
'size',
'logical_volume',
'replicated',
'state',
'isystem_uuid'])
# never expose the isystem_id attribute
controller_fs.isystem_id = wtypes.Unset
# we display the cgcs file system as glance to the customer
if controller_fs.name == constants.FILESYSTEM_NAME_CGCS:
controller_fs.name = constants.FILESYSTEM_DISPLAY_NAME_CGCS
# never expose the isystem_id attribute, allow exposure for now
# controller_fs.forisystemid = wtypes.Unset
controller_fs.links = [
link.Link.make_link('self', pecan.request.host_url,
'controller_fs', controller_fs.uuid),
link.Link.make_link('bookmark', pecan.request.host_url,
'controller_fs', controller_fs.uuid,
bookmark=True)
]
return controller_fs
class ControllerFsCollection(collection.Collection):
"""API representation of a collection of ControllerFs."""
controller_fs = [ControllerFs]
"A list containing ControllerFs objects"
def __init__(self, **kwargs):
self._type = 'controller_fs'
@classmethod
def convert_with_links(cls, rpc_controller_fs, limit, url=None,
expand=False, **kwargs):
collection = ControllerFsCollection()
collection.controller_fs = [ControllerFs.convert_with_links(p, expand)
for p in rpc_controller_fs]
collection.next = collection.get_next(limit, url=url, **kwargs)
return collection
def _total_size_controller_multi_fs(controller_fs_new_list):
"""This function is called to verify file system capability on
controller with primary (initial) storage backend already configured
calling from initial config (config_controller stage) will result in
failure
"""
total_size = 0
for fs in controller_fs_new_list:
if fs.name == constants.FILESYSTEM_NAME_DATABASE:
total_size += (2 * fs.size)
else:
total_size += fs.size
return total_size
def _total_size_controller_fs(controller_fs_new, controller_fs_list):
"""This function is called to verify file system capability on
controller with primary (initial) storage backend already configured
calling from initial config (config_controller stage) will result in
failure
"""
total_size = 0
for fs in controller_fs_list:
size = fs['size']
if controller_fs_new and fs['name'] == controller_fs_new['name']:
size = controller_fs_new['size']
if fs['name'] == "database":
size = size * 2
total_size += size
LOG.info(
"_total_size_controller_fs total filesysem size %s" % total_size)
return total_size
def _check_relative_controller_multi_fs(controller_fs_new_list):
"""
This function verifies the relative controller_fs sizes.
:param controller_fs_new_list:
:return: None. Raise Client exception on failure.
"""
if cutils.is_virtual():
return
backup_gib_min = constants.BACKUP_OVERHEAD
for fs in controller_fs_new_list:
if fs.name == constants.FILESYSTEM_NAME_DATABASE:
database_gib = fs.size
backup_gib_min += fs.size
elif fs.name == constants.FILESYSTEM_NAME_CGCS:
cgcs_gib = fs.size
backup_gib_min += fs.size
elif fs.name == constants.FILESYSTEM_NAME_BACKUP:
backup_gib = fs.size
if backup_gib < backup_gib_min:
raise wsme.exc.ClientSideError(_("backup size of %d is "
"insufficient. "
"Minimum backup size of %d is "
"required based upon glance size %d "
"and database size %d. "
"Rejecting modification "
"request." %
(backup_gib,
backup_gib_min,
cgcs_gib,
database_gib
)))
def _check_controller_multi_fs(controller_fs_new_list,
ceph_mon_gib_new=None,
cgtsvg_growth_gib=None):
ceph_mons = pecan.request.dbapi.ceph_mon_get_list()
if not ceph_mon_gib_new:
if ceph_mons:
ceph_mon_gib_new = ceph_mons[0].ceph_mon_gib
else:
ceph_mon_gib_new = 0
LOG.info("_check_controller__multi_fs ceph_mon_gib_new = %s" % ceph_mon_gib_new)
cgtsvg_max_free_GiB = _get_controller_cgtsvg_limit()
LOG.info("_check_controller_multi_fs cgtsvg_max_free_GiB = %s " %
cgtsvg_max_free_GiB)
_check_relative_controller_multi_fs(controller_fs_new_list)
LOG.info("_check_controller_multi_fs ceph_mon_gib_new = %s" % ceph_mon_gib_new)
rootfs_configured_size_GiB = \
_total_size_controller_multi_fs(controller_fs_new_list) + ceph_mon_gib_new
LOG.info("_check_controller_multi_fs rootfs_configured_size_GiB = %s" %
rootfs_configured_size_GiB)
if cgtsvg_growth_gib and (cgtsvg_growth_gib > cgtsvg_max_free_GiB):
if ceph_mon_gib_new:
msg = _(
"Total target growth size %s GiB for database "
"(doubled for upgrades), glance, img-conversions, "
"scratch, backup, extension and ceph-mon exceeds "
"growth limit of %s GiB." %
(cgtsvg_growth_gib, cgtsvg_max_free_GiB)
)
else:
msg = _(
"Total target growth size %s GiB for database "
"(doubled for upgrades), glance, img-conversions, scratch, "
"backup and extension exceeds growth limit of %s GiB." %
(cgtsvg_growth_gib, cgtsvg_max_free_GiB)
)
raise wsme.exc.ClientSideError(msg)
def _check_relative_controller_fs(controller_fs_new, controller_fs_list):
"""
This function verifies the relative controller_fs sizes.
:param controller_fs_new:
:param controller_fs_list:
:return: None. Raise Client exception on failure.
"""
if cutils.is_virtual():
return
backup_gib = 0
database_gib = 0
cgcs_gib = 0
for fs in controller_fs_list:
if controller_fs_new and fs['name'] == controller_fs_new['name']:
fs['size'] = controller_fs_new['size']
if fs['name'] == "backup":
backup_gib = fs['size']
elif fs['name'] == constants.DRBD_CGCS:
cgcs_gib = fs['size']
elif fs['name'] == "database":
database_gib = fs['size']
if backup_gib == 0:
LOG.info(
"_check_relative_controller_fs backup filesystem not yet setup")
return
# Required mininum backup filesystem size
backup_gib_min = cgcs_gib + database_gib + constants.BACKUP_OVERHEAD
if backup_gib < backup_gib_min:
raise wsme.exc.ClientSideError(_("backup size of %d is "
"insufficient. "
"Minimum backup size of %d is "
"required based on upon "
"glance=%d and database=%d and "
"backup overhead of %d. "
"Rejecting modification "
"request." %
(backup_gib,
backup_gib_min,
cgcs_gib,
database_gib,
constants.BACKUP_OVERHEAD
)))
def _check_controller_state():
"""
This function verifies the administrative, operational, availability of
each controller.
"""
chosts = pecan.request.dbapi.ihost_get_by_personality(
constants.CONTROLLER)
for chost in chosts:
if (chost.administrative != constants.ADMIN_UNLOCKED or
chost.availability != constants.AVAILABILITY_AVAILABLE or
chost.operational != constants.OPERATIONAL_ENABLED):
# A node can become degraded due to not free space available in a FS
# and thus block the resize operation. If the only alarm that degrades
# a controller node is a filesystem alarm, we shouldn't block the resize
# as the resize itself will clear the degrade.
health_helper = health.Health(pecan.request.dbapi)
degrade_alarms = health_helper.get_alarms_degrade(
pecan.request.context,
alarm_ignore_list=[fm_constants.FM_ALARM_ID_FS_USAGE],
entity_instance_id_filter="controller-")
allowed_resize = False
if (not degrade_alarms and
chost.availability == constants.AVAILABILITY_DEGRADED):
allowed_resize = True
if not allowed_resize:
alarm_explanation = ""
if degrade_alarms:
alarm_explanation = "Check alarms with the following IDs: %s" % str(degrade_alarms)
raise wsme.exc.ClientSideError(
_("This operation requires controllers to be %s, %s, %s. "
"Current status is %s, %s, %s. %s." %
(constants.ADMIN_UNLOCKED, constants.OPERATIONAL_ENABLED,
constants.AVAILABILITY_AVAILABLE,
chost.administrative, chost.operational,
chost.availability, alarm_explanation)))
return True
def _get_controller_cgtsvg_limit():
"""Calculate space for controller fs
returns: cgtsvg_max_free_GiB
"""
cgtsvg0_free_mib = 0
cgtsvg1_free_mib = 0
cgtsvg_max_free_GiB = 0
chosts = pecan.request.dbapi.ihost_get_by_personality(
constants.CONTROLLER)
for chost in chosts:
if chost.hostname == constants.CONTROLLER_0_HOSTNAME:
ipvs = pecan.request.dbapi.ipv_get_by_ihost(chost.uuid)
for ipv in ipvs:
if (ipv.lvm_vg_name == constants.LVG_CGTS_VG and
ipv.pv_state != constants.PROVISIONED):
msg = _("Cannot resize filesystem. There are still "
"unprovisioned physical volumes on controller-0.")
raise wsme.exc.ClientSideError(msg)
ilvgs = pecan.request.dbapi.ilvg_get_by_ihost(chost.uuid)
for ilvg in ilvgs:
if (ilvg.lvm_vg_name == constants.LVG_CGTS_VG and
ilvg.lvm_vg_size and ilvg.lvm_vg_total_pe):
cgtsvg0_free_mib = (int(ilvg.lvm_vg_size) *
int(ilvg.lvm_vg_free_pe) / int(
ilvg.lvm_vg_total_pe)) / (1024 * 1024)
break
else:
ipvs = pecan.request.dbapi.ipv_get_by_ihost(chost.uuid)
for ipv in ipvs:
if (ipv.lvm_vg_name == constants.LVG_CGTS_VG and
ipv.pv_state != constants.PROVISIONED):
msg = _("Cannot resize filesystem. There are still "
"unprovisioned physical volumes on controller-1.")
raise wsme.exc.ClientSideError(msg)
ilvgs = pecan.request.dbapi.ilvg_get_by_ihost(chost.uuid)
for ilvg in ilvgs:
if (ilvg.lvm_vg_name == constants.LVG_CGTS_VG and
ilvg.lvm_vg_size and ilvg.lvm_vg_total_pe):
cgtsvg1_free_mib = (int(ilvg.lvm_vg_size) *
int(ilvg.lvm_vg_free_pe) / int(
ilvg.lvm_vg_total_pe)) / (1024 * 1024)
break
LOG.info("_get_controller_cgtsvg_limit cgtsvg0_free_mib=%s, "
"cgtsvg1_free_mib=%s" % (cgtsvg0_free_mib, cgtsvg1_free_mib))
if cgtsvg0_free_mib > 0 and cgtsvg1_free_mib > 0:
cgtsvg_max_free_GiB = min(cgtsvg0_free_mib, cgtsvg1_free_mib) / 1024
LOG.info("min of cgtsvg0_free_mib=%s and cgtsvg1_free_mib=%s is "
"cgtsvg_max_free_GiB=%s" %
(cgtsvg0_free_mib, cgtsvg1_free_mib, cgtsvg_max_free_GiB))
elif cgtsvg1_free_mib > 0:
cgtsvg_max_free_GiB = cgtsvg1_free_mib / 1024
else:
cgtsvg_max_free_GiB = cgtsvg0_free_mib / 1024
LOG.info("SYS_I filesystem limits cgtsvg0_free_mib=%s, "
"cgtsvg1_free_mib=%s, cgtsvg_max_free_GiB=%s"
% (cgtsvg0_free_mib, cgtsvg1_free_mib, cgtsvg_max_free_GiB))
return cgtsvg_max_free_GiB
def _check_controller_fs(controller_fs_new=None,
ceph_mon_gib_new=None,
cgtsvg_growth_gib=None,
controller_fs_list=None):
ceph_mons = pecan.request.dbapi.ceph_mon_get_list()
if not controller_fs_list:
controller_fs_list = pecan.request.dbapi.controller_fs_get_list()
if not ceph_mon_gib_new:
if ceph_mons:
ceph_mon_gib_new = ceph_mons[0].ceph_mon_gib
else:
ceph_mon_gib_new = 0
else:
if ceph_mons:
cgtsvg_growth_gib = ceph_mon_gib_new - ceph_mons[0].ceph_mon_gib
else:
cgtsvg_growth_gib = ceph_mon_gib_new
cgtsvg_max_free_GiB = _get_controller_cgtsvg_limit()
LOG.info("_check_controller_fs ceph_mon_gib_new = %s" % ceph_mon_gib_new)
LOG.info("_check_controller_fs cgtsvg_growth_gib = %s" % cgtsvg_growth_gib)
LOG.info("_check_controller_fs cgtsvg_max_free_GiB = %s" % cgtsvg_max_free_GiB)
_check_relative_controller_fs(controller_fs_new, controller_fs_list)
rootfs_configured_size_GiB = \
_total_size_controller_fs(controller_fs_new,
controller_fs_list) + ceph_mon_gib_new
LOG.info("_check_controller_fs rootfs_configured_size_GiB = %s" %
rootfs_configured_size_GiB)
if cgtsvg_growth_gib and (cgtsvg_growth_gib > cgtsvg_max_free_GiB):
if ceph_mon_gib_new:
msg = _(
"Total target growth size %s GiB for database "
"(doubled for upgrades), glance, img-conversions, "
"scratch, backup, extension and ceph-mon exceeds "
"growth limit of %s GiB." %
(cgtsvg_growth_gib, cgtsvg_max_free_GiB)
)
else:
msg = _(
"Total target growth size %s GiB for database "
"(doubled for upgrades), glance, img-conversions, scratch, "
"backup and extension exceeds growth limit of %s GiB." %
(cgtsvg_growth_gib, cgtsvg_max_free_GiB)
)
raise wsme.exc.ClientSideError(msg)
def _check_controller_multi_fs_data(context, controller_fs_list_new,
modified_fs):
""" Check controller filesystem data and return growth
returns: cgtsvg_growth_gib
"""
cgtsvg_growth_gib = 0
# Check if we need img_conversions
img_conversion_required = False
lvdisplay_keys = [constants.FILESYSTEM_LV_DICT[constants.FILESYSTEM_NAME_DATABASE],
constants.FILESYSTEM_LV_DICT[constants.FILESYSTEM_NAME_CGCS],
constants.FILESYSTEM_LV_DICT[constants.FILESYSTEM_NAME_BACKUP],
constants.FILESYSTEM_LV_DICT[constants.FILESYSTEM_NAME_SCRATCH],
constants.FILESYSTEM_LV_DICT[constants.FILESYSTEM_NAME_GNOCCHI]]
# On primary region, img-conversions always exists in controller_fs DB table.
# On secondary region, if both glance and cinder are sharing from the primary
# region, img-conversions won't exist in controller_fs DB table. We already
# have semantic check not to allow img-conversions resizing.
if (StorageBackendConfig.has_backend(pecan.request.dbapi, constants.SB_TYPE_LVM) or
StorageBackendConfig.has_backend(pecan.request.dbapi, constants.SB_TYPE_CEPH)):
img_conversion_required = True
lvdisplay_keys.append(constants.FILESYSTEM_LV_DICT[constants.FILESYSTEM_NAME_IMG_CONVERSIONS])
if (constants.FILESYSTEM_NAME_IMG_CONVERSIONS in modified_fs and
not img_conversion_required):
raise wsme.exc.ClientSideError(
_("%s is not modifiable: no cinder backend is "
"currently configured.") % constants.FILESYSTEM_NAME_IMG_CONVERSIONS)
lvdisplay_dict = pecan.request.rpcapi.get_controllerfs_lv_sizes(context)
for key in lvdisplay_keys:
if not lvdisplay_dict.get(key, None):
raise wsme.exc.ClientSideError(_("Unable to determine the "
"current size of %s. "
"Rejecting modification "
"request." % key))
for fs in controller_fs_list_new:
lv = fs.logical_volume
if lvdisplay_dict.get(lv, None):
orig = int(float(lvdisplay_dict[lv]))
new = int(fs.size)
if fs.name == constants.FILESYSTEM_NAME_DATABASE:
orig = orig / 2
if orig > new:
raise wsme.exc.ClientSideError(_("'%s' must be at least: "
"%s" % (fs.name, orig)))
if fs.name == constants.FILESYSTEM_NAME_DATABASE:
cgtsvg_growth_gib += 2 * (new - orig)
else:
cgtsvg_growth_gib += (new - orig)
LOG.info("_check_controller_multi_fs_data cgtsvg_growth_gib=%s" %
cgtsvg_growth_gib)
return cgtsvg_growth_gib
LOCK_NAME = 'ControllerFsController'
class ControllerFsController(rest.RestController):
"""REST controller for ControllerFs."""
_custom_actions = {
'detail': ['GET'],
'update_many': ['PUT'],
}
def __init__(self, from_isystems=False):
self._from_isystems = from_isystems
def _get_controller_fs_collection(self, isystem_uuid, marker, limit,
sort_key, sort_dir, expand=False,
resource_url=None):
if self._from_isystems and not isystem_uuid:
raise exception.InvalidParameterValue(_(
"System id not specified."))
limit = utils.validate_limit(limit)
sort_dir = utils.validate_sort_dir(sort_dir)
marker_obj = None
if marker:
marker_obj = objects.controller_fs.get_by_uuid(
pecan.request.context, marker)
if isystem_uuid:
controller_fs = pecan.request.dbapi.controller_fs_get_by_isystem(
isystem_uuid, limit,
marker_obj,
sort_key=sort_key,
sort_dir=sort_dir)
else:
controller_fs = \
pecan.request.dbapi.controller_fs_get_list(limit, marker_obj,
sort_key=sort_key,
sort_dir=sort_dir)
return ControllerFsCollection.convert_with_links(controller_fs, limit,
url=resource_url,
expand=expand,
sort_key=sort_key,
sort_dir=sort_dir)
@wsme_pecan.wsexpose(ControllerFsCollection, types.uuid, types.uuid, int,
wtypes.text, wtypes.text)
def get_all(self, isystem_uuid=None, marker=None, limit=None,
sort_key='id', sort_dir='asc'):
"""Retrieve a list of controller_fs."""
return self._get_controller_fs_collection(isystem_uuid, marker, limit,
sort_key, sort_dir)
@wsme_pecan.wsexpose(ControllerFsCollection, types.uuid, types.uuid, int,
wtypes.text, wtypes.text)
def detail(self, isystem_uuid=None, marker=None, limit=None,
sort_key='id', sort_dir='asc'):
"""Retrieve a list of controller_fs with detail."""
parent = pecan.request.path.split('/')[:-1][-1]
if parent != "controller_fs":
raise exception.HTTPNotFound
expand = True
resource_url = '/'.join(['controller_fs', 'detail'])
return self._get_controller_fs_collection(isystem_uuid, marker, limit,
sort_key, sort_dir,
expand, resource_url)
@wsme_pecan.wsexpose(ControllerFs, types.uuid)
def get_one(self, controller_fs_uuid):
"""Retrieve information about the given controller_fs."""
if self._from_isystems:
raise exception.OperationNotPermitted
rpc_controller_fs = \
objects.controller_fs.get_by_uuid(pecan.request.context,
controller_fs_uuid)
return ControllerFs.convert_with_links(rpc_controller_fs)
@cutils.synchronized(LOCK_NAME)
@wsme.validate(types.uuid, [ControllerFsPatchType])
@wsme_pecan.wsexpose(ControllerFs, types.uuid,
body=[ControllerFsPatchType])
def patch(self, controller_fs_uuid, patch):
"""Update the current controller_fs configuration."""
raise exception.OperationNotPermitted
@cutils.synchronized(LOCK_NAME)
@wsme.validate(types.uuid, [ControllerFsPatchType])
@wsme_pecan.wsexpose(ControllerFs, types.uuid, body=[[ControllerFsPatchType]])
def update_many(self, isystem_uuid, patch):
"""Update the current controller_fs configuration."""
if self._from_isystems and not isystem_uuid:
raise exception.InvalidParameterValue(_(
"System id not specified."))
# Validate input filesystem names
controller_fs_list = pecan.request.dbapi.controller_fs_get_list()
valid_fs_list = []
if controller_fs_list:
valid_fs_list = {fs.name: fs.size for fs in controller_fs_list}
reinstall_required = False
reboot_required = False
force_resize = False
modified_fs = []
for p_list in patch:
p_obj_list = jsonpatch.JsonPatch(p_list)
for p_obj in p_obj_list:
if p_obj['path'] == '/action':
value = p_obj['value']
patch.remove(p_list)
if value == constants.FORCE_ACTION:
force_resize = True
LOG.info("Force action resize selected")
break
for p_list in patch:
p_obj_list = jsonpatch.JsonPatch(p_list)
for p_obj in p_obj_list:
if p_obj['path'] == '/name':
fs_display_name = p_obj['value']
if fs_display_name == constants.FILESYSTEM_DISPLAY_NAME_CGCS:
fs_name = constants.FILESYSTEM_NAME_CGCS
else:
fs_name = fs_display_name
elif p_obj['path'] == '/size':
size = p_obj['value']
if fs_name not in valid_fs_list.keys() or fs_display_name == constants.FILESYSTEM_NAME_CGCS:
msg = _("ControllerFs update failed: invalid filesystem "
"'%s' " % fs_display_name)
raise wsme.exc.ClientSideError(msg)
elif not cutils.is_int_like(size):
msg = _("ControllerFs update failed: filesystem '%s' "
"size must be an integer " % fs_display_name)
raise wsme.exc.ClientSideError(msg)
elif int(size) <= int(valid_fs_list[fs_name]):
msg = _("ControllerFs update failed: size for filesystem '%s' "
"should be bigger than %s " % (
fs_display_name, valid_fs_list[fs_name]))
raise wsme.exc.ClientSideError(msg)
elif (fs_name == constants.FILESYSTEM_NAME_CGCS and
StorageBackendConfig.get_backend(pecan.request.dbapi,
constants.CINDER_BACKEND_CEPH)):
if force_resize:
LOG.warn("Force resize ControllerFs: %s, though Ceph "
"storage backend is configured" % fs_display_name)
else:
raise wsme.exc.ClientSideError(
_("ControllerFs %s size is not modifiable as Ceph is "
"configured. Update size via Ceph Storage Pools." %
fs_display_name))
if fs_name in constants.SUPPORTED_REPLICATED_FILEYSTEM_LIST:
if utils.is_drbd_fs_resizing():
raise wsme.exc.ClientSideError(
_("A drbd sync operation is currently in progress. "
"Retry again later.")
)
modified_fs += [fs_name]
controller_fs_list_new = []
for fs in controller_fs_list:
replaced = False
for p_list in patch:
p_obj_list = jsonpatch.JsonPatch(p_list)
for p_obj in p_obj_list:
if p_obj['path'] == '/name':
if p_obj['value'] == constants.FILESYSTEM_DISPLAY_NAME_CGCS:
p_obj['value'] = constants.FILESYSTEM_NAME_CGCS
if p_obj['value'] == fs['name']:
try:
controller_fs_list_new += [ControllerFs(
**jsonpatch.apply_patch(fs.as_dict(), p_obj_list))]
replaced = True
break
except utils.JSONPATCH_EXCEPTIONS as e:
raise exception.PatchError(patch=p_list, reason=e)
if replaced:
break
if not replaced:
controller_fs_list_new += [fs]
cgtsvg_growth_gib = _check_controller_multi_fs_data(
pecan.request.context,
controller_fs_list_new,
modified_fs)
if _check_controller_state():
_check_controller_multi_fs(controller_fs_list_new,
cgtsvg_growth_gib=cgtsvg_growth_gib)
for fs in controller_fs_list_new:
if fs.name in modified_fs:
value = {'size': fs.size}
if fs.replicated:
value.update({'state': constants.CONTROLLER_FS_RESIZING_IN_PROGRESS})
pecan.request.dbapi.controller_fs_update(fs.uuid, value)
try:
# perform rpc to conductor to perform config apply
pecan.request.rpcapi.update_storage_config(
pecan.request.context,
update_storage=False,
reinstall_required=reinstall_required,
reboot_required=reboot_required,
filesystem_list=modified_fs
)
except Exception as e:
msg = _("Failed to update filesystem size ")
LOG.error("%s with patch %s with exception %s" % (msg, patch, e))
raise wsme.exc.ClientSideError(msg)
@wsme_pecan.wsexpose(None, types.uuid, status_code=204)
def delete(self, controller_fs_uuid):
"""Delete a controller_fs."""
raise exception.OperationNotPermitted
@cutils.synchronized(LOCK_NAME)
@wsme_pecan.wsexpose(ControllerFs, body=ControllerFs)
def post(self, controllerfs):
"""Create a new controller_fs."""
raise exception.OperationNotPermitted