'restart' ad-hoc command implementation

darcs-hash:20070907181337-86b55-09ec29feccd7d0ea27ea0a3a324eebc89a932aa4.gz
This commit is contained in:
David Rousselie
2007-09-07 20:13:37 +02:00
parent d59220a082
commit f285836f92
6 changed files with 207 additions and 20 deletions

View File

@@ -24,6 +24,7 @@
import re import re
import datetime import datetime
import logging import logging
import threading
from sqlobject.sqlbuilder import AND from sqlobject.sqlbuilder import AND
@@ -885,8 +886,48 @@ class JCLCommandManager(CommandManager):
command_node.setProp("status", STATUS_COMPLETED) command_node.setProp("status", STATUS_COMPLETED)
return (None, []) return (None, [])
def execute_restart(self, info_query): def execute_restart_1(self, info_query, session_context,
return [] command_node, lang_class):
self.add_actions(command_node, [ACTION_NEXT])
result_form = Form(xmlnode_or_type="result",
title="TODO",
instructions="TODO")
result_form.add_field(field_type="hidden",
name="FORM_TYPE",
value="http://jabber.org/protocol/admin")
result_form.add_field(name="delay",
field_type="list-multi",
label="TODO",
required=True)
result_form.add_field(name="announcement",
field_type="text-multi",
label="TODO")
result_form.as_xml(command_node)
return (result_form, [])
def execute_restart_2(self, info_query, session_context,
command_node, lang_class):
self.__logger.debug("Executing command 'restart' step 2")
announcement = session_context["announcement"][0]
delay = int(session_context["delay"][0])
if announcement is not None and announcement != "":
users = account.get_all_users(\
filter=AND(Account.q.userID == User.q.id,
Account.q._status != account.OFFLINE),
distinct=True)
result = []
for user in users:
result.append(Message(from_jid=self.component.jid,
to_jid=user.jid,
body=announcement))
command_node.setProp("status", STATUS_COMPLETED)
def delayed_restart(self, delay):
threading.Event().wait(delay)
self.component.restart = True
restart_thread = threading.Thread(target=lambda : delayed_restart(self, delay),
name="TimerThread")
restart_thread.start()
return (None, result)
def execute_shutdown(self, info_query): def execute_shutdown(self, info_query):
return [] return []

View File

@@ -133,6 +133,7 @@ class JCLComponent(Component, object):
self.lang = lang self.lang = lang
self.running = False self.running = False
self.wait_event = threading.Event() self.wait_event = threading.Event()
self._restart = False
signal.signal(signal.SIGINT, self.signal_handler) signal.signal(signal.SIGINT, self.signal_handler)
signal.signal(signal.SIGTERM, self.signal_handler) signal.signal(signal.SIGTERM, self.signal_handler)
@@ -168,6 +169,16 @@ class JCLComponent(Component, object):
timer_thread.join(JCLComponent.timeout) timer_thread.join(JCLComponent.timeout)
self.disconnect() self.disconnect()
self.__logger.debug("Exitting normally") self.__logger.debug("Exitting normally")
return self._restart
def _get_restart(self):
return self._restart
def _set_restart(self, __restart):
self.running = not __restart
self._restart = __restart
restart = property(_get_restart, _set_restart)
########################################################################### ###########################################################################
# Handlers # Handlers
@@ -183,7 +194,7 @@ class JCLComponent(Component, object):
and self.stream.socket is not None): and self.stream.socket is not None):
self.wait_event.wait(self.time_unit) self.wait_event.wait(self.time_unit)
self.handle_tick() self.handle_tick()
self.__logger.debug("Resetting alarm signal") self.__logger.debug(".")
except Exception, exception: except Exception, exception:
type, value, stack = sys.exc_info() type, value, stack = sys.exc_info()
self.__logger.error("Error in timer thread\n%s\n%s" self.__logger.error("Error in timer thread\n%s\n%s"

View File

@@ -24,6 +24,7 @@ import unittest
import os import os
import tempfile import tempfile
from ConfigParser import ConfigParser from ConfigParser import ConfigParser
import threading
from pyxmpp.jid import JID from pyxmpp.jid import JID
from pyxmpp.presence import Presence from pyxmpp.presence import Presence
@@ -2813,14 +2814,104 @@ class JCLCommandManager_TestCase(JCLTestCase):
self.assertEquals(self.comp.config.get("component", "admins"), self.assertEquals(self.comp.config.get("component", "admins"),
"admin3@test.com,admin4@test.com") "admin3@test.com,admin4@test.com")
# def test_execute_restart(self): def test_execute_restart(self):
# #TODO : implement command self.comp.account_manager.account_classes = (ExampleAccount,
# info_query = Iq(stanza_type="set", Example2Account)
# from_jid="admin@test.com", self.comp.running = True
# to_jid="jcl.test.com") model.db_connect()
# result = self.command_manager.execute_add_user(info_query) user1 = User(jid="test1@test.com")
# self.assertNotEquals(result, None) account11 = ExampleAccount(user=user1,
# self.assertEquals(len(result), 1) name="account11",
jid="account11@jcl.test.com")
account11.status = account.ONLINE
account12 = Example2Account(user=user1,
name="account12",
jid="account12@jcl.test.com")
account12.status = "away"
user2 = User(jid="test2@test.com")
account21 = ExampleAccount(user=user2,
name="account21",
jid="account21@jcl.test.com")
account22 = ExampleAccount(user=user2,
name="account11",
jid="account11@jcl.test.com")
account22.status = "xa"
model.db_disconnect()
info_query = Iq(stanza_type="set",
from_jid="admin@test.com",
to_jid="jcl.test.com")
command_node = info_query.set_new_content(command.COMMAND_NS, "command")
command_node.setProp("node",
"http://jabber.org/protocol/admin#restart")
result = self.command_manager.apply_command_action(\
info_query,
"http://jabber.org/protocol/admin#restart",
"execute")
self.assertNotEquals(result, None)
self.assertEquals(len(result), 1)
xml_command = result[0].xpath_eval("c:command",
{"c": "http://jabber.org/protocol/commands"})[0]
self.assertEquals(xml_command.prop("status"), "executing")
self.assertNotEquals(xml_command.prop("sessionid"), None)
self.__check_actions(result[0], ["next"])
fields = result[0].xpath_eval("c:command/data:x/data:field",
{"c": "http://jabber.org/protocol/commands",
"data": "jabber:x:data"})
self.assertEquals(len(fields), 3)
self.assertEquals(fields[1].prop("var"), "delay")
self.assertEquals(fields[1].prop("type"), "list-multi")
self.assertEquals(fields[2].prop("var"), "announcement")
self.assertEquals(fields[2].prop("type"), "text-multi")
# Second step
info_query = Iq(stanza_type="set",
from_jid="admin@test.com",
to_jid="jcl.test.com")
command_node = info_query.set_new_content(command.COMMAND_NS, "command")
command_node.setProp("node",
"http://jabber.org/protocol/admin#restart")
session_id = xml_command.prop("sessionid")
command_node.setProp("sessionid", session_id)
command_node.setProp("action", "next")
submit_form = Form(xmlnode_or_type="submit")
submit_form.add_field(field_type="list-multi",
name="delay",
value=[0])
submit_form.add_field(field_type="text-multi",
name="announcement",
value=["service will be restarted in 0 second"])
submit_form.as_xml(command_node)
result = self.command_manager.apply_command_action(\
info_query,
"http://jabber.org/protocol/admin#restart",
"execute")
self.assertNotEquals(result, None)
self.assertEquals(len(result), 3)
xml_command = result[0].xpath_eval("c:command",
{"c": "http://jabber.org/protocol/commands"})[0]
self.assertEquals(xml_command.prop("status"), "completed")
self.assertEquals(xml_command.prop("sessionid"), session_id)
self.__check_actions(result[0])
context_session = self.command_manager.sessions[session_id][1]
self.assertEquals(context_session["announcement"],
["service will be restarted in 0 second"])
self.assertEquals(context_session["delay"],
["0"])
self.assertEquals(result[1].get_from(), "jcl.test.com")
self.assertEquals(result[1].get_to(), "test1@test.com")
self.assertEquals(result[1].get_body(), "service will be restarted in 0 second")
self.assertEquals(result[2].get_from(), "jcl.test.com")
self.assertEquals(result[2].get_to(), "test2@test.com")
self.assertEquals(result[2].get_body(), "service will be restarted in 0 second")
self.assertFalse(self.comp.restart)
self.assertTrue(self.comp.running)
threads = threading.enumerate()
self.assertEquals(len(threads), 2)
threading.Event().wait(1)
threads = threading.enumerate()
self.assertEquals(len(threads), 1)
self.assertTrue(self.comp.restart)
self.assertFalse(self.comp.running)
# def test_execute_shutdown(self): # def test_execute_shutdown(self):
# #TODO : implement command # #TODO : implement command
@@ -2832,11 +2923,11 @@ class JCLCommandManager_TestCase(JCLTestCase):
# self.assertEquals(len(result), 1) # self.assertEquals(len(result), 1)
def suite(): def suite():
suite = unittest.TestSuite() test_suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(CommandManager_TestCase, 'test')) test_suite.addTest(unittest.makeSuite(CommandManager_TestCase, 'test'))
suite.addTest(unittest.makeSuite(FieldNoType_TestCase, 'test')) test_suite.addTest(unittest.makeSuite(FieldNoType_TestCase, 'test'))
suite.addTest(unittest.makeSuite(JCLCommandManager_TestCase, 'test')) test_suite.addTest(unittest.makeSuite(JCLCommandManager_TestCase, 'test'))
return suite return test_suite
if __name__ == '__main__': if __name__ == '__main__':
unittest.main(defaultTest='suite') unittest.main(defaultTest='suite')

View File

@@ -242,7 +242,25 @@ class JCLComponent_TestCase(JCLTestCase):
# Tests in subclasses might be more precise # Tests in subclasses might be more precise
self.comp.stream = MockStreamNoConnect() self.comp.stream = MockStreamNoConnect()
self.comp.stream_class = MockStreamNoConnect self.comp.stream_class = MockStreamNoConnect
self.comp.run() result = self.comp.run()
self.assertFalse(result)
self.assertTrue(self.comp.stream.connection_started)
threads = threading.enumerate()
self.assertEquals(len(threads), 1)
self.assertTrue(self.comp.stream.connection_stopped)
if self.comp.queue.qsize():
raise self.comp.queue.get(0)
def test_run_restart(self):
"""Test main loop execution with restart"""
self.comp.time_unit = 1
# Do not loop, handle_tick is virtual
# Tests in subclasses might be more precise
self.comp.stream = MockStreamNoConnect()
self.comp.stream_class = MockStreamNoConnect
self.comp.restart = True
result = self.comp.run()
self.assertTrue(result)
self.assertTrue(self.comp.stream.connection_started) self.assertTrue(self.comp.stream.connection_started)
threads = threading.enumerate() threads = threading.enumerate()
self.assertEquals(len(threads), 1) self.assertEquals(len(threads), 1)
@@ -258,7 +276,6 @@ class JCLComponent_TestCase(JCLTestCase):
self.comp.stream = MockStreamRaiseException() self.comp.stream = MockStreamRaiseException()
self.comp.stream_class = MockStreamRaiseException self.comp.stream_class = MockStreamRaiseException
self.comp.handle_tick = do_nothing self.comp.handle_tick = do_nothing
try: try:
self.comp.run() self.comp.run()
except Exception, e: except Exception, e:

View File

@@ -194,7 +194,9 @@ class JCLRunner(object):
model.db_disconnect() model.db_disconnect()
self.logger.debug(self.component_name + " v" + self.logger.debug(self.component_name + " v" +
self.component_version + " is starting ...") self.component_version + " is starting ...")
run_func() restart = True
while restart:
restart = run_func()
self.logger.debug(self.component_name + " is exiting") self.logger.debug(self.component_name + " is exiting")
finally: finally:
if os.path.exists(self.pid_file): if os.path.exists(self.pid_file):
@@ -209,6 +211,6 @@ class JCLRunner(object):
lang=Lang(self.language), lang=Lang(self.language),
config=self.config, config=self.config,
config_file=self.config_file) config_file=self.config_file)
component.run() return component.run()
self._run(run_func) self._run(run_func)

View File

@@ -137,6 +137,31 @@ class JCLRunner_TestCase(unittest.TestCase):
os.unlink(db_path) os.unlink(db_path)
self.assertFalse(os.access("/tmp/jcl.pid", os.F_OK)) self.assertFalse(os.access("/tmp/jcl.pid", os.F_OK))
def test__run_restart(self):
self.runner.pid_file = "/tmp/jcl.pid"
db_path = tempfile.mktemp("db", "jcltest", DB_DIR)
db_url = "sqlite://" + db_path
self.runner.db_url = db_url
self.i = 0
def restart(self):
self.i += 1
yield True
self.i += 1
yield False
self.i += 1
restart_generator = restart(self)
self.runner._run(lambda : restart_generator.next())
model.db_connect()
# dropTable should succeed because tables should exist
Account.dropTable()
PresenceAccount.dropTable()
User.dropTable()
LegacyJID.dropTable()
model.db_disconnect()
os.unlink(db_path)
self.assertFalse(os.access("/tmp/jcl.pid", os.F_OK))
self.assertEquals(self.i, 2)
def test__get_help(self): def test__get_help(self):
self.assertNotEquals(self.runner._get_help(), None) self.assertNotEquals(self.runner._get_help(), None)