Commit ba6d9405 authored by Pavel Spirek's avatar Pavel Spirek

Major code refactor #2, architecture much improved

parent 909fc553
......@@ -10,8 +10,7 @@ from yangson.instance import NonexistentInstance
from .config import CONFIG_HTTP, NACM_ADMINS, RESTCONF_API_ROOT_data, RESTCONF_NACM_API_ROOT_data
from .helpers import CertHelpers
import jetconf.rest_server
from .data import Rpc, DataLockError, NacmForbiddenError, InstanceAlreadyPresent
from .data import BaseDatastore, Rpc, DataLockError, NacmForbiddenError, InstanceAlreadyPresent
def api_root_handler(prot: "H2Protocol", headers: OrderedDict, stream_id: int):
......@@ -77,52 +76,58 @@ def get(prot: "H2Protocol", headers: OrderedDict, stream_id: int, ds, pth):
prot.conn.send_data(stream_id, response.encode(), end_stream=True)
def get_nacm_api(prot: "H2Protocol", headers: OrderedDict, stream_id: int):
# NACM api request
info(("nacm_api_get: " + headers[":path"]))
def create_get_nacm_api(ds: BaseDatastore):
def get_nacm_api_closure(prot: "H2Protocol", headers: OrderedDict, stream_id: int):
# NACM api request
info(("nacm_api_get: " + headers[":path"]))
url_split = headers[":path"].split("?")
url_path = url_split[0]
if len(url_split) > 1:
query_string = parse_qs(url_split[1])
else:
query_string = {}
url_split = headers[":path"].split("?")
url_path = url_split[0]
if len(url_split) > 1:
query_string = parse_qs(url_split[1])
else:
query_string = {}
username = CertHelpers.get_field(prot.client_cert, "emailAddress")
if username not in NACM_ADMINS:
warn(username + " not allowed to access NACM data")
response = "Forbidden"
http_status = "403"
response += "\n"
response_headers = (
(':status', http_status),
('content-type', 'application/yang.api+json'),
('content-length', len(response)),
('server', CONFIG_HTTP["SERVER_NAME"]),
)
prot.conn.send_headers(stream_id, response_headers)
prot.conn.send_data(stream_id, response.encode(), end_stream=True)
else:
pth = url_path[len(RESTCONF_NACM_API_ROOT_data):] or "/"
get(prot, headers, stream_id, ds.nacm.nacm_ds, pth)
username = CertHelpers.get_field(prot.client_cert, "emailAddress")
if username not in NACM_ADMINS:
warn(username + " not allowed to access NACM data")
response = "Forbidden"
http_status = "403"
return get_nacm_api_closure
response += "\n"
response_headers = (
(':status', http_status),
('content-type', 'application/yang.api+json'),
('content-length', len(response)),
('server', CONFIG_HTTP["SERVER_NAME"]),
)
prot.conn.send_headers(stream_id, response_headers)
prot.conn.send_data(stream_id, response.encode(), end_stream=True)
else:
pth = url_path[len(jetconf.rest_server.RESTCONF_NACM_API_ROOT_data):] or "/"
get(prot, headers, stream_id, jetconf.rest_server.ex_datastore.nacm.nacm_ds, pth)
def create_get_api(ds: BaseDatastore):
def get_api_closure(prot: "H2Protocol", headers: OrderedDict, stream_id: int):
# api request
info(("api_get: " + headers[":path"]))
url_split = headers[":path"].split("?")
url_path = url_split[0]
if len(url_split) > 1:
query_string = parse_qs(url_split[1])
else:
query_string = {}
def get_api(prot: "H2Protocol", headers: OrderedDict, stream_id: int):
# api request
info(("api_get: " + headers[":path"]))
pth = url_path[len(RESTCONF_API_ROOT_data):] or "/"
url_split = headers[":path"].split("?")
url_path = url_split[0]
if len(url_split) > 1:
query_string = parse_qs(url_split[1])
else:
query_string = {}
pth = url_path[len(jetconf.rest_server.RESTCONF_API_ROOT_data):] or "/"
get(prot, headers, stream_id, ds, pth)
get(prot, headers, stream_id, jetconf.rest_server.ex_datastore, pth)
return get_api_closure
def get_file(prot: "H2Protocol", headers: OrderedDict, stream_id: int):
......@@ -174,71 +179,74 @@ def get_file(prot: "H2Protocol", headers: OrderedDict, stream_id: int):
prot.conn.send_data(stream_id, bytes(), end_stream=True)
def put_post_nacm_api(prot: "H2Protocol", headers: OrderedDict, data: bytes, stream_id: int):
data_str = data.decode("utf-8")
info("prijato: " + data_str)
url_split = headers[":path"].split("?")
path = url_split[0]
if len(url_split) > 1:
query_string = parse_qs(url_split[1])
else:
query_string = {}
info(("nacm_api_put: " + path))
info("qs = {}".format(query_string))
username = CertHelpers.get_field(prot.client_cert, "emailAddress")
pth = path[len(jetconf.rest_server.RESTCONF_NACM_API_ROOT_data):]
rpc1 = Rpc()
rpc1.username = username
rpc1.path = pth
json_data = json.loads(data_str)
def create_put_post_nacm_api(ds: BaseDatastore):
def put_post_nacm_api_closure(prot: "H2Protocol", headers: OrderedDict, data: bytes, stream_id: int):
data_str = data.decode("utf-8")
info("prijato: " + data_str)
try:
jetconf.rest_server.ex_datastore.nacm.nacm_ds.lock_data(username)
if headers[":method"] == "PUT":
jetconf.rest_server.ex_datastore.nacm.nacm_ds.put_node_rpc(rpc1, json_data)
url_split = headers[":path"].split("?")
path = url_split[0]
if len(url_split) > 1:
query_string = parse_qs(url_split[1])
else:
ins_pos = (query_string.get("insert") or [None])[0]
jetconf.rest_server.ex_datastore.nacm.nacm_ds.create_node_rpc(rpc1, json_data, insert=ins_pos)
response = "Done\n"
http_status = "200"
except DataLockError as e:
warn(e.msg)
response = "Internal Server Error"
http_status = "500"
except NacmForbiddenError as e:
warn(e.msg)
response = "Forbidden"
http_status = "403"
except NonexistentSchemaNode:
warn("NonexistentSchemaNode: " + pth)
response = "NonexistentSchemaNode"
http_status = "404"
except NonexistentInstance:
warn("NonexistentInstance: " + pth)
response = "NonexistentInstance"
http_status = "404"
except InstanceAlreadyPresent:
warn("InstanceAlreadyPresent: " + pth)
response = "Conflict"
http_status = "409"
finally:
jetconf.rest_server.ex_datastore.nacm.nacm_ds.unlock_data()
jetconf.rest_server.ex_datastore.nacm.update(jetconf.rest_server.ex_datastore.nacm.nacm_ds.get_data_root().member("ietf-netconf-acm:nacm"))
query_string = {}
info(("nacm_api_put: " + path))
info("qs = {}".format(query_string))
username = CertHelpers.get_field(prot.client_cert, "emailAddress")
pth = path[len(RESTCONF_NACM_API_ROOT_data):]
rpc1 = Rpc()
rpc1.username = username
rpc1.path = pth
json_data = json.loads(data_str)
try:
ds.nacm.nacm_ds.lock_data(username)
if headers[":method"] == "PUT":
ds.nacm.nacm_ds.put_node_rpc(rpc1, json_data)
else:
ins_pos = (query_string.get("insert") or [None])[0]
ds.nacm.nacm_ds.create_node_rpc(rpc1, json_data, insert=ins_pos)
response = "Done\n"
http_status = "200"
except DataLockError as e:
warn(e.msg)
response = "Internal Server Error"
http_status = "500"
except NacmForbiddenError as e:
warn(e.msg)
response = "Forbidden"
http_status = "403"
except NonexistentSchemaNode:
warn("NonexistentSchemaNode: " + pth)
response = "NonexistentSchemaNode"
http_status = "404"
except NonexistentInstance:
warn("NonexistentInstance: " + pth)
response = "NonexistentInstance"
http_status = "404"
except InstanceAlreadyPresent:
warn("InstanceAlreadyPresent: " + pth)
response = "Conflict"
http_status = "409"
finally:
ds.nacm.nacm_ds.unlock_data()
ds.nacm.update(ds.nacm.nacm_ds.get_data_root().member("ietf-netconf-acm:nacm"))
response += "\n"
response = response.encode()
response_headers = (
(':status', http_status),
('content-type', 'application/yang.api+json'),
('content-length', len(response)),
('server', CONFIG_HTTP["SERVER_NAME"]),
)
response += "\n"
response = response.encode()
response_headers = (
(':status', http_status),
('content-type', 'application/yang.api+json'),
('content-length', len(response)),
('server', CONFIG_HTTP["SERVER_NAME"]),
)
prot.conn.send_headers(stream_id, response_headers)
prot.conn.send_data(stream_id, response, end_stream=True)
prot.conn.send_headers(stream_id, response_headers)
prot.conn.send_data(stream_id, response, end_stream=True)
return put_post_nacm_api_closure
......@@ -3,14 +3,14 @@ import ssl
from collections import OrderedDict
from colorlog import error, warning as warn, info, debug
from typing import List, Tuple, Dict, Any, Callable
from .nacm import NacmConfig
from .data import JsonDatastore, Rpc, NacmForbiddenError, DataLockError, InstanceAlreadyPresent
import jetconf.http_handlers
from h2.connection import H2Connection
from h2.events import DataReceived, RequestReceived, RemoteSettingsChanged
import jetconf.http_handlers as handlers
from .config import CONFIG_HTTP, RESTCONF_NACM_API_ROOT_data, RESTCONF_API_ROOT_data, load_config, print_config
from .nacm import NacmConfig
from .data import JsonDatastore, Rpc, NacmForbiddenError, DataLockError, InstanceAlreadyPresent
# Function(method, path) -> bool
......@@ -98,22 +98,12 @@ class H2Protocol(asyncio.Protocol):
def run():
global ex_datastore
global gl_handlers
# Load configuration
load_config("jetconf/config.yaml")
print_config()
# Register HTTP handlers
gl_handlers = HandlerList()
gl_handlers.register_handler(lambda m, p: (m == "POST") and (p.startswith(RESTCONF_NACM_API_ROOT_data)), jetconf.http_handlers.put_post_nacm_api)
gl_handlers.register_handler(lambda m, p: (m == "GET") and (p == CONFIG_HTTP["RESTCONF_NACM_API_ROOT"]), jetconf.http_handlers.api_root_handler)
gl_handlers.register_handler(lambda m, p: (m == "GET") and (p == CONFIG_HTTP["RESTCONF_API_ROOT"]), jetconf.http_handlers.api_root_handler)
gl_handlers.register_handler(lambda m, p: (m == "GET") and (p.startswith(RESTCONF_NACM_API_ROOT_data)), jetconf.http_handlers.get_nacm_api)
gl_handlers.register_handler(lambda m, p: (m == "GET") and (p.startswith(RESTCONF_API_ROOT_data)), jetconf.http_handlers.get_api)
gl_handlers.register_handler(lambda m, p: m == "GET", jetconf.http_handlers.get_file)
# NACM init
nacm_data = JsonDatastore("./data", "./data/yang-library-data.json", "NACM data")
nacm_data.load("jetconf/example-data-nacm.json")
......@@ -125,6 +115,19 @@ def run():
ex_datastore.load("jetconf/example-data.json")
ex_datastore.register_nacm(nacmc)
# Register HTTP handlers
get_api = handlers.create_get_api(ex_datastore)
get_nacm_api = handlers.create_get_nacm_api(ex_datastore)
put_post_nacm_api = handlers.create_put_post_nacm_api(ex_datastore)
gl_handlers = HandlerList()
gl_handlers.register_handler(lambda m, p: (m == "POST") and (p.startswith(RESTCONF_NACM_API_ROOT_data)), put_post_nacm_api)
gl_handlers.register_handler(lambda m, p: (m == "GET") and (p == CONFIG_HTTP["RESTCONF_NACM_API_ROOT"]), handlers.api_root_handler)
gl_handlers.register_handler(lambda m, p: (m == "GET") and (p == CONFIG_HTTP["RESTCONF_API_ROOT"]), handlers.api_root_handler)
gl_handlers.register_handler(lambda m, p: (m == "GET") and (p.startswith(RESTCONF_NACM_API_ROOT_data)), get_nacm_api)
gl_handlers.register_handler(lambda m, p: (m == "GET") and (p.startswith(RESTCONF_API_ROOT_data)), get_api)
gl_handlers.register_handler(lambda m, p: m == "GET", handlers.get_file)
# HTTP server init
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_context.options |= (ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_COMPRESSION)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment