Home | Trees | Indices | Help |
|
---|
|
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,2015 Kenneth J. Pronovici. 12 # All rights reserved. 13 # 14 # This program is free software; you can redistribute it and/or 15 # modify it under the terms of the GNU General Public License, 16 # Version 2, as published by the Free Software Foundation. 17 # 18 # This program is distributed in the hope that it will be useful, 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 21 # 22 # Copies of the GNU General Public License are available from 23 # the Free Software Foundation website, http://www.gnu.org/. 24 # 25 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 26 # 27 # Author : Kenneth J. Pronovici <pronovic@ieee.org> 28 # Language : Python 3 (>= 3.4) 29 # Project : Cedar Backup, release 3 30 # Purpose : 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 CedarBackup3.filesystem import FilesystemList 57 from CedarBackup3.util import changeOwnership 58 from CedarBackup3.util import deviceMounted 59 from CedarBackup3.writers.util import readMediaLabel 60 from CedarBackup3.writers.cdwriter import CdWriter 61 from CedarBackup3.writers.dvdwriter import DvdWriter 62 from CedarBackup3.writers.cdwriter import MEDIA_CDR_74, MEDIA_CDR_80, MEDIA_CDRW_74, MEDIA_CDRW_80 63 from CedarBackup3.writers.dvdwriter import MEDIA_DVDPLUSR, MEDIA_DVDPLUSRW 64 from CedarBackup3.config import DEFAULT_MEDIA_TYPE, DEFAULT_DEVICE_TYPE, REWRITABLE_MEDIA_TYPES 65 from CedarBackup3.actions.constants import INDICATOR_PATTERN 66 67 68 ######################################################################## 69 # Module-wide constants and variables 70 ######################################################################## 71 72 logger = logging.getLogger("CedarBackup3.log.actions.util") 73 MEDIA_LABEL_PREFIX = "CEDAR BACKUP" 74 75 76 ######################################################################## 77 # Public utility functions 78 ######################################################################## 79 80 ########################### 81 # findDailyDirs() function 82 ########################### 8385 """ 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 results115 116 117 ########################### 118 # createWriter() function 119 ########################### 120122 """ 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 ################################ 166168 """ 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 with open(filename, "w") as f: 180 f.write("") 181 changeOwnership(filename, backupUser, backupGroup) 182 except Exception as e: 183 logger.error("Error writing [%s]: %s", filename, e) 184 raise e185 186 187 ############################ 188 # getBackupFiles() function 189 ############################ 190192 """ 193 Gets a list of backup files in a target directory. 194 195 Files that match INDICATOR_PATTERN (i.e. C{"cback.store"}, C{"cback.stage"}, 196 etc.) are assumed to be indicator files and are ignored. 197 198 @param targetDir: Directory to look in 199 200 @return: List of backup files in the directory 201 202 @raise ValueError: If the target directory does not exist 203 """ 204 if not os.path.isdir(targetDir): 205 raise ValueError("Target directory [%s] is not a directory or does not exist." % targetDir) 206 fileList = FilesystemList() 207 fileList.excludeDirs = True 208 fileList.excludeLinks = True 209 fileList.excludeBasenamePatterns = INDICATOR_PATTERN 210 fileList.addDirContents(targetDir) 211 return fileList212 213 214 #################### 215 # checkMediaState() 216 #################### 217219 """ 220 Checks state of the media in the backup device to confirm whether it has 221 been initialized for use with Cedar Backup. 222 223 We can tell whether the media has been initialized by looking at its media 224 label. If the media label starts with MEDIA_LABEL_PREFIX, then it has been 225 initialized. 226 227 The check varies depending on whether the media is rewritable or not. For 228 non-rewritable media, we also accept a C{None} media label, since this kind 229 of media cannot safely be initialized. 230 231 @param storeConfig: Store configuration 232 233 @raise ValueError: If media is not initialized. 234 """ 235 mediaLabel = readMediaLabel(storeConfig.devicePath) 236 if storeConfig.mediaType in REWRITABLE_MEDIA_TYPES: 237 if mediaLabel is None: 238 raise ValueError("Media has not been initialized: no media label available") 239 elif not mediaLabel.startswith(MEDIA_LABEL_PREFIX): 240 raise ValueError("Media has not been initialized: unrecognized media label [%s]" % mediaLabel) 241 else: 242 if mediaLabel is None: 243 logger.info("Media has no media label; assuming OK since media is not rewritable.") 244 elif not mediaLabel.startswith(MEDIA_LABEL_PREFIX): 245 raise ValueError("Media has not been initialized: unrecognized media label [%s]" % mediaLabel)246 247 248 ######################### 249 # initializeMediaState() 250 ######################### 251253 """ 254 Initializes state of the media in the backup device so Cedar Backup can 255 recognize it. 256 257 This is done by writing an mostly-empty image (it contains a "Cedar Backup" 258 directory) to the media with a known media label. 259 260 @note: Only rewritable media (CD-RW, DVD+RW) can be initialized. It 261 doesn't make any sense to initialize media that cannot be rewritten (CD-R, 262 DVD+R), since Cedar Backup would then not be able to use that media for a 263 backup. 264 265 @param config: Cedar Backup configuration 266 267 @raise ValueError: If media could not be initialized. 268 @raise ValueError: If the configured media type is not rewritable 269 """ 270 if not config.store.mediaType in REWRITABLE_MEDIA_TYPES: 271 raise ValueError("Only rewritable media types can be initialized.") 272 mediaLabel = buildMediaLabel() 273 writer = createWriter(config) 274 writer.refreshMedia() 275 writer.initializeImage(True, config.options.workingDir, mediaLabel) # always create a new disc 276 tempdir = tempfile.mkdtemp(dir=config.options.workingDir) 277 try: 278 writer.addImageEntry(tempdir, "CedarBackup") 279 writer.writeImage() 280 finally: 281 if os.path.exists(tempdir): 282 try: 283 os.rmdir(tempdir) 284 except: pass285 286 287 #################### 288 # buildMediaLabel() 289 #################### 290292 """ 293 Builds a media label to be used on Cedar Backup media. 294 @return: Media label as a string. 295 """ 296 currentDate = time.strftime("%d-%b-%Y").upper() 297 return "%s %s" % (MEDIA_LABEL_PREFIX, currentDate)298 299 300 ######################################################################## 301 # Private attribute "getter" functions 302 ######################################################################## 303 304 ############################ 305 # _getDeviceType() function 306 ############################ 307309 """ 310 Gets the device type that should be used for storing. 311 312 Use the configured device type if not C{None}, otherwise use 313 L{config.DEFAULT_DEVICE_TYPE}. 314 315 @param config: Config object. 316 @return: Device type to be used. 317 """ 318 if config.store.deviceType is None: 319 deviceType = DEFAULT_DEVICE_TYPE 320 else: 321 deviceType = config.store.deviceType 322 logger.debug("Device type is [%s]", deviceType) 323 return deviceType324 325 326 ########################### 327 # _getMediaType() function 328 ########################### 329331 """ 332 Gets the media type that should be used for storing. 333 334 Use the configured media type if not C{None}, otherwise use 335 C{DEFAULT_MEDIA_TYPE}. 336 337 Once we figure out what configuration value to use, we return a media type 338 value that is valid in one of the supported writers:: 339 340 MEDIA_CDR_74 341 MEDIA_CDRW_74 342 MEDIA_CDR_80 343 MEDIA_CDRW_80 344 MEDIA_DVDPLUSR 345 MEDIA_DVDPLUSRW 346 347 @param config: Config object. 348 349 @return: Media type to be used as a writer media type value. 350 @raise ValueError: If the media type is not valid. 351 """ 352 if config.store.mediaType is None: 353 mediaType = DEFAULT_MEDIA_TYPE 354 else: 355 mediaType = config.store.mediaType 356 if mediaType == "cdr-74": 357 logger.debug("Media type is MEDIA_CDR_74.") 358 return MEDIA_CDR_74 359 elif mediaType == "cdrw-74": 360 logger.debug("Media type is MEDIA_CDRW_74.") 361 return MEDIA_CDRW_74 362 elif mediaType == "cdr-80": 363 logger.debug("Media type is MEDIA_CDR_80.") 364 return MEDIA_CDR_80 365 elif mediaType == "cdrw-80": 366 logger.debug("Media type is MEDIA_CDRW_80.") 367 return MEDIA_CDRW_80 368 elif mediaType == "dvd+r": 369 logger.debug("Media type is MEDIA_DVDPLUSR.") 370 return MEDIA_DVDPLUSR 371 elif mediaType == "dvd+rw": 372 logger.debug("Media type is MEDIA_DVDPLUSRW.") 373 return MEDIA_DVDPLUSRW 374 else: 375 raise ValueError("Media type [%s] is not valid." % mediaType)376
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 | http://epydoc.sourceforge.net |