You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

253 lines
8.8 KiB

  1. # -*- coding: utf-8 -*-
  2. import nilmrun.server
  3. from nilmdb.client.httpclient import HTTPClient, ClientError, ServerError
  4. from nilmdb.utils.printf import *
  5. from nose.plugins.skip import SkipTest
  6. from nose.tools import *
  7. from nose.tools import assert_raises
  8. import itertools
  9. import distutils.version
  10. import os
  11. import sys
  12. import threading
  13. import cStringIO
  14. import simplejson as json
  15. import unittest
  16. import warnings
  17. import time
  18. import re
  19. import urllib2
  20. from urllib2 import urlopen, HTTPError
  21. import requests
  22. import pprint
  23. from testutil.helpers import *
  24. testurl = "http://localhost:32181/"
  25. def setup_module():
  26. global test_server
  27. # Start web app on a custom port
  28. test_server = nilmrun.server.Server(host = "127.0.0.1",
  29. port = 32181,
  30. force_traceback = True)
  31. test_server.start(blocking = False)
  32. def teardown_module():
  33. global test_server
  34. # Close web app
  35. test_server.stop()
  36. class TestClient(object):
  37. def wait_end(self, client, pid, timeout = 5):
  38. start = time.time()
  39. status = None
  40. while (time.time() - start) < timeout:
  41. status = client.get("/process/status", { "pid": pid })
  42. if status["alive"] == False:
  43. return status
  44. raise AssertionError("process " + str(pid) + " didn't die in " +
  45. str(timeout) + " seconds: " + repr(status))
  46. def test_client_01_basic(self):
  47. client = HTTPClient(baseurl = testurl)
  48. version = client.get("/version")
  49. eq_(distutils.version.LooseVersion(version),
  50. distutils.version.LooseVersion(nilmrun.__version__))
  51. in_("This is NilmRun", client.get("/"))
  52. with assert_raises(ClientError):
  53. client.get("/favicon.ico")
  54. def test_client_02_manager(self):
  55. client = HTTPClient(baseurl = testurl)
  56. eq_(client.get("/process/list"), [])
  57. with assert_raises(ClientError) as e:
  58. client.get("/process/status", { "pid": 12345 })
  59. in_("No such PID", str(e.exception))
  60. with assert_raises(ClientError):
  61. client.get("/process/remove", { "pid": 12345 })
  62. in_("No such PID", str(e.exception))
  63. def test_client_03_process_basic(self):
  64. client = HTTPClient(baseurl = testurl, post_json = True)
  65. # start dummy filter
  66. pid = client.post("/run/dummy", { "data": 30 })
  67. eq_(client.get("/process/list"), [pid])
  68. time.sleep(1)
  69. # Verify that status looks OK
  70. status = client.get("/process/status", { "pid": pid, "clear": True })
  71. for x in [ "pid", "alive", "exitcode", "name",
  72. "start_time", "parameters", "log" ]:
  73. in_(x, status)
  74. in_("dummy 0\ndummy 1\ndummy 2\ndummy 3\n", status["log"])
  75. eq_(status["alive"], True)
  76. eq_(status["exitcode"], None)
  77. # Check that the log got cleared
  78. status = client.get("/process/status", { "pid": pid })
  79. nin_("dummy 0\ndummy 1\ndummy 2\ndummy 3\n", status["log"])
  80. # See that it ended properly
  81. status = self.wait_end(client, pid)
  82. in_("dummy 27\ndummy 28\ndummy 29\n", status["log"])
  83. eq_(status["exitcode"], 0)
  84. # Remove it
  85. killstatus = client.post("/process/remove", { "pid": pid })
  86. eq_(status, killstatus)
  87. eq_(client.get("/process/list"), [])
  88. with assert_raises(ClientError) as e:
  89. client.post("/process/remove", { "pid": pid })
  90. in_("No such PID", str(e.exception))
  91. def test_client_04_process_terminate(self):
  92. client = HTTPClient(baseurl = testurl, post_json = True)
  93. # Trigger exception in filter
  94. pid = client.post("/run/dummy", { "data": -1 })
  95. time.sleep(0.5)
  96. status = client.get("/process/status", { "pid": pid })
  97. eq_(status["alive"], False)
  98. eq_(status["exitcode"], 1)
  99. in_("Exception: test exception", status["log"])
  100. client.post("/process/remove", { "pid": pid })
  101. # Kill a running filter by removing it early
  102. newpid = client.post("/run/dummy", { "data": 50 })
  103. ne_(newpid, pid)
  104. time.sleep(0.5)
  105. start = time.time()
  106. status = client.post("/process/remove", { "pid": newpid })
  107. elapsed = time.time() - start
  108. # Should have died in slightly over 1 second
  109. assert(0.5 < elapsed < 2)
  110. eq_(status["alive"], False)
  111. ne_(status["exitcode"], 0)
  112. # No more
  113. eq_(client.get("/process/list"), [])
  114. # Try to remove a running filter that ignored SIGTERM
  115. pid = client.post("/run/dummy", { "data": 0 })
  116. start = time.time()
  117. status = client.post("/process/remove", { "pid": pid })
  118. elapsed = time.time() - start
  119. # Should have died in slightly over 2 seconds
  120. assert(1.5 < elapsed < 3)
  121. eq_(status["alive"], False)
  122. ne_(status["exitcode"], 0)
  123. def test_client_05_trainola_simple(self):
  124. client = HTTPClient(baseurl = testurl, post_json = True)
  125. pid = client.post("/run/trainola", { "data": {} })
  126. status = self.wait_end(client, pid)
  127. ne_(status["exitcode"], 0)
  128. status = client.post("/process/remove", { "pid": pid })
  129. @unittest.skip("needs a running nilmdb")
  130. def test_client_06_trainola(self):
  131. client = HTTPClient(baseurl = testurl, post_json = True)
  132. data = { "url": "http://bucket.mit.edu/nilmdb",
  133. "stream": "/sharon/prep-a",
  134. "start": 1366111383280463,
  135. "end": 1366126163457797,
  136. "columns": [ { "name": "P1", "index": 0 },
  137. { "name": "Q1", "index": 1 },
  138. { "name": "P3", "index": 2 } ],
  139. "exemplars": [
  140. { "name": "Boiler Pump ON",
  141. "url": "http://bucket.mit.edu/nilmdb",
  142. "stream": "/sharon/prep-a",
  143. "start": 1366260494269078,
  144. "end": 1366260608185031,
  145. "columns": [ { "name": "P1", "index": 0 },
  146. { "name": "Q1", "index": 1 }
  147. ]
  148. },
  149. { "name": "Boiler Pump OFF",
  150. "url": "http://bucket.mit.edu/nilmdb",
  151. "stream": "/sharon/prep-a",
  152. "start": 1366260864215764,
  153. "end": 1366260870882998,
  154. "columns": [ { "name": "P1", "index": 0 },
  155. { "name": "Q1", "index": 1 }
  156. ]
  157. }
  158. ]
  159. }
  160. # start trainola
  161. pid = client.post("/run/trainola", { "data": data })
  162. # wait for it to finish
  163. for i in range(60):
  164. time.sleep(1)
  165. if i == 2:
  166. status = client.get("/process/status", { "pid": pid,
  167. "clear": True })
  168. in_("Loading stream data", status['log'])
  169. elif i == 3:
  170. status = client.get("/process/status", { "pid": pid })
  171. nin_("Loading stream data", status['log'])
  172. else:
  173. status = client.get("/process/status", { "pid": pid })
  174. if status["alive"] == False:
  175. break
  176. else:
  177. client.post("/process/remove", {"pid": pid })
  178. raise AssertionError("took too long")
  179. if i < 3:
  180. raise AssertionError("too fast?")
  181. def test_client_07_process_command(self):
  182. client = HTTPClient(baseurl = testurl, post_json = True)
  183. eq_(client.get("/process/list"), [])
  184. def do(args, kill):
  185. pid = client.post("/run/command", { "args": args } )
  186. eq_(client.get("/process/list"), [pid])
  187. if kill:
  188. time.sleep(1)
  189. status = client.get("/process/status", { "pid": pid })
  190. if not status["alive"]:
  191. raise AssertionError("died before we could kill it")
  192. status = client.post("/process/remove", { "pid": pid })
  193. if status["alive"]:
  194. raise AssertionError("didn't get killed")
  195. else:
  196. self.wait_end(client, pid)
  197. status = client.post("/process/remove", { "pid": pid })
  198. return status
  199. # Simple command
  200. status = do(["pwd"], False)
  201. eq_(status["exitcode"], 0)
  202. eq_("/tmp\n", status["log"])
  203. # Command with args
  204. status = do(["expr", "1", "+", "2"], False)
  205. eq_(status["exitcode"], 0)
  206. eq_("3\n", status["log"])
  207. # Missing command
  208. status = do(["/no-such-command-blah-blah"], False)
  209. ne_(status["exitcode"], 0)
  210. # Kill a slow command
  211. status = do(["sleep", "60"], True)
  212. ne_(status["exitcode"], 0)