Merge "Support independent file lock path"
This commit is contained in:
commit
a944ffc48a
|
@ -15,6 +15,42 @@ run without raising an exception:
|
|||
|
||||
>>> import os_brick
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
There are some os-brick connectors that use file locks to prevent concurrent
|
||||
access to critical sections of the code.
|
||||
|
||||
These file locks use the ``oslo.concurrency`` ``lock_utils`` module and require
|
||||
the ``lock_path`` to be configured with the path where locks should be created.
|
||||
|
||||
os-brick can use a specific directory just for its locks or use the same
|
||||
directory as the service using os-brick.
|
||||
|
||||
The os-brick specific configuration option is ``[os_brick]/lock_path``, and if
|
||||
left undefined it will use the value from ``[oslo_concurrency]/lock_path``.
|
||||
|
||||
Setup
|
||||
-----
|
||||
|
||||
Once os_brick has been loaded it needs to be initialized, which is done by
|
||||
calling the ``os_brick.setup`` method with the ``oslo.conf`` configuration.
|
||||
|
||||
It is important that the call to ``setup`` method happens **after** oslo.config
|
||||
has been properly initialized.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from oslo_config import cfg
|
||||
from cinder import version
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
def main():
|
||||
CONF(sys.argv[1:], project='cinder',
|
||||
version=version.version_string())
|
||||
os_brick.setup(CONF)
|
||||
|
||||
Fetch all of the initiator information from the host
|
||||
----------------------------------------------------
|
||||
|
||||
|
@ -25,6 +61,9 @@ a volume to this host.
|
|||
|
||||
from os_brick.initiator import connector
|
||||
|
||||
|
||||
os_brick.setup(CONF)
|
||||
|
||||
# what helper do you want to use to get root access?
|
||||
root_helper = "sudo"
|
||||
# The ip address of the host you are running on
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# Copyright (c) 2022, Red Hat, 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.
|
||||
from oslo_log import log as logging
|
||||
|
||||
from os_brick import opts
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup(conf, **kwargs):
|
||||
"""Setup the os-brick library.
|
||||
|
||||
Service configuration options must have been initialized before this call
|
||||
because oslo's lock_path doesn't have a value before that.
|
||||
|
||||
Having kwargs allows us to receive parameters in the future.
|
||||
"""
|
||||
if kwargs:
|
||||
LOG.warning('Ignoring arguments %s', kwargs.keys())
|
||||
|
||||
opts.set_defaults(conf)
|
|
@ -24,7 +24,6 @@ import platform
|
|||
import socket
|
||||
import sys
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
|
@ -35,8 +34,6 @@ from os_brick import utils
|
|||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
synchronized = lockutils.synchronized_with_prefix('os-brick-')
|
||||
|
||||
# List of connectors to call when getting
|
||||
# the connector properties for a host
|
||||
windows_connector_list = [
|
||||
|
|
|
@ -13,11 +13,16 @@
|
|||
# under the License.
|
||||
|
||||
|
||||
import functools
|
||||
import glob
|
||||
import os
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
from oslo_concurrency import processutils as putils
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import reflection
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from os_brick import exception
|
||||
from os_brick import initiator
|
||||
|
@ -26,6 +31,64 @@ from os_brick.initiator import initiator_connector
|
|||
from os_brick.initiator import linuxscsi
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def synchronized(name, lock_file_prefix='os-brick-', external=False,
|
||||
lock_path=None, semaphores=None, delay=0.01, fair=False,
|
||||
blocking=True):
|
||||
"""os-brick synchronization decorator
|
||||
|
||||
Like the one in lock_utils but defaulting the prefix to os-brick- and using
|
||||
our own lock_path.
|
||||
|
||||
Cannot use lock_utils one because when using the default we don't know the
|
||||
value until setup has been called, which can be after the code using the
|
||||
decorator has been loaded.
|
||||
"""
|
||||
def wrap(f):
|
||||
@functools.wraps(f)
|
||||
def inner(*args, **kwargs):
|
||||
t1 = timeutils.now()
|
||||
t2 = None
|
||||
gotten = True
|
||||
lpath = lock_path or CONF.os_brick.lock_path
|
||||
# TODO: (AA Release) Remove this failsafe
|
||||
if not lpath and CONF.oslo_concurrency.lock_path:
|
||||
LOG.warning("Service needs to call os_brick.setup() before "
|
||||
"connecting volumes, if it doesn't it will break "
|
||||
"on the next release")
|
||||
lpath = CONF.oslo_concurrency.lock_path
|
||||
f_name = reflection.get_callable_name(f)
|
||||
try:
|
||||
LOG.debug('Acquiring lock "%s" by "%s"', name, f_name)
|
||||
with lockutils.lock(name, lock_file_prefix, external, lpath,
|
||||
do_log=False, semaphores=semaphores,
|
||||
delay=delay, fair=fair, blocking=blocking):
|
||||
t2 = timeutils.now()
|
||||
LOG.debug('Lock "%(name)s" acquired by "%(function)s" :: '
|
||||
'waited %(wait_secs)0.3fs',
|
||||
{'name': name,
|
||||
'function': f_name,
|
||||
'wait_secs': (t2 - t1)})
|
||||
return f(*args, **kwargs)
|
||||
except lockutils.AcquireLockFailedException:
|
||||
gotten = False
|
||||
finally:
|
||||
t3 = timeutils.now()
|
||||
if t2 is None:
|
||||
held_secs = "N/A"
|
||||
else:
|
||||
held_secs = "%0.3fs" % (t3 - t2)
|
||||
LOG.debug('Lock "%(name)s" "%(gotten)s" by "%(function)s" ::'
|
||||
' held %(held_secs)s',
|
||||
{'name': name,
|
||||
'gotten': 'released' if gotten else 'unacquired',
|
||||
'function': f_name,
|
||||
'held_secs': held_secs})
|
||||
return inner
|
||||
|
||||
return wrap
|
||||
|
||||
|
||||
class BaseLinuxConnector(initiator_connector.InitiatorConnector):
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
import os
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
from oslo_log import log as logging
|
||||
from oslo_service import loopingcall
|
||||
|
||||
|
@ -25,8 +24,6 @@ from os_brick.initiator.connectors import base
|
|||
from os_brick.initiator import linuxfc
|
||||
from os_brick import utils
|
||||
|
||||
synchronized = lockutils.synchronized_with_prefix('os-brick-')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -168,7 +165,7 @@ class FibreChannelConnector(base.BaseLinuxConnector):
|
|||
return volume_paths
|
||||
|
||||
@utils.trace
|
||||
@synchronized('extend_volume', external=True)
|
||||
@base.synchronized('extend_volume', external=True)
|
||||
@utils.connect_volume_undo_prepare_result
|
||||
def extend_volume(self, connection_properties):
|
||||
"""Update the local kernel's size information.
|
||||
|
@ -191,7 +188,7 @@ class FibreChannelConnector(base.BaseLinuxConnector):
|
|||
|
||||
@utils.trace
|
||||
@utils.connect_volume_prepare_result
|
||||
@synchronized('connect_volume', external=True)
|
||||
@base.synchronized('connect_volume', external=True)
|
||||
def connect_volume(self, connection_properties):
|
||||
"""Attach the volume to instance_name.
|
||||
|
||||
|
@ -316,7 +313,7 @@ class FibreChannelConnector(base.BaseLinuxConnector):
|
|||
return raw_devices
|
||||
|
||||
@utils.trace
|
||||
@synchronized('connect_volume', external=True)
|
||||
@base.synchronized('connect_volume', external=True)
|
||||
@utils.connect_volume_undo_prepare_result(unlink_after=True)
|
||||
def disconnect_volume(self, connection_properties, device_info,
|
||||
force=False, ignore_errors=False):
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
import os
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
from oslo_log import log as logging
|
||||
|
||||
from os_brick import exception
|
||||
|
@ -23,7 +22,6 @@ from os_brick.initiator.connectors import base
|
|||
from os_brick import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
synchronized = lockutils.synchronized_with_prefix('os-brick-')
|
||||
|
||||
|
||||
class HuaweiStorHyperConnector(base.BaseLinuxConnector):
|
||||
|
@ -85,7 +83,7 @@ class HuaweiStorHyperConnector(base.BaseLinuxConnector):
|
|||
return out['dev_addr']
|
||||
|
||||
@utils.trace
|
||||
@synchronized('connect_volume', external=True)
|
||||
@base.synchronized('connect_volume', external=True)
|
||||
def connect_volume(self, connection_properties):
|
||||
"""Connect to a volume.
|
||||
|
||||
|
@ -116,7 +114,7 @@ class HuaweiStorHyperConnector(base.BaseLinuxConnector):
|
|||
return device_info
|
||||
|
||||
@utils.trace
|
||||
@synchronized('connect_volume', external=True)
|
||||
@base.synchronized('connect_volume', external=True)
|
||||
def disconnect_volume(self, connection_properties, device_info,
|
||||
force=False, ignore_errors=False):
|
||||
"""Disconnect a volume from the local host.
|
||||
|
|
|
@ -21,7 +21,6 @@ import re
|
|||
import time
|
||||
from typing import List, Tuple # noqa: H301
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
from oslo_concurrency import processutils as putils
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
|
@ -36,8 +35,6 @@ from os_brick.initiator.connectors import base_iscsi
|
|||
from os_brick.initiator import utils as initiator_utils
|
||||
from os_brick import utils
|
||||
|
||||
synchronized = lockutils.synchronized_with_prefix('os-brick-')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -474,7 +471,7 @@ class ISCSIConnector(base.BaseLinuxConnector, base_iscsi.BaseISCSIConnector):
|
|||
root_helper=self._root_helper)
|
||||
|
||||
@utils.trace
|
||||
@synchronized('extend_volume', external=True)
|
||||
@base.synchronized('extend_volume', external=True)
|
||||
@utils.connect_volume_undo_prepare_result
|
||||
def extend_volume(self, connection_properties: dict):
|
||||
"""Update the local kernel's size information.
|
||||
|
@ -499,7 +496,7 @@ class ISCSIConnector(base.BaseLinuxConnector, base_iscsi.BaseISCSIConnector):
|
|||
|
||||
@utils.trace
|
||||
@utils.connect_volume_prepare_result
|
||||
@synchronized('connect_volume', external=True)
|
||||
@base.synchronized('connect_volume', external=True)
|
||||
def connect_volume(self, connection_properties: dict):
|
||||
"""Attach the volume to instance_name.
|
||||
|
||||
|
@ -831,7 +828,7 @@ class ISCSIConnector(base.BaseLinuxConnector, base_iscsi.BaseISCSIConnector):
|
|||
return device_map
|
||||
|
||||
@utils.trace
|
||||
@synchronized('connect_volume', external=True)
|
||||
@base.synchronized('connect_volume', external=True)
|
||||
@utils.connect_volume_undo_prepare_result(unlink_after=True)
|
||||
def disconnect_volume(self, connection_properties, device_info,
|
||||
force=False, ignore_errors=False):
|
||||
|
@ -1017,7 +1014,7 @@ class ISCSIConnector(base.BaseLinuxConnector, base_iscsi.BaseISCSIConnector):
|
|||
target_iqn = connection_properties['target_iqn']
|
||||
|
||||
lock_name = f'connect_to_iscsi_portal-{portal}-{target_iqn}'
|
||||
method = synchronized(
|
||||
method = base.synchronized(
|
||||
lock_name, external=True)(self._connect_to_iscsi_portal_unsafe)
|
||||
return method(connection_properties)
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ import tempfile
|
|||
import time
|
||||
import traceback
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
from oslo_concurrency import processutils as putils
|
||||
from oslo_log import log as logging
|
||||
|
||||
|
@ -37,7 +36,6 @@ DEVICE_SCAN_ATTEMPTS_DEFAULT = 5
|
|||
DISCOVERY_CLIENT_PORT = 6060
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
synchronized = lockutils.synchronized_with_prefix('os-brick-')
|
||||
nvmec_pattern = ".*nvme[0-9]+[cp][0-9]+.*"
|
||||
nvmec_match = re.compile(nvmec_pattern)
|
||||
|
||||
|
@ -257,7 +255,7 @@ class LightOSConnector(base.BaseLinuxConnector):
|
|||
|
||||
@utils.trace
|
||||
@utils.connect_volume_prepare_result
|
||||
@synchronized('volume_op')
|
||||
@base.synchronized('volume_op')
|
||||
def connect_volume(self, connection_properties):
|
||||
"""Discover and attach the volume.
|
||||
|
||||
|
@ -292,7 +290,7 @@ class LightOSConnector(base.BaseLinuxConnector):
|
|||
return device_info
|
||||
|
||||
@utils.trace
|
||||
@synchronized('volume_op')
|
||||
@base.synchronized('volume_op')
|
||||
@utils.connect_volume_undo_prepare_result(unlink_after=True)
|
||||
def disconnect_volume(self, connection_properties, device_info,
|
||||
force=False, ignore_errors=False):
|
||||
|
@ -335,7 +333,7 @@ class LightOSConnector(base.BaseLinuxConnector):
|
|||
raise exc
|
||||
|
||||
@utils.trace
|
||||
@synchronized('volume_op')
|
||||
@base.synchronized('volume_op')
|
||||
@utils.connect_volume_undo_prepare_result
|
||||
def extend_volume(self, connection_properties):
|
||||
uuid = connection_properties['uuid']
|
||||
|
|
|
@ -17,7 +17,6 @@ import os.path
|
|||
import re
|
||||
import time
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
from oslo_concurrency import processutils as putils
|
||||
from oslo_log import log as logging
|
||||
|
||||
|
@ -35,8 +34,6 @@ DEV_SEARCH_PATH = '/dev/'
|
|||
|
||||
DEVICE_SCAN_ATTEMPTS_DEFAULT = 5
|
||||
|
||||
synchronized = lockutils.synchronized_with_prefix('os-brick-')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -330,7 +327,7 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
|
||||
@utils.trace
|
||||
@utils.connect_volume_prepare_result
|
||||
@synchronized('connect_volume', external=True)
|
||||
@base.synchronized('connect_volume', external=True)
|
||||
def connect_volume(self, connection_properties):
|
||||
"""Discover and attach the volume.
|
||||
|
||||
|
@ -381,7 +378,7 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
return device_info
|
||||
|
||||
@utils.trace
|
||||
@synchronized('connect_volume', external=True)
|
||||
@base.synchronized('connect_volume', external=True)
|
||||
@utils.connect_volume_undo_prepare_result(unlink_after=True)
|
||||
def disconnect_volume(self, connection_properties, device_info,
|
||||
force=False, ignore_errors=False):
|
||||
|
@ -426,7 +423,7 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
raise
|
||||
|
||||
@utils.trace
|
||||
@synchronized('extend_volume', external=True)
|
||||
@base.synchronized('extend_volume', external=True)
|
||||
@utils.connect_volume_undo_prepare_result
|
||||
def extend_volume(self, connection_properties):
|
||||
"""Update the local kernel's size information.
|
||||
|
|
|
@ -16,7 +16,6 @@ import json
|
|||
import os
|
||||
import urllib
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
from oslo_log import log as logging
|
||||
import requests
|
||||
|
||||
|
@ -30,7 +29,6 @@ from os_brick import utils
|
|||
LOG = logging.getLogger(__name__)
|
||||
DEVICE_SCAN_ATTEMPTS_DEFAULT = 3
|
||||
CONNECTOR_CONF_PATH = '/opt/emc/scaleio/openstack/connector.conf'
|
||||
synchronized = lockutils.synchronized_with_prefix('os-brick-')
|
||||
|
||||
|
||||
def io(_type, nr):
|
||||
|
@ -349,7 +347,7 @@ class ScaleIOConnector(base.BaseLinuxConnector):
|
|||
|
||||
@utils.trace
|
||||
@utils.connect_volume_prepare_result
|
||||
@lockutils.synchronized('scaleio', 'scaleio-', external=True)
|
||||
@base.synchronized('scaleio', 'scaleio-', external=True)
|
||||
def connect_volume(self, connection_properties):
|
||||
"""Connect the volume.
|
||||
|
||||
|
@ -463,7 +461,7 @@ class ScaleIOConnector(base.BaseLinuxConnector):
|
|||
return device_info
|
||||
|
||||
@utils.trace
|
||||
@lockutils.synchronized('scaleio', 'scaleio-', external=True)
|
||||
@base.synchronized('scaleio', 'scaleio-', external=True)
|
||||
@utils.connect_volume_undo_prepare_result(unlink_after=True)
|
||||
def disconnect_volume(self, connection_properties, device_info,
|
||||
force=False, ignore_errors=False):
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright (c) 2022, Red Hat, 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.
|
||||
from oslo_config import cfg
|
||||
|
||||
|
||||
_opts = [
|
||||
cfg.StrOpt('lock_path',
|
||||
default=None, # Set by set_defaults method below on setup
|
||||
help='Directory to use for os-brick lock files. Defaults to '
|
||||
'oslo_concurrency.lock_path which is a sensible default '
|
||||
'for compute nodes, but not for HCI deployments or '
|
||||
'controllers where Glance uses Cinder as a backend, as '
|
||||
'locks should use the same directory.'),
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(_opts, group='os_brick')
|
||||
|
||||
|
||||
def list_opts():
|
||||
"""oslo.config.opts entrypoint for sample config generation."""
|
||||
return [('os_brick', _opts)]
|
||||
|
||||
|
||||
def set_defaults(conf=cfg.CONF):
|
||||
"""Set default values that depend on other libraries.
|
||||
|
||||
Service configuration options must have been initialized before this call
|
||||
because oslo's lock_path doesn't have a value before that.
|
||||
|
||||
Called from both os_brick setup and from the oslo.config.opts entrypoint
|
||||
for sample config generation.
|
||||
"""
|
||||
conf.set_default('lock_path', conf.oslo_concurrency.lock_path, 'os_brick')
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Specific location for os-brick file locks using the ``lock_path``
|
||||
configuration option in the ``os_brick`` configuration group. Previously,
|
||||
os-brick used the consuming service's lock_path for its locks, but there
|
||||
are some deployment configurations (for example, Nova and Cinder collocated
|
||||
on the same host) where this would result in anomalous behavior. Default
|
||||
is to use the consuming service's lock_path.
|
||||
|
||||
This change requires a consuming service to call the ``os_brick.setup``
|
||||
method after service configuration options have been called.
|
||||
upgrade:
|
||||
- |
|
||||
To use the os-brick specific file lock location introduced in this release,
|
||||
an external service using the library must call the ``os_brick.setup``
|
||||
method.
|
|
@ -5,6 +5,7 @@
|
|||
pbr>=5.8.0 # Apache-2.0
|
||||
eventlet>=0.30.1,!=0.32.0 # MIT
|
||||
oslo.concurrency>=4.5.0 # Apache-2.0
|
||||
oslo.config>=8.1.0 # Apache-2.0
|
||||
oslo.context>=3.4.0 # Apache-2.0
|
||||
oslo.log>=4.6.1 # Apache-2.0
|
||||
oslo.i18n>=5.1.0 # Apache-2.0
|
||||
|
|
Loading…
Reference in New Issue