استخدم المهام متعددة المثيلات لتشغيل تطبيقات واجهة تمرير الرسائل (MPI) دفعة واحدة

تتيح لك المهام متعددة المثيلات تشغيل مهمة Azure Batch على عقد حوسبة متعددة في وقت واحد. تتيح هذه المهام سيناريوهات حوسبة عالية الأداء مثل تطبيقات واجهة تمرير الرسائل (MPI) دفعة واحدة. في هذه المقالة، ستتعلم كيفية تنفيذ المهام متعددة المثيلات باستخدام مكتبة Batch .NET.

ملاحظة

بينما تركز الأمثلة في هذه المقالة على عقد الحوسبة Batch .NET و MS-MPI و Windows، فإن مفاهيم المهام متعددة المثيلات التي تمت مناقشتها هنا تنطبق على الأنظمة الأساسية والتقنيات الأخرى (Python و Intel MPI على عقد Linux، على سبيل المثال).

نظرة عامة على مهمة متعددة المثيلات

في الدُفعة، يتم تنفيذ كل مهمة عادةً على عقدة حساب واحدة - فأنت ترسل مهامًا متعددة إلى مهمة، وتقوم خدمة الدُفعة بجدولة كل مهمة للتنفيذ على عقدة. ومع ذلك، عن طريق تكوين إعدادات متعددة مثيلاتمهمة، يمكنك إخبار الدُفعة بإنشاء مهمة أساسية واحدة وعدة مهام فرعية يتم تنفيذها بعد ذلك على عقد متعددة.

رسم تخطيطي يعرض نظرة عامة على إعدادات المثيلات المتعددة.

عند إرسال مهمة ذات إعدادات متعددة المثيلات إلى مهمة، تنفذ الدُفعة عدة خطوات فريدة للمهام متعددة المثيلات:

  1. تنشئ خدمة الدفعة مجموعة أساسية واحدة وعدة المهام الفرعية استنادا إلى إعدادات المثيلات المتعددة. يطابق العدد الإجمالي للمهام (الأساسي بالإضافة إلى كافة المهام الفرعية) عدد المثيلات (حساب العقد) التي تحددها في إعدادات المثيلات المتعددة.
  2. تعيين الدفعة إحدى العقد المحسوبة كعقد رئيسيةوجدولة المهمة الأساسية لتنفيذها على الرئيسي. جدولة المهام الفرعية لتنفيذ على باقي العقد حساب المخصصة للمهمة متعدد المثيلات، تسك فرعي واحد لكل عقدة.
  3. تقوم المهام الأساسية وجميع المهام الفرعية بتنزيل أي ملفات موارد شائعة تحددها في إعدادات المثيلات المتعددة.
  4. بعد تحميل ملفات الموارد الشائعة، تقوم المهام الأساسية و الفرعية بتنفيذ أمر التنسيق الذي تحدده في إعدادات المثيلات المتعددة. يتم استخدام الأمر التنسيق عادة لإعداد العقد لتنفيذ المهمة. يمكن أن يتضمن ذلك بدء تشغيل خدمات الخلفية (مثل Microsoft MPIsmpd.exe) والتحقق من أن العقد جاهزة لمعالجة الرسائل بين العقد.
  5. المهمة الأساسية بتنفيذ الأمر التطبيق على العقدة الرئيسية بعد إتمام الأمر التنسيق بنجاح بواسطة الأساسي و كافة المهام الفرعية. الأمر التطبيق هو سطر الأوامر للمهمة متعددة المثيلات نفسها، ويتم تنفيذه فقط بواسطة المهمة الأساسية. في حل يستند إلى MS-MPI هذا هو المكان الذي تقوم فيه بتنفيذ التطبيق الذي تم تمكين MPI باستخدام mpiexec.exe.

ملاحظة

على الرغم من أنها مميزة وظيفيا، "المهمة متعددة المثيلات" ليست نوع مهمة فريدة مثل StartTask أو JobPreparationTask. المهمة متعددة المثيلات هي ببساطة مهمة دفعية قياسية(CloudTask في Batch.NET) تم تكوين إعدادات المثيلات المتعددة الخاصة بها. في هذه المقالة، نشير إلى هذا كمهمة متعددة المثيلات.

متطلبات المهام متعددة المثيلات

تتطلب المهام متعددة المثيلات تجمعا مع تمكين الاتصال بين العقدة، ومع تعطيل تنفيذ المهام المتزامنة. لتعطيل تنفيذ المهمة المتزامنة تعيين الخاصية CloudPool.TaskSlotsPerNode إلى 1.

ملاحظة

تحد المجموعة من حجم تجمع تم تمكين الاتصال بين العقد.

يوضح هذا التعليمات البرمجية المتكررة كيفية إنشاء تجمع للمهام متعددة المثيلات باستخدام مكتبة Batch.NET.

CloudPool myCloudPool =
    myBatchClient.PoolOperations.CreatePool(
        poolId: "MultiInstanceSamplePool",
        targetDedicatedComputeNodes: 3
        virtualMachineSize: "standard_d1_v2",
        VirtualMachineConfiguration: new VirtualMachineConfiguration(
        imageReference: new ImageReference(
                        publisher: "MicrosoftWindowsServer",
                        offer: "WindowsServer",
                        sku: "2019-datacenter-core",
                        version: "latest"),
        nodeAgentSkuId: "batch.node.windows amd64");

// Multi-instance tasks require inter-node communication, and those nodes
// must run only one task at a time.
myCloudPool.InterComputeNodeCommunicationEnabled = true;
myCloudPool.TaskSlotsPerNode = 1;

ملاحظة

إذا حاولت تشغيل مهمة متعددة المثيلات في تجمع مع تعطيل الاتصال الداخلي أو مع قيمة taskSlotsPerNode أكبر من 1، المهمة غير المجدولة أبدا--يبقى إلى أجل غير مسمى في الحالة "نشط".

لن تسمح التجمعات التي تم تمكين InterComputeNodeCommunication تلقائيا بإلغاء توفير العقدة.

استخدام StartTask لتثبيت MPI

لتشغيل تطبيقات MPI مع مهمة متعددة المثيلات، تحتاج أولا إلى تثبيت تطبيق MPI (MS-MPI أو Intel MPI، على سبيل المثال) على العقد حساب في التجمع. هذا هو الوقت المناسب لاستخدام StartTask، الذي ينفذ كلما عقدة ينضم تجمع أو إعادة تشغيل. ينشئ هذا التعليمات البرمجية المتكررة StartTask الذي يحدد حزمة الإعداد MS-MPI كملف مورد. يتم تنفيذ سطر الأوامر الخاص بالمهمة بدء بعد تحميل ملف المورد إلى العقدة. في هذه الحالة، ينفذ سطر الأوامر تثبيت غير مراقب من MS-MPI.

// Create a StartTask for the pool which we use for installing MS-MPI on
// the nodes as they join the pool (or when they are restarted).
StartTask startTask = new StartTask
{
    CommandLine = "cmd /c MSMpiSetup.exe -unattend -force",
    ResourceFiles = new List<ResourceFile> { new ResourceFile("https://mystorageaccount.blob.core.windows.net/mycontainer/MSMpiSetup.exe", "MSMpiSetup.exe") },
    UserIdentity = new UserIdentity(new AutoUserSpecification(elevationLevel: ElevationLevel.Admin)),
    WaitForSuccess = true
};
myCloudPool.StartTask = startTask;

// Commit the fully configured pool to the Batch service to actually create
// the pool and its compute nodes.
await myCloudPool.CommitAsync();

الوصول المباشر إلى الذاكرة.

عند اختيار حجم RDMA قادرة مثل A9 للعقد حساب في تجمع الدفعات الخاص بك، يمكن تطبيق MPI الاستفادة من Azure عالية الأداء، منخفضة الكمون الوصول المباشر إلى الذاكرة (RDMA) شبكة الاتصال.

ابحث عن الأحجام المحددة ك "RDMA قادرة" في أحجام الأجهزة الظاهرية في Azure (لتجمعات تكوين VirtualMachine) أو أحجام الخدمات السحابية (لتجمعات CloudServicesConfiguration).

ملاحظة

للاستفادة من RDMA على لينكس حساب العقد، يجب عليك استخدام إنتل MPI على العقد.

إنشاء مهمة متعددة المثيلات مع Batch .NET

الآن بعد أن قمنا بتغطية متطلبات التجمع وتركيب حزمة MPI، دعونا إنشاء مهمة متعددة المثيلات. في هذا المقتطف، نقوم بإنشاء CloudTaskقياسي، ثم تكوين الخاصية MultiInstanceSettings الخاصة به. كما ذكر سابقا، المهمة متعددة المثيلات ليست نوع مهمة مميزة، ولكن مهمة دفعة قياسية تم تكوينها مع إعدادات متعددة المثيلات.

// Create the multi-instance task. Its command line is the "application command"
// and will be executed *only* by the primary, and only after the primary and
// subtasks execute the CoordinationCommandLine.
CloudTask myMultiInstanceTask = new CloudTask(id: "mymultiinstancetask",
    commandline: "cmd /c mpiexec.exe -wdir %AZ_BATCH_TASK_SHARED_DIR% MyMPIApplication.exe");

// Configure the task's MultiInstanceSettings. The CoordinationCommandLine will be executed by
// the primary and all subtasks.
myMultiInstanceTask.MultiInstanceSettings =
    new MultiInstanceSettings(numberOfNodes) {
    CoordinationCommandLine = @"cmd /c start cmd /c ""%MSMPI_BIN%\smpd.exe"" -d",
    CommonResourceFiles = new List<ResourceFile> {
    new ResourceFile("https://mystorageaccount.blob.core.windows.net/mycontainer/MyMPIApplication.exe",
                     "MyMPIApplication.exe")
    }
};

// Submit the task to the job. Batch will take care of splitting it into subtasks and
// scheduling them for execution on the nodes.
await myBatchClient.JobOperations.AddTaskAsync("mybatchjob", myMultiInstanceTask);

المهمة الأساسية والمهام الفرعية

عند إنشاء إعدادات مثيل متعددة لمهمة، يمكنك تحديد عدد العقد حساب لتنفيذ المهمة. عند إرسال المهمة إلى مهمة، تنشئ خدمة الدفعة مهمة أساسية واحدة والمهام الفرعية الكافية التي تتطابق مع عدد العقد التي حددتها.

يتم تعيين هذه المهام معرف عدد صحيح في نطاق 0 إلى numberOfInstances - 1. المهمة مع معرف 0 هي المهمة الأساسية، وجميع معرفات أخرى هي المهام الفرعية. على سبيل المثال، إذا قمت بإنشاء الإعدادات متعددة المثيلات التالية لمهمة، سيكون للمهمة الأساسية معرف 0، وسيكون لدى المهام الفرعية معرفات من 1 إلى 9.

int numberOfNodes = 10;
myMultiInstanceTask.MultiInstanceSettings = new MultiInstanceSettings(numberOfNodes);

العقدة الرئيسية.

عند إرسال مهمة متعددة المثيلات، يعين خدمة الدفعة إحدى العقد حساب العقدة "الرئيسي" وجداول المهمة الأساسية لتنفيذ على العقدة الرئيسية. تمت جدولة المهام الفرعية للتنفيذ على باقي العقد المخصصة للمهمة متعددة المثيلات.

قيادة التنسيق

يتم تنفيذ الأمر التنسيق بواسطة كل من المهام الأساسية و الفرعية.

استدعاء الأمر التنسيق هو حظر--لا ينفذ الدفعة الأمر التطبيق حتى يتم إرجاع الأمر التنسيق بنجاح لكافة المهام الفرعية. ولذلك يجب أن يبدأ الأمر التنسيق أية خدمات خلفية مطلوبة، والتحقق من أنها جاهزة للاستخدام، ثم إنهاء. على سبيل المثال، هذا الأمر التنسيق لحل باستخدام MS-MPI الإصدار 7 يبدأ تشغيل خدمة SMPD على العقدة، ثم إنهاء:

cmd /c start cmd /c ""%MSMPI_BIN%\smpd.exe"" -d

لاحظ استخدام start في هذا الأمر التنسيق. وهذا مطلوب لأن smpd.exe التطبيق لا يعود مباشرة بعد التنفيذ. بدون استخدام الأمر بدء تشغيل، لن يعود أمر التنسيق هذا، وبالتالي يمنع الأمر التطبيق من التشغيل.

أمر التطبيق

بمجرد انتهاء المهمة الأساسية وكافة المهام الفرعية من تنفيذ أمر التنسيق، يتم تنفيذ سطر الأوامر للمهمة متعددة المثيلات بواسطة المهمة الأساسية فقط. نطلق على هذا الأمر التطبيق لتمييزه عن أمر التنسيق.

بالنسبة لتطبيقات MS-MPI، استخدم الأمر التطبيق لتنفيذ التطبيق الذي تم تمكين MPI الخاص بك باستخدام mpiexec.exe. على سبيل المثال، هنا أمر تطبيق لحل باستخدام MS-MPI الإصدار 7:

cmd /c ""%MSMPI_BIN%\mpiexec.exe"" -c 1 -wdir %AZ_BATCH_TASK_SHARED_DIR% MyMPIApplication.exe

ملاحظة

لأن يستخدم MS-MPI mpiexec.exeCCP_NODES المتغير بشكل افتراضي (راجع متغيرات البيئة)، المثال سطر الأوامر التطبيق أعلاه يستبعد ذلك.

متغيرات البيئة.

تُنشئ الدُفعة متغيرات بيئة متعددة خاصة بمهام متعددة المثيلات على العقد حساب المخصصة لمهمة متعددة المثيلات. يمكن أن تشير خطوط أوامر التنسيق والتطبيق إلى متغيرات البيئة هذه، كما يمكن للبرامج النصية والبرامج التي تنفذها.

يتم إنشاء متغيرات البيئة التالية بواسطة خدمة الدفعة للاستخدام بواسطة مهام متعددة المثيلات:

  • CCP_NODES
  • AZ_BATCH_NODE_LIST
  • AZ_BATCH_HOST_LIST
  • AZ_BATCH_MASTER_NODE
  • AZ_BATCH_TASK_SHARED_DIR
  • AZ_BATCH_IS_CURRENT_NODE_MASTER

للحصول على تفاصيل كاملة حول هذه ومتغيرات بيئة عقدة الحوسبة الدفعية الأخرى، بما في ذلك محتوياتها وإمكانية رؤيتها، راجع حساب متغيرات بيئة العقدة.

تلميح

يحتوي نموذج التعليمات البرمجية MpI Batch Linux على مثال عن كيفية استخدام العديد من متغيرات البيئة.

ملفات الموارد

هناك مجموعتان من ملفات الموارد للنظر في المهام متعددة المثيلات: ملفات الموارد الشائعة التي تقوم كافة المهام بتنزيلها (الأساسية والمهام الفرعية)، وملفات الموارد المحددة للمهمة متعددة المثيلات نفسها، والتي يتم تنزيلها فقط للمهمة الأساسية.

يمكنك تحديد ملف مورد واحد أو أكثر من ملفات الموارد الشائعة في إعدادات المثيلات المتعددة لمهمة. يتم تحميل هذه الملفات الموارد الشائعة من موقع تخزين Azure في الدليل المشترك لمهمة كل عقدة بواسطة الأساسي و كافة المهام الفرعية. يمكنك الوصول إلى الدليل المشترك للمهمة من سطور أوامر التطبيق والتنسيق باستخدام متغير البيئة AZ_BATCH_TASK_SHARED_DIR. المسار AZ_BATCH_TASK_SHARED_DIR متطابق على كل عقدة المخصصة للمهمة متعددة المثيلات، وبالتالي يمكنك مشاركة أمر تنسيق واحد بين الأساسي و كافة المهام الفرعية. لا تقوم الدُفعة "بمشاركة" الدليل بمعنى الوصول عن بُعد، ولكن يمكنك استخدامه كنقطة تحميل أو مشاركة كما هو مذكور سابقًا في تلميح حول متغيرات البيئة.

يتم تحميل ملفات الموارد التي تحددها للمهمة متعددة المثيلات نفسها إلى دليل العمل الخاص بالمهمة AZ_BATCH_TASK_WORKING_DIR بشكل افتراضي. كما ذكرنا، على عكس ملفات الموارد الشائعة، تقوم المهمة الأساسية فقط بتنزيل ملفات الموارد المحددة للمهمة متعددة المثيلات نفسها.

هام

استخدم متغيرات البيئة دوما AZ_BATCH_TASK_SHARED_DIR ثم AZ_BATCH_TASK_WORKING_DIR للإشارة إلى هذه الدلائل في سطور الأوامر. لا تحاول إنشاء المسارات يدويا.

مدة عمل المهمة

يتحكم عمر المهمة الأساسية في عمر المهمة متعددة المثيلات بأكملها. عند إنهاء الأساسي، يتم إنهاء كافة المهام الفرعية. رمز إنهاء الأساسي هو رمز إنهاء المهمة، وبالتالي يتم استخدامه لتحديد نجاح أو فشل المهمة لأغراض إعادة المحاولة.

في حال فشل أي من المهام الفرعية إنهاء مع رمز إرجاع غير صفري على سبيل المثال، تفشل المهمة متعددة المثيلات بأكملها. ثم يتم إنهاء المهمة متعددة المثيلات وإعادة محاولة، حتى حد إعادة المحاولة الخاص بها.

عند حذف مهمة متعددة المثيلات، يتم حذف المهام الفرعية الأساسية وكافة أيضا بواسطة خدمة الدفعة. يتم حذف كافة الدلائل الفرعية وملفاتها من العقد حساب تماما كما لمهمة قياسية.

TaskConstraints لمهمة متعددة المثيلات، مثل MaxTaskRetryCountوMaxWallClockTimeوخصائص RetentionTime يتم تكريمها كما هي لمهمة قياسية، وتنطبق على المهام الأساسية وكافة المهام الفرعية. ومع ذلك، إذا قمت بتغيير خاصية RetentionTime بعد إضافة المهمة متعددة المثيلات إلى الوظيفة، فسيتم تطبيق هذا التغيير فقط على المهمة الأساسية، وستستمر جميع المهام الفرعية في استخدام RetentionTime الأصلي.

تعكس قائمة المهام الحديثة لعقدة حساب معرف المهام الفرعية إذا كانت المهمة الأخيرة جزءا من مهمة متعددة المثيلات.

الحصول على معلومات حول المهام الفرعية

للحصول على معلومات حول المهام الفرعية باستخدام مكتبة Batch .NET، قم باستدعاء الأسلوب CloudTask.ListSubtasks. يقوم هذا الأسلوب بإرجاع معلومات حول كافة المهام الفرعية ومعلومات حول عقدة حساب التي نفذت المهام. من هذه المعلومات، يمكنك تحديد الدليل الجذر لكل subtask معرف التجمع حالته الحالية رمز الإنهاء والمزيد. يمكنك استخدام هذه المعلومات بالاشتراك مع الأسلوب PoolOperations.GetNodeFile للحصول على الملفات الفرعية. لاحظ أن هذا الأسلوب لا يرجع معلومات المهمة الأساسية (معرف 0).

ملاحظة

ما لم ينص على خلاف ذلك، تطبيق أساليب .NET الدفعي التي تعمل على CloudTask متعدد المثيلات نفسها فقط على المهمة الأساسية. على سبيل المثال، عند استدعاء الأسلوب CloudTask.ListNodeFiles على مهمة متعددة المثيلات، يتم إرجاع ملفات المهمة الأساسية فقط.

يوضح مقتطف التعليمات البرمجية التالي كيفية الحصول على معلومات subtask بالإضافة إلى طلب محتويات الملف من العقد التي تم تنفيذها.

// Obtain the job and the multi-instance task from the Batch service
CloudJob boundJob = batchClient.JobOperations.GetJob("mybatchjob");
CloudTask myMultiInstanceTask = boundJob.GetTask("mymultiinstancetask");

// Now obtain the list of subtasks for the task
IPagedEnumerable<SubtaskInformation> subtasks = myMultiInstanceTask.ListSubtasks();

// Asynchronously iterate over the subtasks and print their stdout and stderr
// output if the subtask has completed
await subtasks.ForEachAsync(async (subtask) =>
{
    Console.WriteLine("subtask: {0}", subtask.Id);
    Console.WriteLine("exit code: {0}", subtask.ExitCode);

    if (subtask.State == SubtaskState.Completed)
    {
        ComputeNode node =
            await batchClient.PoolOperations.GetComputeNodeAsync(subtask.ComputeNodeInformation.PoolId,
                                                                 subtask.ComputeNodeInformation.ComputeNodeId);

        NodeFile stdOutFile = await node.GetNodeFileAsync(subtask.ComputeNodeInformation.TaskRootDirectory + "\\" + Constants.StandardOutFileName);
        NodeFile stdErrFile = await node.GetNodeFileAsync(subtask.ComputeNodeInformation.TaskRootDirectory + "\\" + Constants.StandardErrorFileName);
        stdOut = await stdOutFile.ReadAsStringAsync();
        stdErr = await stdErrFile.ReadAsStringAsync();

        Console.WriteLine("node: {0}:", node.Id);
        Console.WriteLine("stdout.txt: {0}", stdOut);
        Console.WriteLine("stderr.txt: {0}", stdErr);
    }
    else
    {
        Console.WriteLine("\tSubtask {0} is in state {1}", subtask.Id, subtask.State);
    }
});

نموذج التعليمة البرمجية

يوضح نموذج التعليمات البرمجية MultiInstanceTasks على GitHub كيفية استخدام مهمة متعددة المثيلات لتشغيل تطبيق MS-MPI على العقد حساب الدفعي. اتبع الخطوات التالية لتشغيل التعليمة البرمجية للعينة:

الإعداد

  1. تحميل MS-MPI SDK والتركيب Redist وتثبيتها. بعد التثبيت يمكنك التحقق من أنه تم تعيين متغيرات البيئة MS-MPI.
  2. إنشاء إصدار إصدارMPIHelloWorld نموذج MPI البرنامج. هذا هو البرنامج الذي سيتم تشغيله على حساب العقد بواسطة المهمة متعددة المثيلات.
  3. إنشاء ملف مضغوط يحتوي MPIHelloWorld.exe على (التي بنيت في الخطوة 2) و MSMpiSetup.exe (التي قمت بتحميلها في الخطوة 1). ستقوم بتحميل هذا الملف البريدي كحزمة تطبيق في الخطوة التالية.
  4. استخدم مدخل Microsoft Azure لإنشاء تطبيقيسمى "MPIHelloWorld"، وحدد الملف المضغوط الذي أنشأته في الخطوة السابقة كإصدار "1.0" من حزمة التطبيق. راجع تحميل وإدارة التطبيقات للحصول على مزيد من المعلومات.

تلميح

تحديث الإصدار من MPIHelloWorld.exe يضمن أنك لست مضطرًا إلى تضمين أي تبعيات إضافية (على سبيل المثال msvcp140d.dll أو vcruntime140d.dll) في حزمة التطبيق الخاصة بك.

التنفيذ

  1. تحميل ملف .zip نماذج دفعية azure من GitHub.

  2. افتح الحل MultiInstanceTasks في Visual Studio 2019. ملف الحل اسمه MultiInstanceTasks.sln.

    azure-batch-samples\CSharp\ArticleProjects\MultiInstanceTasks\

  3. أدخل بيانات اعتماد حساب الدفعة والتخزين AccountSettings.settings في المشروعMicrosoft.Azure.Batch.Samples.Common.

  4. إنشاء وتشغيل الحل MultiInstanceTasks لتنفيذ التطبيق نموذج MPI على حساب العقد في تجمع دفعي.

  5. اختياري: استخدام مدخل Microsoft Azure أو مستكشف دفعي لفحص تجمع العينة المهمة و المهمة ("MultiInstanceSamplePool" " ، "MultiInstanceSampleJob" " ، "MultiInstanceSampleTask") قبل حذف الموارد.

تلميح

يمكنك تنزيل Visual Studio المجتمع مجانا إذا لم يكن لديك Visual Studio بالفعل.

الإخراج MultiInstanceTasks.exe مشابه للنص التالي:

Creating pool [MultiInstanceSamplePool]...
Creating job [MultiInstanceSampleJob]...
Adding task [MultiInstanceSampleTask] to job [MultiInstanceSampleJob]...
Awaiting task completion, timeout in 00:30:00...

Main task [MultiInstanceSampleTask] is in state [Completed] and ran on compute node [tvm-1219235766_1-20161017t162002z]:
---- stdout.txt ----
Rank 2 received string "Hello world" from Rank 0
Rank 1 received string "Hello world" from Rank 0

---- stderr.txt ----

Main task completed, waiting 00:00:10 for subtasks to complete...

---- Subtask information ----
subtask: 1
        exit code: 0
        node: tvm-1219235766_3-20161017t162002z
        stdout.txt:
        stderr.txt:
subtask: 2
        exit code: 0
        node: tvm-1219235766_2-20161017t162002z
        stdout.txt:
        stderr.txt:

Delete job? [yes] no: yes
Delete pool? [yes] no: yes

Sample complete, hit ENTER to exit...

الخطوات التالية