Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
jetconf
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
7
Issues
7
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
labs
jetconf
Commits
863d6bb1
Commit
863d6bb1
authored
Jan 11, 2019
by
Pavel Spirek
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Initial support for RPC Actions
parent
7bb9414d
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
180 additions
and
51 deletions
+180
-51
data.py
jetconf/data.py
+39
-3
handler_base.py
jetconf/handler_base.py
+3
-2
handler_list.py
jetconf/handler_list.py
+17
-3
http_handlers.py
jetconf/http_handlers.py
+112
-41
jetconf.py
jetconf/jetconf.py
+9
-2
No files found.
jetconf/data.py
View file @
863d6bb1
...
...
@@ -26,7 +26,7 @@ from .helpers import PathFormat, ErrorHelpers, LogHelpers, DataHelpers, JsonNode
from
.nacm
import
NacmConfig
,
Permission
,
Action
from
.journal
import
ChangeType
,
UsrChangeJournal
,
RpcInfo
,
DataChange
from
.handler_base
import
ConfDataObjectHandler
,
ConfDataListHandler
,
StateDataContainerHandler
,
StateDataListHandler
from
.handler_list
import
ConfDataHandlerList
,
StateDataHandlerList
,
OpHandlerList
from
.handler_list
import
ConfDataHandlerList
,
StateDataHandlerList
,
OpHandlerList
,
ActionHandlerList
from
.errors
import
(
StagingDataException
,
NoHandlerForStateDataError
,
...
...
@@ -43,10 +43,11 @@ debug_data = LogHelpers.create_module_dbg_logger(__name__)
class
BackendHandlers
:
def
__init__
(
self
):
def
__init__
(
self
,
dm
:
DataModel
):
self
.
conf
=
ConfDataHandlerList
()
self
.
state
=
StateDataHandlerList
()
self
.
op
=
OpHandlerList
()
self
.
action
=
ActionHandlerList
(
dm
)
def
_blankfn
(
*
args
,
**
kwargs
):
pass
...
...
@@ -65,7 +66,7 @@ class BaseDatastore:
self
.
_lock_username
=
None
# type: str
self
.
_usr_journals
=
{}
# type: Dict[str, UsrChangeJournal]
self
.
nacm
=
None
# type: NacmConfig
self
.
handlers
=
BackendHandlers
()
self
.
handlers
=
BackendHandlers
(
self
.
_dm
)
self
.
nacm
=
NacmConfig
(
self
,
self
.
_dm
)
if
with_nacm
else
None
# Returns DataModel object
...
...
@@ -660,6 +661,41 @@ class BaseDatastore:
return
ret_data
# Invoke a node action
def
invoke_action_rpc
(
self
,
root
:
InstanceNode
,
rpc
:
RpcInfo
)
->
JsonNodeT
:
ii
=
self
.
parse_ii
(
rpc
.
path
,
rpc
.
path_format
)
node_ii
=
ii
[
0
:
-
1
]
n
=
root
.
goto
(
node_ii
)
# Evaluate NACM
if
self
.
nacm
and
not
rpc
.
skip_nacm_check
:
nrpc
=
self
.
nacm
.
get_user_rules
(
rpc
.
username
)
if
nrpc
.
check_data_node_permission
(
root
,
node_ii
,
Permission
.
NACM_ACCESS_EXEC
)
==
Action
.
DENY
:
raise
NacmForbiddenError
(
"Invocation of
\"
{}
\"
operation denied for user
\"
{}
\"
"
.
format
(
rpc
.
op_name
,
rpc
.
username
)
)
ii_an
=
ii
[
-
1
]
node_sn
=
n
.
schema_node
sn
=
node_sn
.
get_child
(
ii_an
.
name
,
ii_an
.
namespace
)
action_handler
=
self
.
handlers
.
action
.
get_handler
(
id
(
sn
))
if
action_handler
is
None
:
raise
NoHandlerForOpError
(
rpc
.
path
)
# Get operation input schema
sn_input
=
sn
.
get_child
(
"input"
)
# Input arguments are expected, this will validate them
op_input_args
=
sn_input
.
from_raw
(
rpc
.
op_input_args
)
if
sn_input
.
children
else
None
try
:
ret_data
=
action_handler
(
ii
,
op_input_args
,
rpc
.
username
)
except
Exception
as
e
:
raise
OpHandlerFailedError
(
epretty
(
e
))
return
ret_data
def
add_to_journal_rpc
(
self
,
ch_type
:
ChangeType
,
rpc
:
RpcInfo
,
value
:
Optional
[
JsonNodeT
],
new_root
:
InstanceNode
,
nacm_modified
:
bool
):
usr_journal
=
self
.
_usr_journals
.
get
(
rpc
.
username
)
if
usr_journal
is
not
None
:
...
...
jetconf/handler_base.py
View file @
863d6bb1
...
...
@@ -3,7 +3,7 @@ from typing import Callable, Union
from
yangson.schemanode
import
SchemaNode
from
yangson.instance
import
InstanceRoute
from
.journal
import
DataChange
,
RpcInfo
from
.journal
import
DataChange
from
.helpers
import
JsonNodeT
...
...
@@ -77,4 +77,5 @@ class StateDataListHandler(StateDataHandlerBase):
# ---------- Types ----------
ConfDataHandler
=
Union
[
ConfDataObjectHandler
,
ConfDataListHandler
]
StateDataHandler
=
Union
[
StateDataContainerHandler
,
StateDataListHandler
]
OpHandler
=
Callable
[[
RpcInfo
],
JsonNodeT
]
OpHandler
=
Callable
[[
JsonNodeT
,
str
],
JsonNodeT
]
ActionHandler
=
Callable
[[
InstanceRoute
,
JsonNodeT
,
str
],
JsonNodeT
]
jetconf/handler_list.py
View file @
863d6bb1
from
typing
import
List
,
Dict
,
Tuple
,
Callable
from
typing
import
List
,
Dict
,
Tuple
from
yangson.datamodel
import
DataModel
from
yangson.schemadata
import
SchemaData
from
yangson.typealiases
import
SchemaRoute
from
.handler_base
import
ConfDataHandlerBase
,
StateDataHandlerBase
,
ConfDataHandler
,
StateDataHandler
,
OpHandler
from
.handler_base
import
ConfDataHandlerBase
,
StateDataHandlerBase
,
ConfDataHandler
,
StateDataHandler
,
OpHandler
,
ActionHandler
# ---------- Handler lists ----------
...
...
@@ -52,8 +53,21 @@ class OpHandlerList:
def
__init__
(
self
):
self
.
handlers
=
{}
# type: Dict[str, OpHandler]
def
register
(
self
,
handler
:
Callable
,
op_name
:
str
):
def
register
(
self
,
handler
:
OpHandler
,
op_name
:
str
):
self
.
handlers
[
op_name
]
=
handler
def
get_handler
(
self
,
op_name
:
str
)
->
OpHandler
:
return
self
.
handlers
.
get
(
op_name
)
class
ActionHandlerList
:
def
__init__
(
self
,
dm
:
DataModel
):
self
.
handlers
=
{}
# type: Dict[int, ActionHandler]
self
.
_dm
=
dm
def
register
(
self
,
handler
:
ActionHandler
,
sch_pth
:
str
):
sn
=
self
.
_dm
.
get_schema_node
(
sch_pth
)
self
.
handlers
[
id
(
sn
)]
=
handler
def
get_handler
(
self
,
sch_node_id
:
int
)
->
ActionHandler
:
return
self
.
handlers
.
get
(
sch_node_id
)
jetconf/http_handlers.py
View file @
863d6bb1
...
...
@@ -10,7 +10,7 @@ from typing import Dict, List, Tuple, Any, Optional, Callable
from
yangson.exceptions
import
YangsonException
,
NonexistentSchemaNode
,
SchemaError
,
SemanticError
from
yangson.schemanode
import
ContainerNode
,
ListNode
,
GroupNode
,
LeafNode
from
yangson.instance
import
NonexistentInstance
,
InstanceValueError
,
RootNode
from
yangson.instance
import
NonexistentInstance
,
InstanceValueError
,
RootNode
,
ActionName
from
yangson.instvalue
import
ArrayValue
from
.
import
config
...
...
@@ -469,6 +469,77 @@ class HttpHandlersImpl:
exception
=
e
)
# Check if we are calling an action
ii
=
self
.
ds
.
parse_ii
(
rpc1
.
path
,
rpc1
.
path_format
)
if
isinstance
(
ii
[
-
1
],
ActionName
):
# Calling action on a node
ns
=
tuple
(
filter
(
lambda
seg
:
hasattr
(
seg
,
"namespace"
)
and
(
seg
.
namespace
is
not
None
),
ii
))[
-
1
]
.
namespace
try
:
input_args
=
json_data
[
ns
+
":input"
]
except
KeyError
as
e
:
http_resp
=
HttpResponse
.
error
(
HttpStatus
.
BadRequest
,
RestconfErrType
.
Protocol
,
ERRTAG_INVVALUE
,
exception
=
e
)
else
:
rpc1
.
op_input_args
=
input_args
try
:
root_running
=
self
.
ds
.
get_data_root
()
ret_data
=
self
.
ds
.
invoke_action_rpc
(
root_running
,
rpc1
)
if
ret_data
is
None
:
http_resp
=
HttpResponse
.
empty
(
HttpStatus
.
NoContent
,
status_in_body
=
False
)
else
:
if
not
isinstance
(
ret_data
,
str
):
response
=
json
.
dumps
(
ret_data
,
indent
=
4
)
else
:
response
=
ret_data
http_resp
=
HttpResponse
(
HttpStatus
.
Ok
,
response
.
encode
(),
CTYPE_YANG_JSON
)
except
NacmForbiddenError
as
e
:
http_resp
=
HttpResponse
.
error
(
HttpStatus
.
Forbidden
,
RestconfErrType
.
Protocol
,
ERRTAG_ACCDENIED
,
exception
=
e
)
except
(
NonexistentSchemaNode
,
NonexistentInstance
)
as
e
:
http_resp
=
HttpResponse
.
error
(
HttpStatus
.
NotFound
,
RestconfErrType
.
Protocol
,
ERRTAG_INVVALUE
,
exception
=
e
)
except
NoHandlerForOpError
as
e
:
http_resp
=
HttpResponse
.
error
(
HttpStatus
.
BadRequest
,
RestconfErrType
.
Protocol
,
ERRTAG_OPNOTSUPPORTED
,
exception
=
e
)
except
(
SchemaError
,
SemanticError
)
as
e
:
http_resp
=
HttpResponse
.
error
(
HttpStatus
.
BadRequest
,
RestconfErrType
.
Protocol
,
ERRTAG_INVVALUE
,
exception
=
e
)
except
(
OpHandlerFailedError
,
StagingDataException
,
YangsonException
)
as
e
:
http_resp
=
HttpResponse
.
error
(
HttpStatus
.
InternalServerError
,
RestconfErrType
.
Protocol
,
ERRTAG_OPFAILED
,
exception
=
e
)
except
ValueError
as
e
:
http_resp
=
HttpResponse
.
error
(
HttpStatus
.
BadRequest
,
RestconfErrType
.
Protocol
,
ERRTAG_INVVALUE
,
exception
=
e
)
else
:
# Creating new node
try
:
self
.
ds
.
lock_data
(
username
)
...
...
jetconf/jetconf.py
View file @
863d6bb1
...
...
@@ -5,7 +5,7 @@ from importlib import import_module
from
pkg_resources
import
resource_string
from
yangson.enumerations
import
ContentType
,
ValidationScope
from
yangson.exceptions
import
YangsonException
from
yangson.exceptions
import
YangsonException
,
ModuleNotFound
from
yangson.schemanode
import
SchemaError
,
SemanticError
from
yangson.datamodel
import
DataModel
...
...
@@ -45,6 +45,7 @@ class Jetconf:
usr_state_data_handlers
=
import_module
(
backend_package
+
".usr_state_data_handlers"
)
usr_conf_data_handlers
=
import_module
(
backend_package
+
".usr_conf_data_handlers"
)
usr_op_handlers
=
import_module
(
backend_package
+
".usr_op_handlers"
)
usr_action_handlers
=
import_module
(
backend_package
+
".usr_action_handlers"
)
usr_datastore
=
import_module
(
backend_package
+
".usr_datastore"
)
except
ImportError
as
e
:
raise
JetconfInitError
(
...
...
@@ -59,7 +60,10 @@ class Jetconf:
# Load data model
yang_mod_dir
=
self
.
config
.
glob
[
"YANG_LIB_DIR"
]
yang_lib_str
=
resource_string
(
backend_package
,
"yang-library-data.json"
)
.
decode
(
"utf-8"
)
try
:
datamodel
=
DataModel
(
yang_lib_str
,
[
yang_mod_dir
])
except
ModuleNotFound
as
e
:
raise
JetconfInitError
(
"Cannot find YANG module
\"
{} ({})
\"
in YANG library"
.
format
(
e
.
name
,
e
.
rev
))
# Datastore init
datastore
=
usr_datastore
.
UserDatastore
(
...
...
@@ -93,6 +97,9 @@ class Jetconf:
op_internal
.
register_op_handlers
(
datastore
)
usr_op_handlers
.
register_op_handlers
(
datastore
)
# Register handlers for actions
usr_action_handlers
.
register_action_handlers
(
datastore
)
# Init backend package
if
self
.
usr_init
is
not
None
:
try
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment