def test_httpd(self): # dual stack (addresses is ipv4 and ipv6) if self.ipv6_enabled: server_addresses = (get_availaddr('::1'), get_availaddr()) self.stats_httpd = MyStatsHttpd(*server_addresses) for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertTrue(ht.address_family in set([socket.AF_INET, socket.AF_INET6])) self.assertTrue(isinstance(ht.socket, socket.socket)) ht.socket.close() # to silence warning about resource leak self.stats_httpd.close_mccs() # ditto # dual stack (address is ipv6) if self.ipv6_enabled: server_addresses = get_availaddr('::1') self.stats_httpd = MyStatsHttpd(server_addresses) for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertEqual(ht.address_family, socket.AF_INET6) self.assertTrue(isinstance(ht.socket, socket.socket)) ht.socket.close() self.stats_httpd.close_mccs() # ditto # dual/single stack (address is ipv4) server_addresses = get_availaddr() self.stats_httpd = MyStatsHttpd(server_addresses) for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertEqual(ht.address_family, socket.AF_INET) self.assertTrue(isinstance(ht.socket, socket.socket)) ht.socket.close() self.stats_httpd.close_mccs()
def test_open_template(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) # successful conditions tmpl = self.stats_httpd.open_template( stats_httpd.XML_TEMPLATE_LOCATION) self.assertTrue(isinstance(tmpl, string.Template)) opts = dict( xml_string="<dummy></dummy>", xsl_url_path="/path/to/") lines = tmpl.substitute(opts) for n in opts: self.assertGreater(lines.find(opts[n]), 0) tmpl = self.stats_httpd.open_template( stats_httpd.XSD_TEMPLATE_LOCATION) self.assertTrue(isinstance(tmpl, string.Template)) opts = dict(xsd_namespace="http://host/path/to/") lines = tmpl.substitute(opts) for n in opts: self.assertGreater(lines.find(opts[n]), 0) tmpl = self.stats_httpd.open_template( stats_httpd.XSL_TEMPLATE_LOCATION) self.assertTrue(isinstance(tmpl, string.Template)) opts = dict(xsd_namespace="http://host/path/to/") lines = tmpl.substitute(opts) for n in opts: self.assertGreater(lines.find(opts[n]), 0) # unsuccessful condition self.assertRaises( stats_httpd.StatsHttpdDataError, self.stats_httpd.open_template, '/path/to/foo/bar')
def test_xsd_handler(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) xsd_string = self.stats_httpd.xsd_handler() stats_xsd = xml.etree.ElementTree.fromstring(xsd_string) ns = '{%s}' % XMLNS_XSD stats_xsd = stats_xsd[1].find('%scomplexType/%ssequence/%selement' % ((ns,)*3)) self.assertEqual('item', stats_xsd.attrib['name']) stats_xsd = stats_xsd.find('%scomplexType' % ns) type_types = ('boolean', 'integer', 'real', 'string', 'map', \ 'list', 'named_set', 'any') attribs = [('identifier', 'string', 'required'), ('value', 'string', 'optional'), ('owner', 'string', 'required'), ('uri', 'anyURI', 'required'), ('name', 'string', 'required'), ('type', type_types, 'required'), ('description', 'string', 'optional'), ('title', 'string', 'optional'), ('optional', 'boolean', 'optional'), ('default', 'string', 'optional'), ('format', 'string', 'optional')] for i in range(0, len(attribs)): self.assertEqual(attribs[i][0], stats_xsd[i].attrib['name']) if attribs[i][0] == 'type': stats_xsd_types = \ stats_xsd[i].find('%ssimpleType/%srestriction' % \ ((ns,)*2)) for j in range(0, len(attribs[i][1])): self.assertEqual(attribs[i][1][j], \ stats_xsd_types[j].attrib['value']) else: self.assertEqual(attribs[i][1], stats_xsd[i].attrib['type']) self.assertEqual(attribs[i][2], stats_xsd[i].attrib['use'])
def test_failure_with_a_select_error (self): """checks select.error is raised if the exception except errno.EINTR is raised while it's selecting""" def raise_select_except(*args): raise select.error('dummy error') select.select = raise_select_except self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertRaises(select.error, self.stats_httpd.start)
def test_httpd_anyIPv4(self): # any address (IPv4) server_addresses = get_availaddr(address='0.0.0.0') self.stats_httpd = MyStatsHttpd(server_addresses) for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertEqual(ht.address_family,socket.AF_INET) self.assertTrue(isinstance(ht.socket, socket.socket))
def test_nofailure_with_errno_EINTR(self): """checks no exception is raised if errno.EINTR is raised while it's selecting""" self.__call_count = 0 select.select = lambda r, w, x, t: self.__faked_select( select.error(errno.EINTR)) self.stats_httpd = MyStatsHttpd(get_availaddr()) self.stats_httpd.start() # shouldn't leak the exception self.assertFalse(self.stats_httpd.running) self.assertIsNone(self.stats_httpd.mccs)
def test_commands(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertEqual(self.stats_httpd.command_handler("status", None), isc.config.ccsession.create_answer( 0, "Stats Httpd is up. (PID " + str(os.getpid()) + ")")) self.stats_httpd.running = True self.assertEqual(self.stats_httpd.command_handler("shutdown", None), isc.config.ccsession.create_answer(0)) self.assertFalse(self.stats_httpd.running) self.assertEqual( self.stats_httpd.command_handler("__UNKNOWN_COMMAND__", None), isc.config.ccsession.create_answer( 1, "Unknown command: __UNKNOWN_COMMAND__"))
def test_openclose_mccs(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) mccs = self.stats_httpd.mccs self.assertFalse(self.stats_httpd.mccs.stopped) self.assertFalse(self.stats_httpd.mccs.closed) self.stats_httpd.close_mccs() self.assertTrue(mccs.stopped) self.assertTrue(mccs.closed) self.assertIsNone(self.stats_httpd.mccs) self.stats_httpd.open_mccs() self.assertIsNotNone(self.stats_httpd.mccs) self.stats_httpd.mccs = None self.assertIsNone(self.stats_httpd.mccs) self.assertIsNone(self.stats_httpd.close_mccs())
def test_mccs(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertIsNotNone(self.stats_httpd.mccs.get_socket()) self.assertTrue( isinstance(self.stats_httpd.mccs.get_socket(), socket.socket)) self.assertIsNotNone(self.stats_httpd.cc_session) statistics_spec = self.stats_httpd.get_stats_spec() for mod in DUMMY_DATA: self.assertTrue(mod in statistics_spec) for cfg in statistics_spec[mod]: self.assertTrue('item_name' in cfg) self.assertTrue(cfg['item_name'] in DUMMY_DATA[mod]) self.assertTrue(len(statistics_spec[mod]), len(DUMMY_DATA[mod])) self.stats_httpd.close_mccs() self.assertIsNone(self.stats_httpd.mccs)
def test_config(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertEqual( self.stats_httpd.config_handler(dict(_UNKNOWN_KEY_=None)), isc.config.ccsession.create_answer( 1, "unknown item _UNKNOWN_KEY_")) addresses = get_availaddr() self.assertEqual( self.stats_httpd.config_handler( dict(listen_on=[dict(address=addresses[0],port=addresses[1])])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) self.assertTrue(addr["address"] == addresses[0]) self.assertTrue(addr["port"] == addresses[1]) if self.ipv6_enabled: addresses = get_availaddr("::1") self.assertEqual( self.stats_httpd.config_handler( dict(listen_on=[dict(address=addresses[0],port=addresses[1])])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) self.assertTrue(addr["address"] == addresses[0]) self.assertTrue(addr["port"] == addresses[1]) addresses = get_availaddr() self.assertEqual( self.stats_httpd.config_handler( dict(listen_on=[dict(address=addresses[0],port=addresses[1])])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) self.assertTrue(addr["address"] == addresses[0]) self.assertTrue(addr["port"] == addresses[1]) (ret, arg) = isc.config.ccsession.parse_answer( self.stats_httpd.config_handler( dict(listen_on=[dict(address="1.2.3.4",port=543210)])) ) self.assertEqual(ret, 1)
def test_init(self): server_address = get_availaddr() self.stats_httpd = MyStatsHttpd(server_address) self.assertEqual(self.stats_httpd.running, False) self.assertEqual(self.stats_httpd.poll_intval, 0.5) self.assertNotEqual(len(self.stats_httpd.httpd), 0) self.assertIsNotNone(self.stats_httpd.mccs) self.assertIsNotNone(self.stats_httpd.cc_session) # The real CfgMgr would return 'version', but our test mock omits it, # so the len(config) should be 1 self.assertEqual(len(self.stats_httpd.config), 1) self.assertTrue('listen_on' in self.stats_httpd.config) self.assertEqual(len(self.stats_httpd.config['listen_on']), 1) self.assertTrue('address' in self.stats_httpd.config['listen_on'][0]) self.assertTrue('port' in self.stats_httpd.config['listen_on'][0]) self.assertTrue(server_address in set(self.stats_httpd.http_addrs)) self.assertEqual('StatsHttpd', self.stats_httpd.mccs.\ get_module_spec().get_module_name())
def test_xsl_handler(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) xsl_string = self.stats_httpd.xsl_handler() stats_xsl = xml.etree.ElementTree.fromstring(xsl_string) nst = '{%s}' % XMLNS_XSL nsx = '{%s}' % XMLNS_XHTML self.assertEqual("bind10:statistics", stats_xsl[2].attrib['match']) stats_xsl = stats_xsl[2].find('%stable' % nsx) self.assertEqual('item', stats_xsl[1].attrib['select']) stats_xsl = stats_xsl[1].find('%str' % nsx) self.assertEqual('@uri', stats_xsl[0].find( '%selement/%sattribute/%svalue-of' % ((nst,)*3)).attrib['select']) self.assertEqual('@identifier', stats_xsl[0].find( '%selement/%svalue-of' % ((nst,)*2)).attrib['select']) self.assertEqual('@value', stats_xsl[1].find('%sif' % nst).attrib['test']) self.assertEqual('@value', stats_xsl[1].find('%sif/%svalue-of' % ((nst,)*2)).attrib['select']) self.assertEqual('@description', stats_xsl[2].find('%sif' % nst).attrib['test']) self.assertEqual('@description', stats_xsl[2].find('%sif/%svalue-of' % ((nst,)*2)).attrib['select'])
def test_running(self): # Previous version of this test checks the result of "status" and # "shutdown" commands; however, they are more explicitly tested # in specific tests. In this test we only have to check: # - start() will set 'running' to True # - as long as 'running' is True, it keeps calling select.select # - when running becomes False, it exists from the loop and calls # stop() self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertFalse(self.stats_httpd.running) # In this test we'll call select.select() 2 times: on the first call # stats_httpd.running should be True; on the second call the faked # select() will set it to False. self.__call_count = 0 select.select = lambda r, w, x, t: self.__faked_select() self.stats_httpd.start() self.assertFalse(self.stats_httpd.running) self.assertIsNone(self.stats_httpd.mccs) # stop() clears .mccs
def test_xml_handler(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) module_name = 'Dummy' stats_spec = \ { module_name : [{ "item_name": "foo", "item_type": "string", "item_optional": False, "item_default": "bar", "item_description": "foo is bar", "item_title": "Foo" }, { "item_name": "foo2", "item_type": "list", "item_optional": False, "item_default": [ { "zonename" : "test1", "queries.udp" : 1, "queries.tcp" : 2 }, { "zonename" : "test2", "queries.udp" : 3, "queries.tcp" : 4 } ], "item_title": "Foo bar", "item_description": "Foo bar", "list_item_spec": { "item_name": "foo2-1", "item_type": "map", "item_optional": False, "item_default": {}, "map_item_spec": [ { "item_name": "foo2-1-1", "item_type": "string", "item_optional": False, "item_default": "", "item_title": "Foo2 1 1", "item_description": "Foo bar" }, { "item_name": "foo2-1-2", "item_type": "integer", "item_optional": False, "item_default": 0, "item_title": "Foo2 1 2", "item_description": "Foo bar" }, { "item_name": "foo2-1-3", "item_type": "integer", "item_optional": False, "item_default": 0, "item_title": "Foo2 1 3", "item_description": "Foo bar" } ] } }] } stats_data = \ { module_name : { 'foo':'bar', 'foo2': [ { "foo2-1-1" : "bar1", "foo2-1-2" : 10, "foo2-1-3" : 9 }, { "foo2-1-1" : "bar2", "foo2-1-2" : 8, "foo2-1-3" : 7 } ] } } self.stats_httpd.get_stats_spec = lambda x,y: stats_spec self.stats_httpd.get_stats_data = lambda x,y: stats_data xml_string = self.stats_httpd.xml_handler() stats_xml = xml.etree.ElementTree.fromstring(xml_string) schema_loc = '{%s}schemaLocation' % XMLNS_XSI self.assertEqual(stats_xml.attrib[schema_loc], stats_httpd.XML_ROOT_ATTRIB['xsi:schemaLocation']) stats_data = stats_data[module_name] stats_spec = stats_spec[module_name] names = stats_httpd.item_name_list(stats_data, '') for i in range(0, len(names)): self.assertEqual('%s/%s' % (module_name, names[i]), stats_xml[i].attrib['identifier']) value = isc.cc.data.find(stats_data, names[i]) if type(value) is int: value = str(value) if type(value) is dict or type(value) is list: self.assertFalse('value' in stats_xml[i].attrib) else: self.assertEqual(value, stats_xml[i].attrib['value']) self.assertEqual(module_name, stats_xml[i].attrib['owner']) self.assertEqual(urllib.parse.quote('%s/%s/%s' % (stats_httpd.XML_URL_PATH, module_name, names[i])), stats_xml[i].attrib['uri']) spec = isc.config.find_spec_part(stats_spec, names[i]) self.assertEqual(spec['item_name'], stats_xml[i].attrib['name']) self.assertEqual(spec['item_type'], stats_xml[i].attrib['type']) self.assertEqual(spec['item_description'], stats_xml[i].attrib['description']) self.assertEqual(spec['item_title'], stats_xml[i].attrib['title']) self.assertEqual(str(spec['item_optional']).lower(), stats_xml[i].attrib['optional']) default = spec['item_default'] if type(default) is int: default = str(default) if type(default) is dict or type(default) is list: self.assertFalse('default' in stats_xml[i].attrib) else: self.assertEqual(default, stats_xml[i].attrib['default']) self.assertFalse('item_format' in spec) self.assertFalse('format' in stats_xml[i].attrib)
def test_running_fail(self): # A failure case of start(): we close the (real but dummy) socket for # the CC session. This breaks the select-loop due to exception self.stats_httpd = MyStatsHttpd(get_availaddr()) self.stats_httpd.mccs.get_socket().close() self.assertRaises(ValueError, self.stats_httpd.start)
class TestStatsHttpd(unittest.TestCase): """Tests for StatsHttpd class""" def setUp(self): # set the signal handler for deadlock self.sig_handler = SignalHandler(self.fail) # checking IPv6 enabled on this platform self.ipv6_enabled = is_ipv6_enabled() # instantiation of StatsHttpd indirectly calls gethostbyaddr(), which # can block for an uncontrollable period, leading many undesirable # results. We should rather eliminate the reliance, but until we # can make such fundamental cleanup we replace it with a faked method; # in our test scenario the return value doesn't matter. self.__gethostbyaddr_orig = socket.gethostbyaddr socket.gethostbyaddr = lambda x: ('test.example.', [], None) # Some tests replace this library function. Keep the original for # restor self.__orig_select_select = select.select def tearDown(self): socket.gethostbyaddr = self.__gethostbyaddr_orig if hasattr(self, "stats_httpd"): self.stats_httpd.stop() # reset the signal handler self.sig_handler.reset() # restore original of replaced library select.select = self.__orig_select_select def test_init(self): server_address = get_availaddr() self.stats_httpd = MyStatsHttpd(server_address) self.assertEqual(self.stats_httpd.running, False) self.assertEqual(self.stats_httpd.poll_intval, 0.5) self.assertNotEqual(len(self.stats_httpd.httpd), 0) self.assertIsNotNone(self.stats_httpd.mccs) self.assertIsNotNone(self.stats_httpd.cc_session) # The real CfgMgr would return 'version', but our test mock omits it, # so the len(config) should be 1 self.assertEqual(len(self.stats_httpd.config), 1) self.assertTrue('listen_on' in self.stats_httpd.config) self.assertEqual(len(self.stats_httpd.config['listen_on']), 1) self.assertTrue('address' in self.stats_httpd.config['listen_on'][0]) self.assertTrue('port' in self.stats_httpd.config['listen_on'][0]) self.assertTrue(server_address in set(self.stats_httpd.http_addrs)) self.assertEqual('StatsHttpd', self.stats_httpd.mccs.\ get_module_spec().get_module_name()) def test_init_hterr(self): """Test the behavior of StatsHttpd constructor when open_httpd fails. We specifically check the following two: - close_mccs() is called (so stats-httpd tells ConfigMgr it's shutting down) - the constructor results in HttpServerError exception. """ self.__mccs_closed = False def call_checker(): self.__mccs_closed = True class FailingStatsHttpd(MyStatsHttpd): def open_httpd(self): raise stats_httpd.HttpServerError def close_mccs(self): call_checker() self.assertRaises(stats_httpd.HttpServerError, FailingStatsHttpd) self.assertTrue(self.__mccs_closed) def test_openclose_mccs(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) mccs = self.stats_httpd.mccs self.assertFalse(self.stats_httpd.mccs.stopped) self.assertFalse(self.stats_httpd.mccs.closed) self.stats_httpd.close_mccs() self.assertTrue(mccs.stopped) self.assertTrue(mccs.closed) self.assertIsNone(self.stats_httpd.mccs) self.stats_httpd.open_mccs() self.assertIsNotNone(self.stats_httpd.mccs) self.stats_httpd.mccs = None self.assertIsNone(self.stats_httpd.mccs) self.assertIsNone(self.stats_httpd.close_mccs()) def test_mccs(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertIsNotNone(self.stats_httpd.mccs.get_socket()) self.assertTrue( isinstance(self.stats_httpd.mccs.get_socket(), socket.socket)) self.assertIsNotNone(self.stats_httpd.cc_session) statistics_spec = self.stats_httpd.get_stats_spec() for mod in DUMMY_DATA: self.assertTrue(mod in statistics_spec) for cfg in statistics_spec[mod]: self.assertTrue('item_name' in cfg) self.assertTrue(cfg['item_name'] in DUMMY_DATA[mod]) self.assertTrue(len(statistics_spec[mod]), len(DUMMY_DATA[mod])) self.stats_httpd.close_mccs() self.assertIsNone(self.stats_httpd.mccs) def test_httpd(self): # dual stack (addresses is ipv4 and ipv6) if self.ipv6_enabled: server_addresses = (get_availaddr('::1'), get_availaddr()) self.stats_httpd = MyStatsHttpd(*server_addresses) for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertTrue(ht.address_family in set([socket.AF_INET, socket.AF_INET6])) self.assertTrue(isinstance(ht.socket, socket.socket)) ht.socket.close() # to silence warning about resource leak self.stats_httpd.close_mccs() # ditto # dual stack (address is ipv6) if self.ipv6_enabled: server_addresses = get_availaddr('::1') self.stats_httpd = MyStatsHttpd(server_addresses) for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertEqual(ht.address_family, socket.AF_INET6) self.assertTrue(isinstance(ht.socket, socket.socket)) ht.socket.close() self.stats_httpd.close_mccs() # ditto # dual/single stack (address is ipv4) server_addresses = get_availaddr() self.stats_httpd = MyStatsHttpd(server_addresses) for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertEqual(ht.address_family, socket.AF_INET) self.assertTrue(isinstance(ht.socket, socket.socket)) ht.socket.close() self.stats_httpd.close_mccs() def test_httpd_anyIPv4(self): # any address (IPv4) server_addresses = get_availaddr(address='0.0.0.0') self.stats_httpd = MyStatsHttpd(server_addresses) for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertEqual(ht.address_family,socket.AF_INET) self.assertTrue(isinstance(ht.socket, socket.socket)) def test_httpd_anyIPv6(self): # any address (IPv6) if self.ipv6_enabled: server_addresses = get_availaddr(address='::') self.stats_httpd = MyStatsHttpd(server_addresses) for ht in self.stats_httpd.httpd: self.assertTrue(isinstance(ht, stats_httpd.HttpServer)) self.assertEqual(ht.address_family,socket.AF_INET6) self.assertTrue(isinstance(ht.socket, socket.socket)) def test_httpd_failed(self): # existent hostname self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, get_availaddr(address='localhost')) # nonexistent hostname self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, ('my.host.domain', 8000)) # over flow of port number self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, ('127.0.0.1', 80000)) # negative self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, ('127.0.0.1', -8000)) # alphabet self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, ('127.0.0.1', 'ABCDE')) # Address already in use server_addresses = get_availaddr() server = MyStatsHttpd(server_addresses) self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd, server_addresses) def __faked_select(self, ex=None): """A helper subroutine for tests using faked select.select. See test_running() for basic features. If ex is not None, it's assumed to be an exception object and will be raised on the first call. """ self.assertTrue(self.stats_httpd.running) self.__call_count += 1 if ex is not None and self.__call_count == 1: raise ex if self.__call_count == 2: self.stats_httpd.running = False assert self.__call_count <= 2 # safety net to avoid infinite loop return ([], [], []) def test_running(self): # Previous version of this test checks the result of "status" and # "shutdown" commands; however, they are more explicitly tested # in specific tests. In this test we only have to check: # - start() will set 'running' to True # - as long as 'running' is True, it keeps calling select.select # - when running becomes False, it exists from the loop and calls # stop() self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertFalse(self.stats_httpd.running) # In this test we'll call select.select() 2 times: on the first call # stats_httpd.running should be True; on the second call the faked # select() will set it to False. self.__call_count = 0 select.select = lambda r, w, x, t: self.__faked_select() self.stats_httpd.start() self.assertFalse(self.stats_httpd.running) self.assertIsNone(self.stats_httpd.mccs) # stop() clears .mccs def test_running_fail(self): # A failure case of start(): we close the (real but dummy) socket for # the CC session. This breaks the select-loop due to exception self.stats_httpd = MyStatsHttpd(get_availaddr()) self.stats_httpd.mccs.get_socket().close() self.assertRaises(ValueError, self.stats_httpd.start) def test_failure_with_a_select_error (self): """checks select.error is raised if the exception except errno.EINTR is raised while it's selecting""" def raise_select_except(*args): raise select.error('dummy error') select.select = raise_select_except self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertRaises(select.error, self.stats_httpd.start) def test_nofailure_with_errno_EINTR(self): """checks no exception is raised if errno.EINTR is raised while it's selecting""" self.__call_count = 0 select.select = lambda r, w, x, t: self.__faked_select( select.error(errno.EINTR)) self.stats_httpd = MyStatsHttpd(get_availaddr()) self.stats_httpd.start() # shouldn't leak the exception self.assertFalse(self.stats_httpd.running) self.assertIsNone(self.stats_httpd.mccs) def test_open_template(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) # successful conditions tmpl = self.stats_httpd.open_template( stats_httpd.XML_TEMPLATE_LOCATION) self.assertTrue(isinstance(tmpl, string.Template)) opts = dict( xml_string="<dummy></dummy>", xsl_url_path="/path/to/") lines = tmpl.substitute(opts) for n in opts: self.assertGreater(lines.find(opts[n]), 0) tmpl = self.stats_httpd.open_template( stats_httpd.XSD_TEMPLATE_LOCATION) self.assertTrue(isinstance(tmpl, string.Template)) opts = dict(xsd_namespace="http://host/path/to/") lines = tmpl.substitute(opts) for n in opts: self.assertGreater(lines.find(opts[n]), 0) tmpl = self.stats_httpd.open_template( stats_httpd.XSL_TEMPLATE_LOCATION) self.assertTrue(isinstance(tmpl, string.Template)) opts = dict(xsd_namespace="http://host/path/to/") lines = tmpl.substitute(opts) for n in opts: self.assertGreater(lines.find(opts[n]), 0) # unsuccessful condition self.assertRaises( stats_httpd.StatsHttpdDataError, self.stats_httpd.open_template, '/path/to/foo/bar') def test_commands(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertEqual(self.stats_httpd.command_handler("status", None), isc.config.ccsession.create_answer( 0, "Stats Httpd is up. (PID " + str(os.getpid()) + ")")) self.stats_httpd.running = True self.assertEqual(self.stats_httpd.command_handler("shutdown", None), isc.config.ccsession.create_answer(0)) self.assertFalse(self.stats_httpd.running) self.assertEqual( self.stats_httpd.command_handler("__UNKNOWN_COMMAND__", None), isc.config.ccsession.create_answer( 1, "Unknown command: __UNKNOWN_COMMAND__")) def test_config(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) self.assertEqual( self.stats_httpd.config_handler(dict(_UNKNOWN_KEY_=None)), isc.config.ccsession.create_answer( 1, "unknown item _UNKNOWN_KEY_")) addresses = get_availaddr() self.assertEqual( self.stats_httpd.config_handler( dict(listen_on=[dict(address=addresses[0],port=addresses[1])])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) self.assertTrue(addr["address"] == addresses[0]) self.assertTrue(addr["port"] == addresses[1]) if self.ipv6_enabled: addresses = get_availaddr("::1") self.assertEqual( self.stats_httpd.config_handler( dict(listen_on=[dict(address=addresses[0],port=addresses[1])])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) self.assertTrue(addr["address"] == addresses[0]) self.assertTrue(addr["port"] == addresses[1]) addresses = get_availaddr() self.assertEqual( self.stats_httpd.config_handler( dict(listen_on=[dict(address=addresses[0],port=addresses[1])])), isc.config.ccsession.create_answer(0)) self.assertTrue("listen_on" in self.stats_httpd.config) for addr in self.stats_httpd.config["listen_on"]: self.assertTrue("address" in addr) self.assertTrue("port" in addr) self.assertTrue(addr["address"] == addresses[0]) self.assertTrue(addr["port"] == addresses[1]) (ret, arg) = isc.config.ccsession.parse_answer( self.stats_httpd.config_handler( dict(listen_on=[dict(address="1.2.3.4",port=543210)])) ) self.assertEqual(ret, 1) @unittest.skipUnless(xml_parser, "skipping the test using XMLParser") def test_xml_handler(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) module_name = 'Dummy' stats_spec = \ { module_name : [{ "item_name": "foo", "item_type": "string", "item_optional": False, "item_default": "bar", "item_description": "foo is bar", "item_title": "Foo" }, { "item_name": "foo2", "item_type": "list", "item_optional": False, "item_default": [ { "zonename" : "test1", "queries.udp" : 1, "queries.tcp" : 2 }, { "zonename" : "test2", "queries.udp" : 3, "queries.tcp" : 4 } ], "item_title": "Foo bar", "item_description": "Foo bar", "list_item_spec": { "item_name": "foo2-1", "item_type": "map", "item_optional": False, "item_default": {}, "map_item_spec": [ { "item_name": "foo2-1-1", "item_type": "string", "item_optional": False, "item_default": "", "item_title": "Foo2 1 1", "item_description": "Foo bar" }, { "item_name": "foo2-1-2", "item_type": "integer", "item_optional": False, "item_default": 0, "item_title": "Foo2 1 2", "item_description": "Foo bar" }, { "item_name": "foo2-1-3", "item_type": "integer", "item_optional": False, "item_default": 0, "item_title": "Foo2 1 3", "item_description": "Foo bar" } ] } }] } stats_data = \ { module_name : { 'foo':'bar', 'foo2': [ { "foo2-1-1" : "bar1", "foo2-1-2" : 10, "foo2-1-3" : 9 }, { "foo2-1-1" : "bar2", "foo2-1-2" : 8, "foo2-1-3" : 7 } ] } } self.stats_httpd.get_stats_spec = lambda x,y: stats_spec self.stats_httpd.get_stats_data = lambda x,y: stats_data xml_string = self.stats_httpd.xml_handler() stats_xml = xml.etree.ElementTree.fromstring(xml_string) schema_loc = '{%s}schemaLocation' % XMLNS_XSI self.assertEqual(stats_xml.attrib[schema_loc], stats_httpd.XML_ROOT_ATTRIB['xsi:schemaLocation']) stats_data = stats_data[module_name] stats_spec = stats_spec[module_name] names = stats_httpd.item_name_list(stats_data, '') for i in range(0, len(names)): self.assertEqual('%s/%s' % (module_name, names[i]), stats_xml[i].attrib['identifier']) value = isc.cc.data.find(stats_data, names[i]) if type(value) is int: value = str(value) if type(value) is dict or type(value) is list: self.assertFalse('value' in stats_xml[i].attrib) else: self.assertEqual(value, stats_xml[i].attrib['value']) self.assertEqual(module_name, stats_xml[i].attrib['owner']) self.assertEqual(urllib.parse.quote('%s/%s/%s' % (stats_httpd.XML_URL_PATH, module_name, names[i])), stats_xml[i].attrib['uri']) spec = isc.config.find_spec_part(stats_spec, names[i]) self.assertEqual(spec['item_name'], stats_xml[i].attrib['name']) self.assertEqual(spec['item_type'], stats_xml[i].attrib['type']) self.assertEqual(spec['item_description'], stats_xml[i].attrib['description']) self.assertEqual(spec['item_title'], stats_xml[i].attrib['title']) self.assertEqual(str(spec['item_optional']).lower(), stats_xml[i].attrib['optional']) default = spec['item_default'] if type(default) is int: default = str(default) if type(default) is dict or type(default) is list: self.assertFalse('default' in stats_xml[i].attrib) else: self.assertEqual(default, stats_xml[i].attrib['default']) self.assertFalse('item_format' in spec) self.assertFalse('format' in stats_xml[i].attrib) @unittest.skipUnless(xml_parser, "skipping the test using XMLParser") def test_xsd_handler(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) xsd_string = self.stats_httpd.xsd_handler() stats_xsd = xml.etree.ElementTree.fromstring(xsd_string) ns = '{%s}' % XMLNS_XSD stats_xsd = stats_xsd[1].find('%scomplexType/%ssequence/%selement' % ((ns,)*3)) self.assertEqual('item', stats_xsd.attrib['name']) stats_xsd = stats_xsd.find('%scomplexType' % ns) type_types = ('boolean', 'integer', 'real', 'string', 'map', \ 'list', 'named_set', 'any') attribs = [('identifier', 'string', 'required'), ('value', 'string', 'optional'), ('owner', 'string', 'required'), ('uri', 'anyURI', 'required'), ('name', 'string', 'required'), ('type', type_types, 'required'), ('description', 'string', 'optional'), ('title', 'string', 'optional'), ('optional', 'boolean', 'optional'), ('default', 'string', 'optional'), ('format', 'string', 'optional')] for i in range(0, len(attribs)): self.assertEqual(attribs[i][0], stats_xsd[i].attrib['name']) if attribs[i][0] == 'type': stats_xsd_types = \ stats_xsd[i].find('%ssimpleType/%srestriction' % \ ((ns,)*2)) for j in range(0, len(attribs[i][1])): self.assertEqual(attribs[i][1][j], \ stats_xsd_types[j].attrib['value']) else: self.assertEqual(attribs[i][1], stats_xsd[i].attrib['type']) self.assertEqual(attribs[i][2], stats_xsd[i].attrib['use']) @unittest.skipUnless(xml_parser, "skipping the test using XMLParser") def test_xsl_handler(self): self.stats_httpd = MyStatsHttpd(get_availaddr()) xsl_string = self.stats_httpd.xsl_handler() stats_xsl = xml.etree.ElementTree.fromstring(xsl_string) nst = '{%s}' % XMLNS_XSL nsx = '{%s}' % XMLNS_XHTML self.assertEqual("bind10:statistics", stats_xsl[2].attrib['match']) stats_xsl = stats_xsl[2].find('%stable' % nsx) self.assertEqual('item', stats_xsl[1].attrib['select']) stats_xsl = stats_xsl[1].find('%str' % nsx) self.assertEqual('@uri', stats_xsl[0].find( '%selement/%sattribute/%svalue-of' % ((nst,)*3)).attrib['select']) self.assertEqual('@identifier', stats_xsl[0].find( '%selement/%svalue-of' % ((nst,)*2)).attrib['select']) self.assertEqual('@value', stats_xsl[1].find('%sif' % nst).attrib['test']) self.assertEqual('@value', stats_xsl[1].find('%sif/%svalue-of' % ((nst,)*2)).attrib['select']) self.assertEqual('@description', stats_xsl[2].find('%sif' % nst).attrib['test']) self.assertEqual('@description', stats_xsl[2].find('%sif/%svalue-of' % ((nst,)*2)).attrib['select'])