diff options
| author | LV-426 <lv-426@taproot.org.il> | 2015-01-04 17:16:12 +0200 |
|---|---|---|
| committer | LV-426 <lv-426@taproot.org.il> | 2015-01-04 17:16:12 +0200 |
| commit | f5494d9233d6f9967c614eef23dee1f29af056c9 (patch) | |
| tree | 763da6c98efe56acb342aa09d8c4866624c6f32a /src | |
| parent | f1dd537689307329dfe42c88ee2e8fda3355eb84 (diff) | |
rhizi_api breakup: common, rest & websocket files
Diffstat (limited to 'src')
| -rw-r--r-- | src/server/rhizi_api.py | 165 | ||||
| -rw-r--r-- | src/server/rhizi_server.py | 2 | ||||
| -rw-r--r-- | src/server/rz_api_common.py | 47 | ||||
| -rw-r--r-- | src/server/rz_api_rest.py | 116 | ||||
| -rw-r--r-- | src/server/rz_api_websocket.py | 42 | ||||
| -rw-r--r-- | src/server/rz_mesh.py | 32 |
6 files changed, 231 insertions, 173 deletions
diff --git a/src/server/rhizi_api.py b/src/server/rhizi_api.py index 44edb660..f6a3f128 100644 --- a/src/server/rhizi_api.py +++ b/src/server/rhizi_api.py @@ -1,99 +1,38 @@ """ Rhizi web API -""" -import os -import db_controller as dbc -import json -import logging -import traceback -import crypt_util -import flask -from flask import jsonify +@deprecated: destined to split into rz_api_rest & rz_api_websocket +""" +from datetime import datetime from flask import Flask -from flask import request +from flask import escape +from flask import jsonify from flask import make_response -from flask import session from flask import redirect -from flask import escape -from flask import url_for from flask import render_template +from flask import request from flask import send_from_directory +from flask import session +from flask import url_for +import flask +import json +import logging +import os +import traceback -from rz_kernel import RZ_Kernel - +import crypt_util +import db_controller as dbc from model.graph import Topo_Diff -from model.graph import Attr_Diff from model.model import Link -from datetime import datetime - -log = logging.getLogger('rhizi') - -# injected: DB controller -db_ctl = None - -def __sanitize_input(*args, **kw_args): - pass - -def sanitize_input__node(n): - """ - provide a control point as to which node fields are persisted - """ - assert None != n.get('id'), 'invalid input: node: missing id' - -def sanitize_input__link(l): - """ - provide a control point as to which link fields are persisted - """ - - # expected prop assertions - assert None != l.get('id'), 'invalid input: link: missing id' - assert None != l.get('__src_id'), 'invalid input: link: missing src id' - assert None != l.get('__dst_id'), 'invalid input: link: missing dst id' - - # unexpected prop assertions - assert None == l.get('__type'), 'client is sending us __type link property, it should not' - assert None == l.get('name'), 'client is sending us name link property, it should not' - -def sanitize_input__topo_diff(topo_diff): - for n in topo_diff.node_set_add: - sanitize_input__node(n) - for l in topo_diff.link_set_add: - sanitize_input__link(l) - -def sanitize_input__attr_diff(attr_diff): - pass # TODO: impl - -def _validate_obj__attr_diff(ad): - # check for name attr changes, which are currently forbidden - for n_id, node_attr_diff_set in ad['__type_node'].items(): - for attr_name in node_attr_diff_set['__attr_write'].keys(): - if 'id' == attr_name: - raise Exception('validation error: Attr_Diff: forbidden attribute change: \'id\', n_id: ' + n_id) - -def __response_wrap(data=None, error=None): - """ - wrap response data/errors as dict - this should always be used when returning - data to allow easy return of list objects, assist in error case distinction, etc. - """ - return dict(data=data, error=error) - -def __common_resp_handle(data=None, error=None): - """ - provide common response handling - - @data must be json serializable - @error will be serialized with str() - """ - error_str = str(error) # convert any Exception objects to serializable form - ret_data = __response_wrap(data, error_str) - resp = jsonify(ret_data) # this will create a Flask Response object +from rz_api_common import __sanitize_input +from rz_api_common import sanitize_input__topo_diff +from rz_api_rest import __common_resp_handle +from rz_kernel import RZ_Kernel - resp.headers['Access-Control-Allow-Origin'] = '*' - # more response processing +log = logging.getLogger('rhizi') - return resp +db_ctl = None # injected: DB controller def __common_exec(op, on_success=__common_resp_handle, on_error=__common_resp_handle): """ @@ -183,70 +122,10 @@ def diff_commit__set(): return topo_diff; topo_diff = sanitize_input(request) + op = dbc.DBO_topo_diff_commit(topo_diff) return __common_exec(op) -def diff_commit__topo(): - """ - REST API wrapper around diff_commit__topo(): - - extract topo_diff from request - - handle success/error outcomes - """ - def sanitize_input(req): - topo_diff_dict = request.get_json()['topo_diff'] - topo_diff = Topo_Diff.from_json_dict(topo_diff_dict) - - sanitize_input__topo_diff(topo_diff) - return topo_diff; - - try: - topo_diff = sanitize_input(request) - except Exception as e: - return __common_resp_handle(error='malformed input') - - try: - kernel = flask.current_app.kernel - topo_diff = kernel.diff_commit__topo(db_ctl, topo_diff) - topo_diff_json = topo_diff.to_json_dict() - return __common_resp_handle(data=topo_diff_json) - except Exception as e: - log.error(e.message) - log.error(traceback.print_exc()) - return __common_resp_handle(error=e) - -def diff_commit__attr(): - """ - commit a graph attribute diff - """ - def sanitize_input(req): - attr_diff_dict = request.get_json()['attr_diff'] - attr_diff = Attr_Diff.from_json_dict(attr_diff_dict) - - sanitize_input__attr_diff(attr_diff) - return attr_diff; - - def on_error(e): - # handle DB ERRORS, eg. name attr change error - return __common_resp_handle(error='error occurred') - - try: - attr_diff = sanitize_input(request) - _validate_obj__attr_diff(attr_diff) - except Exception as e: - return __common_resp_handle(error='malformed input') - - try: - kernel = flask.current_app.kernel - attr_diff = kernel.diff_commit__attr(db_ctl, attr_diff) - return __common_resp_handle(data=attr_diff) - except Exception as e: - log.error(e.message) - log.error(traceback.print_exc()) - return __common_resp_handle(error=e) - -def diff_commit__vis(): - pass - def add_node_set(): """ @deprecated: use topo_attr_commit diff --git a/src/server/rhizi_server.py b/src/server/rhizi_server.py index 216085fc..855c75ed 100644 --- a/src/server/rhizi_server.py +++ b/src/server/rhizi_server.py @@ -22,6 +22,7 @@ from flask import send_from_directory from functools import wraps from rz_mesh import init_ws_interface from rz_kernel import RZ_Kernel +import rz_api_rest class Config(object): """ @@ -238,6 +239,7 @@ def init_webapp(cfg, kernel, db_ctl=None): if None == db_ctl: db_ctl = dbc.DB_Controller(cfg) rhizi_api.db_ctl = db_ctl + rz_api_rest.db_ctl = db_ctl webapp.rz_config = cfg webapp.kernel = kernel diff --git a/src/server/rz_api_common.py b/src/server/rz_api_common.py new file mode 100644 index 00000000..68d9008d --- /dev/null +++ b/src/server/rz_api_common.py @@ -0,0 +1,47 @@ +""" +Common public API logic: + - object sanitization for inbound data + - object validation for inbound data + - sanitize_input__XXX: concerned with sanitizing potential currupt data arriving + from external sources. + - validae_object__XXX: concerned with validating the logical state of an object + +""" +def __sanitize_input(*args, **kw_args): + pass + +def sanitize_input__node(n): + """ + provide a control point as to which node fields are persisted + """ + assert None != n.get('id'), 'invalid input: node: missing id' + +def sanitize_input__link(l): + """ + provide a control point as to which link fields are persisted + """ + + # expected prop assertions + assert None != l.get('id'), 'invalid input: link: missing id' + assert None != l.get('__src_id'), 'invalid input: link: missing src id' + assert None != l.get('__dst_id'), 'invalid input: link: missing dst id' + + # unexpected prop assertions + assert None == l.get('__type'), 'client is sending us __type link property, it should not' + assert None == l.get('name'), 'client is sending us name link property, it should not' + +def sanitize_input__topo_diff(topo_diff): + for n in topo_diff.node_set_add: + sanitize_input__node(n) + for l in topo_diff.link_set_add: + sanitize_input__link(l) + +def sanitize_input__attr_diff(attr_diff): + pass # TODO: impl + +def validate_obj__attr_diff(ad): + # check for name attr changes, which are currently forbidden + for n_id, node_attr_diff_set in ad['__type_node'].items(): + for attr_name in node_attr_diff_set['__attr_write'].keys(): + if 'id' == attr_name: + raise Exception('validation error: Attr_Diff: forbidden attribute change: \'id\', n_id: ' + n_id) diff --git a/src/server/rz_api_rest.py b/src/server/rz_api_rest.py new file mode 100644 index 00000000..bb5542d2 --- /dev/null +++ b/src/server/rz_api_rest.py @@ -0,0 +1,116 @@ +""" +Rhizi REST web API: + - make use of rz_kernel for core logic execution + - make use of rz_api_common for common API logic + +""" +from flask import Flask +from flask import escape +from flask import jsonify +from flask import make_response +from flask import redirect +from flask import render_template +from flask import request +from flask import send_from_directory +from flask import session +from flask import url_for +import flask +import logging +import traceback + +from model.graph import Attr_Diff +from model.graph import Topo_Diff +from rz_api_common import sanitize_input__attr_diff +from rz_api_common import sanitize_input__topo_diff +from rz_api_common import validate_obj__attr_diff + + +log = logging.getLogger('rhizi') + +db_ctl = None # injected: DB controller + +def __response_wrap(data=None, error=None): + """ + wrap response data/errors as dict - this should always be used when returning + data to allow easy return of list objects, assist in error case distinction, etc. + """ + return dict(data=data, error=error) + +def __common_resp_handle(data=None, error=None): + """ + provide common response handling + + @data must be json serializable + @error will be serialized with str() + """ + error_str = str(error) # convert any Exception objects to serializable form + ret_data = __response_wrap(data, error_str) + resp = jsonify(ret_data) # this will create a Flask Response object + + resp.headers['Access-Control-Allow-Origin'] = '*' + + # more response processing + + return resp + +def diff_commit__topo(): + """ + REST API wrapper around diff_commit__topo(): + - extract topo_diff from request + - handle success/error outcomes + """ + def sanitize_input(req): + topo_diff_dict = request.get_json()['topo_diff'] + topo_diff = Topo_Diff.from_json_dict(topo_diff_dict) + + sanitize_input__topo_diff(topo_diff) + return topo_diff; + + try: + topo_diff = sanitize_input(request) + except Exception as e: + return __common_resp_handle(error='malformed input') + + try: + kernel = flask.current_app.kernel + topo_diff = kernel.diff_commit__topo(db_ctl, topo_diff) + topo_diff_json = topo_diff.to_json_dict() + return __common_resp_handle(data=topo_diff_json) + except Exception as e: + log.error(e.message) + log.error(traceback.print_exc()) + return __common_resp_handle(error=e) + +def diff_commit__attr(): + """ + commit a graph attribute diff + """ + def sanitize_input(req): + attr_diff_dict = request.get_json()['attr_diff'] + attr_diff = Attr_Diff.from_json_dict(attr_diff_dict) + + sanitize_input__attr_diff(attr_diff) + return attr_diff; + + def on_error(e): + # handle DB ERRORS, eg. name attr change error + return __common_resp_handle(error='error occurred') + + try: + attr_diff = sanitize_input(request) + validate_obj__attr_diff(attr_diff) + except Exception as e: + return __common_resp_handle(error='malformed input') + + try: + kernel = flask.current_app.kernel + attr_diff = kernel.diff_commit__attr(db_ctl, attr_diff) + return __common_resp_handle(data=attr_diff) + except Exception as e: + log.error(e.message) + log.error(traceback.print_exc()) + return __common_resp_handle(error=e) + +def diff_commit__vis(): + pass + diff --git a/src/server/rz_api_websocket.py b/src/server/rz_api_websocket.py new file mode 100644 index 00000000..15243db6 --- /dev/null +++ b/src/server/rz_api_websocket.py @@ -0,0 +1,42 @@ +""" +Rhizi websocket web API +""" + +import logging +from socketio.mixins import BroadcastMixin +from socketio.namespace import BaseNamespace + + +log = logging.getLogger('rhizi') + +class WebSocket_Graph_NS(BaseNamespace, BroadcastMixin): + """ + Rhizi '/graph' websocket namespace + """ + + def __init__(self, *args, **kw): + super(WebSocket_Graph_NS, self).__init__(*args, **kw) + + def multicast_msg(self, msg_name, *args): + self.socket.server.log_multicast(msg_name) + super(WebSocket_Graph_NS, self).broadcast_event_not_me(msg_name, *args) + + def _log_conn(self, prefix_msg): + rmt_addr = self.environ['REMOTE_ADDR'] + rmt_port = self.environ['REMOTE_PORT'] + sid = self.environ['socketio'].sessid + log.info('ws: %s: sid: %s, remote-socket: %s:%s' % (prefix_msg, sid, rmt_addr, rmt_port)) + + def recv_connect(self): + self._log_conn('conn open') + + def recv_disconnect(self): + self._log_conn('conn close') + + def on_diff_commit__topo(self, topo_diff): + log.info('ws: rx: topo diff: ' + str(topo_diff)) + self.multicast_msg('diff_commit__topo', topo_diff) + + def on_diff_commit__attr(self, attr_diff): + log.info('ws: rx: attr diff: ' + str(attr_diff)) + self.multicast_msg('diff_commit__attr', attr_diff) diff --git a/src/server/rz_mesh.py b/src/server/rz_mesh.py index b20aa64b..1d077803 100644 --- a/src/server/rz_mesh.py +++ b/src/server/rz_mesh.py @@ -3,45 +3,17 @@ from flask import request from geventwebsocket.handler import WebSocketHandler import logging from socketio import socketio_manage -from socketio.mixins import BroadcastMixin -from socketio.namespace import BaseNamespace + from socketio.server import SocketIOHandler from socketio.server import SocketIOServer from functools import wraps from rz_kernel import RZ_Kernel from model.graph import Attr_Diff, Topo_Diff +from rz_api_websocket import WebSocket_Graph_NS log = logging.getLogger('rhizi') -class WebSocket_Graph_NS(BaseNamespace, BroadcastMixin): - """ - Rhizi '/graph' websocket namespace - """ - def multicast_msg(self, msg_name, *args): - self.socket.server.log_multicast(msg_name) - super(WebSocket_Graph_NS, self).broadcast_event_not_me(msg_name, *args) - - def _log_conn(self, prefix_msg): - rmt_addr = self.environ['REMOTE_ADDR'] - rmt_port = self.environ['REMOTE_PORT'] - sid = self.environ['socketio'].sessid - log.info('ws: %s: sid: %s, remote-socket: %s:%s' % (prefix_msg, sid, rmt_addr, rmt_port)) - - def recv_connect(self): - self._log_conn('conn open') - - def recv_disconnect(self): - self._log_conn('conn close') - - def on_diff_commit__topo(self, topo_diff): - log.info('ws: rx: topo diff: ' + str(topo_diff)) - self.multicast_msg('diff_commit__topo', topo_diff) - - def on_diff_commit__attr(self, attr_diff): - log.info('ws: rx: attr diff: ' + str(attr_diff)) - self.multicast_msg('diff_commit__attr', attr_diff) - class RZ_WebSocket_Server(SocketIOServer): """ Rhizi customized SocketIOServer: |
