Thread: [cedar-backup-svn] SF.net SVN: cedar-backup: [867] cedar-backup2/trunk (Page 2)
Brought to you by:
pronovic
|
From: <pro...@us...> - 2008-03-18 23:54:56
|
Revision: 867
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=867&view=rev
Author: pronovic
Date: 2008-03-18 16:54:52 -0700 (Tue, 18 Mar 2008)
Log Message:
-----------
Add tests for PercentageQuantity
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/extend/capacity.py
cedar-backup2/trunk/test/capacitytests.py
Modified: cedar-backup2/trunk/CedarBackup2/extend/capacity.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/extend/capacity.py 2008-03-18 23:45:43 UTC (rev 866)
+++ cedar-backup2/trunk/CedarBackup2/extend/capacity.py 2008-03-18 23:54:52 UTC (rev 867)
@@ -139,10 +139,10 @@
"""
if value is not None:
if len(value) < 1:
- raise ValueError("Quantity must be a non-empty string.")
+ raise ValueError("Percentage must be a non-empty string.")
floatValue = float(value)
- if floatValue < 0.0:
- raise ValueError("Quantity cannot be negative.")
+ if floatValue < 0.0 or floatValue > 100.0:
+ raise ValueError("Percentage must be a positive value from 0.0 to 100.0")
self._quantity = value # keep around string
def _getQuantity(self):
@@ -154,8 +154,11 @@
def _getPercentage(self):
"""
Property target used to get the quantity as a floating point number.
+ If there is no quantity set, then a value of 0.0 is returned.
"""
- return float(self.quantity)
+ if self.quantity is not None:
+ return float(self.quantity)
+ return 0.0
quantity = property(_getQuantity, _setQuantity, None, doc="Percentage value, as a string")
percentage = property(_getPercentage, None, None, "Percentage value, as a floating point number.")
Modified: cedar-backup2/trunk/test/capacitytests.py
===================================================================
--- cedar-backup2/trunk/test/capacitytests.py 2008-03-18 23:45:43 UTC (rev 866)
+++ cedar-backup2/trunk/test/capacitytests.py 2008-03-18 23:54:52 UTC (rev 867)
@@ -114,6 +114,203 @@
# Test Case Classes
#######################################################################
+###############################
+# TestPercentageQuantity class
+###############################
+
+class TestPercentageQuantity(unittest.TestCase):
+
+ """Tests for the PercentageQuantity class."""
+
+ ##################
+ # Utility methods
+ ##################
+
+ def failUnlessAssignRaises(self, exception, object, property, value):
+ """Equivalent of L{failUnlessRaises}, but used for property assignments instead."""
+ failUnlessAssignRaises(self, exception, object, property, value)
+
+
+ ############################
+ # Test __repr__ and __str__
+ ############################
+
+ def testStringFuncs_001(self):
+ """
+ Just make sure that the string functions don't have errors (i.e. bad variable names).
+ """
+ obj = PercentageQuantity()
+ obj.__repr__()
+ obj.__str__()
+
+
+ ##################################
+ # Test constructor and attributes
+ ##################################
+
+ def testConstructor_001(self):
+ """
+ Test constructor with no values filled in.
+ """
+ quantity = PercentageQuantity()
+ self.failUnlessEqual(None, quantity.quantity)
+ self.failUnlessEqual(0.0, quantity.percentage)
+
+ def testConstructor_002(self):
+ """
+ Test constructor with all values filled in, with valid values.
+ """
+ quantity = PercentageQuantity("6")
+ self.failUnlessEqual("6", quantity.quantity)
+ self.failUnlessEqual(6.0, quantity.percentage)
+
+ def testConstructor_003(self):
+ """
+ Test assignment of quantity attribute, None value.
+ """
+ quantity = PercentageQuantity(quantity="1.0")
+ self.failUnlessEqual("1.0", quantity.quantity)
+ self.failUnlessEqual(1.0, quantity.percentage)
+ quantity.quantity = None
+ self.failUnlessEqual(None, quantity.quantity)
+ self.failUnlessEqual(0.0, quantity.percentage)
+
+ def testConstructor_004(self):
+ """
+ Test assignment of quantity attribute, valid values.
+ """
+ quantity = PercentageQuantity()
+ self.failUnlessEqual(None, quantity.quantity)
+ self.failUnlessEqual(0.0, quantity.percentage)
+ quantity.quantity = "1.0"
+ self.failUnlessEqual("1.0", quantity.quantity)
+ self.failUnlessEqual(1.0, quantity.percentage)
+ quantity.quantity = ".1"
+ self.failUnlessEqual(".1", quantity.quantity)
+ self.failUnlessEqual(0.1, quantity.percentage)
+ quantity.quantity = "12"
+ self.failUnlessEqual("12", quantity.quantity)
+ self.failUnlessEqual(12.0, quantity.percentage)
+ quantity.quantity = "0.5"
+ self.failUnlessEqual("0.5", quantity.quantity)
+ self.failUnlessEqual(0.5, quantity.percentage)
+ quantity.quantity = "0.25E2"
+ self.failUnlessEqual("0.25E2", quantity.quantity)
+ self.failUnlessEqual(0.25e2, quantity.percentage)
+ if hexFloatLiteralAllowed():
+ # Some interpreters allow this, some don't
+ quantity.quantity = "0x0C"
+ self.failUnlessEqual("0x0C", quantity.quantity)
+ self.failUnlessEqual(12.0, quantity.percentage)
+
+ def testConstructor_005(self):
+ """
+ Test assignment of quantity attribute, invalid value (empty).
+ """
+ quantity = PercentageQuantity()
+ self.failUnlessEqual(None, quantity.quantity)
+ self.failUnlessAssignRaises(ValueError, quantity, "quantity", "")
+ self.failUnlessEqual(None, quantity.quantity)
+
+ def testConstructor_006(self):
+ """
+ Test assignment of quantity attribute, invalid value (not a floating point number).
+ """
+ quantity = PercentageQuantity()
+ self.failUnlessEqual(None, quantity.quantity)
+ self.failUnlessAssignRaises(ValueError, quantity, "quantity", "blech")
+ self.failUnlessEqual(None, quantity.quantity)
+
+ def testConstructor_007(self):
+ """
+ Test assignment of quantity attribute, invalid value (negative number).
+ """
+ quantity = PercentageQuantity()
+ self.failUnlessEqual(None, quantity.quantity)
+ self.failUnlessAssignRaises(ValueError, quantity, "quantity", "-3")
+ self.failUnlessEqual(None, quantity.quantity)
+ self.failUnlessAssignRaises(ValueError, quantity, "quantity", "-6.8")
+ self.failUnlessEqual(None, quantity.quantity)
+ self.failUnlessAssignRaises(ValueError, quantity, "quantity", "-0.2")
+ self.failUnlessEqual(None, quantity.quantity)
+ self.failUnlessAssignRaises(ValueError, quantity, "quantity", "-.1")
+ self.failUnlessEqual(None, quantity.quantity)
+
+ def testConstructor_008(self):
+ """
+ Test assignment of quantity attribute, invalid value (larger than 100%).
+ """
+ quantity = PercentageQuantity()
+ self.failUnlessEqual(None, quantity.quantity)
+ self.failUnlessAssignRaises(ValueError, quantity, "quantity", "100.0001")
+ self.failUnlessEqual(None, quantity.quantity)
+ self.failUnlessAssignRaises(ValueError, quantity, "quantity", "101")
+ self.failUnlessEqual(None, quantity.quantity)
+ self.failUnlessAssignRaises(ValueError, quantity, "quantity", "1e6")
+ self.failUnlessEqual(None, quantity.quantity)
+
+
+ ############################
+ # Test comparison operators
+ ############################
+
+ def testComparison_001(self):
+ """
+ Test comparison of two identical objects, all attributes None.
+ """
+ quantity1 = PercentageQuantity()
+ quantity2 = PercentageQuantity()
+ self.failUnlessEqual(quantity1, quantity2)
+ self.failUnless(quantity1 == quantity2)
+ self.failUnless(not quantity1 < quantity2)
+ self.failUnless(quantity1 <= quantity2)
+ self.failUnless(not quantity1 > quantity2)
+ self.failUnless(quantity1 >= quantity2)
+ self.failUnless(not quantity1 != quantity2)
+
+ def testComparison_002(self):
+ """
+ Test comparison of two identical objects, all attributes non-None.
+ """
+ quantity1 = PercentageQuantity("12")
+ quantity2 = PercentageQuantity("12")
+ self.failUnlessEqual(quantity1, quantity2)
+ self.failUnless(quantity1 == quantity2)
+ self.failUnless(not quantity1 < quantity2)
+ self.failUnless(quantity1 <= quantity2)
+ self.failUnless(not quantity1 > quantity2)
+ self.failUnless(quantity1 >= quantity2)
+ self.failUnless(not quantity1 != quantity2)
+
+ def testComparison_003(self):
+ """
+ Test comparison of two differing objects, quantity differs (one None).
+ """
+ quantity1 = PercentageQuantity()
+ quantity2 = PercentageQuantity(quantity="12")
+ self.failIfEqual(quantity1, quantity2)
+ self.failUnless(not quantity1 == quantity2)
+ self.failUnless(quantity1 < quantity2)
+ self.failUnless(quantity1 <= quantity2)
+ self.failUnless(not quantity1 > quantity2)
+ self.failUnless(not quantity1 >= quantity2)
+ self.failUnless(quantity1 != quantity2)
+
+ def testComparison_004(self):
+ """
+ Test comparison of two differing objects, quantity differs.
+ """
+ quantity1 = PercentageQuantity("10")
+ quantity2 = PercentageQuantity("12")
+ self.failIfEqual(quantity1, quantity2)
+ self.failUnless(not quantity1 == quantity2)
+ self.failUnless(quantity1 < quantity2)
+ self.failUnless(quantity1 <= quantity2)
+ self.failUnless(not quantity1 > quantity2)
+ self.failUnless(not quantity1 >= quantity2)
+ self.failUnless(quantity1 != quantity2)
+
+
##########################
# TestCapacityConfig class
##########################
@@ -694,7 +891,7 @@
def suite():
"""Returns a suite containing all the test cases in this module."""
return unittest.TestSuite((
-# unittest.makeSuite(TestPercentageQuantity, 'test'),
+ unittest.makeSuite(TestPercentageQuantity, 'test'),
unittest.makeSuite(TestCapacityConfig, 'test'),
unittest.makeSuite(TestLocalConfig, 'test'),
))
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-19 00:24:40
|
Revision: 868
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=868&view=rev
Author: pronovic
Date: 2008-03-18 17:24:19 -0700 (Tue, 18 Mar 2008)
Log Message:
-----------
Document capacity extension in user manual
Modified Paths:
--------------
cedar-backup2/trunk/Changelog
cedar-backup2/trunk/manual/src/extensions.xml
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-18 23:54:52 UTC (rev 867)
+++ cedar-backup2/trunk/Changelog 2008-03-19 00:24:19 UTC (rev 868)
@@ -6,7 +6,10 @@
- Add CollectDir.linkDepth attribute
- Modify collect action to obey CollectDir.linkDepth
- Update user manual to discuss new attribute
- * Refactored ByteQuantity functionality out of split and into config.
+ * Implement a capacity-checking extension (closes: #1915496).
+ - Add new extension in CedarBackup2/extend/capacity.py
+ - Refactor ByteQuantity out of split.py and into config.py
+ - Update user manual to discuss new extension
Version 2.15.3 16 Mar 2008
Modified: cedar-backup2/trunk/manual/src/extensions.xml
===================================================================
--- cedar-backup2/trunk/manual/src/extensions.xml 2008-03-18 23:54:52 UTC (rev 867)
+++ cedar-backup2/trunk/manual/src/extensions.xml 2008-03-19 00:24:19 UTC (rev 868)
@@ -7,7 +7,7 @@
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
-# Copyright (c) 2005-2007 Kenneth J. Pronovici.
+# Copyright (c) 2005-2008 Kenneth J. Pronovici.
# All rights reserved.
#
# This work is free; you can redistribute it and/or modify it
@@ -1492,7 +1492,8 @@
</para>
<programlisting>
-<extensions> <action>
+<extensions>
+ <action>
<name>split</name>
<module>CedarBackup2.extend.split</module>
<function>executeAction</function>
@@ -1574,5 +1575,114 @@
</sect1>
+ <!-- ################################################################# -->
+
+ <sect1 id="cedar-extensions-capacity">
+
+ <title>Capacity Extension</title>
+
+ <para>
+ The capacity extension checks the current capacity of the media in the
+ writer and prints a warning if the media exceeds an indicated
+ capacity. The capacity is indicated either by a maximum percentage
+ utilized or by a minimum number of bytes that must remain unused.
+ </para>
+
+ <para>
+ This action can be run at any time, but is probably best run as the
+ last action on any given day, so you get as much notice as possible
+ that your media is full and needs to be replaced.
+ </para>
+
+ <para>
+ To enable this extension, add the following section to the Cedar Backup
+ configuration file:
+ </para>
+
+ <programlisting>
+<extensions> <action>
+ <name>capacity</name>
+ <module>CedarBackup2.extend.capacity</module>
+ <function>executeAction</function>
+ <index>299</index>
+ </action>
+</extensions>
+ </programlisting>
+
+ <para>
+ This extension relies on the options and store configuration sections
+ in the standard Cedar Backup configuration file, and then also
+ requires its own <literal>capacity</literal> configuration section.
+ This is an example Capacity configuration section that configures the
+ extension to warn if the media is more than 95.5% full:
+ </para>
+
+ <programlisting>
+<capacity>
+ <max_percentage>95.5</max_percentage>
+</capacity>
+ </programlisting>
+
+ <para>
+ This example configures the extension to warn if the media has fewer
+ than 16 MB free:
+ </para>
+
+ <programlisting>
+<capacity>
+ <min_bytes>16 MB</min_bytes>
+</capacity>
+ </programlisting>
+
+ <para>
+ The following elements are part of the Capacity configuration section:
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><literal>max_percentage</literal></term>
+ <listitem>
+ <para>Maximum percentage of the media that may be utilized.</para>
+ <para>
+ You must provide either this value <emphasis>or</emphasis> the
+ <literal>min_bytes</literal> value.
+ </para>
+ <para>
+ <emphasis>Restrictions:</emphasis> Must be a floating point
+ number between 0.0 and 100.0
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>min_bytes</literal></term>
+ <listitem>
+ <para>Minimum number of free bytes that must be available.</para>
+ <para>
+ You can enter this value in two different forms. It can
+ either be a simple number, in which case the value is assumed
+ to be in bytes; or it can be a number followed by a unit
+ (KB, MB, GB).
+ </para>
+ <para>
+ Valid examples are <quote>10240</quote>, <quote>250
+ MB</quote> or <quote>1.1 GB</quote>.
+ </para>
+ <para>
+ You must provide either this value <emphasis>or</emphasis> the
+ <literal>max_percentage</literal> value.
+ </para>
+ <para>
+ <emphasis>Restrictions:</emphasis> Must be a byte quantity as
+ described above.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </sect1>
+
</chapter>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-19 00:52:42
|
Revision: 869
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=869&view=rev
Author: pronovic
Date: 2008-03-18 17:52:36 -0700 (Tue, 18 Mar 2008)
Log Message:
-----------
Document "link farm" collect configuration
Modified Paths:
--------------
cedar-backup2/trunk/Changelog
cedar-backup2/trunk/manual/src/basic.xml
cedar-backup2/trunk/manual/src/config.xml
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-19 00:24:19 UTC (rev 868)
+++ cedar-backup2/trunk/Changelog 2008-03-19 00:52:36 UTC (rev 869)
@@ -6,6 +6,7 @@
- Add CollectDir.linkDepth attribute
- Modify collect action to obey CollectDir.linkDepth
- Update user manual to discuss new attribute
+ - Document "link farm" option for collect configuration
* Implement a capacity-checking extension (closes: #1915496).
- Add new extension in CedarBackup2/extend/capacity.py
- Refactor ByteQuantity out of split.py and into config.py
Modified: cedar-backup2/trunk/manual/src/basic.xml
===================================================================
--- cedar-backup2/trunk/manual/src/basic.xml 2008-03-19 00:24:19 UTC (rev 868)
+++ cedar-backup2/trunk/manual/src/basic.xml 2008-03-19 00:52:36 UTC (rev 869)
@@ -7,7 +7,7 @@
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
-# Copyright (c) 2005-2007 Kenneth J. Pronovici.
+# Copyright (c) 2005-2008 Kenneth J. Pronovici.
# All rights reserved.
#
# This work is free; you can redistribute it and/or modify it
@@ -267,9 +267,11 @@
<footnote><para>Analagous to <filename>.cvsignore</filename> in CVS</para></footnote>
or specify absolute paths or filename patterns
<footnote><para>In terms of Python regular expressions</para></footnote>
- to be excluded.
+ to be excluded. You can even configure a backup <quote>link
+ farm</quote> rather than explicitly listing files and directories
+ in configuration.
</para>
-
+
<para>
This action is optional on the master. You only need to configure
and execute the collect action on the master if you have data to
Modified: cedar-backup2/trunk/manual/src/config.xml
===================================================================
--- cedar-backup2/trunk/manual/src/config.xml 2008-03-19 00:24:19 UTC (rev 868)
+++ cedar-backup2/trunk/manual/src/config.xml 2008-03-19 00:52:36 UTC (rev 869)
@@ -7,7 +7,7 @@
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
-# Copyright (c) 2005-2007 Kenneth J. Pronovici.
+# Copyright (c) 2005-2008 Kenneth J. Pronovici.
# All rights reserved.
#
# This work is free; you can redistribute it and/or modify it
@@ -1099,9 +1099,43 @@
The collect configuration section contains configuration options
related the the collect action. This section contains a variable
number of elements, including an optional exclusion section and a
- repeating subsection used to specify which directories to collect.
+ repeating subsection used to specify which directories and/or files
+ to collect. You can also configure an ignore indicator file, which
+ lets users mark their own directories as not backed up.
</para>
+ <sidebar>
+
+ <title>Using a Link Farm</title>
+
+ <para>
+ Sometimes, it's not very convenient to list directories one by
+ one in the Cedar Backup configuration file. For instance, when
+ backing up your home directory, you often exclude as many
+ directories as you include. The ignore file mechanism can be of
+ some help, but it still isn't very convenient if there are a lot
+ of directories to ignore (or if new directories pop up all of the
+ time).
+ </para>
+
+ <para>
+ In this situation, one option is to use a <firstterm>link
+ farm</firstterm> rather than listing all of the directories in
+ configuration. A link farm is a directory that contains nothing
+ but a set of soft links to other files and directories.
+ Normally, Cedar Backup does not follow soft links, but you can
+ override this behavior for individual directories using the
+ <literal>link_depth</literal> option (see below).
+ </para>
+
+ <para>
+ When using a link farm, you still have to deal with each
+ backed-up directory individually, but you don't have to modify
+ configuration. Some users find that this works better for them.
+ </para>
+
+ </sidebar>
+
<para>
In order to actually execute the collect action, you must have
configured at least one collect directory or one collect file.
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-19 01:18:05
|
Revision: 870
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=870&view=rev
Author: pronovic
Date: 2008-03-18 18:17:38 -0700 (Tue, 18 Mar 2008)
Log Message:
-----------
Add total capacity and utilization to MediaCapacity classes
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/extend/capacity.py
cedar-backup2/trunk/CedarBackup2/writers/cdwriter.py
cedar-backup2/trunk/CedarBackup2/writers/dvdwriter.py
cedar-backup2/trunk/Changelog
Modified: cedar-backup2/trunk/CedarBackup2/extend/capacity.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/extend/capacity.py 2008-03-19 00:52:36 UTC (rev 869)
+++ cedar-backup2/trunk/CedarBackup2/extend/capacity.py 2008-03-19 01:17:38 UTC (rev 870)
@@ -55,6 +55,7 @@
import logging
# Cedar Backup modules
+from CedarBackup2.util import displayBytes
from CedarBackup2.config import ByteQuantity, readByteQuantity, addByteQuantityNode
from CedarBackup2.xmlutil import createInputDom, addContainerNode, addStringNode
from CedarBackup2.xmlutil import readFirstChild, readString
@@ -518,17 +519,13 @@
local = LocalConfig(xmlPath=configPath)
if config.store.checkMedia:
checkMediaState(config.store) # raises exception if media is not initialized
- writer = createWriter(config)
- capacity = writer.retrieveCapacity()
+ capacity = createWriter(config).retrieveCapacity()
+ logger.debug("Media capacity: %s" % capacity)
if local.capacity.maxPercentage is not None:
- if capacity.bytesAvailable == 0:
- logger.error("Media has reached capacity limit: media is 100%% utilized.")
- else:
- utilized = (float(capacity.bytesUsed) / float(capacity.bytesAvailable)) * 100.0
- if utilized > local.capacity.maxPercentage.percentage:
- logger.error("Media has reached capacity limit: media is %.2f%% utilized." % utilized)
+ if capacity.utilized > local.capacity.maxPercentage.percentage:
+ logger.error("Media has reached capacity limit: media is %.2f%% utilized." % capacity.utilized)
else: # if local.capacity.bytes is not None
if capacity.bytesAvailable < local.capacity.minBytes.bytes:
- logger.error("Media has reached capacity limit: only %d free bytes remain." % capacity.bytesAvailable)
+ logger.error("Media has reached capacity limit: only %s remain." % displayBytes(capacity.bytesAvailable))
logger.info("Executed the capacity extended action successfully.")
Modified: cedar-backup2/trunk/CedarBackup2/writers/cdwriter.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/writers/cdwriter.py 2008-03-19 00:52:36 UTC (rev 869)
+++ cedar-backup2/trunk/CedarBackup2/writers/cdwriter.py 2008-03-19 01:17:38 UTC (rev 870)
@@ -8,7 +8,7 @@
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
-# Copyright (c) 2004-2007 Kenneth J. Pronovici.
+# Copyright (c) 2004-2008 Kenneth J. Pronovici.
# All rights reserved.
#
# This program is free software; you can redistribute it and/or
@@ -198,7 +198,7 @@
supported or if the disc has no boundaries) or in exactly the form provided
by C{cdrecord -msinfo}. It can be passed as-is to the C{IsoImage} class.
- @sort: __init__, bytesUsed, bytesAvailable, boundaries
+ @sort: __init__, bytesUsed, bytesAvailable, boundaries, totalCapacity, utilized
"""
def __init__(self, bytesUsed, bytesAvailable, boundaries):
@@ -215,27 +215,51 @@
else:
self._boundaries = (int(boundaries[0]), int(boundaries[1]))
+ def __str__(self):
+ """
+ Informal string representation for class instance.
+ """
+ return "%s of %s (%.2f%%)" % (displayBytes(self.bytesUsed), displayBytes(self.totalCapacity), self.utilized)
+
def _getBytesUsed(self):
"""
- Property target used to get the bytes-used value.
+ Property target to get the bytes-used value.
"""
return self._bytesUsed
def _getBytesAvailable(self):
"""
- Property target available to get the bytes-available value.
+ Property target to get the bytes-available value.
"""
return self._bytesAvailable
def _getBoundaries(self):
"""
- Property target available to get the boundaries tuple.
+ Property target to get the boundaries tuple.
"""
return self._boundaries
+ def _getTotalCapacity(self):
+ """
+ Property target to get the total capacity (used + available).
+ """
+ return self.bytesUsed + self.bytesAvailable
+
+ def _getUtilized(self):
+ """
+ Property target to get the percent of capacity which is utilized.
+ """
+ if self.bytesAvailable <= 0.0:
+ return 100.0
+ elif self.bytesUsed <= 0.0:
+ return 0.0
+ return (self.bytesUsed / self.totalCapacity) * 100.0
+
bytesUsed = property(_getBytesUsed, None, None, doc="Space used on disc, in bytes.")
bytesAvailable = property(_getBytesAvailable, None, None, doc="Space available on disc, in bytes.")
boundaries = property(_getBoundaries, None, None, doc="Session disc boundaries, in terms of ISO sectors.")
+ totalCapacity = property(_getTotalCapacity, None, None, doc="Total capacity of the disc, in bytes.")
+ utilized = property(_getUtilized, None, None, "Percentage of the total capacity which is utilized.")
########################################################################
Modified: cedar-backup2/trunk/CedarBackup2/writers/dvdwriter.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/writers/dvdwriter.py 2008-03-19 00:52:36 UTC (rev 869)
+++ cedar-backup2/trunk/CedarBackup2/writers/dvdwriter.py 2008-03-19 01:17:38 UTC (rev 870)
@@ -8,7 +8,7 @@
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
-# Copyright (c) 2007 Kenneth J. Pronovici.
+# Copyright (c) 2007-2008 Kenneth J. Pronovici.
# All rights reserved.
#
# This program is free software; you can redistribute it and/or
@@ -166,7 +166,7 @@
Space used and space available do not include any information about media
lead-in or other overhead.
- @sort: __init__, bytesUsed, bytesAvailable
+ @sort: __init__, bytesUsed, bytesAvailable, totalCapacity, utilized
"""
def __init__(self, bytesUsed, bytesAvailable):
@@ -177,6 +177,12 @@
self._bytesUsed = float(bytesUsed)
self._bytesAvailable = float(bytesAvailable)
+ def __str__(self):
+ """
+ Informal string representation for class instance.
+ """
+ return "%s of %s (%.2f%%)" % (displayBytes(self.bytesUsed), displayBytes(self.totalCapacity), self.utilized)
+
def _getBytesUsed(self):
"""
Property target used to get the bytes-used value.
@@ -189,8 +195,26 @@
"""
return self._bytesAvailable
+ def _getTotalCapacity(self):
+ """
+ Property target to get the total capacity (used + available).
+ """
+ return self.bytesUsed + self.bytesAvailable
+
+ def _getUtilized(self):
+ """
+ Property target to get the percent of capacity which is utilized.
+ """
+ if self.bytesAvailable <= 0.0:
+ return 100.0
+ elif self.bytesUsed <= 0.0:
+ return 0.0
+ return (self.bytesUsed / self.totalCapacity) * 100.0
+
bytesUsed = property(_getBytesUsed, None, None, doc="Space used on disc, in bytes.")
bytesAvailable = property(_getBytesAvailable, None, None, doc="Space available on disc, in bytes.")
+ totalCapacity = property(_getTotalCapacity, None, None, doc="Total capacity of the disc, in bytes.")
+ utilized = property(_getUtilized, None, None, "Percentage of the total capacity which is utilized.")
########################################################################
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-19 00:52:36 UTC (rev 869)
+++ cedar-backup2/trunk/Changelog 2008-03-19 01:17:38 UTC (rev 870)
@@ -7,6 +7,7 @@
- Modify collect action to obey CollectDir.linkDepth
- Update user manual to discuss new attribute
- Document "link farm" option for collect configuration
+ - Added total capacity and utilization to MediaCapacity classes
* Implement a capacity-checking extension (closes: #1915496).
- Add new extension in CedarBackup2/extend/capacity.py
- Refactor ByteQuantity out of split.py and into config.py
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-19 01:48:50
|
Revision: 873
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=873&view=rev
Author: pronovic
Date: 2008-03-18 18:48:47 -0700 (Tue, 18 Mar 2008)
Log Message:
-----------
Release 2.16.0
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/release.py
cedar-backup2/trunk/Changelog
Modified: cedar-backup2/trunk/CedarBackup2/release.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/release.py 2008-03-19 01:32:18 UTC (rev 872)
+++ cedar-backup2/trunk/CedarBackup2/release.py 2008-03-19 01:48:47 UTC (rev 873)
@@ -34,7 +34,7 @@
AUTHOR = "Kenneth J. Pronovici"
EMAIL = "pro...@ie..."
COPYRIGHT = "2004-2008"
-VERSION = "2.15.3"
-DATE = "16 Mar 2008"
+VERSION = "2.16.0"
+DATE = "18 Mar 2008"
URL = "http://cedar-solutions.com/software/cedar-backup"
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-19 01:32:18 UTC (rev 872)
+++ cedar-backup2/trunk/Changelog 2008-03-19 01:48:47 UTC (rev 873)
@@ -1,4 +1,4 @@
-Version 2.16.0 unreleased
+Version 2.16.0 18 Mar 2008
* Make name attribute optional in RemotPeer constructor.
* Add support for collecting soft links (closes: #1854631).
@@ -7,10 +7,10 @@
- Modify collect action to obey CollectDir.linkDepth
- Update user manual to discuss new attribute
- Document "link farm" option for collect configuration
- - Added total capacity and utilization to MediaCapacity classes
* Implement a capacity-checking extension (closes: #1915496).
- Add new extension in CedarBackup2/extend/capacity.py
- Refactor ByteQuantity out of split.py and into config.py
+ - Add total capacity and utilization to MediaCapacity classes
- Update user manual to discuss new extension
Version 2.15.3 16 Mar 2008
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-19 13:23:39
|
Revision: 876
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=876&view=rev
Author: pronovic
Date: 2008-03-19 06:23:20 -0700 (Wed, 19 Mar 2008)
Log Message:
-----------
Change suggested execution index for Capacity
Modified Paths:
--------------
cedar-backup2/trunk/Changelog
cedar-backup2/trunk/manual/src/extensions.xml
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-19 04:06:03 UTC (rev 875)
+++ cedar-backup2/trunk/Changelog 2008-03-19 13:23:20 UTC (rev 876)
@@ -1,3 +1,7 @@
+Version 2.16.0 unreleased
+
+ * Change suggested execution index for Capacity in manual.
+
Version 2.16.0 18 Mar 2008
* Make name attribute optional in RemotPeer constructor.
Modified: cedar-backup2/trunk/manual/src/extensions.xml
===================================================================
--- cedar-backup2/trunk/manual/src/extensions.xml 2008-03-19 04:06:03 UTC (rev 875)
+++ cedar-backup2/trunk/manual/src/extensions.xml 2008-03-19 13:23:20 UTC (rev 876)
@@ -1388,7 +1388,7 @@
<name>encrypt</name>
<module>CedarBackup2.extend.encrypt</module>
<function>executeAction</function>
- <index>299</index>
+ <index>301</index>
</action>
</extensions>
</programlisting>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-20 02:16:30
|
Revision: 877
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=877&view=rev
Author: pronovic
Date: 2008-03-19 19:16:26 -0700 (Wed, 19 Mar 2008)
Log Message:
-----------
Fix utiltests.TestFunctions.testNullDevice_001() for Windows 2000.
Modified Paths:
--------------
cedar-backup2/trunk/Changelog
cedar-backup2/trunk/test/utiltests.py
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-19 13:23:20 UTC (rev 876)
+++ cedar-backup2/trunk/Changelog 2008-03-20 02:16:26 UTC (rev 877)
@@ -1,6 +1,7 @@
Version 2.16.0 unreleased
* Change suggested execution index for Capacity in manual.
+ * Fix utiltests.TestFunctions.testNullDevice_001() for Windows 2000.
Version 2.16.0 18 Mar 2008
Modified: cedar-backup2/trunk/test/utiltests.py
===================================================================
--- cedar-backup2/trunk/test/utiltests.py 2008-03-19 13:23:20 UTC (rev 876)
+++ cedar-backup2/trunk/test/utiltests.py 2008-03-20 02:16:26 UTC (rev 877)
@@ -3418,7 +3418,7 @@
"""
device = nullDevice()
if platformWindows():
- self.failUnlessEqual("NUL", device)
+ self.failUnlessEqual("NUL", device.upper())
else:
self.failUnlessEqual("/dev/null", device)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-20 02:42:38
|
Revision: 878
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=878&view=rev
Author: pronovic
Date: 2008-03-19 19:42:35 -0700 (Wed, 19 Mar 2008)
Log Message:
-----------
Fix typo in filesystemtests.TestFilesystemList.testRemoveLinks_002().
Modified Paths:
--------------
cedar-backup2/trunk/Changelog
cedar-backup2/trunk/test/filesystemtests.py
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-20 02:16:26 UTC (rev 877)
+++ cedar-backup2/trunk/Changelog 2008-03-20 02:42:35 UTC (rev 878)
@@ -2,6 +2,7 @@
* Change suggested execution index for Capacity in manual.
* Fix utiltests.TestFunctions.testNullDevice_001() for Windows 2000.
+ * Fix typo in filesystemtests.TestFilesystemList.testRemoveLinks_002().
Version 2.16.0 18 Mar 2008
Modified: cedar-backup2/trunk/test/filesystemtests.py
===================================================================
--- cedar-backup2/trunk/test/filesystemtests.py 2008-03-20 02:16:26 UTC (rev 877)
+++ cedar-backup2/trunk/test/filesystemtests.py 2008-03-20 02:42:35 UTC (rev 878)
@@ -9794,7 +9794,7 @@
fsList = FilesystemList()
count = fsList.removeLinks(pattern="pattern")
self.failUnlessEqual(0, count)
- self.failUnlessRaises(ValueError, fsList.removeLinks, pattern="*.jpg")
+ self.failUnlessRaises(ValueError, fsList.removeLinks, pattern="*.jpg")
def testRemoveLinks_003(self):
"""
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-20 04:00:31
|
Revision: 879
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=879&view=rev
Author: pronovic
Date: 2008-03-19 21:00:23 -0700 (Wed, 19 Mar 2008)
Log Message:
-----------
Clean up filesystem code that deals with file age, and improve unit tests.
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/filesystem.py
cedar-backup2/trunk/CedarBackup2/testutil.py
cedar-backup2/trunk/CedarBackup2/util.py
cedar-backup2/trunk/Changelog
Modified: cedar-backup2/trunk/CedarBackup2/filesystem.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/filesystem.py 2008-03-20 02:42:35 UTC (rev 878)
+++ cedar-backup2/trunk/CedarBackup2/filesystem.py 2008-03-20 04:00:23 UTC (rev 879)
@@ -52,6 +52,7 @@
import os
import re
import sha
+import math
import logging
import tarfile
@@ -1305,8 +1306,9 @@
for entry in self[:]:
if os.path.isfile(entry) and not os.path.islink(entry):
try:
- age = calculateFileAge(entry)
- if age < daysOld:
+ ageInDays = calculateFileAge(entry)
+ ageInWholeDays = math.floor(ageInDays)
+ if ageInWholeDays < daysOld:
removed += 1
self.remove(entry)
except OSError:
Modified: cedar-backup2/trunk/CedarBackup2/testutil.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/testutil.py 2008-03-20 02:42:35 UTC (rev 878)
+++ cedar-backup2/trunk/CedarBackup2/testutil.py 2008-03-20 04:00:23 UTC (rev 879)
@@ -219,16 +219,25 @@
def changeFileAge(filename, subtract=None):
"""
Changes a file age using the C{os.utime} function.
+
+ @note: Some platforms don't seem to be able to set an age precisely. As a
+ result, whereas we might have intended to set an age of 86400 seconds, we
+ actually get an age of 86399.375 seconds. When util.calculateFileAge()
+ looks at that the file, it calculates an age of 0.999992766204 days, which
+ then gets truncated down to zero whole days. The tests get very confused.
+ To work around this, I always subtract off one additional second as a fudge
+ factor. That way, the file age will be I{at least} as old as requested
+ later on.
+
@param filename: File to operate on.
@param subtract: Number of seconds to subtract from the current time.
@raise ValueError: If a path cannot be encoded properly.
"""
filename = encodePath(filename)
- if subtract is None:
- os.utime(filename, None)
- else:
- newTime = time.time() - subtract
- os.utime(filename, (newTime, newTime))
+ newTime = time.time() - 1;
+ if subtract is not None:
+ newTime -= subtract
+ os.utime(filename, (newTime, newTime))
###########################
Modified: cedar-backup2/trunk/CedarBackup2/util.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/util.py 2008-03-20 02:42:35 UTC (rev 878)
+++ cedar-backup2/trunk/CedarBackup2/util.py 2008-03-20 04:00:23 UTC (rev 879)
@@ -8,7 +8,7 @@
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
-# Copyright (c) 2004-2007 Kenneth J. Pronovici.
+# Copyright (c) 2004-2008 Kenneth J. Pronovici.
# All rights reserved.
#
# Portions copyright (c) 2001, 2002 Python Software Foundation.
@@ -119,9 +119,9 @@
BYTES_PER_MBYTE = BYTES_PER_KBYTE * KBYTES_PER_MBYTE
BYTES_PER_GBYTE = BYTES_PER_MBYTE * MBYTES_PER_GBYTE
-SECONDS_PER_MINUTE = 60
-MINUTES_PER_HOUR = 60
-HOURS_PER_DAY = 24
+SECONDS_PER_MINUTE = 60.0
+MINUTES_PER_HOUR = 60.0
+HOURS_PER_DAY = 24.0
SECONDS_PER_DAY = SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY
UNIT_BYTES = 0
@@ -1415,13 +1415,14 @@
@param file: Path to a file on disk.
- @return: Age of the file in days.
+ @return: Age of the file in days (possibly fractional).
@raise OSError: If the file doesn't exist.
"""
currentTime = int(time.time())
fileStats = os.stat(file)
lastUse = max(fileStats.st_atime, fileStats.st_mtime) # "most recent" is "largest"
- ageInDays = (currentTime - lastUse) / SECONDS_PER_DAY
+ ageInSeconds = currentTime - lastUse
+ ageInDays = ageInSeconds / SECONDS_PER_DAY
return ageInDays
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-20 02:42:35 UTC (rev 878)
+++ cedar-backup2/trunk/Changelog 2008-03-20 04:00:23 UTC (rev 879)
@@ -1,8 +1,14 @@
-Version 2.16.0 unreleased
+Version 2.16.1 unreleased
* Change suggested execution index for Capacity in manual.
* Fix utiltests.TestFunctions.testNullDevice_001() for Windows 2000.
* Fix typo in filesystemtests.TestFilesystemList.testRemoveLinks_002().
+ * Clean up filesystem code that deals with file age, and improve unit tests.
+ - Some platforms apparently cannot set file ages precisely
+ - Change calculateFileAge() to use floats throughout, which is safer
+ - Change removeYoungFiles() to explicitly check on whole days
+ - Put a 1-second fudge factor into unit tests when setting file ages
+ - This is really a test-only bug, but better safe than sorry
Version 2.16.0 18 Mar 2008
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-20 04:19:24
|
Revision: 880
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=880&view=rev
Author: pronovic
Date: 2008-03-19 21:19:19 -0700 (Wed, 19 Mar 2008)
Log Message:
-----------
Fix filesystemtests.TestBackupFileList.testGenerateFitted_004()
Modified Paths:
--------------
cedar-backup2/trunk/Changelog
cedar-backup2/trunk/test/filesystemtests.py
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-20 04:00:23 UTC (rev 879)
+++ cedar-backup2/trunk/Changelog 2008-03-20 04:19:19 UTC (rev 880)
@@ -1,14 +1,15 @@
Version 2.16.1 unreleased
- * Change suggested execution index for Capacity in manual.
- * Fix utiltests.TestFunctions.testNullDevice_001() for Windows 2000.
- * Fix typo in filesystemtests.TestFilesystemList.testRemoveLinks_002().
+ * Change suggested execution index for Capacity extension in manual.
* Clean up filesystem code that deals with file age, and improve unit tests.
- Some platforms apparently cannot set file ages precisely
- Change calculateFileAge() to use floats throughout, which is safer
- Change removeYoungFiles() to explicitly check on whole days
- Put a 1-second fudge factor into unit tests when setting file ages
- - This is really a test-only bug, but better safe than sorry
+ * Fix some unit test failures discovered on Windows 2000.
+ - Fix utiltests.TestFunctions.testNullDevice_001()
+ - Fix filesystemtests.TestBackupFileList.testGenerateFitted_004()
+ - Fix typo in filesystemtests.TestFilesystemList.testRemoveLinks_002()
Version 2.16.0 18 Mar 2008
Modified: cedar-backup2/trunk/test/filesystemtests.py
===================================================================
--- cedar-backup2/trunk/test/filesystemtests.py 2008-03-20 04:00:23 UTC (rev 879)
+++ cedar-backup2/trunk/test/filesystemtests.py 2008-03-20 04:19:19 UTC (rev 880)
@@ -16243,6 +16243,13 @@
"""
Test on a non-empty list containing only valid entries, some of which
fit.
+
+ We can get some strange behavior on Windows, which hits the "links not
+ supported" case. The file tree9/dir002/file002 is 74 bytes, and is
+ supposed to be the only file included because links are not recognized.
+ However, link004 points at file002, and apparently Windows (sometimes?)
+ sees link004 as a real file with a size of 74 bytes. Since only one of
+ the two fits in the fitted list, we just check for one or the other.
"""
self.extractTar("tree9")
path = self.buildPath(["tree9"])
@@ -16263,7 +16270,8 @@
self.failUnless(self.buildPath([ "tree9", "file002", ]) in backupList)
fittedList = backupList.generateFitted(80)
self.failUnlessEqual(1, len(fittedList))
- self.failUnless(self.buildPath([ "tree9", "dir001", "file002", ]) in fittedList)
+ self.failUnless((self.buildPath([ "tree9", "dir002", "file002", ]) in fittedList) or
+ (self.buildPath([ "tree9", "dir002", "link004", ] in fittedList)))
else:
self.failUnlessEqual(15, count)
self.failUnlessEqual(15, len(backupList))
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-20 05:10:04
|
Revision: 884
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=884&view=rev
Author: pronovic
Date: 2008-03-19 22:10:01 -0700 (Wed, 19 Mar 2008)
Log Message:
-----------
Add diagnostic info to unit test output
Modified Paths:
--------------
cedar-backup2/trunk/Changelog
cedar-backup2/trunk/INSTALL
cedar-backup2/trunk/util/test.py
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-20 04:25:33 UTC (rev 883)
+++ cedar-backup2/trunk/Changelog 2008-03-20 05:10:01 UTC (rev 884)
@@ -1,5 +1,6 @@
Version 2.16.1 unreleased
+ * Add diagnostic info to the unit test output, and ask for it in INSTALL.
* Change suggested execution index for Capacity extension in manual.
* Clean up filesystem code that deals with file age, and improve unit tests.
- Some platforms apparently cannot set file ages precisely
Modified: cedar-backup2/trunk/INSTALL
===================================================================
--- cedar-backup2/trunk/INSTALL 2008-03-20 04:25:33 UTC (rev 883)
+++ cedar-backup2/trunk/INSTALL 2008-03-20 05:10:01 UTC (rev 884)
@@ -37,5 +37,6 @@
python util/test.py
If any unit test reports a failure on your system, please email me the
-output from the unit test, so I can fix the problem.
-
+output from the unit test, so I can fix the problem. Please make sure
+to include the diagnostic information printed out at the beginning of
+the test run.
Modified: cedar-backup2/trunk/util/test.py
===================================================================
--- cedar-backup2/trunk/util/test.py 2008-03-20 04:25:33 UTC (rev 883)
+++ cedar-backup2/trunk/util/test.py 2008-03-20 05:10:01 UTC (rev 884)
@@ -94,7 +94,37 @@
import logging
import unittest
+##############################
+# printDiagnostics() function
+##############################
+def printDiagnostics():
+ """
+ Print useful diagnostic information to the screen.
+
+ This is the kind of information I usually ask for when I get a bug report.
+ If I print it out here, I shouldn't have to ask for it as often, since
+ people typically email me the test output.
+ """
+ try:
+ windowsversion = sys.getwindowsversion()
+ except AttributeError:
+ windowsversion = "N/A"
+
+ try:
+ uname = os.uname()
+ except AttributeError:
+ uname = "N/A"
+
+ print ""
+ print "Diagnostics:"
+ print " version.....:", sys.version_info
+ print " encoding....: %s" % sys.getfilesystemencoding() or sys.getdefaultencoding()
+ print " platform....: %s" % sys.platform
+ print " win32.......: %s " % windowsversion
+ print " uname.......: %s %s %s" % (uname[0], uname[2], uname[4])
+
+
##################
# main() function
##################
@@ -222,6 +252,9 @@
if args == [] or "capacity" in args: unittests["capacity"] = capacitytests.suite()
if args != []: print "*** Executing specific tests: %s" % unittests.keys()
+ # Print some diagnostic information
+ printDiagnostics()
+
# Create and run the test suite
print ""
suite = unittest.TestSuite(unittests.values())
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-20 17:10:34
|
Revision: 889
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=889&view=rev
Author: pronovic
Date: 2008-03-20 10:10:32 -0700 (Thu, 20 Mar 2008)
Log Message:
-----------
Refactor diagnostics into a new Diagnostics class
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/util.py
cedar-backup2/trunk/test/utiltests.py
cedar-backup2/trunk/util/test.py
Modified: cedar-backup2/trunk/CedarBackup2/util.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/util.py 2008-03-20 05:19:11 UTC (rev 888)
+++ cedar-backup2/trunk/CedarBackup2/util.py 2008-03-20 17:10:32 UTC (rev 889)
@@ -84,7 +84,10 @@
import time
import logging
import string
+import locale
+from CedarBackup2.release import VERSION, DATE
+
try:
import pwd
import grp
@@ -942,6 +945,153 @@
########################################################################
+# Diagnostics class definition
+########################################################################
+
+class Diagnostics(object):
+
+ """
+ Class holding runtime diagnostic information.
+
+ Diagnostic information is information that is useful to get from users for
+ debugging purposes. I'm consolidating it all here into one object.
+
+ @sort: __init__, __repr__, __str__, __cmp__
+ """
+
+ def __init__(self):
+ """
+ Constructor for the C{Diagnostics} class.
+ """
+
+ def __repr__(self):
+ """
+ Official string representation for class instance.
+ """
+ return "Diagnostics()"
+
+ def __str__(self):
+ """
+ Informal string representation for class instance.
+ """
+ return self.__repr__()
+
+ def getValues(self):
+ """
+ Get a values map for each diagnostic.
+ @return: Map from diagnostic name to diagnostic value.
+ """
+ values = {}
+ values['version'] = self.version
+ values['interpreter'] = self.interpreter
+ values['platform'] = self.platform
+ values['encoding'] = self.encoding
+ values['locale'] = self.locale
+ return values;
+
+ def printDiagnostics(self, fd=sys.stdout, prefix=""):
+ """
+ Pretty-print diagnostic information to a file descriptor.
+ @param fd: File descriptor used to print information.
+ @param prefix: Prefix string (if any) to place onto printed lines
+ @note: The C{fd} is used rather than C{print} to facilitate unit testing.
+ """
+ lines = self._buildDiagnosticLines(prefix)
+ for line in lines:
+ fd.write("%s\n" % line)
+
+ def logDiagnostics(self, method, prefix=""):
+ """
+ Pretty-print diagnostic information to the logger.
+ @param method: Logger method to use for logging (i.e. logger.info)
+ @param prefix: Prefix string (if any) to place onto printed lines
+ """
+ lines = self._buildDiagnosticLines(prefix)
+ for line in lines:
+ method("%s" % line)
+
+ def _buildDiagnosticLines(self, prefix=""):
+ """
+ Build a set of pretty-printed diagnostic lines.
+ @param prefix: Prefix string (if any) to place onto printed lines
+ @return: List of strings, not terminated by newlines.
+ """
+ values = self.getValues()
+ keys = values.keys()
+ keys.sort()
+ max = Diagnostics._getMaxLength(keys) + 3 # three extra dots in output
+ lines = []
+ for key in keys:
+ title = key.title()
+ title += (max - len(title)) * '.'
+ value = values[key]
+ line = "%s%s: %s" % (prefix, title, value)
+ lines.append(line)
+ return lines
+
+ def _getMaxLength(values):
+ """
+ Get the maximum length from among a list of strings.
+ """
+ max = 0
+ for value in values:
+ if len(value) > max:
+ max = len(value)
+ return max
+ _getMaxLength = staticmethod(_getMaxLength)
+
+ def _getVersion(self):
+ """
+ Property target to get the Cedar Backup version.
+ """
+ return "Cedar Backup v%s (%s)" % (VERSION, DATE)
+
+ def _getInterpreter(self):
+ """
+ Property target to get the Python interpreter version.
+ """
+ version = sys.version_info
+ return "Python %d.%d.%d (%s)" % (version[0], version[1], version[2], version[3])
+
+ def _getEncoding(self):
+ """
+ Property target to get the filesystem encoding.
+ """
+ return sys.getfilesystemencoding() or sys.getdefaultencoding()
+
+ def _getPlatform(self):
+ """
+ Property target to get the operating system platform.
+ """
+ platform = sys.platform
+ if platform.startswith("win"):
+ WINDOWS_PLATFORMS = [ "Windows 3.1", "Windows 95/98/ME", "Windows NT/2000/XP", "Windows CE", ]
+ wininfo = sys.getwindowsversion()
+ winversion = "%d.%d.%d" % (wininfo[0], wininfo[1], wininfo[2])
+ winplatform = WINDOWS_PLATFORMS[wininfo[3]]
+ wintext = wininfo[4] # i.e. "Service Pack 2"
+ return "%s (%s %s %s)" % (platform, winplatform, winversion, wintext)
+ else:
+ uname = os.uname()
+ sysname = uname[0] # i.e. Linux
+ release = uname[2] # i.e. 2.16.18-2
+ machine = uname[4] # i.e. i686
+ return "%s (%s %s %s)" % (platform, sysname, release, machine)
+
+ def _getLocale(self):
+ """
+ Property target to get the default locale that is in effect.
+ """
+ return locale.getdefaultlocale()[0]
+
+ version = property(_getVersion, None, None, "Cedar Backup version.")
+ interpreter = property(_getInterpreter, None, None, "Python interpreter version.")
+ platform = property(_getPlatform, None, None, "Platform identifying information.")
+ encoding = property(_getEncoding, None, None, "Filesystem encoding that is in effect.")
+ locale = property(_getLocale, None, None, "Locale that is in effect.")
+
+
+########################################################################
# General utility functions
########################################################################
Modified: cedar-backup2/trunk/test/utiltests.py
===================================================================
--- cedar-backup2/trunk/test/utiltests.py 2008-03-20 05:19:11 UTC (rev 888)
+++ cedar-backup2/trunk/test/utiltests.py 2008-03-20 17:10:32 UTC (rev 889)
@@ -9,7 +9,7 @@
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
-# Copyright (c) 2004-2007 Kenneth J. Pronovici.
+# Copyright (c) 2004-2008 Kenneth J. Pronovici.
# All rights reserved.
#
# This program is free software; you can redistribute it and/or
@@ -79,12 +79,13 @@
import unittest
import tempfile
import time
+import logging
from os.path import isdir
-from CedarBackup2.testutil import findResources, removedir, platformHasEcho, platformWindows
+from CedarBackup2.testutil import findResources, removedir, platformHasEcho, platformWindows, captureOutput
from CedarBackup2.util import UnorderedList, AbsolutePathList, ObjectTypeList
from CedarBackup2.util import RestrictedContentList, RegexMatchList, RegexList
-from CedarBackup2.util import DirectedGraph, PathResolverSingleton
+from CedarBackup2.util import DirectedGraph, PathResolverSingleton, Diagnostics
from CedarBackup2.util import sortDict, resolveCommand, executeCommand, getFunctionReference, encodePath
from CedarBackup2.util import convertSize, UNIT_BYTES, UNIT_SECTORS, UNIT_KBYTES, UNIT_MBYTES, UNIT_GBYTES
from CedarBackup2.util import displayBytes, deriveDayOfWeek, isStartOfWeek
@@ -2010,6 +2011,88 @@
self.failUnlessEqual(result, "/path/to/two")
+########################
+# TestDiagnostics class
+########################
+
+class TestDiagnostics(unittest.TestCase):
+
+ """Tests for the Diagnostics class."""
+
+ def testMethods_001(self):
+ """
+ Test the version attribute.
+ """
+ diagnostics = Diagnostics()
+ self.failIf(diagnostics.version is None)
+ self.failIfEqual("", diagnostics.version)
+
+ def testMethods_002(self):
+ """
+ Test the interpreter attribute.
+ """
+ diagnostics = Diagnostics()
+ self.failIf(diagnostics.interpreter is None)
+ self.failIfEqual("", diagnostics.interpreter)
+
+ def testMethods_003(self):
+ """
+ Test the platform attribute.
+ """
+ diagnostics = Diagnostics()
+ self.failIf(diagnostics.platform is None)
+ self.failIfEqual("", diagnostics.platform)
+
+ def testMethods_004(self):
+ """
+ Test the encoding attribute.
+ """
+ diagnostics = Diagnostics()
+ self.failIf(diagnostics.encoding is None)
+ self.failIfEqual("", diagnostics.encoding)
+
+ def testMethods_005(self):
+ """
+ Test the locale attribute.
+ """
+ diagnostics = Diagnostics()
+ self.failIf(diagnostics.locale is None)
+ self.failIfEqual("", diagnostics.locale)
+
+ def testMethods_006(self):
+ """
+ Test the getValues() method.
+ """
+ diagnostics = Diagnostics()
+ values = diagnostics.getValues()
+ self.failUnlessEqual(diagnostics.version, values['version'])
+ self.failUnlessEqual(diagnostics.interpreter, values['interpreter'])
+ self.failUnlessEqual(diagnostics.platform, values['platform'])
+ self.failUnlessEqual(diagnostics.encoding, values['encoding'])
+ self.failUnlessEqual(diagnostics.locale, values['locale'])
+
+ def testMethods_007(self):
+ """
+ Test the _buildDiagnosticLines() method.
+ """
+ values = Diagnostics().getValues()
+ lines = Diagnostics()._buildDiagnosticLines()
+ self.failUnlessEqual(len(values), len(lines))
+
+ def testMethods_008(self):
+ """
+ Test the printDiagnostics() method.
+ """
+ captureOutput(Diagnostics().printDiagnostics)
+
+ def testMethods_009(self):
+ """
+ Test the logDiagnostics() method.
+ """
+ logger = logging.getLogger("CedarBackup2.test")
+ Diagnostics().logDiagnostics(logger.info)
+
+
######################
# TestFunctions class
######################
@@ -3836,6 +3919,7 @@
unittest.makeSuite(TestRegexList, 'test'),
unittest.makeSuite(TestDirectedGraph, 'test'),
unittest.makeSuite(TestPathResolverSingleton, 'test'),
+ unittest.makeSuite(TestDiagnostics, 'test'),
unittest.makeSuite(TestFunctions, 'test'),
))
Modified: cedar-backup2/trunk/util/test.py
===================================================================
--- cedar-backup2/trunk/util/test.py 2008-03-20 05:19:11 UTC (rev 888)
+++ cedar-backup2/trunk/util/test.py 2008-03-20 17:10:32 UTC (rev 889)
@@ -94,37 +94,7 @@
import logging
import unittest
-##############################
-# printDiagnostics() function
-##############################
-def printDiagnostics():
- """
- Print useful diagnostic information to the screen.
-
- This is the kind of information I usually ask for when I get a bug report.
- If I print it out here, I shouldn't have to ask for it as often, since
- people typically email me the test output.
- """
- try:
- windowsversion = sys.getwindowsversion()
- except AttributeError:
- windowsversion = "N/A"
-
- try:
- uname = "%s %s %s" % (os.uname()[0], os.uname()[2], os.uname()[4])
- except AttributeError:
- uname = "N/A"
-
- print ""
- print "Diagnostics:"
- print " version.....:", sys.version_info
- print " encoding....: %s" % sys.getfilesystemencoding() or sys.getdefaultencoding()
- print " platform....: %s" % sys.platform
- print " win32.......:", windowsversion
- print " uname.......: %s" % uname
-
-
##################
# main() function
##################
@@ -157,7 +127,7 @@
print "location. If the import succeeds, you may be using an"
print "unexpected version of CedarBackup2."
print ""
- from CedarBackup2.util import nullDevice
+ from CedarBackup2.util import nullDevice, Diagnostics
except ImportError, e:
print "Failed to import CedarBackup2 util module: %s" % e
print "You must either run the unit tests from the CedarBackup2 source"
@@ -253,7 +223,8 @@
if args != []: print "*** Executing specific tests: %s" % unittests.keys()
# Print some diagnostic information
- printDiagnostics()
+ print ""
+ Diagnostics().printDiagnostics(prefix="*** ")
# Create and run the test suite
print ""
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-20 17:13:02
|
Revision: 890
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=890&view=rev
Author: pronovic
Date: 2008-03-20 10:12:59 -0700 (Thu, 20 Mar 2008)
Log Message:
-----------
Log diagnostic information on startup
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/cli.py
cedar-backup2/trunk/Changelog
Modified: cedar-backup2/trunk/CedarBackup2/cli.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/cli.py 2008-03-20 17:10:32 UTC (rev 889)
+++ cedar-backup2/trunk/CedarBackup2/cli.py 2008-03-20 17:12:59 UTC (rev 890)
@@ -218,6 +218,7 @@
logger.info("Cedar Backup run started.")
logger.info("Options were [%s]" % options)
logger.info("Logfile is [%s]" % logfile)
+ Diagnostics().logDiagnostics(method=logger.info)
if options.config is None:
logger.debug("Using default configuration file.")
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-20 17:10:32 UTC (rev 889)
+++ cedar-backup2/trunk/Changelog 2008-03-20 17:12:59 UTC (rev 890)
@@ -1,7 +1,10 @@
Version 2.16.1 unreleased
- * Add diagnostic info to the unit test output, and ask for it in INSTALL.
* Change suggested execution index for Capacity extension in manual.
+ * Provide support for application-wide diagnostic reporting.
+ - Add util.Diagnostics class to encapsulate information
+ - Log diagnostics when Cedar Backup first starts
+ - Print diagnostics when running unit tests
* Clean up filesystem code that deals with file age, and improve unit tests.
- Some platforms apparently cannot set file ages precisely
- Change calculateFileAge() to use floats throughout, which is safer
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-20 18:19:00
|
Revision: 892
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=892&view=rev
Author: pronovic
Date: 2008-03-20 11:18:58 -0700 (Thu, 20 Mar 2008)
Log Message:
-----------
Tweak diagnostics
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/util.py
cedar-backup2/trunk/test/utiltests.py
Modified: cedar-backup2/trunk/CedarBackup2/util.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/util.py 2008-03-20 17:16:46 UTC (rev 891)
+++ cedar-backup2/trunk/CedarBackup2/util.py 2008-03-20 18:18:58 UTC (rev 892)
@@ -1044,7 +1044,7 @@
"""
Property target to get the Cedar Backup version.
"""
- return "Cedar Backup v%s (%s)" % (VERSION, DATE)
+ return "Cedar Backup %s (%s)" % (VERSION, DATE)
def _getInterpreter(self):
"""
Modified: cedar-backup2/trunk/test/utiltests.py
===================================================================
--- cedar-backup2/trunk/test/utiltests.py 2008-03-20 17:16:46 UTC (rev 891)
+++ cedar-backup2/trunk/test/utiltests.py 2008-03-20 18:18:58 UTC (rev 892)
@@ -2056,8 +2056,7 @@
Test the locale attribute.
"""
diagnostics = Diagnostics()
- self.failIf(diagnostics.locale is None)
- self.failIfEqual("", diagnostics.locale)
+ diagnostics.locale # might not be set, so just make sure method doesn't fail
def testMethods_006(self):
"""
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-20 19:28:55
|
Revision: 893
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=893&view=rev
Author: pronovic
Date: 2008-03-20 12:28:51 -0700 (Thu, 20 Mar 2008)
Log Message:
-----------
Add timestamp to diagnostics
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/util.py
cedar-backup2/trunk/test/utiltests.py
Modified: cedar-backup2/trunk/CedarBackup2/util.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/util.py 2008-03-20 18:18:58 UTC (rev 892)
+++ cedar-backup2/trunk/CedarBackup2/util.py 2008-03-20 19:28:51 UTC (rev 893)
@@ -85,6 +85,7 @@
import logging
import string
import locale
+import datetime
from CedarBackup2.release import VERSION, DATE
@@ -987,6 +988,7 @@
values['platform'] = self.platform
values['encoding'] = self.encoding
values['locale'] = self.locale
+ values['timestamp'] = self.timestamp
return values;
def printDiagnostics(self, fd=sys.stdout, prefix=""):
@@ -1085,12 +1087,19 @@
Property target to get the default locale that is in effect.
"""
return locale.getdefaultlocale()[0]
+
+ def _getTimestamp(self):
+ """
+ Property target to get a current date/time stamp.
+ """
+ return datetime.datetime.utcnow().ctime() + " UTC"
version = property(_getVersion, None, None, "Cedar Backup version.")
interpreter = property(_getInterpreter, None, None, "Python interpreter version.")
platform = property(_getPlatform, None, None, "Platform identifying information.")
encoding = property(_getEncoding, None, None, "Filesystem encoding that is in effect.")
locale = property(_getLocale, None, None, "Locale that is in effect.")
+ timestamp = property(_getTimestamp, None, None, "Current timestamp.")
########################################################################
Modified: cedar-backup2/trunk/test/utiltests.py
===================================================================
--- cedar-backup2/trunk/test/utiltests.py 2008-03-20 18:18:58 UTC (rev 892)
+++ cedar-backup2/trunk/test/utiltests.py 2008-03-20 19:28:51 UTC (rev 893)
@@ -2069,6 +2069,7 @@
self.failUnlessEqual(diagnostics.platform, values['platform'])
self.failUnlessEqual(diagnostics.encoding, values['encoding'])
self.failUnlessEqual(diagnostics.locale, values['locale'])
+ self.failUnlessEqual(diagnostics.timestamp, values['timestamp'])
def testMethods_007(self):
"""
@@ -2091,7 +2092,15 @@
logger = logging.getLogger("CedarBackup2.test")
Diagnostics().logDiagnostics(logger.info)
+ def testMethods_010(self):
+ """
+ Test the timestamp attribute.
+ """
+ diagnostics = Diagnostics()
+ self.failIf(diagnostics.timestamp is None)
+ self.failIfEqual("", diagnostics.timestamp)
+
######################
# TestFunctions class
######################
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-20 20:08:11
|
Revision: 894
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=894&view=rev
Author: pronovic
Date: 2008-03-20 13:08:05 -0700 (Thu, 20 Mar 2008)
Log Message:
-----------
Add --diagnostics command-line option
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/cli.py
cedar-backup2/trunk/doc/cback.1
cedar-backup2/trunk/manual/src/commandline.xml
cedar-backup2/trunk/test/clitests.py
Modified: cedar-backup2/trunk/CedarBackup2/cli.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/cli.py 2008-03-20 19:28:51 UTC (rev 893)
+++ cedar-backup2/trunk/CedarBackup2/cli.py 2008-03-20 20:08:05 UTC (rev 894)
@@ -130,11 +130,11 @@
COMBINE_ACTIONS = [ "collect", "stage", "store", "purge", ]
NONCOMBINE_ACTIONS = [ "rebuild", "validate", "initialize", "all", ]
-SHORT_SWITCHES = "hVbqc:fMNl:o:m:Ods"
+SHORT_SWITCHES = "hVbqc:fMNl:o:m:OdsD"
LONG_SWITCHES = [ 'help', 'version', 'verbose', 'quiet',
'config=', 'full', 'managed', 'managed-only',
'logfile=', 'owner=', 'mode=',
- 'output', 'debug', 'stack', ]
+ 'output', 'debug', 'stack', 'diagnostics', ]
#######################################################################
@@ -208,6 +208,9 @@
if options.version:
_version()
return 0
+ if options.diagnostics:
+ _diagnostics()
+ return 0
try:
logfile = setupLogging(options)
@@ -922,6 +925,7 @@
fd.write(" -O, --output Record some sub-command (i.e. cdrecord) output to the log\n")
fd.write(" -d, --debug Write debugging information to the log (implies --output)\n")
fd.write(" -s, --stack Dump a Python stack trace instead of swallowing exceptions\n") # exactly 80 characters in width!
+ fd.write(" -D, --diagnostics Print runtime diagnostics to the screen and exit\n")
fd.write("\n")
fd.write(" The following actions may be specified:\n")
fd.write("\n")
@@ -969,6 +973,23 @@
##########################
+# _diagnostics() function
+##########################
+
+def _diagnostics(fd=sys.stdout):
+ """
+ Prints runtime diagnostics information.
+ @param fd: File descriptor used to print information.
+ @note: The C{fd} is used rather than C{print} to facilitate unit testing.
+ """
+ fd.write("\n")
+ fd.write("Diagnostics:\n")
+ fd.write("\n")
+ Diagnostics().printDiagnostics(fd=fd, prefix=" ")
+ fd.write("\n")
+
+
+##########################
# setupLogging() function
##########################
@@ -1264,6 +1285,7 @@
self._output = False
self._debug = False
self._stacktrace = False
+ self._diagnostics = False
self._actions = None
self.actions = [] # initialize to an empty list; remainder are OK
if argumentList is not None and argumentString is not None:
@@ -1376,6 +1398,11 @@
return -1
else:
return 1
+ if self._diagnostics != other._diagnostics:
+ if self._diagnostics < other._diagnostics:
+ return -1
+ else:
+ return 1
if self._actions != other._actions:
if self._actions < other._actions:
return -1
@@ -1629,6 +1656,22 @@
"""
return self._stacktrace
+ def _setDiagnostics(self, value):
+ """
+ Property target used to set the diagnostics flag.
+ No validations, but we normalize the value to C{True} or C{False}.
+ """
+ if value:
+ self._diagnostics = True
+ else:
+ self._diagnostics = False
+
+ def _getDiagnostics(self):
+ """
+ Property target used to get the diagnostics flag.
+ """
+ return self._diagnostics
+
def _setActions(self, value):
"""
Property target used to set the actions list.
@@ -1666,6 +1709,7 @@
output = property(_getOutput, _setOutput, None, "Command-line output (C{-O,--output}) flag.")
debug = property(_getDebug, _setDebug, None, "Command-line debug (C{-d,--debug}) flag.")
stacktrace = property(_getStacktrace, _setStacktrace, None, "Command-line stacktrace (C{-s,--stack}) flag.")
+ diagnostics = property(_getDiagnostics, _setDiagnostics, None, "Command-line diagnostics (C{-D,--diagnostics}) flag.")
actions = property(_getActions, _setActions, None, "Command-line actions list.")
@@ -1687,7 +1731,7 @@
@raise ValueError: If one of the validations fails.
"""
- if not self.help and not self.version:
+ if not self.help and not self.version and not self.diagnostics:
if self.actions is None or len(self.actions) == 0:
raise ValueError("At least one action must be specified.")
if self.managed and self.managedOnly:
@@ -1755,6 +1799,8 @@
argumentList.append("--debug")
if self.stacktrace:
argumentList.append("--stack")
+ if self.diagnostics:
+ argumentList.append("--diagnostics")
if self.actions is not None:
for action in self.actions:
argumentList.append(action)
@@ -1818,6 +1864,8 @@
argumentString += "--debug "
if self.stacktrace:
argumentString += "--stack "
+ if self.diagnostics:
+ argumentString += "--diagnostics "
if self.actions is not None:
for action in self.actions:
argumentString += "\"%s\" " % action
@@ -1884,6 +1932,8 @@
self.debug = True
if switches.has_key("-s") or switches.has_key("--stack"):
self.stacktrace = True
+ if switches.has_key("-D") or switches.has_key("--diagnostics"):
+ self.diagnostics = True
#########################################################################
Modified: cedar-backup2/trunk/doc/cback.1
===================================================================
--- cedar-backup2/trunk/doc/cback.1 2008-03-20 19:28:51 UTC (rev 893)
+++ cedar-backup2/trunk/doc/cback.1 2008-03-20 20:08:05 UTC (rev 894)
@@ -15,7 +15,7 @@
.\" #
.\" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
.\"
-.TH cback "1" "December 2007" "Cedar Backup" "Kenneth J. Pronovici"
+.TH cback "1" "March 2008" "Cedar Backup" "Kenneth J. Pronovici"
.SH NAME
cback \- Local and remote backups to CD-R/CD-RW media
.SH SYNOPSIS
@@ -124,6 +124,10 @@
than just progating last message it received back up to the user interface.
Under some circumstances, this is useful information to include along with a
bug report.
+.TP
+\fB\-s\fR, \fB\-\-diagnostics\fR
+Display runtime diagnostic information and then exit. This diagnostic
+information is often useful when filing a bug report.
.SH ACTIONS
.TP
\fBall\fR
@@ -215,16 +219,16 @@
.PP
There probably are bugs in this code. However, it is in active use for my own
backups, and I fix problems as I notice them. If you find a bug, please report
-it. If possible, give me all of the error messages that the script printed
-into its log, and also any stack-traces (exceptions) that Python printed. It
-would be even better if you could tell me how to reproduce the problem (i.e. by
-sending me your configuration file).
+it. If possible, give me the output from --diagnostics, all of the error
+messages that the script printed into its log, and also any stack-traces
+(exceptions) that Python printed. It would be even better if you could tell me
+how to reproduce the problem (i.e. by sending me your configuration file).
.PP
Report bugs to <su...@ce...>.
.SH AUTHOR
Written by Kenneth J. Pronovici <pro...@ie...>.
.SH COPYRIGHT
-Copyright (c) 2004-2007 Kenneth J. Pronovici.
+Copyright (c) 2004-2008 Kenneth J. Pronovici.
.br
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
Modified: cedar-backup2/trunk/manual/src/commandline.xml
===================================================================
--- cedar-backup2/trunk/manual/src/commandline.xml 2008-03-20 19:28:51 UTC (rev 893)
+++ cedar-backup2/trunk/manual/src/commandline.xml 2008-03-20 20:08:05 UTC (rev 894)
@@ -109,6 +109,7 @@
-O, --output Record some sub-command (i.e. cdrecord) output to the log
-d, --debug Write debugging information to the log (implies --output)
-s, --stack Dump a Python stack trace instead of swallowing exceptions
+ -D, --diagnostics Print runtime diagnostics to the screen and exit
The following actions may be specified:
@@ -314,6 +315,17 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-D</option>, <option>--diagnostics</option></term>
+ <listitem>
+ <para>
+ Display runtime diagnostic information and then exit.
+ This diagnostic information is often useful when filing a
+ bug report.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
Modified: cedar-backup2/trunk/test/clitests.py
===================================================================
--- cedar-backup2/trunk/test/clitests.py 2008-03-20 19:28:51 UTC (rev 893)
+++ cedar-backup2/trunk/test/clitests.py 2008-03-20 20:08:05 UTC (rev 894)
@@ -84,7 +84,7 @@
from CedarBackup2.config import OptionsConfig, PeersConfig, ExtensionsConfig
from CedarBackup2.config import LocalPeer, RemotePeer
from CedarBackup2.config import ExtendedAction, ActionDependencies, PreActionHook, PostActionHook
-from CedarBackup2.cli import _usage, _version
+from CedarBackup2.cli import _usage, _version, _diagnostics
from CedarBackup2.cli import Options
from CedarBackup2.cli import _ActionSet
from CedarBackup2.action import executeCollect, executeStage, executeStore, executePurge, executeRebuild, executeValidate
@@ -131,7 +131,14 @@
"""
captureOutput(_version)
+ def testSimpleFuncs_003(self):
+ """
+ Test that the _diagnostics() function runs without errors.
+ We don't care what the output is, and we don't check.
+ """
+ captureOutput(_diagnostics)
+
####################
# TestOptions class
####################
@@ -196,6 +203,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_002(self):
@@ -217,6 +225,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_003(self):
@@ -238,6 +247,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_004(self):
@@ -259,6 +269,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_005(self):
@@ -280,6 +291,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_006(self):
@@ -301,6 +313,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_007(self):
@@ -322,6 +335,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_008(self):
@@ -343,6 +357,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_009(self):
@@ -364,6 +379,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_010(self):
@@ -385,6 +401,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_011(self):
@@ -406,6 +423,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_012(self):
@@ -427,6 +445,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_013(self):
@@ -448,6 +467,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_014(self):
@@ -469,6 +489,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_015(self):
@@ -490,6 +511,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_016(self):
@@ -511,6 +533,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_017(self):
@@ -532,6 +555,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_018(self):
@@ -553,6 +577,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_019(self):
@@ -574,6 +599,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_020(self):
@@ -595,6 +621,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_021(self):
@@ -640,6 +667,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_026(self):
@@ -661,6 +689,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_027(self):
@@ -682,6 +711,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_028(self):
@@ -703,6 +733,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_029(self):
@@ -724,7 +755,9 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_030(self):
@@ -746,6 +779,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_031(self):
@@ -767,6 +801,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_032(self):
@@ -788,6 +823,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_033(self):
@@ -833,6 +869,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_038(self):
@@ -854,6 +891,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_039(self):
@@ -875,6 +913,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_040(self):
@@ -896,6 +935,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_041(self):
@@ -965,6 +1005,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_050(self):
@@ -986,6 +1027,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_051(self):
@@ -1007,6 +1049,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_052(self):
@@ -1028,6 +1071,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_053(self):
@@ -1097,6 +1141,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_062(self):
@@ -1118,6 +1163,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_063(self):
@@ -1139,6 +1185,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_064(self):
@@ -1160,6 +1207,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_065(self):
@@ -1181,6 +1229,7 @@
self.failUnlessEqual(True, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_066(self):
@@ -1202,6 +1251,7 @@
self.failUnlessEqual(True, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_067(self):
@@ -1223,6 +1273,7 @@
self.failUnlessEqual(True, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_068(self):
@@ -1244,6 +1295,7 @@
self.failUnlessEqual(True, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_069(self):
@@ -1265,6 +1317,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(True, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_070(self):
@@ -1286,6 +1339,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(True, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_071(self):
@@ -1307,6 +1361,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(True, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_072(self):
@@ -1328,6 +1383,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(True, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_073(self):
@@ -1349,6 +1405,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(True, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_074(self):
@@ -1370,6 +1427,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(True, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_075(self):
@@ -1412,6 +1470,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(True, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual([], options.actions)
def testConstructor_077(self):
@@ -1433,6 +1492,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual(["all", ], options.actions)
def testConstructor_078(self):
@@ -1454,6 +1514,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual(["all", ], options.actions)
def testConstructor_079(self):
@@ -1475,6 +1536,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual(["collect", ], options.actions)
def testConstructor_080(self):
@@ -1496,6 +1558,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual(["collect", ], options.actions)
def testConstructor_081(self):
@@ -1517,6 +1580,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual(["stage", ], options.actions)
def testConstructor_082(self):
@@ -1538,6 +1602,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual(["stage", ], options.actions)
def testConstructor_083(self):
@@ -1559,6 +1624,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual(["store", ], options.actions)
def testConstructor_084(self):
@@ -1580,6 +1646,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual(["store", ], options.actions)
def testConstructor_085(self):
@@ -1601,6 +1668,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual(["purge", ], options.actions)
def testConstructor_086(self):
@@ -1622,6 +1690,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual(["purge", ], options.actions)
def testConstructor_087(self):
@@ -1643,6 +1712,7 @@
self.failUnlessEqual(False, options.output)
self.failUnlessEqual(False, options.debug)
self.failUnlessEqual(False, options.stacktrace)
+ self.failUnlessEqual(False, options.diagnostics)
self.failUnlessEqual(["rebuild", ], options.actions)
def testConstructor_088(self):
@@ -1664,6 +1734,7 @@
self.fa...
[truncated message content] |
|
From: <pro...@us...> - 2008-03-20 20:31:52
|
Revision: 896
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=896&view=rev
Author: pronovic
Date: 2008-03-20 13:31:49 -0700 (Thu, 20 Mar 2008)
Log Message:
-----------
Release 2.17.0
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/release.py
cedar-backup2/trunk/Changelog
Modified: cedar-backup2/trunk/CedarBackup2/release.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/release.py 2008-03-20 20:25:20 UTC (rev 895)
+++ cedar-backup2/trunk/CedarBackup2/release.py 2008-03-20 20:31:49 UTC (rev 896)
@@ -34,7 +34,7 @@
AUTHOR = "Kenneth J. Pronovici"
EMAIL = "pro...@ie..."
COPYRIGHT = "2004-2008"
-VERSION = "2.16.1pre1"
-DATE = "19 Mar 2008"
+VERSION = "2.17.0"
+DATE = "20 Mar 2008"
URL = "http://cedar-solutions.com/software/cedar-backup"
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-20 20:25:20 UTC (rev 895)
+++ cedar-backup2/trunk/Changelog 2008-03-20 20:31:49 UTC (rev 896)
@@ -1,10 +1,11 @@
-Version 2.16.1 unreleased
+Version 2.17.0 20 Mar 2008
* Change suggested execution index for Capacity extension in manual.
* Provide support for application-wide diagnostic reporting.
- Add util.Diagnostics class to encapsulate information
- Log diagnostics when Cedar Backup first starts
- Print diagnostics when running unit tests
+ - Add a new --diagnostics command-line option
* Clean up filesystem code that deals with file age, and improve unit tests.
- Some platforms apparently cannot set file ages precisely
- Change calculateFileAge() to use floats throughout, which is safer
@@ -17,7 +18,7 @@
Version 2.16.0 18 Mar 2008
- * Make name attribute optional in RemotPeer constructor.
+ * Make name attribute optional in RemotePeer constructor.
* Add support for collecting soft links (closes: #1854631).
- Add linkDepth parameter to FilesystemList.addDirContents()
- Add CollectDir.linkDepth attribute
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-20 21:16:57
|
Revision: 898
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=898&view=rev
Author: pronovic
Date: 2008-03-20 14:16:42 -0700 (Thu, 20 Mar 2008)
Log Message:
-----------
Tweak copyright statement
Modified Paths:
--------------
cedar-backup2/trunk/CREDITS
cedar-backup2/trunk/Changelog
Modified: cedar-backup2/trunk/CREDITS
===================================================================
--- cedar-backup2/trunk/CREDITS 2008-03-20 20:32:07 UTC (rev 897)
+++ cedar-backup2/trunk/CREDITS 2008-03-20 21:16:42 UTC (rev 898)
@@ -22,7 +22,7 @@
Pronovici. Some portions have been based on other pieces of open-source
software, as indicated in the source code itself.
-Unless otherwise indicated, all Cedar Backup source code is copyright
+Unless otherwise indicated, all Cedar Backup source code is Copyright
(c) 2004-2008 Kenneth J. Pronovici and is released under the GNU General
Public License. The contents of the GNU General Public License can be
found in the LICENSE file, or can be downloaded from http://www.gnu.org/.
@@ -40,7 +40,7 @@
Source code annotated as "(c) 2001, 2002 Python Software Foundation" was
originally taken from or derived from code within the Python 2.3 codebase.
-This code was released under the Python 2.3 license, which an MIT-style
+This code was released under the Python 2.3 license, which is an MIT-style
academic license. Items under this license include the util.Pipe
implementation based on popen2.Popen4.
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-20 20:32:07 UTC (rev 897)
+++ cedar-backup2/trunk/Changelog 2008-03-20 21:16:42 UTC (rev 898)
@@ -1,3 +1,7 @@
+Version 2.17.1 unreleased
+
+ * Tweak copyright to fix typos.
+
Version 2.17.0 20 Mar 2008
* Change suggested execution index for Capacity extension in manual.
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-21 02:54:26
|
Revision: 901
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=901&view=rev
Author: pronovic
Date: 2008-03-20 19:54:19 -0700 (Thu, 20 Mar 2008)
Log Message:
-----------
Update manual copyright
Modified Paths:
--------------
cedar-backup2/trunk/Changelog
cedar-backup2/trunk/manual/src/book.xml
cedar-backup2/trunk/manual/src/copyright.xml
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-21 02:41:10 UTC (rev 900)
+++ cedar-backup2/trunk/Changelog 2008-03-21 02:54:19 UTC (rev 901)
@@ -1,6 +1,6 @@
Version 2.17.1 unreleased
- * Tweak copyright to fix typos.
+ * Tweak various pieces of copyright information.
Version 2.17.0 20 Mar 2008
Modified: cedar-backup2/trunk/manual/src/book.xml
===================================================================
--- cedar-backup2/trunk/manual/src/book.xml 2008-03-21 02:41:10 UTC (rev 900)
+++ cedar-backup2/trunk/manual/src/book.xml 2008-03-21 02:54:19 UTC (rev 901)
@@ -8,7 +8,7 @@
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
-# Copyright (c) 2005-2006 Kenneth J. Pronovici.
+# Copyright (c) 2005-2008 Kenneth J. Pronovici.
# All rights reserved.
#
# This work is free; you can redistribute it and/or modify it
@@ -69,7 +69,7 @@
</editor>
<copyright>
- <year>2005-2007</year>
+ <year>2005-2008</year>
<holder>Kenneth J. Pronovici</holder>
</copyright>
Modified: cedar-backup2/trunk/manual/src/copyright.xml
===================================================================
--- cedar-backup2/trunk/manual/src/copyright.xml 2008-03-21 02:41:10 UTC (rev 900)
+++ cedar-backup2/trunk/manual/src/copyright.xml 2008-03-21 02:54:19 UTC (rev 901)
@@ -7,7 +7,7 @@
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
-# Copyright (c) 2005-2006 Kenneth J. Pronovici.
+# Copyright (c) 2005-2008 Kenneth J. Pronovici.
# All rights reserved.
#
# This work is free; you can redistribute it and/or modify it
@@ -40,7 +40,7 @@
<programlisting>
-Copyright (c) 2005-2006
+Copyright (c) 2005-2008
Kenneth J. Pronovici
This work is free; you can redistribute it and/or modify it under
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-03-21 03:04:40
|
Revision: 903
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=903&view=rev
Author: pronovic
Date: 2008-03-20 20:04:24 -0700 (Thu, 20 Mar 2008)
Log Message:
-----------
Fix URLs to point to SF
Modified Paths:
--------------
cedar-backup2/trunk/Changelog
cedar-backup2/trunk/manual/src/config.xml
cedar-backup2/trunk/manual/src/install.xml
cedar-backup2/trunk/manual/src/intro.xml
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-21 03:01:48 UTC (rev 902)
+++ cedar-backup2/trunk/Changelog 2008-03-21 03:04:24 UTC (rev 903)
@@ -1,6 +1,9 @@
Version 2.17.1 unreleased
- * Tweak various pieces of copyright information.
+ * Updated copyright statement slightly.
+ * Updated user manual
+ - Brought copyright notices up-to-date
+ - Fixed various URLs that didn't reference SourceForge
Version 2.17.0 20 Mar 2008
Modified: cedar-backup2/trunk/manual/src/config.xml
===================================================================
--- cedar-backup2/trunk/manual/src/config.xml 2008-03-21 03:01:48 UTC (rev 902)
+++ cedar-backup2/trunk/manual/src/config.xml 2008-03-21 03:04:24 UTC (rev 903)
@@ -3064,8 +3064,9 @@
<emphasis>If Cedar Backup ever completes <quote>normally</quote>
but the disc that is created is not usable, please report this as a
bug.
- <footnote id="cedar-config-foot-bugzilla"><para>
- See <ulink url="http://cedar-solutions.com/bugzilla/"/>.</para></footnote>
+ <footnote id="cedar-config-foot-bugs"><para>
+ See <quote>SF Bug Tracking</quote> at
+ <ulink url="http://cedar-backup.sourceforge.net/"/>.</para></footnote>
To be safe, always enable the consistency check option in the
store configuration section.</emphasis>
</para>
@@ -3957,7 +3958,7 @@
<emphasis>If Cedar Backup ever completes <quote>normally</quote>
but the disc that is created is not usable, please report this as a
bug.
- <footnoteref linkend="cedar-config-foot-bugzilla"/>
+ <footnoteref linkend="cedar-config-foot-bugs"/>
To be safe, always enable the consistency check option in the
store configuration section.</emphasis>
</para>
Modified: cedar-backup2/trunk/manual/src/install.xml
===================================================================
--- cedar-backup2/trunk/manual/src/install.xml 2008-03-21 03:01:48 UTC (rev 902)
+++ cedar-backup2/trunk/manual/src/install.xml 2008-03-21 03:04:24 UTC (rev 903)
@@ -7,7 +7,7 @@
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
-# Copyright (c) 2005-2007 Kenneth J. Pronovici.
+# Copyright (c) 2005-2008 Kenneth J. Pronovici.
# All rights reserved.
#
# This work is free; you can redistribute it and/or modify it
@@ -81,8 +81,8 @@
If you would like to use Cedar Backup on a non-Linux system, you
should install the Python source distribution along with all of the
indicated dependencies. Then, please report back to the Cedar
- Backup Users mailing list <footnote><para>See <ulink
- url="http://cedar-solutions.com/listarchives/"/>.</para></footnote>
+ Backup Users mailing list <footnote><para>See <quote>SF Mailing Lists</quote>
+ at <ulink url="http://cedar-backup.sourceforge.net/"/>.</para></footnote>
with information about your platform and any problems you
encountered.
</para>
@@ -110,7 +110,8 @@
Backup.) Otherwise, you need to install from the Cedar Solutions APT
data source. To do this, add the Cedar Solutions APT data source to
your <filename>/etc/apt/sources.list</filename> file. <footnote><para>See
- <ulink url="http://cedar-solutions.com/debian.html"/>.</para></footnote>
+ <quote>SF Bug Tracking</quote> at
+ <ulink url="http://cedar-backup.sourceforge.net/"/>.</para></footnote>
</para>
<para>
@@ -137,10 +138,10 @@
<para>
If you would prefer, you can also download the
<filename>.deb</filename> files and install them by hand with a tool
- such as <command>dpkg</command>. You can find a link to the
- <filename>.deb</filename> files on the Cedar Solutions website.
+ such as <command>dpkg</command>. You can find
+ these files files in the Cedar Solutions APT source.
<footnote id="cedar-install-foot-software"><para>See <ulink
- url="http://cedar-solutions.com/software.html"/>.</para></footnote>
+ url="http://cedar-solutions.com/debian.html"/>.</para></footnote>
</para>
<para>
Modified: cedar-backup2/trunk/manual/src/intro.xml
===================================================================
--- cedar-backup2/trunk/manual/src/intro.xml 2008-03-21 03:01:48 UTC (rev 902)
+++ cedar-backup2/trunk/manual/src/intro.xml 2008-03-21 03:04:24 UTC (rev 903)
@@ -131,8 +131,9 @@
<para>
If you experience a problem, your best bet is to write the Cedar
- Backup Users mailing list. <footnote><para>See <ulink
- url="http://cedar-solutions.com/listarchives/"/>.</para></footnote>
+ Backup Users mailing list. <footnote><para>See
+ <quote>SF Mailing Lists</quote> at
+ <ulink url="http://cedar-backup.sourceforge.net/"/>.</para></footnote>
This is a public list for all Cedar Backup users. If you write to
this list, you might get help from me, or from some other user who has
experienced the same thing you have.
@@ -142,8 +143,8 @@
If you know that the problem you have found constitutes a bug, or
if you would like to make an enhancement request, then feel free to
file a bug report in the Cedar Solutions Bug Tracking System.
- <footnote><para>See <ulink
- url="http://cedar-solutions.com/bugzilla/"/>.</para></footnote>
+ <footnote><para>See <quote>SF Bug Tracking</quote>
+ at <ulink url="http://cedar-backup.sourceforge.net/"/>.</para></footnote>
</para>
<para>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-04-01 01:01:26
|
Revision: 904
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=904&view=rev
Author: pronovic
Date: 2008-03-31 18:01:18 -0700 (Mon, 31 Mar 2008)
Log Message:
-----------
Fixed problem with link_depth (closes: #1930729)
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/filesystem.py
cedar-backup2/trunk/Changelog
cedar-backup2/trunk/test/filesystemtests.py
Modified: cedar-backup2/trunk/CedarBackup2/filesystem.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/filesystem.py 2008-03-21 03:04:24 UTC (rev 903)
+++ cedar-backup2/trunk/CedarBackup2/filesystem.py 2008-04-01 01:01:18 UTC (rev 904)
@@ -424,7 +424,8 @@
The linkDepth parameter controls whether soft links are followed when we
are adding the contents recursively. Any recursive calls reduce the
value by one. If the value zero or less, then soft links will just be
- added as directories, but will not be followed.
+ added as directories, but will not be followed. This means that links
+ are followed to a I{constant depth} starting from the top-most directory.
@param path: Directory path whose contents should be added to the list.
@param includePath: Indicates whether to include the path as well as contents.
@@ -463,7 +464,7 @@
if os.path.islink(entrypath):
if recursive and linkDepth > 0:
newDepth = linkDepth - 1;
- added += self._addDirContentsInternal(entrypath, linkDepth=newDepth)
+ added += self._addDirContentsInternal(entrypath, includePath=False, linkDepth=newDepth)
else:
added += self.addDir(entrypath)
else:
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-03-21 03:04:24 UTC (rev 903)
+++ cedar-backup2/trunk/Changelog 2008-04-01 01:01:18 UTC (rev 904)
@@ -4,6 +4,9 @@
* Updated user manual
- Brought copyright notices up-to-date
- Fixed various URLs that didn't reference SourceForge
+ * Fixed problem with link_depth (closes: #1930729).
+ - Can't add links directly, they're implicitly added later by tar
+ - Changed FilesystemList to use includePath=false for recursive links
Version 2.17.0 20 Mar 2008
Modified: cedar-backup2/trunk/test/filesystemtests.py
===================================================================
--- cedar-backup2/trunk/test/filesystemtests.py 2008-03-21 03:04:24 UTC (rev 903)
+++ cedar-backup2/trunk/test/filesystemtests.py 2008-04-01 01:01:18 UTC (rev 904)
@@ -4884,8 +4884,8 @@
self.failUnless(self.buildPath([ "tree6", "file002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link001", ]) in fsList)
else:
- self.failUnlessEqual(165, count)
- self.failUnlessEqual(165, len(fsList))
+ self.failUnlessEqual(164, count)
+ self.failUnlessEqual(164, len(fsList))
self.failUnless(self.buildPath([ "tree6", "dir001", "dir001", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir001", "dir001", "dir002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir001", "dir001", "dir003", ]) in fsList)
@@ -5017,7 +5017,6 @@
self.failUnless(self.buildPath([ "tree6", "dir003", "link003", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "link004", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "link005", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "link002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir001", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir001", "dir002", ]) in fsList)
@@ -5185,8 +5184,8 @@
self.failUnless(self.buildPath([ "tree6", "file002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link001", ]) in fsList)
else:
- self.failUnlessEqual(247, count)
- self.failUnlessEqual(247, len(fsList))
+ self.failUnlessEqual(240, count)
+ self.failUnlessEqual(240, len(fsList))
self.failUnless(self.buildPath([ "tree6", "dir001", "dir001", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir001", "dir001", "dir002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir001", "dir001", "dir003", ]) in fsList)
@@ -5214,7 +5213,9 @@
self.failUnless(self.buildPath([ "tree6", "dir001", "file002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir001", "file003", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir001", "file004", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir001", "link001", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir001", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir001", "link002", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir001", "link003", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir001", "link001", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir001", "link001", "dir002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir001", "link001", "dir003", ]) in fsList)
@@ -5229,9 +5230,6 @@
self.failUnless(self.buildPath([ "tree6", "dir001", "link001", "link001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir001", "link001", "link002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir001", "link001", "link003", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir001", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir001", "link002", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir001", "link003", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "dir001", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "dir001", "dir002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "dir001", "dir003", ]) in fsList)
@@ -5281,6 +5279,13 @@
self.failUnless(self.buildPath([ "tree6", "dir002", "dir003", "link002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "dir003", "link003", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "dir003", "link004", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir002", "file001", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir002", "file002", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir002", "file003", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir002", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir002", "link001", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir002", "link003", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir002", "link004", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "link002", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "link002", "dir002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "link002", "dir003", ]) in fsList)
@@ -5293,6 +5298,11 @@
self.failUnless(self.buildPath([ "tree6", "dir002", "link002", "file007", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "link002", "file008", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "link002", "file009", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir002", "link002", "link001", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir002", "link002", "link002", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir002", "link002", "link003", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir002", "link002", "link004", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir002", "link002", "link005", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "link005", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "link005", "dir002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "link005", "file001", ]) in fsList)
@@ -5302,24 +5312,10 @@
self.failUnless(self.buildPath([ "tree6", "dir002", "link005", "file005", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "link005", "file006", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "link005", "file007", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir002", "link005", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "link005", "link001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "link005", "link002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "link005", "link003", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir002", "link005", "link004", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir002", "dir001", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir002", "dir001", "link001", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir002", "dir001", "link002", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir002", "dir001", "link003", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir002", "dir001", "link004", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir002", "dir001", "link005", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir002", "file001", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir002", "file002", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir002", "file003", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir002", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir002", "link001", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir002", "link003", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir002", "link004", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "dir001", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "dir001", "dir002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "dir001", "file001", ]) in fsList)
@@ -5346,6 +5342,20 @@
self.failUnless(self.buildPath([ "tree6", "dir003", "dir002", "link002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "dir002", "link003", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "dir002", "link004", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir003", "file001", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir003", "file002", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir003", "file003", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir003", "file004", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir003", "file005", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir003", "file006", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir003", "file007", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir003", "file008", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir003", "file009", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir003", "ignore", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir003", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir003", "link002", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir003", "link003", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "dir003", "link005", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "link001", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "link001", "dir002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "link001", "file001", ]) in fsList)
@@ -5353,7 +5363,6 @@
self.failUnless(self.buildPath([ "tree6", "dir003", "link001", "file003", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "link001", "file004", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "link001", "file005", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "link001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "link001", "link001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "link001", "link002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "link001", "link003", ]) in fsList)
@@ -5369,25 +5378,8 @@
self.failUnless(self.buildPath([ "tree6", "dir003", "link004", "file007", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "link004", "file008", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "link004", "file009", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "link004", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "link004", "link001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "dir003", "link004", "link002", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "file001", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "file002", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "file003", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "file004", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "file005", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "file006", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "file007", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "file008", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "file009", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "ignore", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "link002", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "link003", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "dir003", "link005", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "link002", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "link002", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir001", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir001", "dir002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir001", "dir003", ]) in fsList)
@@ -5399,18 +5391,24 @@
self.failUnless(self.buildPath([ "tree6", "link002", "dir001", "file006", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir001", "file007", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir001", "ignore", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "link002", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir001", "link001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir001", "link002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir001", "link003", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "link002", "dir002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir002", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir002", "dir002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir002", "file001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir002", "file002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir002", "file003", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "link002", "dir002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir002", "link001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "dir002", "link002", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "link002", "link001", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "link002", "file001", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "link002", "file002", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "link002", "file003", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "link002", "file004", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "link002", "link002", ]) in fsList)
+ self.failUnless(self.buildPath([ "tree6", "link002", "link003", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "link001", "dir001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "link001", "dir002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "link001", "dir003", ]) in fsList)
@@ -5425,12 +5423,6 @@
self.failUnless(self.buildPath([ "tree6", "link002", "link001", "link001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "link001", "link002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link002", "link001", "link003", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "link002", "file001", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "link002", "file002", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "link002", "file003", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "link002", "file004", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "link002", "link002", ]) in fsList)
- self.failUnless(self.buildPath([ "tree6", "link002", "link003", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "file001", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "file002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link001", ]) in fsList)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-04-13 18:03:11
|
Revision: 906
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=906&view=rev
Author: pronovic
Date: 2008-04-13 11:03:00 -0700 (Sun, 13 Apr 2008)
Log Message:
-----------
Added util.dereferenceLink() and associated tests
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/util.py
cedar-backup2/trunk/Changelog
cedar-backup2/trunk/test/utiltests.py
Modified: cedar-backup2/trunk/CedarBackup2/util.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/util.py 2008-04-01 02:12:55 UTC (rev 905)
+++ cedar-backup2/trunk/CedarBackup2/util.py 2008-04-13 18:03:00 UTC (rev 906)
@@ -1987,3 +1987,22 @@
os.environ[LANG_VAR] = DEFAULT_LANGUAGE
return os.environ.copy()
+
+#############################
+# dereferenceLink() function
+#############################
+
+def dereferenceLink(path, absolute=True):
+ """
+ Deference a soft link, optionally normalizing it to an absolute path.
+ @param path: Path of link to dereference
+ @param absolute: Whether to normalize the result to an absolute path
+ @return: Dereferenced path, or original path if original is not a link.
+ """
+ if os.path.islink(path):
+ result = os.readlink(path)
+ if absolute and not os.path.isabs(result):
+ result = os.path.abspath(os.path.join(os.path.dirname(path), result))
+ return result
+ return path
+
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-04-01 02:12:55 UTC (rev 905)
+++ cedar-backup2/trunk/Changelog 2008-04-13 18:03:00 UTC (rev 906)
@@ -7,6 +7,7 @@
* Fixed problem with link_depth (closes: #1930729).
- Can't add links directly, they're implicitly added later by tar
- Changed FilesystemList to use includePath=false for recursive links
+ * Added util.dereferenceLink()
Version 2.17.0 20 Mar 2008
Modified: cedar-backup2/trunk/test/utiltests.py
===================================================================
--- cedar-backup2/trunk/test/utiltests.py 2008-04-01 02:12:55 UTC (rev 905)
+++ cedar-backup2/trunk/test/utiltests.py 2008-04-13 18:03:00 UTC (rev 906)
@@ -82,13 +82,14 @@
import logging
from os.path import isdir
-from CedarBackup2.testutil import findResources, removedir, platformHasEcho, platformWindows, captureOutput
+from CedarBackup2.testutil import findResources, removedir, extractTar, buildPath, captureOutput
+from CedarBackup2.testutil import platformHasEcho, platformWindows, platformSupportsLinks
from CedarBackup2.util import UnorderedList, AbsolutePathList, ObjectTypeList
from CedarBackup2.util import RestrictedContentList, RegexMatchList, RegexList
from CedarBackup2.util import DirectedGraph, PathResolverSingleton, Diagnostics
from CedarBackup2.util import sortDict, resolveCommand, executeCommand, getFunctionReference, encodePath
from CedarBackup2.util import convertSize, UNIT_BYTES, UNIT_SECTORS, UNIT_KBYTES, UNIT_MBYTES, UNIT_GBYTES
-from CedarBackup2.util import displayBytes, deriveDayOfWeek, isStartOfWeek
+from CedarBackup2.util import displayBytes, deriveDayOfWeek, isStartOfWeek, dereferenceLink
from CedarBackup2.util import buildNormalizedPath, splitCommandLine, nullDevice
@@ -97,7 +98,7 @@
#######################################################################
DATA_DIRS = [ "./data", "./test/data" ]
-RESOURCES = [ "lotsoflines.py", ]
+RESOURCES = [ "lotsoflines.py", "tree10.tar.gz", ]
#######################################################################
@@ -2137,7 +2138,16 @@
except: pass
return name
+ def extractTar(self, tarname):
+ """Extracts a tarfile with a particular name."""
+ extractTar(self.tmpdir, self.resources['%s.tar.gz' % tarname])
+ def buildPath(self, components):
+ """Builds a complete search path from a list of components."""
+ components.insert(0, self.tmpdir)
+ return buildPath(components)
+
+
##################
# Test sortDict()
##################
@@ -3912,6 +3922,105 @@
self.failUnlessEqual(["cback", "'this", "is", "a", "really", "long", "single-quoted", "argument'", ], result)
+ #########################
+ # Test dereferenceLink()
+ #########################
+
+ def testDereferenceLink_001(self):
+ """
+ Test for a path that is a link, absolute=false.
+ """
+ self.extractTar("tree10")
+ path = self.buildPath(["tree10", "link002"])
+ if platformSupportsLinks():
+ expected = "file002"
+ else:
+ expected = path
+ actual = dereferenceLink(path, absolute=False)
+ self.failUnlessEqual(expected, actual)
+
+ def testDereferenceLink_002(self):
+ """
+ Test for a path that is a link, absolute=true.
+ """
+ self.extractTar("tree10")
+ path = self.buildPath(["tree10", "link002"])
+ if platformSupportsLinks():
+ expected = self.buildPath(["tree10", "file002"])
+ else:
+ expected = path
+ actual = dereferenceLink(path)
+ self.failUnlessEqual(expected, actual)
+ actual = dereferenceLink(path, absolute=True)
+ self.failUnlessEqual(expected, actual)
+
+ def testDereferenceLink_003(self):
+ """
+ Test for a path that is a file (not a link), absolute=false.
+ """
+ self.extractTar("tree10")
+ path = self.buildPath(["tree10", "file001"])
+ expected = path
+ actual = dereferenceLink(path, absolute=False)
+ self.failUnlessEqual(expected, actual)
+
+ def testDereferenceLink_004(self):
+ """
+ Test for a path that is a file (not a link), absolute=true.
+ """
+ self.extractTar("tree10")
+ path = self.buildPath(["tree10", "file001"])
+ expected = path
+ actual = dereferenceLink(path)
+ self.failUnlessEqual(expected, actual)
+ actual = dereferenceLink(path, absolute=True)
+ self.failUnlessEqual(expected, actual)
+
+ def testDereferenceLink_005(self):
+ """
+ Test for a path that is a directory (not a link), absolute=false.
+ """
+ self.extractTar("tree10")
+ path = self.buildPath(["tree10", "dir001"])
+ expected = path
+ actual = dereferenceLink(path, absolute=False)
+ self.failUnlessEqual(expected, actual)
+
+ def testDereferenceLink_006(self):
+ """
+ Test for a path that is a directory (not a link), absolute=true.
+ """
+ self.extractTar("tree10")
+ path = self.buildPath(["tree10", "dir001"])
+ expected = path
+ actual = dereferenceLink(path)
+ self.failUnlessEqual(expected, actual)
+ actual = dereferenceLink(path, absolute=True)
+ self.failUnlessEqual(expected, actual)
+
+ def testDereferenceLink_007(self):
+ """
+ Test for a path that does not exist, absolute=false.
+ """
+ self.extractTar("tree10")
+ path = self.buildPath(["tree10", "blech"])
+ expected = path
+ actual = dereferenceLink(path, absolute=False)
+ self.failUnlessEqual(expected, actual)
+
+ def testDereferenceLink_008(self):
+ """
+ Test for a path that does not exist, absolute=true.
+ """
+ self.extractTar("tree10")
+ path = self.buildPath(["tree10", "blech"])
+ expected = path
+ actual = dereferenceLink(path)
+ self.failUnlessEqual(expected, actual)
+ actual = dereferenceLink(path, absolute=True)
+ self.failUnlessEqual(expected, actual)
+
+
#######################################################################
# Suite definition
#######################################################################
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <pro...@us...> - 2008-04-13 21:44:22
|
Revision: 908
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=908&view=rev
Author: pronovic
Date: 2008-04-13 14:44:16 -0700 (Sun, 13 Apr 2008)
Log Message:
-----------
Implemented dereference option on addDirContents
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/filesystem.py
cedar-backup2/trunk/Changelog
cedar-backup2/trunk/test/filesystemtests.py
Modified: cedar-backup2/trunk/CedarBackup2/filesystem.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/filesystem.py 2008-04-13 20:16:45 UTC (rev 907)
+++ cedar-backup2/trunk/CedarBackup2/filesystem.py 2008-04-13 21:44:16 UTC (rev 908)
@@ -60,6 +60,7 @@
from CedarBackup2.knapsack import firstFit, bestFit, worstFit, alternateFit
from CedarBackup2.util import AbsolutePathList, ObjectTypeList, UnorderedList, RegexList
from CedarBackup2.util import removeKeys, displayBytes, calculateFileAge, encodePath
+from CedarBackup2.util import dereferenceLink
########################################################################
@@ -358,7 +359,7 @@
logger.debug("Added directory to list: [%s]" % path)
return 1
- def addDirContents(self, path, recursive=True, addSelf=True, linkDepth=0):
+ def addDirContents(self, path, recursive=True, addSelf=True, linkDepth=0, dereference=False):
"""
Adds the contents of a directory to the list.
@@ -368,22 +369,26 @@
place. If you only want the directory and its immediate contents to be
added, then pass in C{recursive=False}.
+ If the passed-in directory happens to be a soft link, it will be
+ recursed. However, by default, other directory links within the
+ passed-in directory are not recursed. The C{linkDepth} parameter
+ controls this behavior. This value is the maximum depth of the tree at
+ which directory links should be followed. So, a depth of 0 does not
+ follow any directory links, a depth of 1 follows only directory links
+ within the passed-in directory, a depth of 2 follows the directory links
+ at the next level down, etc.
+
+ The C{dereference} flag also controls link-related behavior. If the
+ C{linkDepth} is greater than zero and C{deference} is true, then any file
+ or directory link will be deferenced before being added to the list. So,
+ the list will contain the path of file or directory that the link points
+ at, rather than the path of the link itself.
+
@note: If a directory's absolute path matches an exclude pattern or path,
or if the directory contains the configured ignore file, then the
directory and all of its contents will be recursively excluded from the
list.
- @note: If the passed-in directory happens to be a soft link, it will be
- recursed. However, the linkDepth parameter controls whether any soft
- links I{within} the directory will be recursed. The link depth is
- maximum depth of the tree at which soft links should be followed. So, a
- depth of 0 does not follow any soft links, a depth of 1 follows only
- links within the passed-in directory, a depth of 2 follows the links at
- the next level down, etc.
-
- @note: Any invalid soft links (i.e. soft links that point to
- non-existent items) will be silently ignored.
-
@note: The L{excludeDirs} flag only controls whether any given directory
path itself is added to the list once it has been discovered. It does
I{not} modify any behavior related to directory recursion.
@@ -400,6 +405,9 @@
@param linkDepth: Maximum depth of the tree at which soft links should be followed
@type linkDepth: Integer value, where zero means not to follow any soft links
+ @param dereference: Whether soft links should be dereferenced
+ @type dereference: Boolean value
+
@return: Number of items recursively added to the list
@raise ValueError: If path is not a directory or does not exist.
@@ -407,9 +415,9 @@
"""
path = encodePath(path)
path = normalizeDir(path)
- return self._addDirContentsInternal(path, addSelf, recursive, linkDepth)
+ return self._addDirContentsInternal(path, addSelf, recursive, linkDepth, dereference)
- def _addDirContentsInternal(self, path, includePath=True, recursive=True, linkDepth=0):
+ def _addDirContentsInternal(self, path, includePath=True, recursive=True, linkDepth=0, dereference=False):
"""
Internal implementation of C{addDirContents}.
@@ -421,27 +429,34 @@
interface, C{addDirContents} ends up being wholly implemented in terms
of this method.
- The linkDepth parameter controls whether soft links are followed when we
- are adding the contents recursively. Any recursive calls reduce the
- value by one. If the value zero or less, then soft links will just be
- added as directories, but will not be followed. This means that links
- are followed to a I{constant depth} starting from the top-most directory.
+ The C{linkDepth} parameter controls whether directory links are followed
+ when we are adding the contents recursively. Any recursive calls reduce
+ the value by one. If the value zero or less, then directory links will
+ just be added as directories, but will not be followed. This means that
+ links are followed to a I{constant depth} starting from the top-most
+ directory.
- There is one difference between soft links and directories: soft links
- that are added recursively are not placed into the list explicitly. This
- is because if we do add the links recursively, the resulting tar file
- gets a little confused (it has a link and a directory with the same
- name).
+ There is one difference between directory links and directories:
+ directory links that are added recursively (and are not deferenced) are
+ not placed into the list explicitly. This is because if we do add the
+ links recursively, the resulting tar file gets a little confused: it has
+ a link and a directory with the same name, which some tar implementations
+ cannot successfully extract.
@param path: Directory path whose contents should be added to the list.
@param includePath: Indicates whether to include the path as well as contents.
@param recursive: Indicates whether directory contents should be added recursively.
@param linkDepth: Depth of soft links that should be followed
+ @param dereference: Whether soft links should be dereferenced when C{linkDepth > 0}
@return: Number of items recursively added to the list
@raise ValueError: If path is not a directory or does not exist.
"""
+ if linkDepth < 1:
+ dereference = False # never dereference if we're not following links
+ if dereference:
+ path = dereferenceLink(path, absolute=True)
added = 0
if not os.path.exists(path) or not os.path.isdir(path):
logger.debug("Path [%s] is not a directory or does not exist on disk." % path)
@@ -464,19 +479,21 @@
added += self.addDir(path) # could actually be excluded by addDir, yet
for entry in os.listdir(path):
entrypath = os.path.join(path, entry)
+ if dereference:
+ entrypath = dereferenceLink(entrypath)
if os.path.isfile(entrypath):
added += self.addFile(entrypath)
elif os.path.isdir(entrypath):
if os.path.islink(entrypath):
if recursive and linkDepth > 0:
newDepth = linkDepth - 1;
- added += self._addDirContentsInternal(entrypath, includePath=False, linkDepth=newDepth)
+ added += self._addDirContentsInternal(entrypath, includePath=False, linkDepth=newDepth, dereference=dereference)
else:
added += self.addDir(entrypath)
else:
if recursive:
newDepth = linkDepth - 1;
- added += self._addDirContentsInternal(entrypath, linkDepth=newDepth)
+ added += self._addDirContentsInternal(entrypath, linkDepth=newDepth, dereference=dereference)
else:
added += self.addDir(entrypath)
return added
@@ -1221,7 +1238,7 @@
# Add methods
##############
- def addDirContents(self, path, recursive=True, addSelf=True, linkDepth=0):
+ def addDirContents(self, path, recursive=True, addSelf=True, linkDepth=0, dereference=False):
"""
Adds the contents of a directory to the list.
@@ -1231,22 +1248,26 @@
place. If you only want the directory and its contents to be added, then
pass in C{recursive=False}.
+ If the passed-in directory happens to be a soft link, it will be
+ recursed. However, by default, other directory links within the
+ passed-in directory are not recursed. The C{linkDepth} parameter
+ controls this behavior. This value is the maximum depth of the tree at
+ which directory links should be followed. So, a depth of 0 does not
+ follow any directory links, a depth of 1 follows only directory links
+ within the passed-in directory, a depth of 2 follows the directory links
+ at the next level down, etc.
+
+ The C{dereference} flag also controls link-related behavior. If the
+ C{linkDepth} is greater than zero and C{deference} is true, then any file
+ or directory link will be deferenced before being added to the list. So,
+ the list will contain the path of file or directory that the link points
+ at, rather than the path of the link itself.
+
@note: If a directory's absolute path matches an exclude pattern or path,
or if the directory contains the configured ignore file, then the
directory and all of its contents will be recursively excluded from the
list.
- @note: If the passed-in directory happens to be a soft link, it will be
- recursed. However, the linkDepth parameter controls whether any soft
- links I{within} the directory will be recursed. The link depth is
- maximum depth of the tree at which soft links should be followed. So, a
- depth of 0 does not follow any soft links, a depth of 1 follows only
- links within the passed-in directory, a depth of 2 follows the links at
- the next level down, etc.
-
- @note: Any invalid soft links (i.e. soft links that point to
- non-existent items) will be silently ignored.
-
@note: The L{excludeDirs} flag only controls whether any given soft link
path itself is added to the list once it has been discovered. It does
I{not} modify any behavior related to directory recursion.
@@ -1266,6 +1287,9 @@
@param linkDepth: Depth of soft links that should be followed
@type linkDepth: Integer value, where zero means not to follow any soft links
+ @param dereference: Whether soft links should be dereferenced
+ @type dereference: Boolean value
+
@return: Number of items recursively added to the list
@raise ValueError: If path is not a directory or does not exist.
@@ -1273,7 +1297,7 @@
"""
path = encodePath(path)
path = normalizeDir(path)
- return super(PurgeItemList, self)._addDirContentsInternal(path, False, recursive, linkDepth)
+ return super(PurgeItemList, self)._addDirContentsInternal(path, False, recursive, linkDepth, dereference)
##################
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-04-13 20:16:45 UTC (rev 907)
+++ cedar-backup2/trunk/Changelog 2008-04-13 21:44:16 UTC (rev 908)
@@ -8,6 +8,7 @@
- Can't add links directly, they're implicitly added later by tar
- Changed FilesystemList to use includePath=false for recursive links
* Added util.dereferenceLink()
+ * Implemented dereference option on addDirContents()
Version 2.17.0 20 Mar 2008
Modified: cedar-backup2/trunk/test/filesystemtests.py
===================================================================
--- cedar-backup2/trunk/test/filesystemtests.py 2008-04-13 20:16:45 UTC (rev 907)
+++ cedar-backup2/trunk/test/filesystemtests.py 2008-04-13 21:44:16 UTC (rev 908)
@@ -131,7 +131,7 @@
DATA_DIRS = [ "./data", "./test/data" ]
RESOURCES = [ "tree1.tar.gz", "tree2.tar.gz", "tree3.tar.gz", "tree4.tar.gz", "tree5.tar.gz",
"tree6.tar.gz", "tree7.tar.gz", "tree8.tar.gz", "tree9.tar.gz", "tree10.tar.gz",
- "tree11.tar.gz", "tree12.tar.gz", "tree13.tar.gz", ]
+ "tree11.tar.gz", "tree12.tar.gz", "tree13.tar.gz", "tree22.tar.gz", ]
INVALID_FILE = "bogus" # This file name should never exist
NOMATCH_PATH = "/something" # This path should never match something we put in a file list
@@ -5431,7 +5431,263 @@
self.failUnless(self.buildPath([ "tree6", "file002", ]) in fsList)
self.failUnless(self.buildPath([ "tree6", "link001", ]) in fsList)
+ def testAddDirContents_093(self):
+ """
+ Attempt to add a directory with linkDepth=0, dereference=False.
+ """
+ self.extractTar("tree22")
+ path = self.buildPath(["tree22", "dir003", ])
+ fsList = FilesystemList()
+ count = fsList.addDirContents(path, linkDepth=0, dereference=False)
+ if not platformSupportsLinks():
+ pass
+ else:
+ self.failUnlessEqual(12, count)
+ self.failUnlessEqual(12, len(fsList))
+ self.failUnless(self.buildPath(["tree22", "dir003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link004", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", ]) in fsList)
+ def testAddDirContents_094(self):
+ """
+ Attempt to add a directory with linkDepth=1, dereference=False.
+ """
+ self.extractTar("tree22")
+ path = self.buildPath(["tree22", "dir003", ])
+ fsList = FilesystemList()
+ count = fsList.addDirContents(path, linkDepth=1, dereference=False)
+ if not platformSupportsLinks():
+ pass
+ else:
+ self.failUnlessEqual(16, count)
+ self.failUnlessEqual(16, len(fsList))
+ self.failUnless(self.buildPath(["tree22", "dir003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link004", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "link002", ]) in fsList)
+
+ def testAddDirContents_095(self):
+ """
+ Attempt to add a directory with linkDepth=2, dereference=False.
+ """
+ self.extractTar("tree22")
+ path = self.buildPath(["tree22", "dir003", ])
+ fsList = FilesystemList()
+ count = fsList.addDirContents(path, linkDepth=2, dereference=False)
+ if not platformSupportsLinks():
+ pass
+ else:
+ self.failUnlessEqual(20, count)
+ self.failUnlessEqual(20, len(fsList))
+ self.failUnless(self.buildPath(["tree22", "dir003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link004", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link003", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link003", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link003", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link001",]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link002",]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "link002", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "link002", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "link002", "link002", ]) in fsList)
+
+ def testAddDirContents_096(self):
+ """
+ Attempt to add a directory with linkDepth=3, dereference=False.
+ """
+ self.extractTar("tree22")
+ path = self.buildPath(["tree22", "dir003", ])
+ fsList = FilesystemList()
+ count = fsList.addDirContents(path, linkDepth=3, dereference=False)
+ if not platformSupportsLinks():
+ pass
+ else:
+ self.failUnlessEqual(20, count)
+ self.failUnlessEqual(20, len(fsList))
+ self.failUnless(self.buildPath(["tree22", "dir003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link004", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link003", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link003", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link003", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link001",]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link002",]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "link002", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "link002", "link001", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", "link002", "link002", ]) in fsList)
+
+ def testAddDirContents_097(self):
+ """
+ Attempt to add a directory with linkDepth=0, dereference=True.
+ """
+ self.extractTar("tree22")
+ path = self.buildPath(["tree22", "dir003", ])
+ fsList = FilesystemList()
+ count = fsList.addDirContents(path, linkDepth=0, dereference=True)
+ if not platformSupportsLinks():
+ pass
+ else:
+ self.failUnlessEqual(12, count)
+ self.failUnlessEqual(12, len(fsList))
+ self.failUnless(self.buildPath(["tree22", "dir003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link004", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "link003", ]) in fsList)
+
+ def testAddDirContents_098(self):
+ """
+ Attempt to add a directory with linkDepth=1, dereference=True.
+ """
+ self.extractTar("tree22")
+ path = self.buildPath(["tree22", "dir003", ])
+ fsList = FilesystemList()
+ count = fsList.addDirContents(path, linkDepth=1, dereference=True)
+ if not platformSupportsLinks():
+ pass
+ else:
+ self.failUnlessEqual(17, count)
+ self.failUnlessEqual(17, len(fsList))
+ self.failUnless(self.buildPath(["tree22", "dir003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "link004", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir001", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir001", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir005" ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir005", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir005", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir005", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir005", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir005", "link002", ]) in fsList)
+
+ def testAddDirContents_099(self):
+ """
+ Attempt to add a directory with linkDepth=2, dereference=True.
+ """
+ self.extractTar("tree22")
+ path = self.buildPath(["tree22", "dir003", ])
+ fsList = FilesystemList()
+ count = fsList.addDirContents(path, linkDepth=2, dereference=True)
+ if not platformSupportsLinks():
+ pass
+ else:
+ self.failUnlessEqual(23, count)
+ self.failUnlessEqual(23, len(fsList))
+ self.failUnless(self.buildPath(["tree22", "dir003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir002", "file004", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir002", "file005", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir001", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir004", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir004", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir004", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir004", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir001", "file002",]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir001", "file003",]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir005", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir005", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir005", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir005", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir002", "file009", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir006", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir006", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir006", "link001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir006", "link002", ]) in fsList)
+
+ def testAddDirContents_100(self):
+ """
+ Attempt to add a directory with linkDepth=3, dereference=True.
+ """
+ self.extractTar("tree22")
+ path = self.buildPath(["tree22", "dir003", ])
+ fsList = FilesystemList()
+ count = fsList.addDirContents(path, linkDepth=3, dereference=True)
+ if not platformSupportsLinks():
+ pass
+ else:
+ self.failUnlessEqual(24, count)
+ self.failUnlessEqual(24, len(fsList))
+ self.failUnless(self.buildPath(["tree22", "dir003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir003", "dir001", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir002", "file004", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir002", "file005", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir001", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir004", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir004", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir004", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir004", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir001", "file002",]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir001", "file003",]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir005", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir005", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir005", "file002", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir005", "file003", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir002", "file009", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir006", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir006", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir007", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir007", "file001", ]) in fsList)
+ self.failUnless(self.buildPath(["tree22", "dir008", "file001", ]) in fsList)
+
+
##################...
[truncated message content] |
|
From: <pro...@us...> - 2008-04-26 19:05:00
|
Revision: 911
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=911&view=rev
Author: pronovic
Date: 2008-04-26 12:04:54 -0700 (Sat, 26 Apr 2008)
Log Message:
-----------
Fall back work on the dereference-link enhancement
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/filesystem.py
cedar-backup2/trunk/CedarBackup2/util.py
cedar-backup2/trunk/Changelog
cedar-backup2/trunk/test/filesystemtests.py
cedar-backup2/trunk/test/utiltests.py
Added Paths:
-----------
cedar-backup2/trunk/doc/dereference/
cedar-backup2/trunk/doc/dereference/Changelog
cedar-backup2/trunk/doc/dereference/README
cedar-backup2/trunk/doc/dereference/filesystem.py
cedar-backup2/trunk/doc/dereference/filesystemtests.py
cedar-backup2/trunk/doc/dereference/tree22.tar.gz
cedar-backup2/trunk/doc/dereference/util.py
cedar-backup2/trunk/doc/dereference/utiltests.py
Removed Paths:
-------------
cedar-backup2/trunk/test/data/tree22.tar.gz
Modified: cedar-backup2/trunk/CedarBackup2/filesystem.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/filesystem.py 2008-04-14 01:27:23 UTC (rev 910)
+++ cedar-backup2/trunk/CedarBackup2/filesystem.py 2008-04-26 19:04:54 UTC (rev 911)
@@ -60,7 +60,6 @@
from CedarBackup2.knapsack import firstFit, bestFit, worstFit, alternateFit
from CedarBackup2.util import AbsolutePathList, ObjectTypeList, UnorderedList, RegexList
from CedarBackup2.util import removeKeys, displayBytes, calculateFileAge, encodePath
-from CedarBackup2.util import dereferenceLink
########################################################################
@@ -359,7 +358,7 @@
logger.debug("Added directory to list: [%s]" % path)
return 1
- def addDirContents(self, path, recursive=True, addSelf=True, linkDepth=0, dereference=False):
+ def addDirContents(self, path, recursive=True, addSelf=True, linkDepth=0):
"""
Adds the contents of a directory to the list.
@@ -369,26 +368,22 @@
place. If you only want the directory and its immediate contents to be
added, then pass in C{recursive=False}.
- If the passed-in directory happens to be a soft link, it will be
- recursed. However, by default, other directory links within the
- passed-in directory are not recursed. The C{linkDepth} parameter
- controls this behavior. This value is the maximum depth of the tree at
- which directory links should be followed. So, a depth of 0 does not
- follow any directory links, a depth of 1 follows only directory links
- within the passed-in directory, a depth of 2 follows the directory links
- at the next level down, etc.
-
- The C{dereference} flag also controls link-related behavior. If the
- C{linkDepth} is greater than zero and C{deference} is true, then any file
- or directory link will be deferenced before being added to the list. So,
- the list will contain the path of file or directory that the link points
- at, rather than the path of the link itself.
-
@note: If a directory's absolute path matches an exclude pattern or path,
or if the directory contains the configured ignore file, then the
directory and all of its contents will be recursively excluded from the
list.
+ @note: If the passed-in directory happens to be a soft link, it will be
+ recursed. However, the linkDepth parameter controls whether any soft
+ links I{within} the directory will be recursed. The link depth is
+ maximum depth of the tree at which soft links should be followed. So, a
+ depth of 0 does not follow any soft links, a depth of 1 follows only
+ links within the passed-in directory, a depth of 2 follows the links at
+ the next level down, etc.
+
+ @note: Any invalid soft links (i.e. soft links that point to
+ non-existent items) will be silently ignored.
+
@note: The L{excludeDirs} flag only controls whether any given directory
path itself is added to the list once it has been discovered. It does
I{not} modify any behavior related to directory recursion.
@@ -405,9 +400,6 @@
@param linkDepth: Maximum depth of the tree at which soft links should be followed
@type linkDepth: Integer value, where zero means not to follow any soft links
- @param dereference: Whether soft links should be dereferenced
- @type dereference: Boolean value
-
@return: Number of items recursively added to the list
@raise ValueError: If path is not a directory or does not exist.
@@ -415,9 +407,9 @@
"""
path = encodePath(path)
path = normalizeDir(path)
- return self._addDirContentsInternal(path, addSelf, recursive, linkDepth, dereference)
+ return self._addDirContentsInternal(path, addSelf, recursive, linkDepth)
- def _addDirContentsInternal(self, path, includePath=True, recursive=True, linkDepth=0, dereference=False):
+ def _addDirContentsInternal(self, path, includePath=True, recursive=True, linkDepth=0):
"""
Internal implementation of C{addDirContents}.
@@ -429,34 +421,27 @@
interface, C{addDirContents} ends up being wholly implemented in terms
of this method.
- The C{linkDepth} parameter controls whether directory links are followed
- when we are adding the contents recursively. Any recursive calls reduce
- the value by one. If the value zero or less, then directory links will
- just be added as directories, but will not be followed. This means that
- links are followed to a I{constant depth} starting from the top-most
- directory.
+ The linkDepth parameter controls whether soft links are followed when we
+ are adding the contents recursively. Any recursive calls reduce the
+ value by one. If the value zero or less, then soft links will just be
+ added as directories, but will not be followed. This means that links
+ are followed to a I{constant depth} starting from the top-most directory.
- There is one difference between directory links and directories:
- directory links that are added recursively (and are not deferenced) are
- not placed into the list explicitly. This is because if we do add the
- links recursively, the resulting tar file gets a little confused: it has
- a link and a directory with the same name, which some tar implementations
- cannot successfully extract.
+ There is one difference between soft links and directories: soft links
+ that are added recursively are not placed into the list explicitly. This
+ is because if we do add the links recursively, the resulting tar file
+ gets a little confused (it has a link and a directory with the same
+ name).
@param path: Directory path whose contents should be added to the list.
@param includePath: Indicates whether to include the path as well as contents.
@param recursive: Indicates whether directory contents should be added recursively.
@param linkDepth: Depth of soft links that should be followed
- @param dereference: Whether soft links should be dereferenced when C{linkDepth > 0}
@return: Number of items recursively added to the list
@raise ValueError: If path is not a directory or does not exist.
"""
- if linkDepth < 1:
- dereference = False # never dereference if we're not following links
- if dereference:
- path = dereferenceLink(path, absolute=True)
added = 0
if not os.path.exists(path) or not os.path.isdir(path):
logger.debug("Path [%s] is not a directory or does not exist on disk." % path)
@@ -479,21 +464,19 @@
added += self.addDir(path) # could actually be excluded by addDir, yet
for entry in os.listdir(path):
entrypath = os.path.join(path, entry)
- if dereference:
- entrypath = dereferenceLink(entrypath)
if os.path.isfile(entrypath):
added += self.addFile(entrypath)
elif os.path.isdir(entrypath):
if os.path.islink(entrypath):
if recursive and linkDepth > 0:
newDepth = linkDepth - 1;
- added += self._addDirContentsInternal(entrypath, includePath=False, linkDepth=newDepth, dereference=dereference)
+ added += self._addDirContentsInternal(entrypath, includePath=False, linkDepth=newDepth)
else:
added += self.addDir(entrypath)
else:
if recursive:
newDepth = linkDepth - 1;
- added += self._addDirContentsInternal(entrypath, linkDepth=newDepth, dereference=dereference)
+ added += self._addDirContentsInternal(entrypath, linkDepth=newDepth)
else:
added += self.addDir(entrypath)
return added
@@ -1238,7 +1221,7 @@
# Add methods
##############
- def addDirContents(self, path, recursive=True, addSelf=True, linkDepth=0, dereference=False):
+ def addDirContents(self, path, recursive=True, addSelf=True, linkDepth=0):
"""
Adds the contents of a directory to the list.
@@ -1248,26 +1231,22 @@
place. If you only want the directory and its contents to be added, then
pass in C{recursive=False}.
- If the passed-in directory happens to be a soft link, it will be
- recursed. However, by default, other directory links within the
- passed-in directory are not recursed. The C{linkDepth} parameter
- controls this behavior. This value is the maximum depth of the tree at
- which directory links should be followed. So, a depth of 0 does not
- follow any directory links, a depth of 1 follows only directory links
- within the passed-in directory, a depth of 2 follows the directory links
- at the next level down, etc.
-
- The C{dereference} flag also controls link-related behavior. If the
- C{linkDepth} is greater than zero and C{deference} is true, then any file
- or directory link will be deferenced before being added to the list. So,
- the list will contain the path of file or directory that the link points
- at, rather than the path of the link itself.
-
@note: If a directory's absolute path matches an exclude pattern or path,
or if the directory contains the configured ignore file, then the
directory and all of its contents will be recursively excluded from the
list.
+ @note: If the passed-in directory happens to be a soft link, it will be
+ recursed. However, the linkDepth parameter controls whether any soft
+ links I{within} the directory will be recursed. The link depth is
+ maximum depth of the tree at which soft links should be followed. So, a
+ depth of 0 does not follow any soft links, a depth of 1 follows only
+ links within the passed-in directory, a depth of 2 follows the links at
+ the next level down, etc.
+
+ @note: Any invalid soft links (i.e. soft links that point to
+ non-existent items) will be silently ignored.
+
@note: The L{excludeDirs} flag only controls whether any given soft link
path itself is added to the list once it has been discovered. It does
I{not} modify any behavior related to directory recursion.
@@ -1287,9 +1266,6 @@
@param linkDepth: Depth of soft links that should be followed
@type linkDepth: Integer value, where zero means not to follow any soft links
- @param dereference: Whether soft links should be dereferenced
- @type dereference: Boolean value
-
@return: Number of items recursively added to the list
@raise ValueError: If path is not a directory or does not exist.
@@ -1297,7 +1273,7 @@
"""
path = encodePath(path)
path = normalizeDir(path)
- return super(PurgeItemList, self)._addDirContentsInternal(path, False, recursive, linkDepth, dereference)
+ return super(PurgeItemList, self)._addDirContentsInternal(path, False, recursive, linkDepth)
##################
Modified: cedar-backup2/trunk/CedarBackup2/util.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/util.py 2008-04-14 01:27:23 UTC (rev 910)
+++ cedar-backup2/trunk/CedarBackup2/util.py 2008-04-26 19:04:54 UTC (rev 911)
@@ -1987,22 +1987,3 @@
os.environ[LANG_VAR] = DEFAULT_LANGUAGE
return os.environ.copy()
-
-#############################
-# dereferenceLink() function
-#############################
-
-def dereferenceLink(path, absolute=True):
- """
- Deference a soft link, optionally normalizing it to an absolute path.
- @param path: Path of link to dereference
- @param absolute: Whether to normalize the result to an absolute path
- @return: Dereferenced path, or original path if original is not a link.
- """
- if os.path.islink(path):
- result = os.readlink(path)
- if absolute and not os.path.isabs(result):
- result = os.path.abspath(os.path.join(os.path.dirname(path), result))
- return result
- return path
-
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-04-14 01:27:23 UTC (rev 910)
+++ cedar-backup2/trunk/Changelog 2008-04-26 19:04:54 UTC (rev 911)
@@ -7,8 +7,6 @@
* Fixed problem with link_depth (closes: #1930729).
- Can't add links directly, they're implicitly added later by tar
- Changed FilesystemList to use includePath=false for recursive links
- * Added util.dereferenceLink()
- * Implemented dereference option on addDirContents()
Version 2.17.0 20 Mar 2008
Added: cedar-backup2/trunk/doc/dereference/Changelog
===================================================================
--- cedar-backup2/trunk/doc/dereference/Changelog (rev 0)
+++ cedar-backup2/trunk/doc/dereference/Changelog 2008-04-26 19:04:54 UTC (rev 911)
@@ -0,0 +1,554 @@
+Version 2.17.1 unreleased
+
+ * Updated copyright statement slightly.
+ * Updated user manual
+ - Brought copyright notices up-to-date
+ - Fixed various URLs that didn't reference SourceForge
+ * Fixed problem with link_depth (closes: #1930729).
+ - Can't add links directly, they're implicitly added later by tar
+ - Changed FilesystemList to use includePath=false for recursive links
+ * Added util.dereferenceLink()
+ * Implemented dereference option on addDirContents()
+
+Version 2.17.0 20 Mar 2008
+
+ * Change suggested execution index for Capacity extension in manual.
+ * Provide support for application-wide diagnostic reporting.
+ - Add util.Diagnostics class to encapsulate information
+ - Log diagnostics when Cedar Backup first starts
+ - Print diagnostics when running unit tests
+ - Add a new --diagnostics command-line option
+ * Clean up filesystem code that deals with file age, and improve unit tests.
+ - Some platforms apparently cannot set file ages precisely
+ - Change calculateFileAge() to use floats throughout, which is safer
+ - Change removeYoungFiles() to explicitly check on whole days
+ - Put a 1-second fudge factor into unit tests when setting file ages
+ * Fix some unit test failures discovered on Windows XP.
+ - Fix utiltests.TestFunctions.testNullDevice_001()
+ - Fix filesystemtests.TestBackupFileList.testGenerateFitted_004()
+ - Fix typo in filesystemtests.TestFilesystemList.testRemoveLinks_002()
+
+Version 2.16.0 18 Mar 2008
+
+ * Make name attribute optional in RemotePeer constructor.
+ * Add support for collecting soft links (closes: #1854631).
+ - Add linkDepth parameter to FilesystemList.addDirContents()
+ - Add CollectDir.linkDepth attribute
+ - Modify collect action to obey CollectDir.linkDepth
+ - Update user manual to discuss new attribute
+ - Document "link farm" option for collect configuration
+ * Implement a capacity-checking extension (closes: #1915496).
+ - Add new extension in CedarBackup2/extend/capacity.py
+ - Refactor ByteQuantity out of split.py and into config.py
+ - Add total capacity and utilization to MediaCapacity classes
+ - Update user manual to discuss new extension
+
+Version 2.15.3 16 Mar 2008
+
+ * Fix testEncodePath_009() to be aware of "UTF-8" encoding.
+ * Fix typos in the PostgreSQL extension section of the manual.
+ * Improve logging when stage action fails (closes: #1854635).
+ * Fix stage action so it works for local users (closes: #1854634).
+
+Version 2.15.2 07 Feb 2008
+
+ * Updated copyright statements now that code changed in year 2008.
+ * Fix two unit test failures when using Python 2.5 (SF #1861878).
+ - Add new function testtutil.hexFloatLiteralAllowed()
+ - Fix splittests.TestByteQuantity.testConstructor_004() for 0xAC
+ - Fix configtests.TestBlankBehavior.testConstructor_006() for 0xAC
+
+Version 2.15.1 19 Dec 2007
+
+ * Improve error reporting for managed client action failures.
+ * Make sure that managed client failure does not kill entire backup.
+ * Add appendix "Securing Password-less SSH Connection" to user manual.
+
+Version 2.15.0 18 Dec 2007
+
+ * Minor documentation tweaks discovered during 3.0 development.
+ * Add support for a new managed backup feature.
+ - Add a new <peers> configuration section (PeersConfig)
+ - Change peers configuration in <stage> to just override <peers>
+ - Modify stage process to take peers list from peers section (if available)
+ - Add new configuration in options and remote peers to support remote shells
+ - Update user manual to discuss managed backup concept and configuration
+ - Add executeRemoteCommand() and executeManagedAction() on peer.RemotePeer
+
+Version 2.14.0 19 Sep 2007
+
+ * Deal properly with programs that localize their output.
+ - Create new util.sanitizeEnvironment() function to set $LANG=C
+ - Call new sanitizeEnvironment() function inside util.executeCommand()
+ - Change extend/split._splitFile() to be more verbose about problems
+ - Update Extension Architecture Interface to mandate $LANG=C
+ - Add split unit tests to catch any locale-related regressions
+ - Thanks to Lukasz Nowak for initial debugging in split extension
+
+Version 2.13.2 10 Jul 2007
+
+ * Tweak some docstring markup to work with Epydoc beta 1.
+ * Apply documentation patch from Lukasz K. Nowak.
+ - Document that mysql extension can back up remote databases
+ - Fix typos in extend/sysinfo.py
+ * Clean up some configuration error messages to be clearer.
+ - Make sure that reported errors always include enough information
+ - Add a prefix argument to some of the specialized lists in util.py
+ * Catch invalid regular expressions in config and filesystem code.
+ - Add new util.RegexList list to contain only valid regexes
+ - Use RegexList in config.ConfigDir and config.CollectConfig
+ - Use RegexList in subversion.RepositoryDir and mbox.MboxDir
+ - Throw ValueError on bad regex in FilesystemList remove() methods
+ - Use RegexList in FilesystemList for all lists of patterns
+
+Version 2.13.1 29 Mar 2007
+
+ * Fix ongoing problems re-initializing previously-written DVDs
+ - Even with -Z, growisofs sometimes wouldn't overwrite DVDs
+ - It turns out that this ONLY happens from cron, not from a terminal
+ - The solution is to use the undocumented option -use-the-force-luke=tty
+ - Also corrected dvdwriter to use option "-dry-run" not "--dry-run"
+
+Version 2.13.0 25 Mar 2007
+
+ * Change writeIndicator() to raise exception on failure (closes #53).
+ * Change buildNormalizedPath() for leading "." so files won't be hidden
+ * Remove bogus usage of tempfile.NamedTemporaryFile in remote peer.
+ * Refactored some common action code into CedarBackup2.actions.util.
+ * Add unit tests for a variety of basic utility functions (closes: #45).
+ - Error-handling was improved in some utility methods
+ - Fundamentally, behavior should be unchanged
+ * Reimplement DVD capacity calculation (initial code from Dmitry Rutsky).
+ - This is now done using a growisofs dry run, without -Z
+ - The old dvd+rw-mediainfo method was unreliable on some systems
+ - Error-handling behavior on CdWriter was also tweaked for consistency
+ * Add code to check media before writing to it (closes: #5).
+ - Create new check_media store configuration option
+ - Implement new initialize action to initialize rewritable media
+ - Media is initialized by writing an initial session with media label
+ - The store action now always writes a media label as well
+ - Update user manual to discuss the new behavior
+ - Add unit tests for new configuration
+ * Implement an optimized media blanking strategy (closes: #48).
+ - When used, Cedar Backup will only blank media when it runs out of space
+ - Initial implementation and manual text provided by Dmitry Rutsky
+ - Add new blanking_behavior store configuration options
+ - Update user manual to document options and discuss usage
+ - Add unit tests for new configuration
+
+Version 2.12.1 26 Feb 2007
+
+ * Fix typo in new split section in the user manual.
+ * Fix incorrect call to new writeIndicatorFile() function in stage action.
+ * Add notes in manual on how to find gpg and split commands.
+
+Version 2.12.0 23 Feb 2007
+
+ * Fix some encrypt unit tests related to config validation
+ * Make util.PathResolverSingleton a new-style class (i.e. inherit from object)
+ * Modify util.changeOwnership() to be a no-op for None user or group
+ * Created new split extension to split large staged files.
+ - Refactored common action utility code into actions/util.py.
+ - Update standard actions, cback-span, and encrypt to use refactored code
+ - Updated user manual to document the new extension and restore process.
+
+Version 2.11.0 21 Feb 2007
+
+ * Fix log message about SCSI id in writers/dvdwriter.py.
+ * Remove TODO from public distribution (use Bugzilla instead).
+ * Minor changes to mbox functionality (refactoring, test cleanup).
+ * Fix bug in knapsack implementation, masked by poor test suite.
+ * Fix filesystem unit tests that had typos in them and wouldn't work
+ * Reorg user manual to move command-line tools to own chapter (closes: #33)
+ * Add validation for duplicate peer and extension names (closes: #37, #38).
+ * Implement new cback-span command-line tool (closes: #51).
+ - Create new util/cback-span script and CedarBackup2.tools package
+ - Implement guts of script in CedarBackup2/tools/span.py
+ - Add new BackupFileList.generateSpan() method and tests
+ - Refactor other util and filesystem code to make things work
+ - Add new section in user manual to discuss new command
+ * Rework validation requiring least one item to collect (closes: #34).
+ - This is no longer a validation error at the configuration level
+ - Instead, the collect action itself will enforce the rule when it is run
+ * Support a <no_eject> flag in store configuration (closes: #39).
+ - Change StoreConfig, CdWriter and DvdWriter to accept new flag
+ - Update user manual to document new flag, along with warnings about it
+ * Support repository directories in Subversion extension (closes: #46).
+ - Add <repository_dir> configuration modeled after <mbox_dir>
+ - Make <type> configuration value optional and for reference only
+ - Refactor code and deprecate BDBRepository and FSFSRepository
+ - Update user manual to reflect new functionality
+
+Version 2.10.1 30 Jan 2007
+
+ * Fix a few places that still referred only to CD/CD-RW.
+ * Fix typo in definition of actions.constants.DIGEST_EXTENSION.
+
+Version 2.10.0 30 Jan 2007
+
+ * Add support for DVD writers and DVD+R/DVD+RW media.
+ - Create new writers.dvdwriter module and DvdWriter class
+ - Support 'dvdwriter' device type, and 'dvd+r' and 'dvd+rw' media types
+ - Rework user manual to properly discuss both CDs and DVDs
+ * Support encrypted staging directories (closes: #33).
+ - Create new 'encrypt' extension and associated unit tests
+ - Document new extension in user manual
+ * Support new action ordering mechanism for extensions.
+ - Extensions can now specify dependencies rather than indexes
+ - Rewrote cli._ActionSet class to use DirectedGraph for dependencies
+ - This functionality is not yet "official"; that will happen later
+ * Refactor and clean up code that implements standard actions.
+ - Split action.py into various other files in the actions package
+ - Move a few of the more generic utility functions into util.py
+ - Preserve public interface via imports in otherwise empty action.py
+ - Change various files to import from the new module locations
+ * Revise and simplify the implied "image writer" interface in CdWriter.
+ - Add the new initializeImage() and addImageEntry() methods
+ - Interface is now initializeImage(), addImageEntry() and writeImage()
+ - Rework actions.store.writeImage() to use new writer interface
+ * Refactor CD writer functionality and clean up code.
+ - Create new writers package to hold all image writers
+ - Move image.py into writers/util.py package
+ - Move most of writer.py into writers/cdwriter.py
+ - Move writer.py validate functions into writers/util.py
+ - Move writertests.py into cdwritertests.py
+ - Move imagetests.py into writersutiltests.py
+ - Preserve public interface via imports in otherwise empty files
+ - Change various files to import from the new module locations
+ * More general code cleanup and minor enhancements.
+ - Modify util/test.py to accept named tests on command line
+ - Fix rebuild action to look at store config instead of stage.
+ - Clean up xmlutil imports in mbox and subversion extensions
+ - Copy Mac OS X (darwin) errors from store action into rebuild action
+ - Check arguments to validateScsiId better (no None path allowed now)
+ - Rename variables in config.py to be more consistent with each other
+ - Add new excludeBasenamePatterns flag to FilesystemList
+ - Add new addSelf flag to FilesystemList.addDirContents()
+ - Create new RegexMatchList class in util.py, and add tests
+ - Create new DirectedGraph class in util.py, and add tests
+ - Create new sortDict() function in util.py, and add tests
+ * Create unit tests for functionality that was not explictly tested before.
+ - ActionHook, PreActionHook, PostActionHook, CommandOverride (config.py)
+ - AbsolutePathList, ObjectTypeList, RestrictedContentList (util.py)
+
+Version 2.9.0 18 Dec 2006
+
+ * Change mbox extension to use ISO-8601 date format when calling grepmail.
+ * Fix error-handling in generateTarfile() when target dir is missing.
+ * Tweak pycheckrc to find fewer expected errors (from standard library).
+ * Fix Debian bug #403546 by supporting more CD writer configurations.
+ - Be looser with SCSI "methods" allowed in valid SCSI id (update regex)
+ - Make <store> config section's <target_scsi_id> parameter optional
+ - Change CdWriter to support "hardware id" as either SCSI id or device
+ - Implement cdrecord commands in terms of hardware id instead of SCSI id
+ - Add documentation in writer.py to discuss how we talk to hardware
+ - Rework user manual's discussion of how to configure SCSI devices
+ * Update Cedar Backup user manual.
+ - Re-order setup procedures to modify cron at end (Debian #403662)
+ - Fix minor typos and misspellings (Debian #403448 among others)
+ - Add discussion about proper ordering of extension actions
+
+Version 2.8.1 04 Sep 2006
+
+ * Changes to fix, update and properly build Cedar Backup manual
+ - Change DocBook XSL configuration to use "current" stylesheet
+ - Tweak manual-generation rules to work around XSL toolchain issues
+ - Document where to find grepmail utility in Appendix B
+ - Create missing documentation for mbox exclusions configuration
+ - Bumped copyright dates to show "(c) 2005-2006" where needed
+ - Made minor changes to some sections based on proofreading
+
+Version 2.8.0 24 Jun 2006
+
+ * Remove outdated comment in xmlutil.py about dependency on PyXML.
+ * Tweak wording in doc/docbook.txt to make it clearer.
+ * Consistently rework "project description" everywhere.
+ * Fix some simple typos in various comments and documentation.
+ * Added recursive flag (default True) to FilesystemList.addDirContents().
+ * Added flat flag (default False) to BackupFileList.generateTarfile().
+ * Created mbox extension in CedarBackup2.extend.mbox (closes: #31).
+ - Updated user manual to document the new extension and restore process.
+ * Added PostgreSQL extension in CedarBackup2.extend.postgresql (closes: #32).
+ - This code was contributed by user Antoine Beaupre ("The Anarcat").
+ - I tweaked it slightly, added configuration tests, and updated the manual.
+ - I have no PostgreSQL databases on which to test the functionality.
+ * Made most unit tests run properly on Windows platform, just for fun.
+ * Re-implement Pipe class (under executeCommand) for Python 2.4+
+ - After Python 2.4, cross-platform subprocess.Popen class is available
+ - Added some new regression tests for executeCommand to stress new Pipe
+ * Switch to newer version of Docbook XSL stylesheet (1.68.1)
+ - The old stylesheet isn't easily available any more (gone from sf.net)
+ - Unfortunately, the PDF output changed somewhat with the new version
+ * Add support for collecting individual files (closes: #30).
+ - Create new config.CollectFile class for use by other classes
+ - Update config.CollectConfig class to contain a list of collect files
+ - Update config.Config class to parse and emit collect file data
+ - Modified collect process in action.py to handle collect files
+ - Updated user manual to discuss new <file> configuraton
+
+Version 2.7.2 22 Dec 2005
+
+ * Remove some bogus writer tests that depended on an arbitrary SCSI device.
+
+Version 2.7.1 13 Dec 2005
+
+ * Tweak the CREDITS file to fix a few typos.
+ * Remove completed tasks in TODO file and reorganize it slightly.
+ * Get rid of sys.exit() calls in util/test.py in favor of simple returns.
+ * Fix implementation ...
[truncated message content] |
|
From: <pro...@us...> - 2008-04-26 19:25:58
|
Revision: 912
http://cedar-backup.svn.sourceforge.net/cedar-backup/?rev=912&view=rev
Author: pronovic
Date: 2008-04-26 12:25:49 -0700 (Sat, 26 Apr 2008)
Log Message:
-----------
Release 2.17.1
Modified Paths:
--------------
cedar-backup2/trunk/CedarBackup2/release.py
cedar-backup2/trunk/Changelog
Modified: cedar-backup2/trunk/CedarBackup2/release.py
===================================================================
--- cedar-backup2/trunk/CedarBackup2/release.py 2008-04-26 19:04:54 UTC (rev 911)
+++ cedar-backup2/trunk/CedarBackup2/release.py 2008-04-26 19:25:49 UTC (rev 912)
@@ -34,7 +34,7 @@
AUTHOR = "Kenneth J. Pronovici"
EMAIL = "pro...@ie..."
COPYRIGHT = "2004-2008"
-VERSION = "2.17.0"
-DATE = "20 Mar 2008"
-URL = "http://cedar-solutions.com/software/cedar-backup"
+VERSION = "2.17.1"
+DATE = "26 Apr 2008"
+URL = "http://cedar-backup.sourceforge.net/"
Modified: cedar-backup2/trunk/Changelog
===================================================================
--- cedar-backup2/trunk/Changelog 2008-04-26 19:04:54 UTC (rev 911)
+++ cedar-backup2/trunk/Changelog 2008-04-26 19:25:49 UTC (rev 912)
@@ -1,7 +1,7 @@
-Version 2.17.1 unreleased
+Version 2.17.1 26 Apr 2008
* Updated copyright statement slightly.
- * Updated user manual
+ * Updated user manual.
- Brought copyright notices up-to-date
- Fixed various URLs that didn't reference SourceForge
* Fixed problem with link_depth (closes: #1930729).
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|