BIND 10 trac1290, updated. dcd6d7ff4c0671a0995fe4051cea0e525d3f82bc [1290] expand querying tool

BIND 10 source code commits bind10-changes at lists.isc.org
Thu Oct 20 10:12:33 UTC 2011


The branch, trac1290 has been updated
       via  dcd6d7ff4c0671a0995fe4051cea0e525d3f82bc (commit)
      from  61fdce086a40930595e70168340ee68080b327bf (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit dcd6d7ff4c0671a0995fe4051cea0e525d3f82bc
Author: Jelte Jansen <jelte at isc.org>
Date:   Thu Oct 20 12:12:11 2011 +0200

    [1290] expand querying tool

-----------------------------------------------------------------------

Summary of changes:
 tests/lettuce/features/querying.py                 |  121 ++++++++++++++++++--
 tests/lettuce/features/server_from_sqlite3.feature |   24 ++++
 tests/lettuce/features/steps.py                    |    1 +
 3 files changed, 135 insertions(+), 11 deletions(-)

-----------------------------------------------------------------------
diff --git a/tests/lettuce/features/querying.py b/tests/lettuce/features/querying.py
index 5a78893..b0b4f97 100644
--- a/tests/lettuce/features/querying.py
+++ b/tests/lettuce/features/querying.py
@@ -2,18 +2,45 @@ from lettuce import *
 import subprocess
 import re
 
+# This script provides querying functionality
+# The most important step is
+#
+# query for <name> [type X] [class X] [to <addr>[:port]] should have rcode <rc>
+#
+# By default, it will send queries to 127.0.0.1:47806 unless specified
+# otherwise. The rcode is always checked. If the result is not NO_ANSWER,
+# the result will be stored in last_query_result, which can then be inspected
+# more closely, for instance with the step
+#
+# last query should have <property> <value>
+#
+
 #
 # define a class to easily access different parts
 # We may consider using our full library for this, but for now
 # simply store several parts of the response as text values in
 # this structure
 #
+# The following attributes are 'parsed' from the response, all as strings,
+# and end up as direct attributes of the QueryResult object:
+# opcode, rcode, id, flags, qdcount, ancount, nscount, adcount
+# (flags is one string with all flags)
+#
 # this will set 'rcode' as the result code, we 'define' one additional
 # rcode, "NO_ANSWER", if the dig process returned an error code itself
-# we will extend as necessary
-class QueryResult:
-    def __init__(self, name, qtype = None, qclass = None, port = 47806):
-        args = [ 'dig', '@localhost', '-p', str(port) ]
+# In this case none of the other attributes will be set.
+#
+# The different sections will be lists of strings, one for each RR in the
+# section. The question section will start with ';', as per dig output
+#
+# See server_from_sqlite3.feature for various examples to perform queries
+class QueryResult(object):
+    status_re = re.compile("opcode: ([A-Z])+, status: ([A-Z]+), id: ([0-9]+)")
+    flags_re = re.compile("flags: ([a-z ]+); QUERY: ([0-9]+), ANSWER: " +
+                          "([0-9]+), AUTHORITY: ([0-9]+), ADDITIONAL: ([0-9]+)")
+
+    def __init__(self, name, qtype, qclass, address, port):
+        args = [ 'dig', '@' + address, '-p', str(port) ]
         if qtype is not None:
             args.append('-t')
             args.append(str(qtype))
@@ -27,16 +54,88 @@ class QueryResult:
         if result != 0:
             self.rcode = "NO_ANSWER"
         else:
-            rcode_re = re.compile("status: ([A-Z]+)")
             self.rcode = None
+            parsing = "HEADER"
+            self.question_section = []
+            self.answer_section = []
+            self.authority_section = []
+            self.additional_section = []
+            self.line_handler = self.parse_header
             for out in dig_process.stdout:
-                rcode_match = rcode_re.search(out)
-                if rcode_match is not None:
-                    self.rcode = rcode_match.group(1)
+                self.line_handler(out)
+
+    def parse_header(self, line):
+        status_match = self.status_re.search(line)
+        flags_match = self.flags_re.search(line)
+        if status_match is not None:
+            self.opcode = status_match.group(1)
+            self.rcode = status_match.group(2)
+        elif flags_match is not None:
+            self.flags = flags_match.group(1)
+            self.qdcount = flags_match.group(2)
+            self.ancount = flags_match.group(3)
+            self.nscount = flags_match.group(4)
+            self.adcount = flags_match.group(5)
+        elif line == ";; QUESTION SECTION:\n":
+            self.line_handler = self.parse_question
+
+    def parse_question(self, line):
+        if line == ";; ANSWER SECTION:\n":
+            self.line_handler = self.parse_answer
+        elif line != "\n":
+            self.question_section.append(line)
+
+    def parse_answer(self, line):
+        if line == ";; AUTHORITY SECTION:\n":
+            self.line_handler = self.parse_authority
+        elif line != "\n":
+            self.answer_section.append(line)
 
+    def parse_authority(self, line):
+        if line == ";; ADDITIONAL SECTION:\n":
+            self.line_handler = self.parse_additional
+        elif line != "\n":
+            self.additional_section.append(line)
 
- at step(u'A query for ([\w.]+) should have rcode ([\w.]+)')
-def query(step, query_name, rcode):
-    query_result = QueryResult(query_name)
+    def parse_authority(self, line):
+        if line.startswith(";; Query time"):
+            self.line_handler = self.parse_footer
+        elif line != "\n":
+            self.additional_section.append(line)
+
+    def parse_footer(self, line):
+        pass
+
+ at step(u'A query for ([\w.]+) (?:type ([A-Z]+) )?(?:class ([A-Z]+) )?' +
+       '(?:to ([^:]+)(?::([0-9]+))? )?should have rcode ([\w.]+)')
+def query(step, query_name, qtype, qclass, addr, port, rcode):
+    if qtype is None:
+        qtype = "A"
+    if qclass is None:
+        qclass = "IN"
+    if addr is None:
+        addr = "127.0.0.1"
+    if port is None:
+        port = 47806
+    query_result = QueryResult(query_name, qtype, qclass, addr, port)
     assert query_result.rcode == rcode, "Got " + query_result.rcode
+    world.last_query_result = query_result
+
+ at step(u'The SOA serial for ([\w.]+) should be ([0-9]+)')
+def query_soa(step, query_name, serial):
+    query_result = QueryResult(query_name, "SOA", "IN", "127.0.0.1", "47806")
+    assert "NOERROR" == query_result.rcode,\
+        "Got " + query_result.rcode + ", expected NOERROR"
+    assert len(query_result.answer_section) == 1,\
+        "Too few or too many answers in SOA response"
+    soa_parts = query_result.answer_section[0].split()
+    assert serial == soa_parts[6],\
+        "Got SOA serial " + soa_parts[6] + ", expected " + serial
 
+ at step(u'last query should have (\S+) (.+)')
+def check_last_query(step, item, value):
+    assert world.last_query_result is not None
+    assert item in world.last_query_result.__dict__
+    lq_val = world.last_query_result.__dict__[item]
+    assert str(value) == str(lq_val),\
+           "Got: " + str(lq_val) + ", expected: " + str(value)
diff --git a/tests/lettuce/features/server_from_sqlite3.feature b/tests/lettuce/features/server_from_sqlite3.feature
index b7af4a7..472528c 100644
--- a/tests/lettuce/features/server_from_sqlite3.feature
+++ b/tests/lettuce/features/server_from_sqlite3.feature
@@ -11,11 +11,35 @@ Feature: SQLite3 backend
         I should see a database file
 
     Scenario: example.org queries
+        # This scenario performs a number of queries and inspects the results
+        # This is not only to test, but also to show the different options
+        # we have to inspect the data
         When I start bind10 with configuration example.org.config
         Then wait for bind10 auth to start
+
         A query for www.example.com should have rcode REFUSED
+
         A query for www.example.org should have rcode NOERROR
+        The last query should have qdcount 1
+        The last query should have ancount 1
+        The last query should have nscount 3
+        The last query should have adcount 0
+        The SOA serial for example.org should be 1234
+
         A query for doesnotexist.example.org should have rcode NXDOMAIN
+        The last query should have qdcount 1
+        The last query should have ancount 0
+        The last query should have nscount 1
+        The last query should have adcount 0
+        The last query should have flags qr aa rd
+
+        A query for www.example.org type TXT should have rcode NOERROR
+        The last query should have ancount 0
+
+        A query for www.example.org class CH should have rcode REFUSED
+        A query for www.example.org to 127.0.0.1 should have rcode NOERROR
+        A query for www.example.org to 127.0.0.1:47806 should have rcode NOERROR
+        A query for www.example.org type A class IN to 127.0.0.1:47806 should have rcode NOERROR
 
     Scenario: changing database
         # This scenario contains a lot of 'wait for' steps
diff --git a/tests/lettuce/features/steps.py b/tests/lettuce/features/steps.py
index 5540fba..70fef48 100644
--- a/tests/lettuce/features/steps.py
+++ b/tests/lettuce/features/steps.py
@@ -13,6 +13,7 @@ def initialize(feature):
     # run the bind10 instance
     world.bind10 = None
     world.bind10_output = []
+    world.last_query_result = None
 
     # Some tests can modify the settings. If the tests fail half-way, or
     # don't clean up, this can leave configurations or data in a bad state,




More information about the bind10-changes mailing list