Page Menu
Home
VyOS Platform
Search
Configure Global Search
Log In
Files
F38741998
configtree.py
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
configtree.py
View Options
# configtree -- a standalone VyOS config file manipulation library (Python bindings)
# Copyright (C) 2018 VyOS maintainers and contributors
#
# This library is free software; you can redistribute it and/or modify it under the terms of
# the GNU Lesser General Public License as published by the Free Software Foundation;
# either version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along with this library;
# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import
re
import
json
from
ctypes
import
cdll
,
c_char_p
,
c_void_p
,
c_int
def
escape_backslash
(
string
:
str
)
->
str
:
"""Escape single backslashes in string that are not in escape sequence"""
p
=
re
.
compile
(
r'(?<!\\)[\\](?!b|f|n|r|t|\\[^bfnrt])'
)
result
=
p
.
sub
(
r'\\\\'
,
string
)
return
result
def
extract_version
(
s
):
""" Extract the version string from the config string """
t
=
re
.
split
(
'(^//)'
,
s
,
maxsplit
=
1
,
flags
=
re
.
MULTILINE
)
return
(
s
,
''
.
join
(
t
[
1
:]))
def
check_path
(
path
):
# Necessary type checking
if
not
isinstance
(
path
,
list
):
raise
TypeError
(
"Expected a list, got a {}"
.
format
(
type
(
path
)))
else
:
pass
class
ConfigTreeError
(
Exception
):
pass
class
ConfigTree
(
object
):
def
__init__
(
self
,
config_string
,
libpath
=
'/usr/lib/libvyosconfig.so.0'
):
self
.
__config
=
None
self
.
__lib
=
cdll
.
LoadLibrary
(
libpath
)
# Import functions
self
.
__from_string
=
self
.
__lib
.
from_string
self
.
__from_string
.
argtypes
=
[
c_char_p
]
self
.
__from_string
.
restype
=
c_void_p
self
.
__get_error
=
self
.
__lib
.
get_error
self
.
__get_error
.
argtypes
=
[]
self
.
__get_error
.
restype
=
c_char_p
self
.
__to_string
=
self
.
__lib
.
to_string
self
.
__to_string
.
argtypes
=
[
c_void_p
]
self
.
__to_string
.
restype
=
c_char_p
self
.
__to_commands
=
self
.
__lib
.
to_commands
self
.
__to_commands
.
argtypes
=
[
c_void_p
]
self
.
__to_commands
.
restype
=
c_char_p
self
.
__to_json
=
self
.
__lib
.
to_json
self
.
__to_json
.
argtypes
=
[
c_void_p
]
self
.
__to_json
.
restype
=
c_char_p
self
.
__to_json_ast
=
self
.
__lib
.
to_json_ast
self
.
__to_json_ast
.
argtypes
=
[
c_void_p
]
self
.
__to_json_ast
.
restype
=
c_char_p
self
.
__set_add_value
=
self
.
__lib
.
set_add_value
self
.
__set_add_value
.
argtypes
=
[
c_void_p
,
c_char_p
,
c_char_p
]
self
.
__set_add_value
.
restype
=
c_int
self
.
__delete_value
=
self
.
__lib
.
delete_value
self
.
__delete_value
.
argtypes
=
[
c_void_p
,
c_char_p
,
c_char_p
]
self
.
__delete_value
.
restype
=
c_int
self
.
__delete
=
self
.
__lib
.
delete_node
self
.
__delete
.
argtypes
=
[
c_void_p
,
c_char_p
]
self
.
__delete
.
restype
=
c_int
self
.
__rename
=
self
.
__lib
.
rename_node
self
.
__rename
.
argtypes
=
[
c_void_p
,
c_char_p
,
c_char_p
]
self
.
__rename
.
restype
=
c_int
self
.
__copy
=
self
.
__lib
.
copy_node
self
.
__copy
.
argtypes
=
[
c_void_p
,
c_char_p
,
c_char_p
]
self
.
__copy
.
restype
=
c_int
self
.
__set_replace_value
=
self
.
__lib
.
set_replace_value
self
.
__set_replace_value
.
argtypes
=
[
c_void_p
,
c_char_p
,
c_char_p
]
self
.
__set_replace_value
.
restype
=
c_int
self
.
__set_valueless
=
self
.
__lib
.
set_valueless
self
.
__set_valueless
.
argtypes
=
[
c_void_p
,
c_char_p
]
self
.
__set_valueless
.
restype
=
c_int
self
.
__exists
=
self
.
__lib
.
exists
self
.
__exists
.
argtypes
=
[
c_void_p
,
c_char_p
]
self
.
__exists
.
restype
=
c_int
self
.
__list_nodes
=
self
.
__lib
.
list_nodes
self
.
__list_nodes
.
argtypes
=
[
c_void_p
,
c_char_p
]
self
.
__list_nodes
.
restype
=
c_char_p
self
.
__return_value
=
self
.
__lib
.
return_value
self
.
__return_value
.
argtypes
=
[
c_void_p
,
c_char_p
]
self
.
__return_value
.
restype
=
c_char_p
self
.
__return_values
=
self
.
__lib
.
return_values
self
.
__return_values
.
argtypes
=
[
c_void_p
,
c_char_p
]
self
.
__return_values
.
restype
=
c_char_p
self
.
__is_tag
=
self
.
__lib
.
is_tag
self
.
__is_tag
.
argtypes
=
[
c_void_p
,
c_char_p
]
self
.
__is_tag
.
restype
=
c_int
self
.
__set_tag
=
self
.
__lib
.
set_tag
self
.
__set_tag
.
argtypes
=
[
c_void_p
,
c_char_p
]
self
.
__set_tag
.
restype
=
c_int
self
.
__destroy
=
self
.
__lib
.
destroy
self
.
__destroy
.
argtypes
=
[
c_void_p
]
config_section
,
version_section
=
extract_version
(
config_string
)
config_section
=
escape_backslash
(
config_section
)
config
=
self
.
__from_string
(
config_section
.
encode
())
if
config
is
None
:
msg
=
self
.
__get_error
()
.
decode
()
raise
ValueError
(
"Failed to parse config: {0}"
.
format
(
msg
))
else
:
self
.
__config
=
config
self
.
__version
=
version_section
def
__del__
(
self
):
if
self
.
__config
is
not
None
:
self
.
__destroy
(
self
.
__config
)
def
__str__
(
self
):
return
self
.
to_string
()
def
to_string
(
self
):
config_string
=
self
.
__to_string
(
self
.
__config
)
.
decode
()
config_string
=
"{0}
\n
{1}"
.
format
(
config_string
,
self
.
__version
)
return
config_string
def
to_commands
(
self
):
return
self
.
__to_commands
(
self
.
__config
)
.
decode
()
def
to_json
(
self
):
return
self
.
__to_json
(
self
.
__config
)
.
decode
()
def
to_json_ast
(
self
):
return
self
.
__to_json_ast
(
self
.
__config
)
.
decode
()
def
set
(
self
,
path
,
value
=
None
,
replace
=
True
):
"""Set new entry in VyOS configuration.
path: configuration path e.g. 'system dns forwarding listen-address'
value: value to be added to node, e.g. '172.18.254.201'
replace: True: current occurance will be replaced
False: new value will be appended to current occurances - use
this for adding values to a multi node
"""
check_path
(
path
)
path_str
=
" "
.
join
(
map
(
str
,
path
))
.
encode
()
if
value
is
None
:
self
.
__set_valueless
(
self
.
__config
,
path_str
)
else
:
if
replace
:
self
.
__set_replace_value
(
self
.
__config
,
path_str
,
str
(
value
)
.
encode
())
else
:
self
.
__set_add_value
(
self
.
__config
,
path_str
,
str
(
value
)
.
encode
())
def
delete
(
self
,
path
):
check_path
(
path
)
path_str
=
" "
.
join
(
map
(
str
,
path
))
.
encode
()
self
.
__delete
(
self
.
__config
,
path_str
)
def
delete_value
(
self
,
path
,
value
):
check_path
(
path
)
path_str
=
" "
.
join
(
map
(
str
,
path
))
.
encode
()
self
.
__delete_value
(
self
.
__config
,
path_str
,
value
.
encode
())
def
rename
(
self
,
path
,
new_name
):
check_path
(
path
)
path_str
=
" "
.
join
(
map
(
str
,
path
))
.
encode
()
newname_str
=
new_name
.
encode
()
# Check if a node with intended new name already exists
new_path
=
path
[:
-
1
]
+
[
new_name
]
if
self
.
exists
(
new_path
):
raise
ConfigTreeError
()
res
=
self
.
__rename
(
self
.
__config
,
path_str
,
newname_str
)
if
(
res
!=
0
):
raise
ConfigTreeError
(
"Path [{}] doesn't exist"
.
format
(
path
))
def
copy
(
self
,
old_path
,
new_path
):
check_path
(
old_path
)
check_path
(
new_path
)
oldpath_str
=
" "
.
join
(
map
(
str
,
old_path
))
.
encode
()
newpath_str
=
" "
.
join
(
map
(
str
,
new_path
))
.
encode
()
# Check if a node with intended new name already exists
if
self
.
exists
(
new_path
):
raise
ConfigTreeError
()
res
=
self
.
__copy
(
self
.
__config
,
oldpath_str
,
newpath_str
)
if
(
res
!=
0
):
raise
ConfigTreeError
(
"Path [{}] doesn't exist"
.
format
(
old_path
))
def
exists
(
self
,
path
):
check_path
(
path
)
path_str
=
" "
.
join
(
map
(
str
,
path
))
.
encode
()
res
=
self
.
__exists
(
self
.
__config
,
path_str
)
if
(
res
==
0
):
return
False
else
:
return
True
def
list_nodes
(
self
,
path
):
check_path
(
path
)
path_str
=
" "
.
join
(
map
(
str
,
path
))
.
encode
()
res_json
=
self
.
__list_nodes
(
self
.
__config
,
path_str
)
.
decode
()
res
=
json
.
loads
(
res_json
)
if
res
is
None
:
raise
ConfigTreeError
(
"Path [{}] doesn't exist"
.
format
(
path_str
))
else
:
return
res
def
return_value
(
self
,
path
):
check_path
(
path
)
path_str
=
" "
.
join
(
map
(
str
,
path
))
.
encode
()
res_json
=
self
.
__return_value
(
self
.
__config
,
path_str
)
.
decode
()
res
=
json
.
loads
(
res_json
)
if
res
is
None
:
raise
ConfigTreeError
(
"Path [{}] doesn't exist"
.
format
(
path_str
))
else
:
return
res
def
return_values
(
self
,
path
):
check_path
(
path
)
path_str
=
" "
.
join
(
map
(
str
,
path
))
.
encode
()
res_json
=
self
.
__return_values
(
self
.
__config
,
path_str
)
.
decode
()
res
=
json
.
loads
(
res_json
)
if
res
is
None
:
raise
ConfigTreeError
(
"Path [{}] doesn't exist"
.
format
(
path_str
))
else
:
return
res
def
is_tag
(
self
,
path
):
check_path
(
path
)
path_str
=
" "
.
join
(
map
(
str
,
path
))
.
encode
()
res
=
self
.
__is_tag
(
self
.
__config
,
path_str
)
if
(
res
>=
1
):
return
True
else
:
return
False
def
set_tag
(
self
,
path
):
check_path
(
path
)
path_str
=
" "
.
join
(
map
(
str
,
path
))
.
encode
()
res
=
self
.
__set_tag
(
self
.
__config
,
path_str
)
if
(
res
==
0
):
return
True
else
:
raise
ConfigTreeError
(
"Path [{}] doesn't exist"
.
format
(
path_str
))
File Metadata
Details
Attached
Mime Type
text/x-script.python
Expires
Mon, Dec 15, 9:08 PM (2 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3057537
Default Alt Text
configtree.py (9 KB)
Attached To
Mode
rVYOSONEX vyos-1x
Attached
Detach File
Event Timeline
Log In to Comment