330 lines
13 KiB
Python
330 lines
13 KiB
Python
|
# Copyright (c) 2009-2011 by Minor Gordon, Bjoern Kolbeck, Zuse Institute Berlin
|
||
|
# Licensed under the BSD License, see LICENSE file for details.
|
||
|
|
||
|
from datetime import datetime
|
||
|
from time import sleep
|
||
|
import sys, os, subprocess, signal
|
||
|
|
||
|
class Server:
|
||
|
def __init__(self,
|
||
|
start_stop_retries,
|
||
|
config_file_path,
|
||
|
run_dir_path,
|
||
|
xtreemfs_dir,
|
||
|
data_dir,
|
||
|
rpc_port,
|
||
|
uuid,
|
||
|
storage_threads):
|
||
|
self._start_stop_retries = start_stop_retries
|
||
|
self._config_file_path = config_file_path
|
||
|
self._run_dir_path = run_dir_path
|
||
|
self._xtreemfs_dir = xtreemfs_dir
|
||
|
self._data_dir = data_dir
|
||
|
self._config = dict()
|
||
|
# Initialize with default values
|
||
|
self._config['listen.port'] = rpc_port
|
||
|
self._config['http_port'] = rpc_port - 2000
|
||
|
self._config['debug.level'] = 6
|
||
|
self._config['uuid'] = uuid
|
||
|
self._config['ssl.enabled'] = 'false'
|
||
|
self._config['storage_threads'] = storage_threads
|
||
|
|
||
|
def configure(self):
|
||
|
pass
|
||
|
# Nothing to do here.
|
||
|
|
||
|
def set_debug_level(self, debug_level):
|
||
|
self._config['debug.level'] = debug_level
|
||
|
|
||
|
def enable_ssl(self,
|
||
|
use_gridssl,
|
||
|
pkcs12_file_path,
|
||
|
pkcs12_passphrase,
|
||
|
trusted_certs_jks_file_path,
|
||
|
trusted_certs_jks_passphrase):
|
||
|
self._config['ssl.enabled'] = 'true'
|
||
|
if use_gridssl:
|
||
|
self._config['ssl.grid_ssl'] = 'true'
|
||
|
else:
|
||
|
self._config['ssl.grid_ssl'] = 'false'
|
||
|
|
||
|
self._config['ssl.service_creds'] = pkcs12_file_path
|
||
|
self._config['ssl.service_creds.pw'] = pkcs12_passphrase
|
||
|
self._config['ssl.service_creds.container'] = 'PKCS12'
|
||
|
self._config['ssl.trusted_certs'] = trusted_certs_jks_file_path
|
||
|
self._config['ssl.trusted_certs.pw'] = trusted_certs_jks_passphrase
|
||
|
self._config['ssl.trusted_certs.container'] = 'JKS'
|
||
|
|
||
|
# set configuration parameters required for SNMP support
|
||
|
def enable_snmp(self,
|
||
|
snmp_port,
|
||
|
snmp_address,
|
||
|
snmp_aclfile):
|
||
|
self._config['snmp.enabled'] = 'true'
|
||
|
self._config['snmp.port'] = snmp_port
|
||
|
self._config['snmp.address'] = snmp_address
|
||
|
self._config['snmp.aclfile'] = snmp_aclfile
|
||
|
|
||
|
|
||
|
# Imports the configuration from the config file.
|
||
|
def read_config_file(self):
|
||
|
self._config = dict()
|
||
|
for line in open(self._config_file_path).readlines():
|
||
|
line_parts = line.split( "=", 1 )
|
||
|
if len( line_parts ) == 2:
|
||
|
self._config[line_parts[0].strip()] = line_parts[1].strip()
|
||
|
|
||
|
# Writes the current configuration to a config file
|
||
|
def write_config_file(self):
|
||
|
text = "# autogenerated by test_server.py at "+str(datetime.now())+"\n"
|
||
|
for k in sorted(self._config.keys()):
|
||
|
text += str(k) + "=" + str(self._config[k]) + "\n"
|
||
|
f = open(self._config_file_path,'w')
|
||
|
f.write(text)
|
||
|
f.close()
|
||
|
|
||
|
def get_config_file_path(self):
|
||
|
return self._config_file_path
|
||
|
|
||
|
def _get_config_property(self, key):
|
||
|
return self._config[key]
|
||
|
|
||
|
def _get_pid_file_path(self):
|
||
|
return os.path.join(self._run_dir_path, self.get_uuid() + ".pid")
|
||
|
|
||
|
def get_http_port(self):
|
||
|
return int(self._config["http_port"])
|
||
|
|
||
|
def get_rpc_port(self):
|
||
|
return int(self._config["listen.port"])
|
||
|
|
||
|
def get_uuid(self):
|
||
|
return self._config["uuid"]
|
||
|
|
||
|
def getServiceUrl(self):
|
||
|
url = "pbrpc://"
|
||
|
if (self._config['ssl.enabled'] == 'true'):
|
||
|
if (self._config['ssl.grid_ssl'] == 'true'):
|
||
|
url = "pbrpcg://"
|
||
|
else:
|
||
|
url = "pbrpcs://"
|
||
|
url += "localhost:" + str(self._config["listen.port"]) + "/"
|
||
|
return url
|
||
|
|
||
|
def is_running(self):
|
||
|
pid_file_path = self._get_pid_file_path()
|
||
|
if os.path.exists(pid_file_path):
|
||
|
pid = open(pid_file_path).read().strip()
|
||
|
|
||
|
try:
|
||
|
pid = int(pid)
|
||
|
except ValueError:
|
||
|
return False
|
||
|
|
||
|
#print "xtestenv: checking if", self.__class__.__name__, "server is running with pid", pid
|
||
|
|
||
|
try:
|
||
|
pid, exitvalue = os.waitpid(int(pid), os.WNOHANG)
|
||
|
if pid != 0 and exitvalue != 0:
|
||
|
return False
|
||
|
else:
|
||
|
return True
|
||
|
except OSError:
|
||
|
return False
|
||
|
else:
|
||
|
return False
|
||
|
|
||
|
def save_status_page(self, to_file_path):
|
||
|
http_port = self.get_http_port()
|
||
|
os.system("wget -O %(to_file_path)s http://localhost:%(http_port)u" % locals())
|
||
|
|
||
|
def start(self,
|
||
|
log_file_path=None):
|
||
|
|
||
|
if sys.platform == "win32" or not self.is_running():
|
||
|
try: os.mkdir(self._run_dir_path)
|
||
|
except: pass
|
||
|
pid_file_path = self._get_pid_file_path()
|
||
|
|
||
|
java_args = [os.path.join(os.environ["JAVA_HOME"], "bin", "java")]
|
||
|
|
||
|
# Enable assertions.
|
||
|
java_args.append("-ea")
|
||
|
|
||
|
# Construct the -cp classpath
|
||
|
XtreemFS_jar_file_path = os.path.abspath(os.path.join(self._xtreemfs_dir, "java", "servers", "dist", "XtreemFS.jar"))
|
||
|
if os.path.exists(XtreemFS_jar_file_path):
|
||
|
classpath = (
|
||
|
XtreemFS_jar_file_path,
|
||
|
os.path.abspath(os.path.join(self._xtreemfs_dir, "java", "lib", "BabuDB.jar")),
|
||
|
os.path.abspath(os.path.join(self._xtreemfs_dir, "java", "lib", "protobuf-java-2.5.0.jar")),
|
||
|
os.path.abspath(os.path.join(self._xtreemfs_dir, "java", "flease", "dist", "Flease.jar")),
|
||
|
os.path.abspath(os.path.join(self._xtreemfs_dir, "java", "foundation", "dist", "Foundation.jar")),
|
||
|
os.path.abspath(os.path.join(self._xtreemfs_dir, "java", "lib", "jdmkrt.jar")),
|
||
|
os.path.abspath(os.path.join(self._xtreemfs_dir, "java", "lib", "commons-codec-1.3.jar")),
|
||
|
)
|
||
|
else:
|
||
|
classpath = (
|
||
|
os.path.join("/usr/share/java", "XtreemFS.jar"),
|
||
|
os.path.join("/usr/share/java", "BabuDB.jar"),
|
||
|
os.path.join("/usr/share/java", "protobuf-java-2.5.0.jar"),
|
||
|
os.path.join("/usr/share/java", "Flease.jar"),
|
||
|
os.path.join("/usr/share/java", "Foundation.jar"),
|
||
|
os.path.join("/usr/share/java", "jdmkrt.jar"),
|
||
|
os.path.join("/usr/share/java", "commons-codec-1.3.jar"),
|
||
|
)
|
||
|
|
||
|
if sys.platform.startswith("win"):
|
||
|
classpath = ";".join(classpath)
|
||
|
else:
|
||
|
classpath = ":".join(classpath)
|
||
|
java_args.extend(("-cp", classpath))
|
||
|
|
||
|
# Name of the class to start
|
||
|
java_args.append("org.xtreemfs." + self.__class__.__name__.lower() + "." + self.__class__.__name__.upper())
|
||
|
|
||
|
# .config file
|
||
|
java_args.append(self.get_config_file_path())
|
||
|
|
||
|
# Don't .join java_args, since Popen wants a sequence when shell=False
|
||
|
|
||
|
if log_file_path is None:
|
||
|
stderr = sys.stderr
|
||
|
stdout = sys.stdout
|
||
|
else:
|
||
|
# Redirect stderr and stdout to a log file
|
||
|
stderr = stdout = open(log_file_path, "a")
|
||
|
|
||
|
#print "xctl: starting", self.__class__.__name__, "server with UUID", self.get_uuid(), "on port", self.get_rpc_port(), "with", " ".join(java_args)
|
||
|
|
||
|
p = subprocess.Popen(java_args, stdout=stdout, stderr=stderr) # No shell=True: we only want one process (java), not two (/bin/sh and java)
|
||
|
if p.returncode is not None:
|
||
|
raise RuntimeError(self.get_uuid() + " failed to start: " + str(p.returncode))
|
||
|
pidfile = open(pid_file_path, "w+")
|
||
|
pidfile.write(str(p.pid))
|
||
|
pidfile.close()
|
||
|
|
||
|
print "xtestenv: started", self.__class__.__name__, "server with UUID", self.get_uuid(), "on port", self.get_rpc_port(), "with pid", p.pid
|
||
|
|
||
|
sleep(1.0)
|
||
|
|
||
|
if not self.is_running():
|
||
|
raise RuntimeError, self.get_uuid() + " failed to start"
|
||
|
else:
|
||
|
print "xtestenv:", self.__class__.__name__, "server with UUID", self.get_uuid(), "is already running"
|
||
|
|
||
|
def stop(self):
|
||
|
pid_file_path = self._get_pid_file_path()
|
||
|
if os.path.exists(pid_file_path):
|
||
|
pid = int(open(pid_file_path).read().strip())
|
||
|
|
||
|
if sys.platform.startswith("win"):
|
||
|
subprocess.call("TASKKILL /PID %(pid)u /F /T" % locals())
|
||
|
killed = True
|
||
|
else:
|
||
|
killed = False
|
||
|
for signo in (signal.SIGTERM, signal.SIGKILL):
|
||
|
for try_i in xrange(self._start_stop_retries):
|
||
|
print "xtestenv: stopping", self.__class__.__name__, "server with pid", pid, "with signal", str(signo) + ", try", try_i
|
||
|
|
||
|
try: os.kill(pid, signo)
|
||
|
except: pass
|
||
|
|
||
|
sleep(0.5)
|
||
|
|
||
|
try:
|
||
|
if os.waitpid(pid, os.WNOHANG)[0] != 0:
|
||
|
killed = True
|
||
|
break
|
||
|
except OSError:
|
||
|
killed = True
|
||
|
break
|
||
|
except:
|
||
|
if DEBUG_ME:
|
||
|
traceback.print_exc()
|
||
|
|
||
|
if killed:
|
||
|
break
|
||
|
|
||
|
if killed:
|
||
|
os.unlink(pid_file_path)
|
||
|
|
||
|
else:
|
||
|
print "xtestenv: no pid file for", self.__class__.__name__, "server"
|
||
|
|
||
|
|
||
|
class DIR(Server):
|
||
|
def configure(self):
|
||
|
try: os.mkdir(self._data_dir)
|
||
|
except: pass
|
||
|
|
||
|
self._config['babudb.debug.level'] = self._config['debug.level']
|
||
|
self._config['babudb.logDir'] = self._data_dir
|
||
|
self._config['babudb.baseDir'] = self._data_dir
|
||
|
self._config['babudb.sync'] = 'FSYNC'
|
||
|
self._config['babudb.worker.maxQueueLength'] = '250'
|
||
|
self._config['babudb.worker.numThreads'] = '0'
|
||
|
self._config['babudb.maxLogfileSize'] = '16777216'
|
||
|
self._config['babudb.checkInterval'] = '300'
|
||
|
self._config['babudb.pseudoSyncWait'] = '200'
|
||
|
self._config['database.dir'] = self._data_dir
|
||
|
self._config['database.log'] = self._data_dir
|
||
|
self._config['authentication_provider'] = 'org.xtreemfs.common.auth.NullAuthProvider'
|
||
|
|
||
|
|
||
|
class MRC(Server):
|
||
|
def configure(self,
|
||
|
dir_host,
|
||
|
dir_port):
|
||
|
try: os.mkdir(self._data_dir)
|
||
|
except: pass
|
||
|
|
||
|
self._config['dir_service.host'] = dir_host
|
||
|
self._config['dir_service.port'] = dir_port
|
||
|
|
||
|
self._config['osd_check_interval'] = 300
|
||
|
self._config['no_atime'] = 'true'
|
||
|
self._config['no_fsync'] = 'true'
|
||
|
self._config['local_clock_renewal'] = 0
|
||
|
self._config['remote_time_sync'] = 60000
|
||
|
self._config['capability_secret'] = 'testsecret'
|
||
|
self._config['database.checkpoint.interval'] = 1800000
|
||
|
self._config['database.checkpoint.idle_interval'] = 1000
|
||
|
self._config['database.checkpoint.logfile_size'] = 16384
|
||
|
|
||
|
self._config['babudb.debug.level'] = self._config['debug.level']
|
||
|
self._config['babudb.logDir'] = self._data_dir
|
||
|
self._config['babudb.baseDir'] = self._data_dir
|
||
|
self._config['babudb.sync'] = 'ASYNC'
|
||
|
self._config['babudb.worker.maxQueueLength'] = '250'
|
||
|
self._config['babudb.worker.numThreads'] = '0'
|
||
|
self._config['babudb.maxLogfileSize'] = '16777216'
|
||
|
self._config['babudb.checkInterval'] = '300'
|
||
|
self._config['babudb.pseudoSyncWait'] = '0'
|
||
|
self._config['database.dir'] = self._data_dir
|
||
|
self._config['database.log'] = self._data_dir
|
||
|
self._config['authentication_provider'] = 'org.xtreemfs.common.auth.NullAuthProvider'
|
||
|
|
||
|
|
||
|
class OSD(Server):
|
||
|
def configure(self,
|
||
|
dir_host,
|
||
|
dir_port):
|
||
|
try: os.mkdir(self._data_dir)
|
||
|
except: pass
|
||
|
|
||
|
self._config['dir_service.host'] = dir_host
|
||
|
self._config['dir_service.port'] = dir_port
|
||
|
|
||
|
self._config['local_clock_renewal'] = 0
|
||
|
self._config['remote_time_sync'] = 60000
|
||
|
self._config['capability_secret'] = 'testsecret'
|
||
|
self._config['report_free_space'] = 'true'
|
||
|
self._config['checksums.enabled'] = 'false'
|
||
|
|
||
|
self._config['object_dir'] = self._data_dir
|
||
|
|
||
|
# Some tests overload the test system, increase timeouts.
|
||
|
self._config['flease.lease_timeout_ms'] = 60000
|
||
|
self._config['flease.message_to_ms'] = 2000
|