Commit ad2ae089 authored by Pavel Spirek's avatar Pavel Spirek

DELETE method implemented

parent e4be8dfa
...@@ -9,7 +9,7 @@ from colorlog import error, warning as warn, info, debug ...@@ -9,7 +9,7 @@ from colorlog import error, warning as warn, info, debug
from typing import List, Any, Dict, TypeVar, Tuple, Set from typing import List, Any, Dict, TypeVar, Tuple, Set
import copy import copy
import yangson.instance import yangson.instance
from yangson.instance import Instance, NonexistentInstance, InstanceError, ArrayValue, ObjectValue, MemberName, EntryKeys from yangson.instance import Instance, NonexistentInstance, InstanceError, ArrayValue, ObjectValue, MemberName, EntryKeys, EntryIndex
from yangson import DataModel from yangson import DataModel
from yangson.datamodel import InstanceIdentifier from yangson.datamodel import InstanceIdentifier
from .helpers import DataHelpers from .helpers import DataHelpers
...@@ -172,6 +172,29 @@ class BaseDatastore: ...@@ -172,6 +172,29 @@ class BaseDatastore:
new_n = n.update(inst_val) new_n = n.update(inst_val)
self._data = new_n.top() self._data = new_n.top()
def delete_node_rpc(self, rpc: Rpc, insert=None, point=None):
ii = self.parse_ii(rpc.path, rpc.path_format)
n = self._data.goto(ii)
n_parent = n.up()
last_isel = ii[-1]
# if self.nacm:
# nrpc = NacmRpc(self.nacm, self, rpc.username)
# if nrpc.check_data_node_path(ii, Permission.NACM_ACCESS_READ) == Action.DENY:
# raise NacmForbiddenError()
# else:
# # Prun subtree data
# n = nrpc.check_data_read_path(ii)
if isinstance(last_isel, EntryIndex):
new_n = n_parent.remove_entry(last_isel.index)
elif isinstance(last_isel, EntryKeys):
new_n = n_parent.remove_entry(n.crumb.pointer_fragment())
elif isinstance(last_isel, MemberName):
new_n = n_parent.remove_member(last_isel.name)
self._data = new_n.top()
# Locks datastore data # Locks datastore data
def lock_data(self, username: str = None, blocking: bool=True): def lock_data(self, username: str = None, blocking: bool=True):
ret = self._data_lock.acquire(blocking=blocking, timeout=1) ret = self._data_lock.acquire(blocking=blocking, timeout=1)
......
...@@ -6,7 +6,7 @@ from colorlog import error, warning as warn, info, debug ...@@ -6,7 +6,7 @@ from colorlog import error, warning as warn, info, debug
from urllib.parse import parse_qs from urllib.parse import parse_qs
from yangson.schema import NonexistentSchemaNode from yangson.schema import NonexistentSchemaNode
from yangson.instance import NonexistentInstance from yangson.instance import NonexistentInstance, InstanceTypeError
from .config import CONFIG_HTTP, NACM_ADMINS, API_ROOT_data, NACM_API_ROOT_data from .config import CONFIG_HTTP, NACM_ADMINS, API_ROOT_data, NACM_API_ROOT_data
from .helpers import CertHelpers from .helpers import CertHelpers
...@@ -61,6 +61,10 @@ def get(prot: "H2Protocol", headers: OrderedDict, stream_id: int, ds, pth): ...@@ -61,6 +61,10 @@ def get(prot: "H2Protocol", headers: OrderedDict, stream_id: int, ds, pth):
warn("NonexistentInstance: " + pth) warn("NonexistentInstance: " + pth)
response = "NonexistentInstance" response = "NonexistentInstance"
http_status = "404" http_status = "404"
except InstanceTypeError:
warn("InstanceTypeError: " + pth)
response = "InstanceTypeError"
http_status = "404"
finally: finally:
ds.unlock_data() ds.unlock_data()
...@@ -250,3 +254,68 @@ def create_put_post_nacm_api(ds: BaseDatastore): ...@@ -250,3 +254,68 @@ def create_put_post_nacm_api(ds: BaseDatastore):
prot.conn.send_data(stream_id, response, end_stream=True) prot.conn.send_data(stream_id, response, end_stream=True)
return put_post_nacm_api_closure return put_post_nacm_api_closure
def create_nacm_api_delete(ds: BaseDatastore):
def put_post_nacm_api_closure(prot: "H2Protocol", headers: OrderedDict, stream_id: int):
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_delete: " + path))
info("qs = {}".format(query_string))
username = CertHelpers.get_field(prot.client_cert, "emailAddress")
pth = path[len(NACM_API_ROOT_data):]
rpc1 = Rpc()
rpc1.username = username
rpc1.path = pth
try:
ds.nacm.nacm_ds.lock_data(username)
ds.nacm.nacm_ds.delete_node_rpc(rpc1)
response = "No Content"
http_status = "204"
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"]),
)
prot.conn.send_headers(stream_id, response_headers)
prot.conn.send_data(stream_id, response, end_stream=True)
return put_post_nacm_api_closure
...@@ -58,9 +58,9 @@ class H2Protocol(asyncio.Protocol): ...@@ -58,9 +58,9 @@ class H2Protocol(asyncio.Protocol):
headers = OrderedDict(event.headers) headers = OrderedDict(event.headers)
http_method = headers[":method"] http_method = headers[":method"]
if http_method == "GET": if http_method in ("GET", "DELETE"):
# Handle GET # Handle immediately, no need to wait for incoming data
self.handle_get(headers, event.stream_id) self.handle_get_delete(headers, event.stream_id)
elif http_method in ("PUT", "POST"): elif http_method in ("PUT", "POST"):
# Store headers and wait for data upload # Store headers and wait for data upload
self.reqs_waiting_upload[event.stream_id] = headers self.reqs_waiting_upload[event.stream_id] = headers
...@@ -71,8 +71,6 @@ class H2Protocol(asyncio.Protocol): ...@@ -71,8 +71,6 @@ class H2Protocol(asyncio.Protocol):
elif isinstance(event, RemoteSettingsChanged): elif isinstance(event, RemoteSettingsChanged):
self.conn.acknowledge_settings(event) self.conn.acknowledge_settings(event)
self.transport.write(self.conn.data_to_send())
def http_handle_upload(self, data: bytes, stream_id: int): def http_handle_upload(self, data: bytes, stream_id: int):
try: try:
headers = self.reqs_waiting_upload.pop(stream_id) headers = self.reqs_waiting_upload.pop(stream_id)
...@@ -83,22 +81,22 @@ class H2Protocol(asyncio.Protocol): ...@@ -83,22 +81,22 @@ class H2Protocol(asyncio.Protocol):
url_split = headers[":path"].split("?") url_split = headers[":path"].split("?")
url_path = url_split[0] url_path = url_split[0]
h = gl_handlers.get_handler(headers[":method"], url_path) h = h2_handlers.get_handler(headers[":method"], url_path)
if h: if h:
h(self, headers, data, stream_id) h(self, headers, data, stream_id)
def handle_get(self, headers: OrderedDict, stream_id: int): def handle_get_delete(self, headers: OrderedDict, stream_id: int):
# Handle GET # Handle GET, DELETE
url_split = headers[":path"].split("?") url_split = headers[":path"].split("?")
url_path = url_split[0] url_path = url_split[0]
h = gl_handlers.get_handler("GET", url_path) h = h2_handlers.get_handler(headers[":method"], url_path)
if h: if h:
h(self, headers, stream_id) h(self, headers, stream_id)
def run(): def run():
global gl_handlers global h2_handlers
# Load configuration # Load configuration
load_config("jetconf/config.yaml") load_config("jetconf/config.yaml")
...@@ -119,14 +117,16 @@ def run(): ...@@ -119,14 +117,16 @@ def run():
get_api = handlers.create_get_api(ex_datastore) get_api = handlers.create_get_api(ex_datastore)
get_nacm_api = handlers.create_get_nacm_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) put_post_nacm_api = handlers.create_put_post_nacm_api(ex_datastore)
nacm_api_delete = handlers.create_nacm_api_delete(ex_datastore)
gl_handlers = HandlerList()
gl_handlers.register_handler(lambda m, p: (m == "POST") and (p.startswith(NACM_API_ROOT_data)), put_post_nacm_api) h2_handlers = HandlerList()
gl_handlers.register_handler(lambda m, p: (m == "GET") and (p == CONFIG_HTTP["NACM_API_ROOT"]), handlers.api_root_handler) h2_handlers.register_handler(lambda m, p: (m == "POST") and (p.startswith(NACM_API_ROOT_data)), put_post_nacm_api)
gl_handlers.register_handler(lambda m, p: (m == "GET") and (p == CONFIG_HTTP["API_ROOT"]), handlers.api_root_handler) h2_handlers.register_handler(lambda m, p: (m == "DELETE") and (p.startswith(NACM_API_ROOT_data)), nacm_api_delete)
gl_handlers.register_handler(lambda m, p: (m == "GET") and (p.startswith(NACM_API_ROOT_data)), get_nacm_api) h2_handlers.register_handler(lambda m, p: (m == "GET") and (p == CONFIG_HTTP["NACM_API_ROOT"]), handlers.api_root_handler)
gl_handlers.register_handler(lambda m, p: (m == "GET") and (p.startswith(API_ROOT_data)), get_api) h2_handlers.register_handler(lambda m, p: (m == "GET") and (p == CONFIG_HTTP["API_ROOT"]), handlers.api_root_handler)
gl_handlers.register_handler(lambda m, p: m == "GET", handlers.get_file) h2_handlers.register_handler(lambda m, p: (m == "GET") and (p.startswith(NACM_API_ROOT_data)), get_nacm_api)
h2_handlers.register_handler(lambda m, p: (m == "GET") and (p.startswith(API_ROOT_data)), get_api)
h2_handlers.register_handler(lambda m, p: m == "GET", handlers.get_file)
# HTTP server init # HTTP server init
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
......
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