diff --git a/cloudooo/handler/ooo/application/application.py b/cloudooo/handler/ooo/application/application.py index 89145d7e55a598bb820c8beeafd90b2b4ff6408a..91da2e74ee8aadea641babe7a9df7e7326255f28 100644 --- a/cloudooo/handler/ooo/application/application.py +++ b/cloudooo/handler/ooo/application/application.py @@ -29,8 +29,7 @@ from zope.interface import implements from cloudooo.interfaces.application import IApplication from cloudooo.util import logger -from cloudooo.handler.ooo.util import waitStopDaemon -from psutil import pid_exists, Process, AccessDenied +from psutil import pid_exists, Process, TimeoutExpired, NoSuchProcess class Application(object): @@ -47,18 +46,59 @@ class Application(object): self.getAddress()[-1], self.pid())) - def stop(self): - """Stop the process""" - if hasattr(self, 'process') and self.status(): + def stop(self, pid=None): + if hasattr(self, 'process'): + if pid is not None and self.process.pid != pid: + # pid already stopped + return False + error = False process_pid = self.process.pid - logger.debug("Stop Pid - %s" % process_pid) + returncode = self.process.poll() + # libreoffice daemon want go to background + # so it return 0, so ignore 0 + if returncode is not None and returncode != 0: + self.daemonOutputLog() + logger.debug("Process %s already ended with returncode %s", process_pid, returncode) + return False + logger.debug("Stop Pid - %s", process_pid) + cmdline = "" try: - self.process.terminate() - waitStopDaemon(self, self.timeout) - finally: - if pid_exists(process_pid) or self.status(): - Process(process_pid).kill() + process = Process(process_pid) + cmdline = " ".join(process.cmdline()) + process.terminate() + returncode = process.wait(self.timeout) + except NoSuchProcess: + pass + except TimeoutExpired: + error = True + logger.error("Process %s survived SIGTERM after %s", process_pid, self.timeout) + try: + process.kill() + returncode = process.wait(self.timeout) + except NoSuchProcess: + pass + except TimeoutExpired: + logger.error("Process %s survived SIGKILL after %s", process_pid, self.timeout) + + if returncode is None: + returncode = self.process.returncode + if error and returncode: + logger.error("Process %s cmdline: %s ended with returncode %s", process_pid, cmdline, returncode) + elif returncode is not None and returncode != 0: + self.daemonOutputLog() + logger.debug("Process %s ended with returncode %s", process_pid, returncode) delattr(self, "process") + return True + + def daemonOutputLog(self): + if hasattr(self, 'process'): + process_pid = self.process.pid + stdout = self.process.stdout.read() + if stdout: + logger.debug("Process %s stdout: %s", process_pid, stdout) + stderr = self.process.stderr.read() + if stderr: + logger.error("Process %s stderr: %s", process_pid, stderr) def loadSettings(self, hostname, port, path_run_dir, **kwargs): """Define attributes for application instance @@ -78,20 +118,13 @@ class Application(object): self.start(init=False) def status(self): - """Check by socket if the openoffice work.""" - pid = self.pid() - if pid is None or not pid_exists(pid): - return False - - process = Process(pid) - try: - for connection in process.connections(): - if connection.status == 'LISTEN' and \ - connection.laddr[1] == self.port: - return True - except AccessDenied: - return False - + """Check daemon running.""" + if hasattr(self, 'process'): + returncode = self.process.poll() + if returncode is None or returncode == 0: + return True + if pid_exists(self.process.pid): + return True return False def getAddress(self): diff --git a/cloudooo/handler/ooo/application/openoffice.py b/cloudooo/handler/ooo/application/openoffice.py index 4acee1b5cd45645795a5a856d38cb2c65545dc40..033c955e24b031f9aab4ada810fb6a9f989f7ce4 100644 --- a/cloudooo/handler/ooo/application/openoffice.py +++ b/cloudooo/handler/ooo/application/openoffice.py @@ -25,20 +25,25 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## - +import uuid import pkg_resources -import psutil -from psutil import AccessDenied from os.path import exists, join from subprocess import Popen, PIPE from threading import Lock from zope.interface import implements from application import Application from cloudooo.interfaces.lockable import ILockable -from cloudooo.util import logger, convertStringToBool +from cloudooo.util import logger +import signal from cloudooo.handler.ooo.util import waitStartDaemon, \ - removeDirectory, waitStopDaemon, \ - socketStatus + removeDirectory, \ + processUsedFilesInPath, \ + kill_procs_tree +try: + import json +except ImportError: + import simplejson as json +from time import sleep class OpenOffice(Application): @@ -55,30 +60,53 @@ class OpenOffice(Application): """ self._bin_soffice = 'soffice.bin' self._lock = Lock() + self.last_test_error = None self._cleanRequest() - def _testOpenOffice(self, host, port): - """Test if OpenOffice was started correctly""" - logger.debug("Test OpenOffice %s - Pid %s" % (self.getAddress()[-1], - self.pid())) + def _run_libreoffice_tester(self, args=[]): python = join(self.office_binary_path, "python") args = [exists(python) and python or "python", pkg_resources.resource_filename("cloudooo", join('handler', 'ooo', "helper", "openoffice_tester.py")), - "--hostname=%s" % host, - "--port=%s" % port, - "--uno_path=%s" % self.uno_path] - logger.debug("Testing Openoffice Instance %s" % port) + "--connection=%s" % self.getConnection(), + "--uno_path=%s" % self.uno_path, + "--office_binary_path=%s" % self.office_binary_path] + args stdout, stderr = Popen(args, stdout=PIPE, stderr=PIPE, close_fds=True).communicate() - stdout_bool = convertStringToBool(stdout.replace("\n", "")) - if stdout_bool and stderr != "": - logger.debug("%s\n%s" % (stderr, stdout)) - return False - else: - logger.debug("Instance %s works" % port) - return True + if stdout == "": + logger.error("openoffice_tester.py cmdline: %s", " ".join(args)) + logger.error("openoffice_tester.py stdout: %s", stdout) + logger.error("openoffice_tester.py stderr: %s", stderr) + return False, stderr + stdout = json.loads(stdout) + return stdout, stderr + + def _testOpenOffice(self): + """Test if LibreOffice was started correctly""" + logger.debug("%s instance testing - Pid %s" % (self.getConnection(), + self.pid())) + stdout, stderr = self._run_libreoffice_tester() + self.last_test_error = stderr + if stdout == True: + logger.debug("%s instance works" % self.getConnection()) + return stdout + + def _terminate(self): + """Send LibreOffice daemon terminate command""" + logger.debug("%s instance terminating - Pid %s" % (self.getConnection(), + self.pid())) + stdout, stderr = self._run_libreoffice_tester(["--terminate"]) + if stderr: + logger.error(stderr) + if stdout == True: + for num in range(5): + if not exists(self.lock_file): + logger.debug("%s terminate" % self.getConnection()) + return stdout + sleep(1) + logger.error("%s can not terminate" % self.getConnection()) + return stdout def _cleanRequest(self): """Define request attribute as 0""" @@ -96,44 +124,65 @@ class OpenOffice(Application): if environment_dict is None: environment_dict = {} Application.loadSettings(self, hostname, port, path_run_dir) + self.path_user_installation = join(self.path_run_dir, \ + "cloudooo_instance_%s" % self.port) + self.lock_file = join(self.path_user_installation, '.lock') self.office_binary_path = office_binary_path self.uno_path = uno_path self.default_language = default_language self.environment_dict = environment_dict + self.connection = "socket,host=%s,port=%d" % (self.hostname, self.port) + self.connection = "pipe,name=cloudooo_libreoffice_%s" % str(uuid.uuid1()) + + def isRunning(self): + if hasattr(self, "process"): + returncode = self.process.poll() + if returncode is not None and returncode != 0: + return False + return True + return False + + def status(self): + if Application.status(self) and \ + self._testOpenOffice(): + # repeat for check if uno client do not terminate + # libreoffice + if Application.status(self): + return True + return False def _startProcess(self, command, env): """Start OpenOffice.org process""" for i in range(5): self.stop() - waitStopDaemon(self, self.timeout) self.process = Popen(command, + stdout=PIPE, + stderr=PIPE, close_fds=True, env=env) - if not waitStartDaemon(self, self.timeout): - continue - if self._testOpenOffice(self.hostname, self.port): - return - - def _releaseOpenOfficePort(self): - for process in psutil.process_iter(): - try: - if process.exe() == join(self.office_binary_path, self._bin_soffice): - for connection in process.connections(): - if connection.status == "LISTEN" and \ - connection.laddr[1] == self.port: - process.terminate() - except AccessDenied, e: - pass - except TypeError, e: - # exception to prevent one psutil issue with zombie processes - logger.debug(e) - except NotImplementedError, e: - logger.error("lsof isn't installed on this machine: " + str(e)) + sleep(1) + if waitStartDaemon(self, self.timeout - 1): + if self.last_test_error: + logger.debug(self.last_test_error) + return True + return False + + def getConnection(self): + return self.connection def start(self, init=True): """Start Instance.""" - self.path_user_installation = join(self.path_run_dir, \ - "cloudooo_instance_%s" % self.port) + if exists(self.lock_file): + pids = processUsedFilesInPath(self.path_user_installation) + if len(pids) == 0: + logger.debug("Stalled lock file: %s", self.lock_file) + else: + logger.debug("kill process used workdir: %s", self.path_user_installation) + _, alive = kill_procs_tree(pids, sig=signal.SIGKILL, timeout=self.timeout) + if len(alive) > 0: + logger.error("process blocks worked directory and alive after SIGKILL: %s", alive) + removeDirectory(self.path_user_installation) + if init and exists(self.path_user_installation): removeDirectory(self.path_user_installation) # Create command with all parameters to start the instance @@ -145,7 +194,7 @@ class OpenOffice(Application): '--nodefault', '--norestore', '--nofirststartwizard', - '--accept=socket,host=%s,port=%d;urp;' % (self.hostname, self.port), + '--accept=%s;urp;' % self.getConnection(), '-env:UserInstallation=file://%s' % self.path_user_installation, '--language=%s' % self.default_language, ] @@ -155,17 +204,27 @@ class OpenOffice(Application): env["HOME"] = self.path_user_installation env["TMP"] = self.path_user_installation env["TMPDIR"] = self.path_user_installation - self._startProcess(self.command, env) - self._cleanRequest() - Application.start(self) - - def stop(self): - """Stop the instance by pid. By the default - the signal is 15.""" - Application.stop(self) - if socketStatus(self.hostname, self.port): - self._releaseOpenOfficePort() - self._cleanRequest() + if self._startProcess(self.command, env): + self._cleanRequest() + logger.debug("%s libreoffice PID: %s started", self.getConnection(), self.pid()) + else: + logger.error("%s libreoffice not started", self.getConnection()) + + def reset(self, *args, **kw): + if Application.stop(self, *args, **kw): + self._cleanRequest() + + def stop(self, pid=None): + if hasattr(self, 'process'): + if pid is not None and self.process.pid != pid: + return False + returncode = self.process.poll() + if returncode is None or returncode == 0: + self._terminate() + if Application.stop(self, pid=pid): + self._cleanRequest() + return True + return False def isLocked(self): """Verify if OOo instance is being used.""" diff --git a/cloudooo/handler/ooo/handler.py b/cloudooo/handler/ooo/handler.py index 9f2a6f80a7b7082c7fbcdb594f7bfbe5b32fc1fc..ae5f52f3bfbdee30a8ca85ef640ce2b00be5ac27 100644 --- a/cloudooo/handler/ooo/handler.py +++ b/cloudooo/handler/ooo/handler.py @@ -83,13 +83,11 @@ class Handler(object): def _getCommand(self, *args, **kw): """Transforms all parameters passed in a command""" - hostname, port = openoffice.getAddress() - kw['hostname'] = hostname - kw['port'] = port python = path.join(self.office_binary_path, "python") command_list = [path.exists(python) and python or "python", pkg_resources.resource_filename(__name__, path.join("helper", "unoconverter.py")), + "--connection=%s" % openoffice.getConnection(), "--uno_path=%s" % self.uno_path, "--office_binary_path=%s" % self.office_binary_path, '--document_url=%s' % self.document.getUrl()] @@ -119,11 +117,21 @@ class Handler(object): self._startTimeout() process = Popen(command_list, stdout=PIPE, stderr=PIPE, close_fds=True, env=openoffice.environment_dict.copy()) + logger.debug("Process %s running", process.pid) stdout, stderr = process.communicate() finally: self._stopTimeout() if pid_exists(process.pid): + logger.debug("Process %s terminated", process.pid) process.terminate() + if (process.returncode != -3) or stderr: + logger.error("Process %s command:%s", process.pid, " ".join(command_list)) + logger.error("Process %s stdout:%s", process.pid, stdout) + logger.error("Process %s stderr:%s", process.pid, stderr) + logger.debug("Process %s terminated with returncode %s", process.pid, process.returncode) + if process.returncode != -3: + raise ValueError("unocnverter.py should always be interrupted by external signal," + " otherwise deadlock can be raised") return stdout, stderr def _callUnoConverter(self, *feature_list, **kw): @@ -131,18 +139,20 @@ class Handler(object): if not openoffice.status(): openoffice.start() command_list = self._getCommand(*feature_list, **kw) + logger.debug("run convert first") stdout, stderr = self._subprocess(command_list) + logger.debug("stop convert first") if not stdout and stderr: first_error = stderr - logger.error(stderr) self.document.restoreOriginal() openoffice.restart() kw['document_url'] = self.document.getUrl() command = self._getCommand(*feature_list, **kw) + logger.debug("run convert second") stdout, stderr = self._subprocess(command) + logger.debug("stop convert second") if not stdout and stderr: second_error = "\nerror of the second run: " + stderr - logger.error(second_error) raise Exception(first_error + second_error) return stdout, stderr @@ -303,6 +313,5 @@ def bootstrapHandler(configuration_dict): # Load all filters openoffice.acquire() - mimemapper.loadFilterList(application_hostname, - openoffice_port, **kw) + mimemapper.loadFilterList(openoffice.getConnection(), **kw) openoffice.release() diff --git a/cloudooo/handler/ooo/helper/helper_util.py b/cloudooo/handler/ooo/helper/helper_util.py index 4d252825b31e6159f2f21dbd30b812f60b299156..cb0d057399d8146bac2399fbc4a6390084ad6f3d 100644 --- a/cloudooo/handler/ooo/helper/helper_util.py +++ b/cloudooo/handler/ooo/helper/helper_util.py @@ -1,8 +1,8 @@ +import signal import sys import os -import time -def getServiceManager(host, port, uno_path, office_binary_path): +def getServiceManager(connection, uno_path, office_binary_path): """Get the ServiceManager from the running OpenOffice.org. """ # Add in sys.path the path of pyuno @@ -21,13 +21,16 @@ def getServiceManager(host, port, uno_path, office_binary_path): uno_context) # Connect to the running OpenOffice.org and get its # context. - # Retry 10 times if needed. - for i in range(10): - try: - uno_connection = resolver.resolve("uno:socket,host=%s,port=%s;urp;StarOffice.ComponentContext" % (host, port)) - break - except: - # I don't know how to import com.sun.star.connection.NoConnectException - time.sleep(1) + uno_connection = resolver.resolve("uno:%s;urp;StarOffice.ComponentContext" % connection) # Get the ServiceManager object return uno_connection.ServiceManager + +def exitOverAbort(main_func): + try: + main_func() + except: + import traceback + sys.stderr.write(traceback.format_exc()) + sys.stdout.flush() + sys.stderr.flush() + os.kill(os.getpid(), signal.SIGQUIT) \ No newline at end of file diff --git a/cloudooo/handler/ooo/helper/openoffice_tester.py b/cloudooo/handler/ooo/helper/openoffice_tester.py index 188dfa61bafdb372a980e3ff12d8b2755d5fbb21..048b12a92b96acdb213aa0adaca1881a03284021 100755 --- a/cloudooo/handler/ooo/helper/openoffice_tester.py +++ b/cloudooo/handler/ooo/helper/openoffice_tester.py @@ -3,38 +3,56 @@ import sys import helper_util from getopt import getopt, GetoptError +try: + import json +except ImportError: + import simplejson as json -def test_openoffice(hostname, port): + +def test_openoffice(connection, uno_path, office_binary_path, terminate=False): + import pyuno try: - helper_util.getServiceManager(hostname, port) + sm = helper_util.getServiceManager(connection, uno_path, office_binary_path) + if terminate: + try: + sm.createInstance("com.sun.star.frame.Desktop").terminate() + except pyuno.getClass("com.sun.star.beans.UnknownPropertyException"): + pass + except pyuno.getClass("com.sun.star.lang.DisposedException"): + pass return True - except Exception, err: - print err + except pyuno.getClass("com.sun.star.connection.NoConnectException"): return False def main(): try: opt_list, arg_list = getopt(sys.argv[1:], "", - ["port=", "hostname=", "uno_path=", - "office_binary_path="]) - except GetoptError, e: - print >> sys.stderr, "%s \nUse --port and --hostname" % e + ["connection=", "uno_path=", + "office_binary_path=", + "terminate"]) + except GetoptError as e: + sys.stderr.write("%s \nUse --connection" % e) sys.exit(2) - port = hostname = uno_path = office_binary_path = None + connection = uno_path = office_binary_path = None + terminate = False for opt, arg in opt_list: - if opt == "--port": - port = arg - elif opt == "--hostname": - hostname = arg + if opt == "--connection": + connection = arg elif opt == "--uno_path": uno_path = arg + if uno_path not in sys.path: + sys.path.append(uno_path) elif opt == "--office_binary_path": office_binary_path = arg + elif opt == "--terminate": + terminate = True - print test_openoffice(hostname, port, uno_path, office_binary_path) - + output = json.dumps(test_openoffice(connection, + uno_path, office_binary_path, + terminate=terminate)) + sys.stdout.write(output) if __name__ == "__main__": - main() + helper_util.exitOverAbort(main) \ No newline at end of file diff --git a/cloudooo/handler/ooo/helper/unoconverter.py b/cloudooo/handler/ooo/helper/unoconverter.py index d8ad040c997df06950ecc889c6addfe0c2aab86b..6cd03fbb02073d7f0b8737200cf2fd2b41a0eb15 100755 --- a/cloudooo/handler/ooo/helper/unoconverter.py +++ b/cloudooo/handler/ooo/helper/unoconverter.py @@ -75,19 +75,23 @@ Options: class UnoConverter(object): """A module to easily work with OpenOffice.org.""" - def __init__(self, hostname, port, document_url, source_format, uno_path, - office_binary_path, refresh): + def __init__(self, service_manager, document_url, source_format, refresh): """ """ - self.hostname = hostname - self.port = port + self.service_manager = service_manager self.document_url = document_url self.document_dir_path = dirname(document_url) self.source_format = source_format self.refresh = refresh - self.uno_path = uno_path - self.office_binary_path = office_binary_path self._load() + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + if traceback: + sys.stderr.write(str(traceback)) + self.document_loaded.close(True) + def _createProperty(self, name, value): """Create property""" from com.sun.star.beans import PropertyValue @@ -171,9 +175,7 @@ class UnoConverter(object): refresh argument tells to uno environment to replace dynamic properties of document before conversion """ - service_manager = helper_util.getServiceManager(self.hostname, self.port, - self.uno_path, - self.office_binary_path) + service_manager = self.service_manager desktop = service_manager.createInstance("com.sun.star.frame.Desktop") uno_url = self.systemPathToFileUrl(self.document_url) uno_document = desktop.loadComponentFromURL( @@ -210,11 +212,8 @@ class UnoConverter(object): dir=self.document_dir_path) property_list = self._getPropertyToExport(output_format) - try: - self.document_loaded.storeToURL(self.systemPathToFileUrl(output_url), - tuple(property_list)) - finally: - self.document_loaded.dispose() + self.document_loaded.storeToURL(self.systemPathToFileUrl(output_url), + tuple(property_list)) return output_url def getMetadata(self): @@ -242,9 +241,7 @@ class UnoConverter(object): except AttributeError: pass - service_manager = helper_util.getServiceManager(self.hostname, self.port, - self.uno_path, - self.office_binary_path) + service_manager = self.service_manager type_detection = service_manager.createInstance("com.sun.star.document.TypeDetection") uno_file_access = service_manager.createInstance("com.sun.star.ucb.SimpleFileAccess") doc = uno_file_access.openFileRead(self.systemPathToFileUrl(self.document_url)) @@ -286,7 +283,6 @@ class UnoConverter(object): user_defined_properties.addProperty(prop, 0, '') user_defined_properties.setPropertyValue(prop, value) self.document_loaded.store() - self.document_loaded.dispose() def help(): @@ -302,7 +298,7 @@ def main(): opt_list, arg_list = getopt(sys.argv[1:], "h", ["help", "test", "convert", "getmetadata", "setmetadata", "uno_path=", "office_binary_path=", - "hostname=", "port=", "source_format=", + "connection=", "source_format=", "document_url=", "destination_format=", "mimemapper=", "metadata=", "refresh=", "unomimemapper_bin="]) @@ -317,16 +313,13 @@ def main(): import json except ImportError: import simplejson as json - refresh = None - hostname = port = document_url = office_binary_path = uno_path =\ + connection = document_url = office_binary_path = uno_path =\ destination_format = source_format = refresh = metadata = mimemapper = None for opt, arg in iter(opt_list): if opt in ('-h', '--help'): help() - elif opt == '--hostname': - hostname = arg - elif opt == '--port': - port = arg + elif opt == '--connection': + connection = arg elif opt == '--document_url': document_url = arg elif opt == '--office_binary_path': @@ -345,30 +338,30 @@ def main(): elif opt == '--mimemapper': mimemapper = json.loads(arg) - - unoconverter = UnoConverter(hostname, port, document_url, source_format, - uno_path, office_binary_path, refresh) - if "--convert" in param_list and not '--getmetadata' in param_list \ - and not destination_format: - output = unoconverter.convert() - elif '--convert' in param_list and destination_format: - output = unoconverter.convert(destination_format) - elif '--getmetadata' in param_list and not '--convert' in param_list: - metadata_dict = unoconverter.getMetadata() - output = encodestring(json.dumps(metadata_dict).encode('utf-8')).decode('utf-8') - elif '--getmetadata' in param_list and '--convert' in param_list: - document_url = unoconverter.convert() - # Instanciate new UnoConverter instance with new url - unoconverter = UnoConverter(hostname, port, document_url, source_format, - uno_path, office_binary_path, refresh) - metadata_dict = unoconverter.getMetadata() - metadata_dict['document_url'] = document_url - output = encodestring(json.dumps(metadata_dict).encode('utf-8')).decode('utf-8') - elif '--setmetadata' in param_list: - unoconverter.setMetadata(metadata) - output = document_url + service_manager = helper_util.getServiceManager(connection, + uno_path, office_binary_path) + if '--getmetadata' in param_list and '--convert' in param_list: + with UnoConverter(service_manager, document_url, source_format, refresh) as unoconverter: + document_url = unoconverter.convert() + # Instanciate new UnoConverter instance with new url + with UnoConverter(service_manager, document_url, source_format, refresh) as unoconverter: + metadata_dict = unoconverter.getMetadata() + metadata_dict['document_url'] = document_url + output = encodestring(json.dumps(metadata_dict).encode('utf-8')).decode('utf-8') + else: + with UnoConverter(service_manager, document_url, source_format, refresh) as unoconverter: + if "--convert" in param_list and not destination_format: + output = unoconverter.convert() + elif '--convert' in param_list and destination_format: + output = unoconverter.convert(destination_format) + elif '--getmetadata' in param_list: + metadata_dict = unoconverter.getMetadata() + output = encodestring(json.dumps(metadata_dict).encode('utf-8')).decode('utf-8') + elif '--setmetadata' in param_list: + unoconverter.setMetadata(metadata) + output = document_url sys.stdout.write(output) if "__main__" == __name__: - main() + helper_util.exitOverAbort(main) \ No newline at end of file diff --git a/cloudooo/handler/ooo/helper/unomimemapper.py b/cloudooo/handler/ooo/helper/unomimemapper.py index 7ee306d3f377bc3cbda5d71957d2fd07fb6b4316..e550e63b171f68e58ac3ed3f06b17ca866dc8c54 100755 --- a/cloudooo/handler/ooo/helper/unomimemapper.py +++ b/cloudooo/handler/ooo/helper/unomimemapper.py @@ -66,9 +66,9 @@ Options: class UnoMimemapper(object): """ """ - def __init__(self, hostname, port, uno_path=None, office_binary_path=None): + def __init__(self, connection, uno_path=None, office_binary_path=None): """ Receives hostname and port from openoffice and create a service manager""" - self.service_manager = helper_util.getServiceManager(hostname, port, + self.service_manager = helper_util.getServiceManager(connection, uno_path, office_binary_path) @@ -111,7 +111,7 @@ def main(): try: opt_list, arg_list = getopt(sys.argv[1:], "h", ["help", "uno_path=", "office_binary_path=", - "hostname=", "port="]) + "connection="]) except GetoptError as msg: msg = msg.msg + "\nUse --help or -h\n" sys.stderr.write(msg) @@ -120,7 +120,7 @@ def main(): if not opt_list: help() - port = hostname = uno_path = office_binary_path = None + connection = uno_path = office_binary_path = None for opt, arg in opt_list: if opt in ("-h", "--help"): help() @@ -128,12 +128,10 @@ def main(): uno_path = arg elif opt == "--office_binary_path": office_binary_path = arg - elif opt == '--hostname': - hostname = arg - elif opt == "--port": - port = arg + elif opt == '--connection': + connection = arg - mimemapper = UnoMimemapper(hostname, port, uno_path, office_binary_path) + mimemapper = UnoMimemapper(connection, uno_path, office_binary_path) filter_dict = mimemapper.getFilterDict() type_dict = mimemapper.getTypeDict() @@ -141,4 +139,4 @@ def main(): sort_keys=True, indent=2, separators=(',', ':'))) if "__main__" == __name__: - main() + helper_util.exitOverAbort(main) \ No newline at end of file diff --git a/cloudooo/handler/ooo/mimemapper.py b/cloudooo/handler/ooo/mimemapper.py index 187e9f6dd3b97f3130ece68d40c4f9604b6f0099..c8a1a1481bc1e6aa36bd641ae65bf59e66468b68 100644 --- a/cloudooo/handler/ooo/mimemapper.py +++ b/cloudooo/handler/ooo/mimemapper.py @@ -33,6 +33,7 @@ from subprocess import STDOUT from zope.interface import implements from filter import Filter from os import environ, path +from cloudooo.util import logger from cloudooo.interfaces.mimemapper import IMimemapper from types import InstanceType import json @@ -87,7 +88,7 @@ class MimeMapper(object): """Verify if filters were loaded""" return self._loaded - def loadFilterList(self, hostname, port, **kw): + def loadFilterList(self, connection, **kw): """Load all filters of openoffice. Keyword arguments: hostname -- host of OpenOffice @@ -121,12 +122,12 @@ class MimeMapper(object): path.join("helper", "unomimemapper.py")), "--uno_path=%s" % uno_path, "--office_binary_path=%s" % office_binary_path, - "--hostname=%s" % hostname, - "--port=%s" % port] + "--connection=%s" % connection] - process = Popen(command, stdout=PIPE, stderr=STDOUT, close_fds=True) + process = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True) stdout, stderr = process.communicate() - if process.returncode: + if stderr: + logger.error(stderr) raise ValueError(stdout) filter_dict, type_dict = json.loads(stdout) diff --git a/cloudooo/handler/ooo/monitor/memory.py b/cloudooo/handler/ooo/monitor/memory.py index 1308284bff1e7ad2ba4d3d1e9dc0529d3f46e5f3..482ee0c7f8aa36e8829fe1fdeae828838a2c34da 100644 --- a/cloudooo/handler/ooo/monitor/memory.py +++ b/cloudooo/handler/ooo/monitor/memory.py @@ -44,15 +44,18 @@ class MonitorMemory(Monitor, Process): Process.__init__(self) self.limit = limit_memory_usage - def create_process(self): - self.process = psutil.Process(int(self.openoffice.pid())) + def create_process(self, pid): + if not hasattr(self, 'process') or \ + self.process.pid != int(pid): + self.process = psutil.Process(pid) + return self.process - def get_memory_usage(self): + def get_memory_usage(self, pid): + if pid is None: + return 0 try: - if not hasattr(self, 'process') or \ - self.process.pid != int(self.openoffice.pid()): - self.create_process() - return self.process.memory_info().rss / (1024 * 1024) + process = self.create_process(pid) + return process.memory_info().rss / (1024 * 1024) except TypeError: logger.debug("OpenOffice is stopped") return 0 @@ -69,9 +72,10 @@ class MonitorMemory(Monitor, Process): self.status_flag = True logger.debug("Start MonitorMemory") while self.status_flag: - if self.get_memory_usage() > self.limit: - logger.debug("Stopping OpenOffice") - self.openoffice.stop() + pid = self.openoffice.pid() + if self.get_memory_usage(pid) > self.limit: + logger.debug("Stopping OpenOffice on memory limit increase") + self.openoffice.reset(pid=pid) sleep(self.interval) logger.debug("Stop MonitorMemory") diff --git a/cloudooo/handler/ooo/monitor/timeout.py b/cloudooo/handler/ooo/monitor/timeout.py index dcaf5aae0d335eb4e090823baa9481c3e10a2f3a..180899c263c5a1fbcbdc92f8f1553bcb295cbe57 100644 --- a/cloudooo/handler/ooo/monitor/timeout.py +++ b/cloudooo/handler/ooo/monitor/timeout.py @@ -50,7 +50,9 @@ class MonitorTimeout(Monitor, Process): sleep(self.interval) if self.openoffice.isLocked(): logger.debug("Stop OpenOffice - Port %s - Pid %s" % (port, pid)) - self.openoffice.stop() + # using pid is not necessary here + # but safe if incorrect use + self.openoffice.reset(pid=pid) def terminate(self): """Stop the process""" diff --git a/cloudooo/handler/ooo/tests/testOooMimemapper.py b/cloudooo/handler/ooo/tests/testOooMimemapper.py index 38bdfcc86347657181dbe6f79938e92b08326281..d11b21ffd6b1ff4a30557428c11840c593f989d5 100644 --- a/cloudooo/handler/ooo/tests/testOooMimemapper.py +++ b/cloudooo/handler/ooo/tests/testOooMimemapper.py @@ -150,9 +150,7 @@ class TestMimeMapper(HandlerTestCase): """Mimemapper is created and load uno path.""" self.mimemapper = MimeMapper() openoffice.acquire() - hostname, port = openoffice.getAddress() - self.mimemapper.loadFilterList(hostname, - port, + self.mimemapper.loadFilterList(openoffice.getConnection(), python_path=self.python_path) openoffice.release() diff --git a/cloudooo/handler/ooo/tests/testOooMonitorMemory.py b/cloudooo/handler/ooo/tests/testOooMonitorMemory.py index 1d16d4c9c87161729af462d0d3be12101778c2cb..2c1ef54dd7d65ec5373530ee93f0db8a2e76281b 100644 --- a/cloudooo/handler/ooo/tests/testOooMonitorMemory.py +++ b/cloudooo/handler/ooo/tests/testOooMonitorMemory.py @@ -32,7 +32,6 @@ from cloudooo.handler.ooo.application.openoffice import openoffice from cloudooo.handler.ooo.monitor.memory import MonitorMemory from psutil import Process from types import IntType -from cloudooo.tests.handlerTestCase import make_suite OPENOFFICE = True @@ -91,7 +90,7 @@ class TestMonitorMemory(unittest.TestCase): def testCreateProcess(self): """Test if the psutil.Process is create correctly""" self.monitor = MonitorMemory(openoffice, 2, 400) - self.monitor.create_process() + self.monitor.create_process(openoffice.pid()) self.assertTrue(hasattr(self.monitor, 'process')) self.assertEquals(type(self.monitor.process), Process) @@ -99,11 +98,11 @@ class TestMonitorMemory(unittest.TestCase): """Test memory usage""" self.monitor = MonitorMemory(openoffice, 2, 400) openoffice.stop() - memory_usage_int = self.monitor.get_memory_usage() + memory_usage_int = self.monitor.get_memory_usage(openoffice.pid()) self.assertEquals(memory_usage_int, 0) if not openoffice.status(): openoffice.start() - memory_usage_int = self.monitor.get_memory_usage() + memory_usage_int = self.monitor.get_memory_usage(openoffice.pid()) self.assertEquals(type(memory_usage_int), IntType) diff --git a/cloudooo/handler/ooo/tests/testOooUnoConverter.py b/cloudooo/handler/ooo/tests/testOooUnoConverter.py index 85a01dc21968d565fd4e4495e541037b6b7f6e37..c763d30e419188d80cd9d36ab8b38fb49c424fd9 100644 --- a/cloudooo/handler/ooo/tests/testOooUnoConverter.py +++ b/cloudooo/handler/ooo/tests/testOooUnoConverter.py @@ -31,7 +31,7 @@ import magic import pkg_resources from subprocess import Popen, PIPE from os.path import exists, join -from cloudooo.tests.handlerTestCase import HandlerTestCase, make_suite +from cloudooo.tests.handlerTestCase import HandlerTestCase from cloudooo.handler.ooo.application.openoffice import openoffice from cloudooo.handler.ooo.document import FileSystemDocument @@ -47,7 +47,7 @@ class TestUnoConverter(HandlerTestCase): def afterSetUp(self): """ """ openoffice.acquire() - self.hostname, self.port = openoffice.getAddress() + self.connection = openoffice.getConnection() data = open("data/test.odt", 'r').read() self.document = FileSystemDocument(self.tmp_url, data, 'odt') @@ -69,15 +69,14 @@ class TestUnoConverter(HandlerTestCase): "--convert", "--uno_path=%s" % self.uno_path, "--office_binary_path=%s" % self.office_binary_path, - "--hostname=%s" % self.hostname, - "--port=%s" % self.port, + "--connection=%s" % self.connection, "--document_url=%s" % self.document.getUrl(), "--destination_format=%s" % "doc", "--source_format=%s" % "odt", "--mimemapper=%s" % mimemapper_pickled] - stdout, stderr = Popen(command, - stdout=PIPE, - stderr=PIPE).communicate() + process = Popen(command, stdout=PIPE, stderr=PIPE) + stdout, stderr = process.communicate() + self.assertEquals(process.returncode, -3) self.assertEquals(stderr, '') output_url = stdout.replace('\n', '') self.assertTrue(exists(output_url), stdout) diff --git a/cloudooo/handler/ooo/tests/testOooUnoMimemapper.py b/cloudooo/handler/ooo/tests/testOooUnoMimemapper.py index 4c27d8e77a00725616a7a6d4c147f0bedc5c5193..8848a275e7bd2fda1b90ce7d67aa91b50733a830 100644 --- a/cloudooo/handler/ooo/tests/testOooUnoMimemapper.py +++ b/cloudooo/handler/ooo/tests/testOooUnoMimemapper.py @@ -54,39 +54,16 @@ class TestUnoMimeMapper(HandlerTestCase): def testCreateLocalAttributes(self): """Test if filters returns correctly the filters and types in dict""" - hostname, host = openoffice.getAddress() python = path.join(self.office_binary_path, "python") command = [path.exists(python) and python or "python", pkg_resources.resource_filename(self.package_namespace, "/helper/unomimemapper.py"), "--uno_path=%s" % self.uno_path, "--office_binary_path=%s" % self.office_binary_path, - "--hostname=%s" % self.hostname, - "--port=%s" % self.openoffice_port] - stdout, stderr = Popen(command, - stdout=PIPE, - stderr=PIPE).communicate() - self.assertEquals(stderr, '') - filter_dict, type_dict = json.loads(stdout) - self.assertTrue('filter_dict' in locals()) - self.assertTrue('type_dict' in locals()) - self.assertNotEquals(filter_dict.get('writer8'), None) - self.assertEquals(type_dict.get('writer8').get('Name'), 'writer8') - self.assertNotEquals(filter_dict.get('writer8'), None) - self.assertEquals(type_dict.get('writer8').get('PreferredFilter'), 'writer8') - self.assertEquals(stderr, '') - - def testCallUnoMimemapperOnlyHostNameAndPort(self): - """ Test call unomimemapper without uno_path and office_binary_path""" - hostname, host = openoffice.getAddress() - command = [path.join(self.office_binary_path, "python"), - pkg_resources.resource_filename(self.package_namespace, - "/helper/unomimemapper.py"), - "--hostname=%s" % self.hostname, - "--port=%s" % self.openoffice_port] - stdout, stderr = Popen(command, - stdout=PIPE, - stderr=PIPE).communicate() + "--connection=%s" % openoffice.getConnection()] + process = Popen(command, stdout=PIPE, stderr=PIPE) + stdout, stderr = process.communicate() + self.assertEquals(process.returncode, -3) self.assertEquals(stderr, '') filter_dict, type_dict = json.loads(stdout) self.assertTrue('filter_dict' in locals()) @@ -99,8 +76,7 @@ class TestUnoMimeMapper(HandlerTestCase): def testWithoutOpenOffice(self): """Test when the openoffice is stopped""" - error_msg = "couldn\'t connect to socket (Success)\n" - hostname, host = openoffice.getAddress() + error_msg = "helper_util.com.sun.star.connection.NoConnectException: Connector : couldn't connect to " openoffice.stop() python = path.join(self.office_binary_path, "python") command = [path.exists(python) and python or "python", @@ -108,13 +84,12 @@ class TestUnoMimeMapper(HandlerTestCase): "/helper/unomimemapper.py"), "--uno_path=%s" % self.uno_path, "--office_binary_path=%s" % self.office_binary_path, - "--hostname=%s" % self.hostname, - "--port=%s" % self.openoffice_port] - stdout, stderr = Popen(command, - stdout=PIPE, - stderr=PIPE).communicate() + "--connection=%s" % openoffice.getConnection()] + process = Popen(command, stdout=PIPE, stderr=PIPE) + stdout, stderr = process.communicate() + self.assertEquals(process.returncode, -3) self.assertEquals(stdout, '') - self.assertTrue(stderr.endswith(error_msg), stderr) + self.assertTrue(error_msg in stderr, stderr) openoffice.start() diff --git a/cloudooo/handler/ooo/tests/testOooUnoOfficeTester.py b/cloudooo/handler/ooo/tests/testOooUnoOfficeTester.py new file mode 100644 index 0000000000000000000000000000000000000000..1a17e8cc450a5d1a2f5318bf20fcf46d1fe0b793 --- /dev/null +++ b/cloudooo/handler/ooo/tests/testOooUnoOfficeTester.py @@ -0,0 +1,153 @@ +############################################################################## +# +# Copyright (c) 2009-2010 Nexedi SA and Contributors. All Rights Reserved. +# Gabriel M. Monnerat +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsibility of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# guarantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program 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. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +import json +import pkg_resources +from cloudooo.handler.ooo.application.openoffice import openoffice +from subprocess import Popen, PIPE +from os import environ, path +from cloudooo.tests.handlerTestCase import HandlerTestCase +from time import sleep + +OPENOFFICE = True + + +class TestUnoOfficeTester(HandlerTestCase): + """Test Case to test all features of script openoffice_tester.py""" + + def afterSetUp(self): + """ """ + self.package_namespace = "cloudooo.handler.ooo" + environ['uno_path'] = '' + environ['office_binary_path'] = '' + openoffice.acquire() + + def tearDown(self): + """ """ + environ['uno_path'] = self.uno_path + environ['office_binary_path'] = self.office_binary_path + openoffice.release() + + def testWithOpenOffice(self): + """Test when the openoffice is started""" + python = path.join(self.office_binary_path, "python") + command = [path.exists(python) and python or "python", + pkg_resources.resource_filename(self.package_namespace, + "/helper/openoffice_tester.py"), + "--uno_path=%s" % self.uno_path, + "--office_binary_path=%s" % self.office_binary_path, + "--connection=%s" % openoffice.getConnection()] + process = Popen(command, stdout=PIPE, stderr=PIPE) + stdout, stderr = process.communicate() + self.assertEquals(process.returncode, -3) + self.assertEquals(stderr, '', stderr) + result = json.loads(stdout) + self.assertTrue(result) + + def testWithoutOpenOffice(self): + """Test when the openoffice is stopped""" + openoffice.stop() + python = path.join(self.office_binary_path, "python") + command = [path.exists(python) and python or "python", + pkg_resources.resource_filename(self.package_namespace, + "/helper/openoffice_tester.py"), + "--uno_path=%s" % self.uno_path, + "--office_binary_path=%s" % self.office_binary_path, + "--connection=%s" % openoffice.getConnection()] + process = Popen(command, stdout=PIPE, stderr=PIPE) + stdout, stderr = process.communicate() + self.assertEquals(process.returncode, -3) + self.assertEquals(stderr, '', stderr) + result = json.loads(stdout) + self.assertFalse(result) + openoffice.start() + + def testExceptionNotify(self): + """Test correctly notify about issues""" + exception = "ModuleNotFoundError: No module named" + exception1 = "ImportError:" + command = ["python", + pkg_resources.resource_filename(self.package_namespace, + "/helper/openoffice_tester.py"), + "--uno_path=%s" % self.uno_path + "not_exist_folder", + "--office_binary_path=%s" % self.office_binary_path, + "--connection=%s" % openoffice.getConnection()] + process = Popen(command, stdout=PIPE, stderr=PIPE) + stdout, stderr = process.communicate() + self.assertEquals(process.returncode, -3) + self.assertEquals(stdout, '', stdout) + self.assertTrue((exception in stderr) or (exception1 in stderr), stderr) + + def testOpenOfficeTermination(self): + """Test termination when the openoffice is started""" + python = path.join(self.office_binary_path, "python") + self.assertTrue(path.exists(openoffice.lock_file), "libreoffice not started - lock_file is not exist") + command = [path.exists(python) and python or "python", + pkg_resources.resource_filename(self.package_namespace, + "/helper/openoffice_tester.py"), + "--uno_path=%s" % self.uno_path, + "--office_binary_path=%s" % self.office_binary_path, + "--connection=%s" % openoffice.getConnection(), + "--terminate"] + process = Popen(command, stdout=PIPE, stderr=PIPE) + stdout, stderr = process.communicate() + for num in range(5): + if not path.exists(openoffice.lock_file): + break + sleep(1) + self.assertEquals(process.returncode, -3) + self.assertEquals(stderr, '', stderr) + result = json.loads(stdout) + self.assertTrue(result) + self.assertFalse(path.exists(openoffice.lock_file), "libreoffice not correctly stopped - lock_file exist") + openoffice.start() + + def testOpenOfficeTerminationIfStoped(self): + """Test termination when the openoffice is stopped""" + openoffice.stop() + self.assertFalse(path.exists(openoffice.lock_file), "libreoffice not correctly stopped - lock_file exist") + python = path.join(self.office_binary_path, "python") + command = [path.exists(python) and python or "python", + pkg_resources.resource_filename(self.package_namespace, + "/helper/openoffice_tester.py"), + "--uno_path=%s" % self.uno_path, + "--office_binary_path=%s" % self.office_binary_path, + "--connection=%s" % openoffice.getConnection(), + "--terminate"] + process = Popen(command, stdout=PIPE, stderr=PIPE) + stdout, stderr = process.communicate() + for num in range(5): + if not path.exists(openoffice.lock_file): + break + sleep(1) + self.assertEquals(process.returncode, -3) + self.assertEquals(stderr, '', stderr) + result = json.loads(stdout) + self.assertFalse(result) + self.assertFalse(path.exists(openoffice.lock_file), "libreoffice not correctly stopped - lock_file exist") + openoffice.start() diff --git a/cloudooo/handler/ooo/util.py b/cloudooo/handler/ooo/util.py index ac48de95e9c6c45117c61b3f84e1382a7f03ae38..a2928b808f2b1c8de3b7cc18142ebdfe33c8e025 100644 --- a/cloudooo/handler/ooo/util.py +++ b/cloudooo/handler/ooo/util.py @@ -26,12 +26,13 @@ # ############################################################################## -from socket import socket, error -from errno import EADDRINUSE +import os from time import sleep from os import remove from shutil import rmtree from cloudooo.util import logger +from psutil import process_iter, Process, NoSuchProcess, wait_procs +import signal def removeDirectory(path): @@ -41,26 +42,13 @@ def removeDirectory(path): except OSError, msg: logger.error(msg) - -def socketStatus(hostname, port): - """Verify if the address is busy.""" - try: - socket().bind((hostname, port),) - # False if the is free - return False - except error, (num, err): - if num == EADDRINUSE: - # True if the isn't free - return True - - def waitStartDaemon(daemon, attempts): """Wait a certain time to start the daemon.""" for num in range(attempts): - if daemon.status(): - return True - elif daemon.pid() is None: + if not daemon.isRunning(): return False + elif daemon.status(): + return True sleep(1) return False @@ -73,6 +61,42 @@ def waitStopDaemon(daemon, attempts=5): sleep(1) return False +def processUsedFilesInPath(path): + pids = set() + for p in process_iter(attrs=['open_files', 'memory_maps']): + for f in (p.info['open_files'] or []) + (p.info['memory_maps'] or []): + if f.path.startswith(path): + pids.add(p.pid) + return pids + +def kill_procs_tree(pids, sig=signal.SIGTERM, + timeout=3, on_terminate=None): + pids = set(pids) + children_pids = set(pids) + for pid in pids: + parent = None + try: + parent = Process(pid) + except NoSuchProcess: + pass + if parent: + children = parent.children(recursive=True) + for p in children: + children_pids.add(p.pid) + my_pid = os.getpid() + if my_pid in children_pids: + children_pids.remove(my_pid) + pids = [] + for pid in children_pids: + try: + p = Process(pid) + p.send_signal(sig) + pids.append(p) + except NoSuchProcess: + pass + gone, alive = wait_procs(pids, timeout=timeout, + callback=on_terminate) + return (gone, alive) def remove_file(filepath): try: diff --git a/cloudooo/tests/handlerTestCase.py b/cloudooo/tests/handlerTestCase.py index e8207f4d02c0b830d0537e55bca1740c38ad9c1d..aab676e0f8144cbd125b5320d59a5c4215d04a47 100644 --- a/cloudooo/tests/handlerTestCase.py +++ b/cloudooo/tests/handlerTestCase.py @@ -29,7 +29,8 @@ import unittest import sys -from os import environ, path, mkdir, putenv +from cloudooo import util +from os import environ, path, mkdir from ConfigParser import ConfigParser from cloudooo.handler.ooo.application.openoffice import openoffice from cloudooo.handler.ooo.mimemapper import mimemapper @@ -58,8 +59,10 @@ def startFakeEnvironment(start_openoffice=True, conf_path=None): uno_path = config.get("app:main", "uno_path") working_path = config.get("app:main", "working_path") hostname = config.get("server:main", "host") - openoffice_port = int(config.get("app:main", "openoffice_port")) + openoffice_port = int(config.get("app:main", "openoffice_port")) + 1 office_binary_path = config.get("app:main", "office_binary_path") + if 0: + util.configureLogger(debug_mode=True) environment_dict = {} for item in config.options("app:main"): if item.startswith("env-"): @@ -68,18 +71,9 @@ def startFakeEnvironment(start_openoffice=True, conf_path=None): check_folder(working_path, tmp_dir) if not environ.get('uno_path'): environ['uno_path'] = uno_path - office_binary_path = config.get("app:main", "office_binary_path") if not environ.get('office_binary_path'): environ['office_binary_path'] = office_binary_path - if uno_path not in sys.path: - sys.path.append(uno_path) - - fundamentalrc_file = '%s/fundamentalrc' % office_binary_path - if path.exists(fundamentalrc_file) and \ - 'URE_BOOTSTRAP' not in environ: - putenv('URE_BOOTSTRAP', 'vnd.sun.star.pathname:%s' % fundamentalrc_file) - if start_openoffice: default_language = config.get('app:main', 'openoffice_user_interface_language', False, @@ -93,11 +87,10 @@ def startFakeEnvironment(start_openoffice=True, conf_path=None): environment_dict) openoffice.start() openoffice.acquire() - hostname, port = openoffice.getAddress() kw = dict(uno_path=config.get("app:main", "uno_path"), office_binary_path=config.get("app:main", "office_binary_path")) if not mimemapper.isLoaded(): - mimemapper.loadFilterList(hostname, port, **kw) + mimemapper.loadFilterList(openoffice.getConnection(), **kw) openoffice.release() return openoffice @@ -108,28 +101,6 @@ def stopFakeEnvironment(stop_openoffice=True): openoffice.stop() return True -if 1: - from cloudooo.handler.ooo.application.openoffice import OpenOffice - from cloudooo.handler.ooo.util import waitStartDaemon, waitStopDaemon - from subprocess import Popen, PIPE - - # patch OpenOffice._startProcess not to put bogus output to stderr, - # that prevents detecting the end of unit test. - def _startProcess(self, command, env): - """Start OpenOffice.org process""" - for i in range(5): - self.stop() - waitStopDaemon(self, self.timeout) - self.process = Popen(command, stderr=PIPE, - close_fds=True, - env=env) - if not waitStartDaemon(self, self.timeout): - continue - if self._testOpenOffice(self.hostname, self.port): - return - - OpenOffice._startProcess = _startProcess - class HandlerTestCase(unittest.TestCase): """Test Case to load cloudooo conf."""