CedarBackup2.actions.util

Package CedarBackup2 :: Package actions :: Module util |
[hide private]
[frames] | no frames]

# Source Code for Module CedarBackup2.actions.util

      1  # -*- coding: iso-8859-1 -*-
      2  # vim: set ft=python ts=3 sw=3 expandtab:
      3  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
      4  #
      5  #              C E D A R
      6  #          S O L U T I O N S       "Software done right."
      7  #           S O F T W A R E
      8  #
      9  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
     10  #
     11  # Copyright (c) 2007,2010 Kenneth J. Pronovici.
     12  # All rights reserved.
     13  #
     14  # This program is free software; you can redistribute it and/or
     15  # modify it under the terms of the GNU General Public License,
     16  # Version 2, as published by the Free Software Foundation.
     17  #
     18  # This program is distributed in the hope that it will be useful,
     19  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     20  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     21  #
     22  # Copies of the GNU General Public License are available from
     23  # the Free Software Foundation website, http://www.gnu.org/.
     24  #
     25  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
     26  #
     27  # Author   : Kenneth J. Pronovici <pronovic@ieee.org>
     28  # Language : Python 2 (>= 2.7)
     29  # Project  : Cedar Backup, release 2
     30  # Purpose  : Implements action-related utilities
     31  #
     32  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
     33
     34  ########################################################################
     35  # Module documentation
     36  ########################################################################
     37
     38  """
     39  Implements action-related utilities
     40  @sort: findDailyDirs
     41  @author: Kenneth J. Pronovici <pronovic@ieee.org>
     42  """
     43
     44
     45  ########################################################################
     46  # Imported modules
     47  ########################################################################
     48
     49  # System modules
     50  import os
     51  import time
     52  import tempfile
     53  import logging
     54
     55  # Cedar Backup modules
     56  from CedarBackup2.filesystem import FilesystemList
     57  from CedarBackup2.util import changeOwnership
     58  from CedarBackup2.util import deviceMounted
     59  from CedarBackup2.writers.util import readMediaLabel
     60  from CedarBackup2.writers.cdwriter import CdWriter
     61  from CedarBackup2.writers.dvdwriter import DvdWriter
     62  from CedarBackup2.writers.cdwriter import MEDIA_CDR_74, MEDIA_CDR_80, MEDIA_CDRW_74, MEDIA_CDRW_80
     63  from CedarBackup2.writers.dvdwriter import MEDIA_DVDPLUSR, MEDIA_DVDPLUSRW
     64  from CedarBackup2.config import DEFAULT_MEDIA_TYPE, DEFAULT_DEVICE_TYPE, REWRITABLE_MEDIA_TYPES
     65  from CedarBackup2.actions.constants import INDICATOR_PATTERN
     66
     67
     68  ########################################################################
     69  # Module-wide constants and variables
     70  ########################################################################
     71
     72  logger = logging.getLogger("CedarBackup2.log.actions.util")
     73  MEDIA_LABEL_PREFIX   = "CEDAR BACKUP"
     74
     75
     76  ########################################################################
     77  # Public utility functions
     78  ########################################################################
     79
     80  ###########################
     81  # findDailyDirs() function
     82  ###########################
     83
     84 -def findDailyDirs(stagingDir, indicatorFile):
     85     """
     86     Returns a list of all daily staging directories that do not contain
     87     the indicated indicator file.
     88
     89     @param stagingDir: Configured staging directory (config.targetDir)
     90
     91     @return: List of absolute paths to daily staging directories.
     92     """
     93     results = FilesystemList()
     94     yearDirs = FilesystemList()
     95     yearDirs.excludeFiles = True
     96     yearDirs.excludeLinks = True
     97     yearDirs.addDirContents(path=stagingDir, recursive=False, addSelf=False)
     98     for yearDir in yearDirs:
     99        monthDirs = FilesystemList()
    100        monthDirs.excludeFiles = True
    101        monthDirs.excludeLinks = True
    102        monthDirs.addDirContents(path=yearDir, recursive=False, addSelf=False)
    103        for monthDir in monthDirs:
    104           dailyDirs = FilesystemList()
    105           dailyDirs.excludeFiles = True
    106           dailyDirs.excludeLinks = True
    107           dailyDirs.addDirContents(path=monthDir, recursive=False, addSelf=False)
    108           for dailyDir in dailyDirs:
    109              if os.path.exists(os.path.join(dailyDir, indicatorFile)):
    110                 logger.debug("Skipping directory [%s]; contains %s.", dailyDir, indicatorFile)
    111              else:
    112                 logger.debug("Adding [%s] to list of daily directories.", dailyDir)
    113                 results.append(dailyDir) # just put it in the list, no fancy operations
    114     return results
    115
    116
    117  ###########################
    118  # createWriter() function
    119  ###########################
    120
    121 -def createWriter(config):
    122     """
    123     Creates a writer object based on current configuration.
    124
    125     This function creates and returns a writer based on configuration.  This is
    126     done to abstract action functionality from knowing what kind of writer is in
    127     use.  Since all writers implement the same interface, there's no need for
    128     actions to care which one they're working with.
    129
    130     Currently, the C{cdwriter} and C{dvdwriter} device types are allowed.  An
    131     exception will be raised if any other device type is used.
    132
    133     This function also checks to make sure that the device isn't mounted before
    134     creating a writer object for it.  Experience shows that sometimes if the
    135     device is mounted, we have problems with the backup.  We may as well do the
    136     check here first, before instantiating the writer.
    137
    138     @param config: Config object.
    139
    140     @return: Writer that can be used to write a directory to some media.
    141
    142     @raise ValueError: If there is a problem getting the writer.
    143     @raise IOError: If there is a problem creating the writer object.
    144     """
    145     devicePath = config.store.devicePath
    146     deviceScsiId = config.store.deviceScsiId
    147     driveSpeed = config.store.driveSpeed
    148     noEject = config.store.noEject
    149     refreshMediaDelay = config.store.refreshMediaDelay
    150     ejectDelay = config.store.ejectDelay
    151     deviceType = _getDeviceType(config)
    152     mediaType = _getMediaType(config)
    153     if deviceMounted(devicePath):
    154        raise IOError("Device [%s] is currently mounted." % (devicePath))
    155     if deviceType == "cdwriter":
    156        return CdWriter(devicePath, deviceScsiId, driveSpeed, mediaType, noEject, refreshMediaDelay, ejectDelay)
    157     elif deviceType == "dvdwriter":
    158        return DvdWriter(devicePath, deviceScsiId, driveSpeed, mediaType, noEject, refreshMediaDelay, ejectDelay)
    159     else:
    160        raise ValueError("Device type [%s] is invalid." % deviceType)
    161
    162
    163  ################################
    164  # writeIndicatorFile() function
    165  ################################
    166
    167 -def writeIndicatorFile(targetDir, indicatorFile, backupUser, backupGroup):
    168     """
    169     Writes an indicator file into a target directory.
    170     @param targetDir: Target directory in which to write indicator
    171     @param indicatorFile: Name of the indicator file
    172     @param backupUser: User that indicator file should be owned by
    173     @param backupGroup: Group that indicator file should be owned by
    174     @raise IOException: If there is a problem writing the indicator file
    175     """
    176     filename = os.path.join(targetDir, indicatorFile)
    177     logger.debug("Writing indicator file [%s].", filename)
    178     try:
    179        open(filename, "w").write("")
    180        changeOwnership(filename, backupUser, backupGroup)
    181     except Exception, e:
    182        logger.error("Error writing [%s]: %s", filename, e)
    183        raise e
    184
    185
    186  ############################
    187  # getBackupFiles() function
    188  ############################
    189
    190 -def getBackupFiles(targetDir):
    191     """
    192     Gets a list of backup files in a target directory.
    193
    194     Files that match INDICATOR_PATTERN (i.e. C{"cback.store"}, C{"cback.stage"},
    195     etc.) are assumed to be indicator files and are ignored.
    196
    197     @param targetDir: Directory to look in
    198
    199     @return: List of backup files in the directory
    200
    201     @raise ValueError: If the target directory does not exist
    202     """
    203     if not os.path.isdir(targetDir):
    204        raise ValueError("Target directory [%s] is not a directory or does not exist." % targetDir)
    205     fileList = FilesystemList()
    206     fileList.excludeDirs = True
    207     fileList.excludeLinks = True
    208     fileList.excludeBasenamePatterns = INDICATOR_PATTERN
    209     fileList.addDirContents(targetDir)
    210     return fileList
    211
    212
    213  ####################
    214  # checkMediaState()
    215  ####################
    216
    217 -def checkMediaState(storeConfig):
    218     """
    219     Checks state of the media in the backup device to confirm whether it has
    220     been initialized for use with Cedar Backup.
    221
    222     We can tell whether the media has been initialized by looking at its media
    223     label.  If the media label starts with MEDIA_LABEL_PREFIX, then it has been
    224     initialized.
    225
    226     The check varies depending on whether the media is rewritable or not.  For
    227     non-rewritable media, we also accept a C{None} media label, since this kind
    228     of media cannot safely be initialized.
    229
    230     @param storeConfig: Store configuration
    231
    232     @raise ValueError: If media is not initialized.
    233     """
    234     mediaLabel = readMediaLabel(storeConfig.devicePath)
    235     if storeConfig.mediaType in REWRITABLE_MEDIA_TYPES:
    236        if mediaLabel is None:
    237           raise ValueError("Media has not been initialized: no media label available")
    238        elif not mediaLabel.startswith(MEDIA_LABEL_PREFIX):
    239           raise ValueError("Media has not been initialized: unrecognized media label [%s]" % mediaLabel)
    240     else:
    241        if mediaLabel is None:
    242           logger.info("Media has no media label; assuming OK since media is not rewritable.")
    243        elif not mediaLabel.startswith(MEDIA_LABEL_PREFIX):
    244           raise ValueError("Media has not been initialized: unrecognized media label [%s]" % mediaLabel)
    245
    246
    247  #########################
    248  # initializeMediaState()
    249  #########################
    250
    251 -def initializeMediaState(config):
    252     """
    253     Initializes state of the media in the backup device so Cedar Backup can
    254     recognize it.
    255
    256     This is done by writing an mostly-empty image (it contains a "Cedar Backup"
    257     directory) to the media with a known media label.
    258
    259     @note: Only rewritable media (CD-RW, DVD+RW) can be initialized.  It
    260     doesn't make any sense to initialize media that cannot be rewritten (CD-R,
    261     DVD+R), since Cedar Backup would then not be able to use that media for a
    262     backup.
    263
    264     @param config: Cedar Backup configuration
    265
    266     @raise ValueError: If media could not be initialized.
    267     @raise ValueError: If the configured media type is not rewritable
    268     """
    269     if not config.store.mediaType in REWRITABLE_MEDIA_TYPES:
    270        raise ValueError("Only rewritable media types can be initialized.")
    271     mediaLabel = buildMediaLabel()
    272     writer = createWriter(config)
    273     writer.refreshMedia()
    274     writer.initializeImage(True, config.options.workingDir, mediaLabel) # always create a new disc
    275     tempdir = tempfile.mkdtemp(dir=config.options.workingDir)
    276     try:
    277        writer.addImageEntry(tempdir, "CedarBackup")
    278        writer.writeImage()
    279     finally:
    280        if os.path.exists(tempdir):
    281           try:
    282              os.rmdir(tempdir)
    283           except: pass
    284
    285
    286  ####################
    287  # buildMediaLabel()
    288  ####################
    289
    290 -def buildMediaLabel():
    291     """
    292     Builds a media label to be used on Cedar Backup media.
    293     @return: Media label as a string.
    294     """
    295     currentDate = time.strftime("%d-%b-%Y").upper()
    296     return "%s %s" % (MEDIA_LABEL_PREFIX, currentDate)
    297
    298
    299  ########################################################################
    300  # Private attribute "getter" functions
    301  ########################################################################
    302
    303  ############################
    304  # _getDeviceType() function
    305  ############################
    306
    307 -def _getDeviceType(config):
    308     """
    309     Gets the device type that should be used for storing.
    310
    311     Use the configured device type if not C{None}, otherwise use
    312     L{config.DEFAULT_DEVICE_TYPE}.
    313
    314     @param config: Config object.
    315     @return: Device type to be used.
    316     """
    317     if config.store.deviceType is None:
    318        deviceType = DEFAULT_DEVICE_TYPE
    319     else:
    320        deviceType = config.store.deviceType
    321     logger.debug("Device type is [%s]", deviceType)
    322     return deviceType
    323
    324
    325  ###########################
    326  # _getMediaType() function
    327  ###########################
    328
    329 -def _getMediaType(config):
    330     """
    331     Gets the media type that should be used for storing.
    332
    333     Use the configured media type if not C{None}, otherwise use
    334     C{DEFAULT_MEDIA_TYPE}.
    335
    336     Once we figure out what configuration value to use, we return a media type
    337     value that is valid in one of the supported writers::
    338
    339        MEDIA_CDR_74
    340        MEDIA_CDRW_74
    341        MEDIA_CDR_80
    342        MEDIA_CDRW_80
    343        MEDIA_DVDPLUSR
    344        MEDIA_DVDPLUSRW
    345
    346     @param config: Config object.
    347
    348     @return: Media type to be used as a writer media type value.
    349     @raise ValueError: If the media type is not valid.
    350     """
    351     if config.store.mediaType is None:
    352        mediaType = DEFAULT_MEDIA_TYPE
    353     else:
    354        mediaType = config.store.mediaType
    355     if mediaType == "cdr-74":
    356        logger.debug("Media type is MEDIA_CDR_74.")
    357        return MEDIA_CDR_74
    358     elif mediaType == "cdrw-74":
    359        logger.debug("Media type is MEDIA_CDRW_74.")
    360        return MEDIA_CDRW_74
    361     elif mediaType == "cdr-80":
    362        logger.debug("Media type is MEDIA_CDR_80.")
    363        return MEDIA_CDR_80
    364     elif mediaType == "cdrw-80":
    365        logger.debug("Media type is MEDIA_CDRW_80.")
    366        return MEDIA_CDRW_80
    367     elif mediaType == "dvd+r":
    368        logger.debug("Media type is MEDIA_DVDPLUSR.")
    369        return MEDIA_DVDPLUSR
    370     elif mediaType == "dvd+rw":
    371        logger.debug("Media type is MEDIA_DVDPLUSRW.")
    372        return MEDIA_DVDPLUSRW
    373     else:
    374        raise ValueError("Media type [%s] is not valid." % mediaType)
    375