<?xml version="1.0" encoding="UTF-8" ?>
<rdf:RDF xmlns="http://usefulinc.com/ns/doap#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><Project><name>iw.recipe.backup</name>
<shortdesc>Zc buildout recipe that provides a backup script</shortdesc>
<description>.. contents::


Code repository: https://ingeniweb.svn.sourceforge.net/svnroot/ingeniweb/iw.recipe.backup/


Change history
**************

0.2.0 (2008-05-08)
==================

  - added 'archive-before-backup' option. [tdesvenain]
    True by default. If set to 0, buildout is not backuped before a restore

  - added 'include-folders' option, that allows to limit backup
    on some directories [tdesvenain]

  - create backups dir if not exists [tdesvenain]

  - add format option instead of archive-root-name
    [gawel]

  - fixing test so it passes on all machines
    [tarek]

0.1.2 (2008-04-11)
==================

  - Added the fs-location option, that allows
    doing a safe backup of a running Data.fs.
    [tarek]

0.1.1 (2008-03-21)
==================

  - Added the `exclude-folders` option

  - Now asks a question before restoring.
    It is more a conveniency since everything
    is backed up in any case.
    [tarek]

0.1.0 (2008-03-12)
==================

 - Initial implementation.
   [tarek]
 - Created recipe with ZopeSkel
   [Ingeniweb].


Detailed Documentation
**********************

Supported options
=================

The recipe supports the following options:

backup-script-name
    Name of the backup script. Default: `backup`

restore-script-name
    Name of the restore script. Default: `restore`

format
    Formated name of the archive.
    Default to %(name)s-%(year)s-%(month)s-%(day)s-%(hour)s-%(minute)s
    where name is the section name

exclude-folders
    Names of folder to avoid backing up. Relative to buildout root.

include-folders
    If set, back up those folders only. Relative to buildout root.

fs-location
    If given, indicates the path of the file system storage.
    The recipe will carefully copy it when doing a backup,
    by using a transaction-level read of the file.
    This means that you can launch a backup without stopping
    the Zope server.

target-folder
    Folder where the archives are stored. **Mandatory**

log-file
    File where all calls are recorded. **Mandatory**

archive-before-restore
    If not set to 0, do a complete backup before a restore

prompt-before-restore
    If not set to 0, prompt before a restore

Example usage
=============

We'll start by creating a buildout that uses the recipe::

    &gt;&gt;&gt; import os
    &gt;&gt;&gt; root =  os.path.split(sample_buildout)[0]
    &gt;&gt;&gt; if root == '':
    ...     root = '.'

Let's copy a real Data.fs in our buildout::

    &gt;&gt;&gt; import shutil
    &gt;&gt;&gt; data_fs = os.path.join(test_dir, 'Data.fs')
    &gt;&gt;&gt; shutil.copyfile(data_fs, join(sample_buildout, 'Data.fs'))

    &gt;&gt;&gt; write('buildout.cfg',
    ... """
    ... [buildout]
    ... parts = backup
    ... index = http://pypi.python.org/simple
    ...
    ... [backup]
    ... recipe = iw.recipe.backup
    ...
    ... format = %(format)s
    ... target-folder = %(root)s
    ... log-file = %(root)s/backup.log
    ... fs-location = ${buildout:directory}/Data.fs
    ... """ % {'root': root, 'format':'%(name)s-%(year)s-%(month)s-%(day)s'})

Let's run the buildout::

    &gt;&gt;&gt; null = system(buildout)

Let's see what we got in the backup script::

    &gt;&gt;&gt; print open(join(sample_buildout, 'bin', 'backup')).read()
    #!...
    &lt;BLANKLINE&gt;
    import sys
    sys.path[0:0] = [
      ...
      ]
    &lt;BLANKLINE&gt;
    import iw.recipe.backup.archive
    &lt;BLANKLINE&gt;
    if __name__ == '__main__':
        iw.recipe.backup.archive.archive_buildout(('/sample-buildout', '%(name)s-%(year)s-%(month)s-%(day)s', '..._TEST_', '/backup.log', [], [], '/sample-buildout/Data.fs', {'name': 'backup'}))
    &lt;BLANKLINE&gt;

Ok, let's call it to backup the current buildout::

    &gt;&gt;&gt; print system(join(sample_buildout, 'bin', 'backup'))
    Starting the backup...
    Archived in /backup-XXXX-XX-XX.tar.gz
    ...
    &lt;BLANKLINE&gt;

We should have a log file generated as well::

    &gt;&gt;&gt; print open(join(root, 'backup.log')).read()
    20... Starting the backup...
    20... INFO Archived in /backup-XXXX-XX-XX.tar.gz

We also have a restore feature::

    &gt;&gt;&gt; print system(join(sample_buildout, 'bin', 'restore'))
    Usage: ...restore archive_name
    &lt;BLANKLINE&gt;

Let's set the user input::

    &gt;&gt;&gt; from iw.recipe.backup.testing import set_input
    &gt;&gt;&gt; set_input('Y')

Oh right, the restore script takes the name of the archive::

    &gt;&gt;&gt; import glob
    &gt;&gt;&gt; arc = glob.glob('%s/*.tar.gz' % root)[0]
    &gt;&gt;&gt; print system(join(sample_buildout, 'bin', 'restore %s' % arc))
    Are you sure you want to restore ? Every data will be lost ! (y/N)  Y
    Archiving current folder before restoring
    Starting the backup...
    Archived in /before-restore-XXXX-XX-XX-XX-XX.tar.gz
    Starting the restore...
    Archive restored in /sample-buildout
    ...
    &lt;BLANKLINE&gt;

And a restore *always* makes an archive on the current folder before
it is applied, to make sure nothing is never lost.

There's also something quite important: make sure the archive and
log files are not located in the buildout !::

    &gt;&gt;&gt; write('buildout.cfg',
    ... """
    ... [buildout]
    ... parts = backup
    ... index = http://pypi.python.org/simple
    ...
    ... [backup]
    ... recipe = iw.recipe.backup
    ...
    ... target-folder = %(root)s
    ... log-file = %(root)s/backup.log
    ... """ % {'root': sample_buildout})

    &gt;&gt;&gt; print system(buildout)
    Uninstalling backup.
    Installing backup.
    While:
      Installing backup.
    &lt;BLANKLINE&gt;
    An internal error occured due to a bug in either zc.buildout or in a
    recipe being used:
    ...
    ValueError: Cannot backup within the buildout ! Check your values
    &lt;BLANKLINE&gt;

A bit of cleaning::

    &gt;&gt;&gt; import glob
    &gt;&gt;&gt; arc = glob.glob('%s/*.tar.gz' % root)
    &gt;&gt;&gt; for f in arc:
    ...     os.remove(f)

We can also exclude some folders from being archived::

    &gt;&gt;&gt; os.mkdir(join(sample_buildout, 'not'))
    &gt;&gt;&gt; open(join(sample_buildout, 'not', 'f'), 'w').write('me file')

    &gt;&gt;&gt; os.mkdir(join(sample_buildout, 'neh'))

    &gt;&gt;&gt; write('buildout.cfg',
    ... """
    ... [buildout]
    ... parts = backup
    ... index = http://pypi.python.org/simple
    ...
    ... [backup]
    ... recipe = iw.recipe.backup
    ...
    ... target-folder = %(root)s
    ... log-file = %(root)s/backup.log
    ... fs-location = ${buildout:directory}/Data.fs
    ... exclude-folders =
    ...     %(sample_buildout)s/not
    ...     %(sample_buildout)s/neh
    ... """ % {'root': root, 'sample_buildout': sample_buildout})

Running the buildout again::

    &gt;&gt;&gt; print system(buildout+' -D')
    Installing backup.
    Generated script '...backup'.
    Generated script '...restore'.
    &lt;BLANKLINE&gt;

Let's backup::

    &gt;&gt;&gt; print system(join(sample_buildout, 'bin', 'backup'))
    Starting the backup...
    Archived in /backup-XXXX-XX-XX-XX-XX.tar.gz
    ...
    &lt;BLANKLINE&gt;

Let's remove the folder and the Data.fs::

    &gt;&gt;&gt; import shutil
    &gt;&gt;&gt; shutil.rmtree(join(sample_buildout, 'not'))
    &gt;&gt;&gt; os.rmdir(join(sample_buildout, 'neh'))
    &gt;&gt;&gt; os.remove(join(sample_buildout, 'Data.fs'))

Let's restore::

    &gt;&gt;&gt; arc = glob.glob('%s/*.tar.gz' % root)[0]
    &gt;&gt;&gt; print system(join(sample_buildout, 'bin', 'restore %s' % arc))
    Are you sure you want to restore ? Every data will be lost ! (y/N)  Y
    ...
    &lt;BLANKLINE&gt;

And make sure the `not` folder is not back !

    &gt;&gt;&gt; os.path.exists(join(sample_buildout, 'not'))
    False
    &gt;&gt;&gt; os.path.exists(join(sample_buildout, 'neh'))
    False

And the `Data.fs` is back, so we're OK::

    &gt;&gt;&gt; os.path.exists(join(sample_buildout, 'Data.fs'))
    True

==============

We can also restrict archive on some folders ::

    &gt;&gt;&gt; os.mkdir(join(sample_buildout, 'not'))
    &gt;&gt;&gt; open(join(sample_buildout, 'not', 'f'), 'w').write('me file')

    &gt;&gt;&gt; os.mkdir(join(sample_buildout, 'neh'))
    &gt;&gt;&gt; open(join(sample_buildout, 'neh', 'f'), 'w').write('me file')
    
    &gt;&gt;&gt; os.mkdir(join(sample_buildout, 'neh/not'))
    &gt;&gt;&gt; open(join(sample_buildout, 'neh/not', 'f'), 'w').write('me file')

    &gt;&gt;&gt; write('buildout.cfg',
    ... """
    ... [buildout]
    ... parts = backup
    ... index = http://pypi.python.org/simple
    ...
    ... [backup]
    ... recipe = iw.recipe.backup
    ...
    ... target-folder = %(root)s
    ... log-file = %(root)s/backup.log
    ... fs-location = ${buildout:directory}/Data.fs
    ... exclude-folders =
    ...     %(sample_buildout)s/not
    ...     %(sample_buildout)s/neh/not
    ... include-folders =
    ...     %(sample_buildout)s/neh
    ... prompt-before-restore = 0
    ... archive-before-restore = 0
    ... """ % {'root': root, 'sample_buildout': sample_buildout})

Running the buildout again::

    &gt;&gt;&gt; print system(buildout+' -D')
    Uninstalling backup.
    Installing backup.
    Generated script '...backup'.
    Generated script '...restore'.
    &lt;BLANKLINE&gt;

Let's see what we got in the backup script::

    &gt;&gt;&gt; print open(join(sample_buildout, 'bin', 'backup')).read()
    #!...
    &lt;BLANKLINE&gt;
    import sys
    sys.path[0:0] = [
      ...
      ]
    &lt;BLANKLINE&gt;
    import iw.recipe.backup.archive
    &lt;BLANKLINE&gt;
    if __name__ == '__main__':
        iw.recipe.backup.archive.archive_buildout(('/sample-buildout', '...', '..._TEST_', '/backup.log', ['/sample-buildout/not', '/sample-buildout/neh/not'], ['/sample-buildout/neh'], '/sample-buildout/Data.fs', {'name': 'backup'}))
    &lt;BLANKLINE&gt;

    
Let's backup::

    &gt;&gt;&gt; print system(join(sample_buildout, 'bin', 'backup'))
    Starting the backup...
    Archived in /backup-XXXX-XX-XX-XX-XX.tar.gz
    &lt;BLANKLINE&gt;

Let's remove the folder and the Data.fs::
    
    &gt;&gt;&gt; import shutil
    &gt;&gt;&gt; shutil.rmtree(join(sample_buildout, 'not'))
    &gt;&gt;&gt; shutil.rmtree(join(sample_buildout, 'neh'))
    &gt;&gt;&gt; os.remove(join(sample_buildout, 'Data.fs'))
    
Let's restore::

    &gt;&gt;&gt; arc = glob.glob('%s/*.tar.gz' % root)[0]
    &gt;&gt;&gt; rest = os.popen(join(sample_buildout, 'bin', 'restore %s' % arc))
	
And make sure the `not` folder and 'neh/not' are not back and neh is back !
    &gt;&gt;&gt; os.path.exists(join(sample_buildout, 'not'))
    False
    &gt;&gt;&gt; os.path.exists(join(sample_buildout, 'neh/not'))
    False
    &gt;&gt;&gt; os.path.exists(join(sample_buildout, 'neh'))
    True

Contributors
************

Ingeniweb, Author


Download
********</description>
<homepage rdf:resource="https://ingeniweb.svn.sourceforge.net/svnroot/ingeniweb/iw.recipe.backup" />
<maintainer><foaf:Person><foaf:name>Ingeniweb</foaf:name>
<foaf:mbox_sha1sum>ce647768fc2915c7851be5591bf96a90966761ef</foaf:mbox_sha1sum></foaf:Person></maintainer>
<release><Version><revision>0.2.0</revision></Version></release>
</Project></rdf:RDF>