From 42a3ba71fd6bc103175b45c1ed42c902693cc3ae Mon Sep 17 00:00:00 2001 From: catborise Date: Thu, 5 Nov 2026 10:52:16 +0300 Subject: [PATCH] fix(instances): a gstfsd host that is down is told in 5 seconds gstfsd_request connected with the 120 seconds it waits for the change: a host that was down held the request's thread two minutes before its page said so, a wait no host bound counts. It now connects within libvirt's CONNECT_TIMEOUT and gives the change its 120 seconds after. --- instances/tests/test_gstfsd.py | 19 +++++++++++++++++++ instances/views.py | 5 ++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/instances/tests/test_gstfsd.py b/instances/tests/test_gstfsd.py index 0620c93f..5180ac3e 100644 --- a/instances/tests/test_gstfsd.py +++ b/instances/tests/test_gstfsd.py @@ -610,6 +610,25 @@ class GstfsdClientTestCase(unittest.TestCase): with self.subTest(name), self.assertRaises(OperationError): self.ask(*sent) + def test_a_host_that_does_not_answer_is_told_in_the_connect_timeout(self): + from unittest.mock import MagicMock, patch + + from instances import views + from vrtManager.connection import CONNECT_TIMEOUT + from vrtManager.util import OperationError + + with patch("instances.views.socket.create_connection", side_effect=TimeoutError("timed out")) as connect: + with self.assertRaisesRegex(OperationError, "gstfsd error: timed out"): + views.gstfsd_request(SimpleNamespace(name="host-a", hostname="192.0.2.1", gstfsd_key="ab" * 32), {}) + self.assertEqual(connect.call_args.kwargs["timeout"], CONNECT_TIMEOUT) + # the change itself, once connected, has its own, longer limit + connected = MagicMock() + connected.makefile.return_value.readline.side_effect = OSError("closed") + with patch("instances.views.socket.create_connection", return_value=connected), \ + self.assertRaises(OperationError): + views.gstfsd_request(SimpleNamespace(name="host-a", hostname="192.0.2.1", gstfsd_key="ab" * 32), {}) + connected.settimeout.assert_called_once_with(views.GSTFSD_TIMEOUT) + def test_a_host_without_a_key_is_not_asked(self): from unittest.mock import patch diff --git a/instances/views.py b/instances/views.py index c9d0d702..f4b19c64 100755 --- a/instances/views.py +++ b/instances/views.py @@ -54,6 +54,7 @@ from operations import claims, jobs from operations.utils import busy_message, summary from webvirtcloud.middleware import error_text, same_origin_referer from vrtManager import util +from vrtManager.connection import CONNECT_TIMEOUT from vrtManager.create import cpu_choices, wvmCreate from vrtManager.instance import USAGE_MAX_WINDOW, wvmInstances from vrtManager.interface import wvmInterface @@ -1026,10 +1027,12 @@ def gstfsd_request(compute, data): "compute": compute.name}) try: # the host name may resolve to IPv4 or IPv6 - s = socket.create_connection((compute.hostname, GSTFSD_PORT), timeout=GSTFSD_TIMEOUT) + # a host that is down is told in seconds, as libvirt's; the change itself may take GSTFSD_TIMEOUT + s = socket.create_connection((compute.hostname, GSTFSD_PORT), timeout=CONNECT_TIMEOUT) except OSError as err: raise util.OperationError(_("gstfsd error: %(err)s") % {"err": err}) from err try: + s.settimeout(GSTFSD_TIMEOUT) stream = s.makefile("rb") challenge = json.loads(stream.readline(GSTFSD_MAX_FRAME))["challenge"] nonce, request = secrets.token_hex(32), json.dumps(data)