base.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. _NOW = 1151365354
  2. _TIMEFORMAT = '%b %d %I:%M %p'
  3. class DummyOptions:
  4. TRACE = 5
  5. make_pipes_error = None
  6. fork_error = None
  7. execv_error = None
  8. kill_error = None
  9. minfds = 5
  10. def __init__(self):
  11. self.identifier = 'supervisor'
  12. self.childlogdir = '/tmp'
  13. self.uid = 999
  14. self.logger = self.getLogger()
  15. self.backofflimit = 10
  16. self.logfile = '/tmp/logfile'
  17. self.nocleanup = False
  18. self.strip_ansi = False
  19. self.pidhistory = {}
  20. self.process_group_configs = []
  21. self.nodaemon = False
  22. self.socket_map = {}
  23. self.mood = 1
  24. self.mustreopen = False
  25. self.realizeargs = None
  26. self.fds_cleaned_up = False
  27. self.rlimit_set = False
  28. self.setuid_called = False
  29. self.httpserver_opened = False
  30. self.signals_set = False
  31. self.daemonized = False
  32. self.make_logger_messages = None
  33. self.autochildlogdir_cleared = False
  34. self.cleaned_up = False
  35. self.pidfile_written = False
  36. self.directory = None
  37. self.waitpid_return = None, None
  38. self.kills = {}
  39. self.signal = None
  40. self.parent_pipes_closed = None
  41. self.child_pipes_closed = None
  42. self.forkpid = 0
  43. self.pgrp_set = None
  44. self.duped = {}
  45. self.written = {}
  46. self.fds_closed = []
  47. self._exitcode = None
  48. self.execv_args = None
  49. self.setuid_msg = None
  50. self.privsdropped = None
  51. self.logs_reopened = False
  52. self.environment_processed = False
  53. self.select_result = [], [], []
  54. self.select_error = None
  55. self.write_accept = None
  56. self.write_error = None
  57. self.tempfile_name = '/foo/bar'
  58. self.remove_error = None
  59. self.removed = []
  60. self.existing = []
  61. self.openreturn = None
  62. self.readfd_result = ''
  63. def getLogger(self, *args, **kw):
  64. logger = DummyLogger()
  65. logger.handlers = [DummyLogger()]
  66. logger.args = args, kw
  67. return logger
  68. def realize(self, args):
  69. self.realizeargs = args
  70. def cleanup_fds(self):
  71. self.fds_cleaned_up = True
  72. def set_rlimits(self):
  73. self.rlimits_set = True
  74. return ['rlimits_set']
  75. def set_uid(self):
  76. self.setuid_called = True
  77. return 'setuid_called'
  78. def openhttpserver(self, supervisord):
  79. self.httpserver_opened = True
  80. def daemonize(self):
  81. self.daemonized = True
  82. def setsignals(self):
  83. self.signals_set = True
  84. def get_socket_map(self):
  85. return self.socket_map
  86. def make_logger(self, critical_msgs, info_msgs):
  87. self.make_logger_messages = critical_msgs, info_msgs
  88. def clear_autochildlogdir(self):
  89. self.autochildlogdir_cleared = True
  90. def cleanup(self):
  91. self.cleaned_up = True
  92. def write_pidfile(self):
  93. self.pidfile_written = True
  94. def waitpid(self):
  95. return self.waitpid_return
  96. def make_process(self, config):
  97. return DummyProcess(config)
  98. def make_group(self, config):
  99. return DummyProcessGroup(config)
  100. def kill(self, pid, sig):
  101. if self.kill_error:
  102. raise OSError(self.kill_error)
  103. self.kills[pid] = sig
  104. def stat(self, filename):
  105. import os
  106. return os.stat(filename)
  107. def get_path(self):
  108. return ["/bin", "/usr/bin", "/usr/local/bin"]
  109. def check_execv_args(self, filename, argv, st):
  110. if filename == '/bad/filename':
  111. from supervisor.options import NotFound
  112. raise NotFound('bad filename')
  113. def make_pipes(self, stderr=True):
  114. if self.make_pipes_error:
  115. raise OSError(self.make_pipes_error)
  116. pipes = {}
  117. pipes['child_stdin'], pipes['stdin'] = (3, 4)
  118. pipes['stdout'], pipes['child_stdout'] = (5, 6)
  119. if stderr:
  120. pipes['stderr'], pipes['child_stderr'] = (7, 8)
  121. else:
  122. pipes['stderr'], pipes['child_stderr'] = None, None
  123. from supervisor.options import dictreverse
  124. return pipes, dictreverse(pipes)
  125. def write(self, fd, chars):
  126. if self.write_error:
  127. raise OSError(self.write_error)
  128. if self.write_accept:
  129. chars = chars[self.write_accept]
  130. data = self.written.setdefault(fd, '')
  131. data += chars
  132. self.written[fd] = data
  133. return len(chars)
  134. def fork(self):
  135. if self.fork_error:
  136. raise OSError(self.fork_error)
  137. return self.forkpid
  138. def close_fd(self, fd):
  139. self.fds_closed.append(fd)
  140. def close_parent_pipes(self, pipes):
  141. self.parent_pipes_closed = pipes
  142. def close_child_pipes(self, pipes):
  143. self.child_pipes_closed = pipes
  144. def setpgrp(self):
  145. self.pgrp_set = True
  146. def dup2(self, frm, to):
  147. self.duped[frm] = to
  148. def _exit(self, code):
  149. self._exitcode = code
  150. def execve(self, filename, argv, environment):
  151. if self.execv_error:
  152. if self.execv_error == 1:
  153. raise OSError(self.execv_error)
  154. else:
  155. raise RuntimeError(self.execv_error)
  156. self.execv_args = (filename, argv)
  157. self.execv_environment = environment
  158. def dropPrivileges(self, uid):
  159. if self.setuid_msg:
  160. return self.setuid_msg
  161. self.privsdropped = uid
  162. def readfd(self, fd):
  163. return self.readfd_result
  164. def reopenlogs(self):
  165. self.logs_reopened = True
  166. def process_environment(self):
  167. self.environment_processed = True
  168. def stripEscapes(self, data):
  169. from supervisor.options import ServerOptions
  170. o = ServerOptions()
  171. return o.stripEscapes(data)
  172. def mktempfile(self, prefix, suffix, dir):
  173. return self.tempfile_name
  174. def select(self, r, w, x, timeout):
  175. import select
  176. if self.select_error:
  177. raise select.error(self.select_error)
  178. return self.select_result
  179. def remove(self, path):
  180. import os
  181. if self.remove_error:
  182. raise os.error(self.remove_error)
  183. self.removed.append(path)
  184. def exists(self, path):
  185. if path in self.existing:
  186. return True
  187. return False
  188. def open(self, name, mode='r'):
  189. if self.openreturn:
  190. return self.openreturn
  191. return open(name, mode)
  192. class DummyLogger:
  193. def __init__(self):
  194. self.reopened = False
  195. self.removed = False
  196. self.closed = False
  197. self.data = []
  198. def info(self, *args):
  199. for arg in args:
  200. self.data.append(arg)
  201. warn = log = debug = critical = trace = info
  202. def reopen(self):
  203. self.reopened = True
  204. def close(self):
  205. self.closed = True
  206. def remove(self):
  207. self.removed = True
  208. def flush(self):
  209. self.flushed = True
  210. class DummySupervisor:
  211. def __init__(self, options=None, state=None, process_groups=None):
  212. if options is None:
  213. self.options = DummyOptions()
  214. else:
  215. self.options = options
  216. if state is None:
  217. from supervisor.supervisord import SupervisorStates
  218. self.state = SupervisorStates.ACTIVE
  219. else:
  220. self.state = state
  221. if process_groups is None:
  222. self.process_groups = {}
  223. else:
  224. self.process_groups = process_groups
  225. def get_state(self):
  226. return self.state
  227. class DummyProcess:
  228. # Initial state; overridden by instance variables
  229. pid = 0 # Subprocess pid; 0 when not running
  230. laststart = 0 # Last time the subprocess was started; 0 if never
  231. laststop = 0 # Last time the subprocess was stopped; 0 if never
  232. delay = 0 # If nonzero, delay starting or killing until this time
  233. administrative_stop = 0 # true if the process has been stopped by an admin
  234. system_stop = 0 # true if the process has been stopped by the system
  235. killing = 0 # flag determining whether we are trying to kill this proc
  236. backoff = 0 # backoff counter (to backofflimit)
  237. waitstatus = None
  238. exitstatus = None
  239. pipes = None
  240. rpipes = None
  241. dispatchers = None
  242. stdout_logged = ''
  243. stderr_logged = ''
  244. spawnerr = None
  245. stdout_buffer = '' # buffer of characters from child stdout output to log
  246. stderr_buffer = '' # buffer of characters from child stderr output to log
  247. stdin_buffer = '' # buffer of characters to send to child process' stdin
  248. def __init__(self, config, state=None):
  249. self.config = config
  250. self.logsremoved = False
  251. self.stop_called = False
  252. self.backoff_secs = None
  253. self.spawned = False
  254. if state is None:
  255. from supervisor.process import ProcessStates
  256. state = ProcessStates.RUNNING
  257. self.state = state
  258. self.error_at_clear = False
  259. self.killed_with = None
  260. self.drained = False
  261. self.stdout_buffer = ''
  262. self.stderr_buffer = ''
  263. self.stdout_logged = ''
  264. self.stderr_logged = ''
  265. self.pipes = {}
  266. self.rpipes = {}
  267. self.dispatchers = {}
  268. self.finished = None
  269. self.logs_reopened = False
  270. self.execv_arg_exception = None
  271. self.input_fd_drained = None
  272. self.output_fd_drained = None
  273. def reopenlogs(self):
  274. self.logs_reopened = True
  275. def removelogs(self):
  276. if self.error_at_clear:
  277. raise IOError('whatever')
  278. self.logsremoved = True
  279. def get_state(self):
  280. return self.state
  281. def stop(self):
  282. self.stop_called = True
  283. self.killing = False
  284. from supervisor.process import ProcessStates
  285. self.state = ProcessStates.STOPPED
  286. def kill(self, signal):
  287. self.killed_with = signal
  288. def spawn(self):
  289. self.spawned = True
  290. from supervisor.process import ProcessStates
  291. self.state = ProcessStates.RUNNING
  292. def drain(self):
  293. self.drained = True
  294. def __cmp__(self, other):
  295. return cmp(self.config.priority, other.config.priority)
  296. def readable_fds(self):
  297. return []
  298. def record_output(self):
  299. self.stdout_logged += self.stdout_buffer
  300. self.stdout_buffer = ''
  301. self.stderr_logged += self.stderr_buffer
  302. self.stderr_buffer = ''
  303. def finish(self, pid, sts):
  304. self.finished = pid, sts
  305. def get_execv_args(self):
  306. if self.execv_arg_exception:
  307. raise self.execv_arg_exception('whatever')
  308. import shlex
  309. commandargs = shlex.split(self.config.command)
  310. program = commandargs[0]
  311. return program, commandargs
  312. def drain_output_fd(self, fd):
  313. self.output_fd_drained = fd
  314. def drain_input_fd(self, fd):
  315. self.input_fd_drained = fd
  316. class DummyPConfig:
  317. def __init__(self, options, name, command, priority=999, autostart=True,
  318. autorestart=True, startsecs=10, startretries=999,
  319. uid=None, stdout_logfile=None, stdout_capturefile=None,
  320. stdout_logfile_backups=0, stdout_logfile_maxbytes=0,
  321. stderr_logfile=None, stderr_capturefile=None,
  322. stderr_logfile_backups=0, stderr_logfile_maxbytes=0,
  323. redirect_stderr=False,
  324. stopsignal=None, stopwaitsecs=10,
  325. exitcodes=(0,2), environment=None):
  326. self.options = options
  327. self.name = name
  328. self.command = command
  329. self.priority = priority
  330. self.autostart = autostart
  331. self.autorestart = autorestart
  332. self.startsecs = startsecs
  333. self.startretries = startretries
  334. self.uid = uid
  335. self.stdout_logfile = stdout_logfile
  336. self.stdout_capturefile = stdout_capturefile
  337. self.stdout_logfile_backups = stdout_logfile_backups
  338. self.stdout_logfile_maxbytes = stdout_logfile_maxbytes
  339. self.stderr_logfile = stderr_logfile
  340. self.stderr_capturefile = stderr_capturefile
  341. self.stderr_logfile_backups = stderr_logfile_backups
  342. self.stderr_logfile_maxbytes = stderr_logfile_maxbytes
  343. self.redirect_stderr = redirect_stderr
  344. if stopsignal is None:
  345. import signal
  346. stopsignal = signal.SIGTERM
  347. self.stopsignal = stopsignal
  348. self.stopwaitsecs = stopwaitsecs
  349. self.exitcodes = exitcodes
  350. self.environment = environment
  351. self.autochildlogs_created = False
  352. def create_autochildlogs(self):
  353. self.autochildlogs_created = True
  354. def makeExecutable(file, substitutions=None):
  355. import os
  356. import sys
  357. import tempfile
  358. if substitutions is None:
  359. substitutions = {}
  360. data = open(file).read()
  361. last = os.path.split(file)[1]
  362. substitutions['PYTHON'] = sys.executable
  363. for key in substitutions.keys():
  364. data = data.replace('<<%s>>' % key.upper(), substitutions[key])
  365. tmpnam = tempfile.mktemp(prefix=last)
  366. f = open(tmpnam, 'w')
  367. f.write(data)
  368. f.close()
  369. os.chmod(tmpnam, 0755)
  370. return tmpnam
  371. def makeSpew(unkillable=False):
  372. import os
  373. here = os.path.dirname(__file__)
  374. if not unkillable:
  375. return makeExecutable(os.path.join(here, 'fixtures/spew.py'))
  376. return makeExecutable(os.path.join(here, 'fixtures/unkillable_spew.py'))
  377. class DummyRequest:
  378. command = 'GET'
  379. _error = None
  380. _done = False
  381. def __init__(self, path, params, query, fragment):
  382. self.args = path, params, query, fragment
  383. self.producers = []
  384. self.headers = {}
  385. def split_uri(self):
  386. return self.args
  387. def error(self, code):
  388. self._error = code
  389. def push(self, producer):
  390. self.producers.append(producer)
  391. def __setitem__(self, header, value):
  392. self.headers[header] = value
  393. def done(self):
  394. self._done = True
  395. class DummyRPCServer:
  396. def __init__(self):
  397. self.supervisor = DummySupervisorRPCNamespace()
  398. self.system = DummySystemRPCNamespace()
  399. class DummySystemRPCNamespace:
  400. pass
  401. class DummySupervisorRPCNamespace:
  402. _restartable = True
  403. _restarted = False
  404. _shutdown = False
  405. def getAPIVersion(self):
  406. return '1.0'
  407. def readProcessLog(self, name, offset, length):
  408. from supervisor import xmlrpc
  409. import xmlrpclib
  410. if name == 'BAD_NAME':
  411. raise xmlrpclib.Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
  412. elif name == 'FAILED':
  413. raise xmlrpclib.Fault(xmlrpc.Faults.FAILED, 'FAILED')
  414. elif name == 'NO_FILE':
  415. raise xmlrpclib.Fault(xmlrpc.Faults.NO_FILE, 'NO_FILE')
  416. a = 'output line\n' * 10
  417. return a[offset:]
  418. def getAllProcessInfo(self):
  419. from supervisor.process import ProcessStates
  420. return [
  421. {
  422. 'name':'foo',
  423. 'pid':11,
  424. 'state':ProcessStates.RUNNING,
  425. 'statename':'RUNNING',
  426. 'start':_NOW - 100,
  427. 'stop':0,
  428. 'spawnerr':'',
  429. 'now':_NOW,
  430. 'description':'foo description',
  431. },
  432. {
  433. 'name':'bar',
  434. 'pid':12,
  435. 'state':ProcessStates.FATAL,
  436. 'statename':'FATAL',
  437. 'start':_NOW - 100,
  438. 'stop':_NOW - 50,
  439. 'spawnerr':'screwed',
  440. 'now':_NOW,
  441. 'description':'bar description',
  442. },
  443. {
  444. 'name':'baz',
  445. 'pid':12,
  446. 'state':ProcessStates.STOPPED,
  447. 'statename':'STOPPED',
  448. 'start':_NOW - 100,
  449. 'stop':_NOW - 25,
  450. 'spawnerr':'',
  451. 'now':_NOW,
  452. 'description':'baz description',
  453. },
  454. ]
  455. def getProcessInfo(self, name):
  456. from supervisor.process import ProcessStates
  457. return {
  458. 'name':'foo',
  459. 'pid':11,
  460. 'state':ProcessStates.RUNNING,
  461. 'statename':'RUNNING',
  462. 'start':_NOW - 100,
  463. 'stop':0,
  464. 'spawnerr':'',
  465. 'now':_NOW,
  466. 'description':'foo description',
  467. }
  468. def startProcess(self, name):
  469. from supervisor import xmlrpc
  470. from xmlrpclib import Fault
  471. if name == 'BAD_NAME':
  472. raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
  473. if name == 'ALREADY_STARTED':
  474. raise Fault(xmlrpc.Faults.ALREADY_STARTED, 'ALREADY_STARTED')
  475. if name == 'SPAWN_ERROR':
  476. raise Fault(xmlrpc.Faults.SPAWN_ERROR, 'SPAWN_ERROR')
  477. return True
  478. def startAllProcesses(self):
  479. from supervisor import xmlrpc
  480. return [
  481. {'name':'foo', 'status': xmlrpc.Faults.SUCCESS,'description': 'OK'},
  482. {'name':'foo2', 'status':xmlrpc.Faults.SUCCESS,'description': 'OK'},
  483. {'name':'failed', 'status':xmlrpc.Faults.SPAWN_ERROR,
  484. 'description':'SPAWN_ERROR'}
  485. ]
  486. def stopProcess(self, name):
  487. from supervisor import xmlrpc
  488. from xmlrpclib import Fault
  489. if name == 'BAD_NAME':
  490. raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
  491. if name == 'NOT_RUNNING':
  492. raise Fault(xmlrpc.Faults.NOT_RUNNING, 'NOT_RUNNING')
  493. if name == 'FAILED':
  494. raise Fault(xmlrpc.Faults.FAILED, 'FAILED')
  495. return True
  496. def stopAllProcesses(self):
  497. from supervisor import xmlrpc
  498. return [
  499. {'name':'foo','status': xmlrpc.Faults.SUCCESS,'description': 'OK'},
  500. {'name':'foo2', 'status':xmlrpc.Faults.SUCCESS,'description': 'OK'},
  501. {'name':'failed', 'status':xmlrpc.Faults.BAD_NAME,
  502. 'description':'FAILED'}
  503. ]
  504. def restart(self):
  505. if self._restartable:
  506. self._restarted = True
  507. return
  508. from xmlrpclib import Fault
  509. from supervisor import xmlrpc
  510. raise Fault(xmlrpc.Faults.SHUTDOWN_STATE, '')
  511. def shutdown(self):
  512. if self._restartable:
  513. self._shutdown = True
  514. return
  515. from xmlrpclib import Fault
  516. from supervisor import xmlrpc
  517. raise Fault(xmlrpc.Faults.SHUTDOWN_STATE, '')
  518. def clearProcessLog(self, name):
  519. from xmlrpclib import Fault
  520. from supervisor import xmlrpc
  521. if name == 'BAD_NAME':
  522. raise Fault(xmlrpc.Faults.BAD_NAME, 'BAD_NAME')
  523. return True
  524. def clearAllProcessLogs(self):
  525. from supervisor import xmlrpc
  526. return [
  527. {'name':'foo', 'status':xmlrpc.Faults.SUCCESS,'description': 'OK'},
  528. {'name':'foo2', 'status':xmlrpc.Faults.SUCCESS,'description': 'OK'},
  529. {'name':'failed','status':xmlrpc.Faults.FAILED,
  530. 'description':'FAILED'}
  531. ]
  532. def raiseError(self):
  533. raise ValueError('error')
  534. def getSupervisorVersion(self):
  535. return '3000'
  536. class DummyPGroupConfig:
  537. def __init__(self, options, name='whatever', priority=999, pconfigs=None):
  538. self.options = options
  539. self.name = name
  540. self.priority = priority
  541. if pconfigs is None:
  542. pconfigs = []
  543. self.process_configs = pconfigs
  544. self.after_setuid_called = False
  545. def after_setuid(self):
  546. self.after_setuid_called = True
  547. class DummyProcessGroup:
  548. def __init__(self, config):
  549. self.config = config
  550. self.necessary_started = False
  551. self.transitioned = False
  552. self.all_stopped = False
  553. self.delay_processes = []
  554. self.dispatchers = {}
  555. def start_necessary(self):
  556. self.necessary_started = True
  557. def select(self):
  558. return self.select_result
  559. def transition(self):
  560. self.transitioned = True
  561. def stop_all(self):
  562. self.all_stopped = True
  563. def get_delay_processes(self):
  564. return self.delay_processes
  565. def get_dispatchers(self):
  566. return self.dispatchers
  567. class PopulatedDummySupervisor(DummySupervisor):
  568. def __init__(self, options, group_name, *pconfigs):
  569. DummySupervisor.__init__(self, options)
  570. self.process_groups = {}
  571. processes = {}
  572. self.group_name = group_name
  573. gconfig = DummyPGroupConfig(options, group_name, pconfigs=pconfigs)
  574. pgroup = DummyProcessGroup(gconfig)
  575. self.process_groups[group_name] = pgroup
  576. for pconfig in pconfigs:
  577. process = DummyProcess(pconfig)
  578. processes[pconfig.name] = process
  579. pgroup.processes = processes
  580. def set_procattr(self, process_name, attr_name, val, group_name=None):
  581. if group_name is None:
  582. group_name = self.group_name
  583. process = self.process_groups[group_name].processes[process_name]
  584. setattr(process, attr_name, val)
  585. class DummyRecorder:
  586. def __init__(self):
  587. self.output_buffer = ''
  588. class DummyDispatcher:
  589. write_event_handled = False
  590. read_event_handled = False
  591. error_handled = False
  592. def __init__(self, readable=False, writable=False, error=False):
  593. self._readable = readable
  594. self._writable = writable
  595. self._error = error
  596. def readable(self):
  597. return self._readable
  598. def writable(self):
  599. return self._writable
  600. def handle_write_event(self):
  601. if self._error:
  602. raise self._error
  603. self.write_event_handled = True
  604. def handle_read_event(self):
  605. if self._error:
  606. raise self._error
  607. self.read_event_handled = True
  608. def handle_error(self):
  609. self.error_handled = True
  610. def lstrip(s):
  611. strings = [x.strip() for x in s.split('\n')]
  612. return '\n'.join(strings)