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-2007 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.3) 
 29  # Project  : Cedar Backup, release 2 
 30  # Revision : $Id: stage.py 1181 2007-03-25 16:18:22Z pronovic $ 
 31  # Purpose  : Implements the standard 'stage' action. 
 32  # 
 33  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
 34   
 35  ######################################################################## 
 36  # Module documentation 
 37  ######################################################################## 
 38   
 39  """ 
 40  Implements the standard 'stage' action. 
 41  @sort: executeStage 
 42  @author: Kenneth J. Pronovici <pronovic@ieee.org> 
 43  """ 
 44   
 45   
 46  ######################################################################## 
 47  # Imported modules 
 48  ######################################################################## 
 49   
 50  # System modules 
 51  import os 
 52  import time 
 53  import logging 
 54   
 55  # Cedar Backup modules 
 56  from CedarBackup2.peer import RemotePeer, LocalPeer 
 57  from CedarBackup2.util import getUidGid, changeOwnership 
 58  from CedarBackup2.actions.constants import DIR_TIME_FORMAT, STAGE_INDICATOR 
 59  from CedarBackup2.actions.util import writeIndicatorFile 
 60   
 61   
 62  ######################################################################## 
 63  # Module-wide constants and variables 
 64  ######################################################################## 
 65   
 66  logger = logging.getLogger("CedarBackup2.log.actions.stage") 
 67   
 68   
 69  ######################################################################## 
 70  # Public functions 
 71  ######################################################################## 
 72   
 73  ########################## 
 74  # executeStage() function 
 75  ########################## 
 76   
77 -def executeStage(configPath, options, config):
78 """ 79 Executes the stage backup action. 80 81 @note: The daily directory is derived once and then we stick with it, just 82 in case a backup happens to span midnite. 83 84 @note: As portions of the stage action is complete, we will write various 85 indicator files so that it's obvious what actions have been completed. Each 86 peer gets a stage indicator in its collect directory, and then the master 87 gets a stage indicator in its daily staging directory. The store process 88 uses the master's stage indicator to decide whether a directory is ready to 89 be stored. Currently, nothing uses the indicator at each peer, and it 90 exists for reference only. 91 92 @param configPath: Path to configuration file on disk. 93 @type configPath: String representing a path on disk. 94 95 @param options: Program command-line options. 96 @type options: Options object. 97 98 @param config: Program configuration. 99 @type config: Config object. 100 101 @raise ValueError: Under many generic error conditions 102 @raise IOError: If there are problems reading or writing files. 103 """ 104 logger.debug("Executing the 'stage' action.") 105 if config.options is None or config.stage is None: 106 raise ValueError("Stage configuration is not properly filled in.") 107 dailyDir = _getDailyDir(config) 108 localPeers = _getLocalPeers(config) 109 remotePeers = _getRemotePeers(config) 110 allPeers = localPeers + remotePeers 111 stagingDirs = _createStagingDirs(config, dailyDir, allPeers) 112 for peer in allPeers: 113 logger.info("Staging peer [%s]." % peer.name) 114 if not peer.checkCollectIndicator(): 115 logger.error("Peer [%s] was not ready to be staged." % peer.name) 116 continue 117 logger.debug("Found collect indicator.") 118 targetDir = stagingDirs[peer.name] 119 ownership = getUidGid(config.options.backupUser, config.options.backupGroup) 120 logger.debug("Using target dir [%s], ownership [%d:%d]." % (targetDir, ownership[0], ownership[1])) 121 try: 122 count = peer.stagePeer(targetDir=targetDir, ownership=ownership) # note: utilize effective user's default umask 123 logger.info("Staged %d files for peer [%s]." % (count, peer.name)) 124 peer.writeStageIndicator() 125 except (ValueError, IOError, OSError), e: 126 logger.error("Error staging [%s]: %s" % (peer.name, e)) 127 writeIndicatorFile(dailyDir, STAGE_INDICATOR, config.options.backupUser, config.options.backupGroup) 128 logger.info("Executed the 'stage' action successfully.")
129 130 131 ######################################################################## 132 # Private utility functions 133 ######################################################################## 134 135 ################################ 136 # _createStagingDirs() function 137 ################################ 138
139 -def _createStagingDirs(config, dailyDir, peers):
140 """ 141 Creates staging directories as required. 142 143 The main staging directory is the passed in daily directory, something like 144 C{staging/2002/05/23}. Then, individual peers get their own directories, 145 i.e. C{staging/2002/05/23/host}. 146 147 @param config: Config object. 148 @param dailyDir: Daily staging directory. 149 @param peers: List of all configured peers. 150 151 @return: Dictionary mapping peer name to staging directory. 152 """ 153 mapping = {} 154 if os.path.isdir(dailyDir): 155 logger.warn("Staging directory [%s] already existed." % dailyDir) 156 else: 157 try: 158 logger.debug("Creating staging directory [%s]." % dailyDir) 159 os.makedirs(dailyDir) 160 for path in [ dailyDir, os.path.join(dailyDir, ".."), os.path.join(dailyDir, "..", ".."), ]: 161 changeOwnership(path, config.options.backupUser, config.options.backupGroup) 162 except Exception, e: 163 raise Exception("Unable to create staging directory: %s" % e) 164 for peer in peers: 165 peerDir = os.path.join(dailyDir, peer.name) 166 mapping[peer.name] = peerDir 167 if os.path.isdir(peerDir): 168 logger.warn("Peer staging directory [%s] already existed." % peerDir) 169 else: 170 try: 171 logger.debug("Creating peer staging directory [%s]." % peerDir) 172 os.makedirs(peerDir) 173 changeOwnership(peerDir, config.options.backupUser, config.options.backupGroup) 174 except Exception, e: 175 raise Exception("Unable to create staging directory: %s" % e) 176 return mapping
177 178 179 ######################################################################## 180 # Private attribute "getter" functions 181 ######################################################################## 182 183 ########################## 184 # _getDailyDir() function 185 ########################## 186
187 -def _getDailyDir(config):
188 """ 189 Gets the daily staging directory. 190 191 This is just a directory in the form C{staging/YYYY/MM/DD}, i.e. 192 C{staging/2000/10/07}, except it will be an absolute path based on 193 C{config.stage.targetDir}. 194 195 @param config: Config object 196 197 @return: Path of daily staging directory. 198 """ 199 dailyDir = os.path.join(config.stage.targetDir, time.strftime(DIR_TIME_FORMAT)) 200 logger.debug("Daily staging directory is [%s]." % dailyDir) 201 return dailyDir
202 203 204 ############################ 205 # _getLocalPeers() function 206 ############################ 207
208 -def _getLocalPeers(config):
209 """ 210 Return a list of L{LocalPeer} objects based on configuration. 211 @param config: Config object. 212 @return: List of L{LocalPeer} objects. 213 """ 214 localPeers = [] 215 if config.stage.localPeers is not None: 216 for peer in config.stage.localPeers: 217 localPeer = LocalPeer(peer.name, peer.collectDir) 218 localPeers.append(localPeer) 219 logger.debug("Found local peer: [%s]" % localPeer.name) 220 return localPeers
221 222 223 ############################# 224 # _getRemotePeers() function 225 ############################# 226
227 -def _getRemotePeers(config):
228 """ 229 Return a list of L{RemotePeer} objects based on configuration. 230 @param config: Config object. 231 @return: List of L{RemotePeer} objects. 232 """ 233 remotePeers = [] 234 if config.stage.remotePeers is not None: 235 for peer in config.stage.remotePeers: 236 remoteUser = _getRemoteUser(config, peer) 237 rcpCommand = _getRcpCommand(config, peer) 238 remotePeer = RemotePeer(peer.name, peer.collectDir, config.options.workingDir, 239 remoteUser, rcpCommand, config.options.backupUser) 240 remotePeers.append(remotePeer) 241 logger.debug("Found remote peer: [%s]" % remotePeer.name) 242 return remotePeers
243 244 245 ############################ 246 # _getRemoteUser() function 247 ############################ 248
249 -def _getRemoteUser(config, remotePeer):
250 """ 251 Gets the remote user associated with a remote peer. 252 Use peer's if possible, otherwise take from options section. 253 @param config: Config object. 254 @param remotePeer: Configuration-style remote peer object. 255 @return: Name of remote user associated with remote peer. 256 """ 257 if remotePeer.remoteUser is None: 258 return config.options.backupUser 259 return remotePeer.remoteUser
260 261 262 ############################ 263 # _getRcpCommand() function 264 ############################ 265
266 -def _getRcpCommand(config, remotePeer):
267 """ 268 Gets the RCP command associated with a remote peer. 269 Use peer's if possible, otherwise take from options section. 270 @param config: Config object. 271 @param remotePeer: Configuration-style remote peer object. 272 @return: RCP command associated with remote peer. 273 """ 274 if remotePeer.rcpCommand is None: 275 return config.options.rcpCommand 276 return remotePeer.rcpCommand
277