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