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

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