def _get_attribute_metadata(self, path): if not path.endswith(":meta"): path = quote(unquote(path) + ":meta") _content_instance, data = list(self.get_content_instances(path))[0] return (data["type"], data["isDomain"], self._parse_metadata(data["metadata"]))
def _create_subscriptions_db_container(self): containers_path = "/applications/" + quote( self.internal_app_id) + "/containers" try: self.scl.create(containers_path, Container(id=self.prefix + "subscription_db")) except SCLNotFound: self.scl.create("/applications", Application(appId=self.internal_app_id)) self._create_subscriptions_db_container()
def _content_instances_path(self): return ("/applications/" + quote(self.internal_app_id) + "/containers/" + self.prefix + "subscription_db/contentInstances")
def discoverContextAvailability(self, discoverContextAvailabilityRequest): relevant_apps = self._get_relevant_apps( discoverContextAvailabilityRequest) result_apps = set() attributes = [] cache = {} for entity_type, entity_id, path in relevant_apps: containers_path = path + "/containers" try: containers = cache[containers_path] except KeyError: containers = cache[containers_path] = self.scl.retrieve( containers_path).resource for reference in containers.containerCollection: if (not unquote(reference.id).endswith(":meta") and (not discoverContextAvailabilityRequest.attributeList or reference.id in discoverContextAvailabilityRequest.attributeList)): result_apps.add((entity_type, entity_id)) try: metadata = cache[reference.id] except KeyError: try: #ci_path = unquote(reference["$t"]) + ":meta/contentInstances" ci_path = unquote( containers_path + '/' + reference.id) + ":meta/contentInstances" meta_content_instances = self.scl.retrieve( quote(ci_path)).resource b64json = meta_content_instances.contentInstanceCollection[ 0].content["$t"] metadata = cache[reference.id] = loads( b64decode(b64json)) except SCLNotFound: metadata = cache[reference.id] = { "type": "Generic", "isDomain": False, "metadata": [] } attributes.append((reference.id, metadata)) if not result_apps: return DiscoverContextAvailabilityResponse(errorCode=404) entity_ids = [ EntityId(type=entity_type, id=entity_id, isPattern=False) for entity_type, entity_id in result_apps ] context_attributes = [] for name, metadata in attributes: context_attributes.append( ContextRegistrationAttribute(name=name, type=metadata["type"], isDomain=metadata["isDomain"], metadata=[ ContextMetadata( type=md["type"], name=md["name"], value=md["value"]) for md in metadata["metadata"] ])) return DiscoverContextAvailabilityResponse( errorCode=200, contextRegistrationResponseList=[ ContextRegistrationResponse( contextRegistration=ContextRegistration( entityIdList=entity_ids, contextRegistrationAttributeList=context_attributes)) ])
def registerContext(self, registerContextRequest): registrationId = ''.join([choice(digits) for _ in range(16)]) for contextRegistration in registerContextRequest.contextRegistrationList: for entityId in contextRegistration.entityIdList: #app_id = entityId.type #app_id = app_id and quote(app_id) or self.default_entity_type #app_id += "." + quote(entityId.id) #app_id = self.id_prefix + app_id app_id = quote(entityId.id) app = Application(appId=app_id) try: scl_app_id = self.scl.create("/applications", app).resourceURI scl_containers_id = scl_app_id + "/containers" if contextRegistration.contextRegistrationAttributeList: sleep(1) try: for contextRegistrationAttribute in contextRegistration.contextRegistrationAttributeList: container_id = quote( contextRegistrationAttribute.name) container = Container(id=container_id) meta_container = Container(id=container_id + ".meta") self.scl.create(scl_containers_id, container) scl_meta_container_id = self.scl.create( scl_containers_id, meta_container).resourceURI metadata = { "type": contextRegistrationAttribute.type, "isDomain": contextRegistrationAttribute.isDomain, "metadata": self._marshal_metadata( contextRegistrationAttribute) } self._create_content_instance( scl_meta_container_id + "/contentInstances", metadata), sleep( len(contextRegistration. contextRegistrationAttributeList)) except OpenMTCError: #self.scl.delete(scl_app_id) return False raise except OpenMTCError as e: return False raise NGSIError(e) response = RegisterContextResponse(errorCode=200, duration=3600 * 24, registrationId=registrationId) response._m2m_path = scl_app_id return response
class NGSIInterface(LoggerMixin): content_instance_content_type = "application/openmtc-fiware-iot+json" id_prefix = "IoT" internal_app_id = id_prefix + "openmtc-ngsi" _internal_app_id_quoted = quote(internal_app_id) default_entity_type = "Generic" _last_notification = None def get_cfg(self, config, param): if param in config: return config[param] return None def __init__(self, scl=None, scl_uri=None, notify_uri=None, config=None, db=None, send_rq=None, *args, **kw): super(NGSIInterface, self).__init__(*args, **kw) self.notify_uri = notify_uri try: self.client_secret = config["client_secret"] self.client_id = config["client_id"] self.username = config["username"] self.password = config["password"] scl_uri = self.get_cfg(config, "scl") or scl_uri or "http://localhost:5001" self.logger.info("Using %s as scl URI." % (scl_uri)) except TypeError: try: config = open(config or "../config.cfg") cfg = json.load(config) self.client_secret = cfg["client_secret"] self.client_id = cfg["client_id"] self.username = cfg["username"] self.password = cfg["password"] scl_uri = self.get_cfg(cfg, "scl") or "http://localhost:5001" self.logger.info("Using %s as scl URI." % (scl_uri)) except Exception as e: self.logger.error("Cannot load configuration file: %s" % (e, )) raise self.access_token = None if scl is None: scl = MIDClient(scl_uri) self.scl = scl if send_rq is not None: self._send_req = send_rq self.db = db or SCLDb(self.scl, self.id_prefix, self, self.name) self.xml_writer = NGSIXMLWriter() self.json_writer = NGSIJSONWriter() self._init_subscriptions() def _init_subscriptions(self): self._check_subscriptions("/applications") apps = self.scl.retrieve("/applications").resource for app in apps.applicationCollection: if not self.is_internal_app(app.appId): #self._check_subscriptions(app["$t"] + "/containers") self._check_subscriptions("/applications/" + app.appId + "/containers") def _check_subscriptions(self, path): path += "/subscriptions" subscriptions = self.scl.retrieve(path).resource for subscription in subscriptions.subscriptionCollection: p = path + "/" + subscription.id subscription = self.scl.retrieve(p).resource if subscription.contact == self.notify_uri: return self._subscribe(path) def _subscribe(self, path): if not path.endswith("/subscriptions"): path += "/subscriptions" self.scl.create(path, Subscription(contact=self.notify_uri)) def is_internal_app(self, app_id): return app_id in (self._internal_app_id_quoted, self.internal_app_id) def split_app_id(self, app_id): app_id = unquote(app_id) entity_type, entity_id = "Generic", app_id if app_id.startswith(self.id_prefix): app_id = app_id[len(self.id_prefix):] try: entity_type, entity_id = app_id.split(".") except ValueError: pass return entity_type, entity_id def _get_appdata(self): scl_applications = self.scl.retrieve("/applications").resource app_data = [] for scl_app_reference in scl_applications.applicationCollection: if self.is_internal_app(scl_app_reference.appId): continue entity_type, entity_id = self.split_app_id(scl_app_reference.appId) app_data.append((entity_type, entity_id, '/applications/' + scl_app_reference.appId)) return app_data def _get_relevant_apps(self, request): return self._get_relevant_apps_dict(request).values() def _get_relevant_apps_dict(self, request): if not request.entityIdList: raise InvalidRequest("entityIdList missing in request.") app_data = self._get_appdata() relevant_apps = {} for entityId in request.entityIdList: for entity_type, entity_id, path in app_data: if not entityId.type or entityId.type == entity_type: if entityId.isPattern: if re.match(entityId.id, entity_id): relevant_apps[entityId] = (entity_type, entity_id, path) elif not entityId.id or entityId.id == entity_id: relevant_apps[entityId] = (entity_type, entity_id, path) return relevant_apps def id_matches(self, entity_type, is_pattern, entity_id, app_type, app_id): if entity_type and entity_type != app_type: return False if is_pattern: return bool(re.match(entity_id, app_id)) return not entity_id or entity_id == app_id def _get_relevant_app(self, entityId, app_data): for entity_type, entity_id, path in app_data: self.logger.debug("---- check %s %s", entity_type, entity_id) if entityId.id == entity_id: # and (not entityId.type or entityId.type == entity_type) return entity_type, entity_id, path def _create_content_instance(self, path, content): content_instance_data = { "content": { "contentType": self.content_instance_content_type, "$t": b64encode(dumps(content)) } } from openmtc.model import ContentInstance ci = ContentInstance( content={ "contentType": self.content_instance_content_type, "$t": b64encode(dumps(content)) }) request_indication = CreateRequestIndication(path, ci, "contentInstance") return self.scl.send_request_indication(request_indication) create_content_instance = _create_content_instance def _marshal_metadata(self, entity): if not entity.metadata: return [] return [{ "name": md.name or "", "type": md.type or "", "value": md.value or "" } for md in entity.metadata] def _parse_metadata(self, meta_data): return [ ContextMetadata(name=md["name"], type=md["type"], value=md["value"]) for md in meta_data ] def _get_latest_instance(self, content_instances): latest = content_instances["latest"] latest_id = latest["id"] for content_instance in content_instances["contentInstanceCollection"][ "contentInstance"]: if latest_id == content_instance["id"]: return content_instance raise Exception("Latest instance (%s) not found in collection." % (latest, )) def get_content_instances(self, path): if not path.endswith("/contentInstances"): path += "/contentInstances" content_instances = self.scl.retrieve(path).resource for content_instance in content_instances.contentInstanceCollection: try: data = content_instance.content["$t"] except KeyError: data = content_instance.content["binaryContent"] try: data = b64decode(data) data = loads(data) except: self.logger.exception("Could not decode content: %s" % (data, )) yield (content_instance, data) def _get_access_token(self, url): """Returns the current access token. Generates a new access token if no current access token can be found""" if self.access_token: return self.access_token data = "client_id=%s&client_secret=%s&grant_type=password&username=%s&password=%s&scope=write" %\ (self.client_id, self.client_secret, self.username, self.password) parsed = urlparse(url) path = urlunparse( ParseResult(parsed.scheme, parsed.netloc, "/oauth2/access_token", None, None, None)) auth_resp = urlopen(Request(path, data), timeout=10) if auth_resp.getcode() != 200: self.logger.error("Error with client credentials") return self.access_token auth_resp_data = json.loads(auth_resp.read()) if "access_token" in auth_resp_data: self.access_token = auth_resp_data["access_token"] else: self.logger.error("Error with client credentials") return self.access_token def _send_req(self, notify_request, content_type, subscription): """Sends a notify request to the Event Service with a given access token. If the access token has expired, this function calls _get_access_token that generates a new one""" if content_type == "json": feed = urlopen(Request( subscription["reference"], self.json_writer.serialize(notify_request), { "Content-Type": "application/json", "Authorization": "OAuth " + self._get_access_token(subscription["reference"]) }), timeout=10) if feed.getcode() == 401: # Access_token has expired self.access_token = None feed = urlopen(Request( subscription["reference"], self.json_writer.serialize(notify_request), { "Content-Type": "application/json", "Authorization": "OAuth " + self._get_access_token(subscription["reference"]) }), timeout=10) else: feed = urlopen(Request(subscription["reference"], self.xml_writer.serialize(notify_request), {"Content-Type": "application/xml"}), timeout=10) return feed def _send_notifications(self, subscriptions, notify_request): """Pushes content to the event service. First tries to update the event, if it is impossible, this function creates a new event (APPEND)""" for subscription in subscriptions: if self.logger.isEnabledFor(DEBUG): self.logger.debug("Sending notification: %s", self.xml_writer.serialize(notify_request)) else: print "Sending notification: %s" % self.xml_writer.serialize( notify_request) try: notify_request.subscriptionId = subscription["subscriptionId"] except AttributeError: # req is an update try: feed = self._send_req(notify_request, "json", subscription) except Exception as e: self.logger.error("Failed to send notification to %s: %s", subscription["reference"], e) try: repload = json.loads(feed.read()) respcode = int( repload["contextResponses"][0]["statusCode"]["code"]) if respcode == 472 or respcode == 404: self.logger.info( "Update notification failed, trying append") notify_request.updateAction = "APPEND" self._send_req(notify_request, "json", subscription) except Exception as e: self.logger.error("Failed to send notification to %s: %s", subscription["reference"], e) else: try: self._send_req(notify_request, "xml", subscription) except Exception as e: self.logger.error("Failed to send notification to %s: %s", subscription["reference"], e) def handle_notification(self, type, notification): self._last_notification = (type, notification) if type not in ("applications", "containers"): raise InvalidRequest(type) singular = type[:-1] collection = notification[singular + "Collection"]["namedReference"] try: latest = collection[-1] except IndexError: return False if ((type == "applications" and self.is_internal_app(latest["id"])) or (type == "containers" and unquote(latest["id"]).endswith(":meta"))): return False getattr(self, "_handle_" + singular)(latest, collection) return True def _handle_application(self, latest_app, collection): self._subscribe(latest_app["$t"] + "/containers") app_type = "" app_id = latest_app["id"] subs = self.db.find_app_subscriptions(app_type, app_id) # TODO send notifications # TODO build cr cr = ContextRegistration( entityIdList=[EntityId(type=app_type, id=app_id, isPattern=False)], contextRegistrationAttributeList=[], #ContextRegistrationAttribute], registrationMetaData=[], providingApplication="eu.fistar.sdcs") crr = ContextRegistrationResponse(contextRegistration=cr) notify_request = NotifyContextAvailabilityRequest( contextRegistrationResponseList=[crr], errorCode=StatusCode(200)) self._send_notifications(subs, notify_request) # TODO alternative: send a RegisterContextRequest? # register_request = RegisterContextRequest(contextRegistrationList=[ cr ]) # self._send_notifications(subs, register_request) def _handle_container(self, container, collection): pass