bridge.sh: die Strophe, gegen vier Anbieterfaelle geprueft
write_bridge_stanza ist von build_bridge getrennt: das Schreiben ist das, was eine Maschine umbringt, und so ist es pruefbar, ohne dafuer ein Netz neu laden zu muessen. Beide Pfade sind ueberschreibbar (CLUPILOT_INTERFACES_FILE, CLUPILOT_IFRELOAD), dieselbe Technik wie CLUPILOT_STORAGE_CFG beim Vorlagenbau. Drei Dinge kann die Fassung mehr als die alte in network.sh: - hwaddress festgenagelt. Eine Bruecke waehlt sonst die kleinste MAC ihrer Ports; bei einem Port ist das dieselbe, aber 'ist dieselbe' und 'bleibt dieselbe' sind zweierlei. - Zusatzrouten des Anbieters wandern mit. Ausgelassen bleiben die Kernel-Route zum eigenen Subnetz und die Link-Route zum Gateway — beide entstehen von selbst, und ein gescheitertes 'up' nimmt bei ifreload die ganze Strophe mit. - IPv6, aber nur statisch und global. SLAAC/DHCPv6 werden bewusst nicht nachgebaut (forwarding=1 laesst den Kernel RAs ohne accept_ra=2 verwerfen) — dafuer gibt es eine Zeile ins Protokoll statt eines stillen Verlusts. Geprueft gegen echte sh: Hetzner /32 routed mit pointopoint, netcup Subnetz ohne, DHCP ohne Adresse, statisches IPv6 mit fe80::1, SLAAC faellt weg, Zusatzrouten kommen mit. network.sh hat sein eigenes build_bridge abgegeben; ein Test haelt jetzt jede der sieben Brueckenfunktionen auf genau einer Stelle im Repo fest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>feat/hostnamen-vergabe
parent
6bda0e1d8a
commit
587d1351d1
|
|
@ -182,3 +182,141 @@ bridge_is_up() {
|
|||
&& bridge_carries_default_route "$CLUPILOT_BRIDGE" \
|
||||
&& bridge_has_address "$CLUPILOT_BRIDGE"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Die Strophe
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Zusatzrouten des Anbieters — alles auf der Karte, was der Kernel nicht von
|
||||
# selbst wieder anlegt.
|
||||
#
|
||||
# Ausgelassen werden zwei Sorten, und beide mit Grund: die Kernel-Route zum
|
||||
# eigenen Subnetz (`proto kernel`) entsteht, sobald die Adresse steht, und die
|
||||
# Link-Route zum Gateway legt `pointopoint` an. Sie trotzdem einzutragen ließe
|
||||
# `ip route add` scheitern — und ein gescheitertes `up` nimmt bei `ifreload`
|
||||
# die ganze Strophe mit.
|
||||
extra_routes() {
|
||||
_iface="$1"
|
||||
_gw="$2"
|
||||
|
||||
"$CLUPILOT_IP" -4 route show dev "$_iface" 2>/dev/null | while IFS= read -r _route; do
|
||||
[ -n "$_route" ] || continue
|
||||
|
||||
case "$_route" in
|
||||
default*) continue ;;
|
||||
"${_gw} "*|"${_gw}") continue ;;
|
||||
*'proto kernel'*) continue ;;
|
||||
esac
|
||||
|
||||
printf ' up ip route add %s dev %s || true\n' "$_route" "$CLUPILOT_BRIDGE"
|
||||
done
|
||||
}
|
||||
|
||||
# Eine statische, globale IPv6-Adresse — oder nichts.
|
||||
#
|
||||
# `dynamic` (SLAAC/DHCPv6) und `temporary` (Privacy Extensions) fliegen raus:
|
||||
# was der Kernel selbst vergibt, gehört nicht in eine Datei geschrieben.
|
||||
static_ipv6_on() {
|
||||
"$CLUPILOT_IP" -6 -o addr show dev "$1" scope global 2>/dev/null \
|
||||
| grep -v '[[:space:]]dynamic[[:space:]]' \
|
||||
| grep -v '[[:space:]]temporary[[:space:]]' \
|
||||
| awk '{ print $4; exit }'
|
||||
}
|
||||
|
||||
ipv6_gateway() {
|
||||
"$CLUPILOT_IP" -6 route show default 2>/dev/null \
|
||||
| awk '{ for (i = 1; i < NF; i++) if ($i == "via") { print $(i+1); exit } }'
|
||||
}
|
||||
|
||||
# Schreibt die Strophe — und sonst nichts. Kein `ifreload`, kein Sichern, kein
|
||||
# Zeitgeber. Getrennt von build_bridge, damit genau das prüfbar ist, was eine
|
||||
# Maschine umbringt, ohne dafür ein Netz neu laden zu müssen.
|
||||
write_bridge_stanza() {
|
||||
_iface="$1"
|
||||
_style="$2"
|
||||
_cidr="$3"
|
||||
_gw="$4"
|
||||
|
||||
_ip4="${_cidr%%/*}"
|
||||
_mac="$(cat "${CLUPILOT_SYS_NET}/${_iface}/address" 2>/dev/null)"
|
||||
|
||||
case "$_style" in
|
||||
dhcp)
|
||||
_inet="iface ${CLUPILOT_BRIDGE} inet dhcp"
|
||||
_addr=''
|
||||
;;
|
||||
routed)
|
||||
# Gateway außerhalb des eigenen Subnetzes. Ohne `pointopoint` findet
|
||||
# der Kernel keinen Weg dorthin: die Route zeigte auf ein Netz, in
|
||||
# dem das Gateway nicht liegt, und die Maschine wäre still weg.
|
||||
_inet="iface ${CLUPILOT_BRIDGE} inet static"
|
||||
_addr=" address ${_ip4}/32
|
||||
pointopoint ${_gw}
|
||||
gateway ${_gw}"
|
||||
;;
|
||||
*)
|
||||
_inet="iface ${CLUPILOT_BRIDGE} inet static"
|
||||
_addr=" address ${_cidr}
|
||||
gateway ${_gw}"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Festgenagelt: eine Brücke wählt sonst die kleinste MAC ihrer Ports, und
|
||||
# manche Anbieter binden den Switchport an die der Karte. Bei einem Port ist
|
||||
# das dieselbe — aber „ist dieselbe" und „bleibt dieselbe" sind zweierlei.
|
||||
_hw=''
|
||||
[ -n "$_mac" ] && _hw=" hwaddress ether ${_mac}"
|
||||
|
||||
_inet6=''
|
||||
_v6="$(static_ipv6_on "$_iface")"
|
||||
_v6gw="$(ipv6_gateway)"
|
||||
|
||||
if [ -n "$_v6" ] && [ -n "$_v6gw" ]; then
|
||||
_inet6="
|
||||
iface ${CLUPILOT_BRIDGE} inet6 static
|
||||
address ${_v6}
|
||||
gateway ${_v6gw}"
|
||||
elif [ -n "$_v6" ] || [ -n "$_v6gw" ]; then
|
||||
log 'IPv6 wird NICHT mit auf die Brücke genommen: keine statische globale Adresse mit Standardgateway (SLAAC/DHCPv6 werden bewusst nicht nachgebaut — Proxmox setzt forwarding=1, und der Kernel verwirft RAs dann ohne accept_ra=2).'
|
||||
fi
|
||||
|
||||
cat > "$CLUPILOT_INTERFACES_FILE" <<EOF
|
||||
# Von CluPilot geschrieben (Schritt EnsureNetworkBridge), weil auf dieser
|
||||
# Maschine keine ${CLUPILOT_BRIDGE} lag: Proxmox auf Debian legt keine an, nur
|
||||
# der ISO-Installer tut das.
|
||||
#
|
||||
# Abgeleitet aus dem LAUFENDEN Zustand, nicht aus der Datei des Anbieters.
|
||||
# Anbieterform: ${_style}. Karte: ${_iface}.
|
||||
#
|
||||
# Die vorherige Fassung liegt unter ${CLUPILOT_NET_BACKUP}.tar.gz.
|
||||
source ${CLUPILOT_INTERFACES_D}/*
|
||||
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
iface ${_iface} inet manual
|
||||
|
||||
auto ${CLUPILOT_BRIDGE}
|
||||
${_inet}
|
||||
${_addr}
|
||||
${_hw}
|
||||
bridge-ports ${_iface}
|
||||
bridge-stp off
|
||||
bridge-fd 0
|
||||
$(extra_routes "$_iface" "$_gw")${_inet6}
|
||||
EOF
|
||||
}
|
||||
|
||||
# Strophe schreiben und anwenden. Ab hier ist die Leitung in Gefahr.
|
||||
build_bridge() {
|
||||
write_bridge_stanza "$@"
|
||||
|
||||
log 'Strophe geschrieben:'
|
||||
cat "$CLUPILOT_INTERFACES_FILE"
|
||||
|
||||
if command -v "$CLUPILOT_IFRELOAD" >/dev/null 2>&1; then
|
||||
"$CLUPILOT_IFRELOAD" -a
|
||||
else
|
||||
systemctl restart networking
|
||||
fi
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,64 +144,12 @@ host_still_reachable() {
|
|||
http_get "$CLUPILOT_PROBE_URL" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Schreibt eine Brücke über die primäre Netzkarte.
|
||||
# `build_bridge` und `write_bridge_stanza` stehen in lib/bridge.sh.
|
||||
#
|
||||
# Läuft NUR, wenn der ISO-Installer keine hinterlassen hat — und dann unter dem
|
||||
# Zeitgeber. Die drei Formen unterscheiden sich in genau der Zeile, die
|
||||
# entscheidet, ob die Maschine danach noch da ist.
|
||||
build_bridge() {
|
||||
_iface="$1"
|
||||
_style="$2"
|
||||
_cidr="$3"
|
||||
_gw="$4"
|
||||
|
||||
_ip="${_cidr%%/*}"
|
||||
|
||||
case "$_style" in
|
||||
dhcp)
|
||||
_inet='iface vmbr0 inet dhcp'
|
||||
_addr=''
|
||||
;;
|
||||
routed)
|
||||
# Gateway außerhalb des eigenen Subnetzes. Ohne `pointopoint` findet
|
||||
# der Kernel keinen Weg dorthin: die Route zeigte auf ein Netz, in
|
||||
# dem das Gateway nicht liegt, und die Maschine wäre still weg.
|
||||
_inet='iface vmbr0 inet static'
|
||||
_addr=" address ${_ip}/32
|
||||
pointopoint ${_gw}
|
||||
gateway ${_gw}"
|
||||
;;
|
||||
*)
|
||||
_inet='iface vmbr0 inet static'
|
||||
_addr=" address ${_cidr}
|
||||
gateway ${_gw}"
|
||||
;;
|
||||
esac
|
||||
|
||||
cat > /etc/network/interfaces <<EOF
|
||||
# Von CluPilots Bootstrap geschrieben, weil der Installer keine vmbr0
|
||||
# hinterlassen hat. Anbieterform: ${_style}.
|
||||
source /etc/network/interfaces.d/*
|
||||
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
iface ${_iface} inet manual
|
||||
|
||||
auto vmbr0
|
||||
${_inet}
|
||||
${_addr}
|
||||
bridge-ports ${_iface}
|
||||
bridge-stp off
|
||||
bridge-fd 0
|
||||
EOF
|
||||
|
||||
if command -v ifreload >/dev/null 2>&1; then
|
||||
ifreload -a
|
||||
else
|
||||
systemctl restart networking
|
||||
fi
|
||||
}
|
||||
# Die dortige Fassung kann drei Dinge mehr, und jedes davon hat einen Grund:
|
||||
# sie nagelt die MAC fest, nimmt die Zusatzrouten des Anbieters mit, und trennt
|
||||
# das SCHREIBEN der Strophe vom Anwenden — damit prüfbar ist, was eine Maschine
|
||||
# umbringt, ohne dafür ein Netz neu laden zu müssen.
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# WireGuard
|
||||
|
|
|
|||
|
|
@ -159,12 +159,15 @@ it('sagt nur dann „Brücke steht", wenn sie Route UND Adresse trägt', functio
|
|||
expect($out)->toBe('NEIN');
|
||||
});
|
||||
|
||||
it('hält detect_primary_interface an genau einer Stelle im Repo', function () {
|
||||
it('hält jede Brückenfunktion an genau einer Stelle im Repo', function () {
|
||||
// Zwei Fassungen wären zwei Installationen, die bei jeder Proxmox-Version
|
||||
// nachgezogen werden müssten — und die zweite fiele erst auf, wenn jemand
|
||||
// sie benutzt. Genau die Entscheidung, die den Rettungssystem-Weg
|
||||
// stillgelegt hat.
|
||||
//
|
||||
// Die Liste wächst mit bridge.sh mit: wer eine Funktion hierher zieht und
|
||||
// die alte Fassung stehen lässt, fällt hier auf und nicht auf einem Host.
|
||||
//
|
||||
// array_merge, NICHT `+`: bei numerischen Schlüsseln behält der
|
||||
// Vereinigungsoperator die linke Seite und wirft die rechte still weg. Und
|
||||
// PHPs glob() kennt kein rekursives `**`, also zwei Muster.
|
||||
|
|
@ -173,14 +176,208 @@ it('hält detect_primary_interface an genau einer Stelle im Repo', function () {
|
|||
glob(base_path('deploy/bootstrap/*.sh')) ?: [],
|
||||
);
|
||||
|
||||
$hits = [];
|
||||
foreach ($files as $file) {
|
||||
if (preg_match('/^detect_primary_interface\(\)/m', (string) file_get_contents($file))) {
|
||||
$hits[] = str_replace(base_path().'/', '', $file);
|
||||
}
|
||||
}
|
||||
$functions = [
|
||||
'detect_primary_interface',
|
||||
'detect_network_style',
|
||||
'bridge_exists',
|
||||
'bridge_carries_default_route',
|
||||
'bridge_has_address',
|
||||
'build_bridge',
|
||||
'write_bridge_stanza',
|
||||
];
|
||||
|
||||
expect($hits)->toBe(['deploy/bootstrap/lib/bridge.sh']);
|
||||
foreach ($functions as $function) {
|
||||
$hits = [];
|
||||
foreach ($files as $file) {
|
||||
if (preg_match('/^'.$function.'\(\)/m', (string) file_get_contents($file))) {
|
||||
$hits[] = str_replace(base_path().'/', '', $file);
|
||||
}
|
||||
}
|
||||
|
||||
expect($hits)->toBe(['deploy/bootstrap/lib/bridge.sh'], $function.' steht nicht genau einmal im Repo');
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Die Strophe
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Vier Anbieterfälle, und sie unterscheiden sich in genau der Zeile, die
|
||||
| entscheidet, ob die Maschine danach noch da ist.
|
||||
*/
|
||||
|
||||
it('schreibt für eine geroutete /32 eine pointopoint-Strophe', function () {
|
||||
// Hetzner dediziert. Ohne `pointopoint` findet der Kernel keinen Weg zum
|
||||
// Gateway: die Route zeigte auf ein Netz, in dem das Gateway nicht liegt,
|
||||
// und die Maschine wäre still weg.
|
||||
$ip = fakeIp($this->dir, [
|
||||
'-4 route show dev enp0s31f6' => "49.12.121.65 scope link\ndefault via 49.12.121.65",
|
||||
'-6 -o addr show dev enp0s31f6 scope global' => '',
|
||||
'-6 route show default' => '',
|
||||
]);
|
||||
$sys = fakeSysNet($this->dir, 'enp0s31f6', 'a8:a1:59:00:11:22');
|
||||
$out = $this->dir.'/interfaces';
|
||||
|
||||
runBridgeSh('write_bridge_stanza enp0s31f6 routed 49.12.121.79/32 49.12.121.65', [
|
||||
'CLUPILOT_IP' => $ip,
|
||||
'CLUPILOT_SYS_NET' => $sys,
|
||||
'CLUPILOT_INTERFACES_FILE' => $out,
|
||||
]);
|
||||
|
||||
expect(file_get_contents($out))
|
||||
->toContain('address 49.12.121.79/32')
|
||||
->toContain('pointopoint 49.12.121.65')
|
||||
->toContain('gateway 49.12.121.65')
|
||||
->toContain('bridge-ports enp0s31f6')
|
||||
->toContain('iface enp0s31f6 inet manual')
|
||||
// Festgenagelt, damit die Brücke nicht irgendeine MAC wählt: manche
|
||||
// Anbieter binden den Switchport an die der Karte.
|
||||
->toContain('hwaddress ether a8:a1:59:00:11:22')
|
||||
->toContain('auto vmbr0');
|
||||
});
|
||||
|
||||
it('schreibt für ein gewöhnliches Subnetz Adresse und Gateway ohne pointopoint', function () {
|
||||
// netcup und die meisten anderen. `pointopoint` wäre hier falsch.
|
||||
$ip = fakeIp($this->dir, [
|
||||
'-4 route show dev ens3' => '192.168.10.0/24 proto kernel scope link src 192.168.10.7',
|
||||
'-6 -o addr show dev ens3 scope global' => '',
|
||||
'-6 route show default' => '',
|
||||
]);
|
||||
$sys = fakeSysNet($this->dir, 'ens3', 'de:ad:be:ef:00:01');
|
||||
$out = $this->dir.'/interfaces';
|
||||
|
||||
runBridgeSh('write_bridge_stanza ens3 subnet 192.168.10.7/24 192.168.10.1', [
|
||||
'CLUPILOT_IP' => $ip,
|
||||
'CLUPILOT_SYS_NET' => $sys,
|
||||
'CLUPILOT_INTERFACES_FILE' => $out,
|
||||
]);
|
||||
|
||||
expect(file_get_contents($out))
|
||||
->toContain('address 192.168.10.7/24')
|
||||
->toContain('gateway 192.168.10.1')
|
||||
->not->toContain('pointopoint')
|
||||
// Die Route zum eigenen Subnetz gehört NICHT in die Zusatzrouten: die
|
||||
// legt der Kernel selbst an, sobald die Adresse steht. Ein zweites Mal
|
||||
// hinzufügen scheitert — und ein gescheitertes `up` nimmt bei
|
||||
// `ifreload` die ganze Strophe mit.
|
||||
->not->toContain('up ip route add 192.168.10.0/24');
|
||||
});
|
||||
|
||||
it('schreibt für DHCP keine Adresse in die Strophe', function () {
|
||||
$ip = fakeIp($this->dir, [
|
||||
'-4 route show dev ens3' => '',
|
||||
'-6 -o addr show dev ens3 scope global' => '',
|
||||
'-6 route show default' => '',
|
||||
]);
|
||||
$sys = fakeSysNet($this->dir, 'ens3', 'de:ad:be:ef:00:02');
|
||||
$out = $this->dir.'/interfaces';
|
||||
|
||||
runBridgeSh('write_bridge_stanza ens3 dhcp 10.0.0.7/24 10.0.0.1', [
|
||||
'CLUPILOT_IP' => $ip,
|
||||
'CLUPILOT_SYS_NET' => $sys,
|
||||
'CLUPILOT_INTERFACES_FILE' => $out,
|
||||
]);
|
||||
|
||||
expect(file_get_contents($out))
|
||||
->toContain('iface vmbr0 inet dhcp')
|
||||
->not->toContain('address 10.0.0.7')
|
||||
->toContain('bridge-ports ens3');
|
||||
});
|
||||
|
||||
it('nimmt eine statische globale IPv6 samt link-local Gateway mit', function () {
|
||||
// Hetzners Form: 2a01:…::2/64 mit fe80::1. Statisch, also derselbe Dreisatz
|
||||
// wie bei v4 — und damit der Fall, der laut Entwurf nichts kostet.
|
||||
$ip = fakeIp($this->dir, [
|
||||
'-4 route show dev enp0s31f6' => '',
|
||||
'-6 -o addr show dev enp0s31f6 scope global' => '2: enp0s31f6 inet6 2a01:4f8:1c1c::2/64 scope global \ valid_lft forever preferred_lft forever',
|
||||
'-6 route show default' => 'default via fe80::1 dev enp0s31f6 metric 1024',
|
||||
]);
|
||||
$sys = fakeSysNet($this->dir, 'enp0s31f6', 'a8:a1:59:00:11:22');
|
||||
$out = $this->dir.'/interfaces';
|
||||
|
||||
runBridgeSh('write_bridge_stanza enp0s31f6 routed 49.12.121.79/32 49.12.121.65', [
|
||||
'CLUPILOT_IP' => $ip,
|
||||
'CLUPILOT_SYS_NET' => $sys,
|
||||
'CLUPILOT_INTERFACES_FILE' => $out,
|
||||
]);
|
||||
|
||||
expect(file_get_contents($out))
|
||||
->toContain('iface vmbr0 inet6 static')
|
||||
->toContain('address 2a01:4f8:1c1c::2/64')
|
||||
->toContain('gateway fe80::1');
|
||||
});
|
||||
|
||||
it('lässt SLAAC-IPv6 liegen und sagt es ins Protokoll', function () {
|
||||
// Proxmox setzt forwarding=1, und der Kernel verwirft
|
||||
// Router-Advertisements dann ohne accept_ra=2. Das sauber hinzubekommen
|
||||
// wäre ein zweiter Satz Fehlerfälle in genau dem Fenster, das diese
|
||||
// Konstruktion zu überleben versucht — und v6 trägt hier keinen Verkehr.
|
||||
// Still verlieren gilt trotzdem nicht.
|
||||
$ip = fakeIp($this->dir, [
|
||||
'-4 route show dev ens3' => '',
|
||||
'-6 -o addr show dev ens3 scope global' => '2: ens3 inet6 2a01:4f8:aaaa::17/64 scope global dynamic \ valid_lft 86000sec preferred_lft 14000sec',
|
||||
'-6 route show default' => 'default via fe80::1 dev ens3',
|
||||
]);
|
||||
$sys = fakeSysNet($this->dir, 'ens3', 'de:ad:be:ef:00:03');
|
||||
$out = $this->dir.'/interfaces';
|
||||
|
||||
$log = runBridgeSh('write_bridge_stanza ens3 subnet 10.0.0.7/24 10.0.0.1', [
|
||||
'CLUPILOT_IP' => $ip,
|
||||
'CLUPILOT_SYS_NET' => $sys,
|
||||
'CLUPILOT_INTERFACES_FILE' => $out,
|
||||
]);
|
||||
|
||||
expect(file_get_contents($out))->not->toContain('inet6 static')
|
||||
->and($log)->toContain('IPv6');
|
||||
});
|
||||
|
||||
it('nimmt die Zusatzrouten des Anbieters mit auf die Brücke', function () {
|
||||
// Was der Anbieter extra eingetragen hat, verliert der Host sonst — und es
|
||||
// fällt erst auf, wenn jemand das Ziel dahinter braucht.
|
||||
$ip = fakeIp($this->dir, [
|
||||
'-4 route show dev enp0s31f6' => "49.12.121.65 scope link\n10.128.0.0/16 via 49.12.121.65\n49.12.121.64/26 proto kernel scope link src 49.12.121.79",
|
||||
'-6 -o addr show dev enp0s31f6 scope global' => '',
|
||||
'-6 route show default' => '',
|
||||
]);
|
||||
$sys = fakeSysNet($this->dir, 'enp0s31f6', 'a8:a1:59:00:11:22');
|
||||
$out = $this->dir.'/interfaces';
|
||||
|
||||
runBridgeSh('write_bridge_stanza enp0s31f6 subnet 49.12.121.79/26 49.12.121.65', [
|
||||
'CLUPILOT_IP' => $ip,
|
||||
'CLUPILOT_SYS_NET' => $sys,
|
||||
'CLUPILOT_INTERFACES_FILE' => $out,
|
||||
]);
|
||||
|
||||
expect(file_get_contents($out))
|
||||
->toContain('up ip route add 10.128.0.0/16 via 49.12.121.65 dev vmbr0')
|
||||
// Weder die Link-Route zum Gateway noch die Kernel-Route zum eigenen
|
||||
// Subnetz: beide entstehen von selbst, und ein zweiter Versuch
|
||||
// scheitert und nimmt die Strophe mit.
|
||||
->not->toContain('up ip route add 49.12.121.65 ')
|
||||
->not->toContain('up ip route add 49.12.121.64/26');
|
||||
});
|
||||
|
||||
it('schreibt eine Strophe, die ein Mensch als von CluPilot erkennt', function () {
|
||||
// Wer in sechs Monaten in diese Datei sieht, muss wissen, wer sie
|
||||
// geschrieben hat und woraus.
|
||||
$ip = fakeIp($this->dir, [
|
||||
'-4 route show dev ens3' => '',
|
||||
'-6 -o addr show dev ens3 scope global' => '',
|
||||
'-6 route show default' => '',
|
||||
]);
|
||||
$sys = fakeSysNet($this->dir, 'ens3', 'de:ad:be:ef:00:04');
|
||||
$out = $this->dir.'/interfaces';
|
||||
|
||||
runBridgeSh('write_bridge_stanza ens3 subnet 10.0.0.7/24 10.0.0.1', [
|
||||
'CLUPILOT_IP' => $ip,
|
||||
'CLUPILOT_SYS_NET' => $sys,
|
||||
'CLUPILOT_INTERFACES_FILE' => $out,
|
||||
]);
|
||||
|
||||
expect(file_get_contents($out))
|
||||
->toContain('Von CluPilot geschrieben')
|
||||
->toContain('EnsureNetworkBridge');
|
||||
});
|
||||
|
||||
it('lässt jede Bibliothek des Bootstraps von einer Shell parsen', function () {
|
||||
|
|
|
|||
Loading…
Reference in New Issue