mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-03 23:45:27 +00:00
[5137] A few fixes and many python style improvements
This commit is contained in:
13
configure.ac
13
configure.ac
@@ -470,7 +470,10 @@ AC_ARG_ENABLE(shell, [AC_HELP_STRING([--enable-shell],
|
|||||||
|
|
||||||
if test "x$enable_shell" != xno ; then
|
if test "x$enable_shell" != xno ; then
|
||||||
# If kea-shell is enabled, we really need python. 2.7 or anything newer will do.
|
# If kea-shell is enabled, we really need python. 2.7 or anything newer will do.
|
||||||
AM_PATH_PYTHON([2.7])
|
AM_PATH_PYTHON([3], [found="yes"], [found="no"])
|
||||||
|
if test "x$found" = xno ; then
|
||||||
|
AM_PATH_PYTHON([2.7])
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
PYTHON=no
|
PYTHON=no
|
||||||
fi
|
fi
|
||||||
@@ -1628,8 +1631,10 @@ AC_CONFIG_FILES([compatcheck/Makefile
|
|||||||
src/bin/admin/tests/cql_tests.sh
|
src/bin/admin/tests/cql_tests.sh
|
||||||
src/bin/agent/tests/test_libraries.h
|
src/bin/agent/tests/test_libraries.h
|
||||||
src/bin/shell/Makefile
|
src/bin/shell/Makefile
|
||||||
|
src/bin/shell/kea-shell
|
||||||
src/bin/shell/tests/Makefile
|
src/bin/shell/tests/Makefile
|
||||||
src/bin/shell/tests/shell_process_tests.sh
|
src/bin/shell/tests/shell_process_tests.sh
|
||||||
|
src/bin/shell/tests/shell_unittest.py
|
||||||
src/hooks/Makefile
|
src/hooks/Makefile
|
||||||
src/hooks/dhcp/Makefile
|
src/hooks/dhcp/Makefile
|
||||||
src/hooks/dhcp/user_chk/Makefile
|
src/hooks/dhcp/user_chk/Makefile
|
||||||
@@ -1720,11 +1725,14 @@ AC_CONFIG_FILES([compatcheck/Makefile
|
|||||||
])
|
])
|
||||||
|
|
||||||
AC_CONFIG_COMMANDS([permissions], [
|
AC_CONFIG_COMMANDS([permissions], [
|
||||||
|
chmod +x src/bin/admin/kea-admin
|
||||||
chmod +x src/bin/dhcp4/tests/dhcp4_process_tests.sh
|
chmod +x src/bin/dhcp4/tests/dhcp4_process_tests.sh
|
||||||
chmod +x src/bin/dhcp6/tests/dhcp6_process_tests.sh
|
chmod +x src/bin/dhcp6/tests/dhcp6_process_tests.sh
|
||||||
chmod +x src/bin/keactrl/keactrl
|
chmod +x src/bin/keactrl/keactrl
|
||||||
chmod +x src/bin/keactrl/tests/keactrl_tests.sh
|
chmod +x src/bin/keactrl/tests/keactrl_tests.sh
|
||||||
chmod +x src/bin/admin/kea-admin
|
chmod +x src/bin/shell/kea-shell
|
||||||
|
chmod +x src/bin/shell/tests/shell_process_tests.sh
|
||||||
|
chmod +x src/bin/shell/tests/shell_unittest.py
|
||||||
chmod +x src/lib/dns/gen-rdatacode.py
|
chmod +x src/lib/dns/gen-rdatacode.py
|
||||||
chmod +x src/lib/log/tests/console_test.sh
|
chmod +x src/lib/log/tests/console_test.sh
|
||||||
chmod +x src/lib/log/tests/destination_test.sh
|
chmod +x src/lib/log/tests/destination_test.sh
|
||||||
@@ -1800,6 +1808,7 @@ if test "$PYTHON" != "no" ; then
|
|||||||
cat >> config.report << END
|
cat >> config.report << END
|
||||||
|
|
||||||
Python:
|
Python:
|
||||||
|
PYTHON: ${PYTHON}
|
||||||
PYTHON_VERSION: ${PYTHON_VERSION}
|
PYTHON_VERSION: ${PYTHON_VERSION}
|
||||||
|
|
||||||
END
|
END
|
||||||
|
@@ -5,7 +5,6 @@ SHTESTS += ca_process_tests.sh
|
|||||||
|
|
||||||
noinst_SCRIPTS = ca_process_tests.sh
|
noinst_SCRIPTS = ca_process_tests.sh
|
||||||
EXTRA_DIST = ca_process_tests.sh.in
|
EXTRA_DIST = ca_process_tests.sh.in
|
||||||
noinst_LTLIBRARIES = libbasic.la
|
|
||||||
|
|
||||||
# test using command-line arguments, so use check-local target instead of TESTS
|
# test using command-line arguments, so use check-local target instead of TESTS
|
||||||
check-local:
|
check-local:
|
||||||
@@ -40,6 +39,8 @@ TESTS_ENVIRONMENT = \
|
|||||||
TESTS =
|
TESTS =
|
||||||
if HAVE_GTEST
|
if HAVE_GTEST
|
||||||
|
|
||||||
|
noinst_LTLIBRARIES = libbasic.la
|
||||||
|
|
||||||
TESTS += ca_unittests
|
TESTS += ca_unittests
|
||||||
|
|
||||||
ca_unittests_SOURCES = ca_cfg_mgr_unittests.cc
|
ca_unittests_SOURCES = ca_cfg_mgr_unittests.cc
|
||||||
|
@@ -5,7 +5,7 @@ EXTRA_DIST =
|
|||||||
if KEA_SHELL
|
if KEA_SHELL
|
||||||
|
|
||||||
# Kea-shell is enabled, here are proper rules for it.
|
# Kea-shell is enabled, here are proper rules for it.
|
||||||
kea_shell_PYTHON = kea-shell.py kea_conn.py kea_connector2.py kea_connector3.py
|
kea_shell_PYTHON = kea_conn.py kea_connector2.py kea_connector3.py
|
||||||
kea_shelldir = @localstatedir@/@PACKAGE@
|
kea_shelldir = @localstatedir@/@PACKAGE@
|
||||||
|
|
||||||
bin_SCRIPTS = kea-shell
|
bin_SCRIPTS = kea-shell
|
||||||
@@ -13,12 +13,11 @@ bin_SCRIPTS = kea-shell
|
|||||||
else
|
else
|
||||||
|
|
||||||
# Kea-shell is disabled, simply keep the files for make dist
|
# Kea-shell is disabled, simply keep the files for make dist
|
||||||
EXTRA_DIST += kea-shell.py kea_conn.py kea_connector2.py kea_connector3.py
|
EXTRA_DIST += kea-shell kea_conn.py kea_connector2.py kea_connector3.py
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
CLEANFILES = kea-shell kea-shell.pyc
|
CLEANFILES = *.pyc
|
||||||
|
|
||||||
|
|
||||||
man_MANS = kea-shell.8
|
man_MANS = kea-shell.8
|
||||||
DISTCLEANFILES = $(man_MANS)
|
DISTCLEANFILES = $(man_MANS)
|
||||||
@@ -38,13 +37,6 @@ $(man_MANS):
|
|||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# This is done here since configure.ac AC_OUTPUT doesn't expand certain variables.
|
|
||||||
kea-shell: kea-shell.py
|
|
||||||
$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" kea-shell.py > kea-shell.tmp
|
|
||||||
$(SED) "s|REPORTED_VERSION|@PACKAGE_VERSION@|" kea-shell.tmp >$@
|
|
||||||
rm -f kea-shell.tmp
|
|
||||||
chmod a+x $@
|
|
||||||
|
|
||||||
install-data-local:
|
install-data-local:
|
||||||
$(mkinstalldirs) $(DESTDIR)/@localstatedir@/@PACKAGE@
|
$(mkinstalldirs) $(DESTDIR)/@localstatedir@/@PACKAGE@
|
||||||
|
|
||||||
|
112
src/bin/shell/kea-shell.in
Normal file
112
src/bin/shell/kea-shell.in
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#!@PYTHON@
|
||||||
|
|
||||||
|
# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Text client for Control Agent process
|
||||||
|
"""
|
||||||
|
|
||||||
|
# First, let's import the right kea_connector.
|
||||||
|
# We have two versions: one for python 2.x and another for python 3.x.
|
||||||
|
# Sadly, there's no unified way to handle http connections. The recommended
|
||||||
|
# way is to use Requests (http://docs.python-requests.org/en/master/), but
|
||||||
|
# that's a stand alone package that requires separate installation. One of
|
||||||
|
# the design requirements was to not require any additional packages, so
|
||||||
|
# the code uses standard libraries available in python. Hence two versions.
|
||||||
|
import sys
|
||||||
|
import signal
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
from kea_conn import CARequest # CAResponse
|
||||||
|
|
||||||
|
if sys.version_info[0] == 2:
|
||||||
|
# This is Python 2.x
|
||||||
|
import kea_connector2 as kea_connector
|
||||||
|
elif sys.version_info[0] == 3:
|
||||||
|
# This is Python 3.x
|
||||||
|
import kea_connector3 as kea_connector
|
||||||
|
else:
|
||||||
|
# This is... have no idea what it is.
|
||||||
|
raise SystemExit("Unknown python version:" + str(sys.version_info[0]))
|
||||||
|
|
||||||
|
def timeout_handler(signum, frame):
|
||||||
|
"""Connection timeoout handler"""
|
||||||
|
del signum, frame
|
||||||
|
print("Connection timeout")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
VERSION = "@PACKAGE_VERSION@"
|
||||||
|
|
||||||
|
def shell_body():
|
||||||
|
"""
|
||||||
|
Second step: Need to parse command line parameters. We will use
|
||||||
|
argparse for that purpose. It does great job with having default
|
||||||
|
values, taking care of the help and sanity checking input
|
||||||
|
parameters.
|
||||||
|
"""
|
||||||
|
parser = argparse.ArgumentParser(description='kea-shell is a simple text '
|
||||||
|
'client that uses REST interface to '
|
||||||
|
'connect to Kea Control Agent.')
|
||||||
|
parser.add_argument('--host', type=str, default='127.0.0.1',
|
||||||
|
help='hostname of the CA to connect to '
|
||||||
|
'(defaul:; 127.0.0.1)')
|
||||||
|
parser.add_argument('--port', type=int, default=8000,
|
||||||
|
help='TCP port of the CA to connect to '
|
||||||
|
'(default: 8000)')
|
||||||
|
parser.add_argument('--timeout', type=int, default='10',
|
||||||
|
help='Timeout (in seconds) when attempting to '
|
||||||
|
'connect to CA (default: 10)')
|
||||||
|
parser.add_argument('command', type=str, nargs="?",
|
||||||
|
default='list-commands',
|
||||||
|
help='command to be executed. If not specified, '
|
||||||
|
'"list-commands" is used')
|
||||||
|
parser.add_argument('-v', action="store_true", help="Prints version")
|
||||||
|
cmd_args = parser.parse_args()
|
||||||
|
|
||||||
|
if cmd_args.v:
|
||||||
|
print(VERSION)
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
# Ok, now time to put the parameters parsed into the structure to be
|
||||||
|
# used by the connection.
|
||||||
|
params = CARequest()
|
||||||
|
params.command = cmd_args.command
|
||||||
|
params.http_host = cmd_args.host
|
||||||
|
params.http_port = cmd_args.port
|
||||||
|
params.timeout = cmd_args.timeout
|
||||||
|
params.version = VERSION
|
||||||
|
|
||||||
|
params.generate_body()
|
||||||
|
params.generate_headers()
|
||||||
|
|
||||||
|
# Load command processor
|
||||||
|
# @todo - command specific processing will be added as part of
|
||||||
|
# future work (either #5138 or #5139, whichever is implemented
|
||||||
|
# first)
|
||||||
|
|
||||||
|
# Read parameters from stdin (they're optional for some commands)
|
||||||
|
for line in sys.stdin:
|
||||||
|
params.params += line
|
||||||
|
|
||||||
|
# Set the timeout timer. If the connection takes too long,
|
||||||
|
# it will send a signal to us.
|
||||||
|
signal.signal(signal.SIGALRM, timeout_handler)
|
||||||
|
signal.alarm(params.timeout)
|
||||||
|
|
||||||
|
# Ok, everything is ready. Let's send the command and get a response.
|
||||||
|
try:
|
||||||
|
resp = kea_connector.send_to_control_agent(params)
|
||||||
|
except Exception as exc:
|
||||||
|
print("Failed to run: " + str(exc))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
resp.print_response()
|
||||||
|
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
shell_body()
|
@@ -1,92 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# This is going to be replaced with the actual version (see kea-shell target
|
|
||||||
# in Makefile.am)
|
|
||||||
VERSION = "REPORTED_VERSION"
|
|
||||||
|
|
||||||
# First, let's import the right kea_connector.
|
|
||||||
# We have two versions: one for python 2.x and another for python 3.x.
|
|
||||||
# Sadly, there's no unified way to handle http connections. The recommended
|
|
||||||
# way is to use Requests (http://docs.python-requests.org/en/master/), but
|
|
||||||
# that's a stand alone package that requires separate installation. One of
|
|
||||||
# the design requirements was to not require any additional packages, so
|
|
||||||
# the code uses standard libraries available in python. Hence two versions.
|
|
||||||
import sys
|
|
||||||
import signal
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
if (sys.version_info[0] == 2):
|
|
||||||
# This is Python 2.x
|
|
||||||
import kea_connector2 as kea_connector
|
|
||||||
else:
|
|
||||||
if (sys.version_info[0] == 3):
|
|
||||||
# This is Python 3.x
|
|
||||||
import kea_connector3 as kea_connector
|
|
||||||
else:
|
|
||||||
# This is... have no idea what it is.
|
|
||||||
raise SystemExit("Unknown python version:" + str(sys.version_info[0]))
|
|
||||||
|
|
||||||
from kea_conn import CARequest, CAResponse
|
|
||||||
|
|
||||||
# Second step: Need to parse command line parameters. We will use argparse for
|
|
||||||
# that purpose. It does great job with having default values, taking care of
|
|
||||||
# the help and sanity checking input parameters.
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='kea-shell is a simple text client that uses REST '
|
|
||||||
'interface to connect to Kea Control Agent.')
|
|
||||||
parser.add_argument('--host', type=str, default='127.0.0.1',
|
|
||||||
help='hostname of the CA to connect to (default; 127.0.0.1)')
|
|
||||||
parser.add_argument('--port', type=int, default=8000,
|
|
||||||
help='TCP port of the CA to connect to (default: 8000)')
|
|
||||||
parser.add_argument('--timeout', type=int, default='10',
|
|
||||||
help='Timeout (in seconds) when attempting to connect to CA (default: 10)')
|
|
||||||
parser.add_argument('command', type=str, nargs="?", default='list-commands',
|
|
||||||
help='command to be executed. If not specified, "list-commands" is used')
|
|
||||||
parser.add_argument('-v', action="store_true", help="Prints version")
|
|
||||||
cmd_args = parser.parse_args()
|
|
||||||
|
|
||||||
if (cmd_args.v):
|
|
||||||
print (VERSION)
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
# Ok, now time to put the parameters parsed into the structure to be used by the
|
|
||||||
# connection.
|
|
||||||
params = CARequest()
|
|
||||||
params.command = cmd_args.command
|
|
||||||
params.http_host = cmd_args.host
|
|
||||||
params.http_port = cmd_args.port
|
|
||||||
params.timeout = cmd_args.timeout
|
|
||||||
params.version = VERSION
|
|
||||||
|
|
||||||
params.generateBody()
|
|
||||||
params.generateHeaders()
|
|
||||||
|
|
||||||
conn = kea_connector.KeaConnector()
|
|
||||||
|
|
||||||
def timeout_handler(signum, frame):
|
|
||||||
print ("Connection timeout")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# Load command processor
|
|
||||||
# @todo - command specific processing will be added as part of future work
|
|
||||||
# (either #5138 or #5139, whichever is implemented first)
|
|
||||||
|
|
||||||
# Read parameters from stdin (they're optional for some commands)
|
|
||||||
for line in sys.stdin:
|
|
||||||
params.params += line
|
|
||||||
|
|
||||||
# Set the timeout timer. If the connection takes too long,
|
|
||||||
# it will send a signal to us.
|
|
||||||
signal.signal(signal.SIGALRM, timeout_handler)
|
|
||||||
signal.alarm(params.timeout)
|
|
||||||
|
|
||||||
# Ok, everything is ready. Let's send the command and get a response.
|
|
||||||
try:
|
|
||||||
resp = conn.sendCA(params)
|
|
||||||
except Exception as e:
|
|
||||||
print("Failed to run: " + str(e))
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
resp.printResp()
|
|
||||||
|
|
||||||
sys.exit(0)
|
|
@@ -49,9 +49,10 @@
|
|||||||
<cmdsynopsis>
|
<cmdsynopsis>
|
||||||
<command>kea-shell</command>
|
<command>kea-shell</command>
|
||||||
<arg><option>-h</option></arg>
|
<arg><option>-h</option></arg>
|
||||||
|
<arg><option>-v</option></arg>
|
||||||
<arg><option>--host</option></arg>
|
<arg><option>--host</option></arg>
|
||||||
<arg><option>--port</option></arg>
|
<arg><option>--port</option></arg>
|
||||||
<arg><option>-v</option></arg>
|
<arg><option>--timeout</option></arg>
|
||||||
<arg><option>command</option></arg>
|
<arg><option>command</option></arg>
|
||||||
</cmdsynopsis>
|
</cmdsynopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
@@ -64,8 +65,8 @@
|
|||||||
Kea Control Agent (CA). It takes command as a command-line parameter
|
Kea Control Agent (CA). It takes command as a command-line parameter
|
||||||
that is being sent to CA with proper JSON
|
that is being sent to CA with proper JSON
|
||||||
encapsulation. Optional parameters may be specified on the
|
encapsulation. Optional parameters may be specified on the
|
||||||
stdin. The request it sent of HTTP and a response is
|
standard input. The request it sent of HTTP and a response is
|
||||||
retrieved. That response is printed out on stdout.
|
retrieved. That response is displayed out on the standard output.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
@@ -78,16 +79,16 @@
|
|||||||
<variablelist>
|
<variablelist>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>-v</option></term>
|
<term><option>-h</option></term>
|
||||||
<listitem><para>
|
<listitem><para>
|
||||||
Display the version.
|
Displays help regarding command line parameters.
|
||||||
</para></listitem>
|
</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>-h</option></term>
|
<term><option>-v</option></term>
|
||||||
<listitem><para>
|
<listitem><para>
|
||||||
Displays help regarding command line parameters.
|
Display the version.
|
||||||
</para></listitem>
|
</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
@@ -1,17 +1,25 @@
|
|||||||
#!/usr/bin/python
|
# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
# This file contains classes used for communication with Control Agent.
|
"""
|
||||||
|
This file contains classes used for communication with Control Agent.
|
||||||
|
"""
|
||||||
|
|
||||||
# This class defines the HTTP request to be sent.
|
|
||||||
# The supported parameters listed are:
|
|
||||||
# - path (specifies the path on the server, CA uses only /)
|
|
||||||
# - http_host - hostname of the CA
|
|
||||||
# - http-port - TCP port of the CA
|
|
||||||
# - command - specifies the command to send (e.g. list-commands)
|
|
||||||
# - timeout - timeout (in ms)
|
|
||||||
# - headers - extra HTTP headers may be added here
|
|
||||||
# - version - version to be reported in HTTP header
|
|
||||||
class CARequest:
|
class CARequest:
|
||||||
|
"""
|
||||||
|
This class defines the HTTP request to be sent.
|
||||||
|
The supported parameters listed are:
|
||||||
|
- path (specifies the path on the server, CA uses only /)
|
||||||
|
- http_host - hostname of the CA
|
||||||
|
- http-port - TCP port of the CA
|
||||||
|
- command - specifies the command to send (e.g. list-commands)
|
||||||
|
- timeout - timeout (in ms)
|
||||||
|
- headers - extra HTTP headers may be added here
|
||||||
|
- version - version to be reported in HTTP header
|
||||||
|
"""
|
||||||
path = '/'
|
path = '/'
|
||||||
http_host = ''
|
http_host = ''
|
||||||
http_port = 0
|
http_port = 0
|
||||||
@@ -20,39 +28,47 @@ class CARequest:
|
|||||||
params = ''
|
params = ''
|
||||||
headers = {}
|
headers = {}
|
||||||
version = ""
|
version = ""
|
||||||
|
# This is a storage for generated command (input data to be sent over POST)
|
||||||
|
content = ''
|
||||||
|
|
||||||
# Generates the content, out of specified command line
|
def generate_body(self):
|
||||||
# and optional content.
|
"""
|
||||||
# @todo: Add support for parameters
|
Generates the content, out of specified command line
|
||||||
# this stores the output in self.content
|
and optional content.
|
||||||
def generateBody(self):
|
@todo: Add support for parameters
|
||||||
|
this stores the output in self.content
|
||||||
|
"""
|
||||||
self.content = '{ "command": "' + self.command + '"'
|
self.content = '{ "command": "' + self.command + '"'
|
||||||
if (len(self.params)):
|
if len(self.params):
|
||||||
self.content += ', "parameters": { ' + self.params + ' }'
|
self.content += ', "parameters": { ' + self.params + ' }'
|
||||||
self.content += ' }'
|
self.content += ' }'
|
||||||
|
|
||||||
# Generate HTTP headers
|
def generate_headers(self):
|
||||||
#
|
"""
|
||||||
# In particular, this method generates Content-Length and its value.
|
Generate HTTP headers
|
||||||
def generateHeaders(self):
|
|
||||||
|
In particular, this method generates Content-Length and its value.
|
||||||
|
"""
|
||||||
self.headers['Content-Type'] = 'application/json'
|
self.headers['Content-Type'] = 'application/json'
|
||||||
self.headers['User-Agent'] = "Kea-shell/%s"%(self.version)
|
self.headers['User-Agent'] = "Kea-shell/%s"%(self.version)
|
||||||
self.headers['Accept'] = '*/*'
|
self.headers['Accept'] = '*/*'
|
||||||
self.headers['Content-Length'] = "%d"%(len(self.content))
|
self.headers['Content-Length'] = "%d"%(len(self.content))
|
||||||
|
|
||||||
# This is a storage for generated command (input data to be sent over POST)
|
|
||||||
content = ''
|
|
||||||
|
|
||||||
# This class represents the HTTP response
|
|
||||||
class CAResponse:
|
class CAResponse:
|
||||||
|
"""
|
||||||
|
This class represents the HTTP response
|
||||||
|
"""
|
||||||
|
|
||||||
# Constructor
|
|
||||||
#
|
|
||||||
# Three mandatory parameters are:
|
|
||||||
# status - numerical number the describe the status (e.g. 200 = OK)
|
|
||||||
# reason - textual explanation of what happened
|
|
||||||
# body - the actual body structure of the response
|
|
||||||
def __init__(self, status, reason, body):
|
def __init__(self, status, reason, body):
|
||||||
|
"""
|
||||||
|
Constructor
|
||||||
|
|
||||||
|
Three mandatory parameters are:
|
||||||
|
status - numerical number the describe the status (e.g. 200 = OK)
|
||||||
|
reason - textual explanation of what happened
|
||||||
|
body - the actual body structure of the response
|
||||||
|
"""
|
||||||
self.status = status
|
self.status = status
|
||||||
self.reason = reason
|
self.reason = reason
|
||||||
self.body = body
|
self.body = body
|
||||||
@@ -61,11 +77,13 @@ class CAResponse:
|
|||||||
reason = ''
|
reason = ''
|
||||||
body = ''
|
body = ''
|
||||||
|
|
||||||
# Used for debugging
|
def print_response(self, debug=False):
|
||||||
#
|
"""
|
||||||
# if defug is true, this prints even more information
|
Used for debugging
|
||||||
def printResp(self, debug = False):
|
|
||||||
if (debug):
|
if debug is true, this prints even more information
|
||||||
|
"""
|
||||||
|
if debug:
|
||||||
print(self.status)
|
print(self.status)
|
||||||
print(self.reason)
|
print(self.reason)
|
||||||
print(self.body)
|
print(self.body)
|
||||||
|
@@ -1,34 +1,39 @@
|
|||||||
#!/usr/bin/python
|
# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
# This is PYTHON 2.x version of HTTP connection establishment
|
"""
|
||||||
|
This is PYTHON 2.x version of HTTP connection establishment
|
||||||
from kea_conn import CARequest, CAResponse
|
"""
|
||||||
|
|
||||||
import httplib
|
import httplib
|
||||||
|
|
||||||
class KeaConnector:
|
from kea_conn import CAResponse # CARequest
|
||||||
def sendCA(self, params):
|
|
||||||
# Estalbish HTTP connection first.
|
|
||||||
conn = httplib.HTTPConnection(params.http_host, params.http_port)
|
|
||||||
conn.connect()
|
|
||||||
|
|
||||||
# Use POST to send it
|
def send_to_control_agent(params):
|
||||||
request = conn.putrequest('POST', params.path)
|
"""Establish HTTP connection first."""
|
||||||
|
conn = httplib.HTTPConnection(params.http_host, params.http_port)
|
||||||
|
conn.connect()
|
||||||
|
|
||||||
# Send the headers first
|
# Use POST to send it
|
||||||
for k in params.headers:
|
_ = conn.putrequest('POST', params.path)
|
||||||
conn.putheader(k, params.headers[k])
|
|
||||||
conn.endheaders()
|
|
||||||
|
|
||||||
# Send the content
|
# Send the headers first
|
||||||
conn.send(params.content)
|
for k in params.headers:
|
||||||
|
conn.putheader(k, params.headers[k])
|
||||||
|
conn.endheaders()
|
||||||
|
|
||||||
# Now get the response
|
# Send the content
|
||||||
resp = conn.getresponse()
|
conn.send(params.content)
|
||||||
|
|
||||||
# Now get the response details, put it in CAResponse and
|
# Now get the response
|
||||||
# return it
|
resp = conn.getresponse()
|
||||||
x = CAResponse(resp.status, resp.reason, resp.read())
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
return (x)
|
# Now get the response details, put it in CAResponse and
|
||||||
|
# return it
|
||||||
|
result = CAResponse(resp.status, resp.reason, resp.read())
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return result
|
||||||
|
@@ -1,20 +1,29 @@
|
|||||||
#!/usr/bin/python
|
# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
from kea_conn import CARequest, CAResponse
|
"""
|
||||||
|
This is PYTHON 3.x version of HTTP connection establishment
|
||||||
|
"""
|
||||||
|
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
|
||||||
class KeaConnector:
|
from kea_conn import CAResponse # CARequest
|
||||||
def sendCA(self, params):
|
|
||||||
# Estalbish HTTP connection first.
|
|
||||||
url = "http://" + params.http_host + ":" + str(params.http_port) + str(params.path)
|
|
||||||
|
|
||||||
req = urllib.request.Request(url = url, data = str.encode(params.content),
|
|
||||||
headers = params.headers)
|
|
||||||
resp = urllib.request.urlopen(req)
|
|
||||||
|
|
||||||
# Now get the response details, put it in CAResponse and
|
def send_to_control_agent(params):
|
||||||
# return it
|
"""Establish HTTP connection first."""
|
||||||
x = CAResponse(resp.getcode(), resp.reason, resp.read().decode("utf-8"))
|
url = "http://" + params.http_host + ":"
|
||||||
|
url += str(params.http_port) + str(params.path)
|
||||||
|
|
||||||
return (x)
|
req = urllib.request.Request(url=url,
|
||||||
|
data=str.encode(params.content),
|
||||||
|
headers=params.headers)
|
||||||
|
resp = urllib.request.urlopen(req)
|
||||||
|
|
||||||
|
# Now get the response details, put it in CAResponse and return it
|
||||||
|
result = CAResponse(resp.getcode(), resp.reason,
|
||||||
|
resp.read().decode("utf-8"))
|
||||||
|
|
||||||
|
return result
|
||||||
|
@@ -36,8 +36,8 @@ CONFIG="{
|
|||||||
}"
|
}"
|
||||||
|
|
||||||
# In these tests we need to use two binaries: Control Agent and Kea shell.
|
# In these tests we need to use two binaries: Control Agent and Kea shell.
|
||||||
# Using bin and bin_path would be confusing, so we omit defining bin and bin_path
|
# Using bin and bin_path would be confusing, so we omit defining bin
|
||||||
# on purpose.
|
# and bin_path on purpose.
|
||||||
ca_bin="kea-ctrl-agent"
|
ca_bin="kea-ctrl-agent"
|
||||||
ca_bin_path=@abs_top_builddir@/src/bin/agent
|
ca_bin_path=@abs_top_builddir@/src/bin/agent
|
||||||
|
|
||||||
@@ -83,8 +83,8 @@ shell_command_test() {
|
|||||||
# of configuration failure).
|
# of configuration failure).
|
||||||
get_pid ${ca_bin}
|
get_pid ${ca_bin}
|
||||||
if [ ${_GET_PIDS_NUM} -ne 1 ]; then
|
if [ ${_GET_PIDS_NUM} -ne 1 ]; then
|
||||||
printf "ERROR: expected one Control Agent process to be started. Found %d processes\
|
printf "ERROR: expected one Control Agent process to be started.\
|
||||||
started.\n" ${_GET_PIDS_NUM}
|
Found %d processes started.\n" ${_GET_PIDS_NUM}
|
||||||
clean_exit 1
|
clean_exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -92,22 +92,26 @@ shell_command_test() {
|
|||||||
# It should be just once on startup.
|
# It should be just once on startup.
|
||||||
get_reconfigs
|
get_reconfigs
|
||||||
if [ ${_GET_RECONFIGS} -ne 1 ]; then
|
if [ ${_GET_RECONFIGS} -ne 1 ]; then
|
||||||
printf "ERROR: server been configured ${_GET_RECONFIGS} time(s), but exactly 1 was expected.\n"
|
printf "ERROR: server been configured ${_GET_RECONFIGS} time(s),\
|
||||||
|
but exactly 1 was expected.\n"
|
||||||
clean_exit 1
|
clean_exit 1
|
||||||
else
|
else
|
||||||
printf "Server successfully configured.\n"
|
printf "Server successfully configured.\n"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Main test phase: send command, check response.
|
# Main test phase: send command, check response.
|
||||||
tmp="echo \"${params}\" | ${shell_bin_path}/${shell_bin} --host 127.0.0.1 --port 8081 ${cmd} > ${tmpfile_path}/shell-stdout.txt"
|
tmp="echo \"${params}\" | ${shell_bin_path}/${shell_bin} --host \
|
||||||
|
127.0.0.1 --port 8081 ${cmd} > ${tmpfile_path}/shell-stdout.txt"
|
||||||
echo "Executing kea-shell ($tmp)"
|
echo "Executing kea-shell ($tmp)"
|
||||||
|
|
||||||
echo "${params}" | ${shell_bin_path}/${shell_bin} --host 127.0.0.1 --port 8081 ${cmd} > ${tmpfile_path}/shell-stdout.txt
|
echo "${params}" | ${shell_bin_path}/${shell_bin} --host 127.0.0.1 \
|
||||||
|
--port 8081 ${cmd} > ${tmpfile_path}/shell-stdout.txt
|
||||||
|
|
||||||
# Check the exit code
|
# Check the exit code
|
||||||
shell_exit_code=$?
|
shell_exit_code=$?
|
||||||
if [ ${shell_exit_code} -ne 0 ]; then
|
if [ ${shell_exit_code} -ne 0 ]; then
|
||||||
echo "ERROR: kea-shell returned ${shell_exit_code} exit code, expected 0."
|
echo "ERROR:" \
|
||||||
|
"kea-shell returned ${shell_exit_code} exit code, expected 0."
|
||||||
else
|
else
|
||||||
echo "kea-shell returned ${shell_exit_code} exit code as expected."
|
echo "kea-shell returned ${shell_exit_code} exit code as expected."
|
||||||
fi
|
fi
|
||||||
@@ -118,7 +122,9 @@ shell_command_test() {
|
|||||||
diff ${tmpfile_path}/shell-stdout.txt ${tmpfile_path}/shell-expected.txt
|
diff ${tmpfile_path}/shell-stdout.txt ${tmpfile_path}/shell-expected.txt
|
||||||
diff_code=$?
|
diff_code=$?
|
||||||
if [ ${diff_code} -ne 0 ]; then
|
if [ ${diff_code} -ne 0 ]; then
|
||||||
echo "ERROR: content returned is different than expected. See ${tmpfile_path}/shell-*.txt"
|
echo "ERROR:" \
|
||||||
|
"content returned is different than expected." \
|
||||||
|
"See ${tmpfile_path}/shell-*.txt"
|
||||||
echo "EXPECTED:"
|
echo "EXPECTED:"
|
||||||
cat ${tmpfile_path}/shell-expected.txt
|
cat ${tmpfile_path}/shell-expected.txt
|
||||||
echo "ACTUAL RESULT:"
|
echo "ACTUAL RESULT:"
|
||||||
@@ -164,7 +170,8 @@ version_test() {
|
|||||||
if test "${REPORTED_VERSION}" == "${EXPECTED_VERSION}"; then
|
if test "${REPORTED_VERSION}" == "${EXPECTED_VERSION}"; then
|
||||||
test_finish 0
|
test_finish 0
|
||||||
else
|
else
|
||||||
printf "ERROR: Expected version ${EXPECTED_VERSION}, got ${REPORTED_VERSION}\n"
|
echo "ERROR:" \
|
||||||
|
"Expected version ${EXPECTED_VERSION}, got ${REPORTED_VERSION}"
|
||||||
test_finish 1
|
test_finish 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -174,5 +181,3 @@ shell_command_test "shell.list-commands" "list-commands" \
|
|||||||
"[ { \"arguments\": [ \"list-commands\" ], \"result\": 0 } ]" ""
|
"[ { \"arguments\": [ \"list-commands\" ], \"result\": 0 } ]" ""
|
||||||
shell_command_test "shell.bogus" "give-me-a-beer" \
|
shell_command_test "shell.bogus" "give-me-a-beer" \
|
||||||
"[ { \"result\": 1, \"text\": \"'give-me-a-beer' command not supported.\" } ]" ""
|
"[ { \"result\": 1, \"text\": \"'give-me-a-beer' command not supported.\" } ]" ""
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,104 +0,0 @@
|
|||||||
# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
|
|
||||||
#
|
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from kea_conn import CARequest
|
|
||||||
|
|
||||||
class CARequestUnitTest(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
This class is dedicated to testing CARequest class. That class
|
|
||||||
is responsible for generation of the body and headers.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
This method is called before each test. Currently it does nothing.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_bodyWithoutParams(self):
|
|
||||||
"""
|
|
||||||
This test verifies if the CARequest object generates the request
|
|
||||||
content properly when there are no parameters.
|
|
||||||
"""
|
|
||||||
|
|
||||||
x = CARequest()
|
|
||||||
x.command = "foo"
|
|
||||||
x.generateBody()
|
|
||||||
self.assertEqual(x.content, '{ "command": "foo" }')
|
|
||||||
|
|
||||||
def test_bodyWithParams(self):
|
|
||||||
"""
|
|
||||||
This test verifies if the CARequest object generates the request
|
|
||||||
content properly when there are parameters.
|
|
||||||
"""
|
|
||||||
|
|
||||||
x = CARequest()
|
|
||||||
x.command = "foo"
|
|
||||||
x.params = '"bar": "baz"'
|
|
||||||
x.generateBody()
|
|
||||||
self.assertEqual(x.content, '{ "command": "foo", "parameters": { "bar": "baz" } }')
|
|
||||||
|
|
||||||
def checkHeader(self, headers, header_name, value):
|
|
||||||
"""
|
|
||||||
Checks if headers array contains an entry specified by header_name and that
|
|
||||||
its value matches specified value
|
|
||||||
"""
|
|
||||||
|
|
||||||
if header_name in headers:
|
|
||||||
if (headers[header_name] == value):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print ("Expected value: " + value + " does not match actual value: "
|
|
||||||
+ headers[header_name])
|
|
||||||
return ()
|
|
||||||
else:
|
|
||||||
print ("Expected header: " + header_name + " missing")
|
|
||||||
return (false)
|
|
||||||
|
|
||||||
def test_headers(self):
|
|
||||||
"""
|
|
||||||
This test checks if the headers are generated properly. Note that since
|
|
||||||
the content is not specified, it is 0. Therefore Content-Length is 0.
|
|
||||||
"""
|
|
||||||
x = CARequest()
|
|
||||||
x.generateHeaders()
|
|
||||||
|
|
||||||
self.assertTrue(self.checkHeader(x.headers, 'Content-Type', 'application/json'))
|
|
||||||
self.assertTrue(self.checkHeader(x.headers, 'Accept', '*/*'))
|
|
||||||
self.assertTrue(self.checkHeader(x.headers, 'Content-Length', "0"))
|
|
||||||
|
|
||||||
def test_headerLength(self):
|
|
||||||
"""
|
|
||||||
This test checks if the headers are generated properly. In this test there
|
|
||||||
is specific content of non-zero length, and its size should be reflected
|
|
||||||
in the header.
|
|
||||||
"""
|
|
||||||
x = CARequest()
|
|
||||||
x.content = '{ "command": "foo" }'
|
|
||||||
x.generateHeaders()
|
|
||||||
|
|
||||||
self.assertTrue(self.checkHeader(x.headers, 'Content-Length', str(len(x.content))))
|
|
||||||
|
|
||||||
def test_headerVersion(self):
|
|
||||||
"""
|
|
||||||
This test checks if the version reported in HTTP headers is generated properly.
|
|
||||||
"""
|
|
||||||
x = CARequest()
|
|
||||||
x.version = "1.2.3"
|
|
||||||
x.generateHeaders()
|
|
||||||
self.assertTrue(self.checkHeader(x.headers, 'User-Agent', 'Kea-shell/1.2.3'))
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
"""
|
|
||||||
This method is called after each test. Currently it does nothing.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
115
src/bin/shell/tests/shell_unittest.py.in
Normal file
115
src/bin/shell/tests/shell_unittest.py.in
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
#!@PYTHON@
|
||||||
|
|
||||||
|
# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Kea shell unittest (python part)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from kea_conn import CARequest
|
||||||
|
|
||||||
|
class CARequestUnitTest(unittest.TestCase):
|
||||||
|
"""
|
||||||
|
This class is dedicated to testing CARequest class. That class
|
||||||
|
is responsible for generation of the body and headers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
This method is called before each test. Currently it does nothing.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_body_without_params(self):
|
||||||
|
"""
|
||||||
|
This test verifies if the CARequest object generates the request
|
||||||
|
content properly when there are no parameters.
|
||||||
|
"""
|
||||||
|
request = CARequest()
|
||||||
|
request.command = "foo"
|
||||||
|
request.generate_body()
|
||||||
|
self.assertEqual(request.content, '{ "command": "foo" }')
|
||||||
|
|
||||||
|
def test_body_with_params(self):
|
||||||
|
"""
|
||||||
|
This test verifies if the CARequest object generates the request
|
||||||
|
content properly when there are parameters.
|
||||||
|
"""
|
||||||
|
request = CARequest()
|
||||||
|
request.command = "foo"
|
||||||
|
request.params = '"bar": "baz"'
|
||||||
|
request.generate_body()
|
||||||
|
self.assertEqual(request.content,
|
||||||
|
'{ "command": "foo", "parameters": { "bar": "baz" } }')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_header(headers, header_name, value):
|
||||||
|
"""
|
||||||
|
Checks if headers array contains an entry specified by
|
||||||
|
header_name and that its value matches specified value
|
||||||
|
"""
|
||||||
|
if header_name in headers:
|
||||||
|
if headers[header_name] == value:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print("Expected value: " + value +
|
||||||
|
" does not match actual value: " +
|
||||||
|
headers[header_name])
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
print("Expected header: " + header_name + " missing")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def test_headers(self):
|
||||||
|
"""
|
||||||
|
This test checks if the headers are generated properly. Note that since
|
||||||
|
the content is not specified, it is 0. Therefore Content-Length is 0.
|
||||||
|
"""
|
||||||
|
request = CARequest()
|
||||||
|
request.generate_headers()
|
||||||
|
|
||||||
|
self.assertTrue(self.check_header(request.headers,
|
||||||
|
'Content-Type', 'application/json'))
|
||||||
|
self.assertTrue(self.check_header(request.headers,
|
||||||
|
'Accept', '*/*'))
|
||||||
|
self.assertTrue(self.check_header(request.headers,
|
||||||
|
'Content-Length', '0'))
|
||||||
|
|
||||||
|
def test_header_length(self):
|
||||||
|
"""
|
||||||
|
This test checks if the headers are generated properly. In
|
||||||
|
this test there is specific content of non-zero length, and
|
||||||
|
its size should be reflected in the header.
|
||||||
|
"""
|
||||||
|
request = CARequest()
|
||||||
|
request.content = '{ "command": "foo" }'
|
||||||
|
request.generate_headers()
|
||||||
|
|
||||||
|
self.assertTrue(self.check_header(request.headers, 'Content-Length',
|
||||||
|
str(len(request.content))))
|
||||||
|
|
||||||
|
def test_header_version(self):
|
||||||
|
"""
|
||||||
|
This test checks if the version reported in HTTP headers is
|
||||||
|
generated properly.
|
||||||
|
"""
|
||||||
|
request = CARequest()
|
||||||
|
request.version = "1.2.3"
|
||||||
|
request.generate_headers()
|
||||||
|
self.assertTrue(self.check_header(request.headers, 'User-Agent',
|
||||||
|
'Kea-shell/1.2.3'))
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""
|
||||||
|
This method is called after each test. Currently it does nothing.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Reference in New Issue
Block a user