"""Provides classes for locking the MCVirt daemon whilst a function is being performed"""
# Copyright (c) 2016 - I.T. Dev Ltd
#
# This file is part of MCVirt.
#
# MCVirt is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# MCVirt is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with MCVirt. If not, see <http://www.gnu.org/licenses/>
import Pyro4
from threading import Lock
from mcvirt.exceptions import MCVirtException
from mcvirt.logger import Logger, getLogNames
from mcvirt.syslogger import Syslogger
[docs]class MethodLock(object):
"""Class for storing/generating/obtaining a lock object"""
_lock = None
@classmethod
[docs] def get_lock(cls):
"""Obtain the lock object and return"""
if cls._lock is None:
cls._lock = Lock()
return cls._lock
[docs]def locking_method(object_type=None, instance_method=True):
"""Provide a decorator method for locking the node whilst performing the method"""
def wrapper(callback):
callback.OBJECT_TYPE = wrapper.object_type
callback.INSTANCE_METHOD = wrapper.instance_method
def lock_log_and_call(*args, **kwargs):
# Attempt to obtain object type and name for logging
object_name, object_type = getLogNames(callback,
wrapper.instance_method,
wrapper.object_type,
args=args,
kwargs=kwargs)
lock = MethodLock.get_lock()
# If the current Pyro connection has the lock, then do not attempt
# to lock again, as this will be caused by a locking method calling
# another locking method, which should not attempt to re-obtain the lock
requires_lock = (not ('has_lock' in dir(Pyro4.current_context) and
Pyro4.current_context.has_lock))
logger = Logger()
if 'proxy_user' in dir(Pyro4.current_context) and Pyro4.current_context.proxy_user:
username = Pyro4.current_context.proxy_user
elif 'username' in dir(Pyro4.current_context):
username = Pyro4.current_context.username
else:
username = ''
if requires_lock:
log = logger.create_log(callback, user=username,
object_name=object_name,
object_type=object_type)
else:
log = None
if requires_lock:
lock.acquire()
# @TODO: lock entire cluster - raise exception if it cannot
# be obtained in short period (~5 seconds)
Pyro4.current_context.has_lock = True
if log:
log.start()
response = None
try:
response = callback(*args, **kwargs)
except MCVirtException as e:
Syslogger.logger().error('An internal MCVirt exception occurred in lock')
Syslogger.logger().error("".join(Pyro4.util.getPyroTraceback()))
if log:
log.finish_error(e)
if requires_lock:
if lock.locked():
lock.release()
Pyro4.current_context.has_lock = False
raise
except Exception as e:
Syslogger.logger().error('Unknown exception occurred in lock')
Syslogger.logger().error("".join(Pyro4.util.getPyroTraceback()))
if log:
log.finish_error_unknown(e)
if requires_lock:
if lock.locked():
lock.release()
Pyro4.current_context.has_lock = False
raise
if log:
log.finish_success()
if requires_lock:
if lock.locked():
lock.release()
Pyro4.current_context.has_lock = False
return response
return lock_log_and_call
wrapper.instance_method = instance_method
wrapper.object_type = object_type
return wrapper