1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
47
48
49
50 import os
51 import time
52 import logging
53
54
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
63
64
65 logger = logging.getLogger("CedarBackup3.log.actions.stage")
66
67
68
69
70
71
72
73
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
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
128 ownership = None
129 logger.debug("Using target dir [%s], ownership [None].", targetDir)
130 try:
131 count = peer.stagePeer(targetDir=targetDir, ownership=ownership)
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
142
143
144
145
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
190
191
192
193
194
195
214
215
216
217
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
239
240
261
262
263
264
265
266
292
293
294
295
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
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
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