diff options
Diffstat (limited to 'python')
-rw-r--r-- | python/Makefile.am | 15 | ||||
-rw-r--r-- | python/Makefile.in | 525 | ||||
-rw-r--r-- | python/libknot/__init__.py.in | 3 | ||||
-rwxr-xr-x | python/libknot/control.py | 441 | ||||
-rw-r--r-- | python/setup.py.in | 27 | ||||
-rwxr-xr-x | python/stats_http.py | 73 | ||||
-rwxr-xr-x | python/stats_influxdb.py | 79 |
7 files changed, 1163 insertions, 0 deletions
diff --git a/python/Makefile.am b/python/Makefile.am new file mode 100644 index 0000000..dd76d62 --- /dev/null +++ b/python/Makefile.am @@ -0,0 +1,15 @@ +EXTRA_DIST = \ + libknot/__init__.py.in \ + libknot/control.py \ + setup.py.in \ + stats_http.py \ + stats_influxdb.py + +clean-local: + -rm -rf dist *.egg-info + +dist: clean-local + python3 setup.py sdist + +upload: + twine upload dist/* diff --git a/python/Makefile.in b/python/Makefile.in new file mode 100644 index 0000000..19d328a --- /dev/null +++ b/python/Makefile.in @@ -0,0 +1,525 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = python +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cc_clang.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/code-coverage.m4 \ + $(top_srcdir)/m4/knot-lib-version.m4 \ + $(top_srcdir)/m4/knot-module.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/sanitizer.m4 $(top_srcdir)/m4/visibility.m4 \ + $(top_srcdir)/m4/knot-version.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/src/config.h +CONFIG_CLEAN_FILES = setup.py +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/setup.py.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_CLANG_VERSION = @CC_CLANG_VERSION@ +CFLAGS = @CFLAGS@ +CFLAG_VISIBILITY = @CFLAG_VISIBILITY@ +CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +HAVE_VISIBILITY = @HAVE_VISIBILITY@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KNOT_VERSION_MAJOR = @KNOT_VERSION_MAJOR@ +KNOT_VERSION_MINOR = @KNOT_VERSION_MINOR@ +KNOT_VERSION_PATCH = @KNOT_VERSION_PATCH@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAG_EXCLUDE_LIBS = @LDFLAG_EXCLUDE_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +cap_ng_CFLAGS = @cap_ng_CFLAGS@ +cap_ng_LIBS = @cap_ng_LIBS@ +conf_mapsize = @conf_mapsize@ +config_dir = @config_dir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +dlopen_LIBS = @dlopen_LIBS@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +external_lmdb_LIBS = @external_lmdb_LIBS@ +fuzzer_CFLAGS = @fuzzer_CFLAGS@ +fuzzer_LDFLAGS = @fuzzer_LDFLAGS@ +gnutls_CFLAGS = @gnutls_CFLAGS@ +gnutls_LIBS = @gnutls_LIBS@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libdnssec_SONAME = @libdnssec_SONAME@ +libdnssec_SOVERSION = @libdnssec_SOVERSION@ +libdnssec_VERSION_INFO = @libdnssec_VERSION_INFO@ +libedit_CFLAGS = @libedit_CFLAGS@ +libedit_LIBS = @libedit_LIBS@ +libexecdir = @libexecdir@ +libfstrm_CFLAGS = @libfstrm_CFLAGS@ +libfstrm_LIBS = @libfstrm_LIBS@ +libidn2_CFLAGS = @libidn2_CFLAGS@ +libidn2_LIBS = @libidn2_LIBS@ +libidn_CFLAGS = @libidn_CFLAGS@ +libidn_LIBS = @libidn_LIBS@ +libknot_SONAME = @libknot_SONAME@ +libknot_SOVERSION = @libknot_SOVERSION@ +libknot_VERSION_INFO = @libknot_VERSION_INFO@ +libmaxminddb_CFLAGS = @libmaxminddb_CFLAGS@ +libmaxminddb_LIBS = @libmaxminddb_LIBS@ +libprotobuf_c_CFLAGS = @libprotobuf_c_CFLAGS@ +libprotobuf_c_LIBS = @libprotobuf_c_LIBS@ +liburcu_CFLAGS = @liburcu_CFLAGS@ +liburcu_LIBS = @liburcu_LIBS@ +liburcu_PKGCONFIG = @liburcu_PKGCONFIG@ +libzscanner_SONAME = @libzscanner_SONAME@ +libzscanner_SOVERSION = @libzscanner_SOVERSION@ +libzscanner_VERSION_INFO = @libzscanner_VERSION_INFO@ +lmdb_CFLAGS = @lmdb_CFLAGS@ +lmdb_LIBS = @lmdb_LIBS@ +localedir = @localedir@ +localstatedir = @localstatedir@ +malloc_LIBS = @malloc_LIBS@ +mandir = @mandir@ +math_LIBS = @math_LIBS@ +mkdir_p = @mkdir_p@ +module_dir = @module_dir@ +module_instdir = @module_instdir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pthread_LIBS = @pthread_LIBS@ +run_dir = @run_dir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +storage_dir = @storage_dir@ +sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = \ + libknot/__init__.py.in \ + libknot/control.py \ + setup.py.in \ + stats_http.py \ + stats_influxdb.py + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign python/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign python/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +setup.py: $(top_builddir)/config.status $(srcdir)/setup.py.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-local mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + clean-local cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +clean-local: + -rm -rf dist *.egg-info + +dist: clean-local + python3 setup.py sdist + +upload: + twine upload dist/* + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/python/libknot/__init__.py.in b/python/libknot/__init__.py.in new file mode 100644 index 0000000..5984340 --- /dev/null +++ b/python/libknot/__init__.py.in @@ -0,0 +1,3 @@ +"""Python libknot interface.""" + +LIBKNOT_VERSION = "@libknot_SOVERSION@" diff --git a/python/libknot/control.py b/python/libknot/control.py new file mode 100755 index 0000000..ced1be6 --- /dev/null +++ b/python/libknot/control.py @@ -0,0 +1,441 @@ +"""Libknot server control interface wrapper. + +Example: + import json + from libknot.control import * + + #load_lib("/usr/lib/libknot.so") + + ctl = KnotCtl() + ctl.connect("/var/run/knot/knot.sock") + + try: + ctl.send_block(cmd="conf-begin") + resp = ctl.receive_block() + + ctl.send_block(cmd="conf-set", section="zone", item="domain", data="test") + resp = ctl.receive_block() + + ctl.send_block(cmd="conf-commit") + resp = ctl.receive_block() + + ctl.send_block(cmd="conf-read", section="zone", item="domain") + resp = ctl.receive_block() + print(json.dumps(resp, indent=4)) + finally: + ctl.send(KnotCtlType.END) + ctl.close() +""" + +from ctypes import cdll, c_void_p, c_int, c_char_p, c_uint, byref +from enum import IntEnum +import sys + +CTL_ALLOC = None +CTL_FREE = None +CTL_SET_TIMEOUT = None +CTL_CONNECT = None +CTL_CLOSE = None +CTL_SEND = None +CTL_RECEIVE = None +CTL_ERROR = None + + +def load_lib(path=None): + """Loads the libknot library.""" + + if path is None: + version = "" + try: + from libknot import LIBKNOT_VERSION + version = ".%u" % int(LIBKNOT_VERSION) + except: + pass + + if sys.platform == "darwin": + path = "libknot%s.dylib" % version + else: + path = "libknot.so%s" % version + LIB = cdll.LoadLibrary(path) + + global CTL_ALLOC + CTL_ALLOC = LIB.knot_ctl_alloc + CTL_ALLOC.restype = c_void_p + + global CTL_FREE + CTL_FREE = LIB.knot_ctl_free + CTL_FREE.argtypes = [c_void_p] + + global CTL_SET_TIMEOUT + CTL_SET_TIMEOUT = LIB.knot_ctl_set_timeout + CTL_SET_TIMEOUT.argtypes = [c_void_p, c_int] + + global CTL_CONNECT + CTL_CONNECT = LIB.knot_ctl_connect + CTL_CONNECT.restype = c_int + CTL_CONNECT.argtypes = [c_void_p, c_char_p] + + global CTL_CLOSE + CTL_CLOSE = LIB.knot_ctl_close + CTL_CLOSE.argtypes = [c_void_p] + + global CTL_SEND + CTL_SEND = LIB.knot_ctl_send + CTL_SEND.restype = c_int + CTL_SEND.argtypes = [c_void_p, c_uint, c_void_p] + + global CTL_RECEIVE + CTL_RECEIVE = LIB.knot_ctl_receive + CTL_RECEIVE.restype = c_int + CTL_RECEIVE.argtypes = [c_void_p, c_void_p, c_void_p] + + global CTL_ERROR + CTL_ERROR = LIB.knot_strerror + CTL_ERROR.restype = c_char_p + CTL_ERROR.argtypes = [c_int] + + +class KnotCtlError(Exception): + """Libknot server control error.""" + + def __init__(self, message, data=None): + """ + @type message: str + @type data: KnotCtlData + """ + + self.message = message + self.data = data + + def __str__(self): + return "%s (data: %s)" % (self.message, self.data) + + +class KnotCtlType(IntEnum): + """Libknot server control data unit types.""" + + END = 0 + DATA = 1 + EXTRA = 2 + BLOCK = 3 + + +class KnotCtlDataIdx(IntEnum): + """Libknot server control data unit indices.""" + + COMMAND = 0 + FLAGS = 1 + ERROR = 2 + SECTION = 3 + ITEM = 4 + ID = 5 + ZONE = 6 + OWNER = 7 + TTL = 8 + TYPE = 9 + DATA = 10 + FILTER = 11 + + +class KnotCtlData(object): + """Libknot server control data unit.""" + + DataArray = c_char_p * len(KnotCtlDataIdx) + + def __init__(self): + self.data = self.DataArray() + + def __str__(self): + string = str() + + for idx in KnotCtlDataIdx: + if self.data[idx]: + if string: + string += ", " + string += "%s = %s" % (idx.name, self.data[idx]) + + return string + + def __getitem__(self, index): + """Data unit item getter. + + @type index: KnotCtlDataIdx + @rtype: str + """ + + value = self.data[index] + if not value: + value = str() + return value if isinstance(value, str) else value.decode() + + def __setitem__(self, index, value): + """Data unit item setter. + + @type index: KnotCtlDataIdx + @type value: str + """ + + self.data[index] = c_char_p(value.encode()) if value else c_char_p() + +class KnotCtl(object): + """Libknot server control interface.""" + + def __init__(self): + if not CTL_ALLOC: + load_lib() + self.obj = CTL_ALLOC() + + def __del__(self): + CTL_FREE(self.obj) + + def set_timeout(self, timeout): + """Sets control socket operations timeout in seconds. + + @type timeout: int + """ + + CTL_SET_TIMEOUT(self.obj, timeout * 1000) + + def connect(self, path): + """Connect to a specified control UNIX socket. + + @type path: str + """ + + ret = CTL_CONNECT(self.obj, path.encode()) + if ret != 0: + err = CTL_ERROR(ret) + raise KnotCtlError(err if isinstance(err, str) else err.decode()) + + def close(self): + """Disconnects from the current control socket.""" + + CTL_CLOSE(self.obj) + + def send(self, data_type, data=None): + """Sends a data unit to the connected control socket. + + @type data_type: KnotCtlType + @type data: KnotCtlData + """ + + ret = CTL_SEND(self.obj, data_type, + data.data if data else c_char_p()) + if ret != 0: + err = CTL_ERROR(ret) + raise KnotCtlError(err if isinstance(err, str) else err.decode()) + + def receive(self, data=None): + """Receives a data unit from the connected control socket. + + @type data: KnotCtlData + @rtype: KnotCtlType + """ + + data_type = c_uint() + ret = CTL_RECEIVE(self.obj, byref(data_type), + data.data if data else c_char_p()) + if ret != 0: + err = CTL_ERROR(ret) + raise KnotCtlError(err if isinstance(err, str) else err.decode()) + return KnotCtlType(data_type.value) + + def send_block(self, cmd, section=None, item=None, identifier=None, zone=None, + owner=None, ttl=None, rtype=None, data=None, flags=None, + filter=None): + """Sends a control query block. + + @type cmd: str + @type section: str + @type item: str + @type identifier: str + @type zone: str + @type owner: str + @type ttl: str + @type rtype: str + @type data: str + @type filter: str + """ + + query = KnotCtlData() + query[KnotCtlDataIdx.COMMAND] = cmd + query[KnotCtlDataIdx.SECTION] = section + query[KnotCtlDataIdx.ITEM] = item + query[KnotCtlDataIdx.ID] = identifier + query[KnotCtlDataIdx.ZONE] = zone + query[KnotCtlDataIdx.OWNER] = owner + query[KnotCtlDataIdx.TTL] = ttl + query[KnotCtlDataIdx.TYPE] = rtype + query[KnotCtlDataIdx.DATA] = data + query[KnotCtlDataIdx.FLAGS] = flags + query[KnotCtlDataIdx.FILTER] = filter + + self.send(KnotCtlType.DATA, query) + self.send(KnotCtlType.BLOCK) + + def _receive_conf(self, out, reply): + + section = reply[KnotCtlDataIdx.SECTION] + ident = reply[KnotCtlDataIdx.ID] + item = reply[KnotCtlDataIdx.ITEM] + data = reply[KnotCtlDataIdx.DATA] + + # Add the section if not exists. + if section not in out: + out[section] = dict() + + # Add the identifier if not exists. + if ident and ident not in out[section]: + out[section][ident] = dict() + + # Return if no item/value. + if not item: + return + + item_level = out[section][ident] if ident else out[section] + + # Treat alone identifier item differently. + if item in ["id", "domain", "target"]: + if data not in out[section]: + out[section][data] = dict() + else: + if item not in item_level: + item_level[item] = list() + + if data: + item_level[item].append(data) + + def _receive_zone_status(self, out, reply): + + zone = reply[KnotCtlDataIdx.ZONE] + rtype = reply[KnotCtlDataIdx.TYPE] + data = reply[KnotCtlDataIdx.DATA] + + # Add the zone if not exists. + if zone not in out: + out[zone] = dict() + + out[zone][rtype] = data + + def _receive_zone(self, out, reply): + + zone = reply[KnotCtlDataIdx.ZONE] + owner = reply[KnotCtlDataIdx.OWNER] + ttl = reply[KnotCtlDataIdx.TTL] + rtype = reply[KnotCtlDataIdx.TYPE] + data = reply[KnotCtlDataIdx.DATA] + + # Add the zone if not exists. + if zone not in out: + out[zone] = dict() + + if owner not in out[zone]: + out[zone][owner] = dict() + + if rtype not in out[zone][owner]: + out[zone][owner][rtype] = dict() + + # Add the key/value. + out[zone][owner][rtype]["ttl"] = ttl + + if not "data" in out[zone][owner][rtype]: + out[zone][owner][rtype]["data"] = [data] + else: + out[zone][owner][rtype]["data"].append(data) + + def _receive_stats(self, out, reply): + + zone = reply[KnotCtlDataIdx.ZONE] + section = reply[KnotCtlDataIdx.SECTION] + item = reply[KnotCtlDataIdx.ITEM] + idx = reply[KnotCtlDataIdx.ID] + data = int(reply[KnotCtlDataIdx.DATA]) + + # Add the zone if not exists. + if zone: + if "zone" not in out: + out["zone"] = dict() + + if zone not in out["zone"]: + out["zone"][zone] = dict() + + section_level = out["zone"][zone] if zone else out + + if section not in section_level: + section_level[section] = dict() + + if idx: + if item not in section_level[section]: + section_level[section][item] = dict() + + section_level[section][item][idx] = data + else: + section_level[section][item] = data + + def receive_stats(self): + """Receives statistics answer and returns it as a structured dictionary. + + @rtype: dict + """ + + out = dict() + err_reply = None + + while True: + reply = KnotCtlData() + reply_type = self.receive(reply) + + # Stop if not data type. + if reply_type not in [KnotCtlType.DATA, KnotCtlType.EXTRA]: + break + + # Check for an error. + if reply[KnotCtlDataIdx.ERROR]: + err_reply = reply + continue + + self._receive_stats(out, reply) + + if err_reply: + raise KnotCtlError(err_reply[KnotCtlDataIdx.ERROR], err_reply) + + return out + + def receive_block(self): + """Receives a control answer and returns it as a structured dictionary. + + @rtype: dict + """ + + out = dict() + err_reply = None + + while True: + reply = KnotCtlData() + reply_type = self.receive(reply) + + # Stop if not data type. + if reply_type not in [KnotCtlType.DATA, KnotCtlType.EXTRA]: + break + + # Check for an error. + if reply[KnotCtlDataIdx.ERROR]: + err_reply = reply + continue + + # Check for config data. + if reply[KnotCtlDataIdx.SECTION]: + self._receive_conf(out, reply) + # Check for zone data. + elif reply[KnotCtlDataIdx.ZONE]: + if reply[KnotCtlDataIdx.OWNER]: + self._receive_zone(out, reply) + else: + self._receive_zone_status(out, reply) + else: + continue + + if err_reply: + raise KnotCtlError(err_reply[KnotCtlDataIdx.ERROR], err_reply) + + return out diff --git a/python/setup.py.in b/python/setup.py.in new file mode 100644 index 0000000..8d57bc9 --- /dev/null +++ b/python/setup.py.in @@ -0,0 +1,27 @@ +import setuptools + +setuptools.setup( + name='libknot', + version='@PACKAGE_VERSION@', + description='Python bindings for libknot', + author='Daniel Salzman', + author_email='daniel.salzman@nic.cz', + url='https://gitlab.labs.nic.cz/knot/knot-dns', + license='GPL-3.0', + packages=['libknot'], + classifiers=[ # See https://pypi.org/classifiers + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Topic :: Internet :: Name Service (DNS)', + 'Topic :: Software Development :: Libraries', + 'Topic :: System :: Systems Administration', + ] +) diff --git a/python/stats_http.py b/python/stats_http.py new file mode 100755 index 0000000..a38d5f7 --- /dev/null +++ b/python/stats_http.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 + +"""Simple program for exposing statistics from Knot DNS over HTTP/HTTPS.""" + +import http.server +import libknot.control +import json +import ssl +import time + +# Configuration. +#libknot.control.load_lib("../src/.libs/libknot.so") +ctl_socket = "/tmp/knot.sock" +ctl_timeout = 2 +ctl_flags = "" # set "F" for all supported counters. +http_host = "127.0.0.1" +http_port = 8080 +ssl_enable = False +ssl_keyfile = "./mykey.key" +ssl_certfile = "./mycert.crt" + + +class StatsServer(http.server.BaseHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header("Content-type", "text/html") + self.end_headers() + + # Connect to Knot server. + ctl = libknot.control.KnotCtl() + ctl.connect(ctl_socket) + ctl.set_timeout(ctl_timeout) + + # Get global metrics. + global_stats = dict() + try: + ctl.send_block(cmd="stats", flags=ctl_flags) + global_stats = ctl.receive_stats() + except: + pass + + # Get zone metrics. + zone_stats = dict() + try: + ctl.send_block(cmd="zone-stats", flags=ctl_flags) + zone_stats = ctl.receive_stats() + except: + pass + + # Disconnect from the server. + ctl.send(libknot.control.KnotCtlType.END) + ctl.close() + + # Publish the stats. + stats = {**global_stats, **zone_stats} + self.wfile.write(bytes(json.dumps(stats, indent=4, sort_keys=True), "utf-8")) + + +httpd = http.server.HTTPServer((http_host, http_port), StatsServer) + +if ssl_enable: + httpd.socket = ssl.wrap_socket(httpd.socket, keyfile=ssl_keyfile, + certfile=ssl_certfile, server_side=True) + +print("%s: HTTP%s Server Start - %s:%s" % + (time.asctime(), "S" if ssl_enable else "", http_host, http_port)) + +try: + httpd.serve_forever() +except KeyboardInterrupt: + pass + +httpd.server_close() diff --git a/python/stats_influxdb.py b/python/stats_influxdb.py new file mode 100755 index 0000000..33d3709 --- /dev/null +++ b/python/stats_influxdb.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 + +"""Simple program for exporting statistics from Knot DNS to influxdb.""" + +import libknot.control +import io +import json +import os +import time + +# Configuration. +#libknot.control.load_lib("../src/.libs/libknot.so") +ctl_socket = "/tmp/knot.sock" +ctl_timeout = 2 +# InfluxDB parameters. +host = "217.31.192.164" +port = "8086" +db = "KnotDNS" +instance = "Knot" +# Send metrics every N seconds. +send_interval = 5 + + +def send(): + # Connect to Knot server. + ctl = libknot.control.KnotCtl() + ctl.connect(ctl_socket) + ctl.set_timeout(ctl_timeout) + + # Get global metrics. + global_stats = dict() + try: + ctl.send_block(cmd="stats", flags="F") + global_stats = ctl.receive_stats() + except: + pass + + # Get zone metrics. + zone_stats = dict() + try: + ctl.send_block(cmd="zone-stats", flags="F") + zone_stats = ctl.receive_stats() + except: + pass + + # Disconnect from the server. + ctl.send(libknot.control.KnotCtlType.END) + ctl.close() + + # Prepare the metrics to publish. + output = io.StringIO() + + stats = {**global_stats, **zone_stats} + timestamp = str(int(time.time())) + + for metric in stats["server"]: + print("server,instance=" + instance + ",metric=" + metric + " value=" + + stats["server"][metric] + " " + timestamp, file=output) + + for group in stats["mod-stats"]: + for metric in stats["mod-stats"][group]: + print(group + ",instance=" + instance + ",metric=" + metric + + " value=" + stats["mod-stats"][group][metric] + " " + timestamp, + file=output) + + # Publish the metrics. + os.system("curl -i -XPOST 'http://%s:%s/write?db=%s&precision=s' --data-binary '%s'" + % (host, port, db, output.getvalue())) + + +print("%s: Graphite sender - Server Start - %s:%s" % + (time.asctime(), host, port)) + +try: + while(True): + send() + time.sleep(send_interval) +except KeyboardInterrupt: + pass |