b10-stats-httpd_test.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. # Copyright (C) 2011 Internet Systems Consortium.
  2. #
  3. # Permission to use, copy, modify, and distribute this software for any
  4. # purpose with or without fee is hereby granted, provided that the above
  5. # copyright notice and this permission notice appear in all copies.
  6. #
  7. # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
  8. # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  9. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  10. # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  12. # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  13. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  14. # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. import unittest
  16. import os
  17. import imp
  18. import socket
  19. import errno
  20. import select
  21. import string
  22. import time
  23. import threading
  24. import http.client
  25. import xml.etree.ElementTree
  26. import isc
  27. import stats_httpd
  28. import stats
  29. from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, TIMEOUT_SEC
  30. DUMMY_DATA = {
  31. 'Boss' : {
  32. "boot_time": "2011-03-04T11:59:06Z"
  33. },
  34. 'Auth' : {
  35. "queries.tcp": 2,
  36. "queries.udp": 3
  37. },
  38. 'Stats' : {
  39. "report_time": "2011-03-04T11:59:19Z",
  40. "boot_time": "2011-03-04T11:59:06Z",
  41. "last_update_time": "2011-03-04T11:59:07Z",
  42. "lname": "4d70d40a_c@host",
  43. "timestamp": 1299239959.560846
  44. }
  45. }
  46. class TestHttpHandler(unittest.TestCase):
  47. """Tests for HttpHandler class"""
  48. def setUp(self):
  49. self.base = BaseModules()
  50. self.stats_server = ThreadingServerManager(MyStats)
  51. self.stats = self.stats_server.server
  52. self.stats_server.run()
  53. def tearDown(self):
  54. self.stats_server.shutdown()
  55. self.base.shutdown()
  56. def test_do_GET(self):
  57. (address, port) = ('127.0.0.1', 65450)
  58. statshttpd_server = ThreadingServerManager(MyStatsHttpd)
  59. self.stats_httpd = statshttpd_server.server
  60. self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]})
  61. self.assertTrue(type(self.stats_httpd.httpd) is list)
  62. self.assertEqual(len(self.stats_httpd.httpd), 0)
  63. statshttpd_server.run()
  64. time.sleep(TIMEOUT_SEC*5)
  65. client = http.client.HTTPConnection(address, port)
  66. client._http_vsn_str = 'HTTP/1.0\n'
  67. client.connect()
  68. # URL is '/bind10/statistics/xml'
  69. client.putrequest('GET', stats_httpd.XML_URL_PATH)
  70. client.endheaders()
  71. response = client.getresponse()
  72. self.assertEqual(response.getheader("Content-type"), "text/xml")
  73. self.assertTrue(int(response.getheader("Content-Length")) > 0)
  74. self.assertEqual(response.status, 200)
  75. root = xml.etree.ElementTree.parse(response).getroot()
  76. self.assertTrue(root.tag.find('stats_data') > 0)
  77. for (k,v) in root.attrib.items():
  78. if k.find('schemaLocation') > 0:
  79. self.assertEqual(v, stats_httpd.XSD_NAMESPACE + ' ' + stats_httpd.XSD_URL_PATH)
  80. for mod in DUMMY_DATA:
  81. for (item, value) in DUMMY_DATA[mod].items():
  82. self.assertIsNotNone(root.find(mod + '/' + item))
  83. # URL is '/bind10/statitics/xsd'
  84. client.putrequest('GET', stats_httpd.XSD_URL_PATH)
  85. client.endheaders()
  86. response = client.getresponse()
  87. self.assertEqual(response.getheader("Content-type"), "text/xml")
  88. self.assertTrue(int(response.getheader("Content-Length")) > 0)
  89. self.assertEqual(response.status, 200)
  90. root = xml.etree.ElementTree.parse(response).getroot()
  91. url_xmlschema = '{http://www.w3.org/2001/XMLSchema}'
  92. tags = [ url_xmlschema + t for t in [ 'element', 'complexType', 'all', 'element' ] ]
  93. xsdpath = '/'.join(tags)
  94. self.assertTrue(root.tag.find('schema') > 0)
  95. self.assertTrue(hasattr(root, 'attrib'))
  96. self.assertTrue('targetNamespace' in root.attrib)
  97. self.assertEqual(root.attrib['targetNamespace'],
  98. stats_httpd.XSD_NAMESPACE)
  99. for elm in root.findall(xsdpath):
  100. self.assertIsNotNone(elm.attrib['name'])
  101. self.assertTrue(elm.attrib['name'] in DUMMY_DATA)
  102. # URL is '/bind10/statitics/xsl'
  103. client.putrequest('GET', stats_httpd.XSL_URL_PATH)
  104. client.endheaders()
  105. response = client.getresponse()
  106. self.assertEqual(response.getheader("Content-type"), "text/xml")
  107. self.assertTrue(int(response.getheader("Content-Length")) > 0)
  108. self.assertEqual(response.status, 200)
  109. root = xml.etree.ElementTree.parse(response).getroot()
  110. url_trans = '{http://www.w3.org/1999/XSL/Transform}'
  111. url_xhtml = '{http://www.w3.org/1999/xhtml}'
  112. xslpath = url_trans + 'template/' + url_xhtml + 'tr'
  113. self.assertEqual(root.tag, url_trans + 'stylesheet')
  114. for tr in root.findall(xslpath):
  115. tds = tr.findall(url_xhtml + 'td')
  116. self.assertIsNotNone(tds)
  117. self.assertEqual(type(tds), list)
  118. self.assertTrue(len(tds) > 2)
  119. self.assertTrue(hasattr(tds[0], 'text'))
  120. self.assertTrue(tds[0].text in DUMMY_DATA)
  121. valueof = tds[2].find(url_trans + 'value-of')
  122. self.assertIsNotNone(valueof)
  123. self.assertTrue(hasattr(valueof, 'attrib'))
  124. self.assertIsNotNone(valueof.attrib)
  125. self.assertTrue('select' in valueof.attrib)
  126. self.assertTrue(valueof.attrib['select'] in \
  127. [ tds[0].text+'/'+item for item in DUMMY_DATA[tds[0].text].keys() ])
  128. # 302 redirect
  129. client._http_vsn_str = 'HTTP/1.1'
  130. client.putrequest('GET', '/')
  131. client.putheader('Host', address)
  132. client.endheaders()
  133. response = client.getresponse()
  134. self.assertEqual(response.status, 302)
  135. self.assertEqual(response.getheader('Location'),
  136. "http://%s:%d%s" % (address, port, stats_httpd.XML_URL_PATH))
  137. # # 404 NotFound
  138. client._http_vsn_str = 'HTTP/1.0'
  139. client.putrequest('GET', '/path/to/foo/bar')
  140. client.endheaders()
  141. response = client.getresponse()
  142. self.assertEqual(response.status, 404)
  143. client.close()
  144. statshttpd_server.shutdown()
  145. def test_do_GET_failed1(self):
  146. # failure case(connection with Stats is down)
  147. (address, port) = ('127.0.0.1', 65451)
  148. statshttpd_server = ThreadingServerManager(MyStatsHttpd)
  149. statshttpd = statshttpd_server.server
  150. statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]})
  151. statshttpd_server.run()
  152. self.assertTrue(self.stats_server.server.running)
  153. self.stats_server.shutdown()
  154. time.sleep(TIMEOUT_SEC*2)
  155. self.assertFalse(self.stats_server.server.running)
  156. statshttpd.cc_session.set_timeout(milliseconds=TIMEOUT_SEC/1000)
  157. client = http.client.HTTPConnection(address, port)
  158. client.connect()
  159. # request XML
  160. client.putrequest('GET', stats_httpd.XML_URL_PATH)
  161. client.endheaders()
  162. response = client.getresponse()
  163. self.assertEqual(response.status, 500)
  164. # request XSD
  165. client.putrequest('GET', stats_httpd.XSD_URL_PATH)
  166. client.endheaders()
  167. response = client.getresponse()
  168. self.assertEqual(response.status, 500)
  169. # request XSL
  170. client.putrequest('GET', stats_httpd.XSL_URL_PATH)
  171. client.endheaders()
  172. response = client.getresponse()
  173. self.assertEqual(response.status, 500)
  174. client.close()
  175. statshttpd_server.shutdown()
  176. def test_do_GET_failed2(self):
  177. # failure case(connection with Stats is down)
  178. (address, port) = ('127.0.0.1', 65452)
  179. statshttpd_server = ThreadingServerManager(MyStatsHttpd)
  180. self.stats_httpd = statshttpd_server.server
  181. self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]})
  182. statshttpd_server.run()
  183. self.stats.mccs.set_command_handler(
  184. lambda cmd, args: \
  185. isc.config.ccsession.create_answer(1, "I have an error.")
  186. )
  187. time.sleep(TIMEOUT_SEC*5)
  188. client = http.client.HTTPConnection(address, port)
  189. client.connect()
  190. # request XML
  191. client.putrequest('GET', stats_httpd.XML_URL_PATH)
  192. client.endheaders()
  193. response = client.getresponse()
  194. self.assertEqual(response.status, 500)
  195. # request XSD
  196. client.putrequest('GET', stats_httpd.XSD_URL_PATH)
  197. client.endheaders()
  198. response = client.getresponse()
  199. self.assertEqual(response.status, 500)
  200. # request XSL
  201. client.putrequest('GET', stats_httpd.XSL_URL_PATH)
  202. client.endheaders()
  203. response = client.getresponse()
  204. self.assertEqual(response.status, 500)
  205. client.close()
  206. statshttpd_server.shutdown()
  207. def test_do_HEAD(self):
  208. (address, port) = ('127.0.0.1', 65453)
  209. statshttpd_server = ThreadingServerManager(MyStatsHttpd)
  210. self.stats_httpd = statshttpd_server.server
  211. self.stats_httpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]})
  212. statshttpd_server.run()
  213. time.sleep(TIMEOUT_SEC*5)
  214. client = http.client.HTTPConnection(address, port)
  215. client.connect()
  216. client.putrequest('HEAD', stats_httpd.XML_URL_PATH)
  217. client.endheaders()
  218. response = client.getresponse()
  219. self.assertEqual(response.status, 200)
  220. client.putrequest('HEAD', '/path/to/foo/bar')
  221. client.endheaders()
  222. response = client.getresponse()
  223. self.assertEqual(response.status, 404)
  224. client.close()
  225. statshttpd_server.shutdown()
  226. def test_log_message(self):
  227. class MyHttpHandler(stats_httpd.HttpHandler):
  228. def __init__(self):
  229. class _Dummy_class_(): pass
  230. self.address_string = lambda : 'dummyhost'
  231. self.log_date_time_string = lambda : \
  232. 'DD/MM/YYYY HH:MI:SS'
  233. self.server = _Dummy_class_()
  234. self.server.log_writer = self.log_writer
  235. def log_writer(self, line):
  236. self.logged_line = line
  237. self.handler = MyHttpHandler()
  238. self.handler.log_message("%s %d", 'ABCDEFG', 12345)
  239. self.assertEqual(self.handler.logged_line,
  240. "[b10-stats-httpd] dummyhost - - "
  241. + "[DD/MM/YYYY HH:MI:SS] ABCDEFG 12345\n")
  242. class TestHttpServerError(unittest.TestCase):
  243. """Tests for HttpServerError exception"""
  244. def test_raises(self):
  245. try:
  246. raise stats_httpd.HttpServerError('Nothing')
  247. except stats_httpd.HttpServerError as err:
  248. self.assertEqual(str(err), 'Nothing')
  249. class TestHttpServer(unittest.TestCase):
  250. """Tests for HttpServer class"""
  251. def setUp(self):
  252. self.base = BaseModules()
  253. def tearDown(self):
  254. self.base.shutdown()
  255. def test_httpserver(self):
  256. statshttpd = stats_httpd.StatsHttpd()
  257. self.assertEqual(type(statshttpd.httpd), list)
  258. self.assertEqual(len(statshttpd.httpd), 0)
  259. class TestStatsHttpdError(unittest.TestCase):
  260. """Tests for StatsHttpdError exception"""
  261. def test_raises(self):
  262. try:
  263. raise stats_httpd.StatsHttpdError('Nothing')
  264. except stats_httpd.StatsHttpdError as err:
  265. self.assertEqual(str(err), 'Nothing')
  266. class TestStatsHttpd(unittest.TestCase):
  267. """Tests for StatsHttpd class"""
  268. def setUp(self):
  269. self.base = BaseModules()
  270. self.stats_server = ThreadingServerManager(MyStats)
  271. self.stats = self.stats_server.server
  272. self.stats_server.run()
  273. self.stats_httpd = stats_httpd.StatsHttpd()
  274. # checking IPv6 enabled on this platform
  275. self.ipv6_enabled = True
  276. try:
  277. sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
  278. sock.bind(("::1",8000))
  279. sock.close()
  280. except socket.error:
  281. self.ipv6_enabled = False
  282. def tearDown(self):
  283. self.stats_httpd.stop()
  284. self.stats_server.shutdown()
  285. self.base.shutdown()
  286. def test_init(self):
  287. self.assertEqual(self.stats_httpd.running, False)
  288. self.assertEqual(self.stats_httpd.poll_intval, 0.5)
  289. self.assertEqual(self.stats_httpd.httpd, [])
  290. self.assertEqual(type(self.stats_httpd.mccs), isc.config.ModuleCCSession)
  291. self.assertEqual(type(self.stats_httpd.cc_session), isc.cc.Session)
  292. self.assertEqual(len(self.stats_httpd.config), 2)
  293. self.assertTrue('listen_on' in self.stats_httpd.config)
  294. self.assertEqual(len(self.stats_httpd.config['listen_on']), 1)
  295. self.assertTrue('address' in self.stats_httpd.config['listen_on'][0])
  296. self.assertTrue('port' in self.stats_httpd.config['listen_on'][0])
  297. self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs))
  298. def test_openclose_mccs(self):
  299. statshttpd = stats_httpd.StatsHttpd()
  300. statshttpd.close_mccs()
  301. self.assertEqual(statshttpd.mccs, None)
  302. statshttpd.open_mccs()
  303. self.assertIsNotNone(statshttpd.mccs)
  304. statshttpd.mccs = None
  305. self.assertEqual(statshttpd.mccs, None)
  306. self.assertEqual(statshttpd.close_mccs(), None)
  307. def test_mccs(self):
  308. self.assertIsNotNone(self.stats_httpd.mccs.get_socket())
  309. self.assertTrue(
  310. isinstance(self.stats_httpd.mccs.get_socket(), socket.socket))
  311. self.assertTrue(
  312. isinstance(self.stats_httpd.cc_session, isc.cc.session.Session))
  313. self.statistics_spec = self.stats_httpd.get_stats_spec()
  314. for mod in DUMMY_DATA:
  315. self.assertTrue(mod in self.statistics_spec)
  316. for cfg in self.statistics_spec[mod]:
  317. self.assertTrue('item_name' in cfg)
  318. self.assertTrue(cfg['item_name'] in DUMMY_DATA[mod])
  319. self.assertTrue(len(self.statistics_spec[mod]), len(DUMMY_DATA[mod]))
  320. self.stats_httpd.close_mccs()
  321. self.assertIsNone(self.stats_httpd.mccs)
  322. def test_httpd(self):
  323. # dual stack (addresses is ipv4 and ipv6)
  324. if self.ipv6_enabled:
  325. self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs))
  326. self.stats_httpd.http_addrs = [ ('::1', 8000), ('127.0.0.1', 8000) ]
  327. self.assertTrue(
  328. stats_httpd.HttpServer.address_family in set([socket.AF_INET, socket.AF_INET6]))
  329. self.stats_httpd.open_httpd()
  330. for ht in self.stats_httpd.httpd:
  331. self.assertTrue(isinstance(ht.socket, socket.socket))
  332. self.stats_httpd.close_httpd()
  333. # dual stack (address is ipv6)
  334. if self.ipv6_enabled:
  335. self.stats_httpd.http_addrs = [ ('::1', 8000) ]
  336. self.stats_httpd.open_httpd()
  337. for ht in self.stats_httpd.httpd:
  338. self.assertTrue(isinstance(ht.socket, socket.socket))
  339. self.stats_httpd.close_httpd()
  340. # dual stack (address is ipv4)
  341. if self.ipv6_enabled:
  342. self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ]
  343. self.stats_httpd.open_httpd()
  344. for ht in self.stats_httpd.httpd:
  345. self.assertTrue(isinstance(ht.socket, socket.socket))
  346. self.stats_httpd.close_httpd()
  347. # only-ipv4 single stack
  348. if not self.ipv6_enabled:
  349. self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ]
  350. self.stats_httpd.open_httpd()
  351. for ht in self.stats_httpd.httpd:
  352. self.assertTrue(isinstance(ht.socket, socket.socket))
  353. self.stats_httpd.close_httpd()
  354. # only-ipv4 single stack (force set ipv6 )
  355. if not self.ipv6_enabled:
  356. self.stats_httpd.http_addrs = [ ('::1', 8000) ]
  357. self.assertRaises(stats_httpd.HttpServerError,
  358. self.stats_httpd.open_httpd)
  359. # hostname
  360. self.stats_httpd.http_addrs = [ ('localhost', 8000) ]
  361. self.stats_httpd.open_httpd()
  362. for ht in self.stats_httpd.httpd:
  363. self.assertTrue(isinstance(ht.socket, socket.socket))
  364. self.stats_httpd.close_httpd()
  365. self.stats_httpd.http_addrs = [ ('my.host.domain', 8000) ]
  366. self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd)
  367. self.assertEqual(type(self.stats_httpd.httpd), list)
  368. self.assertEqual(len(self.stats_httpd.httpd), 0)
  369. self.stats_httpd.close_httpd()
  370. # over flow of port number
  371. self.stats_httpd.http_addrs = [ ('', 80000) ]
  372. self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd)
  373. # negative
  374. self.stats_httpd.http_addrs = [ ('', -8000) ]
  375. self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd)
  376. # alphabet
  377. self.stats_httpd.http_addrs = [ ('', 'ABCDE') ]
  378. self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd)
  379. # Address already in use
  380. self.statshttpd_server = ThreadingServerManager(MyStatsHttpd)
  381. self.statshttpd_server.server.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65454 }]})
  382. self.statshttpd_server.run()
  383. time.sleep(TIMEOUT_SEC)
  384. self.stats_httpd.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65454 }]})
  385. self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd)
  386. self.statshttpd_server.shutdown()
  387. def test_running(self):
  388. self.assertFalse(self.stats_httpd.running)
  389. self.statshttpd_server = ThreadingServerManager(MyStatsHttpd)
  390. self.stats_httpd = self.statshttpd_server.server
  391. self.stats_httpd.load_config({'listen_on' : [{ 'address': '127.0.0.1', 'port' : 65455 }]})
  392. self.statshttpd_server.run()
  393. time.sleep(TIMEOUT_SEC*2)
  394. self.assertTrue(self.stats_httpd.running)
  395. self.statshttpd_server.shutdown()
  396. self.assertFalse(self.stats_httpd.running)
  397. # failure case
  398. self.stats_httpd = stats_httpd.StatsHttpd()
  399. self.stats_httpd.cc_session.close()
  400. self.assertRaises(
  401. isc.cc.session.SessionError, self.stats_httpd.start)
  402. def test_select_failure(self):
  403. def raise_select_except(*args):
  404. raise select.error('dummy error')
  405. def raise_select_except_with_errno(*args):
  406. raise select.error(errno.EINTR)
  407. (address, port) = ('127.0.0.1', 65456)
  408. stats_httpd.select.select = raise_select_except
  409. statshttpd = stats_httpd.StatsHttpd()
  410. statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]})
  411. self.assertRaises(select.error, statshttpd.start)
  412. statshttpd.stop()
  413. stats_httpd.select.select = raise_select_except_with_errno
  414. statshttpd_server = ThreadingServerManager(MyStatsHttpd)
  415. statshttpd = statshttpd_server.server
  416. statshttpd.load_config({'listen_on' : [{ 'address': address, 'port' : port }]})
  417. statshttpd_server.run()
  418. time.sleep(TIMEOUT_SEC*2)
  419. statshttpd_server.shutdown()
  420. def test_open_template(self):
  421. # successful conditions
  422. tmpl = self.stats_httpd.open_template(stats_httpd.XML_TEMPLATE_LOCATION)
  423. self.assertTrue(isinstance(tmpl, string.Template))
  424. opts = dict(
  425. xml_string="<dummy></dummy>",
  426. xsd_namespace="http://host/path/to/",
  427. xsd_url_path="/path/to/",
  428. xsl_url_path="/path/to/")
  429. lines = tmpl.substitute(opts)
  430. for n in opts:
  431. self.assertTrue(lines.find(opts[n])>0)
  432. tmpl = self.stats_httpd.open_template(stats_httpd.XSD_TEMPLATE_LOCATION)
  433. self.assertTrue(isinstance(tmpl, string.Template))
  434. opts = dict(
  435. xsd_string="<dummy></dummy>",
  436. xsd_namespace="http://host/path/to/")
  437. lines = tmpl.substitute(opts)
  438. for n in opts:
  439. self.assertTrue(lines.find(opts[n])>0)
  440. tmpl = self.stats_httpd.open_template(stats_httpd.XSL_TEMPLATE_LOCATION)
  441. self.assertTrue(isinstance(tmpl, string.Template))
  442. opts = dict(
  443. xsl_string="<dummy></dummy>",
  444. xsd_namespace="http://host/path/to/")
  445. lines = tmpl.substitute(opts)
  446. for n in opts:
  447. self.assertTrue(lines.find(opts[n])>0)
  448. # unsuccessful condition
  449. self.assertRaises(
  450. IOError,
  451. self.stats_httpd.open_template, '/path/to/foo/bar')
  452. def test_commands(self):
  453. self.assertEqual(self.stats_httpd.command_handler("status", None),
  454. isc.config.ccsession.create_answer(
  455. 0, "Stats Httpd is up. (PID " + str(os.getpid()) + ")"))
  456. self.stats_httpd.running = True
  457. self.assertEqual(self.stats_httpd.command_handler("shutdown", None),
  458. isc.config.ccsession.create_answer(
  459. 0, "Stats Httpd is shutting down."))
  460. self.assertFalse(self.stats_httpd.running)
  461. self.assertEqual(
  462. self.stats_httpd.command_handler("__UNKNOWN_COMMAND__", None),
  463. isc.config.ccsession.create_answer(
  464. 1, "Unknown command: __UNKNOWN_COMMAND__"))
  465. def test_config(self):
  466. self.assertEqual(
  467. self.stats_httpd.config_handler(dict(_UNKNOWN_KEY_=None)),
  468. isc.config.ccsession.create_answer(
  469. 1, "Unknown known config: _UNKNOWN_KEY_"))
  470. self.assertEqual(
  471. self.stats_httpd.config_handler(
  472. dict(listen_on=[dict(address="127.0.0.2",port=8000)])),
  473. isc.config.ccsession.create_answer(0))
  474. self.assertTrue("listen_on" in self.stats_httpd.config)
  475. for addr in self.stats_httpd.config["listen_on"]:
  476. self.assertTrue("address" in addr)
  477. self.assertTrue("port" in addr)
  478. self.assertTrue(addr["address"] == "127.0.0.2")
  479. self.assertTrue(addr["port"] == 8000)
  480. if self.ipv6_enabled:
  481. self.assertEqual(
  482. self.stats_httpd.config_handler(
  483. dict(listen_on=[dict(address="::1",port=8000)])),
  484. isc.config.ccsession.create_answer(0))
  485. self.assertTrue("listen_on" in self.stats_httpd.config)
  486. for addr in self.stats_httpd.config["listen_on"]:
  487. self.assertTrue("address" in addr)
  488. self.assertTrue("port" in addr)
  489. self.assertTrue(addr["address"] == "::1")
  490. self.assertTrue(addr["port"] == 8000)
  491. self.assertEqual(
  492. self.stats_httpd.config_handler(
  493. dict(listen_on=[dict(address="127.0.0.1",port=54321)])),
  494. isc.config.ccsession.create_answer(0))
  495. self.assertTrue("listen_on" in self.stats_httpd.config)
  496. for addr in self.stats_httpd.config["listen_on"]:
  497. self.assertTrue("address" in addr)
  498. self.assertTrue("port" in addr)
  499. self.assertTrue(addr["address"] == "127.0.0.1")
  500. self.assertTrue(addr["port"] == 54321)
  501. (ret, arg) = isc.config.ccsession.parse_answer(
  502. self.stats_httpd.config_handler(
  503. dict(listen_on=[dict(address="1.2.3.4",port=543210)]))
  504. )
  505. self.assertEqual(ret, 1)
  506. def test_xml_handler(self):
  507. orig_get_stats_data = stats_httpd.StatsHttpd.get_stats_data
  508. stats_httpd.StatsHttpd.get_stats_data = lambda x: {'foo':'bar'}
  509. xml_body1 = stats_httpd.StatsHttpd().open_template(
  510. stats_httpd.XML_TEMPLATE_LOCATION).substitute(
  511. xml_string='<foo>bar</foo>',
  512. xsd_namespace=stats_httpd.XSD_NAMESPACE,
  513. xsd_url_path=stats_httpd.XSD_URL_PATH,
  514. xsl_url_path=stats_httpd.XSL_URL_PATH)
  515. xml_body2 = stats_httpd.StatsHttpd().xml_handler()
  516. self.assertEqual(type(xml_body1), str)
  517. self.assertEqual(type(xml_body2), str)
  518. self.assertEqual(xml_body1, xml_body2)
  519. stats_httpd.StatsHttpd.get_stats_data = lambda x: {'bar':'foo'}
  520. xml_body2 = stats_httpd.StatsHttpd().xml_handler()
  521. self.assertNotEqual(xml_body1, xml_body2)
  522. stats_httpd.StatsHttpd.get_stats_data = orig_get_stats_data
  523. def test_xsd_handler(self):
  524. orig_get_stats_spec = stats_httpd.StatsHttpd.get_stats_spec
  525. stats_httpd.StatsHttpd.get_stats_spec = lambda x: \
  526. [{
  527. "item_name": "foo",
  528. "item_type": "string",
  529. "item_optional": False,
  530. "item_default": "bar",
  531. "item_description": "foo is bar",
  532. "item_title": "Foo"
  533. }]
  534. xsd_body1 = stats_httpd.StatsHttpd().open_template(
  535. stats_httpd.XSD_TEMPLATE_LOCATION).substitute(
  536. xsd_string='<all>' \
  537. + '<element maxOccurs="1" minOccurs="1" name="foo" type="string">' \
  538. + '<annotation><appinfo>Foo</appinfo>' \
  539. + '<documentation>foo is bar</documentation>' \
  540. + '</annotation></element></all>',
  541. xsd_namespace=stats_httpd.XSD_NAMESPACE)
  542. xsd_body2 = stats_httpd.StatsHttpd().xsd_handler()
  543. self.assertEqual(type(xsd_body1), str)
  544. self.assertEqual(type(xsd_body2), str)
  545. self.assertEqual(xsd_body1, xsd_body2)
  546. stats_httpd.StatsHttpd.get_stats_spec = lambda x: \
  547. [{
  548. "item_name": "bar",
  549. "item_type": "string",
  550. "item_optional": False,
  551. "item_default": "foo",
  552. "item_description": "bar is foo",
  553. "item_title": "bar"
  554. }]
  555. xsd_body2 = stats_httpd.StatsHttpd().xsd_handler()
  556. self.assertNotEqual(xsd_body1, xsd_body2)
  557. stats_httpd.StatsHttpd.get_stats_spec = orig_get_stats_spec
  558. def test_xsl_handler(self):
  559. orig_get_stats_spec = stats_httpd.StatsHttpd.get_stats_spec
  560. stats_httpd.StatsHttpd.get_stats_spec = lambda x: \
  561. [{
  562. "item_name": "foo",
  563. "item_type": "string",
  564. "item_optional": False,
  565. "item_default": "bar",
  566. "item_description": "foo is bar",
  567. "item_title": "Foo"
  568. }]
  569. xsl_body1 = stats_httpd.StatsHttpd().open_template(
  570. stats_httpd.XSL_TEMPLATE_LOCATION).substitute(
  571. xsl_string='<xsl:template match="*"><tr>' \
  572. + '<td class="title" title="foo is bar">Foo</td>' \
  573. + '<td><xsl:value-of select="foo" /></td>' \
  574. + '</tr></xsl:template>',
  575. xsd_namespace=stats_httpd.XSD_NAMESPACE)
  576. xsl_body2 = stats_httpd.StatsHttpd().xsl_handler()
  577. self.assertEqual(type(xsl_body1), str)
  578. self.assertEqual(type(xsl_body2), str)
  579. self.assertEqual(xsl_body1, xsl_body2)
  580. stats_httpd.StatsHttpd.get_stats_spec = lambda x: \
  581. [{
  582. "item_name": "bar",
  583. "item_type": "string",
  584. "item_optional": False,
  585. "item_default": "foo",
  586. "item_description": "bar is foo",
  587. "item_title": "bar"
  588. }]
  589. xsl_body2 = stats_httpd.StatsHttpd().xsl_handler()
  590. self.assertNotEqual(xsl_body1, xsl_body2)
  591. stats_httpd.StatsHttpd.get_stats_spec = orig_get_stats_spec
  592. def test_for_without_B10_FROM_SOURCE(self):
  593. # just lets it go through the code without B10_FROM_SOURCE env
  594. # variable
  595. if "B10_FROM_SOURCE" in os.environ:
  596. tmppath = os.environ["B10_FROM_SOURCE"]
  597. os.environ.pop("B10_FROM_SOURCE")
  598. imp.reload(stats_httpd)
  599. os.environ["B10_FROM_SOURCE"] = tmppath
  600. imp.reload(stats_httpd)
  601. if __name__ == "__main__":
  602. unittest.main()