Skip to content

Commit

Permalink
CP-51658: install script to allow stopping all in-progress GC operations
Browse files Browse the repository at this point in the history
Signed-off-by: Mark Syms <[email protected]>
  • Loading branch information
MarkSymsCtx committed Oct 3, 2024
1 parent 130615c commit 1eceeb7
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 2 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ install: precheck
install -m 755 scripts/usb_change $(SM_STAGING)$(LIBEXEC)
install -m 755 scripts/kickpipe $(SM_STAGING)$(LIBEXEC)
install -m 755 scripts/set-iscsi-initiator $(SM_STAGING)$(LIBEXEC)
mkdir -p $(SM_STAGING)/etc/xapi.d/xapi-pre-shutdown/
install -m 755 scripts/stop_all_gc $(SM_STAGING)/etc/xapi.d/xapi-pre-shutdown/
$(MAKE) -C dcopy install DESTDIR=$(SM_STAGING)
ln -sf $(SM_DEST)blktap2.py $(SM_STAGING)$(BIN_DEST)/blktap2
ln -sf $(SM_DEST)lcache.py $(SM_STAGING)$(BIN_DEST)tapdisk-cache-stats
Expand Down
20 changes: 19 additions & 1 deletion drivers/cleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,22 @@

NON_PERSISTENT_DIR = '/run/nonpersistent/sm'

# Signal Handler
SIGTERM = False


class AbortException(util.SMException):
pass


def receiveSignal(signalNumber, frame):
global SIGTERM

util.SMlog("GC: recieved SIGTERM")
SIGTERM = True
return


################################################################################
#
# Util
Expand Down Expand Up @@ -167,7 +178,7 @@ def runAbortable(func, ret, ns, abortTest, pollInterval, timeOut):
if resultFlag.test("failure"):
resultFlag.clear("failure")
raise util.SMException("Child process exited with error")
if abortTest() or abortSignaled:
if abortTest() or abortSignaled or SIGTERM:
os.killpg(pid, signal.SIGKILL)
raise AbortException("Aborting due to signal")
if timeOut and _time() - startTime > timeOut:
Expand Down Expand Up @@ -2966,6 +2977,10 @@ def _gcLoop(sr, dryRun=False, immediate=False):
"Garbage collection for SR %s" % sr.uuid)
_gcLoopPause(sr, dryRun, immediate=immediate)
while True:
if SIGTERM:
Util.log("Term requested")
return

if not sr.xapi.isPluggedHere():
Util.log("SR no longer attached, exiting")
break
Expand Down Expand Up @@ -3191,6 +3206,9 @@ def gc(session, srUuid, inBackground, dryRun=False):
6. If there is something to coalesce, coalesce one pair, then goto 3
"""
Util.log("=== SR %s: gc ===" % srUuid)

signal.signal(signal.SIGTERM, receiveSignal)

if inBackground:
if daemonize():
# we are now running in the background. Catch & log any errors
Expand Down
1 change: 1 addition & 0 deletions mk/sm.spec.in
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ tests/run_python_unittests.sh
/etc/xapi.d/plugins/testing-hooks
/etc/xapi.d/plugins/intellicache-clean
/etc/xapi.d/plugins/trim
/etc/xapi.d/xapi-pre-shutdown/*
/etc/xensource/master.d/02-vhdcleanup
/opt/xensource/bin/blktap2
/opt/xensource/bin/tapdisk-cache-stats
Expand Down
7 changes: 7 additions & 0 deletions scripts/stop_all_gc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

/usr/bin/systemctl list-units SMGC@* --all --no-legend | /usr/bin/cut -d ' ' -f1 | sed 's/\\/\\\\/g' | while read service;
do
echo "Stopping $service"
/usr/bin/systemctl stop "$service"
done
27 changes: 26 additions & 1 deletion tests/test_cleanup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import errno
import signal
import unittest
import unittest.mock as mock

Expand Down Expand Up @@ -40,7 +41,8 @@ def acquireNoblock(self):


class TestRelease(object):
pass
def acquireNoblock(self):
return True


class IrrelevantLock(object):
Expand Down Expand Up @@ -72,6 +74,9 @@ def setUp(self):

self.addCleanup(mock.patch.stopall)

def tearDown(self):
cleanup.SIGTERM = False

def setup_abort_flag(self, ipc_mock, should_abort=False):
flag = mock.Mock()
flag.test = mock.Mock(return_value=should_abort)
Expand All @@ -89,6 +94,26 @@ def mock_cleanup_locks(self):
cleanup.lockGCRunning = TestRelease()
cleanup.lockGCRunning.release = mock.Mock(return_value=None)

def test_term_handler(self):
self.assertFalse(cleanup.SIGTERM)

cleanup.receiveSignal(signal.SIGTERM, None)

self.assertTrue(cleanup.SIGTERM)

@mock.patch('cleanup._create_init_file', autospec=True)
@mock.patch('cleanup.SR', autospec=True)
@mock.patch('cleanup._ensure_xapi_initialised', autospec=True)
def test_loop_exits_on_term(self, mock_init, mock_sr, mock_check_xapi):
# Set the term signel
cleanup.receiveSignal(signal.SIGTERM, None)
mock_session = mock.MagicMock(name='MockSession')
sr_uuid = str(uuid4())
self.mock_cleanup_locks()

# Trigger GC
cleanup.gc(mock_session, sr_uuid, inBackground=False)

def test_lock_if_already_locked(self):
"""
Given an already locked SR, a lock call
Expand Down

0 comments on commit 1eceeb7

Please sign in to comment.