شرح
في مقال سابق ، تم النظر في تنظيم التكرار لبوابات LAN. كحل ، تم اقتراح برنامج نصي يحل المشكلة في ذلك الوقت ، ولكن لديه عدد من العوائق. بعد مرور بعض الوقت ، اتضح إزالة هذه العيوب ، وإعادة كتابة الشفرة جزئيًا والحصول على شيء مقبول في الإخراج. يمكننا الآن أن نقول أن النصوص البرمجية قد تم اختبارها بما يكفي لتسمى مستقرة. لتبسيط فهم النظام بأكمله ، سيتم تكرار النقاط الرئيسية لإعداد الخدمات الثانوية (من حيث موضوع المقالة) جزئيًا أدناه. والسبب بسيط - خلال هذا الوقت تم إعادة صياغة قواعد ipfw أيضًا ، وذهب نظام أسماء النطاقات للعيش في AD على Samba4 باستخدام واجهة أمامية وسجلات محدثة بشكل آمن من isc-dhcpd باستخدام kerberos ، بالإضافة إلى خوادم dns الثانوية كربط على العبّارات ، تكوين CARP ... بشكل عام ، أصبح أكثر إثارة للاهتمام ، ولكن المزيد حول ما وكيف يعمل أدناه. كل شيء يمكن إعطاؤه مع مراجع للمصدر سيتم تصميمه بحيث لا ينتج جوهرًا. ما تم أخذه من أي مكان آخر ، ولكن لم يعد متاحًا ، سيتم تقديمه هنا مع التعليقات ذات الصلة.
مقدمة
لذا ، هناك طريقتان لزيادة مناعة الضوضاء لقناة الاتصال مع العالم الخارجي من جانب المستهلك: النسخ الاحتياطي للبوابة وحجز نقطة الاتصال. وبعبارة أخرى ، في الحالة الأولى ، يتم إنشاء بوابة ثانية ، والتي سيتم تفعيلها إذا فشلت الأولى ، في الحالة الثانية ، سيتم تنظيم قناة إنترنت احتياطية في حالة وجود أي مشاكل مع القناة الرئيسية ، وكلما تقاطعوا ، كلما كان ذلك أفضل ، من الواضح. إذا قام
CARP بحل المهمة الأولى لـ FreeBSD ، فإن المهمة الثانية ، بعد تنظيم القناة الخارجية الثانية ، يمكن حلها مرة أخرى بعدة طرق. كحد أدنى ، يمكنك تنظيم موازنة حركة المرور أو التبديل بين القنوات. نظرًا للاختلاف الكبير في عرض النطاق الترددي للقنوات الخارجية ، لم يكن الخيار الأول يناسبني ، لذلك تمت كتابة المتهم الرئيسي في المنشور:
ToFoIn - مجموعة من نصوص bash التي تهدف إلى حل المشكلات التشخيصية والتحول إلى قناة خارجية عاملة. بعد التنقيح ، يمكن تطبيقه على n مداخل وقنوات m. الوضع ضعيف ، حيث n و m أكثر من 2 ، ولكن يجب أن تعمل النصوص أدناه على قيم كبيرة من n و m ، لأن منطقيا ، لم يتم تعيين الحد. بشكل عام ، أظن أنه باستخدام هذه البرامج النصية من الممكن حل مجموعة واسعة إلى حد ما من المهام ، اعتمادًا على حالة الاتصال ، المحدود ، ربما ، فقط عن طريق الخيال.

تقريبًا ، تفترض بنية الشبكة هذه في أبسط شكل استخدام مجموعة من البرامج النصية ToFoIn. بالطبع ، يجب أن تعمل البرامج النصية في حالة جهاز توجيه واحد ، ولكن في هذه الحالة ، سيكون عليك تغيير وحدة Daemon بشدة لإزالة اعتماد تسلسل الإجراءات على حالة CARP ، والتي ستكون ببساطة غائبة في النظام. مزيد من الحجز لهذه العقد وغيرها يعتمد فقط على درجة أهمية الخدمات المقابلة.
الأهداف والغايات
الهدف من المشروع ، كما كان من قبل ، هو إنشاء حزمة برامج عالمية وقابلة للتطوير بسهولة تركز على تحديد المشاكل في الاتصالات الخارجية والداخلية والتحول تلقائيًا إلى الاتصالات القابلة للتطبيق. بشكل عام ، المنطق هو كما يلي:
- هناك n "موجهات" بقنوات m على كل منها. علاوة على ذلك ، فإن جميع "الموجهات" في تسلسل هرمي صارم ومتصلة ببعضها البعض باستخدام CARP على جميع الواجهات الضرورية.
- يعمل الوكيل بشكل مستقل على كل آلة ، والتي تعتمد مهمتها على حالة CARP الحالية لآلة:
- في حالة النسخ الاحتياطي - اكتشاف الجهاز وتكوينه على جهاز توجيه هو الرئيسي حاليًا ؛
- إذا كان سيدًا - تحقق من حالة الاتصالات في الوقت الحالي ، وإذا لزم الأمر ، قم بالتبديل بين القنوات الخارجية.
الحل
تحتوي الشبكة الداخلية على CARP ، الذي يوفر بوابات النسخ الاحتياطي ويسمح لك بعدم تغيير إعدادات أجهزة الشبكة الأخرى على الشبكة الداخلية عند تبديل القنوات.
تعمل Dhcpd في الوضع الأساسي - الثانوي ، وبصفة عامة ، لا يهم الأدوار الأخرى التي تلعبها أجهزتها - يحدث الاتصال بين dhcpd على الشبكة الداخلية ، والتي تنظر إليها أجهزة التوجيه دائمًا.
تتم إزالة الربط الرئيسي في AD ، وهو مخفي في الشبكة المحلية ، بينما تعمل خوادم الربط الثانوية على أجهزة توجيه البوابة على قدم المساواة.
تختلف قواعد ipfw اعتمادًا على القناة التي تعتبر القناة الرئيسية في لحظة معينة ويتم إعادة تشغيلها بواسطة وحدة Daemon عند تغيير الأدوار.
وأخيرا حول النصوص نفسها. الآن الملفات موجودة في الدلائل المناسبة ، والعمل من المستخدمين لديهم ولها برنامج نصي البداية في rc.d. المهام التي تتطلب الوصول إلى الجذر يتم حلها بواسطة sudo. هناك برنامج نصي للتثبيت يأخذ في الاعتبار الوجود المحتمل لنسخة مثبتة ، بالإضافة إلى ملف إعدادات مفصل إلى حد ما. الوحدات هي نفسها مع تغييرات طفيفة ، وبعضها لم يتغير تقريبًا في الوظائف:
البرنامج الخفي - كما يوحي الاسم - هو العملية الرئيسية التي تدير الاختبار وتبديل الوحدات على جهاز توقيت ، كما تراقب CARP.
المختبر - لا يزال يختبر الاتصالات الخارجية باستخدام الأمر ping. (إذا كان يعمل ، فإنه يعتبر أن الجهاز لديه CARP في الحالة الرئيسية)
الحكم - استنادًا إلى نتائج الاختبار ، ويحدد القناة الخارجية التي تعمل وما إذا كان التبديل ضروريًا ، ويؤدي التبديل (إذا كان قيد التشغيل ، فإنه يعتبر أن الجهاز لديه CARP في الحالة الرئيسية).
الكشافة هي وحدة جديدة. يبدأ عندما يكون CARP في حالة النسخ الاحتياطي. من الضروري تحديد أي من أجهزة التوجيه المتبقية هي حاليًا الجهاز الرئيسي.
مسجل - مسؤول عن تسجيل الأحداث. من الضروري عدم تكرار المعلومات حول الأحداث وتسهيل قراءة المجلة.
الوكالة الدولية للطاقة - تعمل في الموعد المحدد من crontab. يحدد "تجمد" جميع الوحدات ويحاول (كلما أمكن) حل المشاكل التي نشأت. على سبيل المثال ثبت الجميع بكل بساطة.
بالإضافة إلى النصوص نفسها ، يجدر النظر في بعض الملفات الأكثر أهمية:
Tofoin.conf - ملف إعدادات واحد.
Tofoin.log - ملف سجل حدث واحد.
النتيجة _ <رقم القناة الداخلي> هو ملف العمل ، تتم إضافة نتائج الاختبار هنا ، ويتم إنشاؤها في / tmp بجوار .pid وملفات العمل الأخرى.
يمكنني بكل سرور الإجابة على الأسئلة المتعلقة بتشغيل الوحدات ، مع شرح الحلول في التعليقات.
الجزء الفني
المعدات
مقارنة بالوقت السابق ، انتقلت البوابات إلى P4 ، واستقبلت 1536 ميجابايت من ذاكرة الوصول العشوائي وثلاثة محركات أقراص صلبة سعة 40 جيجابايت (مرآة + احتياطية). بطاقات الشبكة لا تزال PCI ، PSUs طبيعية ، وبطبيعة الحال في وجود UPS.
ترتبط الزيادة في السعة بالحديد المنطلق والتحديث الممل للغاية من المصدر ، ولكن في الغالب الأول. OS FreeBSD 11.1، FS zfs.
إعدادات مكونات النظام
مزيد من التفاصيليتم تجميع النواة بمثل هذه المعلمات الإضافية (يمكن أيضًا تعيين شيء ما في اللودر ، ولكن من الأفضل بهذه الطريقة):
options IPFIREWALL
الإعدادات / 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
الإعدادات /etc/rc.conf على الجهاز الأول (تكوين CARP ذو أهمية رئيسية):
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"
وسيلة الإيضاح:
eth0، eth1، eth2 - محولات مادية
vlan666 ، vlan777 ، vlan888 - محولات LAN ظاهرية ،
vlan222 و vlan555 - محولات للاتصال الزائد بين بطاقات الشبكة الخارجية (قد لا تكون هناك حاجة إليهما ، فقد تم استخدامهما بنشاط في وقت سابق)
vlan111 - القناة الخارجية الرئيسية
vlan444 - قناة خارجية احتياطية
vlan333 - الاتصالات الهاتفيةالإعدادات /etc/rc.conf على الجهاز الثاني (تكوين CARP هو محل اهتمام رئيسي ، تتم إزالة بعض الأسطر المكررة):
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"
بعض القواعد التي تكون مفيدة عند تكوين ipfw (nat) هي:
للسماح بحركة مرور CARP:
/sbin/ipfw -q add allow carp from any to any
نات "النووية":
/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
استخدام جداول توجيه محددة مع محولات محددة:
/sbin/ipfw -q add setfib 0 all from any to any via vlan666
بشكل عام ، يمكنني كتابة مقالة منفصلة حول إعدادات ipfw التي استخدمتها ، ولكن هذا وقت آخر.
برامج الطرف الثالث
مزيد من التفاصيلنظرًا لضرورة العمل في وقت واحد مع قناتين خارجيتين أو أكثر ، فمن المناسب أن يكون لديك العديد من جداول التوجيه ، واحدة لكل قناة. وسيكون من الجيد إذا تم إنشاء هذه الجداول عند بدء التشغيل نفسها. سيساعد البرنامج النصي rc.d setfib. يفترض المنطق المستخدم في ToFoIn أن اسم الملف (setfib1 ، setfib2 ، إلخ.) يطابق رقم الجدول الذي يضيف إليه نص برمجي مسار افتراضي. يحتوي الجدول افتراضيًا على الرقم "0".
خوادم DNS مع Bind في الدور الرئيسي تعمل في الوضع الثانوي ، الرئيسي هو samba4 + bind ، مخفي في الشبكة المحلية. تم الكشف عن إعداد ربط ثانوي بشكل جميل في كتاب Cricket Lee و Paul Albitz "DNS و BIND". لا أتذكر أي متطلبات خاصة تأخذ في الاعتبار استخدام samba4 للخوادم الثانوية ، ولا أذكرها في ملف الإعدادات. ما لم ، لقنوات الإنترنت المختلفة ، قد تحتاج إلى إنشاء ملفين مختلفين ، سيتم نسخهما بعد ذلك بواسطة البرنامج النصي ToFoIn إلى المكان الذي سيتم قراءته من خلاله. ويرجع ذلك إلى حقيقة أنه عند تحديد عناوين خوادم DNS لكلا الموفرين في نفس الملف ، مع الأخذ في الاعتبار أن الربط يعمل مع جدول توجيه واحد فقط ، تنشأ مشكلة بشأن حل العناوين من خوادم المنبع التي يتعذر الوصول إليها في لحظة معينة.
تجاوز الفشل isc-dhcpd. Dhcpd ليس ضروريًا لـ ToFoIn ، علاوة على ذلك ، لن يؤثر غيابه على البرامج النصية على الإطلاق ، ومع ذلك ، كما يبدو لي ، فمن المنطقي تمامًا وضع خوادم dhcp على بوابات ومن ثم لا يزال سؤال الفشل ينشأ. وهنا ، بالمقارنة مع المرة الأخيرة ، أصبح الأمر أكثر إثارة للاهتمام ... بالإضافة إلى الإعدادات اللازمة للفشل ، التي وصفتها
في المرة السابقة (بداية قسم "الإعداد المسبق" داخل القائمة المنسدلة).
ستحتاج أيضًا إلى برنامج نصي لتحديث سجلات DNS بأمان في AD باستخدام samba4. يجب تثبيت خادم samba4 نفسه فقط. الإعداد والتشغيل غير مطلوبين ، نحن مهتمون فقط بأدوات الإدارة التي تأتي مع المجموعة. أولئك الذين يرغبون في العثور على معلومات أخرى في قسم "DHCP مع تحديثات DNS الحيوية"
على .
يبدو مخيفا ، لكنه يعمل.
على هذا مع الانتهاء من تكوين برنامج طرف ثالث.
قليلا عن ToFoIn
جميع نصوص المشروع بالإضافة إلى نص التثبيت متاح على
gitlab .
في الختام ، يتم اعتبار مثال لمعلمات ملف إعدادات ToFoIn:عدد الموجهات المستخدمة في النظام:
RNUMBER=2
عند استخدام شبكات فرعية إضافية ، يجب عليك تعيين مسار افتراضي عندما يصبح جهاز التوجيه هو المسار الأساسي. هنا يمكنك تحديد رقم ملف setfib المقابل الذي سيتم إعادة تشغيله. في هذا المثال ، setfib2:
ADDITLAN=2
اسم المحول الداخلي:
INT_IF=vlan666
جميع الواجهات الأخرى التي يتم توصيل أجهزة التوجيه بها عبر CARP. مطلوب للتحكم والحفاظ على نفس الحالة لجميع الواجهات:
ALL_IF="vlan111 vlan333 vlan444 vlan666 vlan777 vlan888"
vhid الذي تم استخدامه عند تكوين CARP:
CARP_VHID=1
يتم استخدام عناوين IP في الشبكة الداخلية لأجهزة التوجيه الأخرى حسب الأهمية ، إذا لزم الأمر ، ثم ASERV_IP_2 ، ASERV_IP_3 ، وما إلى ذلك.
ASERV_IP_1=192.168.0.2
عدد قنوات الاتصال الخارجية:
CNUMBER=2
إعدادات قناة الاتصال الخارجية الرئيسية:
اسم المحول:
EXT_0_IF=vlan111
رقم جدول التوجيه:
RTABLE_0=0
البوابة الافتراضية:
DEFAULT_GATEWAY=2.2.2.1
إعدادات قناة الاتصال الخارجية الاحتياطية:
اسم المحول:
EXT_1_IF=vlan444
رقم جدول التوجيه:
RTABLE_1=1
العبّارة الافتراضية غير مطلوبة ، لأنه بالنسبة لجميع جداول التوجيه ، باستثناء الجدول الرئيسي ، يتم استخدام setfib rc.d script <رقم الجدول> ، والذي ، كما يفترض المنطق ، يجب أن يطابق رقم الجدول.
معلمات وحدة اختبار:
عدد العناوين المطلوب فحصها:
TNUMBER=2
عناوين الآلات التي يتم من خلالها إرسال طلبات ping. من الأفضل استخدام اسم المجال في الحالة الأولى ، وبعد ذلك فقط عنوان IP:
PTARGET_0=ya.ru PTARGET_1=8.8.8.8
عدد حزم ping المرسلة لكل هدف:
PNUMBER=2
إعدادات وحدة التحكيم
عدد الاختبارات الناجحة للقناة الرئيسية قبل العودة إليها. يتم تقريب وقت العودة إلى القناة الرئيسية بعد استئناف عملها تقريبًا بالصيغة: (WNUMBER + 1) * ثوان JUDGEPERIOD.
WNUMBER=3
إعدادات وحدة التسجيل
تشير هاتان المعلمتان إلى عدد المرات التي يقوم فيها المسجل بتسجيل الأحداث المتكررة. بعد تسجيل الحدث ، في المرة التالية التي يتم فيها الإبلاغ عن عدد مرات إعادة المحاولة LOGFREQ1 ، يكون LOGFREQ2 هو عدد مرات إعادة المحاولة. تؤخذ فقط الأحداث المتتالية في الاعتبار.
LOGFREQ1=5 LOGFREQ2=20
توقيت بدء الوحدة في ثوان
فترة إطلاق وحدة الاختبار. من المنطقي الاعتماد على وقت المحاولات الفاشلة لاختبار جميع الأهداف.
TESTERPERIOD=240
فترة إطلاق وحدة القاضي. لا تقم بتثبيت أقل من TESTERPERIOD.
JUDGEPERIOD=300
فترة إطلاق الوحدة الكشفية.
SCOUTPERIOD=360
فترة الانتظار قبل التحقق من توقيت البدء لوحدتي الاختبار والقاضي. من المنطقي تعيين أقل من أو يساوي TESTERPERIOD.
SENSITIVITY=60
الوقت الذي يتم بعده تعليق وحدة العمل. مستخدم بواسطة وحدة المراقبة.
TESTERLIMIT=40 JUDGELIMIT=30 LOGGERLIMIT=20 SCOUTLIMIT=120 WATCHDOGLIMIT=150
مسارات الملفات والأدلة
مسار البرنامج النصي ipfw.
FIRESCRIPT=/etc/firewall.sh
إعدادات Ipfw. إذا لم يتم نقل إعدادات ipfw إلى ملف منفصل ، فإن FIRESCRIPT = FIRESETDEF.
FIRESETDEF=/etc/firewall/config
مسار إعدادات ipfw للقناة الخارجية الرئيسية:
FIRESET_0=/etc/firewall/config_0
المسار إلى إعدادات ipfw للقناة الخارجية الاحتياطية ، إذا لزم الأمر ، يمكنك الاستمرار في FIRESET_2 ، وما إلى ذلك:
FIRESET_1=/etc/firewall/config_1
مسارات لربط الإعدادات
BINDSETDEF=/usr/local/etc/namedb/named.conf
إعدادات الربط للقناة الخارجية الرئيسية:
BINDSET_0=/usr/local/etc/namedb/named.conf.0
ربط الإعدادات للقناة الخارجية الاحتياطية ، إذا لزم الأمر ، يمكنك متابعة BINDSET_2 أخرى ، وما إلى ذلك:
BINDSET_1=/usr/local/etc/namedb/named.conf.1
المسارات إلى جميع الملفات التنفيذية في ToFoIn:
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
سجل الأحداث. لم يتم إنشاء هذا الملف عند التثبيت الآن:
LOGFILE=/var/log/tofoin.log
يتم إنشاء الملفات والأدلة المؤقتة عند بدء تشغيل الوحدات المقابلة ، ويتم حذف بعضها عند إيقافها:
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
الملخص
وتبين أنها مجموعة من البرامج النصية التي تعمل بكفاءة وموثوقية تتوافق بشكل جيد مع مهمة التبديل إلى قناة العمل في حالة وجود جهازي توجيه مع قناتي اتصال خارجيتين.
الخطط
تتعلق خططي لهذا المشروع ، ربما ، بإعادة الكتابة من bash إلى sh الخالص للتخلص من البرامج غير الضرورية على الخادم. من ناحية أخرى ، يعمل كل شيء الآن بشكل مثير للدهشة ولا أشعر حقًا بالتدخل في هذه العملية ، بالإضافة إلى أن التحول إلى sh محفوف ببنية لغة أكثر فظاعة ضرورية لتحقيق نفس النتيجة.
للبقية ، ربما ، سيكون من المفيد النظر في أفضل تطبيق لوحدات الاختبار.
المراجع:
←
المادة السابقة→
صفحة مشروع ToFoIn على gitlab