reqrites Orion CB AE to use new ResourceManagementXAE class

This commit is contained in:
Ronald Steinke 2018-01-10 13:54:52 +01:00
parent febb78d13c
commit d3f33aa153
2 changed files with 80 additions and 216 deletions

View File

@ -1,69 +1,48 @@
""" try:
Copyright (c) 2017 Fraunhofer FOKUS from urllib.parse import urljoin
Licensed under the Apache License, Version 2.0 (the "License"); except ImportError:
you may not use this file except in compliance with the License. from urlparse import urljoin
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import json
from urlparse import urljoin
import requests import requests
from copy import deepcopy as deepcopy
from futile.logging import get_logger from futile.logging import LoggerMixin
logger = get_logger(__name__)
# TODO: check if this is sufficient class OrionAPI(LoggerMixin):
def _isSenML(senml_dict):
is_senml = True
try:
is_senml = is_senml and ("bn") in senml_dict.keys()
is_senml = is_senml and ("n") in senml_dict.keys()
is_senml = is_senml and ("u") in senml_dict.keys()
is_senml = is_senml and ("v") in senml_dict.keys()
is_senml = is_senml and ("t") in senml_dict.keys()
except BaseException:
return False
return is_senml
def _get_type(element):
if isinstance(element, int):
return u"Int"
elif isinstance(element, float):
return u"Float"
elif isinstance(element, bool):
return u"Boolean"
elif isinstance(element, (str, unicode)):
return u"String"
else:
logger.error("Type of \"element\" unknown")
return "Unknown"
class OrionAPI:
def __init__(self, orion_host=None, api_version="v2"): def __init__(self, orion_host=None, api_version="v2"):
super(OrionAPI, self).__init__()
self.host = orion_host self.host = orion_host
self.version = api_version self.version = api_version
def create_entity(self, entitiy_name, entity_type="openmtc", # TODO: check if this is sufficient
fiware_service=""): @staticmethod
def _is_senml(senml_dict):
try:
return (all(key in senml_dict for key in ('bn', 'n', 'u', 't')) and
any(key in senml_dict for key in ('v', 'vs', 'vb')))
except TypeError:
return False
payload_json = {"id": entitiy_name, "type": entity_type} def _get_type(self, element):
if isinstance(element, int):
return u"Int"
elif isinstance(element, float):
return u"Float"
elif isinstance(element, bool):
return u"Boolean"
elif isinstance(element, (str, unicode)):
return u"String"
else:
self.logger.error("Type of \"element\" unknown")
return "Unknown"
def create_entity(self, entity_name, entity_type="openmtc", fiware_service=""):
payload_json = {"id": entity_name, "type": entity_type}
if self.version == "v2": if self.version == "v2":
logger.debug("Send Payload to Orion CB: %s", str(payload_json)) self.logger.debug("Send Payload to Orion CB: %s", str(payload_json))
response = self._request( self._request(
self.host + "/v2/entities", self.host + "/v2/entities",
method="post", method="post",
json=payload_json, json=payload_json,
@ -72,25 +51,24 @@ class OrionAPI:
"Content-type": "application/json", "Content-type": "application/json",
"fiware-service": fiware_service} "fiware-service": fiware_service}
) )
logger.debug("Send Payload to Orion CB: %s", str(payload_json)) self.logger.debug("Send Payload to Orion CB: %s", str(payload_json))
else: else:
logger.error("API version \"%s\" not supported!", self.version) self.logger.error("API version \"%s\" not supported!", self.version)
def update_attributes(self, entity_id, data_senml, fiware_service=""): def update_attributes(self, entity_id, data_senml, fiware_service=""):
data_senml = data_senml[0] if not self._is_senml(data_senml):
if not _isSenML(data_senml): self.logger.error("Data \"%s\" is not valid SenML", data_senml)
logger.error("Data \"%s\" is not valid SenML", data_senml)
return return
if data_senml["v"] == "type" or data_senml["v"] == "id": if data_senml["v"] == "type" or data_senml["v"] == "id":
logger.warn( self.logger.warn(
"SenML[v]=%s contains reserved name. Adding underscore", data_senml["v"]) "SenML[v]=%s contains reserved name. Adding underscore", data_senml["v"])
data_senml["v"] = data_senml["v"] + "_" data_senml["v"] = data_senml["v"] + "_"
payload_json = { payload_json = {
data_senml["n"]: { data_senml["n"]: {
"value": data_senml["v"], "value": data_senml["v"],
"type": _get_type(data_senml["v"]), "type": self._get_type(data_senml["v"]),
"metadata": { "metadata": {
"timestamp": {"value": data_senml["t"], "type": "String"}, "timestamp": {"value": data_senml["t"], "type": "String"},
"bn": {"value": data_senml["bn"], "type": "String"}, "bn": {"value": data_senml["bn"], "type": "String"},
@ -100,7 +78,7 @@ class OrionAPI:
} }
if self.version == "v2": if self.version == "v2":
response = self._request( self._request(
self.host + "/v2/entities/" + entity_id + "/attrs", self.host + "/v2/entities/" + entity_id + "/attrs",
method="post", method="post",
json=payload_json, json=payload_json,
@ -109,29 +87,21 @@ class OrionAPI:
"Content-type": "application/json", "Content-type": "application/json",
"fiware-service": fiware_service} "fiware-service": fiware_service}
) )
logger.debug("Send Payload to Orion CB: %s", str(payload_json)) self.logger.debug("Send Payload to Orion CB: %s", str(payload_json))
else: else:
logger.error("API version \"%s\" not supported!", self.version) self.logger.error("API version \"%s\" not supported!", self.version)
def _request(
self,
url,
method='get',
json=None,
params=None,
headers=None,
raw=False):
def _request(self, url, method='get', json=None, params=None, headers=None, raw=False):
joined_url = urljoin(self.host, url) joined_url = urljoin(self.host, url)
try: try:
req = requests.request(method, joined_url, json=json, req = requests.request(method, joined_url, json=json,
params=params, headers=headers) params=params, headers=headers)
logger.debug("Status Code: %s", req.status_code) self.logger.debug("Status Code: %s", req.status_code)
logger.debug("Content: %s", req.content) self.logger.debug("Content: %s", req.content)
if raw: if raw:
return {"status": req.status_code, "content": req.content} return {"status": req.status_code, "content": req.content}
else: else:
return {"status": req.status_code, "content": req.json()} return {"status": req.status_code, "content": req.json()}
except requests.ConnectionError as e: except requests.ConnectionError as e:
print "Connection Error: " + str(e) self.logger.error("Connection Error: " + str(e))
return {"status": -1, "content": None} return {"status": -1, "content": None}

View File

@ -1,159 +1,53 @@
""" import re
Copyright (c) 2017 Fraunhofer FOKUS
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse
from futile.logging import get_logger
from openmtc_app.onem2m import XAE
from openmtc_onem2m.model import CSETypeIDE, RemoteCSE
from openmtc_app.onem2m import ResourceManagementXAE
from orion_api import OrionAPI from orion_api import OrionAPI
logger = get_logger(__name__)
class OrionContextBroker(ResourceManagementXAE):
class OrionContextBroker(XAE): def __init__(self, orion_host="http://localhost:1026", orion_api="v2", labels=None,
def __init__(self, labels=[""], interval=10,
orion_host="http://localhost:1026", orion_api="v2",
*args, **kw): *args, **kw):
super(OrionContextBroker, self).__init__(*args, **kw) super(OrionContextBroker, self).__init__(*args, **kw)
self.labels = labels if isinstance(labels, basestring):
self.remove_registration = True self.labels = {labels}
self.interval = interval elif hasattr(labels, '__iter__'):
self.labels = set(labels)
else:
self.labels = None
self._entity_names = {}
self.orion_api = OrionAPI(orion_host=orion_host, api_version=orion_api) self.orion_api = OrionAPI(orion_host=orion_host, api_version=orion_api)
def _on_register(self): def _on_register(self):
# init variables self._discover_openmtc_ipe_entities()
self._known_remote_cses = {}
self._discovered_devices = {}
self._discovered_sensors = {}
# connected to backend or gateway? def _sensor_filter(self, sensor):
cse_base = self.get_resource(self.cse_base) if self.labels:
logger.debug("CSE_BASE: %s", cse_base) return len(self.labels.intersection(sensor.labels)) > 0
if (cse_base.cseType == CSETypeIDE.MN_CSE or
cse_base.cseType == CSETypeIDE.AEN_CSE):
logger.debug("CSE_BASE identified as gateway")
# discover gateway
self._discover_cse(cse_base.CSE_ID + '/' + self.cse_base)
else: else:
logger.debug("CSE_BASE identified as backend") return True
# discover backend
self._discover_cse(cse_base.CSE_ID + '/' + self.cse_base)
# discover remote gateways
self._get_remote_cses(cse_base)
# get remote CSEs def _sensor_data_cb(self, sensor_info, sensor_data):
def _get_remote_cses(self, cse_base): def get_entity_name():
def get_cse_base():
handle_cse_base(self.get_resource(self.cse_base))
def handle_cse_base(cb):
for resource in cb.childResource:
if (isinstance(resource, RemoteCSE) and
resource.path not in self._known_remote_cses):
remote_cse = self.get_resource(resource.id)
self._known_remote_cses[resource.path] = remote_cse
remote_cse_base = (remote_cse.CSE_ID + '/' +
remote_cse.CSEBase)
self._discover_cse(remote_cse_base)
handle_cse_base(cse_base)
self.run_forever(self.interval, get_cse_base)
# discover CSE
def _discover_cse(self, cse_base):
def err_cb(errror_response):
try: try:
del self._known_remote_cses[remote_cse_id] id_label = filter(lambda x: (x.startswith('openmtc:id:')),
except KeyError: sensor_info['sensor_labels']).pop()
pass cse_id, dev_id = re.sub('^openmtc:id:', '', id_label).split('/')[:2]
# discover devices except (IndexError, ValueError):
self.periodic_discover(cse_base, {'labels': ['openmtc:device']}, cse_id = sensor_info['cse_id']
self.interval, dev_id = sensor_info['dev_name']
self._discover_devices) try:
self.periodic_discover(cse_base, {'labels': ['openmtc:sensor_data', f_s, e_pre = cse_id.split('~', 1)
'openmtc:actuator_data']}, except ValueError:
self.interval, f_s = ''
self._discover_sensors, err_cb) e_pre = cse_id
return re.sub('[\W]', '_', f_s), '%s-%s' % (e_pre, dev_id)
def _discover_devices(self, discovery):
for device_path in discovery:
self._discovered_devices[device_path] = 0
logger.debug("Discovered devices: %s", self._discovered_devices)
def _handle_sensor_data(self, container, data):
logger.debug("Got Sensor \"%s\" data: %s", container, data)
# XXX if a label contains 3x '/' assume that we need smart orchestra
# naming
try: try:
entity_name = next(lbl for lbl in self.get_resource( fiware_service, entity_name = self._entity_names[sensor_info['ID']]
container).labels if lbl.count('/') == 3) except KeyError:
tenant_name = entity_name.split('/')[0] fiware_service, entity_name = self._entity_names[sensor_info['ID']] = get_entity_name()
entity_name = '-'.join(entity_name.split('/')[1:3]) self.orion_api.create_entity(entity_name, fiware_service=fiware_service)
except Exception as e:
entity_name = container.split('/')[-2]
tenant_name = ""
self.orion_api.create_entity(entity_name, fiware_service=tenant_name) self.orion_api.update_attributes(entity_name, sensor_data, fiware_service=fiware_service)
self.orion_api.update_attributes(
entity_name,
data,
fiware_service=tenant_name)
def _handle_new_sensor(self, sensor_path):
# check labels of openmtc:device
device_labels = self.get_resource(
"/".join(sensor_path.split('/')[0:-1])).labels
# if label configured
if not ((len(self.labels) == 0) or
(len(self.labels) == 1 and self.labels[0] == "")):
# if no matching label
if len(set(self.labels) & set(device_labels)) == 0:
# no matching label no subscription
logger.debug("no matching label for %s", sensor_path)
return
logger.debug("Subscription added for %s", sensor_path)
self.add_container_subscription(sensor_path, self._handle_sensor_data)
def _discover_sensors(self, discovery):
for sensor_path in discovery:
try:
dev_path = [x for x in self._discovered_devices.keys()
if sensor_path.startswith(x)][0]
except IndexError as e: # todo(rst): ignore, but should not happen
logger.debug("%s", e)
logger.debug("%s", sensor_path)
continue
self._discovered_sensors[sensor_path] = {
'ID': sensor_path,
'dev_name': dev_path.split('/')[-1],
'cse_id': sensor_path.split('/')[1],
'data': None,
'type': 'sensor',
'n': None,
'u': None
}
self._handle_new_sensor(sensor_path)