atex 0.7__py3-none-any.whl → 0.8__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.
- atex/cli/fmf.py +93 -0
- atex/cli/testingfarm.py +23 -13
- atex/connection/__init__.py +0 -8
- atex/connection/ssh.py +3 -19
- atex/executor/__init__.py +2 -0
- atex/executor/duration.py +60 -0
- atex/executor/executor.py +378 -0
- atex/executor/reporter.py +106 -0
- atex/{minitmt → executor}/scripts.py +30 -24
- atex/{minitmt → executor}/testcontrol.py +16 -17
- atex/{minitmt/fmf.py → fmf.py} +49 -34
- atex/orchestrator/__init__.py +2 -59
- atex/orchestrator/aggregator.py +66 -123
- atex/orchestrator/orchestrator.py +324 -0
- atex/provision/__init__.py +68 -99
- atex/provision/testingfarm/__init__.py +2 -29
- atex/provision/testingfarm/api.py +55 -40
- atex/provision/testingfarm/testingfarm.py +236 -0
- atex/util/__init__.py +1 -6
- atex/util/log.py +8 -0
- atex/util/path.py +16 -0
- atex/util/ssh_keygen.py +14 -0
- atex/util/threads.py +55 -0
- {atex-0.7.dist-info → atex-0.8.dist-info}/METADATA +97 -2
- atex-0.8.dist-info/RECORD +37 -0
- atex/cli/minitmt.py +0 -175
- atex/minitmt/__init__.py +0 -23
- atex/minitmt/executor.py +0 -348
- atex/provision/nspawn/README +0 -74
- atex/provision/testingfarm/foo.py +0 -1
- atex-0.7.dist-info/RECORD +0 -32
- {atex-0.7.dist-info → atex-0.8.dist-info}/WHEEL +0 -0
- {atex-0.7.dist-info → atex-0.8.dist-info}/entry_points.txt +0 -0
- {atex-0.7.dist-info → atex-0.8.dist-info}/licenses/COPYING.txt +0 -0
atex/minitmt/executor.py
DELETED
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import re
|
|
3
|
-
import time
|
|
4
|
-
import select
|
|
5
|
-
import threading
|
|
6
|
-
import contextlib
|
|
7
|
-
import subprocess
|
|
8
|
-
|
|
9
|
-
from .. import util
|
|
10
|
-
from . import testcontrol, scripts, fmf
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class Duration:
|
|
14
|
-
"""
|
|
15
|
-
A helper for parsing, keeping and manipulating test run time based on
|
|
16
|
-
FMF-defined 'duration' attribute.
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
def __init__(self, fmf_duration):
|
|
20
|
-
"""
|
|
21
|
-
'fmf_duration' is the string specified as 'duration' in FMF metadata.
|
|
22
|
-
"""
|
|
23
|
-
duration = self._fmf_to_seconds(fmf_duration)
|
|
24
|
-
self.end = time.monotonic() + duration
|
|
25
|
-
# keep track of only the first 'save' and the last 'restore',
|
|
26
|
-
# ignore any nested ones (as tracked by '_count')
|
|
27
|
-
self.saved = None
|
|
28
|
-
self.saved_count = 0
|
|
29
|
-
|
|
30
|
-
@staticmethod
|
|
31
|
-
def _fmf_to_seconds(string):
|
|
32
|
-
match = re.fullmatch(r"([0-9]+)([a-z]*)", string)
|
|
33
|
-
if not match:
|
|
34
|
-
raise RuntimeError(f"'duration' has invalid format: {string}")
|
|
35
|
-
length, unit = match.groups()
|
|
36
|
-
if unit == "m":
|
|
37
|
-
return int(length)*60
|
|
38
|
-
elif unit == "h":
|
|
39
|
-
return int(length)*60*60
|
|
40
|
-
elif unit == "d":
|
|
41
|
-
return int(length)*60*60*24
|
|
42
|
-
else:
|
|
43
|
-
return int(length)
|
|
44
|
-
|
|
45
|
-
def set(self, to):
|
|
46
|
-
self.end = time.monotonic() + self._fmf_to_seconds(to)
|
|
47
|
-
|
|
48
|
-
def increment(self, by):
|
|
49
|
-
self.end += self._fmf_to_seconds(by)
|
|
50
|
-
|
|
51
|
-
def decrement(self, by):
|
|
52
|
-
self.end -= self._fmf_to_seconds(by)
|
|
53
|
-
|
|
54
|
-
def save(self):
|
|
55
|
-
if self.saved_count == 0:
|
|
56
|
-
self.saved = self.end - time.monotonic()
|
|
57
|
-
self.saved_count += 1
|
|
58
|
-
|
|
59
|
-
def restore(self):
|
|
60
|
-
if self.saved_count > 1:
|
|
61
|
-
self.saved_count -= 1
|
|
62
|
-
elif self.saved_count == 1:
|
|
63
|
-
self.end = time.monotonic() + self.saved
|
|
64
|
-
self.saved_count = 0
|
|
65
|
-
self.saved = None
|
|
66
|
-
|
|
67
|
-
def out_of_time(self):
|
|
68
|
-
return time.monotonic() > self.end
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
# SETUP global:
|
|
72
|
-
# - create CSVAggregator instance on some destination dir
|
|
73
|
-
|
|
74
|
-
# SETUP of new Provisioner instance:
|
|
75
|
-
# - with SSHConn
|
|
76
|
-
# - rsync test repo to some tmpdir on the remote
|
|
77
|
-
# - install packages from plan
|
|
78
|
-
# - create TMT_PLAN_ENVIRONMENT_FILE and export it to plan setup scripts
|
|
79
|
-
# - run plan setup scripts
|
|
80
|
-
|
|
81
|
-
# SETUP + CLEANUP for one test
|
|
82
|
-
# - create Executor instance
|
|
83
|
-
# - pass TMT_PLAN_ENVIRONMENT_FILE to it
|
|
84
|
-
# - probably as general "environment variables" input,
|
|
85
|
-
# could be reused in the future for ie. TMT_PLAN_NAME or so
|
|
86
|
-
# - pass env from CLI -e switches
|
|
87
|
-
# - pass disconnected SSHConn to it
|
|
88
|
-
# - pass one FMFTest namedtuple to it (test to run)
|
|
89
|
-
# - pass test repo location on the remote host
|
|
90
|
-
# - pass CSVAggregator instance to it, so it can write results/logs
|
|
91
|
-
# - with SSHConn
|
|
92
|
-
# - create wrapper script on the remote
|
|
93
|
-
# - run wrapper script, redirecting stderr to tmpfile via CSVAggregator
|
|
94
|
-
# - poll() / select() over test stdout, parse TEST_CONTROL protocol
|
|
95
|
-
# - on 0.1sec poll timeout
|
|
96
|
-
# - check if SSHConn master is alive, re-connect if not
|
|
97
|
-
# - check test duration against fmf-defined (control-adjusted) duration
|
|
98
|
-
# - ...
|
|
99
|
-
# - make Executor return some complex status
|
|
100
|
-
# - whether testing was destructive (just leave SSHConn disconnected?)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
class TestAbortedError(Exception):
|
|
104
|
-
"""
|
|
105
|
-
Raised when an infrastructure-related issue happened while running a test.
|
|
106
|
-
"""
|
|
107
|
-
pass
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
# TODO: automatic reporting of all partial results that were not finished
|
|
111
|
-
class Executor:
|
|
112
|
-
"""
|
|
113
|
-
Logic for running tests on a remote system and processing results
|
|
114
|
-
and uploaded files by those tests.
|
|
115
|
-
"""
|
|
116
|
-
|
|
117
|
-
def __init__(self, remote, aggregator, remote_dir=None, env=None):
|
|
118
|
-
"""
|
|
119
|
-
'remote' is a reserved class Remote instance, with an active connection.
|
|
120
|
-
|
|
121
|
-
'aggregator' is an instance of a ResultAggregator all the results
|
|
122
|
-
and uploaded files will be written to.
|
|
123
|
-
|
|
124
|
-
'remote_dir' is a path on the remote for storing tests and other
|
|
125
|
-
metadata. If unspecified, a tmpdir is created and used instead.
|
|
126
|
-
|
|
127
|
-
'env' is a dict of extra environment variables to pass to the
|
|
128
|
-
plan prepare scripts and tests.
|
|
129
|
-
"""
|
|
130
|
-
self.lock = threading.RLock()
|
|
131
|
-
self.remote = remote
|
|
132
|
-
self.aggregator = aggregator
|
|
133
|
-
self.cancelled = False
|
|
134
|
-
self.remote_dir = remote_dir
|
|
135
|
-
self.plan_env = env.copy() if env else {}
|
|
136
|
-
|
|
137
|
-
def _get_remote_dir(self):
|
|
138
|
-
# TODO: do not mktemp here, do it in the parent, have remote_dir be mandatory,
|
|
139
|
-
# renamed to 'tests_dir' (just the test repo), cleaned up by external logic
|
|
140
|
-
# - handle custom metadata remote dir in run_test() and clean it up there
|
|
141
|
-
# (for TMT_PLAN_ENVIRONMENT_FILE, wrapper, etc.)
|
|
142
|
-
if not self.remote_dir:
|
|
143
|
-
self.remote_dir = self.remote.cmd(
|
|
144
|
-
("mktemp", "-d", "-p", "/var/tmp"),
|
|
145
|
-
func=util.subprocess_output,
|
|
146
|
-
)
|
|
147
|
-
return self.remote_dir
|
|
148
|
-
|
|
149
|
-
# TODO: do not do this in Executor
|
|
150
|
-
def upload_tests(self, tests_dir):
|
|
151
|
-
"""
|
|
152
|
-
Upload a directory of all tests from a local 'tests_dir' path to
|
|
153
|
-
a temporary directory on the remote host.
|
|
154
|
-
"""
|
|
155
|
-
remote_dir = self._get_remote_dir()
|
|
156
|
-
self.remote.rsync(
|
|
157
|
-
"-rv", "--delete", "--exclude=.git/",
|
|
158
|
-
f"{tests_dir}/",
|
|
159
|
-
f"remote:{remote_dir}/tests",
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
def setup_plan(self, fmf_tests):
|
|
163
|
-
"""
|
|
164
|
-
Install packages and run scripts, presumably extracted from a TMT plan
|
|
165
|
-
provided as 'fmf_tests', an initialized FMFTests instance.
|
|
166
|
-
|
|
167
|
-
Also prepare additional environment for tests, ie. create and export
|
|
168
|
-
a path to TMT_PLAN_ENVIRONMENT_FILE.
|
|
169
|
-
"""
|
|
170
|
-
# install packages from the plan
|
|
171
|
-
if fmf_tests.prepare_pkgs:
|
|
172
|
-
self.remote.cmd(
|
|
173
|
-
(
|
|
174
|
-
"dnf", "-y", "--setopt=install_weak_deps=False",
|
|
175
|
-
"install", *fmf_tests.prepare_pkgs,
|
|
176
|
-
),
|
|
177
|
-
check=True,
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
# create TMT_PLAN_ENVIRONMENT_FILE
|
|
181
|
-
self.plan_env.update(fmf_tests.plan_env)
|
|
182
|
-
env_file = f"{self._get_remote_dir()}/TMT_PLAN_ENVIRONMENT_FILE"
|
|
183
|
-
self.remote.cmd(("truncate", "-s", "0", env_file), check=True)
|
|
184
|
-
self.plan_env["TMT_PLAN_ENVIRONMENT_FILE"] = env_file
|
|
185
|
-
|
|
186
|
-
# run prepare scripts
|
|
187
|
-
env_args = (f"{k}={v}" for k, v in self.plan_env.items())
|
|
188
|
-
for script in fmf_tests.prepare_scripts:
|
|
189
|
-
self.remote.cmd(
|
|
190
|
-
("env", *env_args, "bash"),
|
|
191
|
-
input=script,
|
|
192
|
-
text=True,
|
|
193
|
-
check=True,
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
def run_test(self, fmf_test, env=None):
|
|
197
|
-
"""
|
|
198
|
-
Run one test on the remote system.
|
|
199
|
-
|
|
200
|
-
'fmf_test' is a FMFTest namedtuple with the test to run.
|
|
201
|
-
|
|
202
|
-
'env' is a dict of extra environment variables to pass to the test.
|
|
203
|
-
"""
|
|
204
|
-
env_vars = self.plan_env.copy()
|
|
205
|
-
for item in fmf.listlike(fmf_test.data, "environment"):
|
|
206
|
-
env_vars.update(item)
|
|
207
|
-
env_vars["ATEX_TEST_NAME"] = fmf_test.name
|
|
208
|
-
env_vars["TMT_TEST_NAME"] = fmf_test.name
|
|
209
|
-
if env:
|
|
210
|
-
env_vars.update(env)
|
|
211
|
-
env_args = (f"{k}={v}" for k, v in env_vars.items())
|
|
212
|
-
|
|
213
|
-
# run a setup script, preparing wrapper + test scripts
|
|
214
|
-
remote_dir = self._get_remote_dir()
|
|
215
|
-
setup_script = scripts.test_setup(
|
|
216
|
-
test=fmf_test,
|
|
217
|
-
tests_dir=f"{remote_dir}/tests",
|
|
218
|
-
wrapper_exec=f"{remote_dir}/wrapper.sh",
|
|
219
|
-
test_exec=f"{remote_dir}/test.sh",
|
|
220
|
-
debug=True,
|
|
221
|
-
)
|
|
222
|
-
self.remote.cmd(("bash",), input=setup_script, text=True, check=True)
|
|
223
|
-
|
|
224
|
-
with contextlib.ExitStack() as stack:
|
|
225
|
-
testout_fd = stack.enter_context(self.aggregator.open_tmpfile())
|
|
226
|
-
|
|
227
|
-
duration = Duration(fmf_test.data.get("duration", "5m"))
|
|
228
|
-
|
|
229
|
-
test_proc = None
|
|
230
|
-
control_fd = None
|
|
231
|
-
stack.callback(lambda: os.close(control_fd) if control_fd else None)
|
|
232
|
-
|
|
233
|
-
def abort(msg):
|
|
234
|
-
if test_proc:
|
|
235
|
-
test_proc.kill()
|
|
236
|
-
test_proc.wait()
|
|
237
|
-
self.remote.release()
|
|
238
|
-
raise TestAbortedError(msg) from None
|
|
239
|
-
|
|
240
|
-
try:
|
|
241
|
-
# TODO: probably enum
|
|
242
|
-
state = "starting_test"
|
|
243
|
-
while not duration.out_of_time():
|
|
244
|
-
with self.lock:
|
|
245
|
-
if self.cancelled:
|
|
246
|
-
abort("cancel requested")
|
|
247
|
-
return
|
|
248
|
-
|
|
249
|
-
if state == "starting_test":
|
|
250
|
-
control_fd, pipe_w = os.pipe()
|
|
251
|
-
os.set_blocking(control_fd, False)
|
|
252
|
-
control = testcontrol.TestControl(
|
|
253
|
-
control_fd=control_fd,
|
|
254
|
-
aggregator=self.aggregator,
|
|
255
|
-
duration=duration,
|
|
256
|
-
testout_fd=testout_fd,
|
|
257
|
-
)
|
|
258
|
-
# run the test in the background, letting it log output directly to
|
|
259
|
-
# an opened file (we don't handle it, cmd client sends it to kernel)
|
|
260
|
-
test_proc = self.remote.cmd(
|
|
261
|
-
("env", *env_args, f"{remote_dir}/wrapper.sh"),
|
|
262
|
-
stdout=pipe_w,
|
|
263
|
-
stderr=testout_fd,
|
|
264
|
-
func=util.subprocess_Popen,
|
|
265
|
-
)
|
|
266
|
-
os.close(pipe_w)
|
|
267
|
-
state = "reading_control"
|
|
268
|
-
|
|
269
|
-
elif state == "reading_control":
|
|
270
|
-
rlist, _, xlist = select.select((control_fd,), (), (control_fd,), 0.1)
|
|
271
|
-
if xlist:
|
|
272
|
-
abort(f"got exceptional condition on control_fd {control_fd}")
|
|
273
|
-
elif rlist:
|
|
274
|
-
control.process()
|
|
275
|
-
if control.eof:
|
|
276
|
-
os.close(control_fd)
|
|
277
|
-
control_fd = None
|
|
278
|
-
state = "waiting_for_exit"
|
|
279
|
-
|
|
280
|
-
elif state == "waiting_for_exit":
|
|
281
|
-
# control stream is EOF and it has nothing for us to read,
|
|
282
|
-
# we're now just waiting for proc to cleanly terminate
|
|
283
|
-
try:
|
|
284
|
-
code = test_proc.wait(0.1)
|
|
285
|
-
if code == 0:
|
|
286
|
-
# wrapper exited cleanly, testing is done
|
|
287
|
-
break
|
|
288
|
-
else:
|
|
289
|
-
# unexpected error happened (crash, disconnect, etc.)
|
|
290
|
-
self.remote.disconnect()
|
|
291
|
-
# if reconnect was requested, do so, otherwise abort
|
|
292
|
-
if control.reconnect:
|
|
293
|
-
state = "reconnecting"
|
|
294
|
-
if control.reconnect != "always":
|
|
295
|
-
control.reconnect = None
|
|
296
|
-
else:
|
|
297
|
-
abort(f"test wrapper unexpectedly exited with {code}")
|
|
298
|
-
test_proc = None
|
|
299
|
-
except subprocess.TimeoutExpired:
|
|
300
|
-
pass
|
|
301
|
-
|
|
302
|
-
elif state == "reconnecting":
|
|
303
|
-
try:
|
|
304
|
-
self.remote.connect(block=False)
|
|
305
|
-
state = "reading_control"
|
|
306
|
-
except BlockingIOError:
|
|
307
|
-
pass
|
|
308
|
-
|
|
309
|
-
else:
|
|
310
|
-
raise AssertionError("reached unexpected state")
|
|
311
|
-
|
|
312
|
-
else:
|
|
313
|
-
abort("test duration timeout reached")
|
|
314
|
-
|
|
315
|
-
# testing successful, do post-testing tasks
|
|
316
|
-
|
|
317
|
-
# test wrapper hasn't provided exitcode
|
|
318
|
-
if control.exit_code is None:
|
|
319
|
-
abort("exitcode not reported, wrapper bug?")
|
|
320
|
-
|
|
321
|
-
# partial results that were never reported
|
|
322
|
-
if control.partial_results:
|
|
323
|
-
control.result_seen = True # partial result is also a result
|
|
324
|
-
for result in control.partial_results.values():
|
|
325
|
-
self.aggregator.report(result)
|
|
326
|
-
|
|
327
|
-
# test hasn't reported a single result, add an automatic one
|
|
328
|
-
# as specified in RESULTS.md
|
|
329
|
-
# {"status": "pass", "name": "/some/test", "testout": "output.txt"}
|
|
330
|
-
if not control.result_seen:
|
|
331
|
-
self.aggregator.link_tmpfile_to(fmf_test.name, "output.txt", testout_fd)
|
|
332
|
-
self.aggregator.report({
|
|
333
|
-
"status": "pass" if control.exit_code == 0 else "fail",
|
|
334
|
-
"name": fmf_test.name,
|
|
335
|
-
"testout": "output.txt",
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
except Exception:
|
|
339
|
-
# if the test hasn't reported a single result, but still
|
|
340
|
-
# managed to break something, provide at least the default log
|
|
341
|
-
# for manual investigation - otherwise test output disappears
|
|
342
|
-
if not control.result_seen:
|
|
343
|
-
self.aggregator.link_tmpfile_to(fmf_test.name, "output.txt", testout_fd)
|
|
344
|
-
raise
|
|
345
|
-
|
|
346
|
-
def cancel(self):
|
|
347
|
-
with self.lock:
|
|
348
|
-
self.cancelled = True
|
atex/provision/nspawn/README
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
The idea is to use systemd-nspawn containers on the host, binding
|
|
2
|
-
/dev/kvm to each, thus avoiding the need for nested virt as our first layer
|
|
3
|
-
of Contest tests will run in the containers (installing libvirtd, etc.)
|
|
4
|
-
and the second layer (VMs created by tests) will use virtual machines,
|
|
5
|
-
via a non-nested HVM.
|
|
6
|
-
|
|
7
|
-
systemd-nspawn containers can have CPU core limits, memory limits, etc.
|
|
8
|
-
done via cgroups, so we can provide some level of isolation/safety.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
systemd-nspawn can create its own veth via --network-veth=... and put it into
|
|
12
|
-
a bridge automatically via --network-bridge=...
|
|
13
|
-
|
|
14
|
-
We can then use NetworkManager + firewalld to pre-create a bridge with built-in
|
|
15
|
-
DHCP and NAT to the outside, via something like
|
|
16
|
-
|
|
17
|
-
nmcli connection add type bridge ifname br0 con-name br0 ipv4.method shared ipv6.method ignore
|
|
18
|
-
|
|
19
|
-
According to https://fedoramagazine.org/internet-connection-sharing-networkmanager/
|
|
20
|
-
the ipv4.method=shared :
|
|
21
|
-
|
|
22
|
-
enables IP forwarding for the interface;
|
|
23
|
-
adds firewall rules and enables masquerading;
|
|
24
|
-
starts dnsmasq as a DHCP and DNS server.
|
|
25
|
-
|
|
26
|
-
Specifically it should add MASQUERADE on packets *outgoing* from the bridge subnet,
|
|
27
|
-
so shouldn't need any modification of the upstream eth0 device or any fw rules tied to it.
|
|
28
|
-
|
|
29
|
-
There also seems to be ipv4.addresses 192.168.42.1/24 to modify the subnet?
|
|
30
|
-
|
|
31
|
-
If that doesn't work, firewalld has an External zone that has <masquerade/>
|
|
32
|
-
by default, so
|
|
33
|
-
|
|
34
|
-
nmcli connection modify br0 connection.zone external
|
|
35
|
-
|
|
36
|
-
should work.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
--------
|
|
40
|
-
|
|
41
|
-
TODO: We need some way to get DHCP leases for started containers (so we can connect
|
|
42
|
-
to the containerized sshd).
|
|
43
|
-
|
|
44
|
-
If there is no command for it via nmcli, it should be possible to just
|
|
45
|
-
extract it from wherever NetworkManager pointed dnsmasq to store its leases file.
|
|
46
|
-
|
|
47
|
-
We can then probably correlate --network-veth=... device from systemd-nspawn
|
|
48
|
-
(named after --machine=... name, prefixed with ve-* or vb-* if --network-bridge=* is used)
|
|
49
|
-
to the leased IP address.
|
|
50
|
-
|
|
51
|
-
ls -l /var/lib/NetworkManager/dnsmasq-*.leases
|
|
52
|
-
|
|
53
|
-
Or perhaps parse it out of 'ip neigh' to make sure the guest is *really* up.
|
|
54
|
-
- 'ip neigh' gives us MAC-to-IP, but device is always br0
|
|
55
|
-
- 'ip link show dev vb-contname' should give us the MAC for 'ip neigh'
|
|
56
|
-
- if container veth endpoint uses different mac, we can query bridge forward DB
|
|
57
|
-
via 'bridge fdb' to get all MACs that appeared on the veth
|
|
58
|
-
|
|
59
|
-
--------
|
|
60
|
-
|
|
61
|
-
Containers can be installed via ie.
|
|
62
|
-
|
|
63
|
-
dnf --releasever=41 --installroot=/var/lib/machines/f41 --use-host-config \
|
|
64
|
-
--setopt=install_weak_deps=False \
|
|
65
|
-
install \
|
|
66
|
-
passwd dnf fedora-release vim-minimal util-linux systemd NetworkManager
|
|
67
|
-
|
|
68
|
-
where --use-host-config re-uses host repositories.
|
|
69
|
-
|
|
70
|
-
Maybe consider 'machinectl'-managed containers (start/terminate/kill/reboot/etc.)
|
|
71
|
-
which are just repackaged systemd-nspawn@ services.
|
|
72
|
-
- Especially since there is no concept of "throw away disk snapshot with container exit",
|
|
73
|
-
we always need some copy/clone of the --installroot for each instance of the container,
|
|
74
|
-
so using ie. 'machinectl clone ...' would provide a nice interface for it.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
a = 123
|
atex-0.7.dist-info/RECORD
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
atex/__init__.py,sha256=LdX67gprtHYeAkjLhFPKzpc7ECv2rHxUbHKDGbGXO1c,517
|
|
2
|
-
atex/cli/__init__.py,sha256=erHv68SsybRbdgJ60013y9jVqY1ec-cb9T9ThPCJ_HY,2408
|
|
3
|
-
atex/cli/minitmt.py,sha256=h1jHRGR0nqc7ANVTjmB5aeLron2A04vpzGGMQJLBliY,5793
|
|
4
|
-
atex/cli/testingfarm.py,sha256=4oKWbkJwj0gs4_NnFP7w8fo2VvD5NpaPNG4HCeKm94I,6706
|
|
5
|
-
atex/connection/__init__.py,sha256=O7rJDwf438lX2YgM8uHCC9gkmvaLYt3jRvtDq_FSzME,4107
|
|
6
|
-
atex/connection/ssh.py,sha256=qVKeAY16pLGGS-d4TIF5A2DWWlNGR5qNlAmHo-jAIEc,13985
|
|
7
|
-
atex/minitmt/__init__.py,sha256=wCda0QXwxlxkH4PpY0lGlNWT96_pUj7taNNP0Mo6TQk,522
|
|
8
|
-
atex/minitmt/executor.py,sha256=zmFHo3M5hHDOzfZrXBPFSeqLmLK__q2KkT5A93ELEzY,13415
|
|
9
|
-
atex/minitmt/fmf.py,sha256=HAjQkfrDLQLW2ZBvgQmleVpL_tfNSP9bUuqRt9BdjS4,7559
|
|
10
|
-
atex/minitmt/scripts.py,sha256=WsLsQOmNxinpGUhOqqwN788zjcd5o79VC7I7UeGRwb8,5146
|
|
11
|
-
atex/minitmt/testcontrol.py,sha256=l670ou1oPr5q3RA4QNQ0IgSh3uYvuvzcUW67NLNVgCA,12209
|
|
12
|
-
atex/orchestrator/__init__.py,sha256=pMBBtev20KWAmQdth3RfuTwltXDm57CakDBpAsqyNYM,1592
|
|
13
|
-
atex/orchestrator/aggregator.py,sha256=pTL6Ru70sin5WOc-dl3-bNSEvuOfWq7YLtduwiLX9q8,5463
|
|
14
|
-
atex/provision/__init__.py,sha256=hJ7wbn6_LaCQVrIFilEcDA4z4LxglHYNEsxmizwr_OE,5573
|
|
15
|
-
atex/provision/libvirt/VM_PROVISION,sha256=7pkZ-ozgTyK4qNGC-E-HUznr4IhbosWSASbB72Gknl8,2664
|
|
16
|
-
atex/provision/libvirt/__init__.py,sha256=mAkGtciZsXdR9MVVrjm3OWNXZqTs_33-J1qAszFA0k4,768
|
|
17
|
-
atex/provision/libvirt/setup-libvirt.sh,sha256=CXrEFdrj8CSHXQZCd2RWuRvTmw7QYFTVhZeLuhhXooI,1855
|
|
18
|
-
atex/provision/nspawn/README,sha256=mIjOA6R5sM-1wGDm-7zBcEqax1NMSRzijwyeqVjGoqE,3127
|
|
19
|
-
atex/provision/podman/README,sha256=kgP3vcTfWW9gcQzmXnyucjgWbqjNqm_ZM--pnqNTXRg,1345
|
|
20
|
-
atex/provision/podman/host_container.sh,sha256=buCNz0BlsHY5I64sMSTGQHkvzEK0aeIhpGJXWCQVMXk,2283
|
|
21
|
-
atex/provision/testingfarm/__init__.py,sha256=l4T4VOUQWKYEQXPe0OtfJmuW-aGSiSQ2mdOf6fSjpjU,605
|
|
22
|
-
atex/provision/testingfarm/api.py,sha256=S-9m1D1CaG-1gEgh7oT1RkXPv2sfUw7Od7bW-Wv0uJQ,19262
|
|
23
|
-
atex/provision/testingfarm/foo.py,sha256=WfGpn_saQb5x9Svx-gfViyXPVRF_u-gUjagUwLBClM0,8
|
|
24
|
-
atex/util/__init__.py,sha256=PWU0STjLcQfe2pPaWKygRXQAudy8CNFdbnTGQ488mHs,1550
|
|
25
|
-
atex/util/dedent.py,sha256=SEuJMtLzqz3dQ7g7qyZzEJ9VYynVlk52tQCJY-FveXo,603
|
|
26
|
-
atex/util/log.py,sha256=oqJGXgHZkTgXVbD2-gzA2pqV3L8w0r_czYWIPfVz398,1776
|
|
27
|
-
atex/util/subprocess.py,sha256=IQT9QHe2kMaaO_XPSry-DwObYstGsq6_QdwdbhYDjko,1826
|
|
28
|
-
atex-0.7.dist-info/METADATA,sha256=VvPhdyVKdg_-kmR0y1WkClrYxw24yjoee3jGzkj6--g,5076
|
|
29
|
-
atex-0.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
30
|
-
atex-0.7.dist-info/entry_points.txt,sha256=pLqJdcfeyQTgup2h6dWb6SvkHhtOl-W5Eg9zV8moK0o,39
|
|
31
|
-
atex-0.7.dist-info/licenses/COPYING.txt,sha256=oEuj51jdmbXcCUy7pZ-KE0BNcJTR1okudRp5zQ0yWnU,670
|
|
32
|
-
atex-0.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|