295 lines
12 KiB
Python
295 lines
12 KiB
Python
# Copyright 2018 Huawei Technologies Co., Ltd.
|
|
# 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.
|
|
|
|
"""CLI interface for cinder status commands."""
|
|
|
|
import os
|
|
import sys
|
|
|
|
from oslo_config import cfg
|
|
from oslo_upgradecheck import upgradecheck as uc
|
|
|
|
from cinder import context
|
|
from cinder import db
|
|
from cinder import exception
|
|
from cinder import objects
|
|
from cinder.policy import DEFAULT_POLICY_FILENAME
|
|
# Need to import service to load config
|
|
from cinder import service # noqa
|
|
# We must first register Cinder's objects. Otherwise
|
|
# we cannot import the volume manager.
|
|
objects.register_all()
|
|
import cinder.volume.manager as volume_manager
|
|
|
|
CONF = cfg.CONF
|
|
|
|
SUCCESS = uc.Code.SUCCESS
|
|
FAILURE = uc.Code.FAILURE
|
|
WARNING = uc.Code.WARNING
|
|
REMOVED_DRVRS = [
|
|
"coprhd",
|
|
"drbdmanage",
|
|
"disco",
|
|
"hgst",
|
|
"hpe_lefthand",
|
|
"sheepdog",
|
|
"zfssa",
|
|
]
|
|
|
|
|
|
def _get_enabled_drivers() -> list[str]:
|
|
"""Returns a list of volume_driver entries"""
|
|
volume_drivers = []
|
|
if CONF.enabled_backends:
|
|
backend: str
|
|
for backend in filter(None, CONF.enabled_backends):
|
|
# Each backend group needs to be registered first
|
|
CONF.register_opts(volume_manager.volume_backend_opts,
|
|
group=backend)
|
|
volume_driver = CONF[backend]['volume_driver']
|
|
volume_drivers.append(volume_driver)
|
|
|
|
return volume_drivers
|
|
|
|
|
|
class Checks(uc.UpgradeCommands):
|
|
"""Upgrade checks to run."""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(Checks, self).__init__(*args, **kwargs)
|
|
self.context = context.get_admin_context()
|
|
|
|
def _file_exists(self, path: str) -> bool:
|
|
"""Helper for mocking check of os.path.exists."""
|
|
return os.path.exists(path)
|
|
|
|
def _check_backup_module(self) -> uc.Result:
|
|
"""Checks for the use of backup driver module paths.
|
|
|
|
The use of backup modules for setting backup_driver was deprecated and
|
|
we now only allow the full driver path. This checks that there are not
|
|
any remaining settings using the old method.
|
|
"""
|
|
# We import here to avoid conf loading order issues with cinder.service
|
|
# above.
|
|
import cinder.backup.manager # noqa
|
|
|
|
backup_driver = CONF.backup_driver
|
|
|
|
# Easy check in that a class name will have mixed casing
|
|
if backup_driver == backup_driver.lower():
|
|
return uc.Result(
|
|
FAILURE,
|
|
'Backup driver configuration requires the full path to the '
|
|
'driver, but current setting is using only the module path.')
|
|
|
|
return uc.Result(SUCCESS)
|
|
|
|
def _check_policy_file(self) -> uc.Result:
|
|
"""Checks if a policy.json file is present.
|
|
|
|
With the switch to policy-in-code, policy files should be policy.yaml
|
|
and should only be present if overriding default policy. Just checks
|
|
and warns if the old file is present to make sure they are aware it is
|
|
not being used.
|
|
"""
|
|
# make sure we know where to look for the policy file
|
|
config_dir = CONF.find_file('cinder.conf')
|
|
if not config_dir:
|
|
return uc.Result(
|
|
WARNING,
|
|
'Cannot locate your cinder configuration directory. '
|
|
'Please re-run using the --config-dir <dirname> option.')
|
|
|
|
policy_file = CONF.oslo_policy.policy_file
|
|
json_file = os.path.join(os.path.dirname(config_dir), 'policy.json')
|
|
|
|
if policy_file == DEFAULT_POLICY_FILENAME:
|
|
# Default is being used, check for old json file
|
|
if self._file_exists(json_file):
|
|
return uc.Result(
|
|
WARNING,
|
|
'policy.json file is present. Make sure any changes from '
|
|
'the default policies are present in a policy.yaml file '
|
|
'instead. If you really intend to use a policy.json file, '
|
|
'make sure that its absolute path is set as the value of '
|
|
"the 'policy_file' configuration option in the "
|
|
'[oslo_policy] section of your cinder.conf file.')
|
|
|
|
else:
|
|
# They have configured a custom policy file. It is OK if it does
|
|
# not exist, but we should check and warn about it while we're
|
|
# checking.
|
|
if not policy_file.startswith('/'):
|
|
# policy_file is relative to config_dir
|
|
policy_file = os.path.join(os.path.dirname(config_dir),
|
|
policy_file)
|
|
if not self._file_exists(policy_file):
|
|
return uc.Result(
|
|
WARNING,
|
|
"Configured policy file '%s' does not exist. This may be "
|
|
"expected, but default policies will be used until any "
|
|
"desired overrides are added to the configured file." %
|
|
policy_file)
|
|
|
|
return uc.Result(SUCCESS)
|
|
|
|
def _check_periodic_interval(self) -> uc.Result:
|
|
"""Checks for non-default use of periodic_interval.
|
|
|
|
Some new configuration options have been introduced to supplement
|
|
periodic_interval, which was being used for multiple, possibly
|
|
conflicting purposes. If a non-default value for periodic_interval
|
|
is configured, warn the operator to review whether one of the new
|
|
options is better suited for the periodic task(s) being tuned.
|
|
"""
|
|
periodic_interval = CONF.periodic_interval
|
|
|
|
if periodic_interval != 60:
|
|
return uc.Result(
|
|
WARNING,
|
|
"Detected non-default value for the 'periodic_interval' "
|
|
"option. New configuration options have been introduced to "
|
|
"replace the use of 'periodic_interval' for some purposes. "
|
|
"Please consult the 'Upgrade' section of the Train release "
|
|
"notes for more information.")
|
|
|
|
return uc.Result(SUCCESS)
|
|
|
|
def _check_nested_quota(self) -> uc.Result:
|
|
"""Checks for the use of the nested quota driver.
|
|
|
|
The NestedDbQuotaDriver is deprecated in the Train release and is
|
|
removed in Wallaby release to prepare for upcoming unified limits
|
|
changes.
|
|
"""
|
|
# We import here to avoid conf loading order issues with cinder.service
|
|
# above.
|
|
import cinder.quota # noqa
|
|
quota_driver = CONF.quota_driver
|
|
if quota_driver == 'cinder.quota.NestedDbQuotaDriver':
|
|
return uc.Result(
|
|
FAILURE,
|
|
'The NestedDbQuotaDriver was deprecated in Train release '
|
|
'and is removed in Wallaby release.')
|
|
return uc.Result(SUCCESS)
|
|
|
|
def _check_legacy_windows_config(self) -> uc.Result:
|
|
"""Checks to ensure that the Windows driver path is properly updated.
|
|
|
|
The WindowsDriver was renamed in the Queens release to
|
|
WindowsISCSIDriver to avoid confusion with the SMB driver.
|
|
The backwards compatibility for this has now been removed, so
|
|
any cinder.conf settings still using
|
|
cinder.volume.drivers.windows.windows.WindowsDriver
|
|
must now be updated to use
|
|
cinder.volume.drivers.windows.iscsi.WindowsISCSIDriver.
|
|
"""
|
|
for volume_driver in _get_enabled_drivers():
|
|
if (volume_driver ==
|
|
"cinder.volume.drivers.windows.windows.WindowsDriver"):
|
|
return uc.Result(
|
|
FAILURE,
|
|
'Setting volume_driver to '
|
|
'cinder.volume.drivers.windows.windows.WindowsDriver '
|
|
'is no longer supported. Please update to use '
|
|
'cinder.volume.drivers.windows.iscsi.WindowsISCSIDriver '
|
|
'in cinder.conf.')
|
|
|
|
return uc.Result(SUCCESS)
|
|
|
|
def _check_removed_drivers(self) -> uc.Result:
|
|
"""Checks to ensure that no removed drivers are configured.
|
|
|
|
Checks start with drivers removed in the Stein release.
|
|
"""
|
|
removed_drivers = []
|
|
for volume_driver in _get_enabled_drivers():
|
|
for removed_driver in REMOVED_DRVRS:
|
|
if removed_driver in volume_driver:
|
|
removed_drivers.append(volume_driver)
|
|
|
|
if removed_drivers:
|
|
if len(removed_drivers) > 1:
|
|
return uc.Result(
|
|
FAILURE,
|
|
'The following drivers, which no longer exist, were found '
|
|
'configured in your cinder.conf file:\n%s.\n'
|
|
'These drivers have been removed and all data should '
|
|
'be migrated off of the associated backends before '
|
|
'upgrading Cinder.' % ",\n".join(removed_drivers))
|
|
else:
|
|
return uc.Result(
|
|
FAILURE,
|
|
'Found driver %s configured in your cinder.conf file. '
|
|
'This driver has been removed and all data should '
|
|
'be migrated off of this backend before upgrading '
|
|
'Cinder.' % removed_drivers[0])
|
|
|
|
return uc.Result(SUCCESS)
|
|
|
|
def _check_service_uuid(self) -> uc.Result:
|
|
try:
|
|
db.service_get_by_uuid(self.context, None)
|
|
except exception.ServiceNotFound:
|
|
volumes = db.volume_get_all(self.context,
|
|
limit=1,
|
|
filters={'service_uuid': None})
|
|
if not volumes:
|
|
return uc.Result(SUCCESS)
|
|
return uc.Result(
|
|
FAILURE,
|
|
'Services and volumes must have a service UUID. Please fix this '
|
|
'issue by running Queens online data migrations.')
|
|
|
|
def _check_attachment_specs(self):
|
|
if db.attachment_specs_exist(self.context):
|
|
return uc.Result(
|
|
FAILURE,
|
|
'There should be no more AttachmentSpecs in the system. '
|
|
'Please fix this issue by running Queens online data '
|
|
'migrations.')
|
|
return uc.Result(SUCCESS)
|
|
|
|
_upgrade_checks = (
|
|
# added in Stein
|
|
('Backup Driver Path', _check_backup_module),
|
|
('Use of Policy File', _check_policy_file),
|
|
('Windows Driver Path', _check_legacy_windows_config),
|
|
('Removed Drivers', _check_removed_drivers),
|
|
# added in Train
|
|
('Periodic Interval Use', _check_periodic_interval),
|
|
('Service UUIDs', _check_service_uuid),
|
|
('Attachment specs', _check_attachment_specs),
|
|
# added in Wallaby
|
|
('Use of Nested Quota Driver', _check_nested_quota),
|
|
)
|
|
|
|
|
|
def main():
|
|
# TODO(rosmaita): need to do this because we suggest using the
|
|
# --config-dir option, and if the user gives a bogus value, we
|
|
# get a stacktrace. Needs to be fixed in oslo_upgradecheck
|
|
try:
|
|
return uc.main(CONF, 'cinder', Checks())
|
|
except cfg.ConfigDirNotFoundError:
|
|
return ('ERROR: cannot read the cinder configuration directory.\n'
|
|
'Please re-run using the --config-dir <dirname> option '
|
|
'with a valid cinder configuration directory.')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|