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 """
40 Implements the standard 'stage' action.
41 @sort: executeStage
42 @author: Kenneth J. Pronovici <pronovic@ieee.org>
43 """
44
45
46
47
48
49
50
51 import os
52 import time
53 import logging
54
55
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
64
65
66 logger = logging.getLogger("CedarBackup2.log.actions.stage")
67
68
69
70
71
72
73
74
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 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
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
125 ownership = None
126 logger.debug("Using target dir [%s], ownership [None]." % targetDir)
127 try:
128 count = peer.stagePeer(targetDir=targetDir, ownership=ownership)
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
139
140
141
142
143
144
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
187
188
189
190
191
192
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
212
213
234
235
236
237
238
239
264
265
266
267
268
269
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
285
286
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
300
301
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