diff options
Diffstat (limited to 'python')
-rw-r--r-- | python/vyos/config.py | 8 | ||||
-rw-r--r-- | python/vyos/configtree.py | 37 | ||||
-rw-r--r-- | python/vyos/validate.py | 102 |
3 files changed, 132 insertions, 15 deletions
diff --git a/python/vyos/config.py b/python/vyos/config.py index 5af830480..bcf04225b 100644 --- a/python/vyos/config.py +++ b/python/vyos/config.py @@ -280,8 +280,8 @@ class Config(object): else: try: out = self._run(self._make_command('returnValues', full_path)) - values = out.split() - return list(map(lambda x: re.sub(r'^\'(.*)\'$', r'\1',x), values)) + values = re.findall(r"\'(.*?)\'", out) + return values except VyOSError: return(default) @@ -309,8 +309,8 @@ class Config(object): if self.is_tag(path): try: out = self._run(self._make_command('listNodes', full_path)) - values = out.split() - return list(map(lambda x: re.sub(r'^\'(.*)\'$', r'\1',x), values)) + values = re.findall(r"\'(.*?)\'", out) + return values except VyOSError: return(default) else: diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py index 4b46a1fb3..39fe41669 100644 --- a/python/vyos/configtree.py +++ b/python/vyos/configtree.py @@ -24,6 +24,7 @@ def strip_comments(s): IN_COMMENT = 1 i = len(s) - 1 + state = INITIAL config_end = 0 @@ -42,14 +43,11 @@ def strip_comments(s): break elif (state == INITIAL) and (c == '/'): # A comment begins, or it's a stray slash - try: - if (s[i-1] == '*'): - state = IN_COMMENT - i -= 2 - else: - raise ValueError("Invalid syntax") - except: - raise ValueError("Invalid syntax") + if (s[i-1] == '*'): + state = IN_COMMENT + i -= 2 + else: + raise ValueError("Invalid syntax: stray slash at character {0}".format(i + 1)) elif (state == INITIAL) and (c == '}'): # We are not inside a comment, that's the end of the last node config_end = i + 1 @@ -61,12 +59,12 @@ def strip_comments(s): state = INITIAL i -= 2 except: - raise ValueError("Invalid syntax") + raise ValueError("Invalid syntax: malformed commend end at character {0}".format(i + 1)) elif (state == IN_COMMENT) and (c != '*'): # Ignore everything inside comments, including braces i -= 1 else: - raise ValueError("Invalid syntax") + raise ValueError("Invalid syntax at character {0}: invalid character {1}".format(i + 1, c)) return (s[0:config_end], s[config_end+1:]) @@ -92,6 +90,10 @@ class ConfigTree(object): 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 @@ -112,6 +114,10 @@ class ConfigTree(object): 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.__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 @@ -150,10 +156,12 @@ class ConfigTree(object): config_section, comments_section = strip_comments(config_string) config = self.__from_string(config_section.encode()) if config is None: - raise ValueError("Parse error") + msg = self.__get_error().decode() + raise ValueError("Failed to parse config: {0}".format(msg)) else: self.__config = config self.__comments = comments_section + def __del__(self): if self.__config is not None: self.__destroy(self.__config) @@ -193,6 +201,13 @@ class ConfigTree(object): self.__delete_value(self.__config, path_str, value.encode()) + def rename(self, path, newname): + check_path(path) + path_str = " ".join(map(str, path)).encode() + newname_str = newname.encode() + + self.__rename(self.__config, path_str, newname_str) + def exists(self, path): check_path(path) path_str = " ".join(map(str, path)).encode() diff --git a/python/vyos/validate.py b/python/vyos/validate.py new file mode 100644 index 000000000..8def0a510 --- /dev/null +++ b/python/vyos/validate.py @@ -0,0 +1,102 @@ +# Copyright 2018 VyOS maintainers and contributors <maintainers@vyos.io> +# +# 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, see <http://www.gnu.org/licenses/>. + +import netifaces +import ipaddress + +def is_ipv4(addr): + """ + Check addr if it is an IPv4 address/network. + + Return True/False + """ + if ipaddress.ip_network(addr).version == 4: + return True + else: + return False + +def is_ipv6(addr): + """ + Check addr if it is an IPv6 address/network. + + Return True/False + """ + if ipaddress.ip_network(addr).version == 6: + return True + else: + return False + +def is_addr_assigned(addr): + """ + Verify if the given IPv4/IPv6 address is assigned to any interface on this + system. + + Return True/False + """ + + # determine IP version (AF_INET or AF_INET6) depending on passed address + addr_type = netifaces.AF_INET + if is_ipv6(addr): + addr_type = netifaces.AF_INET6 + + for interface in netifaces.interfaces(): + # check if the requested address type is configured at all + if addr_type in netifaces.ifaddresses(interface).keys(): + # Check every IP address on this interface for a match + for ip in netifaces.ifaddresses(interface)[addr_type]: + # Check if it matches to the address requested + if ip['addr'] == addr: + return True + + return False + +def is_subnet_connected(subnet, primary=False): + """ + Verify is the given IPv4/IPv6 subnet is connected to any interface on this + system. + + primary check if the subnet is reachable via the primary IP address of this + interface, or in other words has a broadcast address configured. ISC DHCP + for instance will complain if it should listen on non broadcast interfaces. + + Return True/False + """ + + # determine IP version (AF_INET or AF_INET6) depending on passed address + addr_type = netifaces.AF_INET + if is_ipv6(subnet): + addr_type = netifaces.AF_INET6 + + for interface in netifaces.interfaces(): + # check if the requested address type is configured at all + if addr_type not in netifaces.ifaddresses(interface).keys(): + continue + + # An interface can have multiple addresses, but some software components + # only support the primary address :( + if primary: + ip = netifaces.ifaddresses(interface)[addr_type][0]['addr'] + if ipaddress.ip_address(ip) in ipaddress.ip_network(subnet): + return True + else: + # Check every assigned IP address if it is connected to the subnet + # in question + for ip in netifaces.ifaddresses(interface)[addr_type]: + # remove interface extension (e.g. %eth0) that gets thrown on the end of _some_ addrs + addr = ip['addr'].split('%')[0] + if ipaddress.ip_address(addr) in ipaddress.ip_network(subnet): + return True + + return False |