fix: preserve guest network identities and refresh session profiles
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check real NIC identities on a bridge isolated in user/net/mount namespaces."""
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
from hydra.profiles import Profile
|
||||
from hydra.runtime import Runtime
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--namespace-root', type=Path, help=argparse.SUPPRESS)
|
||||
args = parser.parse_args()
|
||||
if args.namespace_root is None:
|
||||
root = Path(tempfile.mkdtemp(prefix='hydra-network-', dir='/tmp'))
|
||||
print('Evidence: ' + str(root), flush=True)
|
||||
(root / 'parent-namespaces.json').write_text(json.dumps({name: str(Path('/proc/self/ns', name).readlink())
|
||||
for name in ('user', 'net', 'mnt')}))
|
||||
# All bridge and /etc changes below occur after this explicit namespace
|
||||
# boundary. The inherited host bridge configuration is never modified.
|
||||
subprocess.run(['unshare', '-Urnm', '--', str(Path(sys.executable).resolve()),
|
||||
str(Path(__file__).resolve()), '--namespace-root', str(root)], check=True)
|
||||
return
|
||||
root = args.namespace_root
|
||||
parent_namespaces = json.loads((root / 'parent-namespaces.json').read_text())
|
||||
namespaces = {name: str(Path('/proc/self/ns', name).readlink()) for name in ('user', 'net', 'mnt')}
|
||||
assert all(namespaces[name] != parent_namespaces[name] for name in namespaces), 'private namespaces required'
|
||||
private_etc = root / 'etc'
|
||||
(private_etc / 'qemu').mkdir(parents=True)
|
||||
(private_etc / 'qemu/bridge.conf').write_text('allow hydrabr0\n')
|
||||
subprocess.run(['mount', '--bind', str(private_etc), '/etc'], check=True)
|
||||
subprocess.run(['ip', 'link', 'add', 'hydrabr0', 'type', 'bridge'], check=True)
|
||||
subprocess.run(['ip', 'link', 'set', 'hydrabr0', 'up'], check=True)
|
||||
manager = Runtime(root / 'config', root / 'state', root / 'run')
|
||||
result = {'evidence': str(root), 'namespace': 'private user/net/mount',
|
||||
'parent_namespaces': parent_namespaces, 'probe_namespaces': namespaces, 'names': {}}
|
||||
try:
|
||||
for name in ('alpha', 'beta'):
|
||||
manager.add(Profile(name, memory=128, cpus=1, disk_size='8M',
|
||||
graphics='software', network='bridge'))
|
||||
manager.start(name)
|
||||
|
||||
def actual_mac(name):
|
||||
with manager._connect(manager.paths(name)) as qmp:
|
||||
network = qmp.command('human-monitor-command', {'command-line': 'info network'})
|
||||
mac = manager.profile(name).mac
|
||||
assert 'macaddr=' + mac in network, network
|
||||
assert int(mac.split(':')[0], 16) & 3 == 2
|
||||
return mac, network
|
||||
|
||||
for name in ('alpha', 'beta'):
|
||||
mac, network = actual_mac(name)
|
||||
result['names'][name] = {'mac': mac, 'network': network}
|
||||
assert result['names']['alpha']['mac'] != result['names']['beta']['mac']
|
||||
links = json.loads(subprocess.check_output(['ip', '-j', 'link', 'show', 'master', 'hydrabr0'], text=True))
|
||||
assert len(links) == 2, links
|
||||
result['bridge_ports'] = [link['ifname'] for link in links]
|
||||
manager.stop('alpha', force=True)
|
||||
manager.start('alpha')
|
||||
assert actual_mac('alpha')[0] == result['names']['alpha']['mac']
|
||||
result['restart_preserved_mac'] = True
|
||||
result['dhcp_and_guest_network_drivers_pending'] = True
|
||||
result['passed'] = True
|
||||
(root / 'result.json').write_text(json.dumps(result, indent=2) + '\n')
|
||||
print(json.dumps(result, indent=2))
|
||||
finally:
|
||||
for profile in manager.profiles():
|
||||
if manager.status(profile.name)['status'] == 'running':
|
||||
manager.stop(profile.name, force=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user