1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """
23 common classes and code to support manager-side objects
24 """
25
26 from twisted.internet import reactor, defer
27 from twisted.spread import pb, flavors
28 from twisted.python import failure, reflect
29
30 from flumotion.common import errors, interfaces, log, common
31 from flumotion.twisted import pb as fpb
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
72 makeAvatarInitArgs = classmethod(makeAvatarInitArgs)
73
74 - def makeAvatar(klass, heaven, avatarId, remoteIdentity, mind):
75 log.debug('manager-avatar', 'making avatar with avatarId %s',
76 avatarId)
77 def have_args(args):
78 log.debug('manager-avatar', 'instantiating with args=%r', args)
79 return klass(*args)
80 d = klass.makeAvatarInitArgs(heaven, avatarId, remoteIdentity, mind)
81 d.addCallback(have_args)
82 return d
83 makeAvatar = classmethod(makeAvatar)
84
87
89 """
90 Call the given remote method, and log calling and returning nicely.
91
92 @param name: name of the remote method
93 @type name: str
94 """
95 level = log.DEBUG
96 if name == 'ping':
97 level = log.LOG
98
99 return self.mindCallRemoteLogging(level, -1, name, *args, **kwargs)
100
102 """
103 Get the IPv4 address of the machine the PB client is connecting from,
104 as seen from the avatar.
105
106 @returns: the IPv4 address the client is coming from, or None.
107 @rtype: str or None
108 """
109 if self.mind:
110 peer = self.mind.broker.transport.getPeer()
111 return peer.host
112
113 return None
114
117 """
118 Get a list of (bundleName, md5sum) of all dependency bundles,
119 starting with this bundle, in the correct order.
120 Any of bundleName, fileName, moduleName may be given.
121
122 @type bundleName: str or list of str
123 @param bundleName: the name of the bundle for fetching
124 @type fileName: str or list of str
125 @param fileName: the name of the file requested for fetching
126 @type moduleName: str or list of str
127 @param moduleName: the name of the module requested for import
128
129 @rtype: list of (str, str) tuples of (bundleName, md5sum)
130 """
131 bundleNames = []
132 fileNames = []
133 moduleNames = []
134 if bundleName:
135 if isinstance(bundleName, str):
136 bundleNames.append(bundleName)
137 else:
138 bundleNames.extend(bundleName)
139 self.debug('asked to get bundle sums for bundles %r' % bundleName)
140 if fileName:
141 if isinstance(fileName, str):
142 fileNames.append(fileName)
143 else:
144 fileNames.extend(fileName)
145 self.debug('asked to get bundle sums for files %r' % fileNames)
146 if moduleName:
147 if isinstance(moduleName, str):
148 moduleNames.append(moduleName)
149 else:
150 moduleNames.extend(moduleName)
151 self.debug('asked to get bundle sums for modules %r' % moduleNames)
152
153 basket = self.vishnu.getBundlerBasket()
154
155
156 for fileName in fileNames:
157 bundleName = basket.getBundlerNameByFile(fileName)
158 if not bundleName:
159 msg = 'containing ' + fileName
160 self.warning('No bundle %s' % msg)
161 raise errors.NoBundleError(msg)
162 else:
163 bundleNames.append(bundleName)
164
165 for moduleName in moduleNames:
166 bundleName = basket.getBundlerNameByImport(moduleName)
167 if not bundleName:
168 msg = 'for module ' + moduleName
169 self.warning('No bundle %s' % msg)
170 raise errors.NoBundleError(msg)
171 else:
172 bundleNames.append(bundleName)
173
174 deps = []
175 for bundleName in bundleNames:
176 thisdeps = basket.getDependencies(bundleName)
177 self.debug('dependencies of %s: %r' % (bundleName, thisdeps[1:]))
178 deps.extend(thisdeps)
179
180 sums = []
181 for dep in deps:
182 bundler = basket.getBundlerByName(dep)
183 if not bundler:
184 self.warning('Did not find bundle with name %s' % dep)
185 else:
186 sums.append((dep, bundler.bundle().md5sum))
187
188 self.debug('requested bundles: %r' % [x[0] for x in sums])
189 return sums
190
192 """
193 Get a list of (bundleName, md5sum) of all dependency bundles,
194 starting with this bundle, in the correct order.
195
196 @param filename: the name of the file in a bundle
197 @type filename: str
198
199 @returns: list of (bundleName, md5sum) tuples
200 @rtype: list of (str, str) tuples
201 """
202 self.debug('asked to get bundle sums for file %s' % filename)
203 basket = self.vishnu.getBundlerBasket()
204 bundleName = basket.getBundlerNameByFile(filename)
205 if not bundleName:
206 self.warning('Did not find a bundle for file %s' % filename)
207 raise errors.NoBundleError("for file %s" % filename)
208
209 return self.perspective_getBundleSums(bundleName)
210
212 """
213 Get the zip files for the given list of bundles.
214
215 @param bundles: the names of the bundles to get
216 @type bundles: list of str
217
218 @returns: dictionary of bundleName -> zipdata
219 @rtype: dict of str -> str
220 """
221 basket = self.vishnu.getBundlerBasket()
222 zips = {}
223 for name in bundles:
224 bundler = basket.getBundlerByName(name)
225 if not bundler:
226 raise errors.NoBundleError('The bundle named "%s" was not found'
227 % (name,))
228 zips[name] = bundler.bundle().getZip()
229 return zips
230
232 """
233 Authenticate the given keycard.
234 If no bouncerName given, authenticate against the manager's bouncer.
235 If a bouncerName is given, authenticate against the given bouncer
236 in the atmosphere.
237
238 @since: 0.3.1
239
240 @param bouncerName: the name of the atmosphere bouncer, or None
241 @type bouncerName: str or None
242 @param keycard: the keycard to authenticate
243 @type keycard: L{flumotion.common.keycards.Keycard}
244
245 @returns: a deferred, returning the keycard or None.
246 """
247 if not bouncerName:
248 self.debug(
249 'asked to authenticate keycard %r using manager bouncer' %
250 keycard)
251 return self.vishnu.bouncer.authenticate(keycard)
252
253 self.debug('asked to authenticate keycard %r using bouncer %s' % (
254 keycard, bouncerName))
255 avatarId = common.componentId('atmosphere', bouncerName)
256 if not self.heaven.hasAvatar(avatarId):
257 self.warning('No bouncer with id %s registered' % avatarId)
258 raise errors.UnknownComponentError(avatarId)
259
260 bouncerAvatar = self.heaven.getAvatar(avatarId)
261 return bouncerAvatar.authenticate(keycard)
262
264 """
265 Resets the expiry timeout for keycards issued by issuerName. See
266 L{flumotion.component.bouncers.bouncer} for more information.
267
268 @since: 0.4.3
269
270 @param bouncerName: the name of the atmosphere bouncer, or None
271 @type bouncerName: str or None
272 @param issuerName: the issuer for which keycards should be kept
273 alive; that is to say, keycards with the
274 attribute 'issuerName' set to this value will
275 have their ttl values reset.
276 @type issuerName: str
277 @param ttl: the new expiry timeout
278 @type ttl: number
279
280 @returns: a deferred which will fire success or failure.
281 """
282 self.debug('keycards keepAlive on behalf of %s, ttl=%d',
283 issuerName, ttl)
284
285 if not bouncerName:
286 return self.vishnu.bouncer.keepAlive(issuerName, ttl)
287
288 self.debug('looking for bouncer %s in atmosphere', bouncerName)
289 avatarId = common.componentId('atmosphere', bouncerName)
290 if not self.heaven.hasAvatar(avatarId):
291 self.warning('No bouncer with id %s registered', avatarId)
292 raise errors.UnknownComponentError(avatarId)
293
294 bouncerAvatar = self.heaven.getAvatar(avatarId)
295 return bouncerAvatar.keepAlive(issuerName, ttl)
296
298 """
299 Get the keycard classes the manager's bouncer can authenticate.
300
301 @since: 0.3.1
302
303 @returns: a deferred, returning a list of keycard class names
304 @rtype: L{twisted.internet.defer.Deferred} firing list of str
305 """
306 list = self.vishnu.bouncer.keycardClasses
307 return [reflect.qual(c) for c in list]
308
310 """
311 I am a base class for heavens in the manager.
312
313 @cvar avatarClass: the class object this heaven instantiates avatars from.
314 To be set in subclass.
315 @ivar avatars: a dict of avatarId -> Avatar
316 @type avatars: dict of str -> L{ManagerAvatar}
317 @ivar vishnu: the Vishnu in control of all the heavens
318 @type vishnu: L{flumotion.manager.manager.Vishnu}
319 """
320 avatarClass = None
321
323 """
324 @param vishnu: the Vishnu in control of all the heavens
325 @type vishnu: L{flumotion.manager.manager.Vishnu}
326 """
327 self.vishnu = vishnu
328 self.avatars = {}
329
330
331
333 """
334 Get the avatar with the given id.
335
336 @param avatarId: id of the avatar to get
337 @type avatarId: str
338
339 @returns: the avatar with the given id
340 @rtype: L{ManagerAvatar}
341 """
342 return self.avatars[avatarId]
343
345 """
346 Check if a component with that name is registered.
347
348 @param avatarId: id of the avatar to check
349 @type avatarId: str
350
351 @returns: True if an avatar with that id is registered
352 @rtype: bool
353 """
354 return self.avatars.has_key(avatarId)
355
357 """
358 Get all avatars in this heaven.
359
360 @returns: a list of all avatars in this heaven
361 @rtype: list of L{ManagerAvatar}
362 """
363 return self.avatars.values()
364