r873 - zope-common/branches/etch
lunar at alioth.debian.org
lunar at alioth.debian.org
Mon May 14 11:51:32 UTC 2007
Author: lunar
Date: 2007-05-14 11:51:31 +0000 (Mon, 14 May 2007)
New Revision: 873
Added:
zope-common/branches/etch/debian/
zope-common/branches/etch/dzhandle
zope-common/branches/etch/dzhandle.sgml
Log:
zope-common=0.5.31 is the version currently in etch.
Copied: zope-common/branches/etch/debian (from rev 871, zope-common/tags/0.5.31/debian)
Copied: zope-common/branches/etch/dzhandle (from rev 871, zope-common/tags/0.5.31/dzhandle)
===================================================================
--- zope-common/branches/etch/dzhandle (rev 0)
+++ zope-common/branches/etch/dzhandle 2007-05-14 11:51:31 UTC (rev 873)
@@ -0,0 +1,2394 @@
+#! /usr/bin/python
+
+# Copyright (C) 2005-2006 Matthias Klose <doko at ubuntu.com>
+# Fabio Tranchitella <kobold at debian.org>
+#
+# 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 os
+import sys
+import re
+import glob
+import fnmatch
+import shutil
+import pwd, grp
+import logging
+import subprocess
+
+from optparse import OptionParser
+
+program = os.path.basename(sys.argv[0])
+
+# Debian/Ubuntu zope packages:
+# ----------------------------
+
+zope_packages = (
+ { 'name': 'zope2.6',
+ 'version': '2.6',
+ 'prefix': '/usr/lib/zope',
+ 'instance': '/var/lib/zope/instance',
+ 'zeoinstance': '/var/lib/zope/zeo',
+ 'pyver': '2.2'
+ },
+ { 'name': 'zope2.7',
+ 'version': '2.7',
+ 'prefix': '/usr/lib/zope2.7',
+ 'instance': '/var/lib/zope2.7/instance',
+ 'zeoinstance': '/var/lib/zope2.7/zeo',
+ 'pyver': '2.3'
+ },
+ { 'name': 'zope2.8',
+ 'version': '2.8',
+ 'prefix': '/usr/lib/zope2.8',
+ 'instance': '/var/lib/zope2.8/instance',
+ 'zeoinstance': '/var/lib/zope2.8/zeo',
+ 'pyver': '2.3'
+ },
+ { 'name': 'zope2.9',
+ 'version': '2.9',
+ 'prefix': '/usr/lib/zope2.9',
+ 'instance': '/var/lib/zope2.9/instance',
+ 'zeoinstance': '/var/lib/zope2.9/zeo',
+ 'pyver': '2.4'
+ },
+ { 'name': 'zope2.10',
+ 'version': '2.10',
+ 'prefix': '/usr/lib/zope2.10',
+ 'instance': '/var/lib/zope2.10/instance',
+ 'zeoinstance': '/var/lib/zope2.10/zeo',
+ 'pyver': '2.4'
+ },
+ { 'name': 'zope3',
+ 'version': '3',
+ 'prefix': '/usr/lib/zope3',
+ 'instance': '/var/lib/zope3/instance',
+ 'zeoinstance': '/var/lib/zope3/zeo',
+ 'pyver': '2.4'
+ }
+ )
+
+# installation types for addons:
+# ------------------------------
+# ADDON_MASTER addon from Debian package in /usr/lib/zope
+# ADDON_LINKED addon in instance, addon directory symlinked to ADDON_MASTER
+# ADDON_TREELINKED addon in instance, all files in addon symlinked to ADDON_MASTER
+# ADDON_COPIED addon in instance, copied from ADDON_MASTER
+# ADDON_MANUAL addon in instance without .dzfile, manually copied?
+
+NO_ADDON, ADDON_MASTER, ADDON_LINKED, ADDON_TREELINKED, ADDON_COPIED, ADDON_MANUAL = range(6)
+addon_options = ['-', 'master', 'linked', 'tree-linked', 'copied', 'manually installed']
+addon_techniques = addon_options[ADDON_LINKED:ADDON_COPIED+1]
+addon_modes = ['all', 'manual']
+
+def addon_technique_name(code):
+ return addon_options[code]
+
+def addon_technique_code(name):
+ return addon_options.index(name)
+
+known_actions = {}
+def register_action(action_class):
+ known_actions[action_class.name] = action_class
+
+personal_conf = {}
+
+class DZError(Exception):
+ pass
+
+# --------------------------------------------------------------------------- #
+
+def is_root():
+ return os.getuid() == 0
+
+def strlist(sequence):
+ return ', '.join(["`%s'" % s for s in sequence])
+
+def available_zope_versions():
+ return [z['version'] for z in zope_packages if os.path.isdir(z['prefix'])]
+
+def filter_zope_version(version):
+ return [z for z in zope_packages if z['version'] == version][0]
+
+def read_config_file(fn, required=None):
+ attrs = {}
+ for line in file(fn):
+ line = line.strip()
+ if not line or line.startswith('#'):
+ continue
+ try:
+ key, value = [field.strip() for field in line.split(':', 1)]
+ if value.count('#') > 0: value = value.split('#')[0].strip()
+ except:
+ raise DZError, "error reading %s\n\t%s" % (fn, line)
+ if attrs.has_key(key):
+ raise DZError, "duplicate field `%s' in %s" % (key, fn)
+ if value == '':
+ raise DZError, "missing value for field `%s' in %s" % (key, fn)
+ attrs[key] = value
+ if required:
+ for attr in required:
+ if not attrs.has_key(attr):
+ raise DZError, "missing field `%s' in %s" % (attr, fn)
+ return attrs
+
+def write_config_file(fn, attrs, uid=None, gid=None):
+ fd = file(fn, 'w')
+ for key, value in attrs.items():
+ if value != None:
+ fd.write('%s: %s\n' % (key, value))
+ fd.close()
+
+# --------------------------------------------------------------------------- #
+
+class InstanceListFile:
+ def __init__(self, mode, addon_name, oklist=True):
+ if oklist:
+ self.fn = '/var/lib/zope/dzhandle/%s__%s' % (mode, addon_name)
+ else:
+ self.fn = '/var/lib/zope/dzhandle/%s_failed__%s' % (mode, addon_name)
+ self.read()
+
+ def read(self):
+ """read file with <instance name> <instance version> lines,
+ return list of fields, empty list, if file does not exist"""
+ self.lines = []
+ try:
+ fd = file(self.fn)
+ for line in fd.readlines():
+ fields = line.split()
+ if not (fields[:2]) in self.lines:
+ self.lines.append(fields[:2])
+ self.exists = True
+ fd.close()
+ except IOError:
+ self.exists = False
+
+ def write(self):
+ if self.lines:
+ fd = file(self.fn, 'w')
+ for name, version in self.lines:
+ fd.write("%s %s\n" % (name, version))
+ fd.close()
+ else:
+ os.unlink(self.fn)
+
+ def remove(self, instance):
+ """remove instance from list and write file,
+ unlink file, if list is empty"""
+ try:
+ self.lines.remove([instance.name, instance.version])
+ except ValueError:
+ return
+ self.write()
+
+ def append(self, instance):
+ """append instance information to instance list"""
+ info = [instance.name, instance.version]
+ if info in self.lines:
+ return
+ self.lines.append(info)
+ self.write()
+
+ def get_lines(self):
+ for line in self.lines:
+ yield line
+
+class Addon:
+ def __init__(self, path, dzfile_required=True):
+ self.type = None
+ if not os.path.isdir(path):
+ raise DZError, "%s directory doesn't exist: `%s'" % (self.kind, path)
+ self.path = path
+ if not os.path.isfile(os.path.join(path, self.dzname)):
+ if dzfile_required:
+ raise DZError, "`%s' file not found`%s'" \
+ % (self.dzname, os.path.join(self.path, self.dzname))
+ self.type = ADDON_MANUAL
+ self.name = os.path.basename(path)
+ self.package = None
+ self.version = None
+ self.depends = None
+ self.recommends = None
+ self.suggests = None
+ self.is_global = False
+ self.zopeversions = " ".join([z['version'] for z in zope_packages])
+ else:
+ self.read_dzfile()
+ if self.type == None:
+ if path.startswith('/usr/share/zope') or \
+ path.startswith('/usr/lib/zope'):
+ self.type = ADDON_MASTER
+ elif os.path.islink(path):
+ self.type = ADDON_LINKED
+ elif os.path.islink(os.path.join(path, self.dzname)):
+ self.type = ADDON_TREELINKED
+ else:
+ self.type = ADDON_COPIED
+
+ def read_dzfile(self):
+ fn = os.path.join(self.path, self.dzname)
+ attrs = read_config_file(fn, ('Name', 'Package', 'Version'))
+ self.name = attrs['Name']
+ self.package = attrs['Package']
+ self.version = attrs['Version']
+ self.directory = attrs.get('Directory', self.name)
+ if not os.path.basename(self.path) in (self.name, self.directory):
+ raise DZError, "Product or Directory field doesn't match path in `%s'" % fn
+ self.depends = attrs.get('Depends', None)
+ self.recommends = attrs.get('Recommends', None)
+ self.suggests = attrs.get('Suggests', None)
+ self.zopeversions = attrs.get('ZopeVersions', \
+ " ".join([z['version'] for z in zope_packages]))
+ self.is_global = (attrs.get('Global', None) == 'yes')
+
+ def write_dzfile(self, fn=None):
+ fn = fn or os.path.join(self.path, self.dzname)
+ fd = file(fn, 'w')
+ fd.write('Package: %s\n' % self.package)
+ fd.write('Name: %s\n' % self.name)
+ fd.write('Version: %s\n' % self.version)
+ if self.name != self.directory:
+ fd.write('Directory: %s\n' % self.directory)
+ if self.depends:
+ fd.write('Depends: %s\n' % self.depends)
+ if self.recommends:
+ fd.write('Recommends: %s\n' % self.recommends)
+ if self.suggests:
+ fd.write('Suggests: %s\n' % self.suggests)
+ if self.zopeversions:
+ fd.write('ZopeVersions: %s\n' % self.zopeversions)
+ fd.close()
+
+ def available_in_instance(self, instance):
+ """checks if the addon is available in the instance
+ """
+
+ if self.is_global:
+ ipath = os.path.join(instance.home, self.subdir, self.name + '.installed')
+ return os.path.exists(ipath)
+
+ ipath = os.path.join(instance.home, self.subdir, self.name)
+ return os.path.isdir(ipath)
+
+ def installed_in_instance(self, instance):
+ """If the addon is available in the instance, return it, else return None.
+ """
+
+ if self.is_global:
+ ipath = os.path.join(instance.home, self.subdir, self.name + '.installed')
+ return os.path.exists(ipath) and self or None
+
+ ipath = os.path.join(instance.home, self.subdir, self.name)
+ if not os.path.isdir(ipath):
+ return None
+ iaddon = self.__class__(ipath,
+ dzfile_required=\
+ os.path.isfile(os.path.join(ipath, self.dzname)))
+ return iaddon
+
+ def installed_by_in_instances(self, instances, exclude=[]):
+ """Return a list of addons, which are installed by this master addon in
+ instances. Filter out all instances with a addon type found in exclude.
+ """
+ assert self.type == ADDON_MASTER
+
+ iaddons = []
+ for instance in instances:
+ iaddon = self.installed_in_instance(instance)
+ if iaddon and not iaddon.type in exclude and iaddon.installed_by(self):
+ iaddons.append(iaddon)
+ return iaddons
+
+ def find_master(self):
+ """locate the addon, that this addon was installed from"""
+
+ if self.type == ADDON_MANUAL:
+ return None
+ master = None
+ for prefix in ('/usr/share/zope', '/usr/lib/zope'):
+ path = os.path.join(prefix, self.subdir, self.directory)
+ try:
+ master = self.addonClass(path)
+ except DZError, msg:
+ pass
+ else:
+ break
+ if not master:
+ raise DZError, msg
+ return master
+
+ def installed_by(self, master):
+ """checks if the installed addon was installed by the `master' addon
+ """
+ assert self.type != ADDON_MASTER and master.type == ADDON_MASTER or self.is_global
+
+ if self.is_global:
+ return True
+ elif self.name != master.name or self.package != master.package:
+ # other addon, or same addon installed by other package
+ return False
+ elif self.type == ADDON_COPIED:
+ # copied from master to instance, version may mismatch
+ return True
+
+ # consistency checks ...
+ realpath = os.path.realpath(self.path)
+ master_realpath = os.path.realpath(master.path)
+ if self.type == ADDON_LINKED and realpath != master_realpath:
+ print "CONSISTENCY CHECK FAILED: %s != %s" % (realpath, master_realpath)
+ return False
+ realpath = os.path.realpath(os.path.join(self.path, self.dzname))
+ master_realpath = os.path.realpath(os.path.join(master.path, self.dzname))
+ if self.type == ADDON_TREELINKED and realpath != master_realpath:
+ print "CONSISTENCY CHECK FAILED: %s != %s" % (realpath, master_realpath)
+ return False
+
+ return True
+
+ def printit(self, stream=sys.stdout):
+ print >>stream, self.kind, self.name, self.directory, \
+ self.package, self.version, self.path, self.depends
+
+class ProductAttributes:
+ kind = 'product'
+ subdir = 'Products'
+ dzname = '.dzproduct'
+
+class ExtensionAttributes:
+ kind = 'extension'
+ subdir = 'Extensions'
+ dzname = '.dzextension'
+
+class Product(ProductAttributes, Addon):
+ pass
+
+class Extension(ExtensionAttributes, Addon):
+ pass
+
+ProductAttributes.addonClass = Product
+ExtensionAttributes.addonClass = Extension
+
+# --------------------------------------------------------------------------- #
+
+class DZAction:
+ _option_parser = None
+ name = None
+ help = ""
+ def __init__(self):
+ self.errors_occured = 0
+ parser = self.get_option_parser()
+ parser.set_usage(
+ 'usage: %s [<option> ...] %s [<option> ...]' % (program, self.name))
+
+ def get_option_parser(self):
+ if not self._option_parser:
+ p = OptionParser()
+ self._option_parser = p
+ return self._option_parser
+
+ def info(self, msg, stream=sys.stderr):
+ logging.info('%s %s: %s', program, self.name, msg)
+
+ def warn(self, msg, stream=sys.stderr):
+ logging.warn('%s %s: %s', program, self.name, msg)
+
+ def error(self, msg, stream=sys.stderr, go_on=False):
+ logging.error('%s %s: %s', program, self.name, msg)
+ self.errors_occured += 1
+ if not go_on:
+ sys.exit(1)
+
+ def parse_args(self, arguments):
+ self.options, self.args = self._option_parser.parse_args(arguments)
+ return self.options, self.args
+
+ def check_args(self, global_options):
+ return self.errors_occured
+
+ def run(self, global_opts):
+ pass
+
+# --------------------------------------------------------------------------- #
+
+class DZPreinst(DZAction):
+ kind = None
+ def get_option_parser(self):
+ if not self._option_parser:
+ p = OptionParser()
+ p.add_option('-m', '--mode',
+ action='store', dest='mode')
+ self._option_parser = p
+ return self._option_parser
+
+ def check_args(self, global_options):
+ if len(self.args) != 3:
+ self.error('--mode=<script args> <package> <directory> <%s name>' % self.kind)
+ if self.options.mode == None:
+ self.error("missing `--mode=<script args>'")
+ self.mode_args = self.options.mode.split()
+ if not self.mode_args[0] in ('install', 'upgrade'):
+ self.error("unknown mode `%s'" % self.options.mode)
+ self.mode = self.mode_args[0]
+ del self.mode_args[0]
+ package, directory, addon_name = self.args
+
+ ilist = InstanceListFile(self.mode, addon_name)
+ if ilist.exists:
+ self.info("file `%s' exists" % ilist.fn)
+ ilist = InstanceListFile(self.mode, addon_name, oklist=False)
+ if ilist.exists:
+ self.info("file `%s' exists" % ilist.fn)
+
+ # nothing to check for upgrades
+ if self.mode == 'upgrade':
+ return self.errors_occured
+
+ instances = locate_instances(versions=global_options.zversion)
+ for instance in instances:
+ addon_path = os.path.join(instance.home, self.subdir, addon_name)
+
+ if instance.is_addon_excluded(addon_name):
+ continue
+ if not os.path.exists(addon_path):
+ # not installed in instance
+ continue
+ try:
+ iaddon = self.addonClass(directory, dzfile_required=False)
+ except DZError, msg:
+ # some error reading the dzfile ...
+ # maybe the product hasn't been unpacked yet, let's skip it
+ continue
+ if iaddon.type == ADDON_MANUAL:
+ if instance.addon_installation_required():
+ self.error("keep manually installed %s `%s' in instance "
+ "`%s' (%s), ignoring %s `%s' from package `%s'." %
+ (self.kind, iaddon.name, instance.name, instance.version,
+ self.kind, addon_name, package),
+ go_on=True)
+ else:
+ self.info("keep manually installed %s `%s' in instance "
+ "`%s' (%s), ignoring %s `%s' from package `%s'." %
+ (self.kind, iaddon.name, instance.name, instance.version,
+ self.kind, addon_name, package))
+ continue
+ if (iaddon.package, iaddon.name, iaddon.directory) \
+ != (package, addon_name, os.path.basename(directory)):
+ # not installed from this product
+ self.info("found %s `%s' installed from package `%s' in instance "
+ "`%s' (%s) while installing package `%s'." %
+ (self.kind, iaddon.name, iaddon.package,
+ instance.name, instance.version, package))
+ continue
+ if iaddon.type == ADDON_COPIED:
+ if instance.addon_installation_required():
+ self.error("keep copied %s `%s' in instance "
+ "`%s' (%s), ignoring %s `%s' from package `%s'." %
+ (self.kind, iaddon.name, instance.name, instance.version,
+ self.kind, addon_name, package),
+ go_on=True)
+ self.warn("keep copied %s `%s' in instance "
+ "`%s' (%s), ignoring %s `%s' from package `%s'." %
+ (self.kind, iaddon.name, instance.name, instance.version,
+ self.kind, addon_name, package))
+ continue
+ assert iaddon.type in (ADDON_LINKED, ADDON_TREELINKED)
+
+ return self.errors_occured
+
+ def run(self, global_options):
+ pass
+
+class DZPreinstProduct(ProductAttributes, DZPreinst):
+ name = 'preinst-%s' % ProductAttributes.kind
+ help = 'handle preinst of a packaged %s' % ProductAttributes.kind
+
+class DZPreinstExtension(ExtensionAttributes, DZPreinst):
+ name = 'preinst-%s' % ExtensionAttributes.kind
+ help = 'handle preinst of a packaged %s' % ExtensionAttributes.kind
+
+register_action(DZPreinstProduct)
+register_action(DZPreinstExtension)
+
+
+class DZPostinst(DZAction):
+ kind = None
+ def get_option_parser(self):
+ if not self._option_parser:
+ p = OptionParser()
+ p.add_option('-m', '--mode',
+ action='store', dest='mode')
+ self._option_parser = p
+ return self._option_parser
+
+ def check_args(self, global_options):
+ if len(self.args) != 1:
+ self.error('--mode=<script args> <directory>')
+ if self.options.mode == None:
+ self.error("missing `--mode=<script arguments>'")
+ self.mode_args = self.options.mode.split()
+ if not self.mode_args[0] in ('configure', 'abort-upgrade', 'abort-remove'):
+ self.error("unknown mode `%s'" % self.options.mode)
+ self.mode = self.mode_args[0]
+ del self.mode_args[0]
+
+ directory = self.args[0]
+ try:
+ self.addon = addon = self.addonClass(directory)
+ except DZError, msg:
+ self.error(msg)
+
+ all_instances = locate_instances(versions=global_options.zversion)
+
+ if self.mode == 'configure':
+ # instances which are beeing upgraded go first
+ self.upgrade_ilist = InstanceListFile('upgrade', addon.directory)
+ self.toupgrade = []
+ for i_name, i_version in self.upgrade_ilist.get_lines():
+ match = [i for i in all_instances
+ if i.name == i_name and i.version == i_version]
+ if len(match) > 0:
+ self.toupgrade.append(match[0])
+
+ self.toinstall = self.toupgrade[:]
+
+ # new installations, same checks as in 'preinst install'
+ for instance in all_instances:
+ addon_path = os.path.join(instance.home, self.subdir, addon.name)
+
+ if instance in self.toupgrade:
+ continue
+ if instance.is_addon_excluded(addon.name):
+ continue
+ if not instance.addon_installation_required():
+ continue
+ if not os.path.exists(addon_path):
+ # not installed in instance
+ if instance.version in self.addon.zopeversions:
+ self.toinstall.append(instance)
+ continue
+ try:
+ iaddon = self.addonClass(addon_path, dzfile_required=False)
+ except DZError, msg:
+ # some error reading the dzfile ...
+ self.error(msg, go_on=True)
+ continue
+ if iaddon.type == ADDON_MANUAL:
+ if instance.addon_installation_required():
+ self.error("keep manually installed %s `%s' in instance "
+ "`%s' (%s), ignoring %s `%s' from package `%s'." %
+ (self.kind, iaddon.name, instance.name, instance.version,
+ self.kind, addon.name, addon.package),
+ go_on=True)
+ else:
+ self.info("keep manually installed %s `%s' in instance "
+ "`%s' (%s), ignoring %s `%s' from package `%s'." %
+ (self.kind, iaddon.name, instance.name, instance.version,
+ self.kind, addon.name, addon.package))
+ continue
+ if (iaddon.package, iaddon.name, iaddon.directory) \
+ != (addon.package, addon.name, os.path.basename(directory)):
+ # not installed from this product
+ self.info("found %s `%s' installed from package `%s' in instance "
+ "`%s' (%s) while installing package `%s'." %
+ (self.kind, iaddon.name, iaddon.package,
+ instance.name, instance.version, addon.package))
+ continue
+ if iaddon.type == ADDON_COPIED:
+ if instance.addon_installation_required():
+ self.error("keep copied %s `%s' in instance "
+ "`%s' (%s), ignoring %s `%s' from package `%s'." %
+ (self.kind, iaddon.name, instance.name, instance.version,
+ self.kind, addon.name, addon.package),
+ go_on=True)
+ self.warn("keep copied %s `%s' in instance "
+ "`%s' (%s), ignoring %s `%s' from package `%s'." %
+ (self.kind, iaddon.name, instance.name, instance.version,
+ self.kind, addon.name, addon.package))
+ continue
+ assert iaddon.type in (ADDON_LINKED, ADDON_TREELINKED)
+ self.toinstall.append(instance)
+
+ elif self.mode == 'abort-upgrade':
+ self.info("%s nyi" % self.mode)
+ elif self.mode == 'abort-remove':
+ self.info("%s nyi" % self.mode)
+
+ return self.errors_occured
+
+ def run(self, global_options):
+ if self.mode == 'configure':
+ for instance in self.toinstall:
+ if instance in self.toupgrade:
+ self.toupgrade.remove(instance)
+ try:
+ instance.add_addon(self.addon, global_options)
+ except DZError, msg:
+ failed_ilist = InstanceListFile('upgrade', self.addon.directory, oklist=False)
+ failed_ilist.append(instance)
+ self.error(msg)
+ else:
+ self.upgrade_ilist.remove(instance)
+ else:
+ try:
+ instance.add_addon(self.addon, global_options)
+ except DZError, msg:
+ self.error(msg)
+ instance.handle_restart_policy()
+
+ elif self.mode == 'abort-upgrade':
+ self.info("%s nyi" % self.mode)
+ elif self.mode == 'abort-remove':
+ self.info("%s nyi" % self.mode)
+
+class DZPostinstProduct(ProductAttributes, DZPostinst):
+ name = 'postinst-%s' % ProductAttributes.kind
+ help = 'handle postinst of a packaged %s' % ProductAttributes.kind
+
+class DZPostinstExtension(ExtensionAttributes, DZPostinst):
+ name = 'postinst-%s' % ExtensionAttributes.kind
+ help = 'handle postinst of a packaged %s' % ExtensionAttributes.kind
+
+register_action(DZPostinstProduct)
+register_action(DZPostinstExtension)
+
+
+class DZPrerm(DZAction):
+ kind = None
+ def get_option_parser(self):
+ if not self._option_parser:
+ p = OptionParser()
+ p.add_option('-m', '--mode',
+ action='store', dest='mode')
+ self._option_parser = p
+ return self._option_parser
+
+ def check_args(self, global_options):
+ if len(self.args) != 1:
+ self.error('--mode=<script args> <directory>')
+ if self.options.mode == None:
+ self.error("missing `--mode=<script arguments>'")
+ self.mode_args = self.options.mode.split()
+ if not self.mode_args[0] in ('upgrade', 'remove'):
+ self.error("unknown mode `%s'" % self.options.mode)
+ self.mode = self.mode_args[0]
+ del self.mode_args[0]
+
+ directory = self.args[0]
+ try:
+ self.addon = self.addonClass(directory)
+ except DZError, msg:
+ self.error(msg)
+
+ instances = locate_instances(versions=global_options.zversion)
+ self.toremove = []
+ for instance in instances:
+ iaddon = self.addon.installed_in_instance(instance)
+ if not iaddon:
+ continue
+ elif iaddon.installed_by(self.addon):
+ # consider removal
+ if iaddon.type == ADDON_COPIED:
+ if global_options.force:
+ self.toremove.append((iaddon, instance))
+ else:
+ self.warn("keep copy of %s `%s' in `%s'"
+ % (iaddon.kind, iaddon.name, instance.name))
+
+ else:
+ assert iaddon.type in (ADDON_LINKED, ADDON_TREELINKED) or \
+ iaddon.is_global
+ if self.mode == 'remove' and instance.addon_mode == 'manual':
+ self.warn("denied removal of %s `%s' from instance `%s' (%s)"
+ " in addon-mode `%s'"
+ % (iaddon.kind, iaddon.name, instance.name,
+ instance.version, instance.addon_mode))
+ else:
+ self.toremove.append((iaddon, instance))
+ else:
+ # manually installed addon, or installed by another package
+ self.warn("keep %s `%s' in instance `%s' not installed from `%s'" %
+ (iaddon.kind, iaddon.name, instance.name, self.addon.path))
+
+ return self.errors_occured
+
+ def run(self, global_options):
+ for iaddon, instance in self.toremove:
+ try:
+ instance.remove_addon(self.addon, global_options)
+ except DZError, msg:
+ failed_ilist = InstanceListFile(self.mode,
+ self.addon.directory,
+ oklist=False)
+ failed_ilist.append(instance)
+ self.error(msg)
+ else:
+ if self.mode == 'upgrade':
+ ilist = InstanceListFile(self.mode, self.addon.directory)
+ ilist.append(instance)
+
+
+class DZPrermProduct(ProductAttributes, DZPrerm):
+ name = 'prerm-%s' % ProductAttributes.kind
+ help = 'handle prerm of a packaged %s' % ProductAttributes.kind
+
+class DZPrermExtension(ExtensionAttributes, DZPrerm):
+ name = 'prerm-%s' % ExtensionAttributes.kind
+ help = 'handle prerm of a packaged %s' % ExtensionAttributes.kind
+
+register_action(DZPrermProduct)
+register_action(DZPrermExtension)
+
+
+class DZPostrm(DZAction):
+ kind = None
+ def get_option_parser(self):
+ if not self._option_parser:
+ p = OptionParser()
+ p.add_option('-m', '--mode',
+ action='store', dest='mode')
+ self._option_parser = p
+ return self._option_parser
+
+ def check_args(self, global_options):
+ if len(self.args) != 3:
+ self.error('--mode=<script args> <package> <directory> <%s name>' % self.kind)
+ if self.options.mode == None:
+ self.error("missing `--mode=<script arguments>'")
+ self.mode_args = self.options.mode.split()
+ if not self.mode_args[0] in ('remove'):
+ self.error("unknown mode `%s'" % self.options.mode)
+ self.mode = self.mode_args[0]
+ del self.mode_args[0]
+ package, directory, addon_name = self.args
+ return self.errors_occured
+
+ def run(self, global_options):
+ pass
+
+class DZPostrmProduct(ProductAttributes, DZPostrm):
+ name = 'postrm-%s' % ProductAttributes.kind
+ help = 'handle postrm of a packaged %s' % ProductAttributes.kind
+
+class DZPostrmExtension(ExtensionAttributes, DZPostrm):
+ name = 'postrm-%s' % ExtensionAttributes.kind
+ help = 'handle postrm of a packaged %s' % ExtensionAttributes.kind
+
+register_action(DZPostrmProduct)
+register_action(DZPostrmExtension)
+
+
+class DZDinstall(DZAction):
+ kind = None
+ def get_option_parser(self):
+ if not self._option_parser:
+ p = OptionParser()
+ p.add_option('-r', '--restart',
+ action='store', dest='restart', default='end')
+ self._option_parser = p
+ return self._option_parser
+
+ def check_args(self, global_options):
+ if not self.args:
+ self.error('no %ss to install' % self.kind)
+ if not self.options.restart in ('configuring', 'end', 'manually'):
+ self.error("invalid restart argument `%s'" % self.options.restart)
+ self.addons = []
+ for arg in self.args:
+ try:
+ addon = self.addonClass(arg)
+ except DZError, msg:
+ self.error(msg)
+ self.addons.append(addon)
+
+ instances = locate_instances()
+ # consider instances with Addon-Mode 'all' only
+ self.instances = [i for i in instances if i.addon_installation_required()]
+ self.toinstall = []
+ for addon in self.addons:
+ for instance in self.instances:
+ iaddon = addon.installed_in_instance(instance)
+ if iaddon:
+ # verify it's from the same package
+ if iaddon.installed_by(addon):
+ # safe to upgrade
+ self.toinstall.append((addon, iaddon, instance))
+ elif iaddon.type == ADDON_MANUAL:
+ self.error("found manually installed %s `%s' in instance "
+ "`%s' (%s) while installing package `%s'. Keep it" %
+ (self.kind, iaddon.name, instance.name, instance.version,
+ addon.package),
+ go_on=True)
+ elif iaddon.package != addon.package:
+ self.error("found %s `%s' installed from package `%s' in instance "
+ "`%s' (%s) while installing package `%s'. Keep it" %
+ (self.kind, iaddon.name, iaddon.package,
+ instance.name, instance.version, addon.package),
+ go_on=True)
+ else:
+ self.toinstall.append((addon, None, instance))
+ return self.errors_occured
+
+ def run(self, global_options):
+ for addon, old_addon, instance in self.toinstall:
+ if old_addon:
+ instance.remove_addon(old_addon, global_options)
+ instance.add_addon(addon, global_options)
+ instance.handle_restart_policy()
+
+class DZDinstallProduct(ProductAttributes, DZDinstall):
+ name = 'dinstall-product'
+ help = 'install a packaged product'
+
+class DZDinstallExtension(ExtensionAttributes, DZDinstall):
+ name = 'dinstall-extension'
+ help = 'install a packaged extension'
+
+register_action(DZDinstallProduct)
+register_action(DZDinstallExtension)
+
+
+class DZDremove(DZAction):
+ kind = None
+ def get_option_parser(self):
+ if not self._option_parser:
+ p = OptionParser()
+ self._option_parser = p
+ return self._option_parser
+
+ def check_args(self, global_options):
+ if not self.args:
+ self.error('no %ss to remove' % self.kind)
+ self.addons = []
+ for arg in self.args:
+ try:
+ addon = self.addonClass(arg)
+ except DZError, msg:
+ self.error(msg)
+ else:
+ self.addons.append(addon)
+
+ instances = locate_instances()
+ self.toremove = []
+ for addon in self.addons:
+ for instance in instances:
+ iaddon = addon.installed_in_instance(instance)
+ if not iaddon:
+ continue
+ if iaddon.installed_by(addon):
+ # consider removal
+ if iaddon.type == ADDON_COPIED:
+ if global_options.force:
+ self.toremove.append((addon, iaddon, instance))
+ else:
+ self.warn("leaving copy of %s `%s' in `%s'"
+ % (iaddon.kind, iaddon.name, instance.name))
+ else:
+ assert iaddon.type in (ADDON_LINKED, ADDON_TREELINKED)
+ self.toremove.append((addon, iaddon, instance))
+ else:
+ # manually installed addon, or installed by another package
+ self.warn("leaving %s `%s' in instance `%s' not installed from `%s'" %
+ (iaddon.kind, iaddon.name, instance.name, addon.path))
+ return self.errors_occured
+
+ def run(self, global_options):
+ for addon, iaddon, instance in self.toremove:
+ instance.remove_addon(addon, global_options)
+
+
+class DZDremoveProduct(ProductAttributes, DZDremove):
+ name = 'dremove-product'
+ help = 'remove a packaged product'
+
+class DZDremoveExtension(ExtensionAttributes, DZDremove):
+ name = 'dremove-extension'
+ help = 'remove a packaged extension'
+
+register_action(DZDremoveProduct)
+register_action(DZDremoveExtension)
+
+
+class DZAddAddon(DZAction):
+ kind = None
+ def get_option_parser(self):
+ if not self._option_parser:
+ p = OptionParser()
+ p.add_option('-l', '--lazy',
+ help="add missing addons only (error on manually installed addons)",
+ default=False, action='store_true', dest='lazy')
+ p.add_option('-t', '--installation-technique',
+ help="how to install addons (linked, tree-linked, copied)",
+ default=None, action='store', dest='atechnique')
+ self._option_parser = p
+ return self._option_parser
+
+ def find_addon(self, arg, lazy = False):
+ candidates = [addon for addon in self.locatedaddons
+ if (getattr(addon, 'directory', None) == arg) and \
+ addon.zopeversions.count(self.instance.version) > 0]
+
+ if not candidates:
+ candidates = [addon for addon in self.locatedaddons
+ if (addon.name == arg) and \
+ addon.zopeversions.count(self.instance.version) > 0]
+
+ if candidates: return candidates[0]
+ elif not lazy: self.error("unknown %s `%s'" % (self.kind, arg))
+ else: return None
+
+ def find_related(self, related, installed):
+ if not related:
+ return []
+
+ depends = []
+ for d in related.replace(' ','').split(','):
+ if d.count('|') > 0:
+ match = None
+ for alt in d.split('|'):
+ match = [i for i in installed if i.name == alt or \
+ getattr(i, 'directory', None) == alt]
+ if match: break
+ if match: continue
+
+ for alt in d.split('|'):
+ match = self.find_addon(alt, True)
+ if match: break
+ else:
+ match = self.find_addon(d)
+
+ if match and match not in (installed + depends):
+ depends.append(match)
+ depends += self.find_related(match.depends, installed + depends)
+
+ return depends
+
+ def check_args(self, global_options):
+ if len(self.args) < 2:
+ self.error('<instance> <%s> [<%s>]' % (self.kind, self.kind))
+ try:
+ instance = locate_instance(self.args[0],
+ versions=global_options.zversion)
+ except DZError, msg:
+ self.error(msg)
+
+ self.instance = instance
+
+ if self.options.atechnique:
+ if not self.options.atechnique in addon_techniques:
+ self.error("unknown addon technique `%s'" % self.options.atechnique)
+ self.options.atechnique = addon_technique_code(self.options.atechnique)
+ if self.options.atechnique != instance.addon_technique \
+ and not global_options.force:
+ self.error("addon technique `%s' doesn't match "
+ "the instance's default (`%s'). Use -f to override"
+ % (self.options.atechnique, instance.addon_technique))
+
+ self.addons = []
+ self.locatedaddons = locate_addons(self.addonClass, zopeversions=global_options.zversion)
+ for arg in self.args[1:]:
+ self.addons.append(self.find_addon(arg))
+
+ to_be_installed = [i.name for i in self.addons]
+
+ self.installed = instance.installed_addons()
+ for i in self.addons:
+ self.addons += self.find_related(i.depends, self.installed + self.addons)
+
+ for addon in self.addons[:]:
+ iaddon = addon.installed_in_instance(instance)
+ if not iaddon:
+ continue
+ if iaddon.installed_by(addon):
+ if self.options.lazy:
+ # warn about copied addons
+ if iaddon.type == ADDON_COPIED:
+ self.warn("%s `%s' (%s, %s) in instance `%s' (%s) . Keep it" %
+ (self.kind, iaddon.name, iaddon.version,
+ addon_technique_name(iaddon.type),
+ instance.name, instance.version))
+ self.addons.remove(addon)
+ elif addon.name not in to_be_installed:
+ self.addons.remove(addon)
+ else:
+ self.error("%s `%s' (%s, %s) in instance `%s' (%s) . Keep it" %
+ (self.kind, iaddon.name, iaddon.version,
+ addon_technique_name(iaddon.type),
+ instance.name, instance.version),
+ go_on=True)
+ elif iaddon.type == ADDON_MANUAL:
+ self.error("manually installed %s `%s' in instance `%s' (%s). Keep it" %
+ (self.kind, iaddon.name, instance.name, instance.version),
+ go_on=True)
+ else:
+ self.error("found %s `%s' installed from package `%s' in instance "
+ "`%s' (%s) while adding adding %s from `%s'. Keep it" %
+ (self.kind, iaddon.name, iaddon.package,
+ instance.name, instance.version, self.kind, addon.package),
+ go_on=True)
+
+ return self.errors_occured
+
+ def run(self, global_options):
+ for addon in self.addons:
+ self.instance.add_addon(addon, global_options, self.options.atechnique)
+ self.info("added %s `%s' to instance `%s' (%s)"
+ % (self.kind, addon.name, self.instance.name, self.instance.version))
+
+
+class DZAddProduct(ProductAttributes, DZAddAddon):
+ name = 'add-product'
+ help = 'add a product to an instance'
+
+class DZAddExtension(ExtensionAttributes, DZAddAddon):
+ name = 'add-extension'
+ help = 'add an extension to an instance'
+
+register_action(DZAddProduct)
+register_action(DZAddExtension)
+
+
+class DZRemoveAddon(DZAction):
+ kind = None
+ def get_option_parser(self):
+ if not self._option_parser:
+ p = OptionParser()
+ p.add_option('-f', '--force',
+ help="force removal of %ss" % self.kind,
+ default=False, action='store_true', dest='force')
+ p.add_option('-l', '--lazy',
+ help="don't complain about already removed addons",
+ default=False, action='store_true', dest='lazy')
+ self._option_parser = p
+ return self._option_parser
+
+ def check_args(self, global_options):
+ if len(self.args) < 2:
+ self.error('<instance> <%s> [<%s>]' % (self.kind, self.kind))
+ try:
+ self.instance = locate_instance(self.args[0],
+ versions=global_options.zversion)
+ except DZError, msg:
+ self.error(msg)
+
+ addons = []
+ for arg in self.args[1:]:
+ if ':' in arg:
+ addon_base, addon_suffix = arg.split(':', 1)
+ else:
+ addon_base, addon_suffix = arg, ''
+ ipath = os.path.join(self.instance.home, self.subdir, addon_base)
+ if os.path.exists(ipath + '.installed'):
+ path = open(ipath + '.installed', 'r').read()
+ addon = self.addonClass(path, dzfile_required=False)
+ addons.append((addon, addon))
+ continue
+ elif not os.path.isdir(ipath):
+ if not self.options.lazy:
+ self.error("%s `%s' not found in instance `%s' (%s)"
+ % (self.kind, addon_base, self.instance.name, self.instance.version),
+ go_on=True)
+ continue
+ try:
+ iaddon = self.addonClass(ipath, dzfile_required=False)
+ except DZError, msg:
+ self.error(msg, go_on=True)
+ else:
+ try:
+ addon = iaddon.find_master()
+ except DZError, msg:
+ self.error(msg, go_on=True)
+ else:
+ addons.append((addon, iaddon))
+
+ for addon, iaddon in addons:
+ if global_options.force or self.options.force:
+ continue
+ if iaddon.type in (ADDON_COPIED, ADDON_MANUAL):
+ self.error("not removing %s %s `%s' from instance `%s', use -f to override"
+ % (addon_technique_name(iaddon.type), self.kind, iaddon.name,
+ self.instance.name),
+ go_on=True)
+ elif self.instance.addon_mode == 'manual':
+ # linked or tree-linked
+ self.error("not removing %s %s `%s' from manually managed instance `%s', use -f to override"
+ % (addon_technique_name(iaddon.type), self.kind, iaddon.name,
+ self.instance.name),
+ go_on=True)
+
+ self.addons = addons
+ return self.errors_occured
+
+ def run(self, global_options):
+ for addon, iaddon in self.addons:
+ try:
+ self.instance.remove_addon(addon, global_options)
+ self.info("removed %s `%s' from instance `%s' (%s)"
+ % (self.kind, addon.directory, self.instance.name, self.instance.version))
+ except DZError, msg:
+ self.error(msg, go_on=True)
+
+ if self.errors_occured:
+ sys.exit(1)
+
+class DZRemoveProduct(ProductAttributes, DZRemoveAddon):
+ name = 'remove-product'
+ help = 'remove a product from an instance'
+
+class DZRemoveExtension(ExtensionAttributes, DZRemoveAddon):
+ name = 'remove-extension'
+ help = 'remove an extension from an instance'
+
+register_action(DZRemoveProduct)
+register_action(DZRemoveExtension)
+
+
+class DZListAddons(DZAction):
+ def get_option_parser(self):
+ if not self._option_parser:
+ p = OptionParser()
+ self._option_parser = p
+ return self._option_parser
+
+ def check_args(self, global_options):
+ if not self.args:
+ # list available products
+ self.instance = None
+ elif len(self.args) == 1:
+ try:
+ self.instance = locate_instance(self.args[0],
+ versions=global_options.zversion)
+ except DZError, msg:
+ self.error(msg)
+ else:
+ self.error('[<instance>]')
+ return self.errors_occured
+
+ def run(self, global_options):
+ if self.instance:
+ print 'Listing products installed for instance', self.instance.name
+ addons = self.instance.installed_addons()
+ else:
+ if len(global_options.zversion) > 1:
+ print 'Listing products available for all version(s):', ' '.join(global_options.zversion)
+ addons = locate_addons(self.addonClass, zopeversions=global_options.zversion)
+ for addon in addons:
+ addon.printit()
+
+class DZListProducts(ProductAttributes, DZListAddons):
+ name = 'list-products'
+ help = 'show all products managed by dzhandle'
+
+class DZListExtensions(ExtensionAttributes, DZListAddons):
+ name = 'list-extensions'
+ help = 'show all extensions managed by dzhandle'
+
+register_action(DZListProducts)
+register_action(DZListExtensions)
+
+
+class DZListInstances(DZAction):
+ name = 'list-instances'
+ help = 'print list of instances'
+
+ def check_args(self, global_options):
+ if len(self.args) > 1:
+ self.error("[<instance pattern>]")
+ self.instances = locate_instances(versions=global_options.zversion)
+ if self.args:
+ pattern = self.args[0]
+ instances = [i for i in self.instances if fnmatch.fnmatch(i.name, pattern)]
+ if not instances:
+ self.error("no instance matching pattern `%s'" % pattern)
+ self.instances = instances
+ return self.errors_occured
+
+ def run(self, global_options):
+ for instance in self.instances:
+ print instance.formatted_str(global_options.verbose)
+ if global_options.verbose:
+ pass
+
+register_action(DZListInstances)
+
+
+class DZListZeoInstances(DZAction):
+ name = 'list-zeoinstances'
+ help = 'print list of ZEO instances'
+
+ def check_args(self, global_options):
+ if len(self.args) > 1:
+ self.error("[<instance pattern>]")
+ self.instances = locate_zeoinstances(versions=global_options.zversion)
+ if self.args:
+ pattern = self.args[0]
+ instances = [i for i in self.instances if fnmatch.fnmatch(i.name, pattern)]
+ if not instances:
+ self.error("no ZEO instance matching pattern `%s'" % pattern)
+ self.instances = instances
+ return self.errors_occured
+
+ def run(self, global_options):
+ for instance in self.instances:
+ print instance.formatted_str(global_options.verbose)
+ if global_options.verbose:
+ pass
+
+register_action(DZListZeoInstances)
+
+
+class DZRestartPendingInstances(DZAction):
+ name = 'restart-pending-instances'
+ help = "restart instances with `restart-pending' markers"
+
+ def check_args(self, global_options):
+ if self.args:
+ self.error("no arguments required")
+ return self.errors_occured
+
+ def run(self, global_options):
+ for zversion in available_zope_versions():
+ zope = filter_zope_version(version=zversion)
+ pattern = os.path.join(zope['instance'], '*', 'var', 'restart-pending')
+ for pending in glob.glob(pattern):
+ home = os.path.dirname(os.path.dirname(pending))
+ instance = ZopeInstance(home, zope['version'])
+ if instance.is_running():
+ instance.zopectl('restart')
+ os.unlink(pending)
+
+ # compatibility with old 2.6 installations
+ if os.path.exists('/var/run/zope.restart'):
+ subprocess.call(['/usr/sbin/invoke-rc.d', 'zope', 'restart'])
+ os.unlink('/var/run/zope.restart')
+
+register_action(DZRestartPendingInstances)
+
+
+class DZShowInstance(DZAction):
+ name = 'show-instance'
+ help = 'print information about an instance'
+
+ def check_args(self, global_options):
+ if not self.args:
+ self.error('no instance to list')
+ if len(self.args) > 1:
+ self.error('<instance>')
+ try:
+ self.instance = locate_instance(self.args[0],
+ versions=global_options.zversion)
+ except DZError, msg:
+ self.error(msg)
+ return self.errors_occured
+
+ def run(self, global_options):
+ print self.instance.formatted_str(global_options.verbose)
+
+register_action(DZShowInstance)
+
+
+class DZInstanceRestartPolicy(DZAction):
+ name = 'instance-restart-policy'
+ help = 'get/set a policy on addon installation for an instance'
+
+ def check_args(self, global_options):
+ if not self.args:
+ self.error('<instance> [<policy>]')
+ self.policy = None
+ if len(self.args) > 1:
+ self.policy = self.args[1]
+ elif len(self.args) > 2:
+ self.error('<instance> [<policy>]')
+ try:
+ self.instance = locate_instance(self.args[0],
+ versions=global_options.zversion)
+ except DZError, msg:
+ self.error(msg)
+ if self.policy:
+ try:
+ self.instance.set_restart_policy(self.policy)
+ except DZError, msg:
+ self.error(msg)
+ return self.errors_occured
+
+ def run(self, global_options):
+ if self.policy:
+ self.instance.set_restart_policy(self.policy)
+ self.instance.write_dzfile(global_options.uid, global_options.gid)
+ else:
+ print self.instance.restart_policy
+
+register_action(DZInstanceRestartPolicy)
+
+
+class DZInstanceAddonTechnique(DZAction):
+ name = 'instance-addon-technique'
+ help = 'get/set an addon install technique for an instance'
+
+ def check_args(self, global_options):
+ if not self.args:
+ self.error('<instance> [<addon technique>]')
+ self.technique = None
+ if len(self.args) > 1:
+ self.technique = self.args[1]
+ if not self.technique in addon_techniques:
+ self.error("unknown addon technique `%s'" % self.technique)
+ elif len(self.args) > 2:
+ self.error('<instance> [<addon technique>]')
+ try:
+ self.instance = locate_instance(self.args[0],
+ versions=global_options.zversion)
+ except DZError, msg:
+ self.error(msg)
+ return self.errors_occured
+
+ def run(self, global_options):
+ if self.technique:
+ # set technique
+ self.instance.set_addon_technique(self.technique)
+ self.instance.write_dzfile(global_options.uid, global_options.gid)
+ else:
+ print self.instance.addon_technique
+
+register_action(DZInstanceAddonTechnique)
+
+
+class DZInstanceAddonMode(DZAction):
+ name = 'instance-addon-mode'
+ help = 'set an addon mode for an instance'
+
+ def check_args(self, global_options):
+ if not self.args:
+ self.error('<instance> [<addon mode>]')
+ self.addon_mode = None
+ if len(self.args) > 1:
+ self.addon_mode = self.args[1]
+ if not self.addon_mode in addon_modes:
+ self.error("unknown addon mode `%s'" % self.addon_mode)
+ elif len(self.args) > 2:
+ self.error('<instance> [<addon mode>]')
+ try:
+ self.instance = locate_instance(self.args[0],
+ versions=global_options.zversion)
+ except DZError, msg:
+ self.error(msg)
+ return self.errors_occured
+
+ def run(self, global_options):
+ if self.addon_mode:
+ # set addon_mode
+ self.instance.set_addon_mode(self.addon_mode)
+ self.instance.write_dzfile(global_options.uid, global_options.gid)
+ # TODO: manual -> all: add addons
+ else:
+ print self.instance.addon_mode
+
+register_action(DZInstanceAddonMode)
+
+
+class DZMakeInstance(DZAction):
+ name = 'make-instance'
+ help = 'run zope version mkzopeinstance'
+
+ def __init__(self):
+ DZAction.__init__(self)
+ self.get_option_parser().set_usage(
+ 'usage: %s [<option> ...] %s <instance-name> [<option> ...]' % (program, self.name))
+
+ def get_option_parser(self):
+ if not self._option_parser:
+ p = OptionParser()
+ p.add_option('-m', '--addon-mode',
+ help="which products and extension to install (all, manual)",
+ action='store', dest='amode')
+ p.add_option('-t', '--addon-install-technique',
+ help="how to install addons (linked, tree-linked, copied)",
+ default="tree-linked", action='store', dest='atechnique')
+ p.add_option('-r', '--restart',
+ help="when to restart on configuration (configuring, end, manually)",
+ default="end", action='store', dest='restart')
+ p.add_option('-u', '--user', help="user and password for the initial user (user:password)",
+ action='store', dest='user')
+ p.add_option('', '--service-user', help="system user used to run this instance (user:group)",
+ action='store', dest='srvuser', default='zope:zope')
+ p.add_option('', '--service-port', help="HTTP port used to run this instance",
+ action='store', dest='srvport', default='9673')
+ self._option_parser = p
+ return self._option_parser
+
+ def check_args(self, global_options):
+ if len(self.args) != 1:
+ self._option_parser.print_help()
+ sys.exit(1)
+
+ if not global_options.zversion:
+ self.error('no zope version to make instance for')
+
+ if len(global_options.zversion) > 1:
+ self.error('ambiguous zope version to make instance for: %s'
+ % strlist(global_options.zversion))
+
+ if self.options.user and not ":" in self.options.user:
+ self.error('user must be specified as name:password')
+
+ if not is_root() and self.options.srvuser == 'zope:zope':
+ self.uid = os.getuid()
+ self.gid = os.getgid()
+ self.options.srvuser = "%s:%s" % \
+ (pwd.getpwuid(os.getuid())[0], grp.getgrgid(os.getgid())[0])
+ elif not ":" in self.options.srvuser:
+ self.error('service user must be specified as user:group')
+ else:
+ try:
+ uid, gid = self.options.srvuser.split(":")
+ self.uid = pwd.getpwnam(uid)[2]
+ self.gid = grp.getgrnam(gid)[2]
+ except KeyError, msg:
+ self.error(msg)
+
+ self.zversion = global_options.zversion[0]
+ self.instance_name = self.args[0]
+
+ if not self.options.amode:
+ self.error("missing option -m <addon-mode>")
+ if not self.options.amode in addon_modes:
+ self.error("unknown addon mode `%s'" % self.options.amode)
+
+ if not self.options.atechnique in addon_techniques:
+ self.error("unknown addon technique `%s'" % self.options.atechnique)
+
+ if not self.options.restart in ('configuring', 'end', 'manually'):
+ self.error("unknown restart policy `%s'" % self.options.restart)
+
+ self.zope = filter_zope_version(version=self.zversion)
+ if is_root():
+ self.instance_home = os.path.join(self.zope['instance'], self.instance_name)
+ else:
+ self.instance_home = os.path.join(personal_conf['instances'], self.zope['name'], self.instance_name)
+ if os.path.exists(self.instance_home):
+ self.error("instance home `%s' already exists" % self.instance_home)
+ return self.errors_occured
+
+ def run(self, global_options):
+ if self.zversion.startswith('2.'):
+ cmd = [os.path.join(self.zope['prefix'], 'bin', 'mkzopeinstance.py')]
+ else:
+ cmd = [os.path.join(self.zope['prefix'], 'bin', 'mkzopeinstance'), '--password-manager=MD5']
+ cmd.append('--dir=%s' % self.instance_home)
+ cmd.append('--layout=%s' % (is_root() and 'fhs' or 'zope'))
+ if self.options.user:
+ cmd.append('--user=' + self.options.user)
+ if self.options.srvuser:
+ cmd.append('--service-user=' + self.options.srvuser)
+ if self.options.srvport:
+ cmd.append('--service-port=' + self.options.srvport)
+
+ # zope3's mkzopeinstance doesn't create the parents dir
+ if self.zope['version'] == '3' and not os.path.isdir(self.instance_home):
+ os.makedirs(self.instance_home)
+ os.rmdir(self.instance_home)
+
+ rv = subprocess.call(cmd)
+ if rv:
+ subprocess.call(['/bin/rm', '-fr', self.instance_home])
+ sys.exit(rv)
+ instance = ZopeInstance(self.instance_home, self.zversion, read_dzfile=False)
+ instance.set_addon_mode(self.options.amode)
+ instance.set_addon_technique(self.options.atechnique)
+ instance.set_restart_policy(self.options.restart)
+ instance.write_dzfile(uid=self.uid, gid=self.gid)
+ if not instance.addon_installation_required():
+ return
+ for addon in locate_addons(zopeversions=global_options.zversion):
+ try:
+ instance.add_addon(addon, global_options, None)
+ except DZError, msg:
+ # more than one product version ..., ignore it for now
+ self.warn(msg)
+
+register_action(DZMakeInstance)
+
+
+class DZMakeZeoInstance(DZAction):
+ name = 'make-zeoinstance'
+ help = 'run zope version mkzeoinstance'
+
+ def __init__(self):
+ DZAction.__init__(self)
+ self.get_option_parser().set_usage(
+ 'usage: %s [<option> ...] %s <zeoinstance-name> [<port>]' % (program, self.name))
+
+ def get_option_parser(self):
+ if not self._option_parser:
+ p = OptionParser()
+ self._option_parser = p
+ return self._option_parser
+
+ def check_args(self, global_options):
+ if len(self.args) not in (1, 2):
+ self._option_parser.print_help()
+ sys.exit(1)
+
+ if not global_options.zversion:
+ self.error('no zope version to make instance for')
+
+ if len(global_options.zversion) > 1:
+ self.error('ambiguous zope version to make instance for: %s'
+ % strlist(global_options.zversion))
+
+ self.zversion = global_options.zversion[0]
+ self.instance_name = self.args[0]
+ if len(self.args) > 1:
+ try:
+ self.port = int(self.args[1])
+ except ValueError:
+ self.error('the zeo-instance port number has to be integer')
+ else:
+ self.port = None
+ self.zope = filter_zope_version(version=self.zversion)
+
+ if is_root():
+ self.instance_home = os.path.join(self.zope['zeoinstance'], self.instance_name)
+ else:
+ self.instance_home = os.path.join(personal_conf['zeoinstances'], self.zope['name'], self.instance_name)
+ if os.path.exists(self.instance_home):
+ self.error("instance home `%s' already exists" % self.instance_home)
+ return self.errors_occured
+
+ def run(self, global_options):
+ if self.zversion.startswith('2.'):
+ cmd = [os.path.join(self.zope['prefix'], 'bin', 'mkzeoinstance.py')]
+ else:
+ cmd = [os.path.join(self.zope['prefix'], 'bin', 'mkzeoinstance')]
+
+ cmd.append(self.instance_home)
+ if self.port != None:
+ cmd.append(str(self.port))
+
+ rv = subprocess.call(cmd)
+ if rv:
+ subprocess.call(['/bin/rm', '-fr', self.instance_home])
+ sys.exit(rv)
+
+register_action(DZMakeZeoInstance)
+
+
+class DZRemoveInstance(DZAction):
+ name = 'remove-instance'
+ help = 'remove addons from an instance (except data files), mark it as removed'
+
+ def check_args(self, global_options):
+ if len(self.args) != 1:
+ self.error('<instance>')
+ try:
+ self.instance = locate_instance(self.args[0],
+ versions=global_options.zversion)
+ except DZError, msg:
+ self.error(msg)
+ return self.errors_occured
+
+ def run(self, global_options):
+ cmd = ["rm", "-rf",
+ self.instance.home + "/Products",
+ self.instance.home + "/Extensions",
+ self.instance.home + "/lib",
+ self.instance.home + "/bin",
+ self.instance.home + "/inituser",
+ ]
+ rv = subprocess.call(cmd)
+ if rv:
+ sys.exit(rv)
+
+register_action(DZRemoveInstance)
+
+
+class DZPurgeInstance(DZAction):
+ name = 'purge-instance'
+ help = 'purge files in an instance (including data files)'
+
+ def check_args(self, global_options):
+ if len(self.args) != 1:
+ self.error('<instance>')
+ try:
+ self.instance = locate_instance(self.args[0],
+ versions=global_options.zversion)
+ except DZError, msg:
+ self.error(msg)
+ return self.errors_occured
+
+ def run(self, global_options):
+ cmd = ["rm", "-rf",
+ self.instance.home,
+ "/etc/zope%s/%s" % (self.instance.version, self.instance.name),
+ "/var/log/zope%s/%s" % (self.instance.version, self.instance.name),
+ ]
+ rv = subprocess.call(cmd)
+ if rv:
+ sys.exit(rv)
+
+register_action(DZPurgeInstance)
+
+
+class DZPurgeZeoInstance(DZAction):
+ name = 'purge-zeoinstance'
+ help = 'purge files in a ZEO instance (including data files)'
+
+ def check_args(self, global_options):
+ if len(self.args) != 1:
+ self.error('<zeo_instance>')
+ try:
+ self.instance = locate_zeoinstance(self.args[0],
+ versions=global_options.zversion)
+ except DZError, msg:
+ self.error(msg)
+ return self.errors_occured
+
+ def run(self, global_options):
+ cmd = ["rm", "-rf",
+ self.instance.home]
+ rv = subprocess.call(cmd)
+ if rv:
+ sys.exit(rv)
+
+register_action(DZPurgeZeoInstance)
+
+
+class DZZopectl(DZAction):
+ name = 'zopectl'
+ help = 'call zopectl for a given instance'
+
+ def get_option_parser(self):
+ if not self._option_parser:
+ class UnknownOptionParser(OptionParser):
+ def parse_args(self, args):
+ opts, tmp = OptionParser.parse_args(self, [])
+ return opts, args
+ p = UnknownOptionParser()
+ self._option_parser = p
+ return self._option_parser
+
+ def check_args(self, global_options):
+ if len(self.args) < 1 or self.args[0].startswith('-'):
+ self.error('<instance> <zdctl-action> [<zdctl options>]')
+ try:
+ self.instance = locate_instance(self.args[0],
+ versions=global_options.zversion)
+ except DZError, msg:
+ self.error(msg)
+ if len(self.args) > 1 and self.args[1].startswith('-'):
+ print >>sys.stderr, ("missing zdctl action, entering interactive mode ...")
+ if [arg for arg in self.args if arg in ('-i', '--interactive')]:
+ print >>sys.stderr, ("entering interactive mode after executing command ...")
+ return self.errors_occured
+
+ def run(self, global_options):
+ rv = self.instance.zopectl(self.args[1:])
+ if rv:
+ sys.exit(rv)
+
+register_action(DZZopectl)
+
+
+class DZZeoctl(DZAction):
+ name = 'zeoctl'
+ help = 'call zeoctl for a given instance'
+
+ def get_option_parser(self):
+ if not self._option_parser:
+ class UnknownOptionParser(OptionParser):
+ def parse_args(self, args):
+ opts, tmp = OptionParser.parse_args(self, [])
+ return opts, args
+ p = UnknownOptionParser()
+ self._option_parser = p
+ return self._option_parser
+
+ def check_args(self, global_options):
+ if len(self.args) < 1 or self.args[0].startswith('-'):
+ self.error('<instance> <zeoctl-action> [<zeoctl options>]')
+ try:
+ self.instance = locate_zeoinstance(self.args[0],
+ versions=global_options.zversion)
+ except DZError, msg:
+ self.error(msg)
+ if len(self.args) > 1 and self.args[1].startswith('-'):
+ print >>sys.stderr, ("missing zeoctl action, entering interactive mode ...")
+ if [arg for arg in self.args if arg in ('-i', '--interactive')]:
+ print >>sys.stderr, ("entering interactive mode after executing command ...")
+ return self.errors_occured
+
+ def run(self, global_options):
+ rv = self.instance.zeoctl(self.args[1:])
+ if rv:
+ sys.exit(rv)
+
+register_action(DZZeoctl)
+
+
+class ZopeInstance:
+ def __init__(self, home, version, read_dzfile = True):
+ self.home = home
+ self.name = os.path.basename(home)
+ self.version = version
+ self.addon_technique = None
+ self.restart_policy = None
+ self.addon_mode = None
+ self.excluded_addons = []
+ self.new_layout = os.path.exists(os.path.join(home, 'bin', 'runzope'))
+ self.is_purged = False
+ self.is_purged |= not os.path.exists(os.path.join(home, 'var', 'Data.fs'))
+ self.is_purged |= not os.path.exists(os.path.join(home, 'var', 'Data.fs.index'))
+ self.is_removed = False
+ self.is_removed |= not os.path.exists(os.path.join(home, 'bin', 'zopectl'))
+ if os.path.isfile(os.path.join(home, 'inituser')):
+ self.userfile = 'inituser'
+ elif os.path.isfile(os.path.join(home, 'access')):
+ self.userfile = 'access'
+ else:
+ self.userfile = None
+ if read_dzfile: self.read_dzfile()
+ self._installed_addons = None
+
+ def set_addon_technique(self, addon_technique):
+ old_technique = self.addon_technique
+ if isinstance(addon_technique, str):
+ self.addon_technique = list(addon_options).index(addon_technique)
+ elif isinstance(addon_technique, int):
+ self.addon_technique = addon_technique
+ else:
+ raise TypeError, "unknown type for addon technique"
+ if old_technique != None and self.addon_technique != old_technique:
+ print >>sys.stderr, "WARNING: change of instance addon technique not yet supported"
+ # manage addons
+ # self.write_dzfile()
+
+ def set_restart_policy(self, policy):
+ if policy in (None, 'configuring', 'end', 'manually'):
+ self.restart_policy = policy
+ else:
+ raise ValueError, "unknown restart policy `%s'" % policy
+
+ def set_addon_mode(self, addon_mode):
+ if addon_mode in addon_modes:
+ self.addon_mode = addon_mode
+ else:
+ raise ValueError, "unknown addon_mode `%s'" % addon_mode
+
+ def addon_installation_required(self, addon=None):
+ return self.addon_mode == 'all'
+
+ def is_addon_excluded(self, name):
+ return name in self.excluded_addons
+
+ def read_dzfile(self):
+ fn = os.path.join(self.home, 'etc', 'debian_policy')
+ self.dzfile_exists = os.path.exists(fn)
+ if not self.dzfile_exists:
+ self.set_addon_mode('manual')
+ self.set_addon_technique(ADDON_MANUAL)
+ self.set_restart_policy('manually')
+ return
+ attrs = read_config_file(fn, required=['Name', 'Addon-Mode',
+ 'Addon-Technique', 'Restart-Policy'])
+
+ try:
+ self.set_addon_technique(attrs['Addon-Technique'])
+ except ValueError:
+ raise DZError, \
+ "unknown addon technique `%s' in `%s'" % (attrs['Addon-Technique'], fn)
+
+ try:
+ self.set_addon_mode(attrs['Addon-Mode'])
+ except ValueError:
+ raise DZError, \
+ "unknown addon mode `%s' in `%s'" % (attrs['Addon-Mode'], fn)
+
+ try:
+ self.set_restart_policy(attrs['Restart-Policy'])
+ except KeyError:
+ pass
+ except ValueError, msg:
+ raise DZError, "%s in `%s'" % (attrs['Restart-Policy'], fn)
+
+ try:
+ self.excluded_addons = attrs['Excluded-Addons'].split()
+ except KeyError:
+ pass
+
+ if attrs['Name'] != self.name:
+ raise DZError, "instance name `%s' doesn't match in `%s'" % (attrs['Name'], fn)
+
+ def write_dzfile(self, uid=None, gid=None):
+ fn = os.path.join(self.home, 'etc', 'debian_policy')
+ if os.path.exists(fn):
+ try:
+ os.path.unlink(fn + '.old')
+ except:
+ pass
+ os.rename(fn, fn + '.old')
+ attrs = {'Name': self.name,
+ 'Addon-Technique': addon_technique_name(self.addon_technique),
+ 'Addon-Mode': self.addon_mode,
+ 'Restart-Policy': self.restart_policy
+ }
+ if self.excluded_addons:
+ attrs['Excluded-Addons'] = ' '.join(self.excluded_addons)
+ write_config_file(fn, attrs, uid=uid, gid=gid)
+
+ def installed_addons(self):
+ if self._installed_addons == None:
+ self._installed_addons = locate_addons(instance=self)
+ return self._installed_addons
+
+ def add_addon(self, addon, global_options, addon_technique=None):
+ installed = [a for a in self.installed_addons() if addon.name == a.name]
+ if installed:
+ a = installed[0]
+ print "%s `%s' already available in instance `%s'" % (a.kind, a.name, self.name)
+ return
+
+ addon_technique = addon_technique or self.addon_technique
+ if addon_technique == ADDON_MANUAL:
+ return
+ assert addon_technique in (ADDON_LINKED, ADDON_TREELINKED, ADDON_COPIED)
+
+ if self.version.startswith("3"):
+ files = []
+ files.extend(glob.glob(os.path.join(addon.path, '*-configure.zcml')))
+ files.extend(glob.glob(os.path.join(addon.path, '*-meta.zcml')))
+ files.extend(glob.glob(os.path.join(addon.path, '*-ftesting.zcml')))
+ for f in files:
+ filename = os.path.split(f)[1]
+ os.symlink(f, os.path.join(self.home, "etc/package-includes/", filename))
+
+ target_path = os.path.join(self.home, addon.subdir, addon.name)
+ if addon.is_global:
+ open(target_path + '.installed', 'w').write(addon.path)
+ target_path = addon.path
+ elif addon_technique == ADDON_LINKED:
+ os.symlink(addon.path, target_path)
+ if global_options.verbose:
+ print "linked: %s -> %s" % (target_path, addon.path)
+ elif addon_technique == ADDON_TREELINKED:
+ copytree(addon.path, target_path, copy_all=False,
+ uid=global_options.uid, gid=global_options.gid)
+ if global_options.verbose:
+ print "tree linked: %s -> %s" % (target_path, addon.path)
+ elif addon_technique == ADDON_COPIED:
+ copytree(addon.path, target_path, copy_all=True,
+ uid=global_options.uid, gid=global_options.gid)
+ if global_options.verbose:
+ print "copied: %s -> %s" % (addon.path, target_path)
+
+ added_addon = addon.addonClass(target_path, False)
+ self._installed_addons.append(added_addon)
+ logging.info("added %s `%s'", added_addon.kind, added_addon.path)
+
+ def remove_addon(self, addon, global_options):
+ installed = [a for a in self.installed_addons() if getattr(addon, 'name', None) == a.name]
+ if len(installed) != 1:
+ raise DZError, "%s `%s' not installed in instance `%s'" \
+ % (addon.kind, addon.name, self.name)
+ installed = installed[0]
+ assert addon.name == installed.name
+
+ if self.version.startswith("3"):
+ files = []
+ files.extend(glob.glob(os.path.join(addon.path, '*-configure.zcml')))
+ files.extend(glob.glob(os.path.join(addon.path, '*-meta.zcml')))
+ files.extend(glob.glob(os.path.join(addon.path, '*-ftesting.zcml')))
+ for f in files:
+ filename = os.path.split(f)[1]
+ target = os.path.join(self.home, "etc/package-includes/", filename)
+ if os.path.exists(target):
+ os.unlink(target)
+
+ if addon.is_global:
+ os.unlink(os.path.join(self.home, addon.subdir, addon.name) + '.installed')
+ elif installed.type == ADDON_LINKED:
+ os.unlink(installed.path)
+ elif installed.type in (ADDON_MANUAL, ADDON_COPIED) and not global_options.force:
+ raise DZError, "not removing copied or manually installed %s `%s'" \
+ % (installed.kind, installed.name)
+ elif installed.type in (ADDON_TREELINKED, ADDON_COPIED) and not global_options.force:
+ if os.path.islink(installed.path):
+ os.unlink(installed.path)
+ else:
+ try:
+ deltree(addon.path, installed.path)
+ except Exception, msg:
+ raise DZError, msg
+ elif global_options.force:
+ if os.path.islink(installed.path):
+ os.unlink(installed.path)
+ else:
+ shutil.rmtree(installed.path)
+ else:
+ return
+
+ self._installed_addons.remove(installed)
+ logging.info("removed %s `%s'", installed.kind, installed.path)
+
+ def handle_restart_policy(self, policy='end', reason='-'):
+ # instance restart policy overwrites option
+ if self.restart_policy:
+ policy = self.restart_policy
+
+ if not self.is_running():
+ return
+
+ if policy == 'manually':
+ return
+ elif policy == 'end' and self.is_running():
+ fd = file(os.path.join(self.home, 'var', 'restart-pending'), 'a+')
+ fd.write(reason + '\n')
+ fd.close()
+ elif policy == 'configuring':
+ rv = self.zopectl('restart')
+ if rv:
+ sys.exit(rv)
+
+ def is_running(self):
+ cmd = [os.path.join(self.home, 'bin', 'zopectl'), 'status']
+ return os.path.isfile(cmd[0]) and \
+ "program running;" in subprocess.Popen(args=cmd,
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.read()
+
+ def zopectl(self, *args):
+ cmd = [os.path.join(self.home, 'bin', 'zopectl')]
+ os.chdir(self.home)
+ if args and isinstance(args[0], list):
+ cmd.extend(args[0])
+ else:
+ cmd.extend(args)
+ return subprocess.call(cmd)
+
+ def formatted_str(self, verbose):
+ s = '%(name)-20s %(version)-5s ' % self.__dict__
+ s += ' addon-mode=%s' % self.addon_mode
+ if isinstance(self.addon_technique, int):
+ addon_technique = addon_options[self.addon_technique]
+ else:
+ addon_technique = self.addon_technique
+ s += ' addon-technique=%s' % addon_technique
+ if self.userfile: s += ' userfile=' + self.userfile
+ if not self.new_layout: s += ' layout=(2.5 and before)'
+ if self.is_purged: s += ' purged'
+ elif self.is_removed: s += ' removed'
+ if verbose:
+ pass
+ return s
+
+
+class ZeoInstance:
+
+ def __init__(self, home, version):
+ self.home = home
+ self.name = os.path.basename(home)
+ self.version = version
+
+ def is_running(self):
+ cmd = [os.path.join(self.home, 'bin', 'zeoctl'), 'status']
+ return not "not running" in subprocess.Popen(args=cmd,
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT).read()
+
+ def zeoctl(self, *args):
+ cmd = [os.path.join(self.home, 'bin', 'zeoctl')]
+ os.chdir(self.home)
+ if args and isinstance(args[0], list):
+ cmd.extend(args[0])
+ else:
+ cmd.extend(args)
+ return subprocess.call(cmd)
+
+ def formatted_str(self, verbose):
+ s = '%(name)-20s %(version)-5s ' % self.__dict__
+ if verbose:
+ pass
+ return s
+
+
+def copytree(sourcedir, destdir, copy_all=True, uid=None, gid=None):
+ os.mkdir(destdir)
+ destdir = os.path.abspath(destdir)
+ shutil.copymode(sourcedir, destdir)
+ if not uid is None:
+ os.chown(destdir, uid, gid)
+ saved_pwd = os.getcwd()
+ os.chdir(sourcedir)
+ for root, dirs, files in os.walk(sourcedir):
+ if root == sourcedir:
+ relroot = ''
+ else:
+ relroot = root[len(sourcedir)+1:]
+ for name in files:
+ source = os.path.join(root, name)
+ target = os.path.join(destdir, relroot, name)
+ if copy_all and os.path.islink(source):
+ print "copy target of symlink %s" % source
+ if copy_all:
+ shutil.copy2(source, target)
+ if not uid is None:
+ os.chown(target, uid, gid)
+ else:
+ os.symlink(source, target)
+ for name in dirs:
+ source = os.path.join(root, name)
+ target = os.path.join(destdir, relroot, name)
+ if os.path.islink(source):
+ print "symlink replaced by directory %s" % source
+ os.mkdir(target)
+ shutil.copymode(sourcedir, destdir)
+ if not uid is None:
+ os.chown(target, uid, gid)
+ os.chdir(saved_pwd)
+
+def deltree(sourcedir, destdir):
+ """remove files found in sourcedir in destdir, remove
+ corresponding .'py[co]' files as well"""
+
+ destdir = os.path.abspath(destdir)
+ dzfile = None
+ if os.path.exists(os.path.join(destdir, '.dzproduct')):
+ dzfile = os.path.join(destdir, '.dzproduct')
+ elif os.path.exists(os.path.join(destdir, '.dzextension')):
+ dzfile = os.path.join(destdir, '.dzextension')
+ if dzfile:
+ saved_dzfile = os.path.join(os.path.dirname(destdir),
+ '.dz.%d' % os.getpid())
+ shutil.move(dzfile, saved_dzfile)
+ try:
+ saved_pwd = os.getcwd()
+ os.chdir(sourcedir)
+ for root, dirs, files in os.walk(sourcedir, topdown=False):
+ if root == sourcedir:
+ relroot = ''
+ else:
+ relroot = root[len(sourcedir)+1:]
+ for name in files:
+ #source = os.path.join(root, name)
+ target = os.path.join(destdir, relroot, name)
+
+ if os.path.exists(target):
+ os.unlink(target)
+ if target.endswith('.py'):
+ if os.path.exists(target + 'c'):
+ os.unlink(target + 'c')
+ if os.path.exists(target + 'o'):
+ os.unlink(target + 'o')
+ for name in dirs:
+ #source = os.path.join(root, name)
+ target = os.path.join(destdir, relroot, name)
+ if os.path.islink(target):
+ # should not happen
+ os.unlink(target)
+ else:
+ try:
+ os.rmdir(target)
+ except OSError, msg:
+ pass
+ #print >>sys.stderr, "rmdir `%s' failed: %s" % (target, msg)
+ os.rmdir(destdir)
+ except (IOError, OSError), msg:
+ print >>sys.stderr, msg
+ if dzfile:
+ shutil.move(saved_dzfile, dzfile)
+ raise
+ else:
+ os.unlink(saved_dzfile)
+ os.chdir(saved_pwd)
+
+def locate_instances(versions=None):
+ instances = []
+ pkgs = [(z['version'], z['instance']) for z in zope_packages
+ if not versions or z['version'] in versions]
+ for version, instance_dir in pkgs:
+ inst_list = glob.glob(os.path.join(instance_dir, '*'))
+ if not is_root():
+ inst_list.extend(glob.glob(os.path.join(
+ personal_conf['instances'], 'zope%s' % version, '*')))
+ for home in inst_list:
+ if os.path.isdir(os.path.join(home, 'var')):
+ instance = ZopeInstance(home, version)
+ instances.append(instance)
+ return instances
+
+def locate_zeoinstances(versions=None):
+ instances = []
+ pkgs = [(z['version'], z['zeoinstance']) for z in zope_packages
+ if not versions or z['version'] in versions]
+ for version, instance_dir in pkgs:
+ inst_list = glob.glob(os.path.join(instance_dir, '*'))
+ if not is_root():
+ inst_list.extend(glob.glob(os.path.join(
+ personal_conf['zeoinstances'], 'zope%s' % version, '*')))
+ for home in inst_list:
+ if os.path.isdir(os.path.join(home, 'var')):
+ instance = ZeoInstance(home, version)
+ instances.append(instance)
+ return instances
+
+def locate_instance(name, versions=None):
+ instances = locate_instances(versions=versions)
+ known = [i for i in instances if i.name == name]
+ if not known:
+ raise DZError, "unknown instance `%s'" % name
+ elif len(known) > 1:
+ s = ', '.join(["`%s' (%s)" % (i.name, i.version) for i in known])
+ raise DZError, "ambiguous instance name `%s' matching %s" % (name, s)
+ return known[0]
+
+def locate_zeoinstance(name, versions=None):
+ instances = locate_zeoinstances(versions=versions)
+ known = [i for i in instances if i.name == name]
+ if not known:
+ raise DZError, "unknown ZEO instance `%s'" % name
+ elif len(known) > 1:
+ s = ', '.join(["`%s' (%s)" % (i.name, i.version) for i in known])
+ raise DZError, "ambiguous ZEO instance name `%s' matching %s" % (name, s)
+ return known[0]
+
+def locate_addons(addonClass=None, arch=None, instance=None, zopeversions=None):
+ if instance:
+ prefixes = [instance.home]
+ elif arch == 'any':
+ prefixes = ['/usr/lib/zope']
+ elif arch == 'all':
+ prefixes = ['/usr/share/zope', '/usr/local/share/zope']
+ else:
+ prefixes = ['/usr/share/zope', '/usr/lib/zope', '/usr/local/share/zope']
+ if addonClass == Product:
+ subdirs = [Product.subdir]
+ elif addonClass == Extension:
+ subdirs = [Extension.subdir]
+ else:
+ subdirs = [Product.subdir, Extension.subdir]
+ locals = []
+ if not instance and not is_root():
+ locals.append(personal_conf['products'])
+ addons = []
+ for d in locals + [os.path.join(pf, sd) for pf in prefixes for sd in subdirs]:
+ for path in glob.glob(os.path.join(d, '*')):
+ if os.path.isfile(path) and path.endswith('.installed'):
+ path = open(path, 'r').read()
+ addonClass = Product
+ elif not os.path.isdir(path):
+ continue
+ elif not addonClass:
+ if os.path.basename(os.path.dirname(path)) == Product.subdir:
+ addonClass = Product
+ else:
+ addonClass = Extension
+ addon = addonClass(path, False)
+ zver = False
+ if zopeversions != None:
+ for i in addon.zopeversions.split():
+ if i in zopeversions:
+ zver = True
+ break
+ else:
+ zver = True
+ if zver:
+ addons.append(addon)
+ return addons
+
+# --------------------------------------------------------------------------- #
+
+# print an error message
+def usage(stream, msg=None):
+ print >>stream, msg
+ print >>stream, "use `%s help' for help on actions and arguments" % program
+ print >>stream
+ sys.exit(1)
+
+# match a string with the list of available actions
+def action_matches(action, actions):
+ prog = re.compile('[^-]*?-'.join(action.split('-')))
+ return [a for a in actions if prog.match(a)]
+
+# parse command line arguments
+def parse_options(args):
+ shortusage = 'usage: %prog [<option> ...] <action> [<option> ...]'
+ parser = OptionParser(usage=shortusage)
+ parser.disable_interspersed_args()
+
+ # setup the parsers object
+ parser.remove_option('-h')
+ parser.add_option('-h', '--help',
+ help='help screen',
+ action='store_true', dest='help')
+ parser.add_option('-v', '--verbose',
+ help='verbose mode',
+ action='store_true', dest='verbose')
+ parser.add_option('-f', '--force',
+ help='force things, i.e. overwriting, removing stuff',
+ action='store_true', dest='force')
+ parser.add_option('-z', '--zope-version',
+ help='limit actions to comma separated list of zope versions',
+ action='store', dest='zversion')
+ parser.add_option('-n', '--dry-run',
+ help='do not execute commands, print only (not yet implemented)',
+ action='store', default=False, dest='dryrun')
+ parser.add_option('-u', '--user',
+ help='<user>[:<group>] ownership for new and copied files',
+ action='store', dest='user')
+ parser.add_option('-c', '--config-file',
+ help='configuration file (default is /etc/dzhandle.conf)',
+ action='store', dest='conffile', default="/etc/dzhandle.conf")
+ global_options, args = parser.parse_args()
+
+ # Print the help screen and exit
+ if len(args) == 0 or args[0].lower() == 'help' or global_options.help:
+ parser.print_help()
+ print "\nactions:"
+ for n, a in sorted(known_actions.items()):
+ print " %-21s %s" % (n, a.help)
+ print ""
+ sys.exit(1)
+
+ # dry-run option not yet implemented
+ if global_options.dryrun:
+ print >>sys.stderr, "option --dry-run not yet implemented"
+ sys.exit(1)
+
+ # check if the specified zope versions really exist
+ known_versions = [z['version'] for z in zope_packages]
+ if global_options.zversion:
+ zversion = global_options.zversion.split(',')
+ unknown_versions = [v for v in zversion if not v in known_versions]
+ if unknown_versions:
+ usage(sys.stderr, 'unknown zope version(s) %s' % strlist(unknown_versions))
+ global_options.zversion = zversion
+ else:
+ global_options.zversion = known_versions
+
+ # get the uid/gid for zope user/group
+ global_options.uid = None
+ global_options.gid = None
+ if global_options.user:
+ if ':' in global_options.user:
+ user, group = global_options.user.split(':')
+ else:
+ user = global_options.user
+ group = None
+ elif not is_root():
+ user = pwd.getpwuid(os.getuid())[0]
+ group = grp.getgrgid(os.getgid())[0]
+ else:
+ user = 'zope'
+ group = 'zope'
+ try:
+ user_info = pwd.getpwnam(user)
+ if group:
+ group_info = grp.getgrnam(group)
+ else:
+ group_info = grp.getgrgid(user_info[3])
+ except KeyError, msg:
+ usage(sys.stderr, msg)
+ global_options.uid = user_info[2]
+ global_options.gid = group_info[2]
+
+ # check if the specified action really exists
+ action_name = args[0]
+ del args[0]
+ matching_actions = action_matches(action_name, known_actions.keys())
+ if len(matching_actions) == 0:
+ usage(sys.stderr, "unknown action `%s'" % action_name)
+ elif len(matching_actions) > 1:
+ usage(sys.stderr,
+ "ambiguous action `%s', matching actions: %s"
+ % (action_name, strlist(matching_actions)))
+ else:
+ action_name = matching_actions[0]
+
+ # instantiate an object for the action and parse the remaining arguments
+ action = known_actions[action_name]()
+ action_options, action_names = action.parse_args(args)
+
+ return global_options, action
+
+# setup logging stuff
+def setup_logging(logfile=None, logfilelevel='INFO', loglevel='WARN'):
+ env_level = os.environ.get('DZHANDLE', None)
+ if env_level != None:
+ loglevel = logging.getLevelName(env_level)
+ if isinstance(loglevel, str):
+ loglevel = logging.INFO
+
+ logging.basicConfig(format='%(message)s',
+ level=loglevel,
+ stream=sys.stderr)
+
+ # are we logging to a file?
+ if logfile:
+ logfilelevel = logging.getLevelName(env_level)
+ if isinstance(logfilelevel, str):
+ logfilelevel = logging.INFO
+
+ from logging.handlers import TimedRotatingFileHandler
+ try:
+ fhandler = TimedRotatingFileHandler(logfile, when='D', backupCount=3)
+ except IOError, msg:
+ print >>sys.stderr, msg
+ else:
+ fhandler.setLevel(logfilelevel)
+ formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s')
+ fhandler.setFormatter(formatter)
+ logging.getLogger('').addHandler(fhandler)
+
+# main routine
+def main():
+ global_options, action = parse_options(sys.argv[1:])
+
+ # read config file
+ try:
+ config = read_config_file(global_options.conffile)
+ except IOError:
+ config = {}
+ except DZError, msg:
+ print >>sys.stderr, msg
+ sys.exit(1)
+
+ # read personal configuration file
+ uid = os.getuid()
+ if not is_root():
+ home_dir = pwd.getpwuid(uid)[5]
+ conf = os.path.join(home_dir, '.dzhandle.conf')
+ personal_conf['instances'] = os.path.join(home_dir, 'zope/instance')
+ personal_conf['products'] = os.path.join(home_dir, 'zope/products')
+ personal_conf['zeoinstances'] = os.path.join(home_dir, 'zope/zeo')
+ if os.path.exists(conf):
+ personal_conf.update(read_config_file(conf))
+ else:
+ write_config_file(conf, personal_conf)
+
+ # setup logging stuff
+ setup_logging(config.get('logfile', None),
+ config.get('logfilelevel', logging.INFO),
+ config.get('loglevel', logging.WARN)
+ )
+ logging.debug('dzhandle ' + ' '.join(sys.argv[1:]))
+
+ # check the arguments according to the action called
+ if action.check_args(global_options):
+ sys.exit(1)
+
+ # run the action and exit
+ rv = action.run(global_options)
+ sys.exit(rv)
+
+# call the main routine
+if __name__ == '__main__':
+ main()
Copied: zope-common/branches/etch/dzhandle.sgml (from rev 871, zope-common/tags/0.5.31/dzhandle.sgml)
===================================================================
--- zope-common/branches/etch/dzhandle.sgml (rev 0)
+++ zope-common/branches/etch/dzhandle.sgml 2007-05-14 11:51:31 UTC (rev 873)
@@ -0,0 +1,326 @@
+<!doctype refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
+
+ <!-- Fill in your name for FIRSTNAME and SURNAME. -->
+ <!ENTITY dhfirstname "<firstname>Fabio</firstname>">
+ <!ENTITY dhsurname "<surname>Tranchitella</surname>">
+ <!-- Please adjust the date whenever revising the manpage. -->
+ <!ENTITY dhdate "<date>Aug 19, 2005</date>">
+ <!-- SECTION should be 1-8, maybe w/ subsection other parameters are
+ allowed: see man(7), man(1). -->
+ <!ENTITY dhsection "<manvolnum>1</manvolnum>">
+ <!ENTITY dhemail "<email>kobold at debian.org</email>">
+ <!ENTITY dhusername "Fabio Tranchitella">
+ <!ENTITY dhucpackage "<refentrytitle>dzhandle</refentrytitle>">
+ <!ENTITY dhpackage "dzhandle">
+
+ <!ENTITY debian "<productname>Debian GNU/Linux</productname> and <productname>Ubuntu Linux</productname>">
+ <!ENTITY gnu "<acronym>GNU</acronym>">
+]>
+
+<refentry>
+ <docinfo>
+ <address>&dhemail;</address>
+ <author>&dhfirstname; &dhsurname;</author>
+ <copyright>
+ <year>2005</year>
+ <holder>&dhusername;</holder>
+ </copyright>
+ &dhdate;
+ </docinfo>
+
+ <refmeta>
+ &dhucpackage;
+ &dhsection;
+ </refmeta>
+
+ <refnamediv>
+ <refname>&dhpackage;</refname>
+ <refpurpose>Debian/Ubuntu Zope packages handling command line utility </refpurpose>
+ </refnamediv>
+
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>&dhpackage;</command>
+ <arg>options</arg>
+ <command>action</command>
+ <arg>action options</arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>
+ <command>&dhpackage;</command> is the command-line utility for handling
+ Zope servers, instances and products. It is a system administration tool and a
+ Debian/Ubuntu maintainer's helper script at the same time: using &dhpackage;
+ you can manage your Zope installations, create instances, add Zope products to
+ them, start and stop them, but it is also used by the Debian/Ubuntu Zope
+ packages for their installation and removal.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>OPTIONS</title>
+ <variablelist>
+ <varlistentry>
+ <term><option>-h, --help</option></term>
+ <listitem><para>Print usage information and exit</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>-f, --force</option></term>
+ <listitem><para>Force things, for example file overwriting or removing</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>-z, --zope-version=ZVERSIONS</option></term>
+ <listitem><para>limit actions to a comma separated list of zope versions; default behaviour is to act on all zope versions available</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>-u, --user=USER[:GROUP]</option></term>
+ <listitem><para>User/Group ownership for new and copied files</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>-c, --config-file=FILE</option></term>
+ <listitem><para>Configuration file; default is /etc/dzhandle.conf</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>-v, --verbose</option></term>
+ <listitem><para>Enable verbose mode (not yet implemented)</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>-n, --dry-run</option></term>
+ <listitem><para>Do not execute commands, print only them (not yet implemented)</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>ACTIONS (instances handling)</title>
+ <variablelist>
+ <varlistentry>
+ <term><option>make-instance</option> <instance></term>
+ <listitem>
+ <para>Create a new instance running Zope version specific mkzopeinstance.</para>
+ <variablelist>
+ <varlistentry>
+ <term>-m AMODE, --addon-mode=AMODE</term>
+ <listitem><para>Which products and extensions will be installed: `all' means every product
+ or extension available will be automatically installed, `manual' means that the products
+ or extensions will be installed manually through &dhpackage;.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-t ATECHNIQUE, --addon-install-technique=ATECHNIQUE</term>
+ <listitem><para>How to install addons: `linked' means the product or extension directory
+ will be symlinked into the instance home, `tree-linked' means the directory structure will
+ be re-created and then files symlinked, `copied' means the file will be copied into the
+ instance home.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-r RESTART, --restart=RESTART</term>
+ <listitem><para>when to restart on configuration of new products or extensions: `configuring'
+ means after every product or extension configuration, `end' means at the end of the installation
+ of all packages, `manually' means no automatic restart will happen.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-u USER, --user=USER</term>
+ <listitem><para>user and password for the initial user (in the form `user:password'); these
+ settigs can be modified later using zpasswd.py utility from Zope.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>--service-user=SRVUSER</term>
+ <listitem><para>system user used to run this instance (in the form `user:groupdefault', the
+ default is `zope:zope'); this setting can be modified later editing the zope.conf file of
+ the instance.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>--service-port=SRVPORT</term>
+ <listitem><para>HTTP port used to run this instance (default 9673); this setting can be modified
+ later editing the zope.conf file of the instance.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>remove-instance</option> <instance></term>
+ <listitem><para>Remove an instance (except data files) and mark it as removed.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>purge-instance</option> <instance></term>
+ <listitem><para>Purge files for an instance (including data files).</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>show-instance</option> <instance></term>
+ <listitem><para>Print a short summary about an instance.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>list-instances</option></term>
+ <listitem><para>Print the list of available instances.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>instance-addon-mode</option> <instance> [<mode>]</term>
+ <listitem><para>Get or set an addon-mode for an instance. </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>instance-addon-technique</option> <instance> [<technique>]</term>
+ <listitem><para>Get or set an addon-install-technique for an instance.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>instance-restart-policy</option> <instance> [<restart-policy>]</term>
+ <listitem><para>Get or set a policy on addon-installation for an instance.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>zopectl</option> <instance> <zdctl-action> [<zdctl options>]</term>
+ <listitem><para>Call a zopectl action (e.g. `start', `stop' or `restart') for a given instance.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>restart-pending-instances</option></term>
+ <listitem><para>Restart instances with `restart-pending' markers.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>ACTIONS (ZEO instances handling)</title>
+ <variablelist>
+ <varlistentry>
+ <term><option>make-zeoinstance</option> <instance></term>
+ <listitem>
+ <para>Create a new instance running Zope version specific mkzeoinstance.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>purge-zeoinstance</option> <instance></term>
+ <listitem><para>Purge files for a ZEO instance (including data files).</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>list-zeoinstances</option></term>
+ <listitem><para>Print the list of available ZEO instances.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>zeoctl</option> <instance> <zeotl-action> [<zeotl options>]</term>
+ <listitem><para>Call a zeoctl action (e.g. `start', `stop' or `restart') for a given ZEO instance.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+
+ <refsect1>
+ <title>ACTIONS (products and extensions handling)</title>
+ <variablelist>
+ <varlistentry>
+ <term><option>list-products</option>, <option>list-extensions</option></term>
+ <listitem><para>show all products or extensions managed by dzhandle</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>add-product</option>, <option>add-extension</option></term>
+ <listitem>
+ <para>add a product or extension to an instance</para>
+ <variablelist>
+ <varlistentry>
+ <term>-l, --lazy</term>
+ <listitem><para>Add missing addons only (error on manually installed addons).</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-t ATECHNIQUE, --addon-install-technique=ATECHNIQUE</term>
+ <listitem><para>How to install the specified addons.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>remove-product</option>, <option>remove-extension</option></term>
+ <listitem>
+ <para>remove a product or extension from an instance</para>
+ <variablelist>
+ <varlistentry>
+ <term>-l, --lazy</term>
+ <listitem><para>Do not complain about already removed addons.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-f, --force</term>
+ <listitem><para>Force removal of the addons.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>ACTIONS (for Zope products maintainers)</title>
+ <para>
+ The following actions should be used inside the maintainer scripts of Debian/Ubuntu
+ packages of Zope products and extensions. If the package uses zope-debhelper's
+ <productname>dh_installzope</productname>, these actions will be automatically included.
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term><option>dinstall-extension, dinstall-product</option></term>
+ <listitem><para>install a packaged extension/product</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>dremove-extension, dremove-product</option></term>
+ <listitem><para>remove a packaged extension/product</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>postinst-extension, postinst-product</option></term>
+ <listitem><para>handle postinst of a packaged extension/product</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>postrm-extension, postrm-product</option></term>
+ <listitem><para>handle postrm of a packaged extension/product</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>preinst-extension, preinst-product</option></term>
+ <listitem><para>handle preinst of a packaged extension/product</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>prerm-extension, prerm-product</option></term>
+ <listitem><para>handle prerm of a packaged extension/product</para></listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>SEE ALSO</title>
+ <para>dh_installzope(1), dh_installzopeinstance(1)</para>
+ </refsect1>
+
+ <refsect1>
+ <title>AUTHOR</title>
+
+ <para>This manual page was written by &dhusername; <&dhemail;> for
+ the &debian; systems (but may be used by others).</para>
+
+ <para>Permission is granted to copy, distribute and/or modify
+ this document under the terms of the <acronym>GNU</acronym> Free
+ Documentation License, Version 1.1 or any later version
+ published by the Free Software Foundation; with no Invariant
+ Sections, no Front-Cover Texts and no Back-Cover Texts. A copy
+ of the license can be found under
+ <filename>/usr/share/common-licenses/FDL</filename>.</para>
+ </refsect1>
+
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:2
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:nil
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+-->
More information about the pkg-zope-commits
mailing list