qlever 0.2.5__py3-none-any.whl → 0.5.41__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qlever/Qleverfiles/Qleverfile.dblp +36 -0
- qlever/Qleverfiles/Qleverfile.dblp-plus +33 -0
- qlever/Qleverfiles/Qleverfile.dbpedia +30 -0
- qlever/Qleverfiles/Qleverfile.default +51 -0
- qlever/Qleverfiles/Qleverfile.dnb +40 -0
- qlever/Qleverfiles/Qleverfile.fbeasy +29 -0
- qlever/Qleverfiles/Qleverfile.freebase +28 -0
- qlever/Qleverfiles/Qleverfile.imdb +36 -0
- qlever/Qleverfiles/Qleverfile.ohm-planet +41 -0
- qlever/Qleverfiles/Qleverfile.olympics +31 -0
- qlever/Qleverfiles/Qleverfile.orkg +30 -0
- qlever/Qleverfiles/Qleverfile.osm-country +39 -0
- qlever/Qleverfiles/Qleverfile.osm-planet +39 -0
- qlever/Qleverfiles/Qleverfile.osm-planet-from-pbf +42 -0
- qlever/Qleverfiles/Qleverfile.pubchem +131 -0
- qlever/Qleverfiles/Qleverfile.scientists +29 -0
- qlever/Qleverfiles/Qleverfile.uniprot +74 -0
- qlever/Qleverfiles/Qleverfile.vvz +31 -0
- qlever/Qleverfiles/Qleverfile.wikidata +42 -0
- qlever/Qleverfiles/Qleverfile.wikipathways +40 -0
- qlever/Qleverfiles/Qleverfile.yago-4 +33 -0
- qlever/__init__.py +44 -1380
- qlever/command.py +87 -0
- qlever/commands/__init__.py +0 -0
- qlever/commands/add_text_index.py +115 -0
- qlever/commands/benchmark_queries.py +1019 -0
- qlever/commands/cache_stats.py +125 -0
- qlever/commands/clear_cache.py +88 -0
- qlever/commands/extract_queries.py +120 -0
- qlever/commands/get_data.py +48 -0
- qlever/commands/index.py +333 -0
- qlever/commands/index_stats.py +306 -0
- qlever/commands/log.py +66 -0
- qlever/commands/materialized_view.py +110 -0
- qlever/commands/query.py +142 -0
- qlever/commands/rebuild_index.py +176 -0
- qlever/commands/reset_updates.py +59 -0
- qlever/commands/settings.py +115 -0
- qlever/commands/setup_config.py +97 -0
- qlever/commands/start.py +336 -0
- qlever/commands/status.py +50 -0
- qlever/commands/stop.py +90 -0
- qlever/commands/system_info.py +130 -0
- qlever/commands/ui.py +271 -0
- qlever/commands/update.py +90 -0
- qlever/commands/update_wikidata.py +1204 -0
- qlever/commands/warmup.py +41 -0
- qlever/config.py +223 -0
- qlever/containerize.py +167 -0
- qlever/log.py +55 -0
- qlever/qlever_main.py +79 -0
- qlever/qleverfile.py +530 -0
- qlever/util.py +330 -0
- qlever-0.5.41.dist-info/METADATA +127 -0
- qlever-0.5.41.dist-info/RECORD +59 -0
- {qlever-0.2.5.dist-info → qlever-0.5.41.dist-info}/WHEEL +1 -1
- qlever-0.5.41.dist-info/entry_points.txt +2 -0
- qlever-0.5.41.dist-info/top_level.txt +1 -0
- build/lib/qlever/__init__.py +0 -1383
- build/lib/qlever/__main__.py +0 -4
- qlever/__main__.py +0 -4
- qlever-0.2.5.dist-info/METADATA +0 -277
- qlever-0.2.5.dist-info/RECORD +0 -12
- qlever-0.2.5.dist-info/entry_points.txt +0 -2
- qlever-0.2.5.dist-info/top_level.txt +0 -4
- src/qlever/__init__.py +0 -1383
- src/qlever/__main__.py +0 -4
- {qlever-0.2.5.dist-info → qlever-0.5.41.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
from os import environ
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from qlever.command import QleverCommand
|
|
8
|
+
from qlever.log import log
|
|
9
|
+
from qlever.util import get_random_string
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SetupConfigCommand(QleverCommand):
|
|
13
|
+
"""
|
|
14
|
+
Class for executing the `setup-config` command.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
self.qleverfiles_path = Path(__file__).parent.parent / "Qleverfiles"
|
|
19
|
+
self.qleverfile_names = [
|
|
20
|
+
p.name.split(".")[1] for p in self.qleverfiles_path.glob("Qleverfile.*")
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
def description(self) -> str:
|
|
24
|
+
return "Get a pre-configured Qleverfile"
|
|
25
|
+
|
|
26
|
+
def should_have_qleverfile(self) -> bool:
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
def relevant_qleverfile_arguments(self) -> dict[str, list[str]]:
|
|
30
|
+
return {}
|
|
31
|
+
|
|
32
|
+
def additional_arguments(self, subparser) -> None:
|
|
33
|
+
subparser.add_argument(
|
|
34
|
+
"config_name",
|
|
35
|
+
type=str,
|
|
36
|
+
choices=self.qleverfile_names,
|
|
37
|
+
help="The name of the pre-configured Qleverfile to create",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def execute(self, args) -> bool:
|
|
41
|
+
# Show a warning if `QLEVER_OVERRIDE_SYSTEM_NATIVE` is set.
|
|
42
|
+
qlever_is_running_in_container = environ.get("QLEVER_IS_RUNNING_IN_CONTAINER")
|
|
43
|
+
if qlever_is_running_in_container:
|
|
44
|
+
log.warning(
|
|
45
|
+
"The environment variable `QLEVER_IS_RUNNING_IN_CONTAINER` is set, "
|
|
46
|
+
"therefore the Qleverfile is modified to use `SYSTEM = native` "
|
|
47
|
+
"(since inside the container, QLever should run natively)"
|
|
48
|
+
)
|
|
49
|
+
log.info("")
|
|
50
|
+
# Construct the command line and show it.
|
|
51
|
+
qleverfile_path = self.qleverfiles_path / f"Qleverfile.{args.config_name}"
|
|
52
|
+
setup_config_cmd = (
|
|
53
|
+
f"cat {qleverfile_path}"
|
|
54
|
+
f" | sed -E 's/(^ACCESS_TOKEN.*)/\\1_{get_random_string(12)}/'"
|
|
55
|
+
)
|
|
56
|
+
if qlever_is_running_in_container:
|
|
57
|
+
setup_config_cmd += (
|
|
58
|
+
" | sed -E 's/(^SYSTEM[[:space:]]*=[[:space:]]*).*/\\1native/'"
|
|
59
|
+
)
|
|
60
|
+
setup_config_cmd += "> Qleverfile"
|
|
61
|
+
self.show(setup_config_cmd, only_show=args.show)
|
|
62
|
+
if args.show:
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
# If there is already a Qleverfile in the current directory, exit.
|
|
66
|
+
qleverfile_path = Path("Qleverfile")
|
|
67
|
+
if qleverfile_path.exists():
|
|
68
|
+
log.error("`Qleverfile` already exists in current directory")
|
|
69
|
+
log.info("")
|
|
70
|
+
log.info(
|
|
71
|
+
"If you want to create a new Qleverfile using "
|
|
72
|
+
"`qlever setup-config`, delete the existing Qleverfile "
|
|
73
|
+
"first"
|
|
74
|
+
)
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
# Copy the Qleverfile to the current directory.
|
|
78
|
+
try:
|
|
79
|
+
subprocess.run(
|
|
80
|
+
setup_config_cmd,
|
|
81
|
+
shell=True,
|
|
82
|
+
check=True,
|
|
83
|
+
stdin=subprocess.DEVNULL,
|
|
84
|
+
stdout=subprocess.DEVNULL,
|
|
85
|
+
)
|
|
86
|
+
except Exception as e:
|
|
87
|
+
log.error(
|
|
88
|
+
f'Could not copy "{qleverfile_path}"' f" to current directory: {e}"
|
|
89
|
+
)
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
# If we get here, everything went well.
|
|
93
|
+
log.info(
|
|
94
|
+
f'Created Qleverfile for config "{args.config_name}"'
|
|
95
|
+
f" in current directory"
|
|
96
|
+
)
|
|
97
|
+
return True
|
qlever/commands/start.py
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
from qlever.command import QleverCommand
|
|
7
|
+
from qlever.commands.cache_stats import CacheStatsCommand
|
|
8
|
+
from qlever.commands.settings import SettingsCommand
|
|
9
|
+
from qlever.commands.status import StatusCommand
|
|
10
|
+
from qlever.commands.stop import StopCommand
|
|
11
|
+
from qlever.commands.warmup import WarmupCommand
|
|
12
|
+
from qlever.containerize import Containerize
|
|
13
|
+
from qlever.log import log
|
|
14
|
+
from qlever.qleverfile import Qleverfile
|
|
15
|
+
from qlever.util import binary_exists, is_qlever_server_alive, run_command
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Construct the command line based on the config file.
|
|
19
|
+
def construct_command(args) -> str:
|
|
20
|
+
start_cmd = (
|
|
21
|
+
f"{args.server_binary}"
|
|
22
|
+
f" -i {args.name}"
|
|
23
|
+
f" -j {args.num_threads}"
|
|
24
|
+
f" -p {args.port}"
|
|
25
|
+
f" -m {args.memory_for_queries}"
|
|
26
|
+
f" -c {args.cache_max_size}"
|
|
27
|
+
f" -e {args.cache_max_size_single_entry}"
|
|
28
|
+
f" -k {args.cache_max_num_entries}"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
if args.timeout:
|
|
32
|
+
start_cmd += f" -s {args.timeout}"
|
|
33
|
+
if args.access_token:
|
|
34
|
+
start_cmd += f" -a {args.access_token}"
|
|
35
|
+
if args.persist_updates:
|
|
36
|
+
start_cmd += " --persist-updates"
|
|
37
|
+
if args.only_pso_and_pos_permutations:
|
|
38
|
+
start_cmd += " --only-pso-and-pos-permutations"
|
|
39
|
+
if args.use_patterns == "no":
|
|
40
|
+
start_cmd += " --no-patterns"
|
|
41
|
+
if args.use_text_index == "yes":
|
|
42
|
+
start_cmd += " -t"
|
|
43
|
+
start_cmd += f" > {args.name}.server-log.txt 2>&1"
|
|
44
|
+
return start_cmd
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# Kill existing server on the same port. Trust that StopCommand() works?
|
|
48
|
+
# Maybe return StopCommand().execute(args) and handle it with a try except?
|
|
49
|
+
def kill_existing_server(args) -> bool:
|
|
50
|
+
args.cmdline_regex = f"^ServerMain.* -p {args.port}"
|
|
51
|
+
args.no_containers = True
|
|
52
|
+
if not StopCommand().execute(args):
|
|
53
|
+
log.error("Stopping the existing server failed")
|
|
54
|
+
return False
|
|
55
|
+
log.info("")
|
|
56
|
+
return True
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# Run the command in a container
|
|
60
|
+
def wrap_command_in_container(args, start_cmd) -> str:
|
|
61
|
+
if not args.server_container:
|
|
62
|
+
args.server_container = f"qlever.server.{args.name}"
|
|
63
|
+
start_cmd = Containerize().containerize_command(
|
|
64
|
+
start_cmd,
|
|
65
|
+
args.system,
|
|
66
|
+
"run -d --restart=unless-stopped",
|
|
67
|
+
args.image,
|
|
68
|
+
args.server_container,
|
|
69
|
+
volumes=[("$(pwd)", "/index")],
|
|
70
|
+
ports=[(args.port, args.port)],
|
|
71
|
+
working_directory="/index",
|
|
72
|
+
)
|
|
73
|
+
return start_cmd
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# Set the index description.
|
|
77
|
+
def set_index_description(access_arg, port, desc) -> bool:
|
|
78
|
+
curl_cmd = (
|
|
79
|
+
f"curl -Gs http://localhost:{port}/api"
|
|
80
|
+
f' --data-urlencode "index-description={desc}"'
|
|
81
|
+
f" {access_arg} > /dev/null"
|
|
82
|
+
)
|
|
83
|
+
log.debug(curl_cmd)
|
|
84
|
+
try:
|
|
85
|
+
run_command(curl_cmd)
|
|
86
|
+
except Exception as e:
|
|
87
|
+
log.error(f"Setting the index description failed ({e})")
|
|
88
|
+
return False
|
|
89
|
+
return True
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# Set the text description.
|
|
93
|
+
def set_text_description(access_arg, port, text_desc) -> bool:
|
|
94
|
+
curl_cmd = (
|
|
95
|
+
f"curl -Gs http://localhost:{port}/api"
|
|
96
|
+
f' --data-urlencode "text-description={text_desc}"'
|
|
97
|
+
f" {access_arg} > /dev/null"
|
|
98
|
+
)
|
|
99
|
+
log.debug(curl_cmd)
|
|
100
|
+
try:
|
|
101
|
+
run_command(curl_cmd)
|
|
102
|
+
except Exception as e:
|
|
103
|
+
log.error(f"Setting the text description failed ({e})")
|
|
104
|
+
return False
|
|
105
|
+
return True
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class StartCommand(QleverCommand):
|
|
109
|
+
"""
|
|
110
|
+
Class for executing the `start` command.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(self):
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
def description(self) -> str:
|
|
117
|
+
return (
|
|
118
|
+
"Start the QLever server (requires that you have built "
|
|
119
|
+
"an index with `qlever index` before)"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def should_have_qleverfile(self) -> bool:
|
|
123
|
+
return True
|
|
124
|
+
|
|
125
|
+
def relevant_qleverfile_arguments(self) -> dict[str, list[str]]:
|
|
126
|
+
return {
|
|
127
|
+
"data": ["name", "description", "text_description"],
|
|
128
|
+
"server": [
|
|
129
|
+
"server_binary",
|
|
130
|
+
"host_name",
|
|
131
|
+
"port",
|
|
132
|
+
"access_token",
|
|
133
|
+
"memory_for_queries",
|
|
134
|
+
"cache_max_size",
|
|
135
|
+
"cache_max_size_single_entry",
|
|
136
|
+
"cache_max_num_entries",
|
|
137
|
+
"num_threads",
|
|
138
|
+
"timeout",
|
|
139
|
+
"persist_updates",
|
|
140
|
+
"only_pso_and_pos_permutations",
|
|
141
|
+
"use_patterns",
|
|
142
|
+
"use_text_index",
|
|
143
|
+
"warmup_cmd",
|
|
144
|
+
],
|
|
145
|
+
"runtime": ["system", "image", "server_container"],
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
def additional_arguments(self, subparser) -> None:
|
|
149
|
+
subparser.add_argument(
|
|
150
|
+
"--kill-existing-with-same-port",
|
|
151
|
+
action="store_true",
|
|
152
|
+
default=False,
|
|
153
|
+
help="If a QLever server is already running "
|
|
154
|
+
"on the same port, kill it before "
|
|
155
|
+
"starting a new server",
|
|
156
|
+
)
|
|
157
|
+
subparser.add_argument(
|
|
158
|
+
"--no-warmup",
|
|
159
|
+
action="store_true",
|
|
160
|
+
default=False,
|
|
161
|
+
help="Do not execute the warmup command",
|
|
162
|
+
)
|
|
163
|
+
subparser.add_argument(
|
|
164
|
+
"--run-in-foreground",
|
|
165
|
+
action="store_true",
|
|
166
|
+
default=False,
|
|
167
|
+
help="Run the server in the foreground "
|
|
168
|
+
"(default: run in the background with `nohup`)",
|
|
169
|
+
)
|
|
170
|
+
subparser.add_argument(
|
|
171
|
+
"runtime_parameters",
|
|
172
|
+
nargs="*",
|
|
173
|
+
help="Space-separated list of runtime parameters to set "
|
|
174
|
+
"(in the form `key=value`) once the server is running",
|
|
175
|
+
).completer = lambda **kwargs: [
|
|
176
|
+
f"{key}=" for key in Qleverfile.SERVER_RUNTIME_PARAMETERS
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
def execute(self, args) -> bool:
|
|
180
|
+
# Set the endpoint URL.
|
|
181
|
+
args.endpoint_url = f"http://{args.host_name}:{args.port}"
|
|
182
|
+
|
|
183
|
+
# Kill existing server with the same name if so desired.
|
|
184
|
+
#
|
|
185
|
+
# TODO: This is currently disabled because I never used it once over
|
|
186
|
+
# the past weeks and it is not clear to me what the use case is.
|
|
187
|
+
if False: # or args.kill_existing_with_same_name:
|
|
188
|
+
args.cmdline_regex = f"^ServerMain.* -i {args.name}"
|
|
189
|
+
args.no_containers = True
|
|
190
|
+
StopCommand().execute(args)
|
|
191
|
+
log.info("")
|
|
192
|
+
|
|
193
|
+
# Kill existing server on the same port if so desired.
|
|
194
|
+
if args.kill_existing_with_same_port:
|
|
195
|
+
if args.kill_existing_with_same_port and not kill_existing_server(
|
|
196
|
+
args
|
|
197
|
+
):
|
|
198
|
+
return False
|
|
199
|
+
|
|
200
|
+
# Construct the command line based on the config file.
|
|
201
|
+
start_cmd = construct_command(args)
|
|
202
|
+
|
|
203
|
+
# Run the command in a container (if so desired). Otherwise run with
|
|
204
|
+
# `nohup` so that it keeps running after the shell is closed. With
|
|
205
|
+
# `--run-in-foreground`, run the server in the foreground.
|
|
206
|
+
if args.system in Containerize.supported_systems():
|
|
207
|
+
start_cmd = wrap_command_in_container(args, start_cmd)
|
|
208
|
+
elif args.run_in_foreground:
|
|
209
|
+
start_cmd = f"{start_cmd}"
|
|
210
|
+
else:
|
|
211
|
+
start_cmd = f"nohup {start_cmd} &"
|
|
212
|
+
|
|
213
|
+
# Show the command line.
|
|
214
|
+
self.show(start_cmd, only_show=args.show)
|
|
215
|
+
if args.show:
|
|
216
|
+
if args.runtime_parameters:
|
|
217
|
+
log.info("")
|
|
218
|
+
SettingsCommand().execute(args)
|
|
219
|
+
return True
|
|
220
|
+
|
|
221
|
+
# When running natively, check if the binary exists and works.
|
|
222
|
+
if args.system == "native":
|
|
223
|
+
if not binary_exists(args.server_binary, "server-binary"):
|
|
224
|
+
return False
|
|
225
|
+
|
|
226
|
+
# Check if a QLever server is already running on this port.
|
|
227
|
+
if is_qlever_server_alive(args.endpoint_url):
|
|
228
|
+
log.error(f"QLever server already running on {args.endpoint_url}")
|
|
229
|
+
log.info("")
|
|
230
|
+
log.info(
|
|
231
|
+
"To kill the existing server, use `qlever stop` "
|
|
232
|
+
"or `qlever start` with option "
|
|
233
|
+
"--kill-existing-with-same-port`"
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# Show output of status command.
|
|
237
|
+
args.cmdline_regex = f"^ServerMain.* -p *{args.port}"
|
|
238
|
+
log.info("")
|
|
239
|
+
StatusCommand().execute(args)
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
# Remove already existing container.
|
|
243
|
+
if (
|
|
244
|
+
args.system in Containerize.supported_systems()
|
|
245
|
+
and args.kill_existing_with_same_port
|
|
246
|
+
):
|
|
247
|
+
try:
|
|
248
|
+
run_command(f"{args.system} rm -f {args.server_container}")
|
|
249
|
+
except Exception as e:
|
|
250
|
+
log.error(f"Removing existing container failed: {e}")
|
|
251
|
+
return False
|
|
252
|
+
|
|
253
|
+
# Check if another process is already listening.
|
|
254
|
+
# if self.net_connections_enabled:
|
|
255
|
+
# if port in [conn.laddr.port for conn
|
|
256
|
+
# in psutil.net_connections()]:
|
|
257
|
+
# log.error(f"Port {port} is already in use by another process"
|
|
258
|
+
# f" (use `lsof -i :{port}` to find out which one)")
|
|
259
|
+
# return False
|
|
260
|
+
|
|
261
|
+
# Execute the command line.
|
|
262
|
+
try:
|
|
263
|
+
process = run_command(
|
|
264
|
+
start_cmd,
|
|
265
|
+
use_popen=args.run_in_foreground,
|
|
266
|
+
)
|
|
267
|
+
except Exception as e:
|
|
268
|
+
log.error(f"Starting the QLever server failed ({e})")
|
|
269
|
+
return False
|
|
270
|
+
|
|
271
|
+
# Tail the server log until the server is ready (note that the `exec`
|
|
272
|
+
# is important to make sure that the tail process is killed and not
|
|
273
|
+
# just the bash process).
|
|
274
|
+
if args.run_in_foreground:
|
|
275
|
+
log.info(
|
|
276
|
+
f"Follow {args.name}.server-log.txt as long as the server is"
|
|
277
|
+
f" running (Ctrl-C stops the server)"
|
|
278
|
+
)
|
|
279
|
+
else:
|
|
280
|
+
log.info(
|
|
281
|
+
f"Follow {args.name}.server-log.txt until the server is ready"
|
|
282
|
+
f" (Ctrl-C stops following the log, but NOT the server)"
|
|
283
|
+
)
|
|
284
|
+
log.info("")
|
|
285
|
+
tail_cmd = f"exec tail -f {args.name}.server-log.txt"
|
|
286
|
+
tail_proc = subprocess.Popen(tail_cmd, shell=True)
|
|
287
|
+
while not is_qlever_server_alive(args.endpoint_url):
|
|
288
|
+
time.sleep(1)
|
|
289
|
+
|
|
290
|
+
# Set the description for the index and text.
|
|
291
|
+
access_arg = f'--data-urlencode "access-token={args.access_token}"'
|
|
292
|
+
if args.description:
|
|
293
|
+
ret = set_index_description(
|
|
294
|
+
access_arg, args.port, args.description
|
|
295
|
+
)
|
|
296
|
+
if not ret:
|
|
297
|
+
return False
|
|
298
|
+
if args.text_description:
|
|
299
|
+
ret = set_text_description(
|
|
300
|
+
access_arg, args.port, args.text_description
|
|
301
|
+
)
|
|
302
|
+
if not ret:
|
|
303
|
+
return False
|
|
304
|
+
|
|
305
|
+
# Kill the tail process. NOTE: `tail_proc.kill()` does not work.
|
|
306
|
+
if not args.run_in_foreground:
|
|
307
|
+
tail_proc.terminate()
|
|
308
|
+
|
|
309
|
+
# Execute the warmup command.
|
|
310
|
+
if args.warmup_cmd and not args.no_warmup:
|
|
311
|
+
log.info("")
|
|
312
|
+
if not WarmupCommand().execute(args):
|
|
313
|
+
log.error("Warmup failed")
|
|
314
|
+
return False
|
|
315
|
+
|
|
316
|
+
# Show cache stats.
|
|
317
|
+
if not args.run_in_foreground:
|
|
318
|
+
log.info("")
|
|
319
|
+
args.detailed = False
|
|
320
|
+
args.sparql_endpoint = None
|
|
321
|
+
CacheStatsCommand().execute(args)
|
|
322
|
+
|
|
323
|
+
# Apply settings if any.
|
|
324
|
+
if args.runtime_parameters:
|
|
325
|
+
log.info("")
|
|
326
|
+
SettingsCommand().execute(args)
|
|
327
|
+
|
|
328
|
+
# With `--run-in-foreground`, wait until the server is stopped.
|
|
329
|
+
if args.run_in_foreground:
|
|
330
|
+
try:
|
|
331
|
+
process.wait()
|
|
332
|
+
except KeyboardInterrupt:
|
|
333
|
+
process.terminate()
|
|
334
|
+
tail_proc.terminate()
|
|
335
|
+
|
|
336
|
+
return True
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import psutil
|
|
4
|
+
|
|
5
|
+
from qlever.command import QleverCommand
|
|
6
|
+
from qlever.util import show_process_info
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class StatusCommand(QleverCommand):
|
|
10
|
+
"""
|
|
11
|
+
Class for executing the `status` command.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
def description(self) -> str:
|
|
18
|
+
return ("Show QLever processes running on this machine")
|
|
19
|
+
|
|
20
|
+
def should_have_qleverfile(self) -> bool:
|
|
21
|
+
return False
|
|
22
|
+
|
|
23
|
+
def relevant_qleverfile_arguments(self) -> dict[str: list[str]]:
|
|
24
|
+
return {}
|
|
25
|
+
|
|
26
|
+
def additional_arguments(self, subparser) -> None:
|
|
27
|
+
subparser.add_argument("--cmdline-regex",
|
|
28
|
+
default="^(ServerMain|IndexBuilderMain)",
|
|
29
|
+
help="Show only processes where the command "
|
|
30
|
+
"line matches this regex")
|
|
31
|
+
|
|
32
|
+
def execute(self, args) -> bool:
|
|
33
|
+
# Show action description.
|
|
34
|
+
self.show(f"Show all processes on this machine where "
|
|
35
|
+
f"the command line matches {args.cmdline_regex}"
|
|
36
|
+
f" using Python's psutil library", only_show=args.show)
|
|
37
|
+
if args.show:
|
|
38
|
+
return True
|
|
39
|
+
|
|
40
|
+
# Show the results as a table.
|
|
41
|
+
num_processes_found = 0
|
|
42
|
+
for proc in psutil.process_iter():
|
|
43
|
+
show_heading = num_processes_found == 0
|
|
44
|
+
process_shown = show_process_info(proc, args.cmdline_regex,
|
|
45
|
+
show_heading=show_heading)
|
|
46
|
+
if process_shown:
|
|
47
|
+
num_processes_found += 1
|
|
48
|
+
if num_processes_found == 0:
|
|
49
|
+
print("No processes found")
|
|
50
|
+
return True
|
qlever/commands/stop.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from qlever.command import QleverCommand
|
|
4
|
+
from qlever.commands.status import StatusCommand
|
|
5
|
+
from qlever.containerize import Containerize
|
|
6
|
+
from qlever.log import log
|
|
7
|
+
from qlever.util import stop_process_with_regex
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def stop_container(server_container: str) -> bool:
|
|
11
|
+
"""
|
|
12
|
+
Try to stop and remove container. return True iff it was stopped
|
|
13
|
+
successfully. Gives log info accordingly.
|
|
14
|
+
"""
|
|
15
|
+
for container_system in Containerize.supported_systems():
|
|
16
|
+
if Containerize.stop_and_remove_container(
|
|
17
|
+
container_system, server_container
|
|
18
|
+
):
|
|
19
|
+
log.info(
|
|
20
|
+
f"{container_system.capitalize()} container with "
|
|
21
|
+
f'name "{server_container}" stopped and removed'
|
|
22
|
+
)
|
|
23
|
+
return True
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class StopCommand(QleverCommand):
|
|
28
|
+
"""
|
|
29
|
+
Class for executing the `stop` command.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
def description(self) -> str:
|
|
36
|
+
return "Stop QLever server for a given dataset or port"
|
|
37
|
+
|
|
38
|
+
def should_have_qleverfile(self) -> bool:
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
def relevant_qleverfile_arguments(self) -> dict[str: list[str]]:
|
|
42
|
+
return {"data": ["name"],
|
|
43
|
+
"server": ["port"],
|
|
44
|
+
"runtime": ["server_container"]}
|
|
45
|
+
|
|
46
|
+
def additional_arguments(self, subparser) -> None:
|
|
47
|
+
subparser.add_argument("--cmdline-regex",
|
|
48
|
+
default="ServerMain.* -i [^ ]*%%NAME%%",
|
|
49
|
+
help="Show only processes where the command "
|
|
50
|
+
"line matches this regex")
|
|
51
|
+
subparser.add_argument("--no-containers", action="store_true",
|
|
52
|
+
default=False,
|
|
53
|
+
help="Do not look for containers, only for "
|
|
54
|
+
"native processes")
|
|
55
|
+
|
|
56
|
+
def execute(self, args) -> bool:
|
|
57
|
+
# Show action description.
|
|
58
|
+
cmdline_regex = args.cmdline_regex.replace("%%NAME%%", args.name)
|
|
59
|
+
description = f"Checking for processes matching \"{cmdline_regex}\""
|
|
60
|
+
if not args.no_containers:
|
|
61
|
+
description += (f" and for Docker container with name "
|
|
62
|
+
f"\"{args.server_container}\"")
|
|
63
|
+
self.show(description, only_show=args.show)
|
|
64
|
+
if args.show:
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
# First check if there is container running and if yes, stop and remove
|
|
68
|
+
# it (unless the user has specified `--no-containers`).
|
|
69
|
+
if not args.no_containers:
|
|
70
|
+
if stop_container(args.server_container):
|
|
71
|
+
return True
|
|
72
|
+
|
|
73
|
+
# Check if there is a process running on the server port using psutil.
|
|
74
|
+
# NOTE: On MacOS, some of the proc's returned by psutil.process_iter()
|
|
75
|
+
# no longer exist when we try to access them, so we just skip them.
|
|
76
|
+
stop_process_results = stop_process_with_regex(cmdline_regex)
|
|
77
|
+
if stop_process_results is None:
|
|
78
|
+
return False
|
|
79
|
+
if len(stop_process_results) > 0:
|
|
80
|
+
return all(stop_process_results)
|
|
81
|
+
|
|
82
|
+
# If no matching process found, show a message and the output of the
|
|
83
|
+
# status command.
|
|
84
|
+
message = "No matching process found" if args.no_containers else \
|
|
85
|
+
"No matching process or container found"
|
|
86
|
+
log.error(message)
|
|
87
|
+
args.cmdline_regex = "^ServerMain.* -i [^ ]*"
|
|
88
|
+
log.info("")
|
|
89
|
+
StatusCommand().execute(args)
|
|
90
|
+
return True
|