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,11 +197,27 @@ class GlanceTest(ServiceTest):
return glance_endpoint.url return glance_endpoint.url
def test_loop(test): class TestRunner(object):
"""Run a test in a loop, with the option to gracefully exit"""
stop_now = False
def __init__(self):
signal.signal(signal.SIGINT, self.prep_exit)
signal.signal(signal.SIGTERM, self.prep_exit)
def prep_exit(self, signum, frame):
self.stop_now = True
logger.info("Received signal, stopping")
def test_loop(self, test):
"""Main loop to execute tests """Main loop to execute tests
Executes and times interactions with OpenStack services to gather timing Executes and times interactions with OpenStack services to gather
data. timing data.
Execution can be ended by sending SIGINT or SIGTERM and the running
test will finish.
:param: test - on object that performs some action :param: test - on object that performs some action
against an OpenStack service API. against an OpenStack service API.
""" """
@ -212,7 +229,6 @@ def test_loop(test):
exc_list = (ConnectFailure, InternalServerError, BadGateway, exc_list = (ConnectFailure, InternalServerError, BadGateway,
glance_exc.CommunicationError, glance_exc.CommunicationError,
glance_exc.HTTPInternalServerError) glance_exc.HTTPInternalServerError)
try:
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,7 +265,8 @@ 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:
if self.stop_now:
sys.exit() sys.exit()
@ -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)