Allow graceful exiting with SIGINT and SIGTERM

The test_loop function was only gracefully interrupted with a keyboard
interrupt before. This is fine for interactive testing, but in an
automated scenario the script may be running in the background and need
to receive a signal from some other process, such as Ansible.

This patch adds a handler for SIGINT and SIGTERM that will allow the
current iteration of the loop to finish, then exit the program cleanly
by putting the test_loop function into a class. The test_loop function
is not refactored other than to add the `stop_now` flag, though the
internal variables may be amenable to later refactoring to use the
class.

Change-Id: I0667d2c7093e2821afdfcea987cec6b58515885f
This commit is contained in:
Nolan Brubaker 2017-05-22 11:35:11 -04:00
parent 56ac318f21
commit 325076bef6

View File

@ -25,6 +25,7 @@ from keystoneauth1.exceptions.http import InternalServerError
from keystoneclient.v3 import client as key_client from keystoneclient.v3 import client as key_client
import logging import logging
import os import os
import signal
import sys import sys
import time import time
from glanceclient import Client from glanceclient import Client
@ -145,7 +146,7 @@ class KeystoneTest(ServiceTest):
# the test_loop function # the test_loop function
try: try:
keystone.projects.list() keystone.projects.list()
msg = "Project lest retrieved" msg = "Project list retrieved"
except InternalServerError: except InternalServerError:
msg = "Failed to get project list" msg = "Failed to get project list"
return msg return msg
@ -196,23 +197,38 @@ class GlanceTest(ServiceTest):
return glance_endpoint.url return glance_endpoint.url
def test_loop(test): class TestRunner(object):
"""Main loop to execute tests """Run a test in a loop, with the option to gracefully exit"""
stop_now = False
Executes and times interactions with OpenStack services to gather timing def __init__(self):
data. signal.signal(signal.SIGINT, self.prep_exit)
:param: test - on object that performs some action signal.signal(signal.SIGTERM, self.prep_exit)
against an OpenStack service API.
""" def prep_exit(self, signum, frame):
disconnected = None self.stop_now = True
# Has to be a tuple for python syntax reasons. logger.info("Received signal, stopping")
# This is currently the set needed for glance; should probably
# provide some way of letting a test say which exceptions should def test_loop(self, test):
# be caught for a service. """Main loop to execute tests
exc_list = (ConnectFailure, InternalServerError, BadGateway,
glance_exc.CommunicationError, Executes and times interactions with OpenStack services to gather
glance_exc.HTTPInternalServerError) timing data.
try:
Execution can be ended by sending SIGINT or SIGTERM and the running
test will finish.
:param: test - on object that performs some action
against an OpenStack service API.
"""
disconnected = None
# Has to be a tuple for python syntax reasons.
# This is currently the set needed for glance; should probably
# provide some way of letting a test say which exceptions should
# be caught for a service.
exc_list = (ConnectFailure, InternalServerError, BadGateway,
glance_exc.CommunicationError,
glance_exc.HTTPInternalServerError)
while True: while True:
try: try:
# Pause for a bit so we're not generating more data than we # Pause for a bit so we're not generating more data than we
@ -249,8 +265,9 @@ def test_loop(test):
except (exc_list): except (exc_list):
if not disconnected: if not disconnected:
disconnected = datetime.datetime.now() disconnected = datetime.datetime.now()
except KeyboardInterrupt:
sys.exit() if self.stop_now:
sys.exit()
available_tests = { available_tests = {
@ -294,4 +311,6 @@ if __name__ == "__main__":
target_test = target_test_class() target_test = target_test_class()
target_test.configure_logger(logger) target_test.configure_logger(logger)
test_loop(target_test) runner = TestRunner()
runner.test_loop(target_test)