103 lines
3.5 KiB
Python
103 lines
3.5 KiB
Python
# Copyright (C) 2021 Nippon Telegraph and Telephone Corporation
|
|
# 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.
|
|
|
|
import functools
|
|
import inspect
|
|
|
|
from oslo_log import log as logging
|
|
|
|
from tacker.common import coordination
|
|
|
|
from tacker.sol_refactored.common import exceptions as sol_ex
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
# NOTE: It is used to prevent operation for the same vnf instance
|
|
# from being processed at the same time. It can be applied between
|
|
# threads of a process and different processes (e.g. tacker-server
|
|
# and tacker-conductor) on a same host.
|
|
# Note that race condition of very short time is not considered.
|
|
|
|
def lock_vnf_instance(inst_arg, delay=False):
|
|
# NOTE: tacker-server issues RPC call to tacker-conductor
|
|
# (just) before the lock released. 'delay' is for tacker-conductor
|
|
# to be able to wait if it receives RPC call before tacker-server
|
|
# releases the lock.
|
|
|
|
def operation_lock(func):
|
|
@functools.wraps(func)
|
|
def wrapper(*args, **kwargs):
|
|
coord = coordination.COORDINATOR
|
|
# ensure coordination start
|
|
# NOTE: it is noop if already started.
|
|
coord.start()
|
|
|
|
sig = inspect.signature(func)
|
|
call_args = sig.bind(*args, **kwargs).arguments
|
|
inst_id = inst_arg.format(**call_args)
|
|
lock = coord.get_lock(inst_id)
|
|
|
|
blocking = False if not delay else 10
|
|
# NOTE: 'with lock' is not used since it can't handle
|
|
# lock failed exception well.
|
|
if not lock.acquire(blocking=blocking):
|
|
LOG.error("Locking vnfInstance %s failed.", inst_id)
|
|
raise sol_ex.OtherOperationInProgress(inst_id=inst_id)
|
|
|
|
try:
|
|
LOG.debug("vnfInstance %s locked.", inst_id)
|
|
return func(*args, **kwargs)
|
|
finally:
|
|
lock.release()
|
|
|
|
return wrapper
|
|
|
|
return operation_lock
|
|
|
|
|
|
def lock_resources(res_arg, delay=False):
|
|
|
|
def operation_lock(func):
|
|
@functools.wraps(func)
|
|
def wrapper(*args, **kwargs):
|
|
coord = coordination.COORDINATOR
|
|
# ensure coordination start
|
|
# NOTE: it is noop if already started.
|
|
coord.start()
|
|
|
|
sig = inspect.signature(func)
|
|
call_args = sig.bind(*args, **kwargs).arguments
|
|
res_id = res_arg.format(**call_args)
|
|
lock = coord.get_lock(res_id)
|
|
|
|
blocking = False if not delay else 10
|
|
# NOTE: 'with lock' is not used since it can't handle
|
|
# lock failed exception well.
|
|
if not lock.acquire(blocking=blocking):
|
|
LOG.debug("Locking resources %s failed.", res_id)
|
|
raise sol_ex.ResourcesOtherOperationInProgress(inst_id=res_id)
|
|
|
|
try:
|
|
LOG.debug("resources %s locked.", res_id)
|
|
return func(*args, **kwargs)
|
|
finally:
|
|
lock.release()
|
|
|
|
return wrapper
|
|
|
|
return operation_lock
|