1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
# -*- coding: utf-8 -*-
"""
This is an abstract class providing common methods shared by all service types.
The semantics of the lock file mechanism may be found `here <https://github.com/subuser-security/subuser/issues/31>`_ .
"""
#external imports
import abc
import json
import os
import errno
import fcntl
import shutil
import sys
from collections import OrderedDict
#internal imports
from subuserlib.classes.userOwnedObject import UserOwnedObject
class Service(UserOwnedObject):
__metaclass__ = abc.ABCMeta
def __init__(self,user,subuser):
self.__subuser = subuser
UserOwnedObject.__init__(self,user)
@abc.abstractmethod
def start(self,serviceStatus):
"""
Start the service. Block untill the service has started. Returns a modified service status dictionary with any service specific properties set.
"""
pass
@abc.abstractmethod
def stop(self,serviceStatus):
"""
Stop the service. Block untill the service has stopped.
"""
pass
@abc.abstractmethod
def cleanUp(self):
pass
@abc.abstractmethod
def isRunning(self,serviceStatus):
"""
Returns True if the services is running.
"""
pass
def getLockfileDir(self):
return os.path.join(self.user.config["lock-dir"],"services",self.__subuser.name)
def getLockfilePath(self):
return os.path.join(self.getLockfileDir(),self.name+".json")
def removeLockFile(self):
os.remove(self.getLockfilePath())
try:
os.rmdir(self.getLockfileDir())
except OSError:
pass
def getLock(self):
try:
self.user.endUser.makedirs(self.getLockfileDir())
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
while True:
try:
lockFd = open(self.getLockfilePath(),mode="r+")
break
except IOError:
self.user.endUser.create_file(self.getLockfilePath())
fcntl.flock(lockFd,fcntl.LOCK_EX)
return lockFd
def addClient(self):
"""
Increase the services client counter, starting the service if necessary. Blocks untill the service is ready to accept the new client.
"""
with self.getLock() as lockFile:
try:
serviceStatus = json.load(lockFile, object_pairs_hook=OrderedDict)
except ValueError:
serviceStatus = {}
serviceStatus["client-counter"] = 0
if serviceStatus["client-counter"] == 0 or not self.isRunning(serviceStatus):
serviceStatus["client-counter"] = 0
serviceStatus = self.start(serviceStatus)
serviceStatus["client-counter"] = serviceStatus["client-counter"] + 1
lockFile.seek(0)
lockFile.truncate()
json.dump(serviceStatus,lockFile)
fcntl.flock(lockFile,fcntl.LOCK_UN)
def removeClient(self):
"""
Decrease the services client counter, stopping the service if no longer necessary.
"""
noMoreClients = False
with self.getLock() as lockFile:
try:
lock_info = lockFile.read()
serviceStatus = json.loads(lock_info, object_pairs_hook=OrderedDict)
except ValueError as e:
sys.exit("Error in lock file. Failed to release lock:\n"+lock_info+"\n"+str(e))
serviceStatus["client-counter"] = serviceStatus["client-counter"] - 1
if serviceStatus["client-counter"] < 0:
raise RemoveClientException("The client-counter is already zero. Client cannot be removed! \n" + str(serviceStatus))
if serviceStatus["client-counter"] == 0:
self.stop(serviceStatus)
noMoreClients = True
lockFile.seek(0)
lockFile.truncate()
json.dump(serviceStatus,lockFile)
if noMoreClients:
self.removeLockFile()
fcntl.flock(lockFile,fcntl.LOCK_UN)
def cleanUpIfNotRunning(self):
"""
Check if running.
If not running, run cleanUp method.
"""
with self.getLock() as lockFile:
try:
serviceStatus = json.load(lockFile, object_pairs_hook=OrderedDict)
except ValueError:
serviceStatus = {}
serviceStatus["client-counter"] = 0
if serviceStatus["client-counter"] == 0 or not self.isRunning(serviceStatus):
self.cleanUp()
self.removeLockFile()
fcntl.flock(lockFile,fcntl.LOCK_UN)
class RemoveClientException(Exception):
pass
|