Skip to content

Commit bf1310f

Browse files
author
xianglongfei_uniontech
committed
Fixed the issue:Supports specifying port ranges.
Signed-off-by: xianglongfei_uniontech <xianglongfei@uniontech.com>
1 parent 74b7379 commit bf1310f

3 files changed

Lines changed: 78 additions & 51 deletions

File tree

avocado/core/status/server.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,41 @@
11
import asyncio
22
import os
33

4+
from avocado.core.output import LOG_JOB
45
from avocado.core.settings import settings
6+
from avocado.utils.network import ports as network_ports
7+
8+
9+
def resolve_listen_uri(uri):
10+
"""
11+
Normalize a status server URI that may contain a port range into
12+
a concrete "host:port" endpoint.
13+
"""
14+
if ":" not in uri:
15+
return uri
16+
host, port_spec = uri.rsplit(":", 1)
17+
if "-" not in port_spec:
18+
return uri
19+
20+
start_s, end_s = port_spec.split("-", 1)
21+
start = int(start_s)
22+
end = int(end_s)
23+
if start > end:
24+
raise ValueError(
25+
f"Invalid port range (start > end) in status server URI: {uri}"
26+
)
27+
28+
port = network_ports.find_free_port(
29+
start_port=start,
30+
end_port=end,
31+
address=host,
32+
sequent=True,
33+
)
34+
if port is None:
35+
raise OSError(
36+
f"Could not bind status server to any port in range {start}-{end} on {host}"
37+
)
38+
return f"{host}:{port}"
539

640

741
class StatusServer:
@@ -16,7 +50,7 @@ def __init__(self, uri, repo):
1650
messages
1751
:type repo: :class:`avocado.core.status.repo.StatusRepo`
1852
"""
19-
self._uri = uri
53+
self._uri = resolve_listen_uri(uri)
2054
self._repo = repo
2155
self._server_task = None
2256

@@ -27,7 +61,7 @@ def uri(self):
2761
async def create_server(self):
2862
limit = settings.as_dict().get("run.status_server_buffer_size")
2963
if ":" in self._uri:
30-
host, port = self._uri.split(":")
64+
host, port = self._uri.rsplit(":", 1)
3165
port = int(port)
3266
self._server_task = await asyncio.start_server(
3367
self.cb, host=host, port=port, limit=limit
@@ -36,6 +70,7 @@ async def create_server(self):
3670
self._server_task = await asyncio.start_unix_server(
3771
self.cb, path=self._uri, limit=limit
3872
)
73+
LOG_JOB.info("Status server listening on %s", self._uri)
3974

4075
async def serve_forever(self):
4176
if self._server_task is None:

avocado/plugins/runner_nrunner.py

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@
1818

1919
import asyncio
2020
import multiprocessing
21-
import os
22-
import platform
2321
import random
24-
import tempfile
2522

2623
from avocado.core.dispatcher import SpawnerDispatcher
2724
from avocado.core.exceptions import JobError, JobFailFast
@@ -31,11 +28,12 @@
3128
from avocado.core.plugin_interfaces import CLI, Init, SuiteRunner
3229
from avocado.core.settings import settings
3330
from avocado.core.status.repo import StatusRepo
34-
from avocado.core.status.server import StatusServer
31+
from avocado.core.status.server import StatusServer, resolve_listen_uri
3532
from avocado.core.task.runtime import RuntimeTaskGraph
3633
from avocado.core.task.statemachine import TaskStateMachine, Worker
3734

38-
DEFAULT_SERVER_URI = "127.0.0.1:8888"
35+
# Default port range so multiple avocado runs can bind without conflict
36+
DEFAULT_SERVER_URI = "127.0.0.1:8888-9000"
3937

4038

4139
class RunnerInit(Init):
@@ -55,10 +53,9 @@ def initialize(self):
5553
)
5654

5755
help_msg = (
58-
"If the status server should automatically choose "
59-
'a "status_server_listen" and "status_server_uri" '
60-
"configuration. Default is to auto configure a "
61-
"status server."
56+
"If the status server should automatically choose a listen address "
57+
"from the default port range so multiple runs do not conflict. "
58+
"When disabled, use status_server_listen/status_server_uri."
6259
)
6360
settings.register_option(
6461
section=section,
@@ -69,10 +66,10 @@ def initialize(self):
6966
)
7067

7168
help_msg = (
72-
'URI where status server will listen on. Usually a "HOST:PORT" '
73-
'string. This is only effective if "status_server_auto" is disabled. '
74-
'If "status_server_uri" is not set, the value from "status_server_listen " '
75-
"will be used."
69+
'URI where status server will listen. "HOST:PORT" or "HOST:START-END" '
70+
"port range (default: 127.0.0.1:8888-9000). Only used when "
71+
'"status_server_auto" is disabled. If "status_server_uri" is not set, '
72+
'"status_server_listen" is used.'
7673
)
7774
settings.register_option(
7875
section=section,
@@ -83,12 +80,10 @@ def initialize(self):
8380
)
8481

8582
help_msg = (
86-
"URI for connecting to the status server, usually "
87-
'a "HOST:PORT" string. Use this if your status server '
88-
"is in another host, or different port. This is only "
89-
'effective if "status_server_auto" is disabled. '
90-
'If "status_server_listen" is not set, the value from "status_server_uri" '
91-
"will be used."
83+
'URI for connecting to the status server: "HOST:PORT" or "HOST:START-END" '
84+
'port range (default: 127.0.0.1:8888-9000). Only used when "status_server_auto" '
85+
'is disabled. If "status_server_listen" is not set, '
86+
'"status_server_uri" is used.'
9287
)
9388
settings.register_option(
9489
section=section,
@@ -207,19 +202,8 @@ class Runner(SuiteRunner):
207202
name = "nrunner"
208203
description = "nrunner based implementation of job compliant runner"
209204

210-
def __init__(self):
211-
super().__init__()
212-
self.status_server_dir = None
213-
214205
def _determine_status_server(self, test_suite, config_key):
215-
if test_suite.config.get("run.status_server_auto"):
216-
# no UNIX domain sockets on Windows
217-
if platform.system() != "Windows":
218-
if self.status_server_dir is None:
219-
self.status_server_dir = tempfile.TemporaryDirectory(
220-
prefix="avocado_"
221-
)
222-
return os.path.join(self.status_server_dir.name, ".status_server.sock")
206+
"""Return listen/uri config; default is a port range so multiple runs work."""
223207
return test_suite.config.get(config_key)
224208

225209
def _sync_status_server_urls(self, config):
@@ -240,10 +224,19 @@ def _sync_status_server_urls(self, config):
240224
def _create_status_server(self, test_suite, job):
241225
self._sync_status_server_urls(test_suite.config)
242226
listen = self._determine_status_server(test_suite, "run.status_server_listen")
227+
try:
228+
resolved_listen = resolve_listen_uri(listen)
229+
except (ValueError, OSError) as exc:
230+
raise JobError(str(exc)) from exc
231+
if resolved_listen != listen:
232+
test_suite.config["run.status_server_listen"] = resolved_listen
233+
server_uri = test_suite.config.get("run.status_server_uri")
234+
if server_uri in (listen, DEFAULT_SERVER_URI):
235+
test_suite.config["run.status_server_uri"] = resolved_listen
243236
# pylint: disable=W0201
244237
self.status_repo = StatusRepo(job.unique_id)
245238
# pylint: disable=W0201
246-
self.status_server = StatusServer(listen, self.status_repo)
239+
self.status_server = StatusServer(resolved_listen, self.status_repo)
247240

248241
async def _update_status(self, job):
249242
message_handler = MessageHandler()
@@ -384,8 +377,6 @@ def run_suite(self, job, test_suite):
384377

385378
job.result.end_tests()
386379
self.status_server.close()
387-
if self.status_server_dir is not None:
388-
self.status_server_dir.cleanup()
389380

390381
# Update the overall summary with found test statuses, which will
391382
# determine the Avocado command line exit status

man/avocado.rst

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -179,23 +179,24 @@ Options for subcommand `run` (`avocado run --help`)::
179179
nrunner specific options:
180180
--shuffle Shuffle the tasks to be executed
181181
--status-server-disable-auto
182-
If the status server should automatically choose a
183-
"status_server_listen" and "status_server_uri"
184-
configuration. Default is to auto configure a status
185-
server.
182+
Disable automatic status server port selection. By
183+
default, a port range (127.0.0.1:8888-9000) is used
184+
so multiple avocado runs can run without port
185+
conflicts. When disabled, use
186+
--status-server-listen/--status-server-uri.
186187
--status-server-listen HOST_PORT
187-
URI where status server will listen on. Usually a
188-
"HOST:PORT" string. This is only effective if
189-
"status_server_auto" is disabled. If
190-
"status_server_uri" is not set, the value from
191-
"status_server_listen " will be used.
188+
URI where status server will listen: "HOST:PORT" or
189+
"HOST:START-END" (default 127.0.0.1:8888-9000). An
190+
available port in the range is chosen. Only used
191+
when --status-server-disable-auto is set. If
192+
"status_server_uri" is not set,
193+
"status_server_listen" is used.
192194
--status-server-uri HOST_PORT
193-
URI for connecting to the status server, usually a
194-
"HOST:PORT" string. Use this if your status server is
195-
in another host, or different port. This is only
196-
effective if "status_server_auto" is disabled. If
197-
"status_server_listen" is not set, the value from
198-
"status_server_uri" will be used.
195+
URI for connecting to the status server: "HOST:PORT"
196+
or "HOST:START-END". Only used when
197+
--status-server-disable-auto is set. If
198+
"status_server_listen" is not set,
199+
"status_server_uri" is used.
199200
--max-parallel-tasks NUMBER_OF_TASKS
200201
Number of maximum number tasks running in parallel.
201202
You can disable parallel execution by setting this to

0 commit comments

Comments
 (0)