Commit dc2ea48d authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge branch 'master' into trac213-incremental

Conflicts:
	src/bin/bind10/bind10_src.py.in
parents b513f0ab 01c6801b
315. [func] tomek
libdhcp: Support for DHCPv4 packet manipulation is now implemented.
All fixed fields are now supported. Generic support for DHCPv4
options is available (both parsing and assembly). There is no code
that uses this new functionality yet, so it is not usable directly
at this time. This code will be used by upcoming b10-dhcp4 daemon.
(Trac #1228, git 31d5a4f66b18cca838ca1182b9f13034066427a7)
314. [bug] jelte
b10-xfrin would previously initiate incoming transfers upon
receiving NOTIFY messages from any address (if the zone was
known to b10-xfrin, and using the configured address). It now
only starts a transfer if the source address from the NOTIFY
packet matches the configured master address and port. This was
really already fixed in release bind10-devel-20111014, but there
were some deferred cleanups to add.
(Trac #1298, git 1177bfe30e17a76bea6b6447e14ae9be9e1ca8c2)
313. [func] jinmei
datasrc: Added C++ API for adding zone differences to database
based data sources. It's intended to be used for the support for
IXFR-in and dynamic update (so they can subsequently be retrieved
for IXFR-out). The addRecordDiff method of the DatabaseAccessor
defines the interface, and a concrete implementation for SQLite3
was provided.
(Trac #1329, git 1aa233fab1d74dc776899df61181806679d14013)
312. [func] jelte
Added an initial framework for doing system tests using the
cucumber-based BDD tool Lettuce. A number of general steps are
included, for instance running bind10 with specific
configurations, sending queries, and inspecting query answers. A
few very basic tests are included as well.
(Trac #1290, git 6b75c128bcdcefd85c18ccb6def59e9acedd4437)
311. [bug] jelte
Fixed a bug in bindctl where tab-completion for names that
contain a hyphen resulted in unexpected behaviour, such as
appending the already-typed part again.
(Trac #1345, git f80ab7879cc29f875c40dde6b44e3796ac98d6da)
310. [bug] jelte
Fixed a bug where bindctl could not set a value that is optional
and has no default, resulting in the error that the setting
itself was unknown. bindctl now correctly sees the setting and
is able to set it.
(Trac #1344, git 0e776c32330aee466073771600390ce74b959b38)
309. [bug] jelte
Fixed a bug in bindctl where the removal of elements from a set
with default values was not stored, unless the set had been
modified in another way already.
(Trac #1343, git 25c802dd1c30580b94345e83eeb6a168ab329a33)
308. [build] jelte
The configure script will now use pkg-config for finding
information about the Botan library. If pkg-config is unavailable,
or unaware of Botan, it will fall back to botan-config. It will
also use botan-config when a specific botan library directory is
given using the '--with-botan=' flag
(Trac #1194, git dc491833cf75ac1481ba1475795b0f266545013d)
307. [func] vorner
When zone transfer in fails with IXFR, it is retried with AXFR
automatically.
(Trac #1279, git cd3588c9020d0310f949bfd053c4d3a4bd84ef88)
306. [bug] Stephen
Boss process now waits for the configuration manager to initialize
itself before continuing with startup. This fixes a race condition
......
......@@ -447,41 +447,64 @@ if test "${botan_path}" != "yes" ; then
AC_MSG_ERROR([${botan_path}/bin/botan-config not found])
fi
else
# First see if pkg-config knows of it.
# Unfortunately, the botan.pc files have their minor version in them
# too, so we need to try them one by one
BOTAN_CONFIG=""
AC_PATH_PROG([PKG_CONFIG], [pkg-config])
if test "$PKG_CONFIG" != "" ; then
BOTAN_VERSIONS="botan-1.10 botan-1.9 botan-1.8"
for version in $BOTAN_VERSIONS; do
AC_MSG_CHECKING([Checking botan version with pkg-config $version])
if [ $PKG_CONFIG --exists ${version} ]; then
AC_MSG_RESULT([found])
BOTAN_CONFIG="$PKG_CONFIG ${version}"
break
else
AC_MSG_RESULT([not found])
fi
done
fi
# If we had no pkg-config, or it didn't know about botan, use botan-config
if test "$BOTAN_CONFIG" = "" ; then
AC_PATH_PROG([BOTAN_CONFIG], [botan-config])
fi
fi
if test -x "${BOTAN_CONFIG}" ; then
BOTAN_LDFLAGS=`${BOTAN_CONFIG} --libs`
# We expect botan-config --libs to contain -L<path_to_libbotan>, but
# this is not always the case. As a heuristics workaround we add
# -L`botan-config --prefix/lib` in this case. Same for BOTAN_INCLUDES
# (but using include instead of lib) below.
BOTAN_LDFLAGS=`${BOTAN_CONFIG} --libs`
BOTAN_INCLUDES=`${BOTAN_CONFIG} --cflags`
# We expect botan-config --libs to contain -L<path_to_libbotan>, but
# this is not always the case. As a heuristics workaround we add
# -L`botan-config --prefix/lib` in this case. Same for BOTAN_INCLUDES
# (but using include instead of lib) below.
if [ $BOTAN_CONFIG --prefix >/dev/null 2>&1 ] ; then
echo ${BOTAN_LDFLAGS} | grep -- -L > /dev/null || \
BOTAN_LDFLAGS="-L`${BOTAN_CONFIG} --prefix`/lib ${BOTAN_LDFLAGS}"
BOTAN_INCLUDES=`${BOTAN_CONFIG} --cflags`
BOTAN_LDFLAGS="-L`${BOTAN_CONFIG} --prefix`/lib ${BOTAN_LDFLAGS}"
echo ${BOTAN_INCLUDES} | grep -- -I > /dev/null || \
BOTAN_INCLUDES="-I`${BOTAN_CONFIG} --prefix`/include ${BOTAN_INCLUDES}"
# See python_rpath for some info on why we do this
if test $rpath_available = yes; then
BOTAN_RPATH=
for flag in ${BOTAN_LDFLAGS}; do
BOTAN_RPATH="${BOTAN_RPATH} `echo $flag | sed -ne 's/^\(\-L\)/-R/p'`"
done
AC_SUBST(BOTAN_RPATH)
# According to the libtool manual, it should be sufficient if we
# specify the "-R libdir" in our wrapper library of botan (no other
# programs will need libbotan directly); "libdir" should be added to
# the program's binary image. But we've seen in our build environments
# that (some versions of?) libtool doesn't propagate -R as documented,
# and it caused a linker error at run time. To work around this, we
# also add the rpath to the global LDFLAGS.
LDFLAGS="$BOTAN_RPATH $LDFLAGS"
fi
AC_SUBST(BOTAN_LDFLAGS)
AC_SUBST(BOTAN_INCLUDES)
BOTAN_INCLUDES="-I`${BOTAN_CONFIG} --prefix`/include ${BOTAN_INCLUDES}"
fi
# See python_rpath for some info on why we do this
if test $rpath_available = yes; then
BOTAN_RPATH=
for flag in ${BOTAN_LDFLAGS}; do
BOTAN_RPATH="${BOTAN_RPATH} `echo $flag | sed -ne 's/^\(\-L\)/-R/p'`"
done
AC_SUBST(BOTAN_RPATH)
# According to the libtool manual, it should be sufficient if we
# specify the "-R libdir" in our wrapper library of botan (no other
# programs will need libbotan directly); "libdir" should be added to
# the program's binary image. But we've seen in our build environments
# that (some versions of?) libtool doesn't propagate -R as documented,
# and it caused a linker error at run time. To work around this, we
# also add the rpath to the global LDFLAGS.
LDFLAGS="$BOTAN_RPATH $LDFLAGS"
fi
AC_SUBST(BOTAN_LDFLAGS)
AC_SUBST(BOTAN_INCLUDES)
CPPFLAGS_SAVED=$CPPFLAGS
CPPFLAGS="$BOTAN_INCLUDES $CPPFLAGS"
......@@ -968,6 +991,7 @@ AC_OUTPUT([doc/version.ent
src/lib/util/python/mkpywrapper.py
src/lib/util/python/gen_wiredata.py
src/lib/server_common/tests/data_path.h
tests/lettuce/setup_intree_bind10.sh
tests/system/conf.sh
tests/system/run.sh
tests/system/glue/setup.sh
......
......@@ -167,6 +167,24 @@ Query::addNXDOMAINProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
}
}
void
Query::addWildcardProof(ZoneFinder& finder) {
// The query name shouldn't exist in the zone if there were no wildcard
// substitution. Confirm that by specifying NO_WILDCARD. It should result
// in NXDOMAIN and an NSEC RR that proves it should be returned.
const ZoneFinder::FindResult fresult =
finder.find(qname_, RRType::NSEC(), NULL,
dnssec_opt_ | ZoneFinder::NO_WILDCARD);
if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
fresult.rrset->getRdataCount() == 0) {
isc_throw(BadNSEC, "Unexpected result for wildcard proof");
return;
}
response_.addRRset(Message::SECTION_AUTHORITY,
boost::const_pointer_cast<RRset>(fresult.rrset),
dnssec_);
}
void
Query::addAuthAdditional(ZoneFinder& finder) {
// Fill in authority and addtional sections.
......@@ -259,6 +277,7 @@ Query::process() {
break;
}
case ZoneFinder::CNAME:
case ZoneFinder::WILDCARD_CNAME:
/*
* We don't do chaining yet. Therefore handling a CNAME is
* mostly the same as handling SUCCESS, but we didn't get
......@@ -271,8 +290,15 @@ Query::process() {
response_.addRRset(Message::SECTION_ANSWER,
boost::const_pointer_cast<RRset>(db_result.rrset),
dnssec_);
// If the answer is a result of wildcard substitution,
// add a proof that there's no closer name.
if (dnssec_ && db_result.code == ZoneFinder::WILDCARD_CNAME) {
addWildcardProof(*result.zone_finder);
}
break;
case ZoneFinder::SUCCESS:
case ZoneFinder::WILDCARD:
if (qtype_is_any) {
// If quety type is ANY, insert all RRs under the domain
// into answer section.
......@@ -299,6 +325,12 @@ Query::process() {
{
addAuthAdditional(*result.zone_finder);
}
// If the answer is a result of wildcard substitution,
// add a proof that there's no closer name.
if (dnssec_ && db_result.code == ZoneFinder::WILDCARD) {
addWildcardProof(*result.zone_finder);
}
break;
case ZoneFinder::DELEGATION:
response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
......@@ -324,10 +356,9 @@ Query::process() {
}
break;
default:
// These are new result codes (WILDCARD and WILDCARD_NXRRSET)
// They should not happen from the in-memory and the database
// backend isn't used yet.
// TODO: Implement before letting the database backends in
// This is basically a bug of the data source implementation,
// but could also happen in the middle of development where
// we try to add a new result code.
isc_throw(isc::NotImplemented, "Unknown result code");
break;
}
......
......@@ -77,6 +77,11 @@ private:
void addNXDOMAINProof(isc::datasrc::ZoneFinder& finder,
isc::dns::ConstRRsetPtr nsec);
/// Add NSEC RRs that prove a wildcard answer is the best one.
///
/// This corresponds to Section 3.1.3.3 of RFC 4035.
void addWildcardProof(isc::datasrc::ZoneFinder& finder);
/// \brief Look up additional data (i.e., address records for the names
/// included in NS or MX records) and add them to the additional section.
///
......
......@@ -92,6 +92,14 @@ const char* const other_zone_rrs =
"cnamemailer.example.com. 3600 IN CNAME www.example.com.\n"
"cnamemx.example.com. 3600 IN MX 10 cnamemailer.example.com.\n"
"mx.delegation.example.com. 3600 IN A 192.0.2.100\n";
// Wildcards
const char* const wild_txt = "*.wild.example.com. 3600 IN A 192.0.2.7\n";
const char* const nsec_wild_txt =
"*.wild.example.com. 3600 IN NSEC www.example.com. A NSEC RRSIG\n";
const char* const cnamewild_txt =
"*.cnamewild.example.com. 3600 IN CNAME www.example.org.\n";
const char* const nsec_cnamewild_txt = "*.cnamewild.example.com. "
"3600 IN NSEC delegation.example.com. CNAME NSEC RRSIG\n";
// Used in NXDOMAIN proof test. We are going to test some unusual case where
// the best possible wildcard is below the "next domain" of the NSEC RR that
// proves the NXDOMAIN, i.e.,
......@@ -170,7 +178,8 @@ public:
cname_nxdom_txt << cname_out_txt << dname_txt << dname_a_txt <<
other_zone_rrs << no_txt << nz_txt <<
nsec_apex_txt << nsec_mx_txt << nsec_no_txt << nsec_nz_txt <<
nsec_nxdomain_txt << nsec_www_txt << nonsec_a_txt;
nsec_nxdomain_txt << nsec_www_txt << nonsec_a_txt <<
wild_txt << nsec_wild_txt << cnamewild_txt << nsec_cnamewild_txt;
masterLoad(zone_stream, origin_, rrclass_,
boost::bind(&MockZoneFinder::loadRRset, this, _1));
......@@ -259,6 +268,24 @@ private:
boost::scoped_ptr<ZoneFinder::FindResult> nsec_result_;
};
// A helper function that generates a new RRset based on "wild_rrset",
// replacing its owner name with 'real_name'.
ConstRRsetPtr
substituteWild(const RRset& wild_rrset, const Name& real_name) {
RRsetPtr rrset(new RRset(real_name, wild_rrset.getClass(),
wild_rrset.getType(), wild_rrset.getTTL()));
// For simplicity we only consider the case with one RDATA (for now)
rrset->addRdata(wild_rrset.getRdataIterator()->getCurrent());
ConstRRsetPtr wild_sig = wild_rrset.getRRsig();
if (wild_sig) {
RRsetPtr sig(new RRset(real_name, wild_sig->getClass(),
wild_sig->getType(), wild_sig->getTTL()));
sig->addRdata(wild_sig->getRdataIterator()->getCurrent());
rrset->addRRsig(sig);
}
return (rrset);
}
ZoneFinder::FindResult
MockZoneFinder::find(const Name& name, const RRType& type,
RRsetList* target, const FindOptions options)
......@@ -365,6 +392,33 @@ MockZoneFinder::find(const Name& name, const RRType& type,
return (FindResult(NXRRSET, RRsetPtr()));
}
// Another possibility is wildcard. For simplicity we only check
// hardcoded specific cases, ignoring other details such as canceling
// due to the existence of closer name.
if ((options & NO_WILDCARD) == 0) {
const Name wild_suffix("wild.example.com");
if (name.compare(wild_suffix).getRelation() ==
NameComparisonResult::SUBDOMAIN) {
domain = domains_.find(Name("*").concatenate(wild_suffix));
assert(domain != domains_.end());
RRsetStore::const_iterator found_rrset = domain->second.find(type);
assert(found_rrset != domain->second.end());
return (FindResult(WILDCARD,
substituteWild(*found_rrset->second, name)));
}
const Name cnamewild_suffix("cnamewild.example.com");
if (name.compare(cnamewild_suffix).getRelation() ==
NameComparisonResult::SUBDOMAIN) {
domain = domains_.find(Name("*").concatenate(cnamewild_suffix));
assert(domain != domains_.end());
RRsetStore::const_iterator found_rrset =
domain->second.find(RRType::CNAME());
assert(found_rrset != domain->second.end());
return (FindResult(WILDCARD_CNAME,
substituteWild(*found_rrset->second, name)));
}
}
// This is an NXDOMAIN case.
// If we need DNSSEC proof, find the "previous name" that has an NSEC RR
// and return NXDOMAIN with the found NSEC. Otherwise, just return the
......@@ -804,6 +858,72 @@ TEST_F(QueryTest, nxrrsetWithoutNSEC) {
NULL, mock_finder->getOrigin());
}
TEST_F(QueryTest, wildcardNSEC) {
// The qname matches *.wild.example.com. The response should contain
// an NSEC that proves the non existence of a closer name.
Query(memory_client, Name("www.wild.example.com"), RRType::A(), response,
true).process();
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 6, 6,
(string(wild_txt).replace(0, 1, "www") +
string("www.wild.example.com. 3600 IN RRSIG ") +
getCommonRRSIGText("A") + "\n").c_str(),
(zone_ns_txt + string("example.com. 3600 IN RRSIG NS 5 "
"3 3600 20000101000000 "
"20000201000000 12345 "
"example.com. FAKEFAKEFAKE\n") +
string(nsec_wild_txt) +
string("*.wild.example.com. 3600 IN RRSIG ") +
getCommonRRSIGText("NSEC") + "\n").c_str(),
NULL, // we are not interested in additionals in this test
mock_finder->getOrigin());
}
TEST_F(QueryTest, CNAMEwildNSEC) {
// Similar to the previous case, but the matching wildcard record is
// CNAME.
Query(memory_client, Name("www.cnamewild.example.com"), RRType::A(),
response, true).process();
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
(string(cnamewild_txt).replace(0, 1, "www") +
string("www.cnamewild.example.com. 3600 IN RRSIG ") +
getCommonRRSIGText("CNAME") + "\n").c_str(),
(string(nsec_cnamewild_txt) +
string("*.cnamewild.example.com. 3600 IN RRSIG ") +
getCommonRRSIGText("NSEC") + "\n").c_str(),
NULL, // we are not interested in additionals in this test
mock_finder->getOrigin());
}
TEST_F(QueryTest, badWildcardProof1) {
// Unexpected case in wildcard proof: ZoneFinder::find() returns SUCCESS
// when NXDOMAIN is expected.
mock_finder->setNSECResult(Name("www.wild.example.com"),
ZoneFinder::SUCCESS,
mock_finder->delegation_rrset_);
EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
RRType::A(), response, true).process(),
Query::BadNSEC);
}
TEST_F(QueryTest, badWildcardProof2) {
// "wildcard proof" doesn't return RRset.
mock_finder->setNSECResult(Name("www.wild.example.com"),
ZoneFinder::NXDOMAIN, ConstRRsetPtr());
EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
RRType::A(), response, true).process(),
Query::BadNSEC);
}
TEST_F(QueryTest, badWildcardProof3) {
// "wildcard proof" returns empty NSEC.
mock_finder->setNSECResult(Name("www.wild.example.com"),
ZoneFinder::NXDOMAIN,
mock_finder->empty_nsec_rrset_);
EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
RRType::A(), response, true).process(),
Query::BadNSEC);
}
/*
* This tests that when there's no SOA and we need a negative answer. It should
* throw in that case.
......
......@@ -635,6 +635,8 @@ class BoB:
args = ["b10-cmdctl"]
if self.cmdctl_port is not None:
args.append("--port=" + str(self.cmdctl_port))
if self.verbose:
args.append("-v")
return self.start_process("b10-cmdctl", args, self.c_channel_env,
self.cmdctl_port)
......
......@@ -45,6 +45,5 @@ export B10_FROM_BUILD
BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
export BIND10_MSGQ_SOCKET_FILE
cd ${BIND10_PATH}
exec ${PYTHON_EXEC} -O bind10 "$@"
exec ${PYTHON_EXEC} -O ${BIND10_PATH}/bind10 "$@"
......@@ -46,6 +46,16 @@ except ImportError:
# if we have readline support, use that, otherwise use normal stdio
try:
import readline
# This is a fix for the problem described in
# http://bind10.isc.org/ticket/1345
# If '-' is seen as a word-boundary, the final completion-step
# (as handled by the cmd module, and hence outside our reach) can
# mistakenly add data twice, resulting in wrong completion results
# The solution is to remove it.
delims = readline.get_completer_delims()
delims = delims.replace('-', '')
readline.set_completer_delims(delims)
my_readline = readline.get_line_buffer
except ImportError:
my_readline = sys.stdin.readline
......@@ -61,21 +71,21 @@ Type \"<module_name> <command_name> help\" for help on the specific command.
\nAvailable module names: """
class ValidatedHTTPSConnection(http.client.HTTPSConnection):
'''Overrides HTTPSConnection to support certification
'''Overrides HTTPSConnection to support certification
validation. '''
def __init__(self, host, ca_certs):
http.client.HTTPSConnection.__init__(self, host)
self.ca_certs = ca_certs
def connect(self):
''' Overrides the connect() so that we do
''' Overrides the connect() so that we do
certificate validation. '''
sock = socket.create_connection((self.host, self.port),
self.timeout)
if self._tunnel_host:
self.sock = sock
self._tunnel()
req_cert = ssl.CERT_NONE
if self.ca_certs:
req_cert = ssl.CERT_REQUIRED
......@@ -85,7 +95,7 @@ class ValidatedHTTPSConnection(http.client.HTTPSConnection):
ca_certs=self.ca_certs)
class BindCmdInterpreter(Cmd):
"""simple bindctl example."""
"""simple bindctl example."""
def __init__(self, server_port='localhost:8080', pem_file=None,
csv_file_dir=None):
......@@ -118,29 +128,33 @@ class BindCmdInterpreter(Cmd):
socket.gethostname())).encode())
digest = session_id.hexdigest()
return digest
def run(self):
'''Parse commands from user and send them to cmdctl. '''
try:
if not self.login_to_cmdctl():
return
return 1
self.cmdloop()
print('\nExit from bindctl')
return 0
except FailToLogin as err:
# error already printed when this was raised, ignoring
pass
return 1
except KeyboardInterrupt:
print('\nExit from bindctl')
return 0
except socket.error as err:
print('Failed to send request, the connection is closed')
return 1
except http.client.CannotSendRequest:
print('Can not send request, the connection is busy')
return 1
def _get_saved_user_info(self, dir, file_name):
''' Read all the available username and password pairs saved in
''' Read all the available username and password pairs saved in
file(path is "dir + file_name"), Return value is one list of elements
['name', 'password'], If get information failed, empty list will be
['name', 'password'], If get information failed, empty list will be
returned.'''
if (not dir) or (not os.path.exists(dir)):
return []
......@@ -166,7 +180,7 @@ class BindCmdInterpreter(Cmd):
if not os.path.exists(dir):
os.mkdir(dir, 0o700)
csvfilepath = dir + file_name
csvfilepath = dir + file_name
csvfile = open(csvfilepath, 'w')
os.chmod(csvfilepath, 0o600)
writer = csv.writer(csvfile)
......@@ -180,7 +194,7 @@ class BindCmdInterpreter(Cmd):
return True
def login_to_cmdctl(self):
'''Login to cmdctl with the username and password inputted
'''Login to cmdctl with the username and password inputted
from user. After the login is sucessful, the username and
password will be saved in 'default_user.csv', when run the next
time, username and password saved in 'default_user.csv' will be
......@@ -246,14 +260,14 @@ class BindCmdInterpreter(Cmd):
if self.login_to_cmdctl():
# successful, so try send again
status, reply_msg = self._send_message(url, body)
if reply_msg:
return json.loads(reply_msg.decode())
else:
return {}
def send_POST(self, url, post_param = None):
def send_POST(self, url, post_param = None):
'''Send POST request to cmdctl, session id is send with the name
'cookie' in header.
Format: /module_name/command_name
......@@ -312,12 +326,12 @@ class BindCmdInterpreter(Cmd):
def _validate_cmd(self, cmd):
'''validate the parameters and merge some parameters together,
merge algorithm is based on the command line syntax, later, if
a better command line syntax come out, this function should be
updated first.
a better command line syntax come out, this function should be
updated first.
'''
if not cmd.module in self.modules:
raise CmdUnknownModuleSyntaxError(cmd.module)
module_info = self.modules[cmd.module]
if not module_info.has_command_with_name(cmd.command):
raise CmdUnknownCmdSyntaxError(cmd.module, cmd.command)
......@@ -325,17 +339,17 @@ class BindCmdInterpreter(Cmd):
command_info = module_info.get_command_with_name(cmd.command)
manda_params = command_info.get_mandatory_param_names()
all_params = command_info.get_param_names()
# If help is entered, don't do further parameter validation.
for val in cmd.params.keys():
if val == "help":
return
params = cmd.params.copy()
if not params and manda_params:
raise CmdMissParamSyntaxError(cmd.module, cmd.command, manda_params[0])
params = cmd.params.copy()
if not params and manda_params:
raise CmdMissParamSyntaxError(cmd.module, cmd.command, manda_params[0])
elif params and not all_params:
raise CmdUnknownParamSyntaxError(cmd.module, cmd.command,
raise CmdUnknownParamSyntaxError(cmd.module, cmd.command,
list(params.keys())[0])
elif params:
param_name = None
......@@ -366,7 +380,7 @@ class BindCmdInterpreter(Cmd):
param_name = command_info.get_param_name_by_position(name, param_count)
cmd.params[param_name] = cmd.params[name]
del cmd.params[name]
elif not name in all_params:
raise CmdUnknownParamSyntaxError(cmd.module, cmd.command, name)
......@@ -375,7 +389,7 @@ class BindCmdInterpreter(Cmd):
if not name in params and not param_nr in params:
raise CmdMissParamSyntaxError(cmd.module, cmd.command, name)
param_nr += 1
# Convert parameter value according parameter spec file.
# Ignore check for commands belongs to module 'config'
if cmd.module != CONFIG_MODULE_NAME:
......@@ -384,9 +398,9 @@ class BindCmdInterpreter(Cmd):
try:
cmd.params[param_name] = isc.config.config_data.convert_type(param_spec, cmd.params[param_name])
except isc.cc.data.DataTypeError as e:
raise isc.cc.data.DataTypeError('Invalid parameter value for \"%s\", the type should be \"%s\" \n'
raise isc.cc.data.DataTypeError('Invalid parameter value for \"%s\", the type should be \"%s\" \n'
% (param_name, param_spec['item_type']) + str(e))
def _handle_cmd<