atex 0.8__py3-none-any.whl → 0.9__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 +73 -23
- atex/cli/libvirt.py +127 -0
- atex/cli/testingfarm.py +12 -0
- atex/connection/__init__.py +13 -11
- atex/connection/podman.py +63 -0
- atex/connection/ssh.py +31 -33
- atex/executor/executor.py +131 -107
- atex/executor/reporter.py +66 -71
- atex/executor/scripts.py +9 -3
- atex/executor/testcontrol.py +43 -30
- atex/fmf.py +94 -74
- atex/orchestrator/__init__.py +3 -2
- atex/orchestrator/aggregator.py +63 -58
- atex/orchestrator/orchestrator.py +194 -133
- atex/provision/__init__.py +11 -11
- atex/provision/libvirt/__init__.py +2 -24
- atex/provision/libvirt/libvirt.py +465 -0
- atex/provision/libvirt/locking.py +168 -0
- atex/provision/libvirt/setup-libvirt.sh +21 -1
- atex/provision/podman/__init__.py +1 -0
- atex/provision/podman/podman.py +274 -0
- atex/provision/testingfarm/api.py +69 -26
- atex/provision/testingfarm/testingfarm.py +29 -31
- atex/util/libvirt.py +18 -0
- atex/util/log.py +23 -8
- atex/util/named_mapping.py +158 -0
- atex/util/threads.py +64 -20
- {atex-0.8.dist-info → atex-0.9.dist-info}/METADATA +27 -46
- atex-0.9.dist-info/RECORD +43 -0
- atex/provision/podman/README +0 -59
- atex/provision/podman/host_container.sh +0 -74
- atex-0.8.dist-info/RECORD +0 -37
- {atex-0.8.dist-info → atex-0.9.dist-info}/WHEEL +0 -0
- {atex-0.8.dist-info → atex-0.9.dist-info}/entry_points.txt +0 -0
- {atex-0.8.dist-info → atex-0.9.dist-info}/licenses/COPYING.txt +0 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Provides a namedtuple-inspired frozen mapping-backed data structure.
|
|
3
|
+
|
|
4
|
+
class MyMap(NamedMapping):
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
m = MyMap(a=123, b=456)
|
|
8
|
+
|
|
9
|
+
m["a"] # 123
|
|
10
|
+
m.a # 123
|
|
11
|
+
m["a"] = 9 # KeyError (is read-only)
|
|
12
|
+
m.a = 9 # AttributeError (is read-only)
|
|
13
|
+
|
|
14
|
+
Like namedtuple, you can specify required keys that always need to be given
|
|
15
|
+
to the constructor:
|
|
16
|
+
|
|
17
|
+
class MyMap(NamedMapping, required=("key1", "key2")):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
m = MyMap(a=123, b=456, key1=999) # KeyError (key2 not specified)
|
|
21
|
+
|
|
22
|
+
Similarly, you can specify defaults (for required or non-required keys),
|
|
23
|
+
as a dict, that are used if omitted from the constructor:
|
|
24
|
+
|
|
25
|
+
class MyMap(NamedMapping, defaults={"key": 678}):
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
m = MyMap() # will have m.key == 678
|
|
29
|
+
|
|
30
|
+
A class instance can unpack via ** with the entirety of its mapping contents:
|
|
31
|
+
|
|
32
|
+
m = MyMap(key2=456)
|
|
33
|
+
both = {'key1': 123, **m} # contains both keys
|
|
34
|
+
|
|
35
|
+
You can also chain (append to) required / default values through inheritance:
|
|
36
|
+
|
|
37
|
+
class MyMap(NamedMapping, required=("key1",), defaults={"key2": 234}):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
class AnotherMap(MyMap, required=("key3",))
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
m = AnotherMap() # KeyError (key1 and key3 are required)
|
|
44
|
+
|
|
45
|
+
isinstance(m, MyMap) # would be True
|
|
46
|
+
|
|
47
|
+
When instantiating, it is also possible to copy just the required keys from
|
|
48
|
+
another dict-like object (does not have to be a parent of the class):
|
|
49
|
+
|
|
50
|
+
class SmallMap(NamedMapping, required=("key1", "key2")):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
class BigMap(SmallMap, required=("key3", "key4")):
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
b = BigMap(key1=123, key2=456, key3=789, key4=0)
|
|
57
|
+
|
|
58
|
+
s = SmallMap._from(b) # will copy just key1 and key2
|
|
59
|
+
s = SmallMap._from(b, extra=555) # can pass extra **kwargs to __init__
|
|
60
|
+
s = SmallMap(**b) # will copy all keys
|
|
61
|
+
|
|
62
|
+
Note that this is a fairly basic implementation without __hash__, etc.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
import abc
|
|
66
|
+
import collections
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class _NamedMappingMeta(abc.ABCMeta):
|
|
70
|
+
def __new__(
|
|
71
|
+
metacls, name, bases, namespace, *, required=None, default=None, **kwargs, # noqa: N804
|
|
72
|
+
):
|
|
73
|
+
new_required = []
|
|
74
|
+
for base in bases:
|
|
75
|
+
new_required.extend(getattr(base, "_required", ()))
|
|
76
|
+
if required:
|
|
77
|
+
new_required.extend(required)
|
|
78
|
+
namespace["_required"] = tuple(set(new_required))
|
|
79
|
+
|
|
80
|
+
new_default = {}
|
|
81
|
+
for base in bases:
|
|
82
|
+
new_default.update(getattr(base, "_default", {}))
|
|
83
|
+
if default:
|
|
84
|
+
new_default.update(default)
|
|
85
|
+
namespace["_default"] = new_default
|
|
86
|
+
|
|
87
|
+
return super().__new__(metacls, name, bases, namespace, **kwargs)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class NamedMapping(collections.abc.Mapping, metaclass=_NamedMappingMeta):
|
|
91
|
+
__slots__ = ("_data",)
|
|
92
|
+
|
|
93
|
+
def __init__(self, **keys):
|
|
94
|
+
data = {}
|
|
95
|
+
if hasattr(self, "_default"):
|
|
96
|
+
data.update(self._default)
|
|
97
|
+
data.update(keys)
|
|
98
|
+
if hasattr(self, "_required"):
|
|
99
|
+
for key in self._required:
|
|
100
|
+
if key not in data:
|
|
101
|
+
raise KeyError(f"'{self.__class__.__name__}' requires key '{key}'")
|
|
102
|
+
object.__setattr__(self, "_data", data)
|
|
103
|
+
|
|
104
|
+
@classmethod
|
|
105
|
+
def _from(cls, foreign, **keys):
|
|
106
|
+
"""
|
|
107
|
+
(keys is like for __init__)
|
|
108
|
+
"""
|
|
109
|
+
foreign_data = {}
|
|
110
|
+
if hasattr(cls, "_required"):
|
|
111
|
+
for key in cls._required:
|
|
112
|
+
if key in foreign:
|
|
113
|
+
foreign_data[key] = foreign[key]
|
|
114
|
+
foreign_data.update(keys)
|
|
115
|
+
return cls(**foreign_data)
|
|
116
|
+
|
|
117
|
+
def __getattr__(self, item):
|
|
118
|
+
if item in ("_data", "_required", "_default"):
|
|
119
|
+
return super().__getattr__(item)
|
|
120
|
+
try:
|
|
121
|
+
return self._data[item]
|
|
122
|
+
except KeyError:
|
|
123
|
+
raise AttributeError(
|
|
124
|
+
f"'{self.__class__.__name__}' object has no attribute '{item}'",
|
|
125
|
+
name=item,
|
|
126
|
+
) from None
|
|
127
|
+
|
|
128
|
+
def __setattr__(self, name, value):
|
|
129
|
+
raise AttributeError(
|
|
130
|
+
f"'{self}' is read-only, cannot set '{name}'",
|
|
131
|
+
name=name,
|
|
132
|
+
obj=value,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def __getitem__(self, key):
|
|
136
|
+
return self._data[key]
|
|
137
|
+
|
|
138
|
+
def __setitem__(self, key, value):
|
|
139
|
+
raise ValueError(f"'{self}' is read-only, cannot set '{key}'")
|
|
140
|
+
|
|
141
|
+
def __delitem__(self, key):
|
|
142
|
+
raise ValueError(f"'{self}' is read-only, cannot delete '{key}'")
|
|
143
|
+
|
|
144
|
+
def __contains__(self, key):
|
|
145
|
+
return key in self._data
|
|
146
|
+
|
|
147
|
+
def __iter__(self):
|
|
148
|
+
return iter(self._data)
|
|
149
|
+
|
|
150
|
+
def __len__(self):
|
|
151
|
+
return len(self._data)
|
|
152
|
+
|
|
153
|
+
def __repr__(self):
|
|
154
|
+
return (
|
|
155
|
+
f"{self.__class__.__name__}("
|
|
156
|
+
+ ", ".join((f"{k}={repr(v)}" for k,v in self._data.items()))
|
|
157
|
+
+ ")"
|
|
158
|
+
)
|
atex/util/threads.py
CHANGED
|
@@ -1,48 +1,85 @@
|
|
|
1
|
-
import collections
|
|
2
1
|
import queue
|
|
3
2
|
import threading
|
|
4
3
|
|
|
4
|
+
from .named_mapping import NamedMapping
|
|
5
|
+
|
|
5
6
|
# TODO: documentation; this is like concurrent.futures, but with daemon=True support
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class ThreadQueue:
|
|
9
|
-
ThreadReturn
|
|
10
|
+
class ThreadReturn(NamedMapping, required=("thread", "returned", "exception")):
|
|
11
|
+
pass
|
|
12
|
+
|
|
10
13
|
Empty = queue.Empty
|
|
11
14
|
|
|
12
15
|
def __init__(self, daemon=False):
|
|
16
|
+
self.lock = threading.RLock()
|
|
13
17
|
self.queue = queue.SimpleQueue()
|
|
14
18
|
self.daemon = daemon
|
|
15
19
|
self.threads = set()
|
|
16
20
|
|
|
17
|
-
def _wrapper(self, func,
|
|
21
|
+
def _wrapper(self, func, func_args, func_kwargs, **user_kwargs):
|
|
18
22
|
current_thread = threading.current_thread()
|
|
19
23
|
try:
|
|
20
|
-
ret = func(*
|
|
21
|
-
result = self.ThreadReturn(
|
|
24
|
+
ret = func(*func_args, **func_kwargs)
|
|
25
|
+
result = self.ThreadReturn(
|
|
26
|
+
thread=current_thread,
|
|
27
|
+
returned=ret,
|
|
28
|
+
exception=None,
|
|
29
|
+
**user_kwargs,
|
|
30
|
+
)
|
|
22
31
|
except Exception as e:
|
|
23
|
-
result = self.ThreadReturn(
|
|
32
|
+
result = self.ThreadReturn(
|
|
33
|
+
thread=current_thread,
|
|
34
|
+
returned=None,
|
|
35
|
+
exception=e,
|
|
36
|
+
**user_kwargs,
|
|
37
|
+
)
|
|
24
38
|
self.queue.put(result)
|
|
25
39
|
|
|
26
|
-
def start_thread(self, target,
|
|
27
|
-
|
|
28
|
-
|
|
40
|
+
def start_thread(self, target, target_args=None, target_kwargs=None, **user_kwargs):
|
|
41
|
+
"""
|
|
42
|
+
Start a new thread and call 'target' as a callable inside it, passing it
|
|
43
|
+
'target_args' as arguments and 'target_kwargs' as keyword arguments.
|
|
44
|
+
|
|
45
|
+
Any additional 'user_kwargs' specified are NOT passed to the callable,
|
|
46
|
+
but instead become part of the ThreadReturn namespace returned by the
|
|
47
|
+
.get_raw() method.
|
|
48
|
+
"""
|
|
29
49
|
t = threading.Thread(
|
|
30
50
|
target=self._wrapper,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
kwargs=kwargs,
|
|
51
|
+
args=(target, target_args or (), target_kwargs or {}),
|
|
52
|
+
kwargs=user_kwargs,
|
|
34
53
|
daemon=self.daemon,
|
|
35
54
|
)
|
|
55
|
+
with self.lock:
|
|
56
|
+
self.threads.add(t)
|
|
36
57
|
t.start()
|
|
37
|
-
|
|
58
|
+
|
|
59
|
+
def get_raw(self, block=True, timeout=None):
|
|
60
|
+
"""
|
|
61
|
+
Wait for and return the next available ThreadReturn instance on the
|
|
62
|
+
queue, as enqueued by a finished callable started by the .start_thread()
|
|
63
|
+
method.
|
|
64
|
+
"""
|
|
65
|
+
with self.lock:
|
|
66
|
+
if block and timeout is None and not self.threads:
|
|
67
|
+
raise AssertionError("no threads are running, would block forever")
|
|
68
|
+
treturn = self.queue.get(block=block, timeout=timeout)
|
|
69
|
+
with self.lock:
|
|
70
|
+
self.threads.remove(treturn.thread)
|
|
71
|
+
return treturn
|
|
38
72
|
|
|
39
73
|
# get one return value from any thread's function, like .as_completed()
|
|
40
74
|
# or concurrent.futures.FIRST_COMPLETED
|
|
41
75
|
def get(self, block=True, timeout=None):
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
76
|
+
"""
|
|
77
|
+
Wait for and return the next available return value of a callable
|
|
78
|
+
enqueued via the .start_thread() method.
|
|
79
|
+
|
|
80
|
+
If the callable raised an exception, the exception is re-raised here.
|
|
81
|
+
"""
|
|
82
|
+
treturn = self.get_raw(block, timeout)
|
|
46
83
|
if treturn.exception is not None:
|
|
47
84
|
raise treturn.exception
|
|
48
85
|
else:
|
|
@@ -50,6 +87,13 @@ class ThreadQueue:
|
|
|
50
87
|
|
|
51
88
|
# wait for all threads to finish (ignoring queue contents)
|
|
52
89
|
def join(self):
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
90
|
+
"""
|
|
91
|
+
Wait for all threads to finish, ignoring the state of the queue.
|
|
92
|
+
"""
|
|
93
|
+
while True:
|
|
94
|
+
with self.lock:
|
|
95
|
+
try:
|
|
96
|
+
thread = self.threads.pop()
|
|
97
|
+
except KeyError:
|
|
98
|
+
break
|
|
99
|
+
thread.join()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: atex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9
|
|
4
4
|
Summary: Ad-hoc Test EXecutor
|
|
5
5
|
Project-URL: Homepage, https://github.com/RHSecurityCompliance/atex
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -10,6 +10,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
10
10
|
Classifier: Topic :: Software Development :: Testing
|
|
11
11
|
Requires-Python: >=3.11
|
|
12
12
|
Requires-Dist: fmf>=1.6
|
|
13
|
+
Requires-Dist: pyyaml
|
|
13
14
|
Requires-Dist: urllib3<3,>=2
|
|
14
15
|
Description-Content-Type: text/markdown
|
|
15
16
|
|
|
@@ -45,6 +46,30 @@ BREAK. DO NOT USE IT (for now).
|
|
|
45
46
|
Unless specified otherwise, any content within this repository is distributed
|
|
46
47
|
under the GNU GPLv3 license, see the [COPYING.txt](COPYING.txt) file for more.
|
|
47
48
|
|
|
49
|
+
## Testing this project
|
|
50
|
+
|
|
51
|
+
There are some limited sanity tests provided via `pytest`, although:
|
|
52
|
+
|
|
53
|
+
* Some require additional variables (ie. Testing Farm) and will ERROR
|
|
54
|
+
without them.
|
|
55
|
+
* Some take a long time (ie. Testing Farm) due to system provisioning
|
|
56
|
+
taking a long time, so install `pytest-xdist` and run with a large `-n`.
|
|
57
|
+
|
|
58
|
+
Currently, the recommended approach is to split the execution:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
# synchronously, because podman CLI has concurrency issues
|
|
62
|
+
pytest tests/provision/test_podman.py
|
|
63
|
+
|
|
64
|
+
# in parallel, because provisioning takes a long time
|
|
65
|
+
export TESTING_FARM_API_TOKEN=...
|
|
66
|
+
export TESTING_FARM_COMPOSE=...
|
|
67
|
+
pytest -n 20 tests/provision/test_podman.py
|
|
68
|
+
|
|
69
|
+
# fast enough for synchronous execution
|
|
70
|
+
pytest tests/fmf
|
|
71
|
+
```
|
|
72
|
+
|
|
48
73
|
## Parallelism and cleanup
|
|
49
74
|
|
|
50
75
|
There are effectively 3 methods of running things in parallel in Python:
|
|
@@ -149,49 +174,5 @@ TODO: codestyle from contest
|
|
|
149
174
|
- the whole point is to make usecase-targeted easy-to-use tools that don't
|
|
150
175
|
intimidate users with 1 KB long command line, and runcontest is a nice example
|
|
151
176
|
|
|
152
|
-
- TL;DR - use a modular pythonic approach, not a
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
- Orchestrator with
|
|
156
|
-
- add_provisioner(<class>, max_workers=1) # will instantiate <class> at most max_workers at a time
|
|
157
|
-
- algo
|
|
158
|
-
- for all provisioner classes, spawns classes*max_workers as new Threads
|
|
159
|
-
- waits for any .reserve() to return
|
|
160
|
-
- creates a new Thread for minitmt, gives it p.get_ssh() details
|
|
161
|
-
- minitmt will
|
|
162
|
-
- establish a SSHConn
|
|
163
|
-
- install test deps, copy test repo over, prepare socket dir on SUT, etc.
|
|
164
|
-
- run the test in the background as
|
|
165
|
-
f=os.open('some/test/log', os.WRONLY); subprocess.Popen(..., stdout=f, stderr=f, stdin=subprocess.DEVNULL)
|
|
166
|
-
- read/process Unix sock results in the foreground, non-blocking,
|
|
167
|
-
probably calling some Orchestrator-provided function to store results persistently
|
|
168
|
-
- regularly check Popen proc status, re-accept UNIX sock connection, etc., etc.
|
|
169
|
-
- minitmt also has some Thread-independent way to .cancel(), killing the proc, closing SSHConn, etc.
|
|
170
|
-
|
|
171
|
-
- while waiting for minitmt Threads to finish, to re-assign existing Provisioner instances
|
|
172
|
-
to new minitmt Threads, .. Orchestrator uses some logic to select, which TestRun
|
|
173
|
-
would be ideal to run next
|
|
174
|
-
- TestRun probably has some "fitness" function that returns some priority number
|
|
175
|
-
when given a Provisioner instance (?) ...
|
|
176
|
-
- something from minitmt would also have access to the Provisioner instance
|
|
177
|
-
- the idea is to allow some logic to set "hey I set up nested VM snapshot on this thing"
|
|
178
|
-
on the Provisioner instance, and if another /hardening/oscap TestRun finds
|
|
179
|
-
a Provisioner instance like that, it would return high priority
|
|
180
|
-
- ...
|
|
181
|
-
- similar to "fitness" like function, we need some "applicability" function
|
|
182
|
-
- if TestRun is mixed to RHEL-9 && x86_64, we need it to return True
|
|
183
|
-
for a Provisioner instance that provides RHEL-9 and x86_64, but False otherwise
|
|
184
|
-
|
|
185
|
-
- basically Orchestrator has
|
|
186
|
-
- .add_provisioner()
|
|
187
|
-
- .run_test() # called with an exclusively-borrowed Provisioner instance
|
|
188
|
-
- if Provisioner is_alive()==False after .run_test(), instantiate a new one from the same inst.__class__
|
|
189
|
-
- if test failed and reruns > 0, try run_test() again (or maybe re-queue the test)
|
|
190
|
-
- .output_result() # called by run_test() to persistently log a test result
|
|
191
|
-
- .applicable() # return True if a passed TestRun is meant for a passed Platform (Provisioner?)
|
|
192
|
-
- if no TestRun returns True, the Provisioner is .release()d because we don't need it anymore
|
|
193
|
-
- .fitness() # return -inf / 0 / +inf with how much should a passed TestRun run on a Provisioner
|
|
194
|
-
- MAYBE combine applicable() and fitness() into one function, next_test() ?
|
|
195
|
-
- given the free Provisioner and a list of TestRuns, select which should run next on the Provisioner
|
|
196
|
-
- if none is chosen, .release() the Provisioner without replacement, continue
|
|
177
|
+
- TL;DR - use a modular pythonic approach, not a gluetool-style long CLI
|
|
197
178
|
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
atex/__init__.py,sha256=LdX67gprtHYeAkjLhFPKzpc7ECv2rHxUbHKDGbGXO1c,517
|
|
2
|
+
atex/fmf.py,sha256=gkJXIaRO7_KvwJR-V6Tc1NVn4a9Hq7hoBLQLhxYIdbg,8834
|
|
3
|
+
atex/cli/__init__.py,sha256=erHv68SsybRbdgJ60013y9jVqY1ec-cb9T9ThPCJ_HY,2408
|
|
4
|
+
atex/cli/fmf.py,sha256=HfbTgFbCwK4Nuyq6vtGutcq_4-4kj-tmoqzXUn3AYtY,3573
|
|
5
|
+
atex/cli/libvirt.py,sha256=vGcan9u0Nor_DaEjgOj3Yzjtlq4WNVLr5oYMb8zFb6M,3743
|
|
6
|
+
atex/cli/testingfarm.py,sha256=DHMupwU3AjbkwkzvzdhlxtsuBnxVaba5i8T8g9sKRug,7569
|
|
7
|
+
atex/connection/__init__.py,sha256=dj8ZBcEspom7Z_UjecfLGBRNvLZ3dyGR9q19i_B4xpY,3880
|
|
8
|
+
atex/connection/podman.py,sha256=JUsi4jOkNFGsEHCqUQF003R2yRfJPsMnUSBEZM3H3Kk,1953
|
|
9
|
+
atex/connection/ssh.py,sha256=c8v01mj0pf6VU8z2cLLCvPGPmGUZp8wQsTG441k3Qy4,13674
|
|
10
|
+
atex/executor/__init__.py,sha256=XCfhi7QDELjey7N1uzhMjc46Kp1Jsd5bOCf52I27SCE,85
|
|
11
|
+
atex/executor/duration.py,sha256=x06sItKOZi6XA8KszQwZGpIb1Z_L-HWqIwZKo2SDo0s,1759
|
|
12
|
+
atex/executor/executor.py,sha256=OcKBXQ77OktmzPWAbdCb81Yj44Jr4SEkwW_CxdEhFP4,15674
|
|
13
|
+
atex/executor/reporter.py,sha256=MceFmHFt0bTEClBZbRI1WnFbfMhR0e1noOzcu7gjKuQ,3403
|
|
14
|
+
atex/executor/scripts.py,sha256=6xqP-m_9UCVb3z0tCWIMczimz_cTeCIJucSp0AGYsRw,5567
|
|
15
|
+
atex/executor/testcontrol.py,sha256=O4V4jRx7lZv-El1i9jkPha-aWzjq_8m6loK7sNGVD14,12695
|
|
16
|
+
atex/orchestrator/__init__.py,sha256=GSHtWoZjJrLiatjUHsMAkYB8VebSQD9ApQw0ymCrGTQ,212
|
|
17
|
+
atex/orchestrator/aggregator.py,sha256=naypN8T42iPau_4eLeXak-INR1_vsYhP_QSp3xLM_FA,3837
|
|
18
|
+
atex/orchestrator/orchestrator.py,sha256=qAwka7MqpAWA6mLgsMl1m1HpQP2mERcaAASNvJwgfTM,14570
|
|
19
|
+
atex/provision/__init__.py,sha256=Pj_JnDaJA8DzKRGmLcc2JiwPsuQc2TYlE-hDR56SwTY,4083
|
|
20
|
+
atex/provision/libvirt/VM_PROVISION,sha256=7pkZ-ozgTyK4qNGC-E-HUznr4IhbosWSASbB72Gknl8,2664
|
|
21
|
+
atex/provision/libvirt/__init__.py,sha256=pKG5IpZSC2IHs5wL2ecQx_fd9AzAXEbZmDzA7RyZsfM,119
|
|
22
|
+
atex/provision/libvirt/libvirt.py,sha256=7wux-zhQoNODJoPB4iMqPlGEJe7rEEZeLSOtMU3TB0M,18160
|
|
23
|
+
atex/provision/libvirt/locking.py,sha256=e9zbtR0uzJZBAzucfhs9VQaDCx-jp0pWJi1pb7JPK4w,5630
|
|
24
|
+
atex/provision/libvirt/setup-libvirt.sh,sha256=oCMy9SCnbC_QuAzO2sFwvB5ui1kMQ6uviHsgdXyoFXc,2428
|
|
25
|
+
atex/provision/podman/__init__.py,sha256=V9miIDQV-CyDVcbf2-1qtDbXDhhZzJza5oXas-JxI8o,66
|
|
26
|
+
atex/provision/podman/podman.py,sha256=FJK4uLDuHBj8_3Wlg4a-4JgGIRGyLX4v7UVcxEorHbA,9578
|
|
27
|
+
atex/provision/testingfarm/__init__.py,sha256=kZncgLGdRCR4FMaRQr2GTwJ8vjlA-24ri8JO2ueZJuw,113
|
|
28
|
+
atex/provision/testingfarm/api.py,sha256=9xeoIN28KumJyMUEUoyEUZcIxVH9G34Wecyy3bp2Cmg,21612
|
|
29
|
+
atex/provision/testingfarm/testingfarm.py,sha256=-Q33FAretSGPWdmNJUywJZVfXYGtsAfLP5uqRZTGfQQ,8631
|
|
30
|
+
atex/util/__init__.py,sha256=cWHFbtQ4mDlKe6lXyPDWRmWJOTcHDGfVuW_-GYa8hB0,1473
|
|
31
|
+
atex/util/dedent.py,sha256=SEuJMtLzqz3dQ7g7qyZzEJ9VYynVlk52tQCJY-FveXo,603
|
|
32
|
+
atex/util/libvirt.py,sha256=kDZmT6xLYEZkQNLZY98gJ2M48DDWXxHF8rQY9PnjB3U,660
|
|
33
|
+
atex/util/log.py,sha256=70f6YiF2RwNUfE5BXZMSQonzfI_uxQoD7_S4bRD_Btw,2466
|
|
34
|
+
atex/util/named_mapping.py,sha256=UBMe9TetjV-DGPhjYjJ42YtC40FVPKAAEROXl9MA5fo,4700
|
|
35
|
+
atex/util/path.py,sha256=x-kXqiWCVodfZWbEwtC5A8LFvutpDIPYv2m0boZSlXU,504
|
|
36
|
+
atex/util/ssh_keygen.py,sha256=9yuSl2yBV7pG3Qfsf9tossVC00nbIUrAeLdbwTykpjk,384
|
|
37
|
+
atex/util/subprocess.py,sha256=IQT9QHe2kMaaO_XPSry-DwObYstGsq6_QdwdbhYDjko,1826
|
|
38
|
+
atex/util/threads.py,sha256=46-5nV-qJqi1YZ4qEshmZXGUxr8j9_9xT9eEpkjRr5I,3355
|
|
39
|
+
atex-0.9.dist-info/METADATA,sha256=iLGFHVxeK9KWrPaRdL4hkbNkb-OgJkeohHhxYz90tAI,6857
|
|
40
|
+
atex-0.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
41
|
+
atex-0.9.dist-info/entry_points.txt,sha256=pLqJdcfeyQTgup2h6dWb6SvkHhtOl-W5Eg9zV8moK0o,39
|
|
42
|
+
atex-0.9.dist-info/licenses/COPYING.txt,sha256=oEuj51jdmbXcCUy7pZ-KE0BNcJTR1okudRp5zQ0yWnU,670
|
|
43
|
+
atex-0.9.dist-info/RECORD,,
|
atex/provision/podman/README
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
making a podman image from the currently installed OS:
|
|
3
|
-
|
|
4
|
-
1) dnf install into a separate installroot
|
|
5
|
-
|
|
6
|
-
dnf
|
|
7
|
-
--installroot=$INSTALLROOT \
|
|
8
|
-
--setopt=install_weak_deps=False \
|
|
9
|
-
--setopt=tsflags=nodocs \
|
|
10
|
-
-y groupinstall minimal-environment
|
|
11
|
-
|
|
12
|
-
as root (doesn't work well with unshare, maybe could work via bwrap (bubblewrap))
|
|
13
|
-
|
|
14
|
-
maybe the unprivileged solution is pulling image from hub + installing @minimal-environment
|
|
15
|
-
into it (perhaps via podman build)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
2) post process it
|
|
19
|
-
|
|
20
|
-
echo -n > "$INSTALLROOT/etc/machine-id"
|
|
21
|
-
echo container > "$INSTALLROOT/etc/hostname"
|
|
22
|
-
|
|
23
|
-
rm -rf "$INSTALLROOT/etc/yum.repos.d"
|
|
24
|
-
cp -f /etc/yum.repos.d/* "$INSTALLROOT/etc/yum.repos.d/."
|
|
25
|
-
cp -f /etc/pki/rpm-gpg/* "$INSTALLROOT/etc/pki/rpm-gpg/."
|
|
26
|
-
|
|
27
|
-
echo install_weak_deps=False >> "$INSTALLROOT/etc/dnf/dnf.conf"
|
|
28
|
-
echo tsflags=nodocs >> "$INSTALLROOT/etc/dnf/dnf.conf"
|
|
29
|
-
|
|
30
|
-
ln -sf \
|
|
31
|
-
/usr/lib/systemd/system/multi-user.target \
|
|
32
|
-
"$INSTALLROOT/etc/systemd/system/default.target"
|
|
33
|
-
|
|
34
|
-
# disable auditd
|
|
35
|
-
# disable other services
|
|
36
|
-
# set root password
|
|
37
|
-
|
|
38
|
-
dnf clean all --installroot="$INSTALLROOT"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
3) pack it
|
|
42
|
-
|
|
43
|
-
tar --xattrs -C "$INSTALLROOT" -cvf tarball.tar .
|
|
44
|
-
|
|
45
|
-
rm -rf "$INSTALLROOT"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
4) import it to podman
|
|
49
|
-
|
|
50
|
-
podman import --change 'CMD ["/sbin/init"]' tarball.tar my-image-name
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
5) run it
|
|
54
|
-
|
|
55
|
-
podman {run,create} --systemd=always --cgroups=split ...
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
------------------------------
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
if [[ $# -lt 1 ]]; then
|
|
4
|
-
echo "usage: $0 <podman-image-name>" >&2
|
|
5
|
-
exit 1
|
|
6
|
-
fi
|
|
7
|
-
image_name="$1"
|
|
8
|
-
|
|
9
|
-
set -e -x
|
|
10
|
-
|
|
11
|
-
tmpdir=$(mktemp -d -p /var/tmp)
|
|
12
|
-
trap "rm -rf '$tmpdir'" EXIT
|
|
13
|
-
|
|
14
|
-
installroot="$tmpdir/root"
|
|
15
|
-
|
|
16
|
-
dnf \
|
|
17
|
-
--installroot="$installroot" \
|
|
18
|
-
--setopt=install_weak_deps=False \
|
|
19
|
-
--setopt=tsflags=nodocs \
|
|
20
|
-
-q -y groupinstall minimal-environment
|
|
21
|
-
|
|
22
|
-
echo -n > "$installroot/etc/machine-id"
|
|
23
|
-
#echo container > "$installroot/etc/hostname"
|
|
24
|
-
|
|
25
|
-
cp -f /etc/yum.repos.d/* "$installroot/etc/yum.repos.d/."
|
|
26
|
-
cp -f /etc/pki/rpm-gpg/* "$installroot/etc/pki/rpm-gpg/."
|
|
27
|
-
|
|
28
|
-
echo install_weak_deps=False >> "$installroot/etc/dnf/dnf.conf"
|
|
29
|
-
echo tsflags=nodocs >> "$installroot/etc/dnf/dnf.conf"
|
|
30
|
-
|
|
31
|
-
ln -sf \
|
|
32
|
-
/usr/lib/systemd/system/multi-user.target \
|
|
33
|
-
"$installroot/etc/systemd/system/default.target"
|
|
34
|
-
|
|
35
|
-
systemctl --root="$installroot" disable \
|
|
36
|
-
auditd.service crond.service rhsmcertd.service sshd.service
|
|
37
|
-
|
|
38
|
-
#encrypted=$(openssl passwd -6 somepass)
|
|
39
|
-
#usermod --root="$installroot" --password "$encrypted" root
|
|
40
|
-
|
|
41
|
-
dnf clean packages --installroot="$installroot"
|
|
42
|
-
|
|
43
|
-
tar --xattrs -C "$installroot" -cf "$tmpdir/packed.tar" .
|
|
44
|
-
|
|
45
|
-
rm -rf "$installroot"
|
|
46
|
-
|
|
47
|
-
podman import \
|
|
48
|
-
--change 'CMD ["/sbin/init"]' \
|
|
49
|
-
"$tmpdir/packed.tar" "$image_name"
|
|
50
|
-
|
|
51
|
-
# start as
|
|
52
|
-
# podmn {run,create} --systemd=always --cgroups=split --device /dev/kvm ...
|
|
53
|
-
#
|
|
54
|
-
# podman run -t -i \
|
|
55
|
-
# --systemd=always --cgroups=split \
|
|
56
|
-
# --device /dev/kvm \
|
|
57
|
-
# --network=bridge \
|
|
58
|
-
# --cap-add NET_ADMIN --cap-add NET_RAW --cap-add SYS_MODULE \
|
|
59
|
-
# --mount type=bind,src=/lib/modules,dst=/lib/modules,ro \
|
|
60
|
-
# --mount type=bind,src=/proc/sys/net,dst=/proc/sys/net,rw \
|
|
61
|
-
# my_container
|
|
62
|
-
#
|
|
63
|
-
# as unprivileged user:
|
|
64
|
-
# podman run -t -i \
|
|
65
|
-
# --systemd=always --cgroups=split --network=bridge --privileged \
|
|
66
|
-
# my_container
|
|
67
|
-
#
|
|
68
|
-
# container setup:
|
|
69
|
-
# dnf -y install libvirt-daemon qemu-kvm libvirt-client libvirt-daemon-driver-qemu virt-install libvirt-daemon-driver-storage libvirt-daemon-config-network
|
|
70
|
-
# echo $'user = "root"\ngroup = "root"\nremember_owner = 0' >> /etc/libvirt/qemu.conf
|
|
71
|
-
# systemctl start virtqemud.socket virtstoraged.socket virtnetworkd.socket
|
|
72
|
-
# virsh net-start default
|
|
73
|
-
# virt-install --install fedora40 --disk /var/lib/libvirt/images/foo.qcow2,size=20 --console pty --check disk_size=off --unattended --graphics none
|
|
74
|
-
|
atex-0.8.dist-info/RECORD
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
atex/__init__.py,sha256=LdX67gprtHYeAkjLhFPKzpc7ECv2rHxUbHKDGbGXO1c,517
|
|
2
|
-
atex/fmf.py,sha256=ofbrJx2362qHAxERS-WulK4TMpbp0C4HQ-Js917Ll9w,7871
|
|
3
|
-
atex/cli/__init__.py,sha256=erHv68SsybRbdgJ60013y9jVqY1ec-cb9T9ThPCJ_HY,2408
|
|
4
|
-
atex/cli/fmf.py,sha256=5DbA-3rfbFZ41fJ5z7Tiz5FmuZhXNC7gRAQfIGX7pXc,2516
|
|
5
|
-
atex/cli/testingfarm.py,sha256=wdN26TE9jZ0ozet-JBQQgIcRi0WIV3u_i-7_YYi_SUg,7248
|
|
6
|
-
atex/connection/__init__.py,sha256=xFwGOvlFez1lIt1AD6WXgEEIbsF22pSpQFv41GEAGAI,3798
|
|
7
|
-
atex/connection/ssh.py,sha256=vrrSfVdQoz5kWiZbiPuM8KGneMl2Tlb0VeJIHTFSSYs,13626
|
|
8
|
-
atex/executor/__init__.py,sha256=XCfhi7QDELjey7N1uzhMjc46Kp1Jsd5bOCf52I27SCE,85
|
|
9
|
-
atex/executor/duration.py,sha256=x06sItKOZi6XA8KszQwZGpIb1Z_L-HWqIwZKo2SDo0s,1759
|
|
10
|
-
atex/executor/executor.py,sha256=QYYSlEfBZIm95NhM1gwd2ROeshSAavYu2DP_4TTHlQs,14770
|
|
11
|
-
atex/executor/reporter.py,sha256=nW_Uls3R4Ev80a2ZNJl3nxAYrcYhXk5Cy9nAUMlYPrc,3326
|
|
12
|
-
atex/executor/scripts.py,sha256=yE4Lbfu-TPkBcB5t15-t-tF79H8pBJWbWP6MKRSvKsw,5356
|
|
13
|
-
atex/executor/testcontrol.py,sha256=-rfihfE6kryIGurFrHBPSS8ANaIJkzX-zfpOO8To-9o,12204
|
|
14
|
-
atex/orchestrator/__init__.py,sha256=eF-6ix5rFEu85fBFzgSdTYau7bNTkIQndAU7QqeI-FA,105
|
|
15
|
-
atex/orchestrator/aggregator.py,sha256=5-8nHVeW6kwImoEYOsQqsx6UBdbKc5xuj6qlg7dtOF8,3642
|
|
16
|
-
atex/orchestrator/orchestrator.py,sha256=tQu_d8_9y3rOLHskb694NJKNvxplQWAZ2R452Sy3AXw,12056
|
|
17
|
-
atex/provision/__init__.py,sha256=2d_hRVPxXF5BVbQ_Gn1OR-F2xuqRn8O0yyVbvSrtTIg,4043
|
|
18
|
-
atex/provision/libvirt/VM_PROVISION,sha256=7pkZ-ozgTyK4qNGC-E-HUznr4IhbosWSASbB72Gknl8,2664
|
|
19
|
-
atex/provision/libvirt/__init__.py,sha256=mAkGtciZsXdR9MVVrjm3OWNXZqTs_33-J1qAszFA0k4,768
|
|
20
|
-
atex/provision/libvirt/setup-libvirt.sh,sha256=CXrEFdrj8CSHXQZCd2RWuRvTmw7QYFTVhZeLuhhXooI,1855
|
|
21
|
-
atex/provision/podman/README,sha256=kgP3vcTfWW9gcQzmXnyucjgWbqjNqm_ZM--pnqNTXRg,1345
|
|
22
|
-
atex/provision/podman/host_container.sh,sha256=buCNz0BlsHY5I64sMSTGQHkvzEK0aeIhpGJXWCQVMXk,2283
|
|
23
|
-
atex/provision/testingfarm/__init__.py,sha256=kZncgLGdRCR4FMaRQr2GTwJ8vjlA-24ri8JO2ueZJuw,113
|
|
24
|
-
atex/provision/testingfarm/api.py,sha256=jiEJhYxMTzRihayceHcnDnGKNZJisYWn2o_TAdCI2Xo,19943
|
|
25
|
-
atex/provision/testingfarm/testingfarm.py,sha256=wp8W3bwOmQdO-UUOdqu_JLtOZTGaNg-wERFfLySwZmI,8587
|
|
26
|
-
atex/util/__init__.py,sha256=cWHFbtQ4mDlKe6lXyPDWRmWJOTcHDGfVuW_-GYa8hB0,1473
|
|
27
|
-
atex/util/dedent.py,sha256=SEuJMtLzqz3dQ7g7qyZzEJ9VYynVlk52tQCJY-FveXo,603
|
|
28
|
-
atex/util/log.py,sha256=KZkuw4jl8YTUOHZ4wNBrfDeg16VpLa82-IZYFHfqwgk,1995
|
|
29
|
-
atex/util/path.py,sha256=x-kXqiWCVodfZWbEwtC5A8LFvutpDIPYv2m0boZSlXU,504
|
|
30
|
-
atex/util/ssh_keygen.py,sha256=9yuSl2yBV7pG3Qfsf9tossVC00nbIUrAeLdbwTykpjk,384
|
|
31
|
-
atex/util/subprocess.py,sha256=IQT9QHe2kMaaO_XPSry-DwObYstGsq6_QdwdbhYDjko,1826
|
|
32
|
-
atex/util/threads.py,sha256=bezDIEIMcQinmG7f5E2K6_mHJQOlwx7W3I9CKkCYAYA,1830
|
|
33
|
-
atex-0.8.dist-info/METADATA,sha256=dvXW146ZvIfyzqPqGbKmhTNScLTZM7C5K0FLrGNGIJ0,8981
|
|
34
|
-
atex-0.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
35
|
-
atex-0.8.dist-info/entry_points.txt,sha256=pLqJdcfeyQTgup2h6dWb6SvkHhtOl-W5Eg9zV8moK0o,39
|
|
36
|
-
atex-0.8.dist-info/licenses/COPYING.txt,sha256=oEuj51jdmbXcCUy7pZ-KE0BNcJTR1okudRp5zQ0yWnU,670
|
|
37
|
-
atex-0.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|