Commit 88ebcd9b authored by Guido van Rossum's avatar Guido van Rossum

Add ZConfig support. The -s option goes away; if you want multiple

storages, it's much easier to specify those in a config file.

XXX This still needs a unit test suite.
parent 80e68aea
...@@ -14,17 +14,16 @@ ...@@ -14,17 +14,16 @@
############################################################################## ##############################################################################
"""Start the ZEO storage server. """Start the ZEO storage server.
Usage: %s [-a ADDRESS] [-f FILENAME] [-s STORAGE] Usage: %s [-a ADDRESS] [-C URL] [-f FILENAME] [-h]
Options: Options:
-a/--address ADDRESS -- server address of the form PORT, HOST:PORT, or PATH -a/--address ADDRESS -- server address of the form PORT, HOST:PORT, or PATH
(a PATH must contain at least one "/") (a PATH must contain at least one "/")
-C/--configuration URL -- configuration file or URL
-f/--filename FILENAME -- filename for FileStorage -f/--filename FILENAME -- filename for FileStorage
-s/--storage STORAGE -- storage specification of the form -h/--help -- print this usage message and exit
NAME=MODULE[:ATTRIBUTE]
(multiple -s options are supported)
-a is required; either -f must be used or -s must be used. Unless -C is specified, -a and -f are required.
""" """
# The code here is designed to be reused by other, similar servers. # The code here is designed to be reused by other, similar servers.
...@@ -41,6 +40,10 @@ import socket ...@@ -41,6 +40,10 @@ import socket
import zLOG import zLOG
import ZConfig
import ZConfig.Common
import ZConfig.Storage
class Options: class Options:
...@@ -53,6 +56,9 @@ class Options: ...@@ -53,6 +56,9 @@ class Options:
errors related to the command line. errors related to the command line.
""" """
configuration = None
rootconf = None
args = [] args = []
def __init__(self, args=None, progname=None, doc=None): def __init__(self, args=None, progname=None, doc=None):
...@@ -90,8 +96,8 @@ class Options: ...@@ -90,8 +96,8 @@ class Options:
self.check_options() self.check_options()
# Default set of options. Subclasses should override. # Default set of options. Subclasses should override.
_short_options = "h" _short_options = "C:h"
_long_options = ["--help"] _long_options = ["--configuration=", "--help"]
def handle_option(self, opt, arg): def handle_option(self, opt, arg):
"""Handle one option. Subclasses should override. """Handle one option. Subclasses should override.
...@@ -100,15 +106,25 @@ class Options: ...@@ -100,15 +106,25 @@ class Options:
When -h is detected, print the module docstring to stdout and exit(0). When -h is detected, print the module docstring to stdout and exit(0).
""" """
if opt == "-h" or opt == "--help": if opt in ("-C", "--configuration"):
self.set_configuration(arg)
if opt in ("-h", "--help"):
self.help() self.help()
def set_configuration(self, arg):
self.configuration = arg
def check_options(self): def check_options(self):
"""Check options. Subclasses may override. """Check options. Subclasses may override.
This can be used to ensure certain options are set, etc. This can be used to ensure certain options are set, etc.
""" """
pass self.load_configuration()
def load_configuration(self):
if self.rootconf or not self.configuration:
return
self.rootconf = ZConfig.load(self.configuration)
def help(self): def help(self):
"""Print a long help message (self.doc) to stdout and exit(0). """Print a long help message (self.doc) to stdout and exit(0).
...@@ -130,17 +146,20 @@ class Options: ...@@ -130,17 +146,20 @@ class Options:
class ZEOOptions(Options): class ZEOOptions(Options):
family = None hostname = None # A subclass may set this
address = None hostconf = None # <Host> section
storages = None zeoconf = None # <ZEO> section
filename = None
_short_options = "a:f:hs:" family = None # set by -a; AF_UNIX or AF_INET
address = None # set by -a; string or (host, port)
storages = None # set by -f
_short_options = "a:C:f:h"
_long_options = [ _long_options = [
"--address=", "--address=",
"--configuration=",
"--filename=", "--filename=",
"--help", "--help",
"--storage=",
] ]
def handle_option(self, opt, arg): def handle_option(self, opt, arg):
...@@ -162,32 +181,74 @@ class ZEOOptions(Options): ...@@ -162,32 +181,74 @@ class ZEOOptions(Options):
self.usage("invalid port number: %r" % port) self.usage("invalid port number: %r" % port)
self.address = (host, port) self.address = (host, port)
elif opt in ("-f", "--filename"): elif opt in ("-f", "--filename"):
self.filename = arg from ZODB.FileStorage import FileStorage
elif opt in ("-s", "--storage"): self.storages = {"1": (FileStorage, {"file_name": arg})}
if self.storages is None:
self.storages = {}
if not "=" in arg:
self.usage("use -s/--storage storagename=module[:attribute]")
name, rest = arg.split("=", 1)
if ":" in rest:
module, attr = rest.split(":", 1)
else:
module = rest
attr = name
self.storages[name] = module, attr
else: else:
# Pass it to the base class, for --help/-h # Pass it to the base class, for --help/-h
Options.handle_option(self, opt, arg) Options.handle_option(self, opt, arg)
def check_options(self): def check_options(self):
if self.storages and self.filename: Options.check_options(self) # Calls load_configuration()
self.usage("can't use both -s/--storage and -f/--filename") if not self.storages:
if not self.storages and not self.filename: self.usage("no storages specified; use -f or -C")
self.usage("need one of -s/--storage or -f/--filename")
if self.family is None: if self.family is None:
self.usage("need -a/--address [host:]port or unix path") self.usage("no server address specified; use -a or -C")
if self.args: if self.args:
self.usage("no positional arguments supported") self.usage("positional arguments are not supported")
def load_configuration(self):
Options.load_configuration(self) # Sets self.rootconf
if not self.rootconf:
return
try:
self.hostconf = self.rootconf.getSection("Host")
except ZConfig.Common.ConfigurationConflictingSectionError:
if not self.hostname:
self.hostname = socket.getfqdn()
self.hostconf = self.rootconf.getSection("Host", self.hostname)
if self.hostconf is None:
# If no <Host> section exists, fall back to the root
self.hostconf = self.rootconf
self.zeoconf = self.hostconf.getSection("ZEO")
if self.zeoconf is None:
# If no <ZEO> section exists, fall back to the host (or root)
self.zeoconf = self.hostconf
# Now extract options from various configuration sections
self.load_zeoconf()
self.load_storages()
def load_zeoconf(self):
# Get some option defaults from the configuration
if self.family:
# -a option overrides
return
port = self.zeoconf.getint("server-port")
path = self.zeoconf.get("path")
if port and path:
self.usage(
"Configuration contains conflicting ZEO information:\n"
"Exactly one of 'path' and 'server-port' may be given.")
if port:
host = self.hostconf.get("hostname", "")
self.family = socket.AF_INET
self.address = (host, port)
elif path:
self.family = socket.AF_UNIX
self.address = path
def load_storages(self):
# Get the storage specifications
if self.storages:
# -f option overrides
return
storagesections = self.zeoconf.getChildSections("Storage")
self.storages = {}
for section in storagesections:
name = section.name
if not name:
name = str(1 + len(self.storages))
self.storages[name] = ZConfig.Storage.getStorageInfo(section)
class ZEOServer: class ZEOServer:
...@@ -234,37 +295,11 @@ class ZEOServer: ...@@ -234,37 +295,11 @@ class ZEOServer:
pass pass
def open_storages(self): def open_storages(self):
if self.options.storages:
self.load_storages(self.options.storages)
else:
from ZODB.FileStorage import FileStorage
info("opening storage '1': %r" % self.options.filename)
storage = FileStorage(self.options.filename)
self.storages = {"1": storage}
def load_storages(self, storages):
self.storages = {} self.storages = {}
for name, (module, attr) in storages.items(): for name, (cls, args) in self.options.storages.items():
info("opening storage %r (%r:%r)" % (name, module, attr)) info("open storage %r: %s.%s(**%r)" %
self.storages[name] = self.get_storage(module, attr) (name, cls.__module__, cls.__name__, args))
self.storages[name] = cls(**args)
_storage_cache = {}
def get_storage(self, module, attr):
# XXX This may fail with ImportError or AttributeError
path = sys.path
dir, module = os.path.split(module)
if module.lower().endswith('.py'):
module = module[:-3]
im = self._storage_cache.get((dir, module))
if im is None:
if dir:
path = [dir] + path
import imp
im = imp.find_module(module, path)
im = imp.load_module(module, *im)
self._storage_cache[(dir, module)] = im
return getattr(im, attr)
def setup_signals(self): def setup_signals(self):
"""Set up signal handlers. """Set up signal handlers.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment