| 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,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 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 CedarBackup3.peer import RemotePeer, LocalPeer
56 from CedarBackup3.util import getUidGid, changeOwnership, isStartOfWeek, isRunningAsRoot
57 from CedarBackup3.actions.constants import DIR_TIME_FORMAT, STAGE_INDICATOR
58 from CedarBackup3.actions.util import writeIndicatorFile
59
60
61 ########################################################################
62 # Module-wide constants and variables
63 ########################################################################
64
65 logger = logging.getLogger("CedarBackup3.log.actions.stage")
66
67
68 ########################################################################
69 # Public functions
70 ########################################################################
71
72 ##########################
73 # executeStage() function
74 ##########################
75
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) as 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
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.warning("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 as 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.warning("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 as 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
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
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
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
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
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
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
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
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 | http://epydoc.sourceforge.net |