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 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 852 2008-03-16 23:34:19Z 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 if os.getuid() == 0: 120 # Since we're running as root, we can change ownership 121 ownership = getUidGid(config.options.backupUser, config.options.backupGroup) 122 logger.debug("Using target dir [%s], ownership [%d:%d]." % (targetDir, ownership[0], ownership[1])) 123 else: 124 # Non-root cannot change ownership, so don't set it 125 ownership = None 126 logger.debug("Using target dir [%s], ownership [None]." % targetDir) 127 try: 128 count = peer.stagePeer(targetDir=targetDir, ownership=ownership) # note: utilize effective user's default umask 129 logger.info("Staged %d files for peer [%s]." % (count, peer.name)) 130 peer.writeStageIndicator() 131 except (ValueError, IOError, OSError), e: 132 logger.error("Error staging [%s]: %s" % (peer.name, e)) 133 writeIndicatorFile(dailyDir, STAGE_INDICATOR, config.options.backupUser, config.options.backupGroup) 134 logger.info("Executed the 'stage' action successfully.")
135 136 137 ######################################################################## 138 # Private utility functions 139 ######################################################################## 140 141 ################################ 142 # _createStagingDirs() function 143 ################################ 144
145 -def _createStagingDirs(config, dailyDir, peers):
146 """ 147 Creates staging directories as required. 148 149 The main staging directory is the passed in daily directory, something like 150 C{staging/2002/05/23}. Then, individual peers get their own directories, 151 i.e. C{staging/2002/05/23/host}. 152 153 @param config: Config object. 154 @param dailyDir: Daily staging directory. 155 @param peers: List of all configured peers. 156 157 @return: Dictionary mapping peer name to staging directory. 158 """ 159 mapping = {} 160 if os.path.isdir(dailyDir): 161 logger.warn("Staging directory [%s] already existed." % dailyDir) 162 else: 163 try: 164 logger.debug("Creating staging directory [%s]." % dailyDir) 165 os.makedirs(dailyDir) 166 for path in [ dailyDir, os.path.join(dailyDir, ".."), os.path.join(dailyDir, "..", ".."), ]: 167 changeOwnership(path, config.options.backupUser, config.options.backupGroup) 168 except Exception, e: 169 raise Exception("Unable to create staging directory: %s" % e) 170 for peer in peers: 171 peerDir = os.path.join(dailyDir, peer.name) 172 mapping[peer.name] = peerDir 173 if os.path.isdir(peerDir): 174 logger.warn("Peer staging directory [%s] already existed." % peerDir) 175 else: 176 try: 177 logger.debug("Creating peer staging directory [%s]." % peerDir) 178 os.makedirs(peerDir) 179 changeOwnership(peerDir, config.options.backupUser, config.options.backupGroup) 180 except Exception, e: 181 raise Exception("Unable to create staging directory: %s" % e) 182 return mapping
183 184 185 ######################################################################## 186 # Private attribute "getter" functions 187 ######################################################################## 188 189 ########################## 190 # _getDailyDir() function 191 ########################## 192
193 -def _getDailyDir(config):
194 """ 195 Gets the daily staging directory. 196 197 This is just a directory in the form C{staging/YYYY/MM/DD}, i.e. 198 C{staging/2000/10/07}, except it will be an absolute path based on 199 C{config.stage.targetDir}. 200 201 @param config: Config object 202 203 @return: Path of daily staging directory. 204 """ 205 dailyDir = os.path.join(config.stage.targetDir, time.strftime(DIR_TIME_FORMAT)) 206 logger.debug("Daily staging directory is [%s]." % dailyDir) 207 return dailyDir
208 209 210 ############################ 211 # _getLocalPeers() function 212 ############################ 213
214 -def _getLocalPeers(config):
215 """ 216 Return a list of L{LocalPeer} objects based on configuration. 217 @param config: Config object. 218 @return: List of L{LocalPeer} objects. 219 """ 220 localPeers = [] 221 configPeers = None 222 if config.stage.hasPeers(): 223 logger.debug("Using list of local peers from stage configuration.") 224 configPeers = config.stage.localPeers 225 elif config.peers is not None and config.peers.hasPeers(): 226 logger.debug("Using list of local peers from peers configuration.") 227 configPeers = config.peers.localPeers 228 if configPeers is not None: 229 for peer in configPeers: 230 localPeer = LocalPeer(peer.name, peer.collectDir) 231 localPeers.append(localPeer) 232 logger.debug("Found local peer: [%s]" % localPeer.name) 233 return localPeers
234 235 236 ############################# 237 # _getRemotePeers() function 238 ############################# 239
240 -def _getRemotePeers(config):
241 """ 242 Return a list of L{RemotePeer} objects based on configuration. 243 @param config: Config object. 244 @return: List of L{RemotePeer} objects. 245 """ 246 remotePeers = [] 247 configPeers = None 248 if config.stage.hasPeers(): 249 logger.debug("Using list of remote peers from stage configuration.") 250 configPeers = config.stage.remotePeers 251 elif config.peers is not None and config.peers.hasPeers(): 252 logger.debug("Using list of remote peers from peers configuration.") 253 configPeers = config.peers.remotePeers 254 if configPeers is not None: 255 for peer in configPeers: 256 remoteUser = _getRemoteUser(config, peer) 257 localUser = _getLocalUser(config) 258 rcpCommand = _getRcpCommand(config, peer) 259 remotePeer = RemotePeer(peer.name, peer.collectDir, config.options.workingDir, 260 remoteUser, rcpCommand, localUser) 261 remotePeers.append(remotePeer) 262 logger.debug("Found remote peer: [%s]" % remotePeer.name) 263 return remotePeers
264 265 266 ############################ 267 # _getRemoteUser() function 268 ############################ 269
270 -def _getRemoteUser(config, remotePeer):
271 """ 272 Gets the remote user associated with a remote peer. 273 Use peer's if possible, otherwise take from options section. 274 @param config: Config object. 275 @param remotePeer: Configuration-style remote peer object. 276 @return: Name of remote user associated with remote peer. 277 """ 278 if remotePeer.remoteUser is None: 279 return config.options.backupUser 280 return remotePeer.remoteUser
281 282 283 ########################### 284 # _getLocalUser() function 285 ########################### 286
287 -def _getLocalUser(config):
288 """ 289 Gets the remote user associated with a remote peer. 290 @param config: Config object. 291 @return: Name of local user that should be used 292 """ 293 if os.getuid() != 0: 294 return None 295 return config.options.backupUser
296 297 298 ############################ 299 # _getRcpCommand() function 300 ############################ 301
302 -def _getRcpCommand(config, remotePeer):
303 """ 304 Gets the RCP command associated with a remote peer. 305 Use peer's if possible, otherwise take from options section. 306 @param config: Config object. 307 @param remotePeer: Configuration-style remote peer object. 308 @return: RCP command associated with remote peer. 309 """ 310 if remotePeer.rcpCommand is None: 311 return config.options.rcpCommand 312 return remotePeer.rcpCommand
313