prevent running second copy of fuelmenu
Create a file and take a lock on it before running main part of fuel-menu, so running two copies would be impossible. This patch adds an option -l, --lock-file to have ability to specify path to lock file. Change-Id: I8ab41e6e068caa0881e0affcbfb5695a9025b762 Closes-Bug: #1566401
This commit is contained in:
parent
cc4628c865
commit
3f84153673
@ -11,11 +11,14 @@
|
||||
# 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 __future__ import print_function
|
||||
import fcntl
|
||||
import logging
|
||||
import os
|
||||
import random as _random
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from fuelmenu import consts
|
||||
|
||||
@ -86,3 +89,21 @@ def gensalt():
|
||||
sha512prefix = "$6$"
|
||||
random_letters = ''.join(random.choice(letters) for _ in range(16))
|
||||
return sha512prefix + random_letters
|
||||
|
||||
|
||||
def lock_running(lock_file):
|
||||
"""Tries to acquire app lock
|
||||
|
||||
:param lock_file: a path to a file for locking
|
||||
|
||||
:returns: True in case of success, False, if lock's already held
|
||||
"""
|
||||
global lock_file_obj
|
||||
lock_file_obj = open(lock_file, "w")
|
||||
try:
|
||||
fcntl.lockf(lock_file_obj, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
return True
|
||||
except IOError:
|
||||
print("Another copy of fuelmenu is running. "
|
||||
"Please exit it and try again.", file=sys.stderr)
|
||||
return False
|
||||
|
@ -21,6 +21,8 @@ PUPPET_LOGFILE = "/var/log/puppet/fuelmenu-puppet.log"
|
||||
SETTINGS_FILE = "/etc/fuel/astute.yaml"
|
||||
RELEASE_FILE = "/etc/fuel_release"
|
||||
|
||||
DEFAULT_LOCK_FILE = "/var/run/fuelmenu.lock"
|
||||
|
||||
PRE_DEPLOYMENT_MODE = "pre"
|
||||
POST_DEPLOYMENT_MODE = "post"
|
||||
|
||||
|
@ -469,8 +469,17 @@ def main(*args, **kwargs):
|
||||
parser.add_option("-i", "--iface", dest="iface", metavar="IFACE",
|
||||
default=default_iface, help="Set IFACE as primary.")
|
||||
|
||||
parser.add_option("-l", "--lock-file",
|
||||
default=consts.DEFAULT_LOCK_FILE,
|
||||
help="Path to the process lock file. If unspecified, "
|
||||
"the default {} is used."
|
||||
.format(consts.DEFAULT_LOCK_FILE))
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if not utils.lock_running(options.lock_file):
|
||||
sys.exit(1)
|
||||
|
||||
if not network.is_interface_has_ip(options.iface):
|
||||
print("Selected interface '{0}' has no assigned IP. "
|
||||
"Could not start.".format(options.iface))
|
||||
|
@ -14,6 +14,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import signal
|
||||
import tempfile
|
||||
|
||||
from fuelmenu.common import utils
|
||||
|
||||
import mock
|
||||
@ -73,3 +77,54 @@ class TestUtils(unittest.TestCase):
|
||||
mocked_open.side_effect = IOError()
|
||||
data = utils.get_fuel_version()
|
||||
self.assertEqual("", data)
|
||||
|
||||
def test_lock_running(self):
|
||||
lock_file = tempfile.mktemp()
|
||||
self.assertTrue(utils.lock_running(lock_file))
|
||||
|
||||
def test_lock_running_fail(self):
|
||||
|
||||
def handler(signum, frame):
|
||||
raise Exception("Timeout occured while running unit test "
|
||||
"test_lock_running_fail")
|
||||
|
||||
# set an alarm, because test may hang
|
||||
signal.signal(signal.SIGALRM, handler)
|
||||
signal.setitimer(signal.ITIMER_REAL, 3)
|
||||
|
||||
lock_file = tempfile.mktemp()
|
||||
|
||||
read_fd1, write_fd1 = os.pipe()
|
||||
read_fd2, write_fd2 = os.pipe()
|
||||
pid = os.fork()
|
||||
if pid == 0:
|
||||
# Run lock_running in child first
|
||||
os.close(read_fd1)
|
||||
os.close(write_fd2)
|
||||
write_f1 = os.fdopen(write_fd1, 'w')
|
||||
read_f2 = os.fdopen(read_fd2, 'r')
|
||||
|
||||
utils.lock_running(lock_file)
|
||||
|
||||
write_f1.write('x')
|
||||
write_f1.close()
|
||||
read_f2.read()
|
||||
|
||||
# exit from child by issuing execve, so that unit
|
||||
# testing framework will not finish in two instances
|
||||
os.execlp('true', 'true')
|
||||
else:
|
||||
# then in parent
|
||||
os.close(write_fd1)
|
||||
os.close(read_fd2)
|
||||
read_f1 = os.fdopen(read_fd1, 'r')
|
||||
write_f2 = os.fdopen(write_fd2, 'w')
|
||||
read_f1.read()
|
||||
|
||||
# child is holding lock at this point
|
||||
self.assertFalse(utils.lock_running(lock_file))
|
||||
|
||||
write_f2.write('x')
|
||||
write_f2.close()
|
||||
|
||||
signal.alarm(0)
|
||||
|
Loading…
Reference in New Issue
Block a user