ToFoIn v 1. Reservierung von Gateways und Umschalten zwischen externen Kanälen in FreeBSD

Anmerkung


In einem früheren Artikel wurde die Organisation der Redundanz für LAN-Gateways betrachtet. Als Lösung wurde ein Skript vorgeschlagen, das zu diesem Zeitpunkt das Problem löste, jedoch eine Reihe von Nachteilen aufwies. Nach einiger Zeit stellte sich heraus, dass diese Mängel beseitigt wurden, der Code teilweise neu geschrieben wurde und an der Ausgabe etwas Akzeptables erzielt wurde. Jetzt können wir sagen, dass die Skripte genug getestet wurden, um als stabil bezeichnet zu werden. Um das Verständnis des gesamten Systems zu vereinfachen, werden die wichtigsten Punkte für die Einrichtung von Sekundärdiensten (in Bezug auf das Thema des Artikels) im Folgenden teilweise dupliziert. Der Grund ist einfach: Während dieser Zeit wurden auch die ipfw-Regeln überarbeitet, DNS wurde in AD auf Samba4 mit einem Bind-Frontend und sicher aktualisierten Datensätzen von isc-dhcpd unter Verwendung von Kerberos sowie sekundären DNS-Servern als Bindung an die Gateways in Betrieb genommen. konfiguriertes CARP ... Im Allgemeinen ist es viel interessanter geworden, aber weiter unten erfahren Sie, was und wie es funktioniert. Alles, was mit Verweisen auf die Quelle angegeben werden kann, wird so gestaltet, dass keine Essenz entsteht. Was von anderen Orten genommen wurde, aber nicht mehr verfügbar ist, wird hier mit relevanten Kommentaren angegeben.

Einführung


Es gibt also zwei Möglichkeiten, um die Störfestigkeit des Kommunikationskanals mit der Außenwelt seitens des Verbrauchers zu erhöhen: Sicherung des Gateways und Reservierung des Verbindungspunkts. Mit anderen Worten, im ersten Fall wird ein zweites Gateway eingerichtet, das aktiviert wird, wenn das erste ausfällt. Im zweiten Fall wird ein Backup-Internetkanal organisiert, falls Probleme mit dem Hauptkanal auftreten. Je weiter sie sich kreuzen, desto besser. Wenn CARP die erste Aufgabe für FreeBSD löst, kann die zweite nach dem Organisieren des zweiten externen Kanals wieder auf verschiedene Arten gelöst werden. Zumindest können Sie den Verkehrsausgleich oder das Umschalten zwischen Kanälen organisieren. Aufgrund des großen Unterschieds in der Bandbreite externer Kanäle passte die erste Option nicht zu mir, daher wurde der Hauptschuldige der Veröffentlichung geschrieben: ToFoIn - eine Reihe von Bash-Skripten, die darauf abzielen, Diagnoseprobleme zu lösen und zu einem funktionierenden externen Kanal zu wechseln. Nach der Verfeinerung kann es auf n Gateways und m Kanäle angewendet werden. Die Situation ist schwach, wo n und m mehr als 2 sind, aber die folgenden Skripte sollten bis zu großen Werten von n und m funktionieren, weil Logischerweise ist das Limit nicht festgelegt. Im Allgemeinen vermute ich, dass es mit diesen Skripten möglich ist, eine ziemlich breite Palette von Aufgaben zu lösen, abhängig vom Status der Verbindung, die möglicherweise nur durch Vorstellungskraft begrenzt ist.


Ungefähr diese Netzwerktopologie setzt in ihrer einfachsten Form die Verwendung einer Reihe von ToFoIn-Skripten voraus. Natürlich sollten die Skripte bei einem einzelnen Router funktionieren, aber in diesem Fall müssen Sie das Daemon-Modul stark ändern, um die Abhängigkeit der Reihenfolge der Aktionen vom CARP-Status zu beseitigen, die im System einfach nicht vorhanden sind. Eine weitere Reservierung dieser und anderer Knoten hängt nur vom Grad der Wichtigkeit der entsprechenden Dienste ab.

Ziele und Vorgaben


Ziel des Projekts ist es nach wie vor, ein universelles und leicht skalierbares Softwarepaket zu erstellen, das sich darauf konzentriert, Probleme bei externen und internen Verbindungen zu identifizieren und automatisch auf funktionsfähige Verbindungen umzuschalten. Im Allgemeinen lautet die Logik wie folgt:

  • Es gibt n "Router" mit jeweils m externen Kanälen. Darüber hinaus befinden sich alle n „Router“ in einer strengen Hierarchie und sind über CARP an allen erforderlichen Schnittstellen miteinander verbunden.
  • Ein Agent arbeitet unabhängig auf jedem Computer, dessen Aufgabe auf dem aktuellen CARP-Status seines Computers basiert:
    • Bei Sicherung - Erkennen und konfigurieren Sie den Computer auf einem Router, der derzeit der Master ist.
    • Wenn Master - Überprüfen Sie den Status der Verbindungen zum aktuellen Zeitpunkt und wechseln Sie gegebenenfalls zwischen externen Kanälen.

Lösung


Das interne Netzwerk verfügt über CARP, das Backup-Gateways bereitstellt und es Ihnen ermöglicht, die Einstellungen anderer Netzwerkgeräte im internen Netzwerk beim Kanalwechsel nicht zu ändern.

Dhcpd arbeitet im primären - sekundären Modus und im Allgemeinen spielt es keine Rolle, welche anderen Rollen sein Computer spielt - die Verbindung zwischen dhcpd erfolgt im internen Netzwerk, das Router immer betrachten.

Die Masterbindung wird in AD entfernt, das im lokalen Netzwerk verborgen ist, während sekundäre Bindungsserver auf den Gateway-Routern gleichberechtigt arbeiten.

Die ipfw-Regeln unterscheiden sich je nachdem, welcher Kanal zu einem bestimmten Zeitpunkt als Hauptkanal betrachtet wird, und werden vom Daemon-Modul beim Rollenwechsel neu gestartet.

Zum Schluss noch zu den Skripten. Jetzt befinden sich die Dateien in den entsprechenden Verzeichnissen, arbeiten von ihrem Benutzer aus und haben ein Startskript in rc.d. Aufgaben, die Root-Zugriff erfordern, werden von sudo gelöst. Es gibt ein Installationsskript, das das mögliche Vorhandensein einer installierten Version berücksichtigt, sowie eine ziemlich detaillierte Einstellungsdatei. Die Module sind mit geringfügigen Änderungen identisch, einige haben sich in der Funktionalität fast nicht geändert:

Daemon ist - wie der Name schon sagt - der Hauptprozess, der die Test- und Switch-Module auf einem Timer ausführt und auch CARP überwacht.
Tester - testet weiterhin auf externe Kommunikation mit dem Befehl ping. (Wenn es ausgeführt wird, wird davon ausgegangen, dass sich auf der Maschine CARP im Master-Status befindet.)
Beurteilung - Bestimmt anhand der Testergebnisse, welcher externe Kanal funktioniert und ob eine Umschaltung erforderlich ist, führt eine Umschaltung durch (wenn sie ausgeführt wird, wird davon ausgegangen, dass die Maschine CARP im Master-Status hat).
Scout ist ein neues Modul. Es beginnt, wenn sich CARP im Sicherungsstatus befindet. Es muss ermittelt werden, welcher der verbleibenden Router derzeit der Hauptrouter ist.
Logger - verantwortlich für die Ereignisprotokollierung. Es ist notwendig, dass Informationen über Ereignisse nicht dupliziert werden und das Magazin leichter zu lesen ist.
Watchdog - läuft planmäßig von crontab aus. Es ermittelt das „Einfrieren“ aller Module und versucht (wann immer möglich), die aufgetretenen Probleme zu lösen. Das heißt, Nagel alle, einfach ausgedrückt.

Zusätzlich zu den Skripten selbst sollten einige wichtigere Dateien berücksichtigt werden:

Tofoin.conf - eine einzelne Einstellungsdatei.
Tofoin.log - eine einzelne Ereignisprotokolldatei.
Ergebnis_ <interne Kanalnummer> ist die Arbeitsdatei. Die Testergebnisse werden hier hinzugefügt und in / tmp neben .pid und anderen Arbeitsdateien erstellt.

Gerne beantworte ich die Fragen zum Betrieb der Module und erkläre die Lösungen in den Kommentaren.

Technischer Teil


Ausrüstung


Im Vergleich zur vorherigen Zeit wurden die Gateways auf P4 verschoben, erhielten 1536 MB RAM und drei 40-GB-Festplatten (Spiegel + Ersatz). Netzwerkkarten sind immer noch PCI, Netzteile sind normal, natürlich in Gegenwart einer USV.
Die Erhöhung der Kapazität ist mit dem freigesetzten Eisen und der übermäßig mühsamen Aktualisierung von der Quelle verbunden, meistens jedoch mit der ersten. OS FreeBSD 11.1, FS zfs.

Systemkomponenteneinstellungen


Weitere Details
Der Kernel wird mit solchen zusätzlichen Parametern kompiliert (etwas kann auch im Loader eingestellt werden, aber es ist besser so):

options IPFIREWALL # ipfw firewall options IPFIREWALL_VERBOSE options IPFIREWALL_VERBOSE_LIMIT=50 options IPFIREWALL_NAT options LIBALIAS options DUMMYNET options HZ=1000 options ROUTETABLES=4 options KSTACK_PAGES=4 options KVA_PAGES=512 device carp 

Einstellungen /boot/loader.conf:
 geom_mirror_load="YES" zfs_load="YES" kern.geom.label.gptid.enable="0" vm.kmem_size="1024M" vm.kmem_size_max="1024M" vfs.zfs.arc_max="512M" vfs.zfs.vdev.cache.size="30M" vfs.zfs.prefetch_disable=1 kern.vty=vt 

Einstellungen /etc/rc.conf auf dem ersten Computer (CARP-Konfiguration ist von primärem Interesse)
 ifconfig_eth0="up" vlans_eth0="vlan111 vlan222" create_args_vlan111="vlan 111" create_args_vlan222="vlan 222" ifconfig_eth1="up" vlans_eth1="vlan333 vlan444 vlan555" create_args_vlan333="vlan 333" create_args_vlan444="vlan 444" create_args_vlan555="vlan 555" ifconfig_eth2="up" vlans_eth2="vlan666 vlan777 vlan888" create_args_vlan666="vlan 666" create_args_vlan777="vlan 777" create_args_vlan888="vlan 888" ifconfig_vlan666="inet 192.168.0.1/24" ifconfig_vlan666_alias0="vhid 1 advskew 100 pass MyPassword alias 192.168.0.5/32" ifconfig_vlan777="inet 192.168.1.1/24" ifconfig_vlan777_alias0="vhid 1 advskew 100 pass MyPassword alias 192.168.1.5/32" ifconfig_vlan888="inet 192.168.2.1/24" ifconfig_vlan888_alias0="vhid 1 advskew 100 pass MyPassword alias 192.168.2.5/32" ifconfig_vlan111="inet 192.168.3.1/30" ifconfig_vlan111_alias0="vhid 1 advskew 100 pass MyPassword alias 1.1.1.2/24" ifconfig_vlan222="inet 192.168.4.1/30" ifconfig_vlan333="inet 192.168.5.1/30" ifconfig_vlan333_alias0="vhid 1 advskew 100 pass MyPassword alias 2.2.2.2/30" ifconfig_vlan444="inet 192.168.6.1/30" ifconfig_vlan444_alias0="vhid 1 advskew 100 pass MyPassword alias 3.3.3.2/30" ifconfig_vlan555="inet 192.168.7.1/30" defaultrouter="1.1.1.1" setfib1_enable="YES" setfib1_defaultrouter="3.3.3.1" setfib2_enable="YES" setfib2_defaultrouter="2.2.2.1" zfs_enable="YES" named_enable="YES" dhcpd_enable="YES" firewall_enable="YES" firewall_logging="YES" firewall_script="/etc/firewall.sh" gateway_enable="YES" tofoin_enable="YES" 

Legende:
eth0, eth1, eth2 - physikalische Adapter
vlan666, vlan777, vlan888 - virtuelle LAN-Adapter,
vlan222 und vlan555 - Adapter für redundante Kommunikation zwischen externen Netzwerkkarten (sie werden möglicherweise nicht mehr benötigt, sie wurden früher aktiv verwendet)
vlan111 - der externe Hauptkanal
vlan444 - externen Kanal sichern
vlan333 - Telefonie
Einstellungen /etc/rc.conf auf dem zweiten Computer (CARP-Konfiguration ist von Hauptinteresse, einige der doppelten Zeilen werden entfernt):
 ifconfig_vlan666="inet 192.168.0.2/24" ifconfig_vlan666_alias0="vhid 1 advskew 0 pass MyPassword alias 192.168.0.5/32" ifconfig_vlan777="inet 192.168.1.2/24" ifconfig_vlan777_alias0="vhid 1 advskew 0 pass MyPassword alias 192.168.1.5/32" ifconfig_vlan888="inet 192.168.2.2/24" ifconfig_vlan888_alias0="vhid 1 advskew 0 pass MyPassword alias 192.168.2.5/32" ifconfig_vlan111="inet 192.168.3.2/30" ifconfig_vlan111_alias0="vhid 1 advskew 0 pass MyPassword alias 1.1.1.2/24" ifconfig_vlan222="inet 192.168.4.2/30" ifconfig_vlan333="inet 192.168.5.2/30" ifconfig_vlan333_alias0="vhid 1 advskew 0 pass MyPassword alias 2.2.2.2/30" ifconfig_vlan444="inet 192.168.6.2/30" ifconfig_vlan444_alias0="vhid 1 advskew 0 pass MyPassword alias 3.3.3.2/30" ifconfig_vlan555="inet 192.168.7.2/30" defaultrouter="1.1.1.1" setfib1_enable="YES" setfib1_defaultrouter="3.3.3.1" setfib2_enable="YES" setfib2_defaultrouter="2.2.2.1" 

Einige Regeln, die bei der Konfiguration von ipfw (nat) nützlich sind, sind:
So erlauben Sie CARP-Verkehr:
 /sbin/ipfw -q add allow carp from any to any 

"Nuclear" nat:
 /sbin/ipfw -q nat 1 config log ip vlan111 reset same_ports deny_in unreg_only /sbin/ipfw -q add nat 1 ip from any to any in 

Verwenden bestimmter Routing-Tabellen mit bestimmten Adaptern:
 /sbin/ipfw -q add setfib 0 all from any to any via vlan666 

Im Allgemeinen könnte ich einen separaten Artikel über die von mir verwendeten ipfw-Einstellungen schreiben, aber dies ist ein anderes Mal.

Software von Drittanbietern


Weitere Details
Da gleichzeitig mit zwei oder mehr externen Kanälen gearbeitet werden muss, ist es zweckmäßig, mehrere Routing-Tabellen zu haben, eine für jeden Kanal. Und es wäre schön, wenn diese Tabellen beim Start selbst erstellt würden. Das rc.d setfib-Skript hilft dabei. Die in ToFoIn verwendete Logik setzt voraus, dass der Dateiname (setfib1, setfib2 usw.) mit der Nummer der Tabelle übereinstimmt, in die ein einzelnes Skript eine Standardroute hinzufügt. Die Tabelle hat standardmäßig die Nummer "0".
DNS-Server mit Bind in der Hauptrolle arbeiten im sekundären Modus, der wichtigste ist samba4 + bind, versteckt im lokalen Netzwerk. Das Einrichten der sekundären Bindung wird in Cricket Lee und Paul Albitz '"DNS and BIND" -Buch auf wunderbare Weise offenbart. Ich kann mich an keine besonderen Anforderungen erinnern, die die Verwendung von samba4 für sekundäre Server berücksichtigen, und ich erwähne sie nicht in der Einstellungsdatei. Es sei denn, Sie müssen für verschiedene Internetkanäle möglicherweise zwei verschiedene Dateien erstellen, die dann vom ToFoIn-Skript an die Stelle kopiert werden, von der die Bindung selbst sie liest. Dies liegt an der Tatsache, dass bei der Angabe der Adressen der DNS-Server beider Anbieter in derselben Datei unter Berücksichtigung der Bindung mit nur einer Routing-Tabelle ein Problem hinsichtlich der Auflösung von Adressen von Upstream-Servern auftritt, auf die zu einem bestimmten Zeitpunkt nicht zugegriffen werden kann.
Failover isc-dhcpd. Dhcpd ist für ToFoIn nicht unbedingt erforderlich. Darüber hinaus wirkt sich das Fehlen überhaupt nicht auf Skripte aus. Wie es mir jedoch erscheint, ist es durchaus logisch, DHCP-Server auf Gateways zu platzieren, und dann stellt sich immer noch die Frage nach dem Failover. Und hier wurde es im Vergleich zum letzten Mal interessanter ... Zusätzlich zu den Einstellungen, die für das Failover erforderlich sind, das ich beim letzten Mal beschrieben habe (der Anfang des Abschnitts "Voreinstellung" im Dropdown-Menü).
Sie benötigen außerdem ein Skript, um DNS-Datensätze in AD mithilfe von samba4 sicher zu aktualisieren. Der Samba4-Server selbst sollte nur installiert werden. Das Einrichten und Starten ist nicht erforderlich. Wir interessieren uns nur für die mit dem Kit gelieferten Verwaltungstools. Weitere Informationen finden Sie im Abschnitt "DHCP mit dynamischen DNS-Updates" unter .
Es sieht beängstigend aus, aber es funktioniert.
Damit ist die Konfiguration der Software von Drittanbietern abgeschlossen.

Ein bisschen über ToFoIn


Der gesamte Projekttext zusammen mit dem Installationsskript ist auf gitlab verfügbar.

Abschließend wird ein Beispiel für die Parameter der ToFoIn-Einstellungsdatei betrachtet:
Die Anzahl der im System verwendeten Router:
 RNUMBER=2 

Wenn Sie zusätzliche Subnetze verwenden, müssen Sie eine Standardroute festlegen, wenn der Router zur primären Route wird. Hier können Sie die Nummer der entsprechenden setfib-Datei angeben, die neu gestartet werden soll. In diesem Beispiel setfib2:
 ADDITLAN=2 

Interner Adaptername:
 INT_IF=vlan666 

Alle anderen Schnittstellen, an denen Router über CARP verbunden sind. Erforderlich, um den gleichen Status aller Schnittstellen zu steuern und aufrechtzuerhalten:
 ALL_IF="vlan111 vlan333 vlan444 vlan666 vlan777 vlan888" 

vhid, das bei der Konfiguration von CARP verwendet wurde:
 CARP_VHID=1 

IP-Adressen im internen Netzwerk anderer Router werden bei Bedarf in der Reihenfolge ihrer Wichtigkeit einfach als ASERV_IP_2, ASERV_IP_3 usw. verwendet.
 ASERV_IP_1=192.168.0.2 

Anzahl der externen Verbindungskanäle:
 CNUMBER=2 

Einstellungen für den externen Hauptverbindungskanal:
Adaptername:
 EXT_0_IF=vlan111 

Nummer der Routing-Tabelle:
 RTABLE_0=0 

Standard-Gateway:
 DEFAULT_GATEWAY=2.2.2.1 

Einstellungen für den externen Backup-Verbindungskanal:
Adaptername:
 EXT_1_IF=vlan444 

Nummer der Routing-Tabelle:
 RTABLE_1=1 

Ein Standard-Gateway ist nicht erforderlich, da für alle Routing-Tabellen außer der Haupttabelle das rc.d-Skript setfib <Tabellennummer> verwendet wird, das, wie die Logik annimmt, mit der Tabellennummer übereinstimmen muss.
Parameter des Testmoduls:
Die Anzahl der zu überprüfenden Adressen:
 TNUMBER=2 

Adressen der Maschinen, von denen Ping-Anfragen gesendet werden. Verwenden Sie am besten im ersten Fall den Domainnamen und erst danach die IP-Adresse:
 PTARGET_0=ya.ru PTARGET_1=8.8.8.8 

Die Anzahl der pro Ziel gesendeten Ping-Pakete:
 PNUMBER=2 

Einstellungen des Richtermoduls
Die Anzahl der erfolgreichen Tests des Hauptkanals vor der Rückkehr. Die Rückkehrzeit zum Hauptkanal nach Wiederaufnahme seines Betriebs wird ungefähr durch die Formel berechnet: (WNUMBER + 1) * JUDGEPERIOD Sekunden.
 WNUMBER=3 

Einstellungen des Logger-Moduls
Diese beiden Parameter geben an, wie oft der Logger wiederkehrende Ereignisse aufzeichnet. Nach der Aufzeichnung des Ereignisses ist LOGFREQ2 die Anzahl der Wiederholungsversuche, wenn das nächste Mal die Anzahl der Wiederholungen von LOGFREQ1 gemeldet wird. Es werden nur Ereignisse in einer Reihe berücksichtigt.
 LOGFREQ1=5 LOGFREQ2=20 

Modulstart-Timer in Sekunden
Die Startphase des Tester-Moduls. Es ist sinnvoll, sich auf die Zeit fehlgeschlagener Versuche zu verlassen, um alle Ziele zu testen.
 TESTERPERIOD=240 

Die Startphase des Judge-Moduls. Installieren Sie nicht weniger als TESTERPERIOD.
 JUDGEPERIOD=300 

Die Startphase des Scout-Moduls.
 SCOUTPERIOD=360 

Die Wartezeit vor dem Überprüfen der Startzeitgeber der Tester- und Judge-Module. Es ist logisch, den Wert von TESTERPERIOD kleiner oder gleich zu setzen.
 SENSITIVITY=60 

Die Zeit, nach der ein Arbeitsmodul als aufgehängt gilt. Wird vom Watchdog-Modul verwendet.
 TESTERLIMIT=40 JUDGELIMIT=30 LOGGERLIMIT=20 SCOUTLIMIT=120 WATCHDOGLIMIT=150 

Pfade zu Dateien und Verzeichnissen
Pfad zum ipfw-Skript.
 FIRESCRIPT=/etc/firewall.sh 

Ipfw Einstellungen. Wenn die ipfw-Einstellungen nicht in eine separate Datei verschoben werden, ist FIRESCRIPT = FIRESETDEF.
 FIRESETDEF=/etc/firewall/config 

Pfad zu den ipfw-Einstellungen für den externen Hauptkanal:
 FIRESET_0=/etc/firewall/config_0 

Über den Pfad zu den ipfw-Einstellungen für den externen Sicherungskanal können Sie bei Bedarf weiter FIRESET_2 usw.:
 FIRESET_1=/etc/firewall/config_1 

Pfade zum Binden von Einstellungen
 BINDSETDEF=/usr/local/etc/namedb/named.conf 

Bindungseinstellungen für den externen Hauptkanal:
 BINDSET_0=/usr/local/etc/namedb/named.conf.0 

Bindungseinstellungen für den externen Sicherungskanal;
 BINDSET_1=/usr/local/etc/namedb/named.conf.1 

Pfade zu allen ausführbaren ToFoIn-Dateien:
 DAEMON=/local/sbin/tofoin/daemon.sh TESTER=/usr/local/sbin/tofoin/tester.sh JUDGE=/usr/local/sbin/tofoin/judge.sh LOGGER=/usr/local/sbin/tofoin/logger.sh SCOUT=/usr/local/sbin/tofoin/scout.sh WATCHDOG=/usr/local/sbin/tofoin/watchdog.sh 

Ereignisprotokoll. Diese Datei wird bei der Installation NICHT erstellt:
 LOGFILE=/var/log/tofoin.log 

Temporäre Dateien und Verzeichnisse werden beim Starten der entsprechenden Module erstellt, einige werden beim Stoppen gelöscht:
 DIR_TMP=/tmp/tofoin DIR_PID=/var/run/tofoin JUDGEMETER=/tmp/tofoin/judgemeter PREVSTATE=/tmp/tofoin/prevstate SCOUTGATE=/tmp/tofoin/scoutgate LOGTMP=/tmp/tofoin/logger.tmp LOGMETER=/tmp/tofoin/logmeter DAEMON_PID=/var/run/tofoin/daemon.pid TESTER_PID=/var/run/tofoin SCOUT_PID=/var/run/tofoin/scout.pid JUDGE_PID=/var/run/tofoin/judge.pid LOGGER_PID=/var/run/tofoin/logger.pid WATCHDOG_PID=/var/run/tofoin_watchdog.pid 


Zusammenfassung


Es stellte sich heraus, dass es sich um einen voll funktionsfähigen und zuverlässigen Satz von Skripten handelt, der die Aufgabe des Wechsels zum Arbeitskanal bei 2 Routern mit 2 externen Kommunikationskanälen gut bewältigt.

Pläne


Meine Pläne für dieses Projekt beziehen sich vielleicht darauf, von Bash auf Pure Sh umzuschreiben, um unnötige Software auf dem Server loszuwerden. Auf der anderen Seite funktioniert jetzt alles erstaunlich und ich habe wirklich keine Lust, mich in diesen Prozess einzumischen. Außerdem ist der Wechsel zu sh mit schrecklicheren Sprachkonstrukten behaftet, die notwendig sind, um das gleiche Ergebnis zu erzielen.
Im Übrigen lohnt es sich wahrscheinlich, die beste Implementierung von Testmodulen in Betracht zu ziehen.

Referenzen:


Vorheriger Artikel
ToFoIn-Projektseite auf gitlab

Source: https://habr.com/ru/post/de421857/


All Articles