CedarBackup2.config

Package CedarBackup2 :: Module config |
[hide private]
[frames] | no frames]

# Source Code for Module CedarBackup2.config

       1  # -*- coding: iso-8859-1 -*-
       2  # vim: set ft=python ts=3 sw=3 expandtab:
       3  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
       4  #
       5  #              C E D A R
       6  #          S O L U T I O N S       "Software done right."
       7  #           S O F T W A R E
       8  #
       9  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
      10  #
      11  # Copyright (c) 2004-2008,2010 Kenneth J. Pronovici.
      12  # All rights reserved.
      13  #
      14  # This program is free software; you can redistribute it and/or
      15  # modify it under the terms of the GNU General Public License,
      16  # Version 2, as published by the Free Software Foundation.
      17  #
      18  # This program is distributed in the hope that it will be useful,
      19  # but WITHOUT ANY WARRANTY; without even the implied warranty of
      20  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      21  #
      22  # Copies of the GNU General Public License are available from
      23  # the Free Software Foundation website, http://www.gnu.org/.
      24  #
      25  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
      26  #
      27  # Author   : Kenneth J. Pronovici <pronovic@ieee.org>
      28  # Language : Python (>= 2.5)
      29  # Project  : Cedar Backup, release 2
      30  # Revision : $Id: config.py 1041 2013-05-10 02:05:13Z pronovic $
      31  # Purpose  : Provides configuration-related objects.
      32  #
      33  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
      34
      35  ########################################################################
      36  # Module documentation
      37  ########################################################################
      38
      39  """
      40  Provides configuration-related objects.
      41
      42  Summary
      43  =======
      44
      45     Cedar Backup stores all of its configuration in an XML document typically
      46     called C{cback.conf}.  The standard location for this document is in
      47     C{/etc}, but users can specify a different location if they want to.
      48
      49     The C{Config} class is a Python object representation of a Cedar Backup XML
      50     configuration file.  The representation is two-way: XML data can be used to
      51     create a C{Config} object, and then changes to the object can be propogated
      52     back to disk.  A C{Config} object can even be used to create a configuration
      53     file from scratch programmatically.
      54
      55     The C{Config} class is intended to be the only Python-language interface to
      56     Cedar Backup configuration on disk.  Cedar Backup will use the class as its
      57     internal representation of configuration, and applications external to Cedar
      58     Backup itself (such as a hypothetical third-party configuration tool written
      59     in Python or a third party extension module) should also use the class when
      60     they need to read and write configuration files.
      61
      62  Backwards Compatibility
      63  =======================
      64
      65     The configuration file format has changed between Cedar Backup 1.x and Cedar
      66     Backup 2.x.  Any Cedar Backup 1.x configuration file is also a valid Cedar
      67     Backup 2.x configuration file.  However, it doesn't work to go the other
      68     direction, as the 2.x configuration files contains additional configuration
      69     is not accepted by older versions of the software.
      70
      71  XML Configuration Structure
      72  ===========================
      73
      74     A C{Config} object can either be created "empty", or can be created based on
      75     XML input (either in the form of a string or read in from a file on disk).
      76     Generally speaking, the XML input I{must} result in a C{Config} object which
      77     passes the validations laid out below in the I{Validation} section.
      78
      79     An XML configuration file is composed of seven sections:
      80
      81        - I{reference}: specifies reference information about the file (author, revision, etc)
      82        - I{extensions}: specifies mappings to Cedar Backup extensions (external code)
      83        - I{options}: specifies global configuration options
      84        - I{peers}: specifies the set of peers in a master's backup pool
      85        - I{collect}: specifies configuration related to the collect action
      86        - I{stage}: specifies configuration related to the stage action
      87        - I{store}: specifies configuration related to the store action
      88        - I{purge}: specifies configuration related to the purge action
      89
      90     Each section is represented by an class in this module, and then the overall
      91     C{Config} class is a composition of the various other classes.
      92
      93     Any configuration section that is missing in the XML document (or has not
      94     been filled into an "empty" document) will just be set to C{None} in the
      95     object representation.  The same goes for individual fields within each
      96     configuration section.  Keep in mind that the document might not be
      97     completely valid if some sections or fields aren't filled in - but that
      98     won't matter until validation takes place (see the I{Validation} section
      99     below).
     100
     101  Unicode vs. String Data
     102  =======================
     103
     104     By default, all string data that comes out of XML documents in Python is
     105     unicode data (i.e. C{u"whatever"}).  This is fine for many things, but when
     106     it comes to filesystem paths, it can cause us some problems.  We really want
     107     strings to be encoded in the filesystem encoding rather than being unicode.
     108     So, most elements in configuration which represent filesystem paths are
     109     coverted to plain strings using L{util.encodePath}.  The main exception is
     110     the various C{absoluteExcludePath} and C{relativeExcludePath} lists.  These
     111     are I{not} converted, because they are generally only used for filtering,
     112     not for filesystem operations.
     113
     114  Validation
     115  ==========
     116
     117     There are two main levels of validation in the C{Config} class and its
     118     children.  The first is field-level validation.  Field-level validation
     119     comes into play when a given field in an object is assigned to or updated.
     120     We use Python's C{property} functionality to enforce specific validations on
     121     field values, and in some places we even use customized list classes to
     122     enforce validations on list members.  You should expect to catch a
     123     C{ValueError} exception when making assignments to configuration class
     124     fields.
     125
     126     The second level of validation is post-completion validation.  Certain
     127     validations don't make sense until a document is fully "complete".  We don't
     128     want these validations to apply all of the time, because it would make
     129     building up a document from scratch a real pain.  For instance, we might
     130     have to do things in the right order to keep from throwing exceptions, etc.
     131
     132     All of these post-completion validations are encapsulated in the
     133     L{Config.validate} method.  This method can be called at any time by a
     134     client, and will always be called immediately after creating a C{Config}
     135     object from XML data and before exporting a C{Config} object to XML.  This
     136     way, we get decent ease-of-use but we also don't accept or emit invalid
     137     configuration files.
     138
     139     The L{Config.validate} implementation actually takes two passes to
     140     completely validate a configuration document.  The first pass at validation
     141     is to ensure that the proper sections are filled into the document.  There
     142     are default requirements, but the caller has the opportunity to override
     143     these defaults.
     144
     145     The second pass at validation ensures that any filled-in section contains
     146     valid data.  Any section which is not set to C{None} is validated according
     147     to the rules for that section (see below).
     148
     149     I{Reference Validations}
     150
     151     No validations.
     152
     153     I{Extensions Validations}
     154
     155     The list of actions may be either C{None} or an empty list C{[]} if desired.
     156     Each extended action must include a name, a module and a function.  Then, an
     157     extended action must include either an index or dependency information.
     158     Which one is required depends on which order mode is configured.
     159
     160     I{Options Validations}
     161
     162     All fields must be filled in except the rsh command.  The rcp and rsh
     163     commands are used as default values for all remote peers.  Remote peers can
     164     also rely on the backup user as the default remote user name if they choose.
     165
     166     I{Peers Validations}
     167
     168     Local peers must be completely filled in, including both name and collect
     169     directory.  Remote peers must also fill in the name and collect directory,
     170     but can leave the remote user and rcp command unset.  In this case, the
     171     remote user is assumed to match the backup user from the options section and
     172     rcp command is taken directly from the options section.
     173
     174     I{Collect Validations}
     175
     176     The target directory must be filled in.  The collect mode, archive mode and
     177     ignore file are all optional.  The list of absolute paths to exclude and
     178     patterns to exclude may be either C{None} or an empty list C{[]} if desired.
     179
     180     Each collect directory entry must contain an absolute path to collect, and
     181     then must either be able to take collect mode, archive mode and ignore file
     182     configuration from the parent C{CollectConfig} object, or must set each
     183     value on its own.  The list of absolute paths to exclude, relative paths to
     184     exclude and patterns to exclude may be either C{None} or an empty list C{[]}
     185     if desired.  Any list of absolute paths to exclude or patterns to exclude
     186     will be combined with the same list in the C{CollectConfig} object to make
     187     the complete list for a given directory.
     188
     189     I{Stage Validations}
     190
     191     The target directory must be filled in.  There must be at least one peer
     192     (remote or local) between the two lists of peers.  A list with no entries
     193     can be either C{None} or an empty list C{[]} if desired.
     194
     195     If a set of peers is provided, this configuration completely overrides
     196     configuration in the peers configuration section, and the same validations
     197     apply.
     198
     199     I{Store Validations}
     200
     201     The device type and drive speed are optional, and all other values are
     202     required (missing booleans will be set to defaults, which is OK).
     203
     204     The image writer functionality in the C{writer} module is supposed to be
     205     able to handle a device speed of C{None}.  Any caller which needs a "real"
     206     (non-C{None}) value for the device type can use C{DEFAULT_DEVICE_TYPE},
     207     which is guaranteed to be sensible.
     208
     209     I{Purge Validations}
     210
     211     The list of purge directories may be either C{None} or an empty list C{[]}
     212     if desired.  All purge directories must contain a path and a retain days
     213     value.
     214
     215  @sort: ActionDependencies, ActionHook, PreActionHook, PostActionHook,
     216         ExtendedAction, CommandOverride, CollectFile, CollectDir, PurgeDir, LocalPeer,
     217         RemotePeer, ReferenceConfig, ExtensionsConfig, OptionsConfig, PeersConfig,
     218         CollectConfig, StageConfig, StoreConfig, PurgeConfig, Config,
     219         DEFAULT_DEVICE_TYPE, DEFAULT_MEDIA_TYPE,
     220         VALID_DEVICE_TYPES, VALID_MEDIA_TYPES,
     221         VALID_COLLECT_MODES, VALID_ARCHIVE_MODES,
     222         VALID_ORDER_MODES
     223
     224  @var DEFAULT_DEVICE_TYPE: The default device type.
     225  @var DEFAULT_MEDIA_TYPE: The default media type.
     226  @var VALID_DEVICE_TYPES: List of valid device types.
     227  @var VALID_MEDIA_TYPES: List of valid media types.
     228  @var VALID_COLLECT_MODES: List of valid collect modes.
     229  @var VALID_COMPRESS_MODES: List of valid compress modes.
     230  @var VALID_ARCHIVE_MODES: List of valid archive modes.
     231  @var VALID_ORDER_MODES: List of valid extension order modes.
     232
     233  @author: Kenneth J. Pronovici <pronovic@ieee.org>
     234  """
     235
     236  ########################################################################
     237  # Imported modules
     238  ########################################################################
     239
     240  # System modules
     241  import os
     242  import re
     243  import logging
     244
     245  # Cedar Backup modules
     246  from CedarBackup2.writers.util import validateScsiId, validateDriveSpeed
     247  from CedarBackup2.util import UnorderedList, AbsolutePathList, ObjectTypeList, parseCommaSeparatedString
     248  from CedarBackup2.util import RegexMatchList, RegexList, encodePath, checkUnique
     249  from CedarBackup2.util import convertSize, UNIT_BYTES, UNIT_KBYTES, UNIT_MBYTES, UNIT_GBYTES
     250  from CedarBackup2.xmlutil import isElement, readChildren, readFirstChild
     251  from CedarBackup2.xmlutil import readStringList, readString, readInteger, readBoolean
     252  from CedarBackup2.xmlutil import addContainerNode, addStringNode, addIntegerNode, addBooleanNode
     253  from CedarBackup2.xmlutil import createInputDom, createOutputDom, serializeDom
     254
     255
     256  ########################################################################
     257  # Module-wide constants and variables
     258  ########################################################################
     259
     260  logger = logging.getLogger("CedarBackup2.log.config")
     261
     262  DEFAULT_DEVICE_TYPE   = "cdwriter"
     263  DEFAULT_MEDIA_TYPE    = "cdrw-74"
     264
     265  VALID_DEVICE_TYPES    = [ "cdwriter", "dvdwriter", ]
     266  VALID_CD_MEDIA_TYPES  = [ "cdr-74", "cdrw-74", "cdr-80", "cdrw-80", ]
     267  VALID_DVD_MEDIA_TYPES = [ "dvd+r", "dvd+rw", ]
     268  VALID_MEDIA_TYPES     = VALID_CD_MEDIA_TYPES + VALID_DVD_MEDIA_TYPES
     269  VALID_COLLECT_MODES   = [ "daily", "weekly", "incr", ]
     270  VALID_ARCHIVE_MODES   = [ "tar", "targz", "tarbz2", ]
     271  VALID_COMPRESS_MODES  = [ "none", "gzip", "bzip2", ]
     272  VALID_ORDER_MODES     = [ "index", "dependency", ]
     273  VALID_BLANK_MODES     = [ "daily", "weekly", ]
     274  VALID_BYTE_UNITS      = [ UNIT_BYTES, UNIT_KBYTES, UNIT_MBYTES, UNIT_GBYTES, ]
     275  VALID_FAILURE_MODES   = [ "none", "all", "daily", "weekly", ]
     276
     277  REWRITABLE_MEDIA_TYPES = [ "cdrw-74", "cdrw-80", "dvd+rw", ]
     278
     279  ACTION_NAME_REGEX     = r"^[a-z0-9]*$"
     280
     281
     282  ########################################################################
     283  # ByteQuantity class definition
     284  ########################################################################
     285
     286 -class ByteQuantity(object):
     287
     288     """
     289     Class representing a byte quantity.
     290
     291     A byte quantity has both a quantity and a byte-related unit.  Units are
     292     maintained using the constants from util.py.
     293
     294     The quantity is maintained internally as a string so that issues of
     295     precision can be avoided.  It really isn't possible to store a floating
     296     point number here while being able to losslessly translate back and forth
     297     between XML and object representations.  (Perhaps the Python 2.4 Decimal
     298     class would have been an option, but I originally wanted to stay compatible
     299     with Python 2.3.)
     300
     301     Even though the quantity is maintained as a string, the string must be in a
     302     valid floating point positive number.  Technically, any floating point
     303     string format supported by Python is allowble.  However, it does not make
     304     sense to have a negative quantity of bytes in this context.
     305
     306     @sort: __init__, __repr__, __str__, __cmp__, quantity, units
     307     """
     308
     309 -   def __init__(self, quantity=None, units=None):
     310        """
     311        Constructor for the C{ByteQuantity} class.
     312
     313        @param quantity: Quantity of bytes, as string ("1.25")
     314        @param units: Unit of bytes, one of VALID_BYTE_UNITS
     315
     316        @raise ValueError: If one of the values is invalid.
     317        """
     318        self._quantity = None
     319        self._units = None
     320        self.quantity = quantity
     321        self.units = units
     322
     323 -   def __repr__(self):
     324        """
     325        Official string representation for class instance.
     326        """
     327        return "ByteQuantity(%s, %s)" % (self.quantity, self.units)
     328
     329 -   def __str__(self):
     330        """
     331        Informal string representation for class instance.
     332        """
     333        return self.__repr__()
     334
     335 -   def __cmp__(self, other):
     336        """
     337        Definition of equals operator for this class.
     338        Lists within this class are "unordered" for equality comparisons.
     339        @param other: Other object to compare to.
     340        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
     341        """
     342        if other is None:
     343           return 1
     344        if self.quantity != other.quantity:
     345           if self.quantity < other.quantity:
     346              return -1
     347           else:
     348              return 1
     349        if self.units != other.units:
     350           if self.units < other.units:
     351              return -1
     352           else:
     353              return 1
     354        return 0
     355
     356 -   def _setQuantity(self, value):
     357        """
     358        Property target used to set the quantity
     359        The value must be a non-empty string if it is not C{None}.
     360        @raise ValueError: If the value is an empty string.
     361        @raise ValueError: If the value is not a valid floating point number
     362        @raise ValueError: If the value is less than zero
     363        """
     364        if value is not None:
     365           if len(value) < 1:
     366              raise ValueError("Quantity must be a non-empty string.")
     367           floatValue = float(value)
     368           if floatValue < 0.0:
     369              raise ValueError("Quantity cannot be negative.")
     370        self._quantity = value # keep around string
     371
     372 -   def _getQuantity(self):
     373        """
     374        Property target used to get the quantity.
     375        """
     376        return self._quantity
     377
     378 -   def _setUnits(self, value):
     379        """
     380        Property target used to set the units value.
     381        If not C{None}, the units value must be one of the values in L{VALID_BYTE_UNITS}.
     382        @raise ValueError: If the value is not valid.
     383        """
     384        if value is not None:
     385           if value not in VALID_BYTE_UNITS:
     386              raise ValueError("Units value must be one of %s." % VALID_BYTE_UNITS)
     387        self._units = value
     388
     389 -   def _getUnits(self):
     390        """
     391        Property target used to get the units value.
     392        """
     393        return self._units
     394
     395 -   def _getBytes(self):
     396        """
     397        Property target used to return the byte quantity as a floating point number.
     398        If there is no quantity set, then a value of 0.0 is returned.
     399        """
     400        if self.quantity is not None and self.units is not None:
     401           return convertSize(self.quantity, self.units, UNIT_BYTES)
     402        return 0.0
     403
     404     quantity = property(_getQuantity, _setQuantity, None, doc="Byte quantity, as a string")
     405     units = property(_getUnits, _setUnits, None, doc="Units for byte quantity, for instance UNIT_BYTES")
     406     bytes = property(_getBytes, None, None, doc="Byte quantity, as a floating point number.")
     407
     408
     409  ########################################################################
     410  # ActionDependencies class definition
     411  ########################################################################
     412
     413 -class ActionDependencies(object):
     414
     415     """
     416     Class representing dependencies associated with an extended action.
     417
     418     Execution ordering for extended actions is done in one of two ways: either by using
     419     index values (lower index gets run first) or by having the extended action specify
     420     dependencies in terms of other named actions.  This class encapsulates the dependency
     421     information for an extended action.
     422
     423     The following restrictions exist on data in this class:
     424
     425        - Any action name must be a non-empty string matching C{ACTION_NAME_REGEX}
     426
     427     @sort: __init__, __repr__, __str__, __cmp__, beforeList, afterList
     428     """
     429
     430 -   def __init__(self, beforeList=None, afterList=None):
     431        """
     432        Constructor for the C{ActionDependencies} class.
     433
     434        @param beforeList: List of named actions that this action must be run before
     435        @param afterList: List of named actions that this action must be run after
     436
     437        @raise ValueError: If one of the values is invalid.
     438        """
     439        self._beforeList = None
     440        self._afterList = None
     441        self.beforeList = beforeList
     442        self.afterList = afterList
     443
     444 -   def __repr__(self):
     445        """
     446        Official string representation for class instance.
     447        """
     448        return "ActionDependencies(%s, %s)" % (self.beforeList, self.afterList)
     449
     450 -   def __str__(self):
     451        """
     452        Informal string representation for class instance.
     453        """
     454        return self.__repr__()
     455
     456 -   def __cmp__(self, other):
     457        """
     458        Definition of equals operator for this class.
     459        @param other: Other object to compare to.
     460        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
     461        """
     462        if other is None:
     463           return 1
     464        if self.beforeList != other.beforeList:
     465           if self.beforeList < other.beforeList:
     466              return -1
     467           else:
     468              return 1
     469        if self.afterList != other.afterList:
     470           if self.afterList < other.afterList:
     471              return -1
     472           else:
     473              return 1
     474        return 0
     475
     476 -   def _setBeforeList(self, value):
     477        """
     478        Property target used to set the "run before" list.
     479        Either the value must be C{None} or each element must be a string matching ACTION_NAME_REGEX.
     480        @raise ValueError: If the value does not match the regular expression.
     481        """
     482        if value is None:
     483           self._beforeList = None
     484        else:
     485           try:
     486              saved = self._beforeList
     487              self._beforeList = RegexMatchList(ACTION_NAME_REGEX, emptyAllowed=False, prefix="Action name")
     488              self._beforeList.extend(value)
     489           except Exception, e:
     490              self._beforeList = saved
     491              raise e
     492
     493 -   def _getBeforeList(self):
     494        """
     495        Property target used to get the "run before" list.
     496        """
     497        return self._beforeList
     498
     499 -   def _setAfterList(self, value):
     500        """
     501        Property target used to set the "run after" list.
     502        Either the value must be C{None} or each element must be a string matching ACTION_NAME_REGEX.
     503        @raise ValueError: If the value does not match the regular expression.
     504        """
     505        if value is None:
     506           self._afterList = None
     507        else:
     508           try:
     509              saved = self._afterList
     510              self._afterList = RegexMatchList(ACTION_NAME_REGEX, emptyAllowed=False, prefix="Action name")
     511              self._afterList.extend(value)
     512           except Exception, e:
     513              self._afterList = saved
     514              raise e
     515
     516 -   def _getAfterList(self):
     517        """
     518        Property target used to get the "run after" list.
     519        """
     520        return self._afterList
     521
     522     beforeList = property(_getBeforeList, _setBeforeList, None, "List of named actions that this action must be run before.")
     523     afterList = property(_getAfterList, _setAfterList, None, "List of named actions that this action must be run after.")
     524
     525
     526  ########################################################################
     527  # ActionHook class definition
     528  ########################################################################
     529
     530 -class ActionHook(object):
     531
     532     """
     533     Class representing a hook associated with an action.
     534
     535     A hook associated with an action is a shell command to be executed either
     536     before or after a named action is executed.
     537
     538     The following restrictions exist on data in this class:
     539
     540        - The action name must be a non-empty string matching C{ACTION_NAME_REGEX}
     541        - The shell command must be a non-empty string.
     542
     543     The internal C{before} and C{after} instance variables are always set to
     544     False in this parent class.
     545
     546     @sort: __init__, __repr__, __str__, __cmp__, action, command, before, after
     547     """
     548
     549 -   def __init__(self, action=None, command=None):
     550        """
     551        Constructor for the C{ActionHook} class.
     552
     553        @param action: Action this hook is associated with
     554        @param command: Shell command to execute
     555
     556        @raise ValueError: If one of the values is invalid.
     557        """
     558        self._action = None
     559        self._command = None
     560        self._before = False
     561        self._after = False
     562        self.action = action
     563        self.command = command
     564
     565 -   def __repr__(self):
     566        """
     567        Official string representation for class instance.
     568        """
     569        return "ActionHook(%s, %s, %s, %s)" % (self.action, self.command, self.before, self.after)
     570
     571 -   def __str__(self):
     572        """
     573        Informal string representation for class instance.
     574        """
     575        return self.__repr__()
     576
     577 -   def __cmp__(self, other):
     578        """
     579        Definition of equals operator for this class.
     580        @param other: Other object to compare to.
     581        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
     582        """
     583        if other is None:
     584           return 1
     585        if self.action != other.action:
     586           if self.action < other.action:
     587              return -1
     588           else:
     589              return 1
     590        if self.command != other.command:
     591           if self.command < other.command:
     592              return -1
     593           else:
     594              return 1
     595        if self.before != other.before:
     596           if self.before < other.before:
     597              return -1
     598           else:
     599              return 1
     600        if self.after != other.after:
     601           if self.after < other.after:
     602              return -1
     603           else:
     604              return 1
     605        return 0
     606
     607 -   def _setAction(self, value):
     608        """
     609        Property target used to set the action name.
     610        The value must be a non-empty string if it is not C{None}.
     611        It must also consist only of lower-case letters and digits.
     612        @raise ValueError: If the value is an empty string.
     613        """
     614        pattern = re.compile(ACTION_NAME_REGEX)
     615        if value is not None:
     616           if len(value) < 1:
     617              raise ValueError("The action name must be a non-empty string.")
     618           if not pattern.search(value):
     619              raise ValueError("The action name must consist of only lower-case letters and digits.")
     620        self._action = value
     621
     622 -   def _getAction(self):
     623        """
     624        Property target used to get the action name.
     625        """
     626        return self._action
     627
     628 -   def _setCommand(self, value):
     629        """
     630        Property target used to set the command.
     631        The value must be a non-empty string if it is not C{None}.
     632        @raise ValueError: If the value is an empty string.
     633        """
     634        if value is not None:
     635           if len(value) < 1:
     636              raise ValueError("The command must be a non-empty string.")
     637        self._command = value
     638
     639 -   def _getCommand(self):
     640        """
     641        Property target used to get the command.
     642        """
     643        return self._command
     644
     645 -   def _getBefore(self):
     646        """
     647        Property target used to get the before flag.
     648        """
     649        return self._before
     650
     651 -   def _getAfter(self):
     652        """
     653        Property target used to get the after flag.
     654        """
     655        return self._after
     656
     657     action = property(_getAction, _setAction, None, "Action this hook is associated with.")
     658     command = property(_getCommand, _setCommand, None, "Shell command to execute.")
     659     before = property(_getBefore, None, None, "Indicates whether command should be executed before action.")
     660     after = property(_getAfter, None, None, "Indicates whether command should be executed after action.")
     661
     662 -class PreActionHook(ActionHook):
     663
     664     """
     665     Class representing a pre-action hook associated with an action.
     666
     667     A hook associated with an action is a shell command to be executed either
     668     before or after a named action is executed.  In this case, a pre-action hook
     669     is executed before the named action.
     670
     671     The following restrictions exist on data in this class:
     672
     673        - The action name must be a non-empty string consisting of lower-case letters and digits.
     674        - The shell command must be a non-empty string.
     675
     676     The internal C{before} instance variable is always set to True in this
     677     class.
     678
     679     @sort: __init__, __repr__, __str__, __cmp__, action, command, before, after
     680     """
     681
     682 -   def __init__(self, action=None, command=None):
     683        """
     684        Constructor for the C{PreActionHook} class.
     685
     686        @param action: Action this hook is associated with
     687        @param command: Shell command to execute
     688
     689        @raise ValueError: If one of the values is invalid.
     690        """
     691        ActionHook.__init__(self, action, command)
     692        self._before = True
     693
     694 -   def __repr__(self):
     695        """
     696        Official string representation for class instance.
     697        """
     698        return "PreActionHook(%s, %s, %s, %s)" % (self.action, self.command, self.before, self.after)
     699
     700 -class PostActionHook(ActionHook):
     701
     702     """
     703     Class representing a pre-action hook associated with an action.
     704
     705     A hook associated with an action is a shell command to be executed either
     706     before or after a named action is executed.  In this case, a post-action hook
     707     is executed after the named action.
     708
     709     The following restrictions exist on data in this class:
     710
     711        - The action name must be a non-empty string consisting of lower-case letters and digits.
     712        - The shell command must be a non-empty string.
     713
     714     The internal C{before} instance variable is always set to True in this
     715     class.
     716
     717     @sort: __init__, __repr__, __str__, __cmp__, action, command, before, after
     718     """
     719
     720 -   def __init__(self, action=None, command=None):
     721        """
     722        Constructor for the C{PostActionHook} class.
     723
     724        @param action: Action this hook is associated with
     725        @param command: Shell command to execute
     726
     727        @raise ValueError: If one of the values is invalid.
     728        """
     729        ActionHook.__init__(self, action, command)
     730        self._after = True
     731
     732 -   def __repr__(self):
     733        """
     734        Official string representation for class instance.
     735        """
     736        return "PostActionHook(%s, %s, %s, %s)" % (self.action, self.command, self.before, self.after)
     737
     738
     739  ########################################################################
     740  # BlankBehavior class definition
     741  ########################################################################
     742
     743 -class BlankBehavior(object):
     744
     745     """
     746     Class representing optimized store-action media blanking behavior.
     747
     748     The following restrictions exist on data in this class:
     749
     750        - The blanking mode must be a one of the values in L{VALID_BLANK_MODES}
     751        - The blanking factor must be a positive floating point number
     752
     753     @sort: __init__, __repr__, __str__, __cmp__, blankMode, blankFactor
     754     """
     755
     756 -   def __init__(self, blankMode=None, blankFactor=None):
     757        """
     758        Constructor for the C{BlankBehavior} class.
     759
     760        @param blankMode: Blanking mode
     761        @param blankFactor: Blanking factor
     762
     763        @raise ValueError: If one of the values is invalid.
     764        """
     765        self._blankMode = None
     766        self._blankFactor = None
     767        self.blankMode = blankMode
     768        self.blankFactor = blankFactor
     769
     770 -   def __repr__(self):
     771        """
     772        Official string representation for class instance.
     773        """
     774        return "BlankBehavior(%s, %s)" % (self.blankMode, self.blankFactor)
     775
     776 -   def __str__(self):
     777        """
     778        Informal string representation for class instance.
     779        """
     780        return self.__repr__()
     781
     782 -   def __cmp__(self, other):
     783        """
     784        Definition of equals operator for this class.
     785        @param other: Other object to compare to.
     786        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
     787        """
     788        if other is None:
     789           return 1
     790        if self.blankMode != other.blankMode:
     791           if self.blankMode < other.blankMode:
     792              return -1
     793           else:
     794              return 1
     795        if self.blankFactor != other.blankFactor:
     796           if self.blankFactor < other.blankFactor:
     797              return -1
     798           else:
     799              return 1
     800        return 0
     801
     802 -   def _setBlankMode(self, value):
     803        """
     804        Property target used to set the blanking mode.
     805        The value must be one of L{VALID_BLANK_MODES}.
     806        @raise ValueError: If the value is not valid.
     807        """
     808        if value is not None:
     809           if value not in VALID_BLANK_MODES:
     810              raise ValueError("Blanking mode must be one of %s." % VALID_BLANK_MODES)
     811        self._blankMode = value
     812
     813 -   def _getBlankMode(self):
     814        """
     815        Property target used to get the blanking mode.
     816        """
     817        return self._blankMode
     818
     819 -   def _setBlankFactor(self, value):
     820        """
     821        Property target used to set the blanking factor.
     822        The value must be a non-empty string if it is not C{None}.
     823        @raise ValueError: If the value is an empty string.
     824        @raise ValueError: If the value is not a valid floating point number
     825        @raise ValueError: If the value is less than zero
     826        """
     827        if value is not None:
     828           if len(value) < 1:
     829              raise ValueError("Blanking factor must be a non-empty string.")
     830           floatValue = float(value)
     831           if floatValue < 0.0:
     832              raise ValueError("Blanking factor cannot be negative.")
     833        self._blankFactor = value # keep around string
     834
     835 -   def _getBlankFactor(self):
     836        """
     837        Property target used to get the blanking factor.
     838        """
     839        return self._blankFactor
     840
     841     blankMode = property(_getBlankMode, _setBlankMode, None, "Blanking mode")
     842     blankFactor = property(_getBlankFactor, _setBlankFactor, None, "Blanking factor")
     843
     844
     845  ########################################################################
     846  # ExtendedAction class definition
     847  ########################################################################
     848
     849 -class ExtendedAction(object):
     850
     851     """
     852     Class representing an extended action.
     853
     854     Essentially, an extended action needs to allow the following to happen::
     855
     856        exec("from %s import %s" % (module, function))
     857        exec("%s(action, configPath")" % function)
     858
     859     The following restrictions exist on data in this class:
     860
     861        - The action name must be a non-empty string consisting of lower-case letters and digits.
     862        - The module must be a non-empty string and a valid Python identifier.
     863        - The function must be an on-empty string and a valid Python identifier.
     864        - If set, the index must be a positive integer.
     865        - If set, the dependencies attribute must be an C{ActionDependencies} object.
     866
     867     @sort: __init__, __repr__, __str__, __cmp__, name, module, function, index, dependencies
     868     """
     869
     870 -   def __init__(self, name=None, module=None, function=None, index=None, dependencies=None):
     871        """
     872        Constructor for the C{ExtendedAction} class.
     873
     874        @param name: Name of the extended action
     875        @param module: Name of the module containing the extended action function
     876        @param function: Name of the extended action function
     877        @param index: Index of action, used for execution ordering
     878        @param dependencies: Dependencies for action, used for execution ordering
     879
     880        @raise ValueError: If one of the values is invalid.
     881        """
     882        self._name = None
     883        self._module = None
     884        self._function = None
     885        self._index = None
     886        self._dependencies = None
     887        self.name = name
     888        self.module = module
     889        self.function = function
     890        self.index = index
     891        self.dependencies = dependencies
     892
     893 -   def __repr__(self):
     894        """
     895        Official string representation for class instance.
     896        """
     897        return "ExtendedAction(%s, %s, %s, %s, %s)" % (self.name, self.module, self.function, self.index, self.dependencies)
     898
     899 -   def __str__(self):
     900        """
     901        Informal string representation for class instance.
     902        """
     903        return self.__repr__()
     904
     905 -   def __cmp__(self, other):
     906        """
     907        Definition of equals operator for this class.
     908        @param other: Other object to compare to.
     909        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
     910        """
     911        if other is None:
     912           return 1
     913        if self.name != other.name:
     914           if self.name < other.name:
     915              return -1
     916           else:
     917              return 1
     918        if self.module != other.module:
     919           if self.module < other.module:
     920              return -1
     921           else:
     922              return 1
     923        if self.function != other.function:
     924           if self.function < other.function:
     925              return -1
     926           else:
     927              return 1
     928        if self.index != other.index:
     929           if self.index < other.index:
     930              return -1
     931           else:
     932              return 1
     933        if self.dependencies != other.dependencies:
     934           if self.dependencies < other.dependencies:
     935              return -1
     936           else:
     937              return 1
     938        return 0
     939
     940 -   def _setName(self, value):
     941        """
     942        Property target used to set the action name.
     943        The value must be a non-empty string if it is not C{None}.
     944        It must also consist only of lower-case letters and digits.
     945        @raise ValueError: If the value is an empty string.
     946        """
     947        pattern = re.compile(ACTION_NAME_REGEX)
     948        if value is not None:
     949           if len(value) < 1:
     950              raise ValueError("The action name must be a non-empty string.")
     951           if not pattern.search(value):
     952              raise ValueError("The action name must consist of only lower-case letters and digits.")
     953        self._name = value
     954
     955 -   def _getName(self):
     956        """
     957        Property target used to get the action name.
     958        """
     959        return self._name
     960
     961 -   def _setModule(self, value):
     962        """
     963        Property target used to set the module name.
     964        The value must be a non-empty string if it is not C{None}.
     965        It must also be a valid Python identifier.
     966        @raise ValueError: If the value is an empty string.
     967        """
     968        pattern = re.compile(r"^([A-Za-z_][A-Za-z0-9_]*)(\.[A-Za-z_][A-Za-z0-9_]*)*$")
     969        if value is not None:
     970           if len(value) < 1:
     971              raise ValueError("The module name must be a non-empty string.")
     972           if not pattern.search(value):
     973              raise ValueError("The module name must be a valid Python identifier.")
     974        self._module = value
     975
     976 -   def _getModule(self):
     977        """
     978        Property target used to get the module name.
     979        """
     980        return self._module
     981
     982 -   def _setFunction(self, value):
     983        """
     984        Property target used to set the function name.
     985        The value must be a non-empty string if it is not C{None}.
     986        It must also be a valid Python identifier.
     987        @raise ValueError: If the value is an empty string.
     988        """
     989        pattern = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
     990        if value is not None:
     991           if len(value) < 1:
     992              raise ValueError("The function name must be a non-empty string.")
     993           if not pattern.search(value):
     994              raise ValueError("The function name must be a valid Python identifier.")
     995        self._function = value
     996
     997 -   def _getFunction(self):
     998        """
     999        Property target used to get the function name.
    1000        """
    1001        return self._function
    1002
    1003 -   def _setIndex(self, value):
    1004        """
    1005        Property target used to set the action index.
    1006        The value must be an integer >= 0.
    1007        @raise ValueError: If the value is not valid.
    1008        """
    1009        if value is None:
    1010           self._index = None
    1011        else:
    1012           try:
    1013              value = int(value)
    1014           except TypeError:
    1015              raise ValueError("Action index value must be an integer >= 0.")
    1016           if value < 0:
    1017              raise ValueError("Action index value must be an integer >= 0.")
    1018           self._index = value
    1019
    1020 -   def _getIndex(self):
    1021        """
    1022        Property target used to get the action index.
    1023        """
    1024        return self._index
    1025
    1026 -   def _setDependencies(self, value):
    1027        """
    1028        Property target used to set the action dependencies information.
    1029        If not C{None}, the value must be a C{ActionDependecies} object.
    1030        @raise ValueError: If the value is not a C{ActionDependencies} object.
    1031        """
    1032        if value is None:
    1033           self._dependencies = None
    1034        else:
    1035           if not isinstance(value, ActionDependencies):
    1036              raise ValueError("Value must be a C{ActionDependencies} object.")
    1037           self._dependencies = value
    1038
    1039 -   def _getDependencies(self):
    1040        """
    1041        Property target used to get action dependencies information.
    1042        """
    1043        return self._dependencies
    1044
    1045     name = property(_getName, _setName, None, "Name of the extended action.")
    1046     module = property(_getModule, _setModule, None, "Name of the module containing the extended action function.")
    1047     function = property(_getFunction, _setFunction, None, "Name of the extended action function.")
    1048     index = property(_getIndex, _setIndex, None, "Index of action, used for execution ordering.")
    1049     dependencies = property(_getDependencies, _setDependencies, None, "Dependencies for action, used for execution ordering.")
    1050
    1051
    1052  ########################################################################
    1053  # CommandOverride class definition
    1054  ########################################################################
    1055
    1056 -class CommandOverride(object):
    1057
    1058     """
    1059     Class representing a piece of Cedar Backup command override configuration.
    1060
    1061     The following restrictions exist on data in this class:
    1062
    1063        - The absolute path must be absolute
    1064
    1065     @note: Lists within this class are "unordered" for equality comparisons.
    1066
    1067     @sort: __init__, __repr__, __str__, __cmp__, command, absolutePath
    1068     """
    1069
    1070 -   def __init__(self, command=None, absolutePath=None):
    1071        """
    1072        Constructor for the C{CommandOverride} class.
    1073
    1074        @param command: Name of command to be overridden.
    1075        @param absolutePath: Absolute path of the overrridden command.
    1076
    1077        @raise ValueError: If one of the values is invalid.
    1078        """
    1079        self._command = None
    1080        self._absolutePath = None
    1081        self.command = command
    1082        self.absolutePath = absolutePath
    1083
    1084 -   def __repr__(self):
    1085        """
    1086        Official string representation for class instance.
    1087        """
    1088        return "CommandOverride(%s, %s)" % (self.command, self.absolutePath)
    1089
    1090 -   def __str__(self):
    1091        """
    1092        Informal string representation for class instance.
    1093        """
    1094        return self.__repr__()
    1095
    1096 -   def __cmp__(self, other):
    1097        """
    1098        Definition of equals operator for this class.
    1099        @param other: Other object to compare to.
    1100        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    1101        """
    1102        if other is None:
    1103           return 1
    1104        if self.command != other.command:
    1105           if self.command < other.command:
    1106              return -1
    1107           else:
    1108              return 1
    1109        if self.absolutePath != other.absolutePath:
    1110           if self.absolutePath < other.absolutePath:
    1111              return -1
    1112           else:
    1113              return 1
    1114        return 0
    1115
    1116 -   def _setCommand(self, value):
    1117        """
    1118        Property target used to set the command.
    1119        The value must be a non-empty string if it is not C{None}.
    1120        @raise ValueError: If the value is an empty string.
    1121        """
    1122        if value is not None:
    1123           if len(value) < 1:
    1124              raise ValueError("The command must be a non-empty string.")
    1125        self._command = value
    1126
    1127 -   def _getCommand(self):
    1128        """
    1129        Property target used to get the command.
    1130        """
    1131        return self._command
    1132
    1133 -   def _setAbsolutePath(self, value):
    1134        """
    1135        Property target used to set the absolute path.
    1136        The value must be an absolute path if it is not C{None}.
    1137        It does not have to exist on disk at the time of assignment.
    1138        @raise ValueError: If the value is not an absolute path.
    1139        @raise ValueError: If the value cannot be encoded properly.
    1140        """
    1141        if value is not None:
    1142           if not os.path.isabs(value):
    1143              raise ValueError("Not an absolute path: [%s]" % value)
    1144        self._absolutePath = encodePath(value)
    1145
    1146 -   def _getAbsolutePath(self):
    1147        """
    1148        Property target used to get the absolute path.
    1149        """
    1150        return self._absolutePath
    1151
    1152     command = property(_getCommand, _setCommand, None, doc="Name of command to be overridden.")
    1153     absolutePath = property(_getAbsolutePath, _setAbsolutePath, None, doc="Absolute path of the overrridden command.")
    1154
    1155
    1156  ########################################################################
    1157  # CollectFile class definition
    1158  ########################################################################
    1159
    1160 -class CollectFile(object):
    1161
    1162     """
    1163     Class representing a Cedar Backup collect file.
    1164
    1165     The following restrictions exist on data in this class:
    1166
    1167        - Absolute paths must be absolute
    1168        - The collect mode must be one of the values in L{VALID_COLLECT_MODES}.
    1169        - The archive mode must be one of the values in L{VALID_ARCHIVE_MODES}.
    1170
    1171     @sort: __init__, __repr__, __str__, __cmp__, absolutePath, collectMode, archiveMode
    1172     """
    1173
    1174 -   def __init__(self, absolutePath=None, collectMode=None, archiveMode=None):
    1175        """
    1176        Constructor for the C{CollectFile} class.
    1177
    1178        @param absolutePath: Absolute path of the file to collect.
    1179        @param collectMode: Overridden collect mode for this file.
    1180        @param archiveMode: Overridden archive mode for this file.
    1181
    1182        @raise ValueError: If one of the values is invalid.
    1183        """
    1184        self._absolutePath = None
    1185        self._collectMode = None
    1186        self._archiveMode = None
    1187        self.absolutePath = absolutePath
    1188        self.collectMode = collectMode
    1189        self.archiveMode = archiveMode
    1190
    1191 -   def __repr__(self):
    1192        """
    1193        Official string representation for class instance.
    1194        """
    1195        return "CollectFile(%s, %s, %s)" % (self.absolutePath, self.collectMode, self.archiveMode)
    1196
    1197 -   def __str__(self):
    1198        """
    1199        Informal string representation for class instance.
    1200        """
    1201        return self.__repr__()
    1202
    1203 -   def __cmp__(self, other):
    1204        """
    1205        Definition of equals operator for this class.
    1206        @param other: Other object to compare to.
    1207        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    1208        """
    1209        if other is None:
    1210           return 1
    1211        if self.absolutePath != other.absolutePath:
    1212           if self.absolutePath < other.absolutePath:
    1213              return -1
    1214           else:
    1215              return 1
    1216        if self.collectMode != other.collectMode:
    1217           if self.collectMode < other.collectMode:
    1218              return -1
    1219           else:
    1220              return 1
    1221        if self.archiveMode != other.archiveMode:
    1222           if self.archiveMode < other.archiveMode:
    1223              return -1
    1224           else:
    1225              return 1
    1226        return 0
    1227
    1228 -   def _setAbsolutePath(self, value):
    1229        """
    1230        Property target used to set the absolute path.
    1231        The value must be an absolute path if it is not C{None}.
    1232        It does not have to exist on disk at the time of assignment.
    1233        @raise ValueError: If the value is not an absolute path.
    1234        @raise ValueError: If the value cannot be encoded properly.
    1235        """
    1236        if value is not None:
    1237           if not os.path.isabs(value):
    1238              raise ValueError("Not an absolute path: [%s]" % value)
    1239        self._absolutePath = encodePath(value)
    1240
    1241 -   def _getAbsolutePath(self):
    1242        """
    1243        Property target used to get the absolute path.
    1244        """
    1245        return self._absolutePath
    1246
    1247 -   def _setCollectMode(self, value):
    1248        """
    1249        Property target used to set the collect mode.
    1250        If not C{None}, the mode must be one of the values in L{VALID_COLLECT_MODES}.
    1251        @raise ValueError: If the value is not valid.
    1252        """
    1253        if value is not None:
    1254           if value not in VALID_COLLECT_MODES:
    1255              raise ValueError("Collect mode must be one of %s." % VALID_COLLECT_MODES)
    1256        self._collectMode = value
    1257
    1258 -   def _getCollectMode(self):
    1259        """
    1260        Property target used to get the collect mode.
    1261        """
    1262        return self._collectMode
    1263
    1264 -   def _setArchiveMode(self, value):
    1265        """
    1266        Property target used to set the archive mode.
    1267        If not C{None}, the mode must be one of the values in L{VALID_ARCHIVE_MODES}.
    1268        @raise ValueError: If the value is not valid.
    1269        """
    1270        if value is not None:
    1271           if value not in VALID_ARCHIVE_MODES:
    1272              raise ValueError("Archive mode must be one of %s." % VALID_ARCHIVE_MODES)
    1273        self._archiveMode = value
    1274
    1275 -   def _getArchiveMode(self):
    1276        """
    1277        Property target used to get the archive mode.
    1278        """
    1279        return self._archiveMode
    1280
    1281     absolutePath = property(_getAbsolutePath, _setAbsolutePath, None, doc="Absolute path of the file to collect.")
    1282     collectMode = property(_getCollectMode, _setCollectMode, None, doc="Overridden collect mode for this file.")
    1283     archiveMode = property(_getArchiveMode, _setArchiveMode, None, doc="Overridden archive mode for this file.")
    1284
    1285
    1286  ########################################################################
    1287  # CollectDir class definition
    1288  ########################################################################
    1289
    1290 -class CollectDir(object):
    1291
    1292     """
    1293     Class representing a Cedar Backup collect directory.
    1294
    1295     The following restrictions exist on data in this class:
    1296
    1297        - Absolute paths must be absolute
    1298        - The collect mode must be one of the values in L{VALID_COLLECT_MODES}.
    1299        - The archive mode must be one of the values in L{VALID_ARCHIVE_MODES}.
    1300        - The ignore file must be a non-empty string.
    1301
    1302     For the C{absoluteExcludePaths} list, validation is accomplished through the
    1303     L{util.AbsolutePathList} list implementation that overrides common list
    1304     methods and transparently does the absolute path validation for us.
    1305
    1306     @note: Lists within this class are "unordered" for equality comparisons.
    1307
    1308     @sort: __init__, __repr__, __str__, __cmp__, absolutePath, collectMode,
    1309            archiveMode, ignoreFile, linkDepth, dereference, absoluteExcludePaths,
    1310            relativeExcludePaths, excludePatterns
    1311     """
    1312
    1313 -   def __init__(self, absolutePath=None, collectMode=None, archiveMode=None, ignoreFile=None,
    1314                  absoluteExcludePaths=None, relativeExcludePaths=None, excludePatterns=None,
    1315                  linkDepth=None, dereference=False, recursionLevel=None):
    1316        """
    1317        Constructor for the C{CollectDir} class.
    1318
    1319        @param absolutePath: Absolute path of the directory to collect.
    1320        @param collectMode: Overridden collect mode for this directory.
    1321        @param archiveMode: Overridden archive mode for this directory.
    1322        @param ignoreFile: Overidden ignore file name for this directory.
    1323        @param linkDepth: Maximum at which soft links should be followed.
    1324        @param dereference: Whether to dereference links that are followed.
    1325        @param absoluteExcludePaths: List of absolute paths to exclude.
    1326        @param relativeExcludePaths: List of relative paths to exclude.
    1327        @param excludePatterns: List of regular expression patterns to exclude.
    1328
    1329        @raise ValueError: If one of the values is invalid.
    1330        """
    1331        self._absolutePath = None
    1332        self._collectMode = None
    1333        self._archiveMode = None
    1334        self._ignoreFile = None
    1335        self._linkDepth = None
    1336        self._dereference = None
    1337        self._recursionLevel = None
    1338        self._absoluteExcludePaths = None
    1339        self._relativeExcludePaths = None
    1340        self._excludePatterns = None
    1341        self.absolutePath = absolutePath
    1342        self.collectMode = collectMode
    1343        self.archiveMode = archiveMode
    1344        self.ignoreFile = ignoreFile
    1345        self.linkDepth = linkDepth
    1346        self.dereference = dereference
    1347        self.recursionLevel = recursionLevel
    1348        self.absoluteExcludePaths = absoluteExcludePaths
    1349        self.relativeExcludePaths = relativeExcludePaths
    1350        self.excludePatterns = excludePatterns
    1351
    1352 -   def __repr__(self):
    1353        """
    1354        Official string representation for class instance.
    1355        """
    1356        return "CollectDir(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.absolutePath, self.collectMode,
    1357                                                                       self.archiveMode, self.ignoreFile,
    1358                                                                       self.absoluteExcludePaths,
    1359                                                                       self.relativeExcludePaths,
    1360                                                                       self.excludePatterns,
    1361                                                                       self.linkDepth, self.dereference,
    1362                                                                       self.recursionLevel)
    1363
    1364 -   def __str__(self):
    1365        """
    1366        Informal string representation for class instance.
    1367        """
    1368        return self.__repr__()
    1369
    1370 -   def __cmp__(self, other):
    1371        """
    1372        Definition of equals operator for this class.
    1373        Lists within this class are "unordered" for equality comparisons.
    1374        @param other: Other object to compare to.
    1375        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    1376        """
    1377        if other is None:
    1378           return 1
    1379        if self.absolutePath != other.absolutePath:
    1380           if self.absolutePath < other.absolutePath:
    1381              return -1
    1382           else:
    1383              return 1
    1384        if self.collectMode != other.collectMode:
    1385           if self.collectMode < other.collectMode:
    1386              return -1
    1387           else:
    1388              return 1
    1389        if self.archiveMode != other.archiveMode:
    1390           if self.archiveMode < other.archiveMode:
    1391              return -1
    1392           else:
    1393              return 1
    1394        if self.ignoreFile != other.ignoreFile:
    1395           if self.ignoreFile < other.ignoreFile:
    1396              return -1
    1397           else:
    1398              return 1
    1399        if self.linkDepth != other.linkDepth:
    1400           if self.linkDepth < other.linkDepth:
    1401              return -1
    1402           else:
    1403              return 1
    1404        if self.dereference != other.dereference:
    1405           if self.dereference < other.dereference:
    1406              return -1
    1407           else:
    1408              return 1
    1409        if self.recursionLevel != other.recursionLevel:
    1410           if self.recursionLevel < other.recursionLevel:
    1411              return -1
    1412           else:
    1413              return 1
    1414        if self.absoluteExcludePaths != other.absoluteExcludePaths:
    1415           if self.absoluteExcludePaths < other.absoluteExcludePaths:
    1416              return -1
    1417           else:
    1418              return 1
    1419        if self.relativeExcludePaths != other.relativeExcludePaths:
    1420           if self.relativeExcludePaths < other.relativeExcludePaths:
    1421              return -1
    1422           else:
    1423              return 1
    1424        if self.excludePatterns != other.excludePatterns:
    1425           if self.excludePatterns < other.excludePatterns:
    1426              return -1
    1427           else:
    1428              return 1
    1429        return 0
    1430
    1431 -   def _setAbsolutePath(self, value):
    1432        """
    1433        Property target used to set the absolute path.
    1434        The value must be an absolute path if it is not C{None}.
    1435        It does not have to exist on disk at the time of assignment.
    1436        @raise ValueError: If the value is not an absolute path.
    1437        @raise ValueError: If the value cannot be encoded properly.
    1438        """
    1439        if value is not None:
    1440           if not os.path.isabs(value):
    1441              raise ValueError("Not an absolute path: [%s]" % value)
    1442        self._absolutePath = encodePath(value)
    1443
    1444 -   def _getAbsolutePath(self):
    1445        """
    1446        Property target used to get the absolute path.
    1447        """
    1448        return self._absolutePath
    1449
    1450 -   def _setCollectMode(self, value):
    1451        """
    1452        Property target used to set the collect mode.
    1453        If not C{None}, the mode must be one of the values in L{VALID_COLLECT_MODES}.
    1454        @raise ValueError: If the value is not valid.
    1455        """
    1456        if value is not None:
    1457           if value not in VALID_COLLECT_MODES:
    1458              raise ValueError("Collect mode must be one of %s." % VALID_COLLECT_MODES)
    1459        self._collectMode = value
    1460
    1461 -   def _getCollectMode(self):
    1462        """
    1463        Property target used to get the collect mode.
    1464        """
    1465        return self._collectMode
    1466
    1467 -   def _setArchiveMode(self, value):
    1468        """
    1469        Property target used to set the archive mode.
    1470        If not C{None}, the mode must be one of the values in L{VALID_ARCHIVE_MODES}.
    1471        @raise ValueError: If the value is not valid.
    1472        """
    1473        if value is not None:
    1474           if value not in VALID_ARCHIVE_MODES:
    1475              raise ValueError("Archive mode must be one of %s." % VALID_ARCHIVE_MODES)
    1476        self._archiveMode = value
    1477
    1478 -   def _getArchiveMode(self):
    1479        """
    1480        Property target used to get the archive mode.
    1481        """
    1482        return self._archiveMode
    1483
    1484 -   def _setIgnoreFile(self, value):
    1485        """
    1486        Property target used to set the ignore file.
    1487        The value must be a non-empty string if it is not C{None}.
    1488        @raise ValueError: If the value is an empty string.
    1489        """
    1490        if value is not None:
    1491           if len(value) < 1:
    1492              raise ValueError("The ignore file must be a non-empty string.")
    1493        self._ignoreFile = value
    1494
    1495 -   def _getIgnoreFile(self):
    1496        """
    1497        Property target used to get the ignore file.
    1498        """
    1499        return self._ignoreFile
    1500
    1501 -   def _setLinkDepth(self, value):
    1502        """
    1503        Property target used to set the link depth.
    1504        The value must be an integer >= 0.
    1505        @raise ValueError: If the value is not valid.
    1506        """
    1507        if value is None:
    1508           self._linkDepth = None
    1509        else:
    1510           try:
    1511              value = int(value)
    1512           except TypeError:
    1513              raise ValueError("Link depth value must be an integer >= 0.")
    1514           if value < 0:
    1515              raise ValueError("Link depth value must be an integer >= 0.")
    1516           self._linkDepth = value
    1517
    1518 -   def _getLinkDepth(self):
    1519        """
    1520        Property target used to get the action linkDepth.
    1521        """
    1522        return self._linkDepth
    1523
    1524 -   def _setDereference(self, value):
    1525        """
    1526        Property target used to set the dereference flag.
    1527        No validations, but we normalize the value to C{True} or C{False}.
    1528        """
    1529        if value:
    1530           self._dereference = True
    1531        else:
    1532           self._dereference = False
    1533
    1534 -   def _getDereference(self):
    1535        """
    1536        Property target used to get the dereference flag.
    1537        """
    1538        return self._dereference
    1539
    1540 -   def _setRecursionLevel(self, value):
    1541        """
    1542        Property target used to set the recursionLevel.
    1543        The value must be an integer.
    1544        @raise ValueError: If the value is not valid.
    1545        """
    1546        if value is None:
    1547           self._recursionLevel = None
    1548        else:
    1549           try:
    1550              value = int(value)
    1551           except TypeError:
    1552              raise ValueError("Recusion level value must be an integer.")
    1553           self._recursionLevel = value
    1554
    1555 -   def _getRecursionLevel(self):
    1556        """
    1557        Property target used to get the action recursionLevel.
    1558        """
    1559        return self._recursionLevel
    1560
    1561 -   def _setAbsoluteExcludePaths(self, value):
    1562        """
    1563        Property target used to set the absolute exclude paths list.
    1564        Either the value must be C{None} or each element must be an absolute path.
    1565        Elements do not have to exist on disk at the time of assignment.
    1566        @raise ValueError: If the value is not an absolute path.
    1567        """
    1568        if value is None:
    1569           self._absoluteExcludePaths = None
    1570        else:
    1571           try:
    1572              saved = self._absoluteExcludePaths
    1573              self._absoluteExcludePaths = AbsolutePathList()
    1574              self._absoluteExcludePaths.extend(value)
    1575           except Exception, e:
    1576              self._absoluteExcludePaths = saved
    1577              raise e
    1578
    1579 -   def _getAbsoluteExcludePaths(self):
    1580        """
    1581        Property target used to get the absolute exclude paths list.
    1582        """
    1583        return self._absoluteExcludePaths
    1584
    1585 -   def _setRelativeExcludePaths(self, value):
    1586        """
    1587        Property target used to set the relative exclude paths list.
    1588        Elements do not have to exist on disk at the time of assignment.
    1589        """
    1590        if value is None:
    1591           self._relativeExcludePaths = None
    1592        else:
    1593           try:
    1594              saved = self._relativeExcludePaths
    1595              self._relativeExcludePaths = UnorderedList()
    1596              self._relativeExcludePaths.extend(value)
    1597           except Exception, e:
    1598              self._relativeExcludePaths = saved
    1599              raise e
    1600
    1601 -   def _getRelativeExcludePaths(self):
    1602        """
    1603        Property target used to get the relative exclude paths list.
    1604        """
    1605        return self._relativeExcludePaths
    1606
    1607 -   def _setExcludePatterns(self, value):
    1608        """
    1609        Property target used to set the exclude patterns list.
    1610        """
    1611        if value is None:
    1612           self._excludePatterns = None
    1613        else:
    1614           try:
    1615              saved = self._excludePatterns
    1616              self._excludePatterns = RegexList()
    1617              self._excludePatterns.extend(value)
    1618           except Exception, e:
    1619              self._excludePatterns = saved
    1620              raise e
    1621
    1622 -   def _getExcludePatterns(self):
    1623        """
    1624        Property target used to get the exclude patterns list.
    1625        """
    1626        return self._excludePatterns
    1627
    1628     absolutePath = property(_getAbsolutePath, _setAbsolutePath, None, doc="Absolute path of the directory to collect.")
    1629     collectMode = property(_getCollectMode, _setCollectMode, None, doc="Overridden collect mode for this directory.")
    1630     archiveMode = property(_getArchiveMode, _setArchiveMode, None, doc="Overridden archive mode for this directory.")
    1631     ignoreFile = property(_getIgnoreFile, _setIgnoreFile, None, doc="Overridden ignore file name for this directory.")
    1632     linkDepth = property(_getLinkDepth, _setLinkDepth, None, doc="Maximum at which soft links should be followed.")
    1633     dereference = property(_getDereference, _setDereference, None, doc="Whether to dereference links that are followed.")
    1634     recursionLevel = property(_getRecursionLevel, _setRecursionLevel, None, "Recursion level to use for recursive directory collection")
    1635     absoluteExcludePaths = property(_getAbsoluteExcludePaths, _setAbsoluteExcludePaths, None, "List of absolute paths to exclude.")
    1636     relativeExcludePaths = property(_getRelativeExcludePaths, _setRelativeExcludePaths, None, "List of relative paths to exclude.")
    1637     excludePatterns = property(_getExcludePatterns, _setExcludePatterns, None, "List of regular expression patterns to exclude.")
    1638
    1639
    1640  ########################################################################
    1641  # PurgeDir class definition
    1642  ########################################################################
    1643
    1644 -class PurgeDir(object):
    1645
    1646     """
    1647     Class representing a Cedar Backup purge directory.
    1648
    1649     The following restrictions exist on data in this class:
    1650
    1651        - The absolute path must be an absolute path
    1652        - The retain days value must be an integer >= 0.
    1653
    1654     @sort: __init__, __repr__, __str__, __cmp__, absolutePath, retainDays
    1655     """
    1656
    1657 -   def __init__(self, absolutePath=None, retainDays=None):
    1658        """
    1659        Constructor for the C{PurgeDir} class.
    1660
    1661        @param absolutePath: Absolute path of the directory to be purged.
    1662        @param retainDays: Number of days content within directory should be retained.
    1663
    1664        @raise ValueError: If one of the values is invalid.
    1665        """
    1666        self._absolutePath = None
    1667        self._retainDays = None
    1668        self.absolutePath = absolutePath
    1669        self.retainDays = retainDays
    1670
    1671 -   def __repr__(self):
    1672        """
    1673        Official string representation for class instance.
    1674        """
    1675        return "PurgeDir(%s, %s)" % (self.absolutePath, self.retainDays)
    1676
    1677 -   def __str__(self):
    1678        """
    1679        Informal string representation for class instance.
    1680        """
    1681        return self.__repr__()
    1682
    1683 -   def __cmp__(self, other):
    1684        """
    1685        Definition of equals operator for this class.
    1686        @param other: Other object to compare to.
    1687        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    1688        """
    1689        if other is None:
    1690           return 1
    1691        if self.absolutePath != other.absolutePath:
    1692           if self.absolutePath < other.absolutePath:
    1693              return -1
    1694           else:
    1695              return 1
    1696        if self.retainDays != other.retainDays:
    1697           if self.retainDays < other.retainDays:
    1698              return -1
    1699           else:
    1700              return 1
    1701        return 0
    1702
    1703 -   def _setAbsolutePath(self, value):
    1704        """
    1705        Property target used to set the absolute path.
    1706        The value must be an absolute path if it is not C{None}.
    1707        It does not have to exist on disk at the time of assignment.
    1708        @raise ValueError: If the value is not an absolute path.
    1709        @raise ValueError: If the value cannot be encoded properly.
    1710        """
    1711        if value is not None:
    1712           if not os.path.isabs(value):
    1713              raise ValueError("Absolute path must, er, be an absolute path.")
    1714        self._absolutePath = encodePath(value)
    1715
    1716 -   def _getAbsolutePath(self):
    1717        """
    1718        Property target used to get the absolute path.
    1719        """
    1720        return self._absolutePath
    1721
    1722 -   def _setRetainDays(self, value):
    1723        """
    1724        Property target used to set the retain days value.
    1725        The value must be an integer >= 0.
    1726        @raise ValueError: If the value is not valid.
    1727        """
    1728        if value is None:
    1729           self._retainDays = None
    1730        else:
    1731           try:
    1732              value = int(value)
    1733           except TypeError:
    1734              raise ValueError("Retain days value must be an integer >= 0.")
    1735           if value < 0:
    1736              raise ValueError("Retain days value must be an integer >= 0.")
    1737           self._retainDays = value
    1738
    1739 -   def _getRetainDays(self):
    1740        """
    1741        Property target used to get the absolute path.
    1742        """
    1743        return self._retainDays
    1744
    1745     absolutePath = property(_getAbsolutePath, _setAbsolutePath, None, "Absolute path of directory to purge.")
    1746     retainDays = property(_getRetainDays, _setRetainDays, None, "Number of days content within directory should be retained.")
    1747
    1748
    1749  ########################################################################
    1750  # LocalPeer class definition
    1751  ########################################################################
    1752
    1753 -class LocalPeer(object):
    1754
    1755     """
    1756     Class representing a Cedar Backup peer.
    1757
    1758     The following restrictions exist on data in this class:
    1759
    1760        - The peer name must be a non-empty string.
    1761        - The collect directory must be an absolute path.
    1762        - The ignore failure mode must be one of the values in L{VALID_FAILURE_MODES}.
    1763
    1764     @sort: __init__, __repr__, __str__, __cmp__, name, collectDir
    1765     """
    1766
    1767 -   def __init__(self, name=None, collectDir=None, ignoreFailureMode=None):
    1768        """
    1769        Constructor for the C{LocalPeer} class.
    1770
    1771        @param name: Name of the peer, typically a valid hostname.
    1772        @param collectDir: Collect directory to stage files from on peer.
    1773        @param ignoreFailureMode: Ignore failure mode for peer.
    1774
    1775        @raise ValueError: If one of the values is invalid.
    1776        """
    1777        self._name = None
    1778        self._collectDir = None
    1779        self._ignoreFailureMode = None
    1780        self.name = name
    1781        self.collectDir = collectDir
    1782        self.ignoreFailureMode = ignoreFailureMode
    1783
    1784 -   def __repr__(self):
    1785        """
    1786        Official string representation for class instance.
    1787        """
    1788        return "LocalPeer(%s, %s, %s)" % (self.name, self.collectDir, self.ignoreFailureMode)
    1789
    1790 -   def __str__(self):
    1791        """
    1792        Informal string representation for class instance.
    1793        """
    1794        return self.__repr__()
    1795
    1796 -   def __cmp__(self, other):
    1797        """
    1798        Definition of equals operator for this class.
    1799        @param other: Other object to compare to.
    1800        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    1801        """
    1802        if other is None:
    1803           return 1
    1804        if self.name != other.name:
    1805           if self.name < other.name:
    1806              return -1
    1807           else:
    1808              return 1
    1809        if self.collectDir != other.collectDir:
    1810           if self.collectDir < other.collectDir:
    1811              return -1
    1812           else:
    1813              return 1
    1814        if self.ignoreFailureMode != other.ignoreFailureMode:
    1815           if self.ignoreFailureMode < other.ignoreFailureMode:
    1816              return -1
    1817           else:
    1818              return 1
    1819        return 0
    1820
    1821 -   def _setName(self, value):
    1822        """
    1823        Property target used to set the peer name.
    1824        The value must be a non-empty string if it is not C{None}.
    1825        @raise ValueError: If the value is an empty string.
    1826        """
    1827        if value is not None:
    1828           if len(value) < 1:
    1829              raise ValueError("The peer name must be a non-empty string.")
    1830        self._name = value
    1831
    1832 -   def _getName(self):
    1833        """
    1834        Property target used to get the peer name.
    1835        """
    1836        return self._name
    1837
    1838 -   def _setCollectDir(self, value):
    1839        """
    1840        Property target used to set the collect directory.
    1841        The value must be an absolute path if it is not C{None}.
    1842        It does not have to exist on disk at the time of assignment.
    1843        @raise ValueError: If the value is not an absolute path.
    1844        @raise ValueError: If the value cannot be encoded properly.
    1845        """
    1846        if value is not None:
    1847           if not os.path.isabs(value):
    1848              raise ValueError("Collect directory must be an absolute path.")
    1849        self._collectDir = encodePath(value)
    1850
    1851 -   def _getCollectDir(self):
    1852        """
    1853        Property target used to get the collect directory.
    1854        """
    1855        return self._collectDir
    1856
    1857 -   def _setIgnoreFailureMode(self, value):
    1858        """
    1859        Property target used to set the ignoreFailure mode.
    1860        If not C{None}, the mode must be one of the values in L{VALID_FAILURE_MODES}.
    1861        @raise ValueError: If the value is not valid.
    1862        """
    1863        if value is not None:
    1864           if value not in VALID_FAILURE_MODES:
    1865              raise ValueError("Ignore failure mode must be one of %s." % VALID_FAILURE_MODES)
    1866        self._ignoreFailureMode = value
    1867
    1868 -   def _getIgnoreFailureMode(self):
    1869        """
    1870        Property target used to get the ignoreFailure mode.
    1871        """
    1872        return self._ignoreFailureMode
    1873
    1874     name = property(_getName, _setName, None, "Name of the peer, typically a valid hostname.")
    1875     collectDir = property(_getCollectDir, _setCollectDir, None, "Collect directory to stage files from on peer.")
    1876     ignoreFailureMode = property(_getIgnoreFailureMode, _setIgnoreFailureMode, None, "Ignore failure mode for peer.")
    1877
    1878
    1879  ########################################################################
    1880  # RemotePeer class definition
    1881  ########################################################################
    1882
    1883 -class RemotePeer(object):
    1884
    1885     """
    1886     Class representing a Cedar Backup peer.
    1887
    1888     The following restrictions exist on data in this class:
    1889
    1890        - The peer name must be a non-empty string.
    1891        - The collect directory must be an absolute path.
    1892        - The remote user must be a non-empty string.
    1893        - The rcp command must be a non-empty string.
    1894        - The rsh command must be a non-empty string.
    1895        - The cback command must be a non-empty string.
    1896        - Any managed action name must be a non-empty string matching C{ACTION_NAME_REGEX}
    1897        - The ignore failure mode must be one of the values in L{VALID_FAILURE_MODES}.
    1898
    1899     @sort: __init__, __repr__, __str__, __cmp__, name, collectDir, remoteUser, rcpCommand
    1900     """
    1901
    1902 -   def __init__(self, name=None, collectDir=None, remoteUser=None,
    1903                  rcpCommand=None, rshCommand=None, cbackCommand=None,
    1904                  managed=False, managedActions=None, ignoreFailureMode=None):
    1905        """
    1906        Constructor for the C{RemotePeer} class.
    1907
    1908        @param name: Name of the peer, must be a valid hostname.
    1909        @param collectDir: Collect directory to stage files from on peer.
    1910        @param remoteUser: Name of backup user on remote peer.
    1911        @param rcpCommand: Overridden rcp-compatible copy command for peer.
    1912        @param rshCommand: Overridden rsh-compatible remote shell command for peer.
    1913        @param cbackCommand: Overridden cback-compatible command to use on remote peer.
    1914        @param managed: Indicates whether this is a managed peer.
    1915        @param managedActions: Overridden set of actions that are managed on the peer.
    1916        @param ignoreFailureMode: Ignore failure mode for peer.
    1917
    1918        @raise ValueError: If one of the values is invalid.
    1919        """
    1920        self._name = None
    1921        self._collectDir = None
    1922        self._remoteUser = None
    1923        self._rcpCommand = None
    1924        self._rshCommand = None
    1925        self._cbackCommand = None
    1926        self._managed = None
    1927        self._managedActions = None
    1928        self._ignoreFailureMode = None
    1929        self.name = name
    1930        self.collectDir = collectDir
    1931        self.remoteUser = remoteUser
    1932        self.rcpCommand = rcpCommand
    1933        self.rshCommand = rshCommand
    1934        self.cbackCommand = cbackCommand
    1935        self.managed = managed
    1936        self.managedActions = managedActions
    1937        self.ignoreFailureMode = ignoreFailureMode
    1938
    1939 -   def __repr__(self):
    1940        """
    1941        Official string representation for class instance.
    1942        """
    1943        return "RemotePeer(%s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.name, self.collectDir, self.remoteUser,
    1944                                                                   self.rcpCommand, self.rshCommand, self.cbackCommand,
    1945                                                                   self.managed, self.managedActions, self.ignoreFailureMode)
    1946
    1947 -   def __str__(self):
    1948        """
    1949        Informal string representation for class instance.
    1950        """
    1951        return self.__repr__()
    1952
    1953 -   def __cmp__(self, other):
    1954        """
    1955        Definition of equals operator for this class.
    1956        @param other: Other object to compare to.
    1957        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    1958        """
    1959        if other is None:
    1960           return 1
    1961        if self.name != other.name:
    1962           if self.name < other.name:
    1963              return -1
    1964           else:
    1965              return 1
    1966        if self.collectDir != other.collectDir:
    1967           if self.collectDir < other.collectDir:
    1968              return -1
    1969           else:
    1970              return 1
    1971        if self.remoteUser != other.remoteUser:
    1972           if self.remoteUser < other.remoteUser:
    1973              return -1
    1974           else:
    1975              return 1
    1976        if self.rcpCommand != other.rcpCommand:
    1977           if self.rcpCommand < other.rcpCommand:
    1978              return -1
    1979           else:
    1980              return 1
    1981        if self.rshCommand != other.rshCommand:
    1982           if self.rshCommand < other.rshCommand:
    1983              return -1
    1984           else:
    1985              return 1
    1986        if self.cbackCommand != other.cbackCommand:
    1987           if self.cbackCommand < other.cbackCommand:
    1988              return -1
    1989           else:
    1990              return 1
    1991        if self.managed != other.managed:
    1992           if self.managed < other.managed:
    1993              return -1
    1994           else:
    1995              return 1
    1996        if self.managedActions != other.managedActions:
    1997           if self.managedActions < other.managedActions:
    1998              return -1
    1999           else:
    2000              return 1
    2001        if self.ignoreFailureMode != other.ignoreFailureMode:
    2002           if self.ignoreFailureMode < other.ignoreFailureMode:
    2003              return -1
    2004           else:
    2005              return 1
    2006        return 0
    2007
    2008 -   def _setName(self, value):
    2009        """
    2010        Property target used to set the peer name.
    2011        The value must be a non-empty string if it is not C{None}.
    2012        @raise ValueError: If the value is an empty string.
    2013        """
    2014        if value is not None:
    2015           if len(value) < 1:
    2016              raise ValueError("The peer name must be a non-empty string.")
    2017        self._name = value
    2018
    2019 -   def _getName(self):
    2020        """
    2021        Property target used to get the peer name.
    2022        """
    2023        return self._name
    2024
    2025 -   def _setCollectDir(self, value):
    2026        """
    2027        Property target used to set the collect directory.
    2028        The value must be an absolute path if it is not C{None}.
    2029        It does not have to exist on disk at the time of assignment.
    2030        @raise ValueError: If the value is not an absolute path.
    2031        @raise ValueError: If the value cannot be encoded properly.
    2032        """
    2033        if value is not None:
    2034           if not os.path.isabs(value):
    2035              raise ValueError("Collect directory must be an absolute path.")
    2036        self._collectDir = encodePath(value)
    2037
    2038 -   def _getCollectDir(self):
    2039        """
    2040        Property target used to get the collect directory.
    2041        """
    2042        return self._collectDir
    2043
    2044 -   def _setRemoteUser(self, value):
    2045        """
    2046        Property target used to set the remote user.
    2047        The value must be a non-empty string if it is not C{None}.
    2048        @raise ValueError: If the value is an empty string.
    2049        """
    2050        if value is not None:
    2051           if len(value) < 1:
    2052              raise ValueError("The remote user must be a non-empty string.")
    2053        self._remoteUser = value
    2054
    2055 -   def _getRemoteUser(self):
    2056        """
    2057        Property target used to get the remote user.
    2058        """
    2059        return self._remoteUser
    2060
    2061 -   def _setRcpCommand(self, value):
    2062        """
    2063        Property target used to set the rcp command.
    2064        The value must be a non-empty string if it is not C{None}.
    2065        @raise ValueError: If the value is an empty string.
    2066        """
    2067        if value is not None:
    2068           if len(value) < 1:
    2069              raise ValueError("The rcp command must be a non-empty string.")
    2070        self._rcpCommand = value
    2071
    2072 -   def _getRcpCommand(self):
    2073        """
    2074        Property target used to get the rcp command.
    2075        """
    2076        return self._rcpCommand
    2077
    2078 -   def _setRshCommand(self, value):
    2079        """
    2080        Property target used to set the rsh command.
    2081        The value must be a non-empty string if it is not C{None}.
    2082        @raise ValueError: If the value is an empty string.
    2083        """
    2084        if value is not None:
    2085           if len(value) < 1:
    2086              raise ValueError("The rsh command must be a non-empty string.")
    2087        self._rshCommand = value
    2088
    2089 -   def _getRshCommand(self):
    2090        """
    2091        Property target used to get the rsh command.
    2092        """
    2093        return self._rshCommand
    2094
    2095 -   def _setCbackCommand(self, value):
    2096        """
    2097        Property target used to set the cback command.
    2098        The value must be a non-empty string if it is not C{None}.
    2099        @raise ValueError: If the value is an empty string.
    2100        """
    2101        if value is not None:
    2102           if len(value) < 1:
    2103              raise ValueError("The cback command must be a non-empty string.")
    2104        self._cbackCommand = value
    2105
    2106 -   def _getCbackCommand(self):
    2107        """
    2108        Property target used to get the cback command.
    2109        """
    2110        return self._cbackCommand
    2111
    2112 -   def _setManaged(self, value):
    2113        """
    2114        Property target used to set the managed flag.
    2115        No validations, but we normalize the value to C{True} or C{False}.
    2116        """
    2117        if value:
    2118           self._managed = True
    2119        else:
    2120           self._managed = False
    2121
    2122 -   def _getManaged(self):
    2123        """
    2124        Property target used to get the managed flag.
    2125        """
    2126        return self._managed
    2127
    2128 -   def _setManagedActions(self, value):
    2129        """
    2130        Property target used to set the managed actions list.
    2131        Elements do not have to exist on disk at the time of assignment.
    2132        """
    2133        if value is None:
    2134           self._managedActions = None
    2135        else:
    2136           try:
    2137              saved = self._managedActions
    2138              self._managedActions = RegexMatchList(ACTION_NAME_REGEX, emptyAllowed=False, prefix="Action name")
    2139              self._managedActions.extend(value)
    2140           except Exception, e:
    2141              self._managedActions = saved
    2142              raise e
    2143
    2144 -   def _getManagedActions(self):
    2145        """
    2146        Property target used to get the managed actions list.
    2147        """
    2148        return self._managedActions
    2149
    2150 -   def _setIgnoreFailureMode(self, value):
    2151        """
    2152        Property target used to set the ignoreFailure mode.
    2153        If not C{None}, the mode must be one of the values in L{VALID_FAILURE_MODES}.
    2154        @raise ValueError: If the value is not valid.
    2155        """
    2156        if value is not None:
    2157           if value not in VALID_FAILURE_MODES:
    2158              raise ValueError("Ignore failure mode must be one of %s." % VALID_FAILURE_MODES)
    2159        self._ignoreFailureMode = value
    2160
    2161 -   def _getIgnoreFailureMode(self):
    2162        """
    2163        Property target used to get the ignoreFailure mode.
    2164        """
    2165        return self._ignoreFailureMode
    2166
    2167     name = property(_getName, _setName, None, "Name of the peer, must be a valid hostname.")
    2168     collectDir = property(_getCollectDir, _setCollectDir, None, "Collect directory to stage files from on peer.")
    2169     remoteUser = property(_getRemoteUser, _setRemoteUser, None, "Name of backup user on remote peer.")
    2170     rcpCommand = property(_getRcpCommand, _setRcpCommand, None, "Overridden rcp-compatible copy command for peer.")
    2171     rshCommand = property(_getRshCommand, _setRshCommand, None, "Overridden rsh-compatible remote shell command for peer.")
    2172     cbackCommand = property(_getCbackCommand, _setCbackCommand, None, "Overridden cback-compatible command to use on remote peer.")
    2173     managed = property(_getManaged, _setManaged, None, "Indicates whether this is a managed peer.")
    2174     managedActions = property(_getManagedActions, _setManagedActions, None, "Overridden set of actions that are managed on the peer.")
    2175     ignoreFailureMode = property(_getIgnoreFailureMode, _setIgnoreFailureMode, None, "Ignore failure mode for peer.")
    2176
    2177
    2178  ########################################################################
    2179  # ReferenceConfig class definition
    2180  ########################################################################
    2181
    2182 -class ReferenceConfig(object):
    2183
    2184     """
    2185     Class representing a Cedar Backup reference configuration.
    2186
    2187     The reference information is just used for saving off metadata about
    2188     configuration and exists mostly for backwards-compatibility with Cedar
    2189     Backup 1.x.
    2190
    2191     @sort: __init__, __repr__, __str__, __cmp__, author, revision, description, generator
    2192     """
    2193
    2194 -   def __init__(self, author=None, revision=None, description=None, generator=None):
    2195        """
    2196        Constructor for the C{ReferenceConfig} class.
    2197
    2198        @param author: Author of the configuration file.
    2199        @param revision: Revision of the configuration file.
    2200        @param description: Description of the configuration file.
    2201        @param generator: Tool that generated the configuration file.
    2202        """
    2203        self._author = None
    2204        self._revision = None
    2205        self._description = None
    2206        self._generator = None
    2207        self.author = author
    2208        self.revision = revision
    2209        self.description = description
    2210        self.generator = generator
    2211
    2212 -   def __repr__(self):
    2213        """
    2214        Official string representation for class instance.
    2215        """
    2216        return "ReferenceConfig(%s, %s, %s, %s)" % (self.author, self.revision, self.description, self.generator)
    2217
    2218 -   def __str__(self):
    2219        """
    2220        Informal string representation for class instance.
    2221        """
    2222        return self.__repr__()
    2223
    2224 -   def __cmp__(self, other):
    2225        """
    2226        Definition of equals operator for this class.
    2227        @param other: Other object to compare to.
    2228        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    2229        """
    2230        if other is None:
    2231           return 1
    2232        if self.author != other.author:
    2233           if self.author < other.author:
    2234              return -1
    2235           else:
    2236              return 1
    2237        if self.revision != other.revision:
    2238           if self.revision < other.revision:
    2239              return -1
    2240           else:
    2241              return 1
    2242        if self.description != other.description:
    2243           if self.description < other.description:
    2244              return -1
    2245           else:
    2246              return 1
    2247        if self.generator != other.generator:
    2248           if self.generator < other.generator:
    2249              return -1
    2250           else:
    2251              return 1
    2252        return 0
    2253
    2254 -   def _setAuthor(self, value):
    2255        """
    2256        Property target used to set the author value.
    2257        No validations.
    2258        """
    2259        self._author = value
    2260
    2261 -   def _getAuthor(self):
    2262        """
    2263        Property target used to get the author value.
    2264        """
    2265        return self._author
    2266
    2267 -   def _setRevision(self, value):
    2268        """
    2269        Property target used to set the revision value.
    2270        No validations.
    2271        """
    2272        self._revision = value
    2273
    2274 -   def _getRevision(self):
    2275        """
    2276        Property target used to get the revision value.
    2277        """
    2278        return self._revision
    2279
    2280 -   def _setDescription(self, value):
    2281        """
    2282        Property target used to set the description value.
    2283        No validations.
    2284        """
    2285        self._description = value
    2286
    2287 -   def _getDescription(self):
    2288        """
    2289        Property target used to get the description value.
    2290        """
    2291        return self._description
    2292
    2293 -   def _setGenerator(self, value):
    2294        """
    2295        Property target used to set the generator value.
    2296        No validations.
    2297        """
    2298        self._generator = value
    2299
    2300 -   def _getGenerator(self):
    2301        """
    2302        Property target used to get the generator value.
    2303        """
    2304        return self._generator
    2305
    2306     author = property(_getAuthor, _setAuthor, None, "Author of the configuration file.")
    2307     revision = property(_getRevision, _setRevision, None, "Revision of the configuration file.")
    2308     description = property(_getDescription, _setDescription, None, "Description of the configuration file.")
    2309     generator = property(_getGenerator, _setGenerator, None, "Tool that generated the configuration file.")
    2310
    2311
    2312  ########################################################################
    2313  # ExtensionsConfig class definition
    2314  ########################################################################
    2315
    2316 -class ExtensionsConfig(object):
    2317
    2318     """
    2319     Class representing Cedar Backup extensions configuration.
    2320
    2321     Extensions configuration is used to specify "extended actions" implemented
    2322     by code external to Cedar Backup.  For instance, a hypothetical third party
    2323     might write extension code to collect database repository data.  If they
    2324     write a properly-formatted extension function, they can use the extension
    2325     configuration to map a command-line Cedar Backup action (i.e. "database")
    2326     to their function.
    2327
    2328     The following restrictions exist on data in this class:
    2329
    2330        - If set, the order mode must be one of the values in C{VALID_ORDER_MODES}
    2331        - The actions list must be a list of C{ExtendedAction} objects.
    2332
    2333     @sort: __init__, __repr__, __str__, __cmp__, orderMode, actions
    2334     """
    2335
    2336 -   def __init__(self, actions=None, orderMode=None):
    2337        """
    2338        Constructor for the C{ExtensionsConfig} class.
    2339        @param actions: List of extended actions
    2340        """
    2341        self._orderMode = None
    2342        self._actions = None
    2343        self.orderMode = orderMode
    2344        self.actions = actions
    2345
    2346 -   def __repr__(self):
    2347        """
    2348        Official string representation for class instance.
    2349        """
    2350        return "ExtensionsConfig(%s, %s)" % (self.orderMode, self.actions)
    2351
    2352 -   def __str__(self):
    2353        """
    2354        Informal string representation for class instance.
    2355        """
    2356        return self.__repr__()
    2357
    2358 -   def __cmp__(self, other):
    2359        """
    2360        Definition of equals operator for this class.
    2361        @param other: Other object to compare to.
    2362        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    2363        """
    2364        if other is None:
    2365           return 1
    2366        if self.orderMode != other.orderMode:
    2367           if self.orderMode < other.orderMode:
    2368              return -1
    2369           else:
    2370              return 1
    2371        if self.actions != other.actions:
    2372           if self.actions < other.actions:
    2373              return -1
    2374           else:
    2375              return 1
    2376        return 0
    2377
    2378 -   def _setOrderMode(self, value):
    2379        """
    2380        Property target used to set the order mode.
    2381        The value must be one of L{VALID_ORDER_MODES}.
    2382        @raise ValueError: If the value is not valid.
    2383        """
    2384        if value is not None:
    2385           if value not in VALID_ORDER_MODES:
    2386              raise ValueError("Order mode must be one of %s." % VALID_ORDER_MODES)
    2387        self._orderMode = value
    2388
    2389 -   def _getOrderMode(self):
    2390        """
    2391        Property target used to get the order mode.
    2392        """
    2393        return self._orderMode
    2394
    2395 -   def _setActions(self, value):
    2396        """
    2397        Property target used to set the actions list.
    2398        Either the value must be C{None} or each element must be an C{ExtendedAction}.
    2399        @raise ValueError: If the value is not a C{ExtendedAction}
    2400        """
    2401        if value is None:
    2402           self._actions = None
    2403        else:
    2404           try:
    2405              saved = self._actions
    2406              self._actions = ObjectTypeList(ExtendedAction, "ExtendedAction")
    2407              self._actions.extend(value)
    2408           except Exception, e:
    2409              self._actions = saved
    2410              raise e
    2411
    2412 -   def _getActions(self):
    2413        """
    2414        Property target used to get the actions list.
    2415        """
    2416        return self._actions
    2417
    2418     orderMode = property(_getOrderMode, _setOrderMode, None, "Order mode for extensions, to control execution ordering.")
    2419     actions = property(_getActions, _setActions, None, "List of extended actions.")
    2420
    2421
    2422  ########################################################################
    2423  # OptionsConfig class definition
    2424  ########################################################################
    2425
    2426 -class OptionsConfig(object):
    2427
    2428     """
    2429     Class representing a Cedar Backup global options configuration.
    2430
    2431     The options section is used to store global configuration options and
    2432     defaults that can be applied to other sections.
    2433
    2434     The following restrictions exist on data in this class:
    2435
    2436        - The working directory must be an absolute path.
    2437        - The starting day must be a day of the week in English, i.e. C{"monday"}, C{"tuesday"}, etc.
    2438        - All of the other values must be non-empty strings if they are set to something other than C{None}.
    2439        - The overrides list must be a list of C{CommandOverride} objects.
    2440        - The hooks list must be a list of C{ActionHook} objects.
    2441        - The cback command must be a non-empty string.
    2442        - Any managed action name must be a non-empty string matching C{ACTION_NAME_REGEX}
    2443
    2444     @sort: __init__, __repr__, __str__, __cmp__, startingDay, workingDir,
    2445           backupUser, backupGroup, rcpCommand, rshCommand, overrides
    2446     """
    2447
    2448 -   def __init__(self, startingDay=None, workingDir=None, backupUser=None,
    2449                  backupGroup=None, rcpCommand=None, overrides=None,
    2450                  hooks=None, rshCommand=None, cbackCommand=None,
    2451                  managedActions=None):
    2452        """
    2453        Constructor for the C{OptionsConfig} class.
    2454
    2455        @param startingDay: Day that starts the week.
    2456        @param workingDir: Working (temporary) directory to use for backups.
    2457        @param backupUser: Effective user that backups should run as.
    2458        @param backupGroup: Effective group that backups should run as.
    2459        @param rcpCommand: Default rcp-compatible copy command for staging.
    2460        @param rshCommand: Default rsh-compatible command to use for remote shells.
    2461        @param cbackCommand: Default cback-compatible command to use on managed remote peers.
    2462        @param overrides: List of configured command path overrides, if any.
    2463        @param hooks: List of configured pre- and post-action hooks.
    2464        @param managedActions: Default set of actions that are managed on remote peers.
    2465
    2466        @raise ValueError: If one of the values is invalid.
    2467        """
    2468        self._startingDay = None
    2469        self._workingDir = None
    2470        self._backupUser = None
    2471        self._backupGroup = None
    2472        self._rcpCommand = None
    2473        self._rshCommand = None
    2474        self._cbackCommand = None
    2475        self._overrides = None
    2476        self._hooks = None
    2477        self._managedActions = None
    2478        self.startingDay = startingDay
    2479        self.workingDir = workingDir
    2480        self.backupUser = backupUser
    2481        self.backupGroup = backupGroup
    2482        self.rcpCommand = rcpCommand
    2483        self.rshCommand = rshCommand
    2484        self.cbackCommand = cbackCommand
    2485        self.overrides = overrides
    2486        self.hooks = hooks
    2487        self.managedActions = managedActions
    2488
    2489 -   def __repr__(self):
    2490        """
    2491        Official string representation for class instance.
    2492        """
    2493        return "OptionsConfig(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.startingDay, self.workingDir,
    2494                                                                          self.backupUser, self.backupGroup,
    2495                                                                          self.rcpCommand, self.overrides,
    2496                                                                          self.hooks, self.rshCommand,
    2497                                                                          self.cbackCommand, self.managedActions)
    2498
    2499 -   def __str__(self):
    2500        """
    2501        Informal string representation for class instance.
    2502        """
    2503        return self.__repr__()
    2504
    2505 -   def __cmp__(self, other):
    2506        """
    2507        Definition of equals operator for this class.
    2508        @param other: Other object to compare to.
    2509        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    2510        """
    2511        if other is None:
    2512           return 1
    2513        if self.startingDay != other.startingDay:
    2514           if self.startingDay < other.startingDay:
    2515              return -1
    2516           else:
    2517              return 1
    2518        if self.workingDir != other.workingDir:
    2519           if self.workingDir < other.workingDir:
    2520              return -1
    2521           else:
    2522              return 1
    2523        if self.backupUser != other.backupUser:
    2524           if self.backupUser < other.backupUser:
    2525              return -1
    2526           else:
    2527              return 1
    2528        if self.backupGroup != other.backupGroup:
    2529           if self.backupGroup < other.backupGroup:
    2530              return -1
    2531           else:
    2532              return 1
    2533        if self.rcpCommand != other.rcpCommand:
    2534           if self.rcpCommand < other.rcpCommand:
    2535              return -1
    2536           else:
    2537              return 1
    2538        if self.rshCommand != other.rshCommand:
    2539           if self.rshCommand < other.rshCommand:
    2540              return -1
    2541           else:
    2542              return 1
    2543        if self.cbackCommand != other.cbackCommand:
    2544           if self.cbackCommand < other.cbackCommand:
    2545              return -1
    2546           else:
    2547              return 1
    2548        if self.overrides != other.overrides:
    2549           if self.overrides < other.overrides:
    2550              return -1
    2551           else:
    2552              return 1
    2553        if self.hooks != other.hooks:
    2554           if self.hooks < other.hooks:
    2555              return -1
    2556           else:
    2557              return 1
    2558        if self.managedActions != other.managedActions:
    2559           if self.managedActions < other.managedActions:
    2560              return -1
    2561           else:
    2562              return 1
    2563        return 0
    2564
    2565 -   def addOverride(self, command, absolutePath):
    2566        """
    2567        If no override currently exists for the command, add one.
    2568        @param command: Name of command to be overridden.
    2569        @param absolutePath: Absolute path of the overrridden command.
    2570        """
    2571        override = CommandOverride(command, absolutePath)
    2572        if self.overrides is None:
    2573           self.overrides = [ override, ]
    2574        else:
    2575           exists = False
    2576           for obj in self.overrides:
    2577              if obj.command == override.command:
    2578                 exists = True
    2579                 break
    2580           if not exists:
    2581              self.overrides.append(override)
    2582
    2583 -   def replaceOverride(self, command, absolutePath):
    2584        """
    2585        If override currently exists for the command, replace it; otherwise add it.
    2586        @param command: Name of command to be overridden.
    2587        @param absolutePath: Absolute path of the overrridden command.
    2588        """
    2589        override = CommandOverride(command, absolutePath)
    2590        if self.overrides is None:
    2591           self.overrides = [ override, ]
    2592        else:
    2593           exists = False
    2594           for obj in self.overrides:
    2595              if obj.command == override.command:
    2596                 exists = True
    2597                 obj.absolutePath = override.absolutePath
    2598                 break
    2599           if not exists:
    2600              self.overrides.append(override)
    2601
    2602 -   def _setStartingDay(self, value):
    2603        """
    2604        Property target used to set the starting day.
    2605        If it is not C{None}, the value must be a valid English day of the week,
    2606        one of C{"monday"}, C{"tuesday"}, C{"wednesday"}, etc.
    2607        @raise ValueError: If the value is not a valid day of the week.
    2608        """
    2609        if value is not None:
    2610           if value not in ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday", ]:
    2611              raise ValueError("Starting day must be an English day of the week, i.e. \"monday\".")
    2612        self._startingDay = value
    2613
    2614 -   def _getStartingDay(self):
    2615        """
    2616        Property target used to get the starting day.
    2617        """
    2618        return self._startingDay
    2619
    2620 -   def _setWorkingDir(self, value):
    2621        """
    2622        Property target used to set the working directory.
    2623        The value must be an absolute path if it is not C{None}.
    2624        It does not have to exist on disk at the time of assignment.
    2625        @raise ValueError: If the value is not an absolute path.
    2626        @raise ValueError: If the value cannot be encoded properly.
    2627        """
    2628        if value is not None:
    2629           if not os.path.isabs(value):
    2630              raise ValueError("Working directory must be an absolute path.")
    2631        self._workingDir = encodePath(value)
    2632
    2633 -   def _getWorkingDir(self):
    2634        """
    2635        Property target used to get the working directory.
    2636        """
    2637        return self._workingDir
    2638
    2639 -   def _setBackupUser(self, value):
    2640        """
    2641        Property target used to set the backup user.
    2642        The value must be a non-empty string if it is not C{None}.
    2643        @raise ValueError: If the value is an empty string.
    2644        """
    2645        if value is not None:
    2646           if len(value) < 1:
    2647              raise ValueError("Backup user must be a non-empty string.")
    2648        self._backupUser = value
    2649
    2650 -   def _getBackupUser(self):
    2651        """
    2652        Property target used to get the backup user.
    2653        """
    2654        return self._backupUser
    2655
    2656 -   def _setBackupGroup(self, value):
    2657        """
    2658        Property target used to set the backup group.
    2659        The value must be a non-empty string if it is not C{None}.
    2660        @raise ValueError: If the value is an empty string.
    2661        """
    2662        if value is not None:
    2663           if len(value) < 1:
    2664              raise ValueError("Backup group must be a non-empty string.")
    2665        self._backupGroup = value
    2666
    2667 -   def _getBackupGroup(self):
    2668        """
    2669        Property target used to get the backup group.
    2670        """
    2671        return self._backupGroup
    2672
    2673 -   def _setRcpCommand(self, value):
    2674        """
    2675        Property target used to set the rcp command.
    2676        The value must be a non-empty string if it is not C{None}.
    2677        @raise ValueError: If the value is an empty string.
    2678        """
    2679        if value is not None:
    2680           if len(value) < 1:
    2681              raise ValueError("The rcp command must be a non-empty string.")
    2682        self._rcpCommand = value
    2683
    2684 -   def _getRcpCommand(self):
    2685        """
    2686        Property target used to get the rcp command.
    2687        """
    2688        return self._rcpCommand
    2689
    2690 -   def _setRshCommand(self, value):
    2691        """
    2692        Property target used to set the rsh command.
    2693        The value must be a non-empty string if it is not C{None}.
    2694        @raise ValueError: If the value is an empty string.
    2695        """
    2696        if value is not None:
    2697           if len(value) < 1:
    2698              raise ValueError("The rsh command must be a non-empty string.")
    2699        self._rshCommand = value
    2700
    2701 -   def _getRshCommand(self):
    2702        """
    2703        Property target used to get the rsh command.
    2704        """
    2705        return self._rshCommand
    2706
    2707 -   def _setCbackCommand(self, value):
    2708        """
    2709        Property target used to set the cback command.
    2710        The value must be a non-empty string if it is not C{None}.
    2711        @raise ValueError: If the value is an empty string.
    2712        """
    2713        if value is not None:
    2714           if len(value) < 1:
    2715              raise ValueError("The cback command must be a non-empty string.")
    2716        self._cbackCommand = value
    2717
    2718 -   def _getCbackCommand(self):
    2719        """
    2720        Property target used to get the cback command.
    2721        """
    2722        return self._cbackCommand
    2723
    2724 -   def _setOverrides(self, value):
    2725        """
    2726        Property target used to set the command path overrides list.
    2727        Either the value must be C{None} or each element must be a C{CommandOverride}.
    2728        @raise ValueError: If the value is not a C{CommandOverride}
    2729        """
    2730        if value is None:
    2731           self._overrides = None
    2732        else:
    2733           try:
    2734              saved = self._overrides
    2735              self._overrides = ObjectTypeList(CommandOverride, "CommandOverride")
    2736              self._overrides.extend(value)
    2737           except Exception, e:
    2738              self._overrides = saved
    2739              raise e
    2740
    2741 -   def _getOverrides(self):
    2742        """
    2743        Property target used to get the command path overrides list.
    2744        """
    2745        return self._overrides
    2746
    2747 -   def _setHooks(self, value):
    2748        """
    2749        Property target used to set the pre- and post-action hooks list.
    2750        Either the value must be C{None} or each element must be an C{ActionHook}.
    2751        @raise ValueError: If the value is not a C{CommandOverride}
    2752        """
    2753        if value is None:
    2754           self._hooks = None
    2755        else:
    2756           try:
    2757              saved = self._hooks
    2758              self._hooks = ObjectTypeList(ActionHook, "ActionHook")
    2759              self._hooks.extend(value)
    2760           except Exception, e:
    2761              self._hooks = saved
    2762              raise e
    2763
    2764 -   def _getHooks(self):
    2765        """
    2766        Property target used to get the command path hooks list.
    2767        """
    2768        return self._hooks
    2769
    2770 -   def _setManagedActions(self, value):
    2771        """
    2772        Property target used to set the managed actions list.
    2773        Elements do not have to exist on disk at the time of assignment.
    2774        """
    2775        if value is None:
    2776           self._managedActions = None
    2777        else:
    2778           try:
    2779              saved = self._managedActions
    2780              self._managedActions = RegexMatchList(ACTION_NAME_REGEX, emptyAllowed=False, prefix="Action name")
    2781              self._managedActions.extend(value)
    2782           except Exception, e:
    2783              self._managedActions = saved
    2784              raise e
    2785
    2786 -   def _getManagedActions(self):
    2787        """
    2788        Property target used to get the managed actions list.
    2789        """
    2790        return self._managedActions
    2791
    2792     startingDay = property(_getStartingDay, _setStartingDay, None, "Day that starts the week.")
    2793     workingDir = property(_getWorkingDir, _setWorkingDir, None, "Working (temporary) directory to use for backups.")
    2794     backupUser = property(_getBackupUser, _setBackupUser, None, "Effective user that backups should run as.")
    2795     backupGroup = property(_getBackupGroup, _setBackupGroup, None, "Effective group that backups should run as.")
    2796     rcpCommand = property(_getRcpCommand, _setRcpCommand, None, "Default rcp-compatible copy command for staging.")
    2797     rshCommand = property(_getRshCommand, _setRshCommand, None, "Default rsh-compatible command to use for remote shells.")
    2798     cbackCommand = property(_getCbackCommand, _setCbackCommand, None, "Default cback-compatible command to use on managed remote peers.")
    2799     overrides = property(_getOverrides, _setOverrides, None, "List of configured command path overrides, if any.")
    2800     hooks = property(_getHooks, _setHooks, None, "List of configured pre- and post-action hooks.")
    2801     managedActions = property(_getManagedActions, _setManagedActions, None, "Default set of actions that are managed on remote peers.")
    2802
    2803
    2804  ########################################################################
    2805  # PeersConfig class definition
    2806  ########################################################################
    2807
    2808 -class PeersConfig(object):
    2809
    2810     """
    2811     Class representing Cedar Backup global peer configuration.
    2812
    2813     This section contains a list of local and remote peers in a master's backup
    2814     pool.  The section is optional.  If a master does not define this section,
    2815     then all peers are unmanaged, and the stage configuration section must
    2816     explicitly list any peer that is to be staged.  If this section is
    2817     configured, then peers may be managed or unmanaged, and the stage section
    2818     peer configuration (if any) completely overrides this configuration.
    2819
    2820     The following restrictions exist on data in this class:
    2821
    2822        - The list of local peers must contain only C{LocalPeer} objects
    2823        - The list of remote peers must contain only C{RemotePeer} objects
    2824
    2825     @note: Lists within this class are "unordered" for equality comparisons.
    2826
    2827     @sort: __init__, __repr__, __str__, __cmp__, localPeers, remotePeers
    2828     """
    2829
    2830 -   def __init__(self, localPeers=None, remotePeers=None):
    2831        """
    2832        Constructor for the C{PeersConfig} class.
    2833
    2834        @param localPeers: List of local peers.
    2835        @param remotePeers: List of remote peers.
    2836
    2837        @raise ValueError: If one of the values is invalid.
    2838        """
    2839        self._localPeers = None
    2840        self._remotePeers = None
    2841        self.localPeers = localPeers
    2842        self.remotePeers = remotePeers
    2843
    2844 -   def __repr__(self):
    2845        """
    2846        Official string representation for class instance.
    2847        """
    2848        return "PeersConfig(%s, %s)" % (self.localPeers, self.remotePeers)
    2849
    2850 -   def __str__(self):
    2851        """
    2852        Informal string representation for class instance.
    2853        """
    2854        return self.__repr__()
    2855
    2856 -   def __cmp__(self, other):
    2857        """
    2858        Definition of equals operator for this class.
    2859        Lists within this class are "unordered" for equality comparisons.
    2860        @param other: Other object to compare to.
    2861        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    2862        """
    2863        if other is None:
    2864           return 1
    2865        if self.localPeers != other.localPeers:
    2866           if self.localPeers < other.localPeers:
    2867              return -1
    2868           else:
    2869              return 1
    2870        if self.remotePeers != other.remotePeers:
    2871           if self.remotePeers < other.remotePeers:
    2872              return -1
    2873           else:
    2874              return 1
    2875        return 0
    2876
    2877 -   def hasPeers(self):
    2878        """
    2879        Indicates whether any peers are filled into this object.
    2880        @return: Boolean true if any local or remote peers are filled in, false otherwise.
    2881        """
    2882        return ((self.localPeers is not None and len(self.localPeers) > 0) or
    2883                (self.remotePeers is not None and len(self.remotePeers) > 0))
    2884
    2885 -   def _setLocalPeers(self, value):
    2886        """
    2887        Property target used to set the local peers list.
    2888        Either the value must be C{None} or each element must be a C{LocalPeer}.
    2889        @raise ValueError: If the value is not an absolute path.
    2890        """
    2891        if value is None:
    2892           self._localPeers = None
    2893        else:
    2894           try:
    2895              saved = self._localPeers
    2896              self._localPeers = ObjectTypeList(LocalPeer, "LocalPeer")
    2897              self._localPeers.extend(value)
    2898           except Exception, e:
    2899              self._localPeers = saved
    2900              raise e
    2901
    2902 -   def _getLocalPeers(self):
    2903        """
    2904        Property target used to get the local peers list.
    2905        """
    2906        return self._localPeers
    2907
    2908 -   def _setRemotePeers(self, value):
    2909        """
    2910        Property target used to set the remote peers list.
    2911        Either the value must be C{None} or each element must be a C{RemotePeer}.
    2912        @raise ValueError: If the value is not a C{RemotePeer}
    2913        """
    2914        if value is None:
    2915           self._remotePeers = None
    2916        else:
    2917           try:
    2918              saved = self._remotePeers
    2919              self._remotePeers = ObjectTypeList(RemotePeer, "RemotePeer")
    2920              self._remotePeers.extend(value)
    2921           except Exception, e:
    2922              self._remotePeers = saved
    2923              raise e
    2924
    2925 -   def _getRemotePeers(self):
    2926        """
    2927        Property target used to get the remote peers list.
    2928        """
    2929        return self._remotePeers
    2930
    2931     localPeers = property(_getLocalPeers, _setLocalPeers, None, "List of local peers.")
    2932     remotePeers = property(_getRemotePeers, _setRemotePeers, None, "List of remote peers.")
    2933
    2934
    2935  ########################################################################
    2936  # CollectConfig class definition
    2937  ########################################################################
    2938
    2939 -class CollectConfig(object):
    2940
    2941     """
    2942     Class representing a Cedar Backup collect configuration.
    2943
    2944     The following restrictions exist on data in this class:
    2945
    2946        - The target directory must be an absolute path.
    2947        - The collect mode must be one of the values in L{VALID_COLLECT_MODES}.
    2948        - The archive mode must be one of the values in L{VALID_ARCHIVE_MODES}.
    2949        - The ignore file must be a non-empty string.
    2950        - Each of the paths in C{absoluteExcludePaths} must be an absolute path
    2951        - The collect file list must be a list of C{CollectFile} objects.
    2952        - The collect directory list must be a list of C{CollectDir} objects.
    2953
    2954     For the C{absoluteExcludePaths} list, validation is accomplished through the
    2955     L{util.AbsolutePathList} list implementation that overrides common list
    2956     methods and transparently does the absolute path validation for us.
    2957
    2958     For the C{collectFiles} and C{collectDirs} list, validation is accomplished
    2959     through the L{util.ObjectTypeList} list implementation that overrides common
    2960     list methods and transparently ensures that each element has an appropriate
    2961     type.
    2962
    2963     @note: Lists within this class are "unordered" for equality comparisons.
    2964
    2965     @sort: __init__, __repr__, __str__, __cmp__, targetDir,
    2966            collectMode, archiveMode, ignoreFile, absoluteExcludePaths,
    2967            excludePatterns, collectFiles, collectDirs
    2968     """
    2969
    2970 -   def __init__(self, targetDir=None, collectMode=None, archiveMode=None, ignoreFile=None,
    2971                  absoluteExcludePaths=None, excludePatterns=None, collectFiles=None,
    2972                  collectDirs=None):
    2973        """
    2974        Constructor for the C{CollectConfig} class.
    2975
    2976        @param targetDir: Directory to collect files into.
    2977        @param collectMode: Default collect mode.
    2978        @param archiveMode: Default archive mode for collect files.
    2979        @param ignoreFile: Default ignore file name.
    2980        @param absoluteExcludePaths: List of absolute paths to exclude.
    2981        @param excludePatterns: List of regular expression patterns to exclude.
    2982        @param collectFiles: List of collect files.
    2983        @param collectDirs: List of collect directories.
    2984
    2985        @raise ValueError: If one of the values is invalid.
    2986        """
    2987        self._targetDir = None
    2988        self._collectMode = None
    2989        self._archiveMode = None
    2990        self._ignoreFile = None
    2991        self._absoluteExcludePaths = None
    2992        self._excludePatterns = None
    2993        self._collectFiles = None
    2994        self._collectDirs = None
    2995        self.targetDir = targetDir
    2996        self.collectMode = collectMode
    2997        self.archiveMode = archiveMode
    2998        self.ignoreFile = ignoreFile
    2999        self.absoluteExcludePaths = absoluteExcludePaths
    3000        self.excludePatterns = excludePatterns
    3001        self.collectFiles = collectFiles
    3002        self.collectDirs = collectDirs
    3003
    3004 -   def __repr__(self):
    3005        """
    3006        Official string representation for class instance.
    3007        """
    3008        return "CollectConfig(%s, %s, %s, %s, %s, %s, %s, %s)" % (self.targetDir, self.collectMode, self.archiveMode,
    3009                                                                  self.ignoreFile, self.absoluteExcludePaths,
    3010                                                                  self.excludePatterns, self.collectFiles, self.collectDirs)
    3011
    3012 -   def __str__(self):
    3013        """
    3014        Informal string representation for class instance.
    3015        """
    3016        return self.__repr__()
    3017
    3018 -   def __cmp__(self, other):
    3019        """
    3020        Definition of equals operator for this class.
    3021        Lists within this class are "unordered" for equality comparisons.
    3022        @param other: Other object to compare to.
    3023        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    3024        """
    3025        if other is None:
    3026           return 1
    3027        if self.targetDir != other.targetDir:
    3028           if self.targetDir < other.targetDir:
    3029              return -1
    3030           else:
    3031              return 1
    3032        if self.collectMode != other.collectMode:
    3033           if self.collectMode < other.collectMode:
    3034              return -1
    3035           else:
    3036              return 1
    3037        if self.archiveMode != other.archiveMode:
    3038           if self.archiveMode < other.archiveMode:
    3039              return -1
    3040           else:
    3041              return 1
    3042        if self.ignoreFile != other.ignoreFile:
    3043           if self.ignoreFile < other.ignoreFile:
    3044              return -1
    3045           else:
    3046              return 1
    3047        if self.absoluteExcludePaths != other.absoluteExcludePaths:
    3048           if self.absoluteExcludePaths < other.absoluteExcludePaths:
    3049              return -1
    3050           else:
    3051              return 1
    3052        if self.excludePatterns != other.excludePatterns:
    3053           if self.excludePatterns < other.excludePatterns:
    3054              return -1
    3055           else:
    3056              return 1
    3057        if self.collectFiles != other.collectFiles:
    3058           if self.collectFiles < other.collectFiles:
    3059              return -1
    3060           else:
    3061              return 1
    3062        if self.collectDirs != other.collectDirs:
    3063           if self.collectDirs < other.collectDirs:
    3064              return -1
    3065           else:
    3066              return 1
    3067        return 0
    3068
    3069 -   def _setTargetDir(self, value):
    3070        """
    3071        Property target used to set the target directory.
    3072        The value must be an absolute path if it is not C{None}.
    3073        It does not have to exist on disk at the time of assignment.
    3074        @raise ValueError: If the value is not an absolute path.
    3075        @raise ValueError: If the value cannot be encoded properly.
    3076        """
    3077        if value is not None:
    3078           if not os.path.isabs(value):
    3079              raise ValueError("Target directory must be an absolute path.")
    3080        self._targetDir = encodePath(value)
    3081
    3082 -   def _getTargetDir(self):
    3083        """
    3084        Property target used to get the target directory.
    3085        """
    3086        return self._targetDir
    3087
    3088 -   def _setCollectMode(self, value):
    3089        """
    3090        Property target used to set the collect mode.
    3091        If not C{None}, the mode must be one of L{VALID_COLLECT_MODES}.
    3092        @raise ValueError: If the value is not valid.
    3093        """
    3094        if value is not None:
    3095           if value not in VALID_COLLECT_MODES:
    3096              raise ValueError("Collect mode must be one of %s." % VALID_COLLECT_MODES)
    3097        self._collectMode = value
    3098
    3099 -   def _getCollectMode(self):
    3100        """
    3101        Property target used to get the collect mode.
    3102        """
    3103        return self._collectMode
    3104
    3105 -   def _setArchiveMode(self, value):
    3106        """
    3107        Property target used to set the archive mode.
    3108        If not C{None}, the mode must be one of L{VALID_ARCHIVE_MODES}.
    3109        @raise ValueError: If the value is not valid.
    3110        """
    3111        if value is not None:
    3112           if value not in VALID_ARCHIVE_MODES:
    3113              raise ValueError("Archive mode must be one of %s." % VALID_ARCHIVE_MODES)
    3114        self._archiveMode = value
    3115
    3116 -   def _getArchiveMode(self):
    3117        """
    3118        Property target used to get the archive mode.
    3119        """
    3120        return self._archiveMode
    3121
    3122 -   def _setIgnoreFile(self, value):
    3123        """
    3124        Property target used to set the ignore file.
    3125        The value must be a non-empty string if it is not C{None}.
    3126        @raise ValueError: If the value is an empty string.
    3127        @raise ValueError: If the value cannot be encoded properly.
    3128        """
    3129        if value is not None:
    3130           if len(value) < 1:
    3131              raise ValueError("The ignore file must be a non-empty string.")
    3132        self._ignoreFile = encodePath(value)
    3133
    3134 -   def _getIgnoreFile(self):
    3135        """
    3136        Property target used to get the ignore file.
    3137        """
    3138        return self._ignoreFile
    3139
    3140 -   def _setAbsoluteExcludePaths(self, value):
    3141        """
    3142        Property target used to set the absolute exclude paths list.
    3143        Either the value must be C{None} or each element must be an absolute path.
    3144        Elements do not have to exist on disk at the time of assignment.
    3145        @raise ValueError: If the value is not an absolute path.
    3146        """
    3147        if value is None:
    3148           self._absoluteExcludePaths = None
    3149        else:
    3150           try:
    3151              saved = self._absoluteExcludePaths
    3152              self._absoluteExcludePaths = AbsolutePathList()
    3153              self._absoluteExcludePaths.extend(value)
    3154           except Exception, e:
    3155              self._absoluteExcludePaths = saved
    3156              raise e
    3157
    3158 -   def _getAbsoluteExcludePaths(self):
    3159        """
    3160        Property target used to get the absolute exclude paths list.
    3161        """
    3162        return self._absoluteExcludePaths
    3163
    3164 -   def _setExcludePatterns(self, value):
    3165        """
    3166        Property target used to set the exclude patterns list.
    3167        """
    3168        if value is None:
    3169           self._excludePatterns = None
    3170        else:
    3171           try:
    3172              saved = self._excludePatterns
    3173              self._excludePatterns = RegexList()
    3174              self._excludePatterns.extend(value)
    3175           except Exception, e:
    3176              self._excludePatterns = saved
    3177              raise e
    3178
    3179 -   def _getExcludePatterns(self):
    3180        """
    3181        Property target used to get the exclude patterns list.
    3182        """
    3183        return self._excludePatterns
    3184
    3185 -   def _setCollectFiles(self, value):
    3186        """
    3187        Property target used to set the collect files list.
    3188        Either the value must be C{None} or each element must be a C{CollectFile}.
    3189        @raise ValueError: If the value is not a C{CollectFile}
    3190        """
    3191        if value is None:
    3192           self._collectFiles = None
    3193        else:
    3194           try:
    3195              saved = self._collectFiles
    3196              self._collectFiles = ObjectTypeList(CollectFile, "CollectFile")
    3197              self._collectFiles.extend(value)
    3198           except Exception, e:
    3199              self._collectFiles = saved
    3200              raise e
    3201
    3202 -   def _getCollectFiles(self):
    3203        """
    3204        Property target used to get the collect files list.
    3205        """
    3206        return self._collectFiles
    3207
    3208 -   def _setCollectDirs(self, value):
    3209        """
    3210        Property target used to set the collect dirs list.
    3211        Either the value must be C{None} or each element must be a C{CollectDir}.
    3212        @raise ValueError: If the value is not a C{CollectDir}
    3213        """
    3214        if value is None:
    3215           self._collectDirs = None
    3216        else:
    3217           try:
    3218              saved = self._collectDirs
    3219              self._collectDirs = ObjectTypeList(CollectDir, "CollectDir")
    3220              self._collectDirs.extend(value)
    3221           except Exception, e:
    3222              self._collectDirs = saved
    3223              raise e
    3224
    3225 -   def _getCollectDirs(self):
    3226        """
    3227        Property target used to get the collect dirs list.
    3228        """
    3229        return self._collectDirs
    3230
    3231     targetDir = property(_getTargetDir, _setTargetDir, None, "Directory to collect files into.")
    3232     collectMode = property(_getCollectMode, _setCollectMode, None, "Default collect mode.")
    3233     archiveMode = property(_getArchiveMode, _setArchiveMode, None, "Default archive mode for collect files.")
    3234     ignoreFile = property(_getIgnoreFile, _setIgnoreFile, None, "Default ignore file name.")
    3235     absoluteExcludePaths = property(_getAbsoluteExcludePaths, _setAbsoluteExcludePaths, None, "List of absolute paths to exclude.")
    3236     excludePatterns = property(_getExcludePatterns, _setExcludePatterns, None, "List of regular expressions patterns to exclude.")
    3237     collectFiles = property(_getCollectFiles, _setCollectFiles, None, "List of collect files.")
    3238     collectDirs = property(_getCollectDirs, _setCollectDirs, None, "List of collect directories.")
    3239
    3240
    3241  ########################################################################
    3242  # StageConfig class definition
    3243  ########################################################################
    3244
    3245 -class StageConfig(object):
    3246
    3247     """
    3248     Class representing a Cedar Backup stage configuration.
    3249
    3250     The following restrictions exist on data in this class:
    3251
    3252        - The target directory must be an absolute path
    3253        - The list of local peers must contain only C{LocalPeer} objects
    3254        - The list of remote peers must contain only C{RemotePeer} objects
    3255
    3256     @note: Lists within this class are "unordered" for equality comparisons.
    3257
    3258     @sort: __init__, __repr__, __str__, __cmp__, targetDir, localPeers, remotePeers
    3259     """
    3260
    3261 -   def __init__(self, targetDir=None, localPeers=None, remotePeers=None):
    3262        """
    3263        Constructor for the C{StageConfig} class.
    3264
    3265        @param targetDir: Directory to stage files into, by peer name.
    3266        @param localPeers: List of local peers.
    3267        @param remotePeers: List of remote peers.
    3268
    3269        @raise ValueError: If one of the values is invalid.
    3270        """
    3271        self._targetDir = None
    3272        self._localPeers = None
    3273        self._remotePeers = None
    3274        self.targetDir = targetDir
    3275        self.localPeers = localPeers
    3276        self.remotePeers = remotePeers
    3277
    3278 -   def __repr__(self):
    3279        """
    3280        Official string representation for class instance.
    3281        """
    3282        return "StageConfig(%s, %s, %s)" % (self.targetDir, self.localPeers, self.remotePeers)
    3283
    3284 -   def __str__(self):
    3285        """
    3286        Informal string representation for class instance.
    3287        """
    3288        return self.__repr__()
    3289
    3290 -   def __cmp__(self, other):
    3291        """
    3292        Definition of equals operator for this class.
    3293        Lists within this class are "unordered" for equality comparisons.
    3294        @param other: Other object to compare to.
    3295        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    3296        """
    3297        if other is None:
    3298           return 1
    3299        if self.targetDir != other.targetDir:
    3300           if self.targetDir < other.targetDir:
    3301              return -1
    3302           else:
    3303              return 1
    3304        if self.localPeers != other.localPeers:
    3305           if self.localPeers < other.localPeers:
    3306              return -1
    3307           else:
    3308              return 1
    3309        if self.remotePeers != other.remotePeers:
    3310           if self.remotePeers < other.remotePeers:
    3311              return -1
    3312           else:
    3313              return 1
    3314        return 0
    3315
    3316 -   def hasPeers(self):
    3317        """
    3318        Indicates whether any peers are filled into this object.
    3319        @return: Boolean true if any local or remote peers are filled in, false otherwise.
    3320        """
    3321        return ((self.localPeers is not None and len(self.localPeers) > 0) or
    3322                (self.remotePeers is not None and len(self.remotePeers) > 0))
    3323
    3324 -   def _setTargetDir(self, value):
    3325        """
    3326        Property target used to set the target directory.
    3327        The value must be an absolute path if it is not C{None}.
    3328        It does not have to exist on disk at the time of assignment.
    3329        @raise ValueError: If the value is not an absolute path.
    3330        @raise ValueError: If the value cannot be encoded properly.
    3331        """
    3332        if value is not None:
    3333           if not os.path.isabs(value):
    3334              raise ValueError("Target directory must be an absolute path.")
    3335        self._targetDir = encodePath(value)
    3336
    3337 -   def _getTargetDir(self):
    3338        """
    3339        Property target used to get the target directory.
    3340        """
    3341        return self._targetDir
    3342
    3343 -   def _setLocalPeers(self, value):
    3344        """
    3345        Property target used to set the local peers list.
    3346        Either the value must be C{None} or each element must be a C{LocalPeer}.
    3347        @raise ValueError: If the value is not an absolute path.
    3348        """
    3349        if value is None:
    3350           self._localPeers = None
    3351        else:
    3352           try:
    3353              saved = self._localPeers
    3354              self._localPeers = ObjectTypeList(LocalPeer, "LocalPeer")
    3355              self._localPeers.extend(value)
    3356           except Exception, e:
    3357              self._localPeers = saved
    3358              raise e
    3359
    3360 -   def _getLocalPeers(self):
    3361        """
    3362        Property target used to get the local peers list.
    3363        """
    3364        return self._localPeers
    3365
    3366 -   def _setRemotePeers(self, value):
    3367        """
    3368        Property target used to set the remote peers list.
    3369        Either the value must be C{None} or each element must be a C{RemotePeer}.
    3370        @raise ValueError: If the value is not a C{RemotePeer}
    3371        """
    3372        if value is None:
    3373           self._remotePeers = None
    3374        else:
    3375           try:
    3376              saved = self._remotePeers
    3377              self._remotePeers = ObjectTypeList(RemotePeer, "RemotePeer")
    3378              self._remotePeers.extend(value)
    3379           except Exception, e:
    3380              self._remotePeers = saved
    3381              raise e
    3382
    3383 -   def _getRemotePeers(self):
    3384        """
    3385        Property target used to get the remote peers list.
    3386        """
    3387        return self._remotePeers
    3388
    3389     targetDir = property(_getTargetDir, _setTargetDir, None, "Directory to stage files into, by peer name.")
    3390     localPeers = property(_getLocalPeers, _setLocalPeers, None, "List of local peers.")
    3391     remotePeers = property(_getRemotePeers, _setRemotePeers, None, "List of remote peers.")
    3392
    3393
    3394  ########################################################################
    3395  # StoreConfig class definition
    3396  ########################################################################
    3397
    3398 -class StoreConfig(object):
    3399
    3400     """
    3401     Class representing a Cedar Backup store configuration.
    3402
    3403     The following restrictions exist on data in this class:
    3404
    3405        - The source directory must be an absolute path.
    3406        - The media type must be one of the values in L{VALID_MEDIA_TYPES}.
    3407        - The device type must be one of the values in L{VALID_DEVICE_TYPES}.
    3408        - The device path must be an absolute path.
    3409        - The SCSI id, if provided, must be in the form specified by L{validateScsiId}.
    3410        - The drive speed must be an integer >= 1
    3411        - The blanking behavior must be a C{BlankBehavior} object
    3412        - The refresh media delay must be an integer >= 0
    3413        - The eject delay must be an integer >= 0
    3414
    3415     Note that although the blanking factor must be a positive floating point
    3416     number, it is stored as a string. This is done so that we can losslessly go
    3417     back and forth between XML and object representations of configuration.
    3418
    3419     @sort: __init__, __repr__, __str__, __cmp__, sourceDir,
    3420            mediaType, deviceType, devicePath, deviceScsiId,
    3421            driveSpeed, checkData, checkMedia, warnMidnite, noEject,
    3422            blankBehavior, refreshMediaDelay, ejectDelay
    3423     """
    3424
    3425 -   def __init__(self, sourceDir=None, mediaType=None, deviceType=None,
    3426                  devicePath=None, deviceScsiId=None, driveSpeed=None,
    3427                  checkData=False, warnMidnite=False, noEject=False,
    3428                  checkMedia=False, blankBehavior=None, refreshMediaDelay=None,
    3429                  ejectDelay=None):
    3430        """
    3431        Constructor for the C{StoreConfig} class.
    3432
    3433        @param sourceDir: Directory whose contents should be written to media.
    3434        @param mediaType: Type of the media (see notes above).
    3435        @param deviceType: Type of the device (optional, see notes above).
    3436        @param devicePath: Filesystem device name for writer device, i.e. C{/dev/cdrw}.
    3437        @param deviceScsiId: SCSI id for writer device, i.e. C{[<method>:]scsibus,target,lun}.
    3438        @param driveSpeed: Speed of the drive, i.e. C{2} for 2x drive, etc.
    3439        @param checkData: Whether resulting image should be validated.
    3440        @param checkMedia: Whether media should be checked before being written to.
    3441        @param warnMidnite: Whether to generate warnings for crossing midnite.
    3442        @param noEject: Indicates that the writer device should not be ejected.
    3443        @param blankBehavior: Controls optimized blanking behavior.
    3444        @param refreshMediaDelay: Delay, in seconds, to add after refreshing media
    3445        @param ejectDelay: Delay, in seconds, to add after ejecting media before closing the tray
    3446
    3447        @raise ValueError: If one of the values is invalid.
    3448        """
    3449        self._sourceDir = None
    3450        self._mediaType = None
    3451        self._deviceType = None
    3452        self._devicePath = None
    3453        self._deviceScsiId = None
    3454        self._driveSpeed = None
    3455        self._checkData = None
    3456        self._checkMedia = None
    3457        self._warnMidnite = None
    3458        self._noEject = None
    3459        self._blankBehavior = None
    3460        self._refreshMediaDelay = None
    3461        self._ejectDelay = None
    3462        self.sourceDir = sourceDir
    3463        self.mediaType = mediaType
    3464        self.deviceType = deviceType
    3465        self.devicePath = devicePath
    3466        self.deviceScsiId = deviceScsiId
    3467        self.driveSpeed = driveSpeed
    3468        self.checkData = checkData
    3469        self.checkMedia = checkMedia
    3470        self.warnMidnite = warnMidnite
    3471        self.noEject = noEject
    3472        self.blankBehavior = blankBehavior
    3473        self.refreshMediaDelay = refreshMediaDelay
    3474        self.ejectDelay = ejectDelay
    3475
    3476 -   def __repr__(self):
    3477        """
    3478        Official string representation for class instance.
    3479        """
    3480        return "StoreConfig(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (
    3481                  self.sourceDir, self.mediaType, self.deviceType,
    3482                  self.devicePath, self.deviceScsiId, self.driveSpeed,
    3483                  self.checkData, self.warnMidnite, self.noEject,
    3484                  self.checkMedia, self.blankBehavior, self.refreshMediaDelay,
    3485                  self.ejectDelay)
    3486
    3487 -   def __str__(self):
    3488        """
    3489        Informal string representation for class instance.
    3490        """
    3491        return self.__repr__()
    3492
    3493 -   def __cmp__(self, other):
    3494        """
    3495        Definition of equals operator for this class.
    3496        @param other: Other object to compare to.
    3497        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    3498        """
    3499        if other is None:
    3500           return 1
    3501        if self.sourceDir != other.sourceDir:
    3502           if self.sourceDir < other.sourceDir:
    3503              return -1
    3504           else:
    3505              return 1
    3506        if self.mediaType != other.mediaType:
    3507           if self.mediaType < other.mediaType:
    3508              return -1
    3509           else:
    3510              return 1
    3511        if self.deviceType != other.deviceType:
    3512           if self.deviceType < other.deviceType:
    3513              return -1
    3514           else:
    3515              return 1
    3516        if self.devicePath != other.devicePath:
    3517           if self.devicePath < other.devicePath:
    3518              return -1
    3519           else:
    3520              return 1
    3521        if self.deviceScsiId != other.deviceScsiId:
    3522           if self.deviceScsiId < other.deviceScsiId:
    3523              return -1
    3524           else:
    3525              return 1
    3526        if self.driveSpeed != other.driveSpeed:
    3527           if self.driveSpeed < other.driveSpeed:
    3528              return -1
    3529           else:
    3530              return 1
    3531        if self.checkData != other.checkData:
    3532           if self.checkData < other.checkData:
    3533              return -1
    3534           else:
    3535              return 1
    3536        if self.checkMedia != other.checkMedia:
    3537           if self.checkMedia < other.checkMedia:
    3538              return -1
    3539           else:
    3540              return 1
    3541        if self.warnMidnite != other.warnMidnite:
    3542           if self.warnMidnite < other.warnMidnite:
    3543              return -1
    3544           else:
    3545              return 1
    3546        if self.noEject != other.noEject:
    3547           if self.noEject < other.noEject:
    3548              return -1
    3549           else:
    3550              return 1
    3551        if self.blankBehavior != other.blankBehavior:
    3552           if self.blankBehavior < other.blankBehavior:
    3553              return -1
    3554           else:
    3555              return 1
    3556        if self.refreshMediaDelay != other.refreshMediaDelay:
    3557           if self.refreshMediaDelay < other.refreshMediaDelay:
    3558              return -1
    3559           else:
    3560              return 1
    3561        if self.ejectDelay != other.ejectDelay:
    3562           if self.ejectDelay < other.ejectDelay:
    3563              return -1
    3564           else:
    3565              return 1
    3566        return 0
    3567
    3568 -   def _setSourceDir(self, value):
    3569        """
    3570        Property target used to set the source directory.
    3571        The value must be an absolute path if it is not C{None}.
    3572        It does not have to exist on disk at the time of assignment.
    3573        @raise ValueError: If the value is not an absolute path.
    3574        @raise ValueError: If the value cannot be encoded properly.
    3575        """
    3576        if value is not None:
    3577           if not os.path.isabs(value):
    3578              raise ValueError("Source directory must be an absolute path.")
    3579        self._sourceDir = encodePath(value)
    3580
    3581 -   def _getSourceDir(self):
    3582        """
    3583        Property target used to get the source directory.
    3584        """
    3585        return self._sourceDir
    3586
    3587 -   def _setMediaType(self, value):
    3588        """
    3589        Property target used to set the media type.
    3590        The value must be one of L{VALID_MEDIA_TYPES}.
    3591        @raise ValueError: If the value is not valid.
    3592        """
    3593        if value is not None:
    3594           if value not in VALID_MEDIA_TYPES:
    3595              raise ValueError("Media type must be one of %s." % VALID_MEDIA_TYPES)
    3596        self._mediaType = value
    3597
    3598 -   def _getMediaType(self):
    3599        """
    3600        Property target used to get the media type.
    3601        """
    3602        return self._mediaType
    3603
    3604 -   def _setDeviceType(self, value):
    3605        """
    3606        Property target used to set the device type.
    3607        The value must be one of L{VALID_DEVICE_TYPES}.
    3608        @raise ValueError: If the value is not valid.
    3609        """
    3610        if value is not None:
    3611           if value not in VALID_DEVICE_TYPES:
    3612              raise ValueError("Device type must be one of %s." % VALID_DEVICE_TYPES)
    3613        self._deviceType = value
    3614
    3615 -   def _getDeviceType(self):
    3616        """
    3617        Property target used to get the device type.
    3618        """
    3619        return self._deviceType
    3620
    3621 -   def _setDevicePath(self, value):
    3622        """
    3623        Property target used to set the device path.
    3624        The value must be an absolute path if it is not C{None}.
    3625        It does not have to exist on disk at the time of assignment.
    3626        @raise ValueError: If the value is not an absolute path.
    3627        @raise ValueError: If the value cannot be encoded properly.
    3628        """
    3629        if value is not None:
    3630           if not os.path.isabs(value):
    3631              raise ValueError("Device path must be an absolute path.")
    3632        self._devicePath = encodePath(value)
    3633
    3634 -   def _getDevicePath(self):
    3635        """
    3636        Property target used to get the device path.
    3637        """
    3638        return self._devicePath
    3639
    3640 -   def _setDeviceScsiId(self, value):
    3641        """
    3642        Property target used to set the SCSI id
    3643        The SCSI id must be valid per L{validateScsiId}.
    3644        @raise ValueError: If the value is not valid.
    3645        """
    3646        if value is None:
    3647           self._deviceScsiId = None
    3648        else:
    3649           self._deviceScsiId = validateScsiId(value)
    3650
    3651 -   def _getDeviceScsiId(self):
    3652        """
    3653        Property target used to get the SCSI id.
    3654        """
    3655        return self._deviceScsiId
    3656
    3657 -   def _setDriveSpeed(self, value):
    3658        """
    3659        Property target used to set the drive speed.
    3660        The drive speed must be valid per L{validateDriveSpeed}.
    3661        @raise ValueError: If the value is not valid.
    3662        """
    3663        self._driveSpeed = validateDriveSpeed(value)
    3664
    3665 -   def _getDriveSpeed(self):
    3666        """
    3667        Property target used to get the drive speed.
    3668        """
    3669        return self._driveSpeed
    3670
    3671 -   def _setCheckData(self, value):
    3672        """
    3673        Property target used to set the check data flag.
    3674        No validations, but we normalize the value to C{True} or C{False}.
    3675        """
    3676        if value:
    3677           self._checkData = True
    3678        else:
    3679           self._checkData = False
    3680
    3681 -   def _getCheckData(self):
    3682        """
    3683        Property target used to get the check data flag.
    3684        """
    3685        return self._checkData
    3686
    3687 -   def _setCheckMedia(self, value):
    3688        """
    3689        Property target used to set the check media flag.
    3690        No validations, but we normalize the value to C{True} or C{False}.
    3691        """
    3692        if value:
    3693           self._checkMedia = True
    3694        else:
    3695           self._checkMedia = False
    3696
    3697 -   def _getCheckMedia(self):
    3698        """
    3699        Property target used to get the check media flag.
    3700        """
    3701        return self._checkMedia
    3702
    3703 -   def _setWarnMidnite(self, value):
    3704        """
    3705        Property target used to set the midnite warning flag.
    3706        No validations, but we normalize the value to C{True} or C{False}.
    3707        """
    3708        if value:
    3709           self._warnMidnite = True
    3710        else:
    3711           self._warnMidnite = False
    3712
    3713 -   def _getWarnMidnite(self):
    3714        """
    3715        Property target used to get the midnite warning flag.
    3716        """
    3717        return self._warnMidnite
    3718
    3719 -   def _setNoEject(self, value):
    3720        """
    3721        Property target used to set the no-eject flag.
    3722        No validations, but we normalize the value to C{True} or C{False}.
    3723        """
    3724        if value:
    3725           self._noEject = True
    3726        else:
    3727           self._noEject = False
    3728
    3729 -   def _getNoEject(self):
    3730        """
    3731        Property target used to get the no-eject flag.
    3732        """
    3733        return self._noEject
    3734
    3735 -   def _setBlankBehavior(self, value):
    3736        """
    3737        Property target used to set blanking behavior configuration.
    3738        If not C{None}, the value must be a C{BlankBehavior} object.
    3739        @raise ValueError: If the value is not a C{BlankBehavior}
    3740        """
    3741        if value is None:
    3742           self._blankBehavior = None
    3743        else:
    3744           if not isinstance(value, BlankBehavior):
    3745              raise ValueError("Value must be a C{BlankBehavior} object.")
    3746           self._blankBehavior = value
    3747
    3748 -   def _getBlankBehavior(self):
    3749        """
    3750        Property target used to get the blanking behavior configuration.
    3751        """
    3752        return self._blankBehavior
    3753
    3754 -   def _setRefreshMediaDelay(self, value):
    3755        """
    3756        Property target used to set the refreshMediaDelay.
    3757        The value must be an integer >= 0.
    3758        @raise ValueError: If the value is not valid.
    3759        """
    3760        if value is None:
    3761           self._refreshMediaDelay = None
    3762        else:
    3763           try:
    3764              value = int(value)
    3765           except TypeError:
    3766              raise ValueError("Action refreshMediaDelay value must be an integer >= 0.")
    3767           if value < 0:
    3768              raise ValueError("Action refreshMediaDelay value must be an integer >= 0.")
    3769           if value == 0:
    3770              value = None  # normalize this out, since it's the default
    3771           self._refreshMediaDelay = value
    3772
    3773 -   def _getRefreshMediaDelay(self):
    3774        """
    3775        Property target used to get the action refreshMediaDelay.
    3776        """
    3777        return self._refreshMediaDelay
    3778
    3779 -   def _setEjectDelay(self, value):
    3780        """
    3781        Property target used to set the ejectDelay.
    3782        The value must be an integer >= 0.
    3783        @raise ValueError: If the value is not valid.
    3784        """
    3785        if value is None:
    3786           self._ejectDelay = None
    3787        else:
    3788           try:
    3789              value = int(value)
    3790           except TypeError:
    3791              raise ValueError("Action ejectDelay value must be an integer >= 0.")
    3792           if value < 0:
    3793              raise ValueError("Action ejectDelay value must be an integer >= 0.")
    3794           if value == 0:
    3795              value = None  # normalize this out, since it's the default
    3796           self._ejectDelay = value
    3797
    3798 -   def _getEjectDelay(self):
    3799        """
    3800        Property target used to get the action ejectDelay.
    3801        """
    3802        return self._ejectDelay
    3803
    3804     sourceDir = property(_getSourceDir, _setSourceDir, None, "Directory whose contents should be written to media.")
    3805     mediaType = property(_getMediaType, _setMediaType, None, "Type of the media (see notes above).")
    3806     deviceType = property(_getDeviceType, _setDeviceType, None, "Type of the device (optional, see notes above).")
    3807     devicePath = property(_getDevicePath, _setDevicePath, None, "Filesystem device name for writer device.")
    3808     deviceScsiId = property(_getDeviceScsiId, _setDeviceScsiId, None, "SCSI id for writer device (optional, see notes above).")
    3809     driveSpeed = property(_getDriveSpeed, _setDriveSpeed, None, "Speed of the drive.")
    3810     checkData = property(_getCheckData, _setCheckData, None, "Whether resulting image should be validated.")
    3811     checkMedia = property(_getCheckMedia, _setCheckMedia, None, "Whether media should be checked before being written to.")
    3812     warnMidnite = property(_getWarnMidnite, _setWarnMidnite, None, "Whether to generate warnings for crossing midnite.")
    3813     noEject = property(_getNoEject, _setNoEject, None, "Indicates that the writer device should not be ejected.")
    3814     blankBehavior = property(_getBlankBehavior, _setBlankBehavior, None, "Controls optimized blanking behavior.")
    3815     refreshMediaDelay = property(_getRefreshMediaDelay, _setRefreshMediaDelay, None, "Delay, in seconds, to add after refreshing media.")
    3816     ejectDelay = property(_getEjectDelay, _setEjectDelay, None, "Delay, in seconds, to add after ejecting media before closing the tray")
    3817
    3818
    3819  ########################################################################
    3820  # PurgeConfig class definition
    3821  ########################################################################
    3822
    3823 -class PurgeConfig(object):
    3824
    3825     """
    3826     Class representing a Cedar Backup purge configuration.
    3827
    3828     The following restrictions exist on data in this class:
    3829
    3830        - The purge directory list must be a list of C{PurgeDir} objects.
    3831
    3832     For the C{purgeDirs} list, validation is accomplished through the
    3833     L{util.ObjectTypeList} list implementation that overrides common list
    3834     methods and transparently ensures that each element is a C{PurgeDir}.
    3835
    3836     @note: Lists within this class are "unordered" for equality comparisons.
    3837
    3838     @sort: __init__, __repr__, __str__, __cmp__, purgeDirs
    3839     """
    3840
    3841 -   def __init__(self, purgeDirs=None):
    3842        """
    3843        Constructor for the C{Purge} class.
    3844        @param purgeDirs: List of purge directories.
    3845        @raise ValueError: If one of the values is invalid.
    3846        """
    3847        self._purgeDirs = None
    3848        self.purgeDirs = purgeDirs
    3849
    3850 -   def __repr__(self):
    3851        """
    3852        Official string representation for class instance.
    3853        """
    3854        return "PurgeConfig(%s)" % self.purgeDirs
    3855
    3856 -   def __str__(self):
    3857        """
    3858        Informal string representation for class instance.
    3859        """
    3860        return self.__repr__()
    3861
    3862 -   def __cmp__(self, other):
    3863        """
    3864        Definition of equals operator for this class.
    3865        Lists within this class are "unordered" for equality comparisons.
    3866        @param other: Other object to compare to.
    3867        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    3868        """
    3869        if other is None:
    3870           return 1
    3871        if self.purgeDirs != other.purgeDirs:
    3872           if self.purgeDirs < other.purgeDirs:
    3873              return -1
    3874           else:
    3875              return 1
    3876        return 0
    3877
    3878 -   def _setPurgeDirs(self, value):
    3879        """
    3880        Property target used to set the purge dirs list.
    3881        Either the value must be C{None} or each element must be a C{PurgeDir}.
    3882        @raise ValueError: If the value is not a C{PurgeDir}
    3883        """
    3884        if value is None:
    3885           self._purgeDirs = None
    3886        else:
    3887           try:
    3888              saved = self._purgeDirs
    3889              self._purgeDirs = ObjectTypeList(PurgeDir, "PurgeDir")
    3890              self._purgeDirs.extend(value)
    3891           except Exception, e:
    3892              self._purgeDirs = saved
    3893              raise e
    3894
    3895 -   def _getPurgeDirs(self):
    3896        """
    3897        Property target used to get the purge dirs list.
    3898        """
    3899        return self._purgeDirs
    3900
    3901     purgeDirs = property(_getPurgeDirs, _setPurgeDirs, None, "List of directories to purge.")
    3902
    3903
    3904  ########################################################################
    3905  # Config class definition
    3906  ########################################################################
    3907
    3908 -class Config(object):
    3909
    3910     ######################
    3911     # Class documentation
    3912     ######################
    3913
    3914     """
    3915     Class representing a Cedar Backup XML configuration document.
    3916
    3917     The C{Config} class is a Python object representation of a Cedar Backup XML
    3918     configuration file.  It is intended to be the only Python-language interface
    3919     to Cedar Backup configuration on disk for both Cedar Backup itself and for
    3920     external applications.
    3921
    3922     The object representation is two-way: XML data can be used to create a
    3923     C{Config} object, and then changes to the object can be propogated back to
    3924     disk.  A C{Config} object can even be used to create a configuration file
    3925     from scratch programmatically.
    3926
    3927     This class and the classes it is composed from often use Python's
    3928     C{property} construct to validate input and limit access to values.  Some
    3929     validations can only be done once a document is considered "complete"
    3930     (see module notes for more details).
    3931
    3932     Assignments to the various instance variables must match the expected
    3933     type, i.e. C{reference} must be a C{ReferenceConfig}.  The internal check
    3934     uses the built-in C{isinstance} function, so it should be OK to use
    3935     subclasses if you want to.
    3936
    3937     If an instance variable is not set, its value will be C{None}.  When an
    3938     object is initialized without using an XML document, all of the values
    3939     will be C{None}.  Even when an object is initialized using XML, some of
    3940     the values might be C{None} because not every section is required.
    3941
    3942     @note: Lists within this class are "unordered" for equality comparisons.
    3943
    3944     @sort: __init__, __repr__, __str__, __cmp__, extractXml, validate,
    3945            reference, extensions, options, collect, stage, store, purge,
    3946            _getReference, _setReference, _getExtensions, _setExtensions,
    3947            _getOptions, _setOptions, _getPeers, _setPeers, _getCollect,
    3948            _setCollect, _getStage, _setStage, _getStore, _setStore,
    3949            _getPurge, _setPurge
    3950     """
    3951
    3952     ##############
    3953     # Constructor
    3954     ##############
    3955
    3956 -   def __init__(self, xmlData=None, xmlPath=None, validate=True):
    3957        """
    3958        Initializes a configuration object.
    3959
    3960        If you initialize the object without passing either C{xmlData} or
    3961        C{xmlPath}, then configuration will be empty and will be invalid until it
    3962        is filled in properly.
    3963
    3964        No reference to the original XML data or original path is saved off by
    3965        this class.  Once the data has been parsed (successfully or not) this
    3966        original information is discarded.
    3967
    3968        Unless the C{validate} argument is C{False}, the L{Config.validate}
    3969        method will be called (with its default arguments) against configuration
    3970        after successfully parsing any passed-in XML.  Keep in mind that even if
    3971        C{validate} is C{False}, it might not be possible to parse the passed-in
    3972        XML document if lower-level validations fail.
    3973
    3974        @note: It is strongly suggested that the C{validate} option always be set
    3975        to C{True} (the default) unless there is a specific need to read in
    3976        invalid configuration from disk.
    3977
    3978        @param xmlData: XML data representing configuration.
    3979        @type xmlData: String data.
    3980
    3981        @param xmlPath: Path to an XML file on disk.
    3982        @type xmlPath: Absolute path to a file on disk.
    3983
    3984        @param validate: Validate the document after parsing it.
    3985        @type validate: Boolean true/false.
    3986
    3987        @raise ValueError: If both C{xmlData} and C{xmlPath} are passed-in.
    3988        @raise ValueError: If the XML data in C{xmlData} or C{xmlPath} cannot be parsed.
    3989        @raise ValueError: If the parsed configuration document is not valid.
    3990        """
    3991        self._reference = None
    3992        self._extensions = None
    3993        self._options = None
    3994        self._peers = None
    3995        self._collect = None
    3996        self._stage = None
    3997        self._store = None
    3998        self._purge = None
    3999        self.reference = None
    4000        self.extensions = None
    4001        self.options = None
    4002        self.peers = None
    4003        self.collect = None
    4004        self.stage = None
    4005        self.store = None
    4006        self.purge = None
    4007        if xmlData is not None and xmlPath is not None:
    4008           raise ValueError("Use either xmlData or xmlPath, but not both.")
    4009        if xmlData is not None:
    4010           self._parseXmlData(xmlData)
    4011           if validate:
    4012              self.validate()
    4013        elif xmlPath is not None:
    4014           xmlData = open(xmlPath).read()
    4015           self._parseXmlData(xmlData)
    4016           if validate:
    4017              self.validate()
    4018
    4019
    4020     #########################
    4021     # String representations
    4022     #########################
    4023
    4024 -   def __repr__(self):
    4025        """
    4026        Official string representation for class instance.
    4027        """
    4028        return "Config(%s, %s, %s, %s, %s, %s, %s, %s)" % (self.reference, self.extensions, self.options,
    4029                                                           self.peers, self.collect, self.stage, self.store,
    4030                                                           self.purge)
    4031
    4032 -   def __str__(self):
    4033        """
    4034        Informal string representation for class instance.
    4035        """
    4036        return self.__repr__()
    4037
    4038
    4039     #############################
    4040     # Standard comparison method
    4041     #############################
    4042
    4043 -   def __cmp__(self, other):
    4044        """
    4045        Definition of equals operator for this class.
    4046        Lists within this class are "unordered" for equality comparisons.
    4047        @param other: Other object to compare to.
    4048        @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other.
    4049        """
    4050        if other is None:
    4051           return 1
    4052        if self.reference != other.reference:
    4053           if self.reference < other.reference:
    4054              return -1
    4055           else:
    4056              return 1
    4057        if self.extensions != other.extensions:
    4058           if self.extensions < other.extensions:
    4059              return -1
    4060           else:
    4061              return 1
    4062        if self.options != other.options:
    4063           if self.options < other.options:
    4064              return -1
    4065           else:
    4066              return 1
    4067        if self.peers != other.peers:
    4068           if self.peers < other.peers:
    4069              return -1
    4070           else:
    4071              return 1
    4072        if self.collect != other.collect:
    4073           if self.collect < other.collect:
    4074              return -1
    4075           else:
    4076              return 1
    4077        if self.stage != other.stage:
    4078           if self.stage < other.stage:
    4079              return -1
    4080           else:
    4081              return 1
    4082        if self.store != other.store:
    4083           if self.store < other.store:
    4084              return -1
    4085           else:
    4086              return 1
    4087        if self.purge != other.purge:
    4088           if self.purge < other.purge:
    4089              return -1
    4090           else:
    4091              return 1
    4092        return 0
    4093
    4094
    4095     #############
    4096     # Properties
    4097     #############
    4098
    4099 -   def _setReference(self, value):
    4100        """
    4101        Property target used to set the reference configuration value.
    4102        If not C{None}, the value must be a C{ReferenceConfig} object.
    4103        @raise ValueError: If the value is not a C{ReferenceConfig}
    4104        """
    4105        if value is None:
    4106           self._reference = None
    4107        else:
    4108           if not isinstance(value, ReferenceConfig):
    4109              raise ValueError("Value must be a C{ReferenceConfig} object.")
    4110           self._reference = value
    4111
    4112 -   def _getReference(self):
    4113        """
    4114        Property target used to get the reference configuration value.
    4115        """
    4116        return self._reference
    4117
    4118 -   def _setExtensions(self, value):
    4119        """
    4120        Property target used to set the extensions configuration value.
    4121        If not C{None}, the value must be a C{ExtensionsConfig} object.
    4122        @raise ValueError: If the value is not a C{ExtensionsConfig}
    4123        """
    4124        if value is None:
    4125           self._extensions = None
    4126        else:
    4127           if not isinstance(value, ExtensionsConfig):
    4128              raise ValueError("Value must be a C{ExtensionsConfig} object.")
    4129           self._extensions = value
    4130
    4131 -   def _getExtensions(self):
    4132        """
    4133        Property target used to get the extensions configuration value.
    4134        """
    4135        return self._extensions
    4136
    4137 -   def _setOptions(self, value):
    4138        """
    4139        Property target used to set the options configuration value.
    4140        If not C{None}, the value must be an C{OptionsConfig} object.
    4141        @raise ValueError: If the value is not a C{OptionsConfig}
    4142        """
    4143        if value is None:
    4144           self._options = None
    4145        else:
    4146           if not isinstance(value, OptionsConfig):
    4147              raise ValueError("Value must be a C{OptionsConfig} object.")
    4148           self._options = value
    4149
    4150 -   def _getOptions(self):
    4151        """
    4152        Property target used to get the options configuration value.
    4153        """
    4154        return self._options
    4155
    4156 -   def _setPeers(self, value):
    4157        """
    4158        Property target used to set the peers configuration value.
    4159        If not C{None}, the value must be an C{PeersConfig} object.
    4160        @raise ValueError: If the value is not a C{PeersConfig}
    4161        """
    4162        if value is None:
    4163           self._peers = None
    4164        else:
    4165           if not isinstance(value, PeersConfig):
    4166              raise ValueError("Value must be a C{PeersConfig} object.")
    4167           self._peers = value
    4168
    4169 -   def _getPeers(self):
    4170        """
    4171        Property target used to get the peers configuration value.
    4172        """
    4173        return self._peers
    4174
    4175 -   def _setCollect(self, value):
    4176        """
    4177        Property target used to set the collect configuration value.
    4178        If not C{None}, the value must be a C{CollectConfig} object.
    4179        @raise ValueError: If the value is not a C{CollectConfig}
    4180        """
    4181        if value is None:
    4182           self._collect = None
    4183        else:
    4184           if not isinstance(value, CollectConfig):
    4185              raise ValueError("Value must be a C{CollectConfig} object.")
    4186           self._collect = value
    4187
    4188 -   def _getCollect(self):
    4189        """
    4190        Property target used to get the collect configuration value.
    4191        """
    4192        return self._collect
    4193
    4194 -   def _setStage(self, value):
    4195        """
    4196        Property target used to set the stage configuration value.
    4197        If not C{None}, the value must be a C{StageConfig} object.
    4198        @raise ValueError: If the value is not a C{StageConfig}
    4199        """
    4200        if value is None:
    4201           self._stage = None
    4202        else:
    4203           if not isinstance(value, StageConfig):
    4204              raise ValueError("Value must be a C{StageConfig} object.")
    4205           self._stage = value
    4206
    4207 -   def _getStage(self):
    4208        """
    4209        Property target used to get the stage configuration value.
    4210        """
    4211        return self._stage
    4212
    4213 -   def _setStore(self, value):
    4214        """
    4215        Property target used to set the store configuration value.
    4216        If not C{None}, the value must be a C{StoreConfig} object.
    4217        @raise ValueError: If the value is not a C{StoreConfig}
    4218        """
    4219        if value is None:
    4220           self._store = None
    4221        else:
    4222           if not isinstance(value, StoreConfig):
    4223              raise ValueError("Value must be a C{StoreConfig} object.")
    4224           self._store = value
    4225
    4226 -   def _getStore(self):
    4227        """
    4228        Property target used to get the store configuration value.
    4229        """
    4230        return self._store
    4231
    4232 -   def _setPurge(self, value):
    4233        """
    4234        Property target used to set the purge configuration value.
    4235        If not C{None}, the value must be a C{PurgeConfig} object.
    4236        @raise ValueError: If the value is not a C{PurgeConfig}
    4237        """
    4238        if value is None:
    4239           self._purge = None
    4240        else:
    4241           if not isinstance(value, PurgeConfig):
    4242              raise ValueError("Value must be a C{PurgeConfig} object.")
    4243           self._purge = value
    4244
    4245 -   def _getPurge(self):
    4246        """
    4247        Property target used to get the purge configuration value.
    4248        """
    4249        return self._purge
    4250
    4251     reference = property(_getReference, _setReference, None, "Reference configuration in terms of a C{ReferenceConfig} object.")
    4252     extensions = property(_getExtensions, _setExtensions, None, "Extensions configuration in terms of a C{ExtensionsConfig} object.")
    4253     options = property(_getOptions, _setOptions, None, "Options configuration in terms of a C{OptionsConfig} object.")
    4254     peers = property(_getPeers, _setPeers, None, "Peers configuration in terms of a C{PeersConfig} object.")
    4255     collect = property(_getCollect, _setCollect, None, "Collect configuration in terms of a C{CollectConfig} object.")
    4256     stage = property(_getStage, _setStage, None, "Stage configuration in terms of a C{StageConfig} object.")
    4257     store = property(_getStore, _setStore, None, "Store configuration in terms of a C{StoreConfig} object.")
    4258     purge = property(_getPurge, _setPurge, None, "Purge configuration in terms of a C{PurgeConfig} object.")
    4259
    4260
    4261     #################
    4262     # Public methods
    4263     #################
    4264
    4265 -   def extractXml(self, xmlPath=None, validate=True):
    4266        """
    4267        Extracts configuration into an XML document.
    4268
    4269        If C{xmlPath} is not provided, then the XML document will be returned as
    4270        a string.  If C{xmlPath} is provided, then the XML document will be written
    4271        to the file and C{None} will be returned.
    4272
    4273        Unless the C{validate} parameter is C{False}, the L{Config.validate}
    4274        method will be called (with its default arguments) against the
    4275        configuration before extracting the XML.  If configuration is not valid,
    4276        then an XML document will not be extracted.
    4277
    4278        @note: It is strongly suggested that the C{validate} option always be set
    4279        to C{True} (the default) unless there is a specific need to write an
    4280        invalid configuration file to disk.
    4281
    4282        @param xmlPath: Path to an XML file to create on disk.
    4283        @type xmlPath: Absolute path to a file.
    4284
    4285        @param validate: Validate the document before extracting it.
    4286        @type validate: Boolean true/false.
    4287
    4288        @return: XML string data or C{None} as described above.
    4289
    4290        @raise ValueError: If configuration within the object is not valid.
    4291        @raise IOError: If there is an error writing to the file.
    4292        @raise OSError: If there is an error writing to the file.
    4293        """
    4294        if validate:
    4295           self.validate()
    4296        xmlData = self._extractXml()
    4297        if xmlPath is not None:
    4298           open(xmlPath, "w").write(xmlData)
    4299           return None
    4300        else:
    4301           return xmlData
    4302
    4303 -   def validate(self, requireOneAction=True, requireReference=False, requireExtensions=False, requireOptions=True,
    4304                  requireCollect=False, requireStage=False, requireStore=False, requirePurge=False, requirePeers=False):
    4305        """
    4306        Validates configuration represented by the object.
    4307
    4308        This method encapsulates all of the validations that should apply to a
    4309        fully "complete" document but are not already taken care of by earlier
    4310        validations.  It also provides some extra convenience functionality which
    4311        might be useful to some people.  The process of validation is laid out in
    4312        the I{Validation} section in the class notes (above).
    4313
    4314        @param requireOneAction: Require at least one of the collect, stage, store or purge sections.
    4315        @param requireReference: Require the reference section.
    4316        @param requireExtensions: Require the extensions section.
    4317        @param requireOptions: Require the options section.
    4318        @param requirePeers: Require the peers section.
    4319        @param requireCollect: Require the collect section.
    4320        @param requireStage: Require the stage section.
    4321        @param requireStore: Require the store section.
    4322        @param requirePurge: Require the purge section.
    4323
    4324        @raise ValueError: If one of the validations fails.
    4325        """
    4326        if requireOneAction and (self.collect, self.stage, self.store, self.purge) == (None, None, None, None):
    4327           raise ValueError("At least one of the collect, stage, store and purge sections is required.")
    4328        if requireReference and self.reference is None:
    4329           raise ValueError("The reference is section is required.")
    4330        if requireExtensions and self.extensions is None:
    4331           raise ValueError("The extensions is section is required.")
    4332        if requireOptions and self.options is None:
    4333           raise ValueError("The options is section is required.")
    4334        if requirePeers and self.peers is None:
    4335           raise ValueError("The peers is section is required.")
    4336        if requireCollect and self.collect is None:
    4337           raise ValueError("The collect is section is required.")
    4338        if requireStage and self.stage is None:
    4339           raise ValueError("The stage is section is required.")
    4340        if requireStore and self.store is None:
    4341           raise ValueError("The store is section is required.")
    4342        if requirePurge and self.purge is None:
    4343           raise ValueError("The purge is section is required.")
    4344        self._validateContents()
    4345
    4346
    4347     #####################################
    4348     # High-level methods for parsing XML
    4349     #####################################
    4350
    4351 -   def _parseXmlData(self, xmlData):
    4352        """
    4353        Internal method to parse an XML string into the object.
    4354
    4355        This method parses the XML document into a DOM tree (C{xmlDom}) and then
    4356        calls individual static methods to parse each of the individual
    4357        configuration sections.
    4358
    4359        Most of the validation we do here has to do with whether the document can
    4360        be parsed and whether any values which exist are valid.  We don't do much
    4361        validation as to whether required elements actually exist unless we have
    4362        to to make sense of the document (instead, that's the job of the
    4363        L{validate} method).
    4364
    4365        @param xmlData: XML data to be parsed
    4366        @type xmlData: String data
    4367
    4368        @raise ValueError: If the XML cannot be successfully parsed.
    4369        """
    4370        (xmlDom, parentNode) = createInputDom(xmlData)
    4371        self._reference = Config._parseReference(parentNode)
    4372        self._extensions = Config._parseExtensions(parentNode)
    4373        self._options = Config._parseOptions(parentNode)
    4374        self._peers = Config._parsePeers(parentNode)
    4375        self._collect = Config._parseCollect(parentNode)
    4376        self._stage = Config._parseStage(parentNode)
    4377        self._store = Config._parseStore(parentNode)
    4378        self._purge = Config._parsePurge(parentNode)
    4379
    4380     @staticmethod
    4381 -   def _parseReference(parentNode):
    4382        """
    4383        Parses a reference configuration section.
    4384
    4385        We read the following fields::
    4386
    4387           author         //cb_config/reference/author
    4388           revision       //cb_config/reference/revision
    4389           description    //cb_config/reference/description
    4390           generator      //cb_config/reference/generator
    4391
    4392        @param parentNode: Parent node to search beneath.
    4393
    4394        @return: C{ReferenceConfig} object or C{None} if the section does not exist.
    4395        @raise ValueError: If some filled-in value is invalid.
    4396        """
    4397        reference = None
    4398        sectionNode = readFirstChild(parentNode, "reference")
    4399        if sectionNode is not None:
    4400           reference = ReferenceConfig()
    4401           reference.author = readString(sectionNode, "author")
    4402           reference.revision = readString(sectionNode, "revision")
    4403           reference.description = readString(sectionNode, "description")
    4404           reference.generator = readString(sectionNode, "generator")
    4405        return reference
    4406
    4407     @staticmethod
    4408 -   def _parseExtensions(parentNode):
    4409        """
    4410        Parses an extensions configuration section.
    4411
    4412        We read the following fields::
    4413
    4414           orderMode            //cb_config/extensions/order_mode
    4415
    4416        We also read groups of the following items, one list element per item::
    4417
    4418           name                 //cb_config/extensions/action/name
    4419           module               //cb_config/extensions/action/module
    4420           function             //cb_config/extensions/action/function
    4421           index                //cb_config/extensions/action/index
    4422           dependencies         //cb_config/extensions/action/depends
    4423
    4424        The extended actions are parsed by L{_parseExtendedActions}.
    4425
    4426        @param parentNode: Parent node to search beneath.
    4427
    4428        @return: C{ExtensionsConfig} object or C{None} if the section does not exist.
    4429        @raise ValueError: If some filled-in value is invalid.
    4430        """
    4431        extensions = None
    4432        sectionNode = readFirstChild(parentNode, "extensions")
    4433        if sectionNode is not None:
    4434           extensions = ExtensionsConfig()
    4435           extensions.orderMode = readString(sectionNode, "order_mode")
    4436           extensions.actions = Config._parseExtendedActions(sectionNode)
    4437        return extensions
    4438
    4439     @staticmethod
    4440 -   def _parseOptions(parentNode):
    4441        """
    4442        Parses a options configuration section.
    4443
    4444        We read the following fields::
    4445
    4446           startingDay    //cb_config/options/starting_day
    4447           workingDir     //cb_config/options/working_dir
    4448           backupUser     //cb_config/options/backup_user
    4449           backupGroup    //cb_config/options/backup_group
    4450           rcpCommand     //cb_config/options/rcp_command
    4451           rshCommand     //cb_config/options/rsh_command
    4452           cbackCommand   //cb_config/options/cback_command
    4453           managedActions //cb_config/options/managed_actions
    4454
    4455        The list of managed actions is a comma-separated list of action names.
    4456
    4457        We also read groups of the following items, one list element per
    4458        item::
    4459
    4460           overrides      //cb_config/options/override
    4461           hooks          //cb_config/options/hook
    4462
    4463        The overrides are parsed by L{_parseOverrides} and the hooks are parsed
    4464        by L{_parseHooks}.
    4465
    4466        @param parentNode: Parent node to search beneath.
    4467
    4468        @return: C{OptionsConfig} object or C{None} if the section does not exist.
    4469        @raise ValueError: If some filled-in value is invalid.
    4470        """
    4471        options = None
    4472        sectionNode = readFirstChild(parentNode, "options")
    4473        if sectionNode is not None:
    4474           options = OptionsConfig()
    4475           options.startingDay = readString(sectionNode, "starting_day")
    4476           options.workingDir = readString(sectionNode, "working_dir")
    4477           options.backupUser = readString(sectionNode, "backup_user")
    4478           options.backupGroup = readString(sectionNode, "backup_group")
    4479           options.rcpCommand = readString(sectionNode, "rcp_command")
    4480           options.rshCommand = readString(sectionNode, "rsh_command")
    4481           options.cbackCommand = readString(sectionNode, "cback_command")
    4482           options.overrides = Config._parseOverrides(sectionNode)
    4483           options.hooks = Config._parseHooks(sectionNode)
    4484           managedActions = readString(sectionNode, "managed_actions")
    4485           options.managedActions = parseCommaSeparatedString(managedActions)
    4486        return options
    4487
    4488     @staticmethod
    4489 -   def _parsePeers(parentNode):
    4490        """
    4491        Parses a peers configuration section.
    4492
    4493        We read groups of the following items, one list element per
    4494        item::
    4495
    4496           localPeers     //cb_config/stage/peer
    4497           remotePeers    //cb_config/stage/peer
    4498
    4499        The individual peer entries are parsed by L{_parsePeerList}.
    4500
    4501        @param parentNode: Parent node to search beneath.
    4502
    4503        @return: C{StageConfig} object or C{None} if the section does not exist.
    4504        @raise ValueError: If some filled-in value is invalid.
    4505        """
    4506        peers = None
    4507        sectionNode = readFirstChild(parentNode, "peers")
    4508        if sectionNode is not None:
    4509           peers = PeersConfig()
    4510           (peers.localPeers, peers.remotePeers) = Config._parsePeerList(sectionNode)
    4511        return peers
    4512
    4513     @staticmethod
    4514 -   def _parseCollect(parentNode):
    4515        """
    4516        Parses a collect configuration section.
    4517
    4518        We read the following individual fields::
    4519
    4520           targetDir            //cb_config/collect/collect_dir
    4521           collectMode          //cb_config/collect/collect_mode
    4522           archiveMode          //cb_config/collect/archive_mode
    4523           ignoreFile           //cb_config/collect/ignore_file
    4524
    4525        We also read groups of the following items, one list element per
    4526        item::
    4527
    4528           absoluteExcludePaths //cb_config/collect/exclude/abs_path
    4529           excludePatterns      //cb_config/collect/exclude/pattern
    4530           collectFiles         //cb_config/collect/file
    4531           collectDirs          //cb_config/collect/dir
    4532
    4533        The exclusions are parsed by L{_parseExclusions}, the collect files are
    4534        parsed by L{_parseCollectFiles}, and the directories are parsed by
    4535        L{_parseCollectDirs}.
    4536
    4537        @param parentNode: Parent node to search beneath.
    4538
    4539        @return: C{CollectConfig} object or C{None} if the section does not exist.
    4540        @raise ValueError: If some filled-in value is invalid.
    4541        """
    4542        collect = None
    4543        sectionNode = readFirstChild(parentNode, "collect")
    4544        if sectionNode is not None:
    4545           collect = CollectConfig()
    4546           collect.targetDir = readString(sectionNode, "collect_dir")
    4547           collect.collectMode = readString(sectionNode, "collect_mode")
    4548           collect.archiveMode = readString(sectionNode, "archive_mode")
    4549           collect.ignoreFile = readString(sectionNode, "ignore_file")
    4550           (collect.absoluteExcludePaths, unused, collect.excludePatterns) = Config._parseExclusions(sectionNode)
    4551           collect.collectFiles = Config._parseCollectFiles(sectionNode)
    4552           collect.collectDirs = Config._parseCollectDirs(sectionNode)
    4553        return collect
    4554
    4555     @staticmethod
    4556 -   def _parseStage(parentNode):
    4557        """
    4558        Parses a stage configuration section.
    4559
    4560        We read the following individual fields::
    4561
    4562           targetDir      //cb_config/stage/staging_dir
    4563
    4564        We also read groups of the following items, one list element per
    4565        item::
    4566
    4567           localPeers     //cb_config/stage/peer
    4568           remotePeers    //cb_config/stage/peer
    4569
    4570        The individual peer entries are parsed by L{_parsePeerList}.
    4571
    4572        @param parentNode: Parent node to search beneath.
    4573
    4574        @return: C{StageConfig} object or C{None} if the section does not exist.
    4575        @raise ValueError: If some filled-in value is invalid.
    4576        """
    4577        stage = None
    4578        sectionNode = readFirstChild(parentNode, "stage")
    4579        if sectionNode is not None:
    4580           stage = StageConfig()
    4581           stage.targetDir = readString(sectionNode, "staging_dir")
    4582           (stage.localPeers, stage.remotePeers) = Config._parsePeerList(sectionNode)
    4583        return stage
    4584
    4585     @staticmethod
    4586 -   def _parseStore(parentNode):
    4587        """
    4588        Parses a store configuration section.
    4589
    4590        We read the following fields::
    4591
    4592           sourceDir         //cb_config/store/source_dir
    4593           mediaType         //cb_config/store/media_type
    4594           deviceType        //cb_config/store/device_type
    4595           devicePath        //cb_config/store/target_device
    4596           deviceScsiId      //cb_config/store/target_scsi_id
    4597           driveSpeed        //cb_config/store/drive_speed
    4598           checkData         //cb_config/store/check_data
    4599           checkMedia        //cb_config/store/check_media
    4600           warnMidnite       //cb_config/store/warn_midnite
    4601           noEject           //cb_config/store/no_eject
    4602
    4603        Blanking behavior configuration is parsed by the C{_parseBlankBehavior}
    4604        method.
    4605
    4606        @param parentNode: Parent node to search beneath.
    4607
    4608        @return: C{StoreConfig} object or C{None} if the section does not exist.
    4609        @raise ValueError: If some filled-in value is invalid.
    4610        """
    4611        store = None
    4612        sectionNode = readFirstChild(parentNode, "store")
    4613        if sectionNode is not None:
    4614           store = StoreConfig()
    4615           store.sourceDir = readString(sectionNode,  "source_dir")
    4616           store.mediaType = readString(sectionNode,  "media_type")
    4617           store.deviceType = readString(sectionNode,  "device_type")
    4618           store.devicePath = readString(sectionNode,  "target_device")
    4619           store.deviceScsiId = readString(sectionNode,  "target_scsi_id")
    4620           store.driveSpeed = readInteger(sectionNode, "drive_speed")
    4621           store.checkData = readBoolean(sectionNode, "check_data")
    4622           store.checkMedia = readBoolean(sectionNode, "check_media")
    4623           store.warnMidnite = readBoolean(sectionNode, "warn_midnite")
    4624           store.noEject = readBoolean(sectionNode, "no_eject")
    4625           store.blankBehavior = Config._parseBlankBehavior(sectionNode)
    4626           store.refreshMediaDelay = readInteger(sectionNode, "refresh_media_delay")
    4627           store.ejectDelay = readInteger(sectionNode, "eject_delay")
    4628        return store
    4629
    4630     @staticmethod
    4631 -   def _parsePurge(parentNode):
    4632        """
    4633        Parses a purge configuration section.
    4634
    4635        We read groups of the following items, one list element per
    4636        item::
    4637
    4638           purgeDirs     //cb_config/purge/dir
    4639
    4640        The individual directory entries are parsed by L{_parsePurgeDirs}.
    4641
    4642        @param parentNode: Parent node to search beneath.
    4643
    4644        @return: C{PurgeConfig} object or C{None} if the section does not exist.
    4645        @raise ValueError: If some filled-in value is invalid.
    4646        """
    4647        purge = None
    4648        sectionNode = readFirstChild(parentNode, "purge")
    4649        if sectionNode is not None:
    4650           purge = PurgeConfig()
    4651           purge.purgeDirs = Config._parsePurgeDirs(sectionNode)
    4652        return purge
    4653
    4654     @staticmethod
    4655 -   def _parseExtendedActions(parentNode):
    4656        """
    4657        Reads extended actions data from immediately beneath the parent.
    4658
    4659        We read the following individual fields from each extended action::
    4660
    4661           name           name
    4662           module         module
    4663           function       function
    4664           index          index
    4665           dependencies   depends
    4666
    4667        Dependency information is parsed by the C{_parseDependencies} method.
    4668
    4669        @param parentNode: Parent node to search beneath.
    4670
    4671        @return: List of extended actions.
    4672        @raise ValueError: If the data at the location can't be read
    4673        """
    4674        lst = []
    4675        for entry in readChildren(parentNode, "action"):
    4676           if isElement(entry):
    4677              action = ExtendedAction()
    4678              action.name = readString(entry, "name")
    4679              action.module = readString(entry, "module")
    4680              action.function = readString(entry, "function")
    4681              action.index = readInteger(entry, "index")
    4682              action.dependencies = Config._parseDependencies(entry)
    4683              lst.append(action)
    4684        if lst == []:
    4685           lst = None
    4686        return lst
    4687
    4688     @staticmethod
    4689 -   def _parseExclusions(parentNode):
    4690        """
    4691        Reads exclusions data from immediately beneath the parent.
    4692
    4693        We read groups of the following items, one list element per item::
    4694
    4695           absolute    exclude/abs_path
    4696           relative    exclude/rel_path
    4697           patterns    exclude/pattern
    4698
    4699        If there are none of some pattern (i.e. no relative path items) then
    4700        C{None} will be returned for that item in the tuple.
    4701
    4702        This method can be used to parse exclusions on both the collect
    4703        configuration level and on the collect directory level within collect
    4704        configuration.
    4705
    4706        @param parentNode: Parent node to search beneath.
    4707
    4708        @return: Tuple of (absolute, relative, patterns) exclusions.
    4709        """
    4710        sectionNode = readFirstChild(parentNode, "exclude")
    4711        if sectionNode is None:
    4712           return (None, None, None)
    4713        else:
    4714           absolute = readStringList(sectionNode, "abs_path")
    4715           relative = readStringList(sectionNode, "rel_path")
    4716           patterns = readStringList(sectionNode, "pattern")
    4717           return (absolute, relative, patterns)
    4718
    4719     @staticmethod
    4720 -   def _parseOverrides(parentNode):
    4721        """
    4722        Reads a list of C{CommandOverride} objects from immediately beneath the parent.
    4723
    4724        We read the following individual fields::
    4725
    4726           command                 command
    4727           absolutePath            abs_path
    4728
    4729        @param parentNode: Parent node to search beneath.
    4730
    4731        @return: List of C{CommandOverride} objects or C{None} if none are found.
    4732        @raise ValueError: If some filled-in value is invalid.
    4733        """
    4734        lst = []
    4735        for entry in readChildren(parentNode, "override"):
    4736           if isElement(entry):
    4737              override = CommandOverride()
    4738              override.command = readString(entry, "command")
    4739              override.absolutePath = readString(entry, "abs_path")
    4740              lst.append(override)
    4741        if lst == []:
    4742           lst = None
    4743        return lst
    4744
    4745     @staticmethod
    4746 -   def _parseHooks(parentNode):
    4747        """
    4748        Reads a list of C{ActionHook} objects from immediately beneath the parent.
    4749
    4750        We read the following individual fields::
    4751
    4752           action                  action
    4753           command                 command
    4754
    4755        @param parentNode: Parent node to search beneath.
    4756
    4757        @return: List of C{ActionHook} objects or C{None} if none are found.
    4758        @raise ValueError: If some filled-in value is invalid.
    4759        """
    4760        lst = []
    4761        for entry in readChildren(parentNode, "pre_action_hook"):
    4762           if isElement(entry):
    4763              hook = PreActionHook()
    4764              hook.action = readString(entry, "action")
    4765              hook.command = readString(entry, "command")
    4766              lst.append(hook)
    4767        for entry in readChildren(parentNode, "post_action_hook"):
    4768           if isElement(entry):
    4769              hook = PostActionHook()
    4770              hook.action = readString(entry, "action")
    4771              hook.command = readString(entry, "command")
    4772              lst.append(hook)
    4773        if lst == []:
    4774           lst = None
    4775        return lst
    4776
    4777     @staticmethod
    4778 -   def _parseCollectFiles(parentNode):
    4779        """
    4780        Reads a list of C{CollectFile} objects from immediately beneath the parent.
    4781
    4782        We read the following individual fields::
    4783
    4784           absolutePath            abs_path
    4785           collectMode             mode I{or} collect_mode
    4786           archiveMode             archive_mode
    4787
    4788        The collect mode is a special case.  Just a C{mode} tag is accepted, but
    4789        we prefer C{collect_mode} for consistency with the rest of the config
    4790        file and to avoid confusion with the archive mode.  If both are provided,
    4791        only C{mode} will be used.
    4792
    4793        @param parentNode: Parent node to search beneath.
    4794
    4795        @return: List of C{CollectFile} objects or C{None} if none are found.
    4796        @raise ValueError: If some filled-in value is invalid.
    4797        """
    4798        lst = []
    4799        for entry in readChildren(parentNode, "file"):
    4800           if isElement(entry):
    4801              cfile = CollectFile()
    4802              cfile.absolutePath = readString(entry, "abs_path")
    4803              cfile.collectMode = readString(entry, "mode")
    4804              if cfile.collectMode is None:
    4805                 cfile.collectMode = readString(entry, "collect_mode")
    4806              cfile.archiveMode = readString(entry, "archive_mode")
    4807              lst.append(cfile)
    4808        if lst == []:
    4809           lst = None
    4810        return lst
    4811
    4812     @staticmethod
    4813 -   def _parseCollectDirs(parentNode):
    4814        """
    4815        Reads a list of C{CollectDir} objects from immediately beneath the parent.
    4816
    4817        We read the following individual fields::
    4818
    4819           absolutePath            abs_path
    4820           collectMode             mode I{or} collect_mode
    4821           archiveMode             archive_mode
    4822           ignoreFile              ignore_file
    4823           linkDepth               link_depth
    4824           dereference             dereference
    4825           recursionLevel          recursion_level
    4826
    4827        The collect mode is a special case.  Just a C{mode} tag is accepted for
    4828        backwards compatibility, but we prefer C{collect_mode} for consistency
    4829        with the rest of the config file and to avoid confusion with the archive
    4830        mode.  If both are provided, only C{mode} will be used.
    4831
    4832        We also read groups of the following items, one list element per
    4833        item::
    4834
    4835           absoluteExcludePaths    exclude/abs_path
    4836           relativeExcludePaths    exclude/rel_path
    4837           excludePatterns         exclude/pattern
    4838
    4839        The exclusions are parsed by L{_parseExclusions}.
    4840
    4841        @param parentNode: Parent node to search beneath.
    4842
    4843        @return: List of C{CollectDir} objects or C{None} if none are found.
    4844        @raise ValueError: If some filled-in value is invalid.
    4845        """
    4846        lst = []
    4847        for entry in readChildren(parentNode, "dir"):
    4848           if isElement(entry):
    4849              cdir = CollectDir()
    4850              cdir.absolutePath = readString(entry, "abs_path")
    4851              cdir.collectMode = readString(entry, "mode")
    4852              if cdir.collectMode is None:
    4853                 cdir.collectMode = readString(entry, "collect_mode")
    4854              cdir.archiveMode = readString(entry, "archive_mode")
    4855              cdir.ignoreFile = readString(entry, "ignore_file")
    4856              cdir.linkDepth = readInteger(entry, "link_depth")
    4857              cdir.dereference = readBoolean(entry, "dereference")
    4858              cdir.recursionLevel = readInteger(entry, "recursion_level")
    4859              (cdir.absoluteExcludePaths, cdir.relativeExcludePaths, cdir.excludePatterns) = Config._parseExclusions(entry)
    4860              lst.append(cdir)
    4861        if lst == []:
    4862           lst = None
    4863        return lst
    4864
    4865     @staticmethod
    4866 -   def _parsePurgeDirs(parentNode):
    4867        """
    4868        Reads a list of C{PurgeDir} objects from immediately beneath the parent.
    4869
    4870        We read the following individual fields::
    4871
    4872           absolutePath            <baseExpr>/abs_path
    4873           retainDays              <baseExpr>/retain_days
    4874
    4875        @param parentNode: Parent node to search beneath.
    4876
    4877        @return: List of C{PurgeDir} objects or C{None} if none are found.
    4878        @raise ValueError: If the data at the location can't be read
    4879        """
    4880        lst = []
    4881        for entry in readChildren(parentNode, "dir"):
    4882           if isElement(entry):
    4883              cdir = PurgeDir()
    4884              cdir.absolutePath = readString(entry, "abs_path")
    4885              cdir.retainDays = readInteger(entry, "retain_days")
    4886              lst.append(cdir)
    4887        if lst == []:
    4888           lst = None
    4889        return lst
    4890
    4891     @staticmethod
    4892 -   def _parsePeerList(parentNode):
    4893        """
    4894        Reads remote and local peer data from immediately beneath the parent.
    4895
    4896        We read the following individual fields for both remote
    4897        and local peers::
    4898
    4899           name        name
    4900           collectDir  collect_dir
    4901
    4902        We also read the following individual fields for remote peers
    4903        only::
    4904
    4905           remoteUser     backup_user
    4906           rcpCommand     rcp_command
    4907           rshCommand     rsh_command
    4908           cbackCommand   cback_command
    4909           managed        managed
    4910           managedActions managed_actions
    4911
    4912        Additionally, the value in the C{type} field is used to determine whether
    4913        this entry is a remote peer.  If the type is C{"remote"}, it's a remote
    4914        peer, and if the type is C{"local"}, it's a remote peer.
    4915
    4916        If there are none of one type of peer (i.e. no local peers) then C{None}
    4917        will be returned for that item in the tuple.
    4918
    4919        @param parentNode: Parent node to search beneath.
    4920
    4921        @return: Tuple of (local, remote) peer lists.
    4922        @raise ValueError: If the data at the location can't be read
    4923        """
    4924        localPeers = []
    4925        remotePeers = []
    4926        for entry in readChildren(parentNode, "peer"):
    4927           if isElement(entry):
    4928              peerType = readString(entry, "type")
    4929              if peerType == "local":
    4930                 localPeer = LocalPeer()
    4931                 localPeer.name = readString(entry, "name")
    4932                 localPeer.collectDir = readString(entry, "collect_dir")
    4933                 localPeer.ignoreFailureMode = readString(entry, "ignore_failures")
    4934                 localPeers.append(localPeer)
    4935              elif peerType == "remote":
    4936                 remotePeer = RemotePeer()
    4937                 remotePeer.name = readString(entry, "name")
    4938                 remotePeer.collectDir = readString(entry, "collect_dir")
    4939                 remotePeer.remoteUser = readString(entry, "backup_user")
    4940                 remotePeer.rcpCommand = readString(entry, "rcp_command")
    4941                 remotePeer.rshCommand = readString(entry, "rsh_command")
    4942                 remotePeer.cbackCommand = readString(entry, "cback_command")
    4943                 remotePeer.ignoreFailureMode = readString(entry, "ignore_failures")
    4944                 remotePeer.managed = readBoolean(entry, "managed")
    4945                 managedActions = readString(entry, "managed_actions")
    4946                 remotePeer.managedActions = parseCommaSeparatedString(managedActions)
    4947                 remotePeers.append(remotePeer)
    4948        if localPeers == []:
    4949           localPeers = None
    4950        if remotePeers == []:
    4951           remotePeers = None
    4952        return (localPeers, remotePeers)
    4953
    4954     @staticmethod
    4955 -   def _parseDependencies(parentNode):
    4956        """
    4957        Reads extended action dependency information from a parent node.
    4958
    4959        We read the following individual fields::
    4960
    4961           runBefore   depends/run_before
    4962           runAfter    depends/run_after
    4963
    4964        Each of these fields is a comma-separated list of action names.
    4965
    4966        The result is placed into an C{ActionDependencies} object.
    4967
    4968        If the dependencies parent node does not exist, C{None} will be returned.
    4969        Otherwise, an C{ActionDependencies} object will always be created, even
    4970        if it does not contain any actual dependencies in it.
    4971
    4972        @param parentNode: Parent node to search beneath.
    4973
    4974        @return: C{ActionDependencies} object or C{None}.
    4975        @raise ValueError: If the data at the location can't be read
    4976        """
    4977        sectionNode = readFirstChild(parentNode, "depends")
    4978        if sectionNode is None:
    4979           return None
    4980        else:
    4981           runBefore = readString(sectionNode, "run_before")
    4982           runAfter = readString(sectionNode, "run_after")
    4983           beforeList = parseCommaSeparatedString(runBefore)
    4984           afterList = parseCommaSeparatedString(runAfter)
    4985           return ActionDependencies(beforeList, afterList)
    4986
    4987     @staticmethod
    4988 -   def _parseBlankBehavior(parentNode):
    4989        """
    4990        Reads a single C{BlankBehavior} object from immediately beneath the parent.
    4991
    4992        We read the following individual fields::
    4993
    4994           blankMode     blank_behavior/mode
    4995           blankFactor   blank_behavior/factor
    4996
    4997        @param parentNode: Parent node to search beneath.
    4998
    4999        @return: C{BlankBehavior} object or C{None} if none if the section is not found
    5000        @raise ValueError: If some filled-in value is invalid.
    5001        """
    5002        blankBehavior = None
    5003        sectionNode = readFirstChild(parentNode, "blank_behavior")
    5004        if sectionNode is not None:
    5005           blankBehavior = BlankBehavior()
    5006           blankBehavior.blankMode = readString(sectionNode, "mode")
    5007           blankBehavior.blankFactor = readString(sectionNode, "factor")
    5008        return blankBehavior
    5009
    5010
    5011     ########################################
    5012     # High-level methods for generating XML
    5013     ########################################
    5014
    5015 -   def _extractXml(self):
    5016        """
    5017        Internal method to extract configuration into an XML string.
    5018
    5019        This method assumes that the internal L{validate} method has been called
    5020        prior to extracting the XML, if the caller cares.  No validation will be
    5021        done internally.
    5022
    5023        As a general rule, fields that are set to C{None} will be extracted into
    5024        the document as empty tags.  The same goes for container tags that are
    5025        filled based on lists - if the list is empty or C{None}, the container
    5026        tag will be empty.
    5027        """
    5028        (xmlDom, parentNode) = createOutputDom()
    5029        Config._addReference(xmlDom, parentNode, self.reference)
    5030        Config._addExtensions(xmlDom, parentNode, self.extensions)
    5031        Config._addOptions(xmlDom, parentNode, self.options)
    5032        Config._addPeers(xmlDom, parentNode, self.peers)
    5033        Config._addCollect(xmlDom, parentNode, self.collect)
    5034        Config._addStage(xmlDom, parentNode, self.stage)
    5035        Config._addStore(xmlDom, parentNode, self.store)
    5036        Config._addPurge(xmlDom, parentNode, self.purge)
    5037        xmlData = serializeDom(xmlDom)
    5038        xmlDom.unlink()
    5039        return xmlData
    5040
    5041     @staticmethod
    5042 -   def _addReference(xmlDom, parentNode, referenceConfig):
    5043        """
    5044        Adds a <reference> configuration section as the next child of a parent.
    5045
    5046        We add the following fields to the document::
    5047
    5048           author         //cb_config/reference/author
    5049           revision       //cb_config/reference/revision
    5050           description    //cb_config/reference/description
    5051           generator      //cb_config/reference/generator
    5052
    5053        If C{referenceConfig} is C{None}, then no container will be added.
    5054
    5055        @param xmlDom: DOM tree as from L{createOutputDom}.
    5056        @param parentNode: Parent that the section should be appended to.
    5057        @param referenceConfig: Reference configuration section to be added to the document.
    5058        """
    5059        if referenceConfig is not None:
    5060           sectionNode = addContainerNode(xmlDom, parentNode, "reference")
    5061           addStringNode(xmlDom, sectionNode, "author", referenceConfig.author)
    5062           addStringNode(xmlDom, sectionNode, "revision", referenceConfig.revision)
    5063           addStringNode(xmlDom, sectionNode, "description", referenceConfig.description)
    5064           addStringNode(xmlDom, sectionNode, "generator", referenceConfig.generator)
    5065
    5066     @staticmethod
    5067 -   def _addExtensions(xmlDom, parentNode, extensionsConfig):
    5068        """
    5069        Adds an <extensions> configuration section as the next child of a parent.
    5070
    5071        We add the following fields to the document::
    5072
    5073           order_mode     //cb_config/extensions/order_mode
    5074
    5075        We also add groups of the following items, one list element per item::
    5076
    5077           actions        //cb_config/extensions/action
    5078
    5079        The extended action entries are added by L{_addExtendedAction}.
    5080
    5081        If C{extensionsConfig} is C{None}, then no container will be added.
    5082
    5083        @param xmlDom: DOM tree as from L{createOutputDom}.
    5084        @param parentNode: Parent that the section should be appended to.
    5085        @param extensionsConfig: Extensions configuration section to be added to the document.
    5086        """
    5087        if extensionsConfig is not None:
    5088           sectionNode = addContainerNode(xmlDom, parentNode, "extensions")
    5089           addStringNode(xmlDom, sectionNode, "order_mode", extensionsConfig.orderMode)
    5090           if extensionsConfig.actions is not None:
    5091              for action in extensionsConfig.actions:
    5092                 Config._addExtendedAction(xmlDom, sectionNode, action)
    5093
    5094     @staticmethod
    5095 -   def _addOptions(xmlDom, parentNode, optionsConfig):
    5096        """
    5097        Adds a <options> configuration section as the next child of a parent.
    5098
    5099        We add the following fields to the document::
    5100
    5101           startingDay    //cb_config/options/starting_day
    5102           workingDir     //cb_config/options/working_dir
    5103           backupUser     //cb_config/options/backup_user
    5104           backupGroup    //cb_config/options/backup_group
    5105           rcpCommand     //cb_config/options/rcp_command
    5106           rshCommand     //cb_config/options/rsh_command
    5107           cbackCommand   //cb_config/options/cback_command
    5108           managedActions //cb_config/options/managed_actions
    5109
    5110        We also add groups of the following items, one list element per
    5111        item::
    5112
    5113           overrides      //cb_config/options/override
    5114           hooks          //cb_config/options/pre_action_hook
    5115           hooks          //cb_config/options/post_action_hook
    5116
    5117        The individual override items are added by L{_addOverride}.  The
    5118        individual hook items are added by L{_addHook}.
    5119
    5120        If C{optionsConfig} is C{None}, then no container will be added.
    5121
    5122        @param xmlDom: DOM tree as from L{createOutputDom}.
    5123        @param parentNode: Parent that the section should be appended to.
    5124        @param optionsConfig: Options configuration section to be added to the document.
    5125        """
    5126        if optionsConfig is not None:
    5127           sectionNode = addContainerNode(xmlDom, parentNode, "options")
    5128           addStringNode(xmlDom, sectionNode, "starting_day", optionsConfig.startingDay)
    5129           addStringNode(xmlDom, sectionNode, "working_dir", optionsConfig.workingDir)
    5130           addStringNode(xmlDom, sectionNode, "backup_user", optionsConfig.backupUser)
    5131           addStringNode(xmlDom, sectionNode, "backup_group", optionsConfig.backupGroup)
    5132           addStringNode(xmlDom, sectionNode, "rcp_command", optionsConfig.rcpCommand)
    5133           addStringNode(xmlDom, sectionNode, "rsh_command", optionsConfig.rshCommand)
    5134           addStringNode(xmlDom, sectionNode, "cback_command", optionsConfig.cbackCommand)
    5135           managedActions = Config._buildCommaSeparatedString(optionsConfig.managedActions)
    5136           addStringNode(xmlDom, sectionNode, "managed_actions", managedActions)
    5137           if optionsConfig.overrides is not None:
    5138              for override in optionsConfig.overrides:
    5139                 Config._addOverride(xmlDom, sectionNode, override)
    5140           if optionsConfig.hooks is not None:
    5141              for hook in optionsConfig.hooks:
    5142                 Config._addHook(xmlDom, sectionNode, hook)
    5143
    5144     @staticmethod
    5145 -   def _addPeers(xmlDom, parentNode, peersConfig):
    5146        """
    5147        Adds a <peers> configuration section as the next child of a parent.
    5148
    5149        We add groups of the following items, one list element per
    5150        item::
    5151
    5152           localPeers     //cb_config/peers/peer
    5153           remotePeers    //cb_config/peers/peer
    5154
    5155        The individual local and remote peer entries are added by
    5156        L{_addLocalPeer} and L{_addRemotePeer}, respectively.
    5157
    5158        If C{peersConfig} is C{None}, then no container will be added.
    5159
    5160        @param xmlDom: DOM tree as from L{createOutputDom}.
    5161        @param parentNode: Parent that the section should be appended to.
    5162        @param peersConfig: Peers configuration section to be added to the document.
    5163        """
    5164        if peersConfig is not None:
    5165           sectionNode = addContainerNode(xmlDom, parentNode, "peers")
    5166           if peersConfig.localPeers is not None:
    5167              for localPeer in peersConfig.localPeers:
    5168                 Config._addLocalPeer(xmlDom, sectionNode, localPeer)
    5169           if peersConfig.remotePeers is not None:
    5170              for remotePeer in peersConfig.remotePeers:
    5171                 Config._addRemotePeer(xmlDom, sectionNode, remotePeer)
    5172
    5173     @staticmethod
    5174 -   def _addCollect(xmlDom, parentNode, collectConfig):
    5175        """
    5176        Adds a <collect> configuration section as the next child of a parent.
    5177
    5178        We add the following fields to the document::
    5179
    5180           targetDir            //cb_config/collect/collect_dir
    5181           collectMode          //cb_config/collect/collect_mode
    5182           archiveMode          //cb_config/collect/archive_mode
    5183           ignoreFile           //cb_config/collect/ignore_file
    5184
    5185        We also add groups of the following items, one list element per
    5186        item::
    5187
    5188           absoluteExcludePaths //cb_config/collect/exclude/abs_path
    5189           excludePatterns      //cb_config/collect/exclude/pattern
    5190           collectFiles         //cb_config/collect/file
    5191           collectDirs          //cb_config/collect/dir
    5192
    5193        The individual collect files are added by L{_addCollectFile} and
    5194        individual collect directories are added by L{_addCollectDir}.
    5195
    5196        If C{collectConfig} is C{None}, then no container will be added.
    5197
    5198        @param xmlDom: DOM tree as from L{createOutputDom}.
    5199        @param parentNode: Parent that the section should be appended to.
    5200        @param collectConfig: Collect configuration section to be added to the document.
    5201        """
    5202        if collectConfig is not None:
    5203           sectionNode = addContainerNode(xmlDom, parentNode, "collect")
    5204           addStringNode(xmlDom, sectionNode, "collect_dir", collectConfig.targetDir)
    5205           addStringNode(xmlDom, sectionNode, "collect_mode", collectConfig.collectMode)
    5206           addStringNode(xmlDom, sectionNode, "archive_mode", collectConfig.archiveMode)
    5207           addStringNode(xmlDom, sectionNode, "ignore_file", collectConfig.ignoreFile)
    5208           if ((collectConfig.absoluteExcludePaths is not None and collectConfig.absoluteExcludePaths != []) or
    5209               (collectConfig.excludePatterns is not None and collectConfig.excludePatterns != [])):
    5210              excludeNode = addContainerNode(xmlDom, sectionNode, "exclude")
    5211              if collectConfig.absoluteExcludePaths is not None:
    5212                 for absolutePath in collectConfig.absoluteExcludePaths:
    5213                    addStringNode(xmlDom, excludeNode, "abs_path", absolutePath)
    5214              if collectConfig.excludePatterns is not None:
    5215                 for pattern in collectConfig.excludePatterns:
    5216                    addStringNode(xmlDom, excludeNode, "pattern", pattern)
    5217           if collectConfig.collectFiles is not None:
    5218              for collectFile in collectConfig.collectFiles:
    5219                 Config._addCollectFile(xmlDom, sectionNode, collectFile)
    5220           if collectConfig.collectDirs is not None:
    5221              for collectDir in collectConfig.collectDirs:
    5222                 Config._addCollectDir(xmlDom, sectionNode, collectDir)
    5223
    5224     @staticmethod
    5225 -   def _addStage(xmlDom, parentNode, stageConfig):
    5226        """
    5227        Adds a <stage> configuration section as the next child of a parent.
    5228
    5229        We add the following fields to the document::
    5230
    5231           targetDir      //cb_config/stage/staging_dir
    5232
    5233        We also add groups of the following items, one list element per
    5234        item::
    5235
    5236           localPeers     //cb_config/stage/peer
    5237           remotePeers    //cb_config/stage/peer
    5238
    5239        The individual local and remote peer entries are added by
    5240        L{_addLocalPeer} and L{_addRemotePeer}, respectively.
    5241
    5242        If C{stageConfig} is C{None}, then no container will be added.
    5243
    5244        @param xmlDom: DOM tree as from L{createOutputDom}.
    5245        @param parentNode: Parent that the section should be appended to.
    5246        @param stageConfig: Stage configuration section to be added to the document.
    5247        """
    5248        if stageConfig is not None:
    5249           sectionNode = addContainerNode(xmlDom, parentNode, "stage")
    5250           addStringNode(xmlDom, sectionNode, "staging_dir", stageConfig.targetDir)
    5251           if stageConfig.localPeers is not None:
    5252              for localPeer in stageConfig.localPeers:
    5253                 Config._addLocalPeer(xmlDom, sectionNode, localPeer)
    5254           if stageConfig.remotePeers is not None:
    5255              for remotePeer in stageConfig.remotePeers:
    5256                 Config._addRemotePeer(xmlDom, sectionNode, remotePeer)
    5257
    5258     @staticmethod
    5259 -   def _addStore(xmlDom, parentNode, storeConfig):
    5260        """
    5261        Adds a <store> configuration section as the next child of a parent.
    5262
    5263        We add the following fields to the document::
    5264
    5265           sourceDir         //cb_config/store/source_dir
    5266           mediaType         //cb_config/store/media_type
    5267           deviceType        //cb_config/store/device_type
    5268           devicePath        //cb_config/store/target_device
    5269           deviceScsiId      //cb_config/store/target_scsi_id
    5270           driveSpeed        //cb_config/store/drive_speed
    5271           checkData         //cb_config/store/check_data
    5272           checkMedia        //cb_config/store/check_media
    5273           warnMidnite       //cb_config/store/warn_midnite
    5274           noEject           //cb_config/store/no_eject
    5275           refreshMediaDelay //cb_config/store/refresh_media_delay
    5276           ejectDelay        //cb_config/store/eject_delay
    5277
    5278        Blanking behavior configuration is added by the L{_addBlankBehavior}
    5279        method.
    5280
    5281        If C{storeConfig} is C{None}, then no container will be added.
    5282
    5283        @param xmlDom: DOM tree as from L{createOutputDom}.
    5284        @param parentNode: Parent that the section should be appended to.
    5285        @param storeConfig: Store configuration section to be added to the document.
    5286        """
    5287        if storeConfig is not None:
    5288           sectionNode = addContainerNode(xmlDom, parentNode, "store")
    5289           addStringNode(xmlDom, sectionNode, "source_dir", storeConfig.sourceDir)
    5290           addStringNode(xmlDom, sectionNode, "media_type", storeConfig.mediaType)
    5291           addStringNode(xmlDom, sectionNode, "device_type", storeConfig.deviceType)
    5292           addStringNode(xmlDom, sectionNode, "target_device", storeConfig.devicePath)
    5293           addStringNode(xmlDom, sectionNode, "target_scsi_id", storeConfig.deviceScsiId)
    5294           addIntegerNode(xmlDom, sectionNode, "drive_speed", storeConfig.driveSpeed)
    5295           addBooleanNode(xmlDom, sectionNode, "check_data", storeConfig.checkData)
    5296           addBooleanNode(xmlDom, sectionNode, "check_media", storeConfig.checkMedia)
    5297           addBooleanNode(xmlDom, sectionNode, "warn_midnite", storeConfig.warnMidnite)
    5298           addBooleanNode(xmlDom, sectionNode, "no_eject", storeConfig.noEject)
    5299           addIntegerNode(xmlDom, sectionNode, "refresh_media_delay", storeConfig.refreshMediaDelay)
    5300           addIntegerNode(xmlDom, sectionNode, "eject_delay", storeConfig.ejectDelay)
    5301           Config._addBlankBehavior(xmlDom, sectionNode, storeConfig.blankBehavior)
    5302
    5303     @staticmethod
    5304 -   def _addPurge(xmlDom, parentNode, purgeConfig):
    5305        """
    5306        Adds a <purge> configuration section as the next child of a parent.
    5307
    5308        We add the following fields to the document::
    5309
    5310           purgeDirs     //cb_config/purge/dir
    5311
    5312        The individual directory entries are added by L{_addPurgeDir}.
    5313
    5314        If C{purgeConfig} is C{None}, then no container will be added.
    5315
    5316        @param xmlDom: DOM tree as from L{createOutputDom}.
    5317        @param parentNode: Parent that the section should be appended to.
    5318        @param purgeConfig: Purge configuration section to be added to the document.
    5319        """
    5320        if purgeConfig is not None:
    5321           sectionNode = addContainerNode(xmlDom, parentNode, "purge")
    5322           if purgeConfig.purgeDirs is not None:
    5323              for purgeDir in purgeConfig.purgeDirs:
    5324                 Config._addPurgeDir(xmlDom, sectionNode, purgeDir)
    5325
    5326     @staticmethod
    5327 -   def _addExtendedAction(xmlDom, parentNode, action):
    5328        """
    5329        Adds an extended action container as the next child of a parent.
    5330
    5331        We add the following fields to the document::
    5332
    5333           name           action/name
    5334           module         action/module
    5335           function       action/function
    5336           index          action/index
    5337           dependencies   action/depends
    5338
    5339        Dependencies are added by the L{_addDependencies} method.
    5340
    5341        The <action> node itself is created as the next child of the parent node.
    5342        This method only adds one action node.  The parent must loop for each action
    5343        in the C{ExtensionsConfig} object.
    5344
    5345        If C{action} is C{None}, this method call will be a no-op.
    5346
    5347        @param xmlDom: DOM tree as from L{createOutputDom}.
    5348        @param parentNode: Parent that the section should be appended to.
    5349        @param action: Purge directory to be added to the document.
    5350        """
    5351        if action is not None:
    5352           sectionNode = addContainerNode(xmlDom, parentNode, "action")
    5353           addStringNode(xmlDom, sectionNode, "name", action.name)
    5354           addStringNode(xmlDom, sectionNode, "module", action.module)
    5355           addStringNode(xmlDom, sectionNode, "function", action.function)
    5356           addIntegerNode(xmlDom, sectionNode, "index", action.index)
    5357           Config._addDependencies(xmlDom, sectionNode, action.dependencies)
    5358
    5359     @staticmethod
    5360 -   def _addOverride(xmlDom, parentNode, override):
    5361        """
    5362        Adds a command override container as the next child of a parent.
    5363
    5364        We add the following fields to the document::
    5365
    5366           command                 override/command
    5367           absolutePath            override/abs_path
    5368
    5369        The <override> node itself is created as the next child of the parent
    5370        node.  This method only adds one override node.  The parent must loop for
    5371        each override in the C{OptionsConfig} object.
    5372
    5373        If C{override} is C{None}, this method call will be a no-op.
    5374
    5375        @param xmlDom: DOM tree as from L{createOutputDom}.
    5376        @param parentNode: Parent that the section should be appended to.
    5377        @param override: Command override to be added to the document.
    5378        """
    5379        if override is not None:
    5380           sectionNode = addContainerNode(xmlDom, parentNode, "override")
    5381           addStringNode(xmlDom, sectionNode, "command", override.command)
    5382           addStringNode(xmlDom, sectionNode, "abs_path", override.absolutePath)
    5383
    5384     @staticmethod
    5385 -   def _addHook(xmlDom, parentNode, hook):
    5386        """
    5387        Adds an action hook container as the next child of a parent.
    5388
    5389        The behavior varies depending on the value of the C{before} and C{after}
    5390        flags on the hook.  If the C{before} flag is set, it's a pre-action hook,
    5391        and we'll add the following fields::
    5392
    5393           action                  pre_action_hook/action
    5394           command                 pre_action_hook/command
    5395
    5396        If the C{after} flag is set, it's a post-action hook, and we'll add the
    5397        following fields::
    5398
    5399           action                  post_action_hook/action
    5400           command                 post_action_hook/command
    5401
    5402        The <pre_action_hook> or <post_action_hook> node itself is created as the
    5403        next child of the parent node.  This method only adds one hook node.  The
    5404        parent must loop for each hook in the C{OptionsConfig} object.
    5405
    5406        If C{hook} is C{None}, this method call will be a no-op.
    5407
    5408        @param xmlDom: DOM tree as from L{createOutputDom}.
    5409        @param parentNode: Parent that the section should be appended to.
    5410        @param hook: Command hook to be added to the document.
    5411        """
    5412        if hook is not None:
    5413           if hook.before:
    5414              sectionNode = addContainerNode(xmlDom, parentNode, "pre_action_hook")
    5415           else:
    5416              sectionNode = addContainerNode(xmlDom, parentNode, "post_action_hook")
    5417           addStringNode(xmlDom, sectionNode, "action", hook.action)
    5418           addStringNode(xmlDom, sectionNode, "command", hook.command)
    5419
    5420     @staticmethod
    5421 -   def _addCollectFile(xmlDom, parentNode, collectFile):
    5422        """
    5423        Adds a collect file container as the next child of a parent.
    5424
    5425        We add the following fields to the document::
    5426
    5427           absolutePath            dir/abs_path
    5428           collectMode             dir/collect_mode
    5429           archiveMode             dir/archive_mode
    5430
    5431        Note that for consistency with collect directory handling we'll only emit
    5432        the preferred C{collect_mode} tag.
    5433
    5434        The <file> node itself is created as the next child of the parent node.
    5435        This method only adds one collect file node.  The parent must loop
    5436        for each collect file in the C{CollectConfig} object.
    5437
    5438        If C{collectFile} is C{None}, this method call will be a no-op.
    5439
    5440        @param xmlDom: DOM tree as from L{createOutputDom}.
    5441        @param parentNode: Parent that the section should be appended to.
    5442        @param collectFile: Collect file to be added to the document.
    5443        """
    5444        if collectFile is not None:
    5445           sectionNode = addContainerNode(xmlDom, parentNode, "file")
    5446           addStringNode(xmlDom, sectionNode, "abs_path", collectFile.absolutePath)
    5447           addStringNode(xmlDom, sectionNode, "collect_mode", collectFile.collectMode)
    5448           addStringNode(xmlDom, sectionNode, "archive_mode", collectFile.archiveMode)
    5449
    5450     @staticmethod
    5451 -   def _addCollectDir(xmlDom, parentNode, collectDir):
    5452        """
    5453        Adds a collect directory container as the next child of a parent.
    5454
    5455        We add the following fields to the document::
    5456
    5457           absolutePath            dir/abs_path
    5458           collectMode             dir/collect_mode
    5459           archiveMode             dir/archive_mode
    5460           ignoreFile              dir/ignore_file
    5461           linkDepth               dir/link_depth
    5462           dereference             dir/dereference
    5463           recursionLevel          dir/recursion_level
    5464
    5465        Note that an original XML document might have listed the collect mode
    5466        using the C{mode} tag, since we accept both C{collect_mode} and C{mode}.
    5467        However, here we'll only emit the preferred C{collect_mode} tag.
    5468
    5469        We also add groups of the following items, one list element per item::
    5470
    5471           absoluteExcludePaths    dir/exclude/abs_path
    5472           relativeExcludePaths    dir/exclude/rel_path
    5473           excludePatterns         dir/exclude/pattern
    5474
    5475        The <dir> node itself is created as the next child of the parent node.
    5476        This method only adds one collect directory node.  The parent must loop
    5477        for each collect directory in the C{CollectConfig} object.
    5478
    5479        If C{collectDir} is C{None}, this method call will be a no-op.
    5480
    5481        @param xmlDom: DOM tree as from L{createOutputDom}.
    5482        @param parentNode: Parent that the section should be appended to.
    5483        @param collectDir: Collect directory to be added to the document.
    5484        """
    5485        if collectDir is not None:
    5486           sectionNode = addContainerNode(xmlDom, parentNode, "dir")
    5487           addStringNode(xmlDom, sectionNode, "abs_path", collectDir.absolutePath)
    5488           addStringNode(xmlDom, sectionNode, "collect_mode", collectDir.collectMode)
    5489           addStringNode(xmlDom, sectionNode, "archive_mode", collectDir.archiveMode)
    5490           addStringNode(xmlDom, sectionNode, "ignore_file", collectDir.ignoreFile)
    5491           addIntegerNode(xmlDom, sectionNode, "link_depth", collectDir.linkDepth)
    5492           addBooleanNode(xmlDom, sectionNode, "dereference", collectDir.dereference)
    5493           addIntegerNode(xmlDom, sectionNode, "recursion_level", collectDir.recursionLevel)
    5494           if ((collectDir.absoluteExcludePaths is not None and collectDir.absoluteExcludePaths != []) or
    5495               (collectDir.relativeExcludePaths is not None and collectDir.relativeExcludePaths != []) or
    5496               (collectDir.excludePatterns is not None and collectDir.excludePatterns != [])):
    5497              excludeNode = addContainerNode(xmlDom, sectionNode, "exclude")
    5498              if collectDir.absoluteExcludePaths is not None:
    5499                 for absolutePath in collectDir.absoluteExcludePaths:
    5500                    addStringNode(xmlDom, excludeNode, "abs_path", absolutePath)
    5501              if collectDir.relativeExcludePaths is not None:
    5502                 for relativePath in collectDir.relativeExcludePaths:
    5503                    addStringNode(xmlDom, excludeNode, "rel_path", relativePath)
    5504              if collectDir.excludePatterns is not None:
    5505                 for pattern in collectDir.excludePatterns:
    5506                    addStringNode(xmlDom, excludeNode, "pattern", pattern)
    5507
    5508     @staticmethod
    5509 -   def _addLocalPeer(xmlDom, parentNode, localPeer):
    5510        """
    5511        Adds a local peer container as the next child of a parent.
    5512
    5513        We add the following fields to the document::
    5514
    5515           name                peer/name
    5516           collectDir          peer/collect_dir
    5517           ignoreFailureMode   peer/ignore_failures
    5518
    5519        Additionally, C{peer/type} is filled in with C{"local"}, since this is a
    5520        local peer.
    5521
    5522        The <peer> node itself is created as the next child of the parent node.
    5523        This method only adds one peer node.  The parent must loop for each peer
    5524        in the C{StageConfig} object.
    5525
    5526        If C{localPeer} is C{None}, this method call will be a no-op.
    5527
    5528        @param xmlDom: DOM tree as from L{createOutputDom}.
    5529        @param parentNode: Parent that the section should be appended to.
    5530        @param localPeer: Purge directory to be added to the document.
    5531        """
    5532        if localPeer is not None:
    5533           sectionNode = addContainerNode(xmlDom, parentNode, "peer")
    5534           addStringNode(xmlDom, sectionNode, "name", localPeer.name)
    5535           addStringNode(xmlDom, sectionNode, "type", "local")
    5536           addStringNode(xmlDom, sectionNode, "collect_dir", localPeer.collectDir)
    5537           addStringNode(xmlDom, sectionNode, "ignore_failures", localPeer.ignoreFailureMode)
    5538
    5539     @staticmethod
    5540 -   def _addRemotePeer(xmlDom, parentNode, remotePeer):
    5541        """
    5542        Adds a remote peer container as the next child of a parent.
    5543
    5544        We add the following fields to the document::
    5545
    5546           name                peer/name
    5547           collectDir          peer/collect_dir
    5548           remoteUser          peer/backup_user
    5549           rcpCommand          peer/rcp_command
    5550           rcpCommand          peer/rcp_command
    5551           rshCommand          peer/rsh_command
    5552           cbackCommand        peer/cback_command
    5553           ignoreFailureMode   peer/ignore_failures
    5554           managed             peer/managed
    5555           managedActions      peer/managed_actions
    5556
    5557        Additionally, C{peer/type} is filled in with C{"remote"}, since this is a
    5558        remote peer.
    5559
    5560        The <peer> node itself is created as the next child of the parent node.
    5561        This method only adds one peer node.  The parent must loop for each peer
    5562        in the C{StageConfig} object.
    5563
    5564        If C{remotePeer} is C{None}, this method call will be a no-op.
    5565
    5566        @param xmlDom: DOM tree as from L{createOutputDom}.
    5567        @param parentNode: Parent that the section should be appended to.
    5568        @param remotePeer: Purge directory to be added to the document.
    5569        """
    5570        if remotePeer is not None:
    5571           sectionNode = addContainerNode(xmlDom, parentNode, "peer")
    5572           addStringNode(xmlDom, sectionNode, "name", remotePeer.name)
    5573           addStringNode(xmlDom, sectionNode, "type", "remote")
    5574           addStringNode(xmlDom, sectionNode, "collect_dir", remotePeer.collectDir)
    5575           addStringNode(xmlDom, sectionNode, "backup_user", remotePeer.remoteUser)
    5576           addStringNode(xmlDom, sectionNode, "rcp_command", remotePeer.rcpCommand)
    5577           addStringNode(xmlDom, sectionNode, "rsh_command", remotePeer.rshCommand)
    5578           addStringNode(xmlDom, sectionNode, "cback_command", remotePeer.cbackCommand)
    5579           addStringNode(xmlDom, sectionNode, "ignore_failures", remotePeer.ignoreFailureMode)
    5580           addBooleanNode(xmlDom, sectionNode, "managed", remotePeer.managed)
    5581           managedActions = Config._buildCommaSeparatedString(remotePeer.managedActions)
    5582           addStringNode(xmlDom, sectionNode, "managed_actions", managedActions)
    5583
    5584     @staticmethod
    5585 -   def _addPurgeDir(xmlDom, parentNode, purgeDir):
    5586        """
    5587        Adds a purge directory container as the next child of a parent.
    5588
    5589        We add the following fields to the document::
    5590
    5591           absolutePath            dir/abs_path
    5592           retainDays              dir/retain_days
    5593
    5594        The <dir> node itself is created as the next child of the parent node.
    5595        This method only adds one purge directory node.  The parent must loop for
    5596        each purge directory in the C{PurgeConfig} object.
    5597
    5598        If C{purgeDir} is C{None}, this method call will be a no-op.
    5599
    5600        @param xmlDom: DOM tree as from L{createOutputDom}.
    5601        @param parentNode: Parent that the section should be appended to.
    5602        @param purgeDir: Purge directory to be added to the document.
    5603        """
    5604        if purgeDir is not None:
    5605           sectionNode = addContainerNode(xmlDom, parentNode, "dir")
    5606           addStringNode(xmlDom, sectionNode, "abs_path", purgeDir.absolutePath)
    5607           addIntegerNode(xmlDom, sectionNode, "retain_days", purgeDir.retainDays)
    5608
    5609     @staticmethod
    5610 -   def _addDependencies(xmlDom, parentNode, dependencies):
    5611        """
    5612        Adds a extended action dependencies to parent node.
    5613
    5614        We add the following fields to the document::
    5615
    5616           runBefore      depends/run_before
    5617           runAfter       depends/run_after
    5618
    5619        If C{dependencies} is C{None}, this method call will be a no-op.
    5620
    5621        @param xmlDom: DOM tree as from L{createOutputDom}.
    5622        @param parentNode: Parent that the section should be appended to.
    5623        @param dependencies: C{ActionDependencies} object to be added to the document
    5624        """
    5625        if dependencies is not None:
    5626           sectionNode = addContainerNode(xmlDom, parentNode, "depends")
    5627           runBefore = Config._buildCommaSeparatedString(dependencies.beforeList)
    5628           runAfter = Config._buildCommaSeparatedString(dependencies.afterList)
    5629           addStringNode(xmlDom, sectionNode, "run_before", runBefore)
    5630           addStringNode(xmlDom, sectionNode, "run_after", runAfter)
    5631
    5632     @staticmethod
    5633 -   def _buildCommaSeparatedString(valueList):
    5634        """
    5635        Creates a comma-separated string from a list of values.
    5636
    5637        As a special case, if C{valueList} is C{None}, then C{None} will be
    5638        returned.
    5639
    5640        @param valueList: List of values to be placed into a string
    5641
    5642        @return: Values from valueList as a comma-separated string.
    5643        """
    5644        if valueList is None:
    5645           return None
    5646        return ",".join(valueList)
    5647
    5648     @staticmethod
    5649 -   def _addBlankBehavior(xmlDom, parentNode, blankBehavior):
    5650        """
    5651        Adds a blanking behavior container as the next child of a parent.
    5652
    5653        We add the following fields to the document::
    5654
    5655           blankMode    blank_behavior/mode
    5656           blankFactor  blank_behavior/factor
    5657
    5658        The <blank_behavior> node itself is created as the next child of the
    5659        parent node.
    5660
    5661        If C{blankBehavior} is C{None}, this method call will be a no-op.
    5662
    5663        @param xmlDom: DOM tree as from L{createOutputDom}.
    5664        @param parentNode: Parent that the section should be appended to.
    5665        @param blankBehavior: Blanking behavior to be added to the document.
    5666        """
    5667        if blankBehavior is not None:
    5668           sectionNode = addContainerNode(xmlDom, parentNode, "blank_behavior")
    5669           addStringNode(xmlDom, sectionNode, "mode", blankBehavior.blankMode)
    5670           addStringNode(xmlDom, sectionNode, "factor", blankBehavior.blankFactor)
    5671
    5672
    5673     #################################################
    5674     # High-level methods used for validating content
    5675     #################################################
    5676
    5677 -   def _validateContents(self):
    5678        """
    5679        Validates configuration contents per rules discussed in module
    5680        documentation.
    5681
    5682        This is the second pass at validation.  It ensures that any filled-in
    5683        section contains valid data.  Any sections which is not set to C{None} is
    5684        validated per the rules for that section, laid out in the module
    5685        documentation (above).
    5686
    5687        @raise ValueError: If configuration is invalid.
    5688        """
    5689        self._validateReference()
    5690        self._validateExtensions()
    5691        self._validateOptions()
    5692        self._validatePeers()
    5693        self._validateCollect()
    5694        self._validateStage()
    5695        self._validateStore()
    5696        self._validatePurge()
    5697
    5698 -   def _validateReference(self):
    5699        """
    5700        Validates reference configuration.
    5701        There are currently no reference-related validations.
    5702        @raise ValueError: If reference configuration is invalid.
    5703        """
    5704        pass
    5705
    5706 -   def _validateExtensions(self):
    5707        """
    5708        Validates extensions configuration.
    5709
    5710        The list of actions may be either C{None} or an empty list C{[]} if
    5711        desired.  Each extended action must include a name, a module, and a
    5712        function.
    5713
    5714        Then, if the order mode is None or "index", an index is required; and if
    5715        the order mode is "dependency", dependency information is required.
    5716
    5717        @raise ValueError: If reference configuration is invalid.
    5718        """
    5719        if self.extensions is not None:
    5720           if self.extensions.actions is not None:
    5721              names = []
    5722              for action in self.extensions.actions:
    5723                 if action.name is None:
    5724                    raise ValueError("Each extended action must set a name.")
    5725                 names.append(action.name)
    5726                 if action.module is None:
    5727                    raise ValueError("Each extended action must set a module.")
    5728                 if action.function is None:
    5729                    raise ValueError("Each extended action must set a function.")
    5730                 if self.extensions.orderMode is None or self.extensions.orderMode == "index":
    5731                    if action.index is None:
    5732                       raise ValueError("Each extended action must set an index, based on order mode.")
    5733                 elif self.extensions.orderMode == "dependency":
    5734                    if action.dependencies is None:
    5735                       raise ValueError("Each extended action must set dependency information, based on order mode.")
    5736              checkUnique("Duplicate extension names exist:", names)
    5737
    5738 -   def _validateOptions(self):
    5739        """
    5740        Validates options configuration.
    5741
    5742        All fields must be filled in except the rsh command.  The rcp and rsh
    5743        commands are used as default values for all remote peers.  Remote peers
    5744        can also rely on the backup user as the default remote user name if they
    5745        choose.
    5746
    5747        @raise ValueError: If reference configuration is invalid.
    5748        """
    5749        if self.options is not None:
    5750           if self.options.startingDay is None:
    5751              raise ValueError("Options section starting day must be filled in.")
    5752           if self.options.workingDir is None:
    5753              raise ValueError("Options section working directory must be filled in.")
    5754           if self.options.backupUser is None:
    5755              raise ValueError("Options section backup user must be filled in.")
    5756           if self.options.backupGroup is None:
    5757              raise ValueError("Options section backup group must be filled in.")
    5758           if self.options.rcpCommand is None:
    5759              raise ValueError("Options section remote copy command must be filled in.")
    5760
    5761 -   def _validatePeers(self):
    5762        """
    5763        Validates peers configuration per rules in L{_validatePeerList}.
    5764        @raise ValueError: If peers configuration is invalid.
    5765        """
    5766        if self.peers is not None:
    5767           self._validatePeerList(self.peers.localPeers, self.peers.remotePeers)
    5768
    5769 -   def _validateCollect(self):
    5770        """
    5771        Validates collect configuration.
    5772
    5773        The target directory must be filled in.  The collect mode, archive mode,
    5774        ignore file, and recursion level are all optional.  The list of absolute
    5775        paths to exclude and patterns to exclude may be either C{None} or an
    5776        empty list C{[]} if desired.
    5777
    5778        Each collect directory entry must contain an absolute path to collect,
    5779        and then must either be able to take collect mode, archive mode and
    5780        ignore file configuration from the parent C{CollectConfig} object, or
    5781        must set each value on its own.  The list of absolute paths to exclude,
    5782        relative paths to exclude and patterns to exclude may be either C{None}
    5783        or an empty list C{[]} if desired.  Any list of absolute paths to exclude
    5784        or patterns to exclude will be combined with the same list in the
    5785        C{CollectConfig} object to make the complete list for a given directory.
    5786
    5787        @raise ValueError: If collect configuration is invalid.
    5788        """
    5789        if self.collect is not None:
    5790           if self.collect.targetDir is None:
    5791              raise ValueError("Collect section target directory must be filled in.")
    5792           if self.collect.collectFiles is not None:
    5793              for collectFile in self.collect.collectFiles:
    5794                 if collectFile.absolutePath is None:
    5795                    raise ValueError("Each collect file must set an absolute path.")
    5796                 if self.collect.collectMode is None and collectFile.collectMode is None:
    5797                    raise ValueError("Collect mode must either be set in parent collect section or individual collect file.")
    5798                 if self.collect.archiveMode is None and collectFile.archiveMode is None:
    5799                    raise ValueError("Archive mode must either be set in parent collect section or individual collect file.")
    5800           if self.collect.collectDirs is not None:
    5801              for collectDir in self.collect.collectDirs:
    5802                 if collectDir.absolutePath is None:
    5803                    raise ValueError("Each collect directory must set an absolute path.")
    5804                 if self.collect.collectMode is None and collectDir.collectMode is None:
    5805                    raise ValueError("Collect mode must either be set in parent collect section or individual collect directory.")
    5806                 if self.collect.archiveMode is None and collectDir.archiveMode is None:
    5807                    raise ValueError("Archive mode must either be set in parent collect section or individual collect directory.")
    5808                 if self.collect.ignoreFile is None and collectDir.ignoreFile is None:
    5809                    raise ValueError("Ignore file must either be set in parent collect section or individual collect directory.")
    5810                 if (collectDir.linkDepth is None or collectDir.linkDepth < 1) and collectDir.dereference:
    5811                    raise ValueError("Dereference flag is only valid when a non-zero link depth is in use.")
    5812
    5813 -   def _validateStage(self):
    5814        """
    5815        Validates stage configuration.
    5816
    5817        The target directory must be filled in, and the peers are
    5818        also validated.
    5819
    5820        Peers are only required in this section if the peers configuration
    5821        section is not filled in.  However, if any peers are filled in
    5822        here, they override the peers configuration and must meet the
    5823        validation criteria in L{_validatePeerList}.
    5824
    5825        @raise ValueError: If stage configuration is invalid.
    5826        """
    5827        if self.stage is not None:
    5828           if self.stage.targetDir is None:
    5829              raise ValueError("Stage section target directory must be filled in.")
    5830           if self.peers is None:
    5831              # In this case, stage configuration is our only configuration and must be valid.
    5832              self._validatePeerList(self.stage.localPeers, self.stage.remotePeers)
    5833           else:
    5834              # In this case, peers configuration is the default and stage configuration overrides.
    5835              # Validation is only needed if it's stage configuration is actually filled in.
    5836              if self.stage.hasPeers():
    5837                 self._validatePeerList(self.stage.localPeers, self.stage.remotePeers)
    5838
    5839 -   def _validateStore(self):
    5840        """
    5841        Validates store configuration.
    5842
    5843        The device type, drive speed, and blanking behavior are optional.  All
    5844        other values are required. Missing booleans will be set to defaults.
    5845
    5846        If blanking behavior is provided, then both a blanking mode and a
    5847        blanking factor are required.
    5848
    5849        The image writer functionality in the C{writer} module is supposed to be
    5850        able to handle a device speed of C{None}.
    5851
    5852        Any caller which needs a "real" (non-C{None}) value for the device type
    5853        can use C{DEFAULT_DEVICE_TYPE}, which is guaranteed to be sensible.
    5854
    5855        This is also where we make sure that the media type -- which is already a
    5856        valid type -- matches up properly with the device type.
    5857
    5858        @raise ValueError: If store configuration is invalid.
    5859        """
    5860        if self.store is not None:
    5861           if self.store.sourceDir is None:
    5862              raise ValueError("Store section source directory must be filled in.")
    5863           if self.store.mediaType is None:
    5864              raise ValueError("Store section media type must be filled in.")
    5865           if self.store.devicePath is None:
    5866              raise ValueError("Store section device path must be filled in.")
    5867           if self.store.deviceType == None or self.store.deviceType == "cdwriter":
    5868              if self.store.mediaType not in VALID_CD_MEDIA_TYPES:
    5869                 raise ValueError("Media type must match device type.")
    5870           elif self.store.deviceType == "dvdwriter":
    5871              if self.store.mediaType not in VALID_DVD_MEDIA_TYPES:
    5872                 raise ValueError("Media type must match device type.")
    5873           if self.store.blankBehavior is not None:
    5874              if self.store.blankBehavior.blankMode is None and self.store.blankBehavior.blankFactor is None:
    5875                 raise ValueError("If blanking behavior is provided, all values must be filled in.")
    5876
    5877 -   def _validatePurge(self):
    5878        """
    5879        Validates purge configuration.
    5880
    5881        The list of purge directories may be either C{None} or an empty list
    5882        C{[]} if desired.  All purge directories must contain a path and a retain
    5883        days value.
    5884
    5885        @raise ValueError: If purge configuration is invalid.
    5886        """
    5887        if self.purge is not None:
    5888           if self.purge.purgeDirs is not None:
    5889              for purgeDir in self.purge.purgeDirs:
    5890                 if purgeDir.absolutePath is None:
    5891                    raise ValueError("Each purge directory must set an absolute path.")
    5892                 if purgeDir.retainDays is None:
    5893                    raise ValueError("Each purge directory must set a retain days value.")
    5894
    5895 -   def _validatePeerList(self, localPeers, remotePeers):
    5896        """
    5897        Validates the set of local and remote peers.
    5898
    5899        Local peers must be completely filled in, including both name and collect
    5900        directory.  Remote peers must also fill in the name and collect
    5901        directory, but can leave the remote user and rcp command unset.  In this
    5902        case, the remote user is assumed to match the backup user from the
    5903        options section and rcp command is taken directly from the options
    5904        section.
    5905
    5906        @param localPeers: List of local peers
    5907        @param remotePeers: List of remote peers
    5908
    5909        @raise ValueError: If stage configuration is invalid.
    5910        """
    5911        if localPeers is None and remotePeers is None:
    5912           raise ValueError("Peer list must contain at least one backup peer.")
    5913        if localPeers is None and remotePeers is not None:
    5914           if len(remotePeers) < 1:
    5915              raise ValueError("Peer list must contain at least one backup peer.")
    5916        elif localPeers is not None and remotePeers is None:
    5917           if len(localPeers) < 1:
    5918              raise ValueError("Peer list must contain at least one backup peer.")
    5919        elif localPeers is not None and remotePeers is not None:
    5920           if len(localPeers) + len(remotePeers) < 1:
    5921              raise ValueError("Peer list must contain at least one backup peer.")
    5922        names = []
    5923        if localPeers is not None:
    5924           for localPeer in localPeers:
    5925              if localPeer.name is None:
    5926                 raise ValueError("Local peers must set a name.")
    5927              names.append(localPeer.name)
    5928              if localPeer.collectDir is None:
    5929                 raise ValueError("Local peers must set a collect directory.")
    5930        if remotePeers is not None:
    5931           for remotePeer in remotePeers:
    5932              if remotePeer.name is None:
    5933                 raise ValueError("Remote peers must set a name.")
    5934              names.append(remotePeer.name)
    5935              if remotePeer.collectDir is None:
    5936                 raise ValueError("Remote peers must set a collect directory.")
    5937              if (self.options is None or self.options.backupUser is None) and remotePeer.remoteUser is None:
    5938                 raise ValueError("Remote user must either be set in options section or individual remote peer.")
    5939              if (self.options is None or self.options.rcpCommand is None) and remotePeer.rcpCommand is None:
    5940                 raise ValueError("Remote copy command must either be set in options section or individual remote peer.")
    5941              if remotePeer.managed:
    5942                 if (self.options is None or self.options.rshCommand is None) and remotePeer.rshCommand is None:
    5943                    raise ValueError("Remote shell command must either be set in options section or individual remote peer.")
    5944                 if (self.options is None or self.options.cbackCommand is None) and remotePeer.cbackCommand is None:
    5945                    raise ValueError("Remote cback command must either be set in options section or individual remote peer.")
    5946                 if ((self.options is None or self.options.managedActions is None or len(self.options.managedActions) < 1)
    5947                      and (remotePeer.managedActions is None or len(remotePeer.managedActions) < 1)):
    5948                    raise ValueError("Managed actions list must be set in options section or individual remote peer.")
    5949        checkUnique("Duplicate peer names exist:", names)
    5950
    5951
    5952  ########################################################################
    5953  # General utility functions
    5954  ########################################################################
    5955
    5956 -def readByteQuantity(parent, name):
    5957     """
    5958     Read a byte size value from an XML document.
    5959
    5960     A byte size value is an interpreted string value.  If the string value
    5961     ends with "MB" or "GB", then the string before that is interpreted as
    5962     megabytes or gigabytes.  Otherwise, it is intepreted as bytes.
    5963
    5964     @param parent: Parent node to search beneath.
    5965     @param name: Name of node to search for.
    5966
    5967     @return: ByteQuantity parsed from XML document
    5968     """
    5969     data = readString(parent, name)
    5970     if data is None:
    5971        return None
    5972     data = data.strip()
    5973     if data.endswith("KB"):
    5974        quantity = data[0:data.rfind("KB")].strip()
    5975        units = UNIT_KBYTES
    5976     elif data.endswith("MB"):
    5977        quantity = data[0:data.rfind("MB")].strip()
    5978        units = UNIT_MBYTES
    5979     elif data.endswith("GB"):
    5980        quantity = data[0:data.rfind("GB")].strip()
    5981        units = UNIT_GBYTES
    5982     else:
    5983        quantity = data.strip()
    5984        units = UNIT_BYTES
    5985     return ByteQuantity(quantity, units)
    5986
    5987 -def addByteQuantityNode(xmlDom, parentNode, nodeName, byteQuantity):
    5988     """
    5989     Adds a text node as the next child of a parent, to contain a byte size.
    5990
    5991     If the C{byteQuantity} is None, then the node will be created, but will
    5992     be empty (i.e. will contain no text node child).
    5993
    5994     The size in bytes will be normalized.  If it is larger than 1.0 GB, it will
    5995     be shown in GB ("1.0 GB").  If it is larger than 1.0 MB ("1.0 MB"), it will
    5996     be shown in MB.  Otherwise, it will be shown in bytes ("423413").
    5997
    5998     @param xmlDom: DOM tree as from C{impl.createDocument()}.
    5999     @param parentNode: Parent node to create child for.
    6000     @param nodeName: Name of the new container node.
    6001     @param byteQuantity: ByteQuantity object to put into the XML document
    6002
    6003     @return: Reference to the newly-created node.
    6004     """
    6005     if byteQuantity is None:
    6006        byteString = None
    6007     elif byteQuantity.units == UNIT_KBYTES:
    6008        byteString = "%s KB" % byteQuantity.quantity
    6009     elif byteQuantity.units == UNIT_MBYTES:
    6010        byteString = "%s MB" % byteQuantity.quantity
    6011     elif byteQuantity.units == UNIT_GBYTES:
    6012        byteString = "%s GB" % byteQuantity.quantity
    6013     else:
    6014        byteString = byteQuantity.quantity
    6015     return addStringNode(xmlDom, parentNode, nodeName, byteString)
    6016