Mercurial > libervia-backend
comparison src/memory/disco.py @ 2148:a543eda2c923
core (memory/disco): getInfos now handle node + use client instead of profile in many methods
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 12 Feb 2017 20:43:20 +0100 |
parents | efe31f0881fb |
children | 91347fe95384 |
comparison
equal
deleted
inserted
replaced
2147:bca699faf416 | 2148:a543eda2c923 |
---|---|
102 """Load persistent hashes""" | 102 """Load persistent hashes""" |
103 self.hashes = HashManager(persistent.PersistentDict("disco")) | 103 self.hashes = HashManager(persistent.PersistentDict("disco")) |
104 return self.hashes.load() | 104 return self.hashes.load() |
105 | 105 |
106 @defer.inlineCallbacks | 106 @defer.inlineCallbacks |
107 def hasFeature(self, feature, jid_=None, profile=C.PROF_KEY_NONE): | 107 def hasFeature(self, client, feature, jid_=None, node=u''): |
108 """Tell if an entity has the required feature | 108 """Tell if an entity has the required feature |
109 | 109 |
110 @param feature: feature namespace | 110 @param feature: feature namespace |
111 @param jid_: jid of the target, or None for profile's server | 111 @param jid_: jid of the target, or None for profile's server |
112 @param profile: %(doc_profile)s | 112 @param node(unicode): optional node to use for disco request |
113 @return: a Deferred which fire a boolean (True if feature is available) | 113 @return: a Deferred which fire a boolean (True if feature is available) |
114 """ | 114 """ |
115 disco_infos = yield self.getInfos(jid_, profile) | 115 disco_infos = yield self.getInfos(client, jid_, node) |
116 defer.returnValue(feature in disco_infos.features) | 116 defer.returnValue(feature in disco_infos.features) |
117 | 117 |
118 @defer.inlineCallbacks | 118 @defer.inlineCallbacks |
119 def checkFeature(self, feature, jid_=None, profile=C.PROF_KEY_NONE): | 119 def checkFeature(self, client, feature, jid_=None, node=u''): |
120 """Like hasFeature, but raise an exception is feature is not Found | 120 """Like hasFeature, but raise an exception is feature is not Found |
121 | 121 |
122 @param feature: feature namespace | 122 @param feature: feature namespace |
123 @param jid_: jid of the target, or None for profile's server | 123 @param jid_: jid of the target, or None for profile's server |
124 @param profile: %(doc_profile)s | 124 @param node(unicode): optional node to use for disco request |
125 | 125 |
126 @raise: exceptions.FeatureNotFound | 126 @raise: exceptions.FeatureNotFound |
127 """ | 127 """ |
128 disco_infos = yield self.getInfos(jid_, profile) | 128 disco_infos = yield self.getInfos(client, jid_, node) |
129 if not feature in disco_infos.features: | 129 if not feature in disco_infos.features: |
130 raise failure.Failure(exceptions.FeatureNotFound) | 130 raise failure.Failure(exceptions.FeatureNotFound) |
131 | 131 |
132 @defer.inlineCallbacks | 132 @defer.inlineCallbacks |
133 def checkFeatures(self, features, jid_=None, identity=None, profile=C.PROF_KEY_NONE): | 133 def checkFeatures(self, client, features, jid_=None, identity=None, node=u''): |
134 """Like checkFeature, but check several features at once, and check also identity | 134 """Like checkFeature, but check several features at once, and check also identity |
135 | 135 |
136 @param features(iterable[unicode]): features to check | 136 @param features(iterable[unicode]): features to check |
137 @param jid_(jid.JID): jid of the target, or None for profile's server | 137 @param jid_(jid.JID): jid of the target, or None for profile's server |
138 @param node(unicode): optional node to use for disco request | |
138 @param identity(None, tuple(unicode, unicode): if not None, the entity must have an identity with this (category, type) tuple | 139 @param identity(None, tuple(unicode, unicode): if not None, the entity must have an identity with this (category, type) tuple |
139 @param profile: %(doc_profile)s | |
140 | 140 |
141 @raise: exceptions.FeatureNotFound | 141 @raise: exceptions.FeatureNotFound |
142 """ | 142 """ |
143 disco_infos = yield self.getInfos(jid_, profile) | 143 disco_infos = yield self.getInfos(client, jid_, node) |
144 if not set(features).issubset(disco_infos.features): | 144 if not set(features).issubset(disco_infos.features): |
145 raise failure.Failure(exceptions.FeatureNotFound()) | 145 raise failure.Failure(exceptions.FeatureNotFound()) |
146 | 146 |
147 if identity is not None and identity not in disco_infos.identities: | 147 if identity is not None and identity not in disco_infos.identities: |
148 raise failure.Failure(exceptions.FeatureNotFound()) | 148 raise failure.Failure(exceptions.FeatureNotFound()) |
149 | 149 |
150 def getInfos(self, jid_=None, profile=C.PROF_KEY_NONE): | 150 def getInfos(self, client, jid_=None, node=u''): |
151 """get disco infos from jid_, filling capability hash if needed | 151 """get disco infos from jid_, filling capability hash if needed |
152 | 152 |
153 @param jid_: jid of the target, or None for profile's server | 153 @param jid_: jid of the target, or None for profile's server |
154 @param profile: %(doc_profile)s | 154 @param node(unicode): optional node to use for disco request |
155 @return: a Deferred which fire disco.DiscoInfo | 155 @return: a Deferred which fire disco.DiscoInfo |
156 """ | 156 """ |
157 client = self.host.getClient(profile) | |
158 if jid_ is None: | 157 if jid_ is None: |
159 jid_ = jid.JID(client.jid.host) | 158 jid_ = jid.JID(client.jid.host) |
160 try: | 159 try: |
161 cap_hash = self.host.memory.getEntityData(jid_, [C.ENTITY_CAP_HASH], client.profile)[C.ENTITY_CAP_HASH] | 160 cap_hash = self.host.memory.getEntityData(jid_, [C.ENTITY_CAP_HASH], client.profile)[C.ENTITY_CAP_HASH] |
162 except (KeyError, exceptions.UnknownEntityError): | 161 except (KeyError, exceptions.UnknownEntityError): |
176 reason = unicode(fail) | 175 reason = unicode(fail) |
177 log.warning(u"Error while requesting disco infos from {jid}: {reason}".format(jid=jid_.full(), reason=reason)) | 176 log.warning(u"Error while requesting disco infos from {jid}: {reason}".format(jid=jid_.full(), reason=reason)) |
178 self.host.memory.updateEntityData(jid_, C.ENTITY_CAP_HASH, CAP_HASH_ERROR, profile_key=client.profile) | 177 self.host.memory.updateEntityData(jid_, C.ENTITY_CAP_HASH, CAP_HASH_ERROR, profile_key=client.profile) |
179 disco_infos = self.hashes[CAP_HASH_ERROR] | 178 disco_infos = self.hashes[CAP_HASH_ERROR] |
180 return disco_infos | 179 return disco_infos |
181 d = client.disco.requestInfo(jid_) | 180 d = client.disco.requestInfo(jid_, nodeIdentifier=node) |
182 d.addCallback(infosCb) | 181 d.addCallback(infosCb) |
183 d.addErrback(infosEb) | 182 d.addErrback(infosEb) |
184 return d | 183 return d |
185 else: | 184 else: |
186 disco_infos = self.hashes[cap_hash] | 185 disco_infos = self.hashes[cap_hash] |
187 return defer.succeed(disco_infos) | 186 return defer.succeed(disco_infos) |
188 | 187 |
189 @defer.inlineCallbacks | 188 @defer.inlineCallbacks |
190 def getItems(self, jid_=None, nodeIdentifier='', profile=C.PROF_KEY_NONE): | 189 def getItems(self, client, jid_=None, node=u''): |
191 """get disco items from jid_, cache them for our own server | 190 """get disco items from jid_, cache them for our own server |
192 | 191 |
193 @param jid_ (jid.JID): jid of the target, or None for profile's server | 192 @param jid_(jid.JID): jid of the target, or None for profile's server |
194 @param nodeIdentifier (str): optional node identifier | 193 @param node(unicode): optional node to use for disco request |
195 @param profile: %(doc_profile)s | |
196 @return: a Deferred which fire disco.DiscoItems | 194 @return: a Deferred which fire disco.DiscoItems |
197 """ | 195 """ |
198 client = self.host.getClient(profile) | |
199 if jid_ is None: | 196 if jid_ is None: |
200 jid_ = jid.JID(client.jid.host) | 197 jid_ = jid.JID(client.jid.host) |
201 # we cache items only for our own server | 198 # we cache items only for our own server |
202 try: | 199 try: |
203 items = self.host.memory.getEntityData(jid_, ["DISCO_ITEMS"], client.profile)["DISCO_ITEMS"] | 200 items = self.host.memory.getEntityData(jid_, ["DISCO_ITEMS"], client.profile)["DISCO_ITEMS"] |
204 log.debug(u"[%s] disco items are in cache" % jid_.full()) | 201 log.debug(u"[%s] disco items are in cache" % jid_.full()) |
205 except (KeyError, exceptions.UnknownEntityError): | 202 except (KeyError, exceptions.UnknownEntityError): |
206 log.debug(u"Caching [%s] disco items" % jid_.full()) | 203 log.debug(u"Caching [%s] disco items" % jid_.full()) |
207 items = yield client.disco.requestItems(jid_, nodeIdentifier) | 204 items = yield client.disco.requestItems(jid_, nodeIdentifier=node) |
208 self.host.memory.updateEntityData(jid_, "DISCO_ITEMS", items, profile_key=client.profile) | 205 self.host.memory.updateEntityData(jid_, "DISCO_ITEMS", items, profile_key=client.profile) |
209 else: | 206 else: |
210 try: | 207 try: |
211 items = yield client.disco.requestItems(jid_, nodeIdentifier) | 208 items = yield client.disco.requestItems(jid_, nodeIdentifier=node) |
212 except StanzaError as e: | 209 except StanzaError as e: |
213 log.warning(u"Error while requesting items for {jid}: {reason}" | 210 log.warning(u"Error while requesting items for {jid}: {reason}" |
214 .format(jid=jid_.full(), reason=e.condition)) | 211 .format(jid=jid_.full(), reason=e.condition)) |
215 items = disco.DiscoItems() | 212 items = disco.DiscoItems() |
216 | 213 |
220 def _infosEb(self, failure_, entity_jid): | 217 def _infosEb(self, failure_, entity_jid): |
221 failure_.trap(StanzaError) | 218 failure_.trap(StanzaError) |
222 log.warning(_(u"Error while requesting [%(jid)s]: %(error)s") % {'jid': entity_jid.full(), | 219 log.warning(_(u"Error while requesting [%(jid)s]: %(error)s") % {'jid': entity_jid.full(), |
223 'error': failure_.getErrorMessage()}) | 220 'error': failure_.getErrorMessage()}) |
224 | 221 |
225 def findServiceEntities(self, category, type_, jid_=None, profile=C.PROF_KEY_NONE): | 222 def findServiceEntities(self, client, category, type_, jid_=None): |
226 """Return all available items of an entity which correspond to (category, type_) | 223 """Return all available items of an entity which correspond to (category, type_) |
227 | 224 |
228 @param category: identity's category | 225 @param category: identity's category |
229 @param type_: identitiy's type | 226 @param type_: identitiy's type |
230 @param jid_: the jid of the target server (None for profile's server) | 227 @param jid_: the jid of the target server (None for profile's server) |
231 @param profile: %(doc_profile)s | |
232 @return: a set of found entities | 228 @return: a set of found entities |
233 @raise defer.CancelledError: the request timed out | 229 @raise defer.CancelledError: the request timed out |
234 """ | 230 """ |
235 found_entities = set() | 231 found_entities = set() |
236 | 232 |
239 found_entities.add(entity_jid) | 235 found_entities.add(entity_jid) |
240 | 236 |
241 def gotItems(items): | 237 def gotItems(items): |
242 defers_list = [] | 238 defers_list = [] |
243 for item in items: | 239 for item in items: |
244 info_d = self.getInfos(item.entity, profile) | 240 info_d = self.getInfos(client, item.entity) |
245 info_d.addCallbacks(infosCb, self._infosEb, [item.entity], None, [item.entity]) | 241 info_d.addCallbacks(infosCb, self._infosEb, [item.entity], None, [item.entity]) |
246 defers_list.append(info_d) | 242 defers_list.append(info_d) |
247 return defer.DeferredList(defers_list) | 243 return defer.DeferredList(defers_list) |
248 | 244 |
249 d = self.getItems(jid_, profile=profile) | 245 d = self.getItems(client, jid_) |
250 d.addCallback(gotItems) | 246 d.addCallback(gotItems) |
251 d.addCallback(lambda dummy: found_entities) | 247 d.addCallback(lambda dummy: found_entities) |
252 reactor.callLater(TIMEOUT, d.cancel) # FIXME: one bad service make a general timeout | 248 reactor.callLater(TIMEOUT, d.cancel) # FIXME: one bad service make a general timeout |
253 return d | 249 return d |
254 | 250 |
255 def findFeaturesSet(self, features, identity=None, jid_=None, profile=C.PROF_KEY_NONE): | 251 def findFeaturesSet(self, client, features, identity=None, jid_=None): |
256 """Return entities (including jid_ and its items) offering features | 252 """Return entities (including jid_ and its items) offering features |
257 | 253 |
258 @param features: iterable of features which must be present | 254 @param features: iterable of features which must be present |
259 @param identity(None, tuple(unicode, unicode)): if not None, accept only this (category/type) identity | 255 @param identity(None, tuple(unicode, unicode)): if not None, accept only this (category/type) identity |
260 @param jid_: the jid of the target server (None for profile's server) | 256 @param jid_: the jid of the target server (None for profile's server) |
261 @param profile: %(doc_profile)s | 257 @param profile: %(doc_profile)s |
262 @return: a set of found entities | 258 @return: a set of found entities |
263 """ | 259 """ |
264 client = self.host.getClient(profile) | |
265 if jid_ is None: | 260 if jid_ is None: |
266 jid_ = jid.JID(client.jid.host) | 261 jid_ = jid.JID(client.jid.host) |
267 features = set(features) | 262 features = set(features) |
268 found_entities = set() | 263 found_entities = set() |
269 | 264 |
274 found_entities.add(entity) | 269 found_entities.add(entity) |
275 | 270 |
276 def gotItems(items): | 271 def gotItems(items): |
277 defer_list = [] | 272 defer_list = [] |
278 for entity in [jid_] + [item.entity for item in items]: | 273 for entity in [jid_] + [item.entity for item in items]: |
279 infos_d = self.getInfos(entity, profile) | 274 infos_d = self.getInfos(client, entity) |
280 infos_d.addCallbacks(infosCb, self._infosEb, [entity], None, [entity]) | 275 infos_d.addCallbacks(infosCb, self._infosEb, [entity], None, [entity]) |
281 defer_list.append(infos_d) | 276 defer_list.append(infos_d) |
282 return defer.DeferredList(defer_list) | 277 return defer.DeferredList(defer_list) |
283 | 278 |
284 d = self.getItems(jid_, profile=profile) | 279 d = self.getItems(client, jid_) |
285 d.addCallback(gotItems) | 280 d.addCallback(gotItems) |
286 d.addCallback(lambda dummy: found_entities) | 281 d.addCallback(lambda dummy: found_entities) |
287 reactor.callLater(TIMEOUT, d.cancel) # FIXME: one bad service make a general timeout | 282 reactor.callLater(TIMEOUT, d.cancel) # FIXME: one bad service make a general timeout |
288 return d | 283 return d |
289 | 284 |
317 """ Discovery method for the bridge | 312 """ Discovery method for the bridge |
318 @param entity_jid_s: entity we want to discover | 313 @param entity_jid_s: entity we want to discover |
319 | 314 |
320 @return: list of tuples | 315 @return: list of tuples |
321 """ | 316 """ |
322 profile = self.host.memory.getProfileName(profile_key) | 317 client = self.host.getClient(profile_key) |
323 entity = jid.JID(entity_jid_s) | 318 entity = jid.JID(entity_jid_s) |
324 disco_infos = yield self.getInfos(entity, profile) | 319 disco_infos = yield self.getInfos(client, entity) |
325 extensions = {} | 320 extensions = {} |
326 for form_type, form in disco_infos.extensions.items(): | 321 for form_type, form in disco_infos.extensions.items(): |
327 fields = [] | 322 fields = [] |
328 for field in form.fieldList: | 323 for field in form.fieldList: |
329 data = {'type': field.fieldType} | 324 data = {'type': field.fieldType} |
339 | 334 |
340 defer.returnValue((disco_infos.features, | 335 defer.returnValue((disco_infos.features, |
341 [(cat, type_, name or '') for (cat, type_), name in disco_infos.identities.items()], | 336 [(cat, type_, name or '') for (cat, type_), name in disco_infos.identities.items()], |
342 extensions)) | 337 extensions)) |
343 | 338 |
339 def items2tuples(self, disco_items): | |
340 """convert disco items to tuple of strings | |
341 | |
342 @param disco_items(iterable[disco.DiscoItem]): items | |
343 @return G(tuple[unicode,unicode,unicode]): serialised items | |
344 """ | |
345 for item in disco_items: | |
346 if not item.entity: | |
347 log.warning(_(u"invalid item (no jid)")) | |
348 continue | |
349 yield (item.entity.full(), item.nodeIdentifier or '', item.name or '') | |
350 | |
344 @defer.inlineCallbacks | 351 @defer.inlineCallbacks |
345 def _discoItems(self, entity_jid_s, profile_key=C.PROF_KEY_NONE): | 352 def _discoItems(self, entity_jid_s, profile_key=C.PROF_KEY_NONE): |
346 """ Discovery method for the bridge | 353 """ Discovery method for the bridge |
354 | |
347 @param entity_jid_s: entity we want to discover | 355 @param entity_jid_s: entity we want to discover |
348 | |
349 @return: list of tuples""" | 356 @return: list of tuples""" |
350 profile = self.host.memory.getProfileName(profile_key) | 357 client = self.host.getClient(profile_key) |
351 entity = jid.JID(entity_jid_s) | 358 entity = jid.JID(entity_jid_s) |
352 disco_items = yield self.getItems(entity, profile=profile) | 359 disco_items = yield self.getItems(client, entity) |
353 try: | 360 ret = list(self.items2tuples(disco_items)) |
354 ret = [(item.entity.full(), item.nodeIdentifier or '', item.name or '') for item in disco_items] | |
355 except AttributeError: | |
356 log.error(u"error while getting items from {entity}, it may result from invalid item(s)".format(entity=entity.full())) | |
357 # second try by ignoring empty items | |
358 ret = [(item.entity.full(), item.nodeIdentifier or '', item.name or '') for item in disco_items if item.entity] | |
359 | |
360 defer.returnValue(ret) | 361 defer.returnValue(ret) |