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