1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 """
19 common classes and code to support manager-side objects
20 """
21
22 from twisted.internet import reactor, defer
23 from twisted.spread import pb
24 from twisted.python import reflect
25
26 from flumotion.common import errors, log, common
27 from flumotion.common.planet import moods
28 from flumotion.twisted import pb as fpb
29
30 __version__ = "$Rev$"
31
32
34 """
35 I am a base class for manager-side avatars to subclass from.
36
37 @ivar avatarId: the id for this avatar, unique inside the heaven
38 @type avatarId: str
39 @ivar heaven: the heaven this avatar is part of
40 @type heaven: L{flumotion.manager.base.ManagerHeaven}
41 @ivar mind: a remote reference to the client-side Medium
42 @type mind: L{twisted.spread.pb.RemoteReference}
43 @ivar vishnu: the vishnu that manages this avatar's heaven
44 @type vishnu: L{flumotion.manager.manager.Vishnu}
45 """
46 remoteLogName = 'medium'
47 logCategory = 'manager-avatar'
48
49 - def __init__(self, heaven, avatarId, remoteIdentity, mind):
50 """
51 @param heaven: the heaven this avatar is part of
52 @type heaven: L{flumotion.manager.base.ManagerHeaven}
53 @param avatarId: id of the avatar to create
54 @type avatarId: str
55 @param remoteIdentity: manager-assigned identity object for this
56 avatar
57 @type remoteIdentity: L{flumotion.common.identity.RemoteIdentity}
58 @param mind: a remote reference to the client-side Medium
59 @type mind: L{twisted.spread.pb.RemoteReference}
60 """
61 fpb.PingableAvatar.__init__(self, avatarId)
62 self.heaven = heaven
63 self.logName = avatarId
64 self.setMind(mind)
65 self.vishnu = heaven.vishnu
66 self.remoteIdentity = remoteIdentity
67
68 self.debug("created new Avatar with id %s", avatarId)
69
71 """
72 Sets a marker that will be prefixed to the log strings. Setting this
73 marker to multiple elements at a time helps debugging.
74 @param marker: A string to prefix all the log strings.
75 @param level: The log level. It can be log.ERROR, log.DEBUG,
76 log.WARN, log.INFO or log.LOG
77 """
78
79 self.writeMarker(marker, level)
80 workers = self.vishnu.workerHeaven.state.get('names')
81 componentStates = self.vishnu.getComponentStates()
82 for worker in workers:
83 self.perspective_workerCallRemote(worker, 'writeFluDebugMarker',
84 level, marker)
85 for componentState in componentStates:
86 m = self.vishnu.getComponentMapper(componentState)
87 if m.avatar:
88 self.perspective_componentCallRemote(componentState,
89 'writeFluDebugMarker',
90 level, marker)
91
94 makeAvatarInitArgs = classmethod(makeAvatarInitArgs)
95
96 - def makeAvatar(klass, heaven, avatarId, remoteIdentity, mind):
97 log.debug('manager-avatar', 'making avatar with avatarId %s',
98 avatarId)
99
100 def have_args(args):
101 log.debug('manager-avatar', 'instantiating with args=%r', args)
102 return klass(*args)
103 d = klass.makeAvatarInitArgs(heaven, avatarId, remoteIdentity, mind)
104 d.addCallback(have_args)
105 return d
106 makeAvatar = classmethod(makeAvatar)
107
110
112 """
113 Call the given remote method, and log calling and returning nicely.
114
115 @param name: name of the remote method
116 @type name: str
117 """
118 level = log.DEBUG
119 if name == 'ping':
120 level = log.LOG
121
122 return self.mindCallRemoteLogging(level, -1, name, *args, **kwargs)
123
125 """
126 Get the IPv4 address of the machine the PB client is connecting from,
127 as seen from the avatar.
128
129 @returns: the IPv4 address the client is coming from, or None.
130 @rtype: str or None
131 """
132 if self.mind:
133 peer = self.mind.broker.transport.getPeer()
134 return peer.host
135
136 return None
137
140 """
141 Get a list of (bundleName, md5sum) of all dependency bundles,
142 starting with this bundle, in the correct order.
143 Any of bundleName, fileName, moduleName may be given.
144
145 @type bundleName: str or list of str
146 @param bundleName: the name of the bundle for fetching
147 @type fileName: str or list of str
148 @param fileName: the name of the file requested for fetching
149 @type moduleName: str or list of str
150 @param moduleName: the name of the module requested for import
151
152 @rtype: list of (str, str) tuples of (bundleName, md5sum)
153 """
154 bundleNames = []
155 fileNames = []
156 moduleNames = []
157 if bundleName:
158 if isinstance(bundleName, str):
159 bundleNames.append(bundleName)
160 else:
161 bundleNames.extend(bundleName)
162 self.debug('asked to get bundle sums for bundles %r' % bundleName)
163 if fileName:
164 if isinstance(fileName, str):
165 fileNames.append(fileName)
166 else:
167 fileNames.extend(fileName)
168 self.debug('asked to get bundle sums for files %r' % fileNames)
169 if moduleName:
170 if isinstance(moduleName, str):
171 moduleNames.append(moduleName)
172 else:
173 moduleNames.extend(moduleName)
174 self.debug('asked to get bundle sums for modules %r' % moduleNames)
175
176 basket = self.vishnu.getBundlerBasket()
177
178
179 for fileName in fileNames:
180 bundleName = basket.getBundlerNameByFile(fileName)
181 if not bundleName:
182 msg = 'containing ' + fileName
183 self.warning('No bundle %s' % msg)
184 raise errors.NoBundleError(msg)
185 else:
186 bundleNames.append(bundleName)
187
188 for moduleName in moduleNames:
189 bundleName = basket.getBundlerNameByImport(moduleName)
190 if not bundleName:
191 msg = 'for module ' + moduleName
192 self.warning('No bundle %s' % msg)
193 raise errors.NoBundleError(msg)
194 else:
195 bundleNames.append(bundleName)
196
197 deps = []
198 for bundleName in bundleNames:
199 thisdeps = basket.getDependencies(bundleName)
200 self.debug('dependencies of %s: %r' % (bundleName, thisdeps[1:]))
201 deps.extend(thisdeps)
202
203 sums = []
204 for dep in deps:
205 bundler = basket.getBundlerByName(dep)
206 if not bundler:
207 self.warning('Did not find bundle with name %s' % dep)
208 else:
209 sums.append((dep, bundler.bundle().md5sum))
210
211 self.debug('requested bundles: %r' % [x[0] for x in sums])
212 return sums
213
215 """
216 Get a list of (bundleName, md5sum) of all dependency bundles,
217 starting with this bundle, in the correct order.
218
219 @param filename: the name of the file in a bundle
220 @type filename: str
221
222 @returns: list of (bundleName, md5sum) tuples
223 @rtype: list of (str, str) tuples
224 """
225 self.debug('asked to get bundle sums for file %s' % filename)
226 basket = self.vishnu.getBundlerBasket()
227 bundleName = basket.getBundlerNameByFile(filename)
228 if not bundleName:
229 self.warning('Did not find a bundle for file %s' % filename)
230 raise errors.NoBundleError("for file %s" % filename)
231
232 return self.perspective_getBundleSums(bundleName)
233
235 """
236 Get the zip files for the given list of bundles.
237
238 @param bundles: the names of the bundles to get
239 @type bundles: list of str
240
241 @returns: dictionary of bundleName -> zipdata
242 @rtype: dict of str -> str
243 """
244 basket = self.vishnu.getBundlerBasket()
245 zips = {}
246 for name in bundles:
247 bundler = basket.getBundlerByName(name)
248 if not bundler:
249 raise errors.NoBundleError(
250 'The bundle named "%s" was not found' % (name, ))
251 zips[name] = bundler.bundle().getZip()
252 return zips
253
255 """
256 Authenticate the given keycard.
257 If no bouncerName given, authenticate against the manager's bouncer.
258 If a bouncerName is given, authenticate against the given bouncer
259 in the atmosphere.
260
261 @since: 0.3.1
262
263 @param bouncerName: the name of the atmosphere bouncer, or None
264 @type bouncerName: str or None
265 @param keycard: the keycard to authenticate
266 @type keycard: L{flumotion.common.keycards.Keycard}
267
268 @returns: a deferred, returning the keycard or None.
269 """
270 if not bouncerName:
271 self.debug(
272 'asked to authenticate keycard %r using manager bouncer' %
273 keycard)
274 return self.vishnu.bouncer.authenticate(keycard)
275
276 self.debug('asked to authenticate keycard %r using bouncer %s' % (
277 keycard, bouncerName))
278 avatarId = common.componentId('atmosphere', bouncerName)
279 if not self.heaven.hasAvatar(avatarId):
280 self.warning('No bouncer with id %s registered' % avatarId)
281 raise errors.UnknownComponentError(avatarId)
282
283 bouncerAvatar = self.heaven.getAvatar(avatarId)
284 return bouncerAvatar.authenticate(keycard)
285
287 """
288 Resets the expiry timeout for keycards issued by issuerName. See
289 L{flumotion.component.bouncers.bouncer} for more information.
290
291 @since: 0.4.3
292
293 @param bouncerName: the name of the atmosphere bouncer, or None
294 @type bouncerName: str or None
295 @param issuerName: the issuer for which keycards should be kept
296 alive; that is to say, keycards with the
297 attribute 'issuerName' set to this value will
298 have their ttl values reset.
299 @type issuerName: str
300 @param ttl: the new expiry timeout
301 @type ttl: number
302
303 @returns: a deferred which will fire success or failure.
304 """
305 self.debug('keycards keepAlive on behalf of %s, ttl=%d',
306 issuerName, ttl)
307
308 if not bouncerName:
309 return self.vishnu.bouncer.keepAlive(issuerName, ttl)
310
311 self.debug('looking for bouncer %s in atmosphere', bouncerName)
312 avatarId = common.componentId('atmosphere', bouncerName)
313 if not self.heaven.hasAvatar(avatarId):
314 self.warning('No bouncer with id %s registered', avatarId)
315 raise errors.UnknownComponentError(avatarId)
316
317 bouncerAvatar = self.heaven.getAvatar(avatarId)
318 return bouncerAvatar.keepAlive(issuerName, ttl)
319
321 """
322 Get the keycard classes the manager's bouncer can authenticate.
323
324 @since: 0.3.1
325
326 @returns: a deferred, returning a list of keycard class names
327 @rtype: L{twisted.internet.defer.Deferred} firing list of str
328 """
329 classes = self.vishnu.bouncer.keycardClasses
330 return [reflect.qual(c) for c in classes]
331
332
334 """
335 I am a base class for heavens in the manager.
336
337 @cvar avatarClass: the class object this heaven instantiates avatars from.
338 To be set in subclass.
339 @ivar avatars: a dict of avatarId -> Avatar
340 @type avatars: dict of str -> L{ManagerAvatar}
341 @ivar vishnu: the Vishnu in control of all the heavens
342 @type vishnu: L{flumotion.manager.manager.Vishnu}
343 """
344 avatarClass = None
345
347 """
348 @param vishnu: the Vishnu in control of all the heavens
349 @type vishnu: L{flumotion.manager.manager.Vishnu}
350 """
351 self.vishnu = vishnu
352 self.avatars = {}
353
354
355
356
358 """
359 Get the avatar with the given id.
360
361 @param avatarId: id of the avatar to get
362 @type avatarId: str
363
364 @returns: the avatar with the given id
365 @rtype: L{ManagerAvatar}
366 """
367 return self.avatars[avatarId]
368
370 """
371 Check if a component with that name is registered.
372
373 @param avatarId: id of the avatar to check
374 @type avatarId: str
375
376 @returns: True if an avatar with that id is registered
377 @rtype: bool
378 """
379 return avatarId in self.avatars
380
382 """
383 Get all avatars in this heaven.
384
385 @returns: a list of all avatars in this heaven
386 @rtype: list of L{ManagerAvatar}
387 """
388 return self.avatars.values()
389