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 ownership = getUidGid(config.options.backupUser, config.options.backupGroup)
120 logger.debug("Using target dir [%s], ownership [%d:%d]." % (targetDir, ownership[0], ownership[1]))
121 try:
122 count = peer.stagePeer(targetDir=targetDir, ownership=ownership)
123 logger.info("Staged %d files for peer [%s]." % (count, peer.name))
124 peer.writeStageIndicator()
125 except (ValueError, IOError, OSError), e:
126 logger.error("Error staging [%s]: %s" % (peer.name, e))
127 writeIndicatorFile(dailyDir, STAGE_INDICATOR, config.options.backupUser, config.options.backupGroup)
128 logger.info("Executed the 'stage' action successfully.")
129
130
131
132
133
134
135
136
137
138
140 """
141 Creates staging directories as required.
142
143 The main staging directory is the passed in daily directory, something like
144 C{staging/2002/05/23}. Then, individual peers get their own directories,
145 i.e. C{staging/2002/05/23/host}.
146
147 @param config: Config object.
148 @param dailyDir: Daily staging directory.
149 @param peers: List of all configured peers.
150
151 @return: Dictionary mapping peer name to staging directory.
152 """
153 mapping = {}
154 if os.path.isdir(dailyDir):
155 logger.warn("Staging directory [%s] already existed." % dailyDir)
156 else:
157 try:
158 logger.debug("Creating staging directory [%s]." % dailyDir)
159 os.makedirs(dailyDir)
160 for path in [ dailyDir, os.path.join(dailyDir, ".."), os.path.join(dailyDir, "..", ".."), ]:
161 changeOwnership(path, config.options.backupUser, config.options.backupGroup)
162 except Exception, e:
163 raise Exception("Unable to create staging directory: %s" % e)
164 for peer in peers:
165 peerDir = os.path.join(dailyDir, peer.name)
166 mapping[peer.name] = peerDir
167 if os.path.isdir(peerDir):
168 logger.warn("Peer staging directory [%s] already existed." % peerDir)
169 else:
170 try:
171 logger.debug("Creating peer staging directory [%s]." % peerDir)
172 os.makedirs(peerDir)
173 changeOwnership(peerDir, config.options.backupUser, config.options.backupGroup)
174 except Exception, e:
175 raise Exception("Unable to create staging directory: %s" % e)
176 return mapping
177
178
179
180
181
182
183
184
185
186
188 """
189 Gets the daily staging directory.
190
191 This is just a directory in the form C{staging/YYYY/MM/DD}, i.e.
192 C{staging/2000/10/07}, except it will be an absolute path based on
193 C{config.stage.targetDir}.
194
195 @param config: Config object
196
197 @return: Path of daily staging directory.
198 """
199 dailyDir = os.path.join(config.stage.targetDir, time.strftime(DIR_TIME_FORMAT))
200 logger.debug("Daily staging directory is [%s]." % dailyDir)
201 return dailyDir
202
203
204
205
206
207
221
222
223
224
225
226
243
244
245
246
247
248
250 """
251 Gets the remote user associated with a remote peer.
252 Use peer's if possible, otherwise take from options section.
253 @param config: Config object.
254 @param remotePeer: Configuration-style remote peer object.
255 @return: Name of remote user associated with remote peer.
256 """
257 if remotePeer.remoteUser is None:
258 return config.options.backupUser
259 return remotePeer.remoteUser
260
261
262
263
264
265
267 """
268 Gets the RCP command associated with a remote peer.
269 Use peer's if possible, otherwise take from options section.
270 @param config: Config object.
271 @param remotePeer: Configuration-style remote peer object.
272 @return: RCP command associated with remote peer.
273 """
274 if remotePeer.rcpCommand is None:
275 return config.options.rcpCommand
276 return remotePeer.rcpCommand
277