On 5/18/23 05:50, Terry Wilson wrote:
> This adds a Python version of the async DNS support added in:
>
> 771680d96 DNS: Add basic support for asynchronous DNS resolving
>
> The above version uses the unbound C library, and this
> implimentation uses the SWIG-wrapped Python version of that.
>
> In the event that the Python unbound library is not available,
> a warning will be logged and the resolve() method will just
> return None. For the case where inet_parse_active() is passed
> an IP address, it will not try to resolve it, so existing
> behavior should be preserved in the case that the unbound
> library is unavailable.
>
> Intentional differences from the C version are as follows:
>
> OVS_HOSTS_FILE environment variable can bet set to override
> the system 'hosts' file. This is primarily to allow testing to
> be done without requiring network connectivity.
>
> Since resolution can still be done via hosts file lookup, DNS
> lookups are not disabled when resolv.conf cannot be loaded.
>
> The Python socket_util module has fallen behind its C equivalent.
> The bare minimum change was done to inet_parse_active() to support
> sync/async dns, as there is no equivalent to
> parse_sockaddr_components(), inet_parse_passive(), etc. A TODO
> was added to bring socket_util.py up to equivalency to the C
> version.
>
> Signed-off-by: Terry Wilson <[email protected]>
> ---
Thanks, Terry! The patch looks good in general, except for a
few things that Adrian pointed out in his review.
See a couple additional comments below.
> .github/workflows/build-and-test.yml | 4 +-
> debian/control.in | 1 +
> python/TODO.rst | 7 +
> python/automake.mk | 2 +
> python/ovs/dns_resolve.py | 257 ++++++++++++++++++++++++
> python/ovs/socket_util.py | 21 +-
> python/ovs/stream.py | 2 +-
> python/ovs/tests/test_dns_resolve.py | 280 +++++++++++++++++++++++++++
> python/setup.py | 6 +-
> rhel/openvswitch-fedora.spec.in | 2 +-
> tests/vlog.at | 2 +
> 11 files changed, 575 insertions(+), 9 deletions(-)
> create mode 100644 python/ovs/dns_resolve.py
> create mode 100644 python/ovs/tests/test_dns_resolve.py
>
<snip>
> diff --git a/python/ovs/dns_resolve.py b/python/ovs/dns_resolve.py
> new file mode 100644
> index 000000000..463a39277
> --- /dev/null
> +++ b/python/ovs/dns_resolve.py
> @@ -0,0 +1,257 @@
Generally, each file with a code should have a copyright header
with a license boilerplate.
It's probably less important for test files, but that is what
our current coding style document is asking to do.
> +import collections
> +import functools
> +import ipaddress
> +import os
> +import threading
> +import time
> +import typing
> +
> +try:
> + import unbound # type: ignore
> +except ImportError:
> + pass
> +
> +import ovs.vlog
> +
> +vlog = ovs.vlog.Vlog("dns_resolve")
> +
> +
> +class DNSRequest:
> + INVALID = 0
> + PENDING = 1
> + GOOD = 2
> + ERROR = 3
> +
> + def __init__(self, name):
> + self.name = name
> + self.state = self.INVALID
> + self.time = None
> + self.result = None # set by DNSResolver._callback
> + self.ttl = None
> +
> + @property
> + def expired(self):
> + return time.time() > self.time + self.ttl
> +
> + @property
> + def is_valid(self):
> + return self.state == self.GOOD and not self.expired
> +
> + def __str__(self):
> + return (f"DNSRequest(name={self.name}, state={self.state}, "
> + f"time={self.time}, result={self.result})")
> +
> +
> +class DefaultReqDict(collections.defaultdict):
> + def __init__(self):
> + super().__init__(DNSRequest)
> +
> + def __missing__(self, key):
> + ret = self.default_factory(key)
> + self[key] = ret
> + return ret
> +
> +
> +class UnboundException(Exception):
> +
> + def __init__(self, message, errno):
> + try:
> + msg = f"{message}: {unbound.ub_strerror(errno)}"
> + except NameError:
> + msg = message
> + super().__init__(msg)
> +
> +
> +def dns_enabled(func):
> + @functools.wraps(func)
> + def wrapper(self, *args, **kwargs):
> + if self.dns_enabled:
> + return func(self, *args, **kwargs)
> + vlog.err("DNS support requires the python unbound library")
> + return wrapper
> +
> +
> +class singleton:
> + def __init__(self, klass):
> + self._klass = klass
> + self._instance = None
> +
> + def __call__(self, *args, **kwargs):
> + if self._instance is None:
> + self._instance = self._klass(*args, **kwargs)
> + return self._instance
> +
> +
> +@singleton
> +class DNSResolver:
> + def __init__(self, is_daemon: bool = False):
> + """Create a resolver instance
> +
> + If is_daemon is true, set the resolver to handle requests
> + asynchronously. The following environment variables are processed:
> +
> + OVS_UNBOUND_CONF: The filename for an unbound.conf file
> + OVS_RESOLVE_CONF: A filename to override the system default
> resolv.conf
s/OVS_RESOLVE_CONF/OVS_RESOLV_CONF/
Best regards, Ilya Maximets.
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev