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

Source Code for Module CedarBackup2.actions.stage

  1  # -*- coding: iso-8859-1 -*- 
  2  # vim: set ft=python ts=3 sw=3 expandtab: 
  3  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
  4  # 
  5  #              C E D A R 
  6  #          S O L U T I O N S       "Software done right." 
  7  #           S O F T W A R E 
  8  # 
  9  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
 10  # 
 11  # Copyright (c) 2004-2008,2010 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.5) 
 29  # Project  : Cedar Backup, release 2 
 30  # Purpose  : Implements the standard 'stage' action. 
 31  # 
 32  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
 33   
 34  ######################################################################## 
 35  # Module documentation 
 36  ######################################################################## 
 37   
 38  """ 
 39  Implements the standard 'stage' action. 
 40  @sort: executeStage 
 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 logging 
 53   
 54  # Cedar Backup modules 
 55  from CedarBackup2.peer import RemotePeer, LocalPeer 
 56  from CedarBackup2.util import getUidGid, changeOwnership, isStartOfWeek, isRunningAsRoot 
 57  from CedarBackup2.actions.constants import DIR_TIME_FORMAT, STAGE_INDICATOR 
 58  from CedarBackup2.actions.util import writeIndicatorFile 
 59   
 60   
 61  ######################################################################## 
 62  # Module-wide constants and variables 
 63  ######################################################################## 
 64   
 65  logger = logging.getLogger("CedarBackup2.log.actions.stage") 
 66   
 67   
 68  ######################################################################## 
 69  # Public functions 
 70  ######################################################################## 
 71   
 72  ########################## 
 73  # executeStage() function 
 74  ########################## 
 75   
76 -def executeStage(configPath, options, config):
77 """ 78 Executes the stage backup action. 79 80 @note: The daily directory is derived once and then we stick with it, just 81 in case a backup happens to span midnite. 82 83 @note: As portions of the stage action is complete, we will write various 84 indicator files so that it's obvious what actions have been completed. Each 85 peer gets a stage indicator in its collect directory, and then the master 86 gets a stage indicator in its daily staging directory. The store process 87 uses the master's stage indicator to decide whether a directory is ready to 88 be stored. Currently, nothing uses the indicator at each peer, and it 89 exists for reference only. 90 91 @param configPath: Path to configuration file on disk. 92 @type configPath: String representing a path on disk. 93 94 @param options: Program command-line options. 95 @type options: Options object. 96 97 @param config: Program configuration. 98 @type config: Config object. 99 100 @raise ValueError: Under many generic error conditions 101 @raise IOError: If there are problems reading or writing files. 102 """ 103 logger.debug("Executing the 'stage' action.") 104 if config.options is None or config.stage is None: 105 raise ValueError("Stage configuration is not properly filled in.") 106 dailyDir = _getDailyDir(config) 107 localPeers = _getLocalPeers(config) 108 remotePeers = _getRemotePeers(config) 109 allPeers = localPeers + remotePeers 110 stagingDirs = _createStagingDirs(config, dailyDir, allPeers) 111 for peer in allPeers: 112 logger.info("Staging peer [%s]." % peer.name) 113 ignoreFailures = _getIgnoreFailuresFlag(options, config, peer) 114 if not peer.checkCollectIndicator(): 115 if not ignoreFailures: 116 logger.error("Peer [%s] was not ready to be staged." % peer.name) 117 else: 118 logger.info("Peer [%s] was not ready to be staged." % peer.name) 119 continue 120 logger.debug("Found collect indicator.") 121 targetDir = stagingDirs[peer.name] 122 if isRunningAsRoot(): 123 # Since we're running as root, we can change ownership 124 ownership = getUidGid(config.options.backupUser, config.options.backupGroup) 125 logger.debug("Using target dir [%s], ownership [%d:%d]." % (targetDir, ownership[0], ownership[1])) 126 else: 127 # Non-root cannot change ownership, so don't set it 128 ownership = None 129 logger.debug("Using target dir [%s], ownership [None]." % targetDir) 130 try: 131 count = peer.stagePeer(targetDir=targetDir, ownership=ownership) # note: utilize effective user's default umask 132 logger.info("Staged %d files for peer [%s]." % (count, peer.name)) 133 peer.writeStageIndicator() 134 except (ValueError, IOError, OSError), e: 135 logger.error("Error staging [%s]: %s" % (peer.name, e)) 136 writeIndicatorFile(dailyDir, STAGE_INDICATOR, config.options.backupUser, config.options.backupGroup) 137 logger.info("Executed the 'stage' action successfully.")
138 139 140 ######################################################################## 141 # Private utility functions 142 ######################################################################## 143 144 ################################ 145 # _createStagingDirs() function 146 ################################ 147
148 -def _createStagingDirs(config, dailyDir, peers):
149 """ 150 Creates staging directories as required. 151 152 The main staging directory is the passed in daily directory, something like 153 C{staging/2002/05/23}. Then, individual peers get their own directories, 154 i.e. C{staging/2002/05/23/host}. 155 156 @param config: Config object. 157 @param dailyDir: Daily staging directory. 158 @param peers: List of all configured peers. 159 160 @return: Dictionary mapping peer name to staging directory. 161 """ 162 mapping = {} 163 if os.path.isdir(dailyDir): 164 logger.warn("Staging directory [%s] already existed." % dailyDir) 165 else: 166 try: 167 logger.debug("Creating staging directory [%s]." % dailyDir) 168 os.makedirs(dailyDir) 169 for path in [ dailyDir, os.path.join(dailyDir, ".."), os.path.join(dailyDir, "..", ".."), ]: 170 changeOwnership(path, config.options.backupUser, config.options.backupGroup) 171 except Exception, e: 172 raise Exception("Unable to create staging directory: %s" % e) 173 for peer in peers: 174 peerDir = os.path.join(dailyDir, peer.name) 175 mapping[peer.name] = peerDir 176 if os.path.isdir(peerDir): 177 logger.warn("Peer staging directory [%s] already existed." % peerDir) 178 else: 179 try: 180 logger.debug("Creating peer staging directory [%s]." % peerDir) 181 os.makedirs(peerDir) 182 changeOwnership(peerDir, config.options.backupUser, config.options.backupGroup) 183 except Exception, e: 184 raise Exception("Unable to create staging directory: %s" % e) 185 return mapping
186 187 188 ######################################################################## 189 # Private attribute "getter" functions 190 ######################################################################## 191 192 #################################### 193 # _getIgnoreFailuresFlag() function 194 #################################### 195
196 -def _getIgnoreFailuresFlag(options, config, peer):
197 """ 198 Gets the ignore failures flag based on options, configuration, and peer. 199 @param options: Options object 200 @param config: Configuration object 201 @param peer: Peer to check 202 @return: Whether to ignore stage failures for this peer 203 """ 204 logger.debug("Ignore failure mode for this peer: %s" % peer.ignoreFailureMode) 205 if peer.ignoreFailureMode is None or peer.ignoreFailureMode == "none": 206 return False 207 elif peer.ignoreFailureMode == "all": 208 return True 209 else: 210 if options.full or isStartOfWeek(config.options.startingDay): 211 return peer.ignoreFailureMode == "weekly" 212 else: 213 return peer.ignoreFailureMode == "daily"
214 215 216 ########################## 217 # _getDailyDir() function 218 ########################## 219
220 -def _getDailyDir(config):
221 """ 222 Gets the daily staging directory. 223 224 This is just a directory in the form C{staging/YYYY/MM/DD}, i.e. 225 C{staging/2000/10/07}, except it will be an absolute path based on 226 C{config.stage.targetDir}. 227 228 @param config: Config object 229 230 @return: Path of daily staging directory. 231 """ 232 dailyDir = os.path.join(config.stage.targetDir, time.strftime(DIR_TIME_FORMAT)) 233 logger.debug("Daily staging directory is [%s]." % dailyDir) 234 return dailyDir
235 236 237 ############################ 238 # _getLocalPeers() function 239 ############################ 240
241 -def _getLocalPeers(config):
242 """ 243 Return a list of L{LocalPeer} objects based on configuration. 244 @param config: Config object. 245 @return: List of L{LocalPeer} objects. 246 """ 247 localPeers = [] 248 configPeers = None 249 if config.stage.hasPeers(): 250 logger.debug("Using list of local peers from stage configuration.") 251 configPeers = config.stage.localPeers 252 elif config.peers is not None and config.peers.hasPeers(): 253 logger.debug("Using list of local peers from peers configuration.") 254 configPeers = config.peers.localPeers 255 if configPeers is not None: 256 for peer in configPeers: 257 localPeer = LocalPeer(peer.name, peer.collectDir, peer.ignoreFailureMode) 258 localPeers.append(localPeer) 259 logger.debug("Found local peer: [%s]" % localPeer.name) 260 return localPeers
261 262 263 ############################# 264 # _getRemotePeers() function 265 ############################# 266
267 -def _getRemotePeers(config):
268 """ 269 Return a list of L{RemotePeer} objects based on configuration. 270 @param config: Config object. 271 @return: List of L{RemotePeer} objects. 272 """ 273 remotePeers = [] 274 configPeers = None 275 if config.stage.hasPeers(): 276 logger.debug("Using list of remote peers from stage configuration.") 277 configPeers = config.stage.remotePeers 278 elif config.peers is not None and config.peers.hasPeers(): 279 logger.debug("Using list of remote peers from peers configuration.") 280 configPeers = config.peers.remotePeers 281 if configPeers is not None: 282 for peer in configPeers: 283 remoteUser = _getRemoteUser(config, peer) 284 localUser = _getLocalUser(config) 285 rcpCommand = _getRcpCommand(config, peer) 286 remotePeer = RemotePeer(peer.name, peer.collectDir, config.options.workingDir, 287 remoteUser, rcpCommand, localUser, 288 ignoreFailureMode=peer.ignoreFailureMode) 289 remotePeers.append(remotePeer) 290 logger.debug("Found remote peer: [%s]" % remotePeer.name) 291 return remotePeers
292 293 294 ############################ 295 # _getRemoteUser() function 296 ############################ 297
298 -def _getRemoteUser(config, remotePeer):
299 """ 300 Gets the remote user associated with a remote peer. 301 Use peer's if possible, otherwise take from options section. 302 @param config: Config object. 303 @param remotePeer: Configuration-style remote peer object. 304 @return: Name of remote user associated with remote peer. 305 """ 306 if remotePeer.remoteUser is None: 307 return config.options.backupUser 308 return remotePeer.remoteUser
309 310 311 ########################### 312 # _getLocalUser() function 313 ########################### 314
315 -def _getLocalUser(config):
316 """ 317 Gets the remote user associated with a remote peer. 318 @param config: Config object. 319 @return: Name of local user that should be used 320 """ 321 if not isRunningAsRoot(): 322 return None 323 return config.options.backupUser
324 325 326 ############################ 327 # _getRcpCommand() function 328 ############################ 329
330 -def _getRcpCommand(config, remotePeer):
331 """ 332 Gets the RCP command associated with a remote peer. 333 Use peer's if possible, otherwise take from options section. 334 @param config: Config object. 335 @param remotePeer: Configuration-style remote peer object. 336 @return: RCP command associated with remote peer. 337 """ 338 if remotePeer.rcpCommand is None: 339 return config.options.rcpCommand 340 return remotePeer.rcpCommand
341