diff --git a/fabric_examples/acceptance_testing/Acceptance Tests 3.1.2/functional_test_3.1.2-gpu.ipynb b/fabric_examples/acceptance_testing/Acceptance Tests 3.1.2/functional_test_3.1.2-gpu.ipynb
index 39e297c7..6d4f8636 100644
--- a/fabric_examples/acceptance_testing/Acceptance Tests 3.1.2/functional_test_3.1.2-gpu.ipynb
+++ b/fabric_examples/acceptance_testing/Acceptance Tests 3.1.2/functional_test_3.1.2-gpu.ipynb
@@ -24,31 +24,9 @@
},
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "----------------------------------- --------------------------------------------------\n",
- "credmgr_host cm.fabric-testbed.net\n",
- "orchestrator_host orchestrator.fabric-testbed.net\n",
- "fabric_token /home/fabric/.tokens.json\n",
- "project_id 990d8a8b-7e50-4d13-a3be-0f133ffa8653\n",
- "bastion_username ibaldin_0000241998\n",
- "bastion_key_filename /home/fabric/work/fabric_config/fabric_bastion_key\n",
- "bastion_public_addr bastion-1.fabric-testbed.net\n",
- "bastion_passphrase None\n",
- "slice_public_key_file /home/fabric/work/fabric_config/slice_key.pub\n",
- "slice_private_key_file /home/fabric/work/fabric_config/slice_key\n",
- "fabric_slice_private_key_passphrase None\n",
- "fablib_log_file /tmp/fablib/fablib.log\n",
- "fablib_log_level INFO\n",
- "----------------------------------- --------------------------------------------------\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"from fabrictestbed_extensions.fablib.fablib import FablibManager as fablib_manager\n",
"\n",
@@ -68,7 +46,7 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
@@ -94,7 +72,7 @@
},
{
"cell_type": "code",
- "execution_count": 16,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
@@ -115,32 +93,9 @@
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "\n",
- "----------- ---------------------------------------------------------------------------\n",
- "Slice Name Slice Test TACC on tacc-w1.fabric-testbed.net on 2022-08-31 01:09:04.792628\n",
- "Slice ID efec5ff9-0099-4f72-9009-a1ab1f55c44e\n",
- "Slice State StableOK\n",
- "Lease End 2022-09-01 01:09:13 +0000\n",
- "----------- ---------------------------------------------------------------------------\n",
- "\n",
- "Retry: 13, Time: 142 sec\n",
- "\n",
- "ID Name Site Host Cores RAM Disk Image Management IP State Error\n",
- "------------------------------------ ------ ------ -------------------------- ------- ----- ------ --------------- --------------- ------- -------\n",
- "c70530c8-4bf6-415c-8f4a-9a0dbf2f183c Node1 TACC tacc-w1.fabric-testbed.net 10 32 100 default_rocky_8 129.114.110.76 Active\n",
- "\n",
- "Time to stable 142 seconds\n",
- "Running post_boot_config ... Time to post boot config 143 seconds\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"try:\n",
" #Create Slice\n",
@@ -172,22 +127,9 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "----------- ---------------------------------------------------------------------------\n",
- "Slice Name Slice Test TACC on tacc-w1.fabric-testbed.net on 2022-08-31 01:09:04.792628\n",
- "Slice ID efec5ff9-0099-4f72-9009-a1ab1f55c44e\n",
- "Slice State StableOK\n",
- "Lease End 2022-09-01 01:09:13 +0000\n",
- "----------- ---------------------------------------------------------------------------\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"try:\n",
" slice = fablib.get_slice(name=slice_name)\n",
@@ -207,40 +149,9 @@
},
{
"cell_type": "code",
- "execution_count": 10,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "----------------- ------------------------------------------------------------------------------------------------------------------------\n",
- "ID c70530c8-4bf6-415c-8f4a-9a0dbf2f183c\n",
- "Name Node1\n",
- "Cores 10\n",
- "RAM 32\n",
- "Disk 100\n",
- "Image default_rocky_8\n",
- "Image Type qcow2\n",
- "Host tacc-w1.fabric-testbed.net\n",
- "Site TACC\n",
- "Management IP 129.114.110.76\n",
- "Reservation State Active\n",
- "Error Message\n",
- "SSH Command ssh -i /home/fabric/work/fabric_config/slice_key -J ibaldin_0000241998@bastion-1.fabric-testbed.net rocky@129.114.110.76\n",
- "----------------- ------------------------------------------------------------------------------------------------------------------------\n",
- "----------- ----------------------------------------------------------\n",
- "Name Node1-gpu1\n",
- "Details NVIDIA Corporation TU102GL [Quadro RTX 6000/8000] (rev a1)\n",
- "Disk (G) 0\n",
- "Units 1\n",
- "PCI Address 0000:25:00.0\n",
- "Model GPU_RTX6000\n",
- "Type GPU\n",
- "----------- ----------------------------------------------------------\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"try:\n",
" node = slice.get_node(name) \n",
@@ -266,22 +177,9 @@
},
{
"cell_type": "code",
- "execution_count": 11,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "stdout: \n",
- "Installed:\n",
- " pciutils-3.7.0-1.el8.x86_64 \n",
- "\n",
- "00:07.0 3D controller: NVIDIA Corporation TU102GL [Quadro RTX 6000/8000] (rev a1)\n",
- "\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"command = \"sudo dnf install -q -y pciutils && lspci | grep 'NVIDIA\\|3D controller'\"\n",
"try:\n",
@@ -302,18 +200,9 @@
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Installing CUDA...\n",
- "Done installing CUDA. Now, reboot for the changes to take effect.\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"commands = [\n",
" 'sudo dnf install -q -y epel-release',\n",
@@ -338,20 +227,9 @@
},
{
"cell_type": "code",
- "execution_count": 13,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "sudo reboot\n",
- "Waiting for slice . Slice state: StableOK\n",
- "Waiting for ssh in slice ... ssh successful\n",
- "Now testing SSH abilites to reconnect...Reconnected!\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"reboot = 'sudo reboot'\n",
"try:\n",
@@ -380,37 +258,9 @@
},
{
"cell_type": "code",
- "execution_count": 14,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "stdout: Wed Aug 31 01:23:03 2022 \n",
- "+-----------------------------------------------------------------------------+\n",
- "| NVIDIA-SMI 515.65.01 Driver Version: 515.65.01 CUDA Version: 11.7 |\n",
- "|-------------------------------+----------------------+----------------------+\n",
- "| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |\n",
- "| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |\n",
- "| | | MIG M. |\n",
- "|===============================+======================+======================|\n",
- "| 0 Quadro RTX 6000 Off | 00000000:00:07.0 Off | 0 |\n",
- "| N/A 30C P0 54W / 250W | 0MiB / 23040MiB | 6% Default |\n",
- "| | | N/A |\n",
- "+-------------------------------+----------------------+----------------------+\n",
- " \n",
- "+-----------------------------------------------------------------------------+\n",
- "| Processes: |\n",
- "| GPU GI CI PID Type Process name GPU Memory |\n",
- "| ID ID Usage |\n",
- "|=============================================================================|\n",
- "| No running processes found |\n",
- "+-----------------------------------------------------------------------------+\n",
- "\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"try:\n",
" stdout, stderr = node.execute(\"nvidia-smi\")\n",
@@ -428,7 +278,7 @@
},
{
"cell_type": "code",
- "execution_count": 15,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
diff --git a/fabric_examples/acceptance_testing/Acceptance Tests 3.1.2/functional_test_3.1.2-nvme.ipynb b/fabric_examples/acceptance_testing/Acceptance Tests 3.1.2/functional_test_3.1.2-nvme.ipynb
index e2aae305..e623255d 100644
--- a/fabric_examples/acceptance_testing/Acceptance Tests 3.1.2/functional_test_3.1.2-nvme.ipynb
+++ b/fabric_examples/acceptance_testing/Acceptance Tests 3.1.2/functional_test_3.1.2-nvme.ipynb
@@ -24,31 +24,9 @@
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "----------------------------------- --------------------------------------------------\n",
- "credmgr_host cm.fabric-testbed.net\n",
- "orchestrator_host orchestrator.fabric-testbed.net\n",
- "fabric_token /home/fabric/.tokens.json\n",
- "project_id 990d8a8b-7e50-4d13-a3be-0f133ffa8653\n",
- "bastion_username ibaldin_0000241998\n",
- "bastion_key_filename /home/fabric/work/fabric_config/fabric_bastion_key\n",
- "bastion_public_addr bastion-1.fabric-testbed.net\n",
- "bastion_passphrase None\n",
- "slice_public_key_file /home/fabric/work/fabric_config/slice_key.pub\n",
- "slice_private_key_file /home/fabric/work/fabric_config/slice_key\n",
- "fabric_slice_private_key_passphrase None\n",
- "fablib_log_file /tmp/fablib/fablib.log\n",
- "fablib_log_level INFO\n",
- "----------------------------------- --------------------------------------------------\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"from fabrictestbed_extensions.fablib.fablib import FablibManager as fablib_manager\n",
"\n",
@@ -68,8 +46,10 @@
},
{
"cell_type": "code",
- "execution_count": 2,
- "metadata": {},
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
"outputs": [],
"source": [
"try:\n",
@@ -92,8 +72,10 @@
},
{
"cell_type": "code",
- "execution_count": 3,
- "metadata": {},
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
"outputs": [],
"source": [
"from datetime import datetime\n",
@@ -114,32 +96,9 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "\n",
- "----------- --------------------------------------------------------------------------------------\n",
- "Slice Name Slice Test 3.1.2-NVMe TACC on tacc-w1.fabric-testbed.net on 2022-08-31 01:50:40.103182\n",
- "Slice ID 20832e92-0c1b-4e04-bf2a-da22be7a88a6\n",
- "Slice State StableOK\n",
- "Lease End 2022-09-01 01:50:43 +0000\n",
- "----------- --------------------------------------------------------------------------------------\n",
- "\n",
- "Retry: 9, Time: 104 sec\n",
- "\n",
- "ID Name Site Host Cores RAM Disk Image Management IP State Error\n",
- "------------------------------------ ------ ------ -------------------------- ------- ----- ------ --------------- --------------- ------- -------\n",
- "b58f1e42-5f3f-43cd-9fa8-57ffbbf001df Node1 TACC tacc-w1.fabric-testbed.net 10 32 100 default_rocky_8 129.114.110.93 Active\n",
- "\n",
- "Time to stable 104 seconds\n",
- "Running post_boot_config ... Time to post boot config 105 seconds\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"try:\n",
" #Create Slice\n",
@@ -171,22 +130,9 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "----------- --------------------------------------------------------------------------------------\n",
- "Slice Name Slice Test 3.1.2-NVMe TACC on tacc-w1.fabric-testbed.net on 2022-08-31 01:50:40.103182\n",
- "Slice ID 20832e92-0c1b-4e04-bf2a-da22be7a88a6\n",
- "Slice State StableOK\n",
- "Lease End 2022-09-01 01:50:43 +0000\n",
- "----------- --------------------------------------------------------------------------------------\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"try:\n",
" slice = fablib.get_slice(name=slice_name)\n",
@@ -206,49 +152,9 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "----------------- ------------------------------------------------------------------------------------------------------------------------\n",
- "ID b58f1e42-5f3f-43cd-9fa8-57ffbbf001df\n",
- "Name Node1\n",
- "Cores 10\n",
- "RAM 32\n",
- "Disk 100\n",
- "Image default_rocky_8\n",
- "Image Type qcow2\n",
- "Host tacc-w1.fabric-testbed.net\n",
- "Site TACC\n",
- "Management IP 129.114.110.93\n",
- "Reservation State Active\n",
- "Error Message\n",
- "SSH Command ssh -i /home/fabric/work/fabric_config/slice_key -J ibaldin_0000241998@bastion-1.fabric-testbed.net rocky@129.114.110.93\n",
- "----------------- ------------------------------------------------------------------------------------------------------------------------\n",
- "----------- -------------------------------------\n",
- "Name Node1-nvme1\n",
- "Details Dell Express Flash NVMe P4510 1TB SFF\n",
- "Disk (G) 0\n",
- "Units 1\n",
- "PCI Address 0000:22:00.0\n",
- "Model NVME_P4510\n",
- "Type NVME\n",
- "----------- -------------------------------------\n",
- "----------- -------------------------------------\n",
- "Name Node1-nvme2\n",
- "Details Dell Express Flash NVMe P4510 1TB SFF\n",
- "Disk (G) 0\n",
- "Units 1\n",
- "PCI Address 0000:21:00.0\n",
- "Model NVME_P4510\n",
- "Type NVME\n",
- "----------- -------------------------------------\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"try:\n",
" node = slice.get_node(name) \n",
@@ -269,40 +175,16 @@
"source": [
"### NVMe PCI Devices\n",
"\n",
- "Run the command lspci
to see your GPU PCI device(s). This is the raw GPU PCI device that is not yet configured for use. You can use the GPUs as you would any GPUs.\n",
+ "Run the command lspci
to see your NVMe PCI device(s). \n",
"\n",
"View node1's NVMe's"
]
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "stdout: \n",
- "Installed:\n",
- " pciutils-3.7.0-1.el8.x86_64 \n",
- "\n",
- "00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)\n",
- "00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]\n",
- "00:01.1 IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II]\n",
- "00:01.2 USB controller: Intel Corporation 82371SB PIIX3 USB [Natoma/Triton II] (rev 01)\n",
- "00:01.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 03)\n",
- "00:02.0 VGA compatible controller: Cirrus Logic GD 5446\n",
- "00:03.0 Ethernet controller: Red Hat, Inc. Virtio network device\n",
- "00:04.0 SCSI storage controller: Red Hat, Inc. Virtio block device\n",
- "00:05.0 Unclassified device [00ff]: Red Hat, Inc. Virtio memory balloon\n",
- "00:06.0 Unclassified device [00ff]: Red Hat, Inc. Virtio RNG\n",
- "00:07.0 Non-Volatile memory controller: Toshiba Corporation NVMe SSD Controller Cx5 (rev 01)\n",
- "00:08.0 Non-Volatile memory controller: Toshiba Corporation NVMe SSD Controller Cx5 (rev 01)\n",
- "\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"command = \"sudo dnf install -q -y pciutils && lspci\"\n",
"try:\n",
@@ -323,7 +205,7 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
@@ -336,43 +218,9 @@
},
{
"cell_type": "code",
- "execution_count": 9,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "stdout: Disk /dev/vda: 100 GiB, 107374182400 bytes, 209715200 sectors\n",
- "Units: sectors of 1 * 512 = 512 bytes\n",
- "Sector size (logical/physical): 512 bytes / 512 bytes\n",
- "I/O size (minimum/optimal): 512 bytes / 512 bytes\n",
- "Disklabel type: dos\n",
- "Disk identifier: 0x2cc9c27e\n",
- "\n",
- "Device Boot Start End Sectors Size Id Type\n",
- "/dev/vda1 * 2048 209715166 209713119 100G 83 Linux\n",
- "\n",
- "\n",
- "Disk /dev/nvme0n1: 894.3 GiB, 960197124096 bytes, 1875385008 sectors\n",
- "Units: sectors of 1 * 512 = 512 bytes\n",
- "Sector size (logical/physical): 512 bytes / 512 bytes\n",
- "I/O size (minimum/optimal): 512 bytes / 512 bytes\n",
- "Disklabel type: gpt\n",
- "Disk identifier: C386A624-8064-4DEB-A794-BE1B9D30318A\n",
- "\n",
- "Device Start End Sectors Size Type\n",
- "/dev/nvme0n1p1 2048 1875384319 1875382272 894.3G Linux filesystem\n",
- "\n",
- "\n",
- "Disk /dev/nvme1n1: 894.3 GiB, 960197124096 bytes, 1875385008 sectors\n",
- "Units: sectors of 1 * 512 = 512 bytes\n",
- "Sector size (logical/physical): 512 bytes / 512 bytes\n",
- "I/O size (minimum/optimal): 512 bytes / 512 bytes\n",
- "\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"command = \"sudo fdisk -l\"\n",
"try:\n",
@@ -391,7 +239,7 @@
},
{
"cell_type": "code",
- "execution_count": 10,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
diff --git a/fabric_examples/acceptance_testing/Acceptance Tests 3.1.2/functional_test_3.1.2-shared-nic.ipynb b/fabric_examples/acceptance_testing/Acceptance Tests 3.1.2/functional_test_3.1.2-shared-nic.ipynb
new file mode 100644
index 00000000..c7d3381a
--- /dev/null
+++ b/fabric_examples/acceptance_testing/Acceptance Tests 3.1.2/functional_test_3.1.2-shared-nic.ipynb
@@ -0,0 +1,288 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Functional Test 3.1.2 - Shared NICs\n",
+ "\n",
+ "This Jupyter notebook will allow you to create VMs on different sites and worker nodes consistent with requirements for test 3.1.2 for testing Shared NIC attachment.\n",
+ "\n",
+ "## Step 1: Configure the Environment\n",
+ "\n",
+ "Before running this notebook, you will need to configure your environment using the [Configure Environment](../../fablib_api/configure_environment/configure_environment.ipynb) notebook. Please stop here, open and run that notebook, then return to this notebook.\n",
+ "\n",
+ "**This only needs to be done once.**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 2: Import the FABlib Library\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from fabrictestbed_extensions.fablib.fablib import FablibManager as fablib_manager\n",
+ "\n",
+ "fablib = fablib_manager()\n",
+ " \n",
+ "fablib.show_config()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 3: Check your existing slices\n",
+ "\n",
+ "Since testing can get confusing, check what slices you actually have. It may print nothing if you have no active slices."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " for slice in fablib.get_slices():\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 4: Create the Test Slice\n",
+ "\n",
+ "The following creates a node with shared NIC. This should be run all worker nodes regardless of type.\n",
+ "\n",
+ "One node with one NIC component is created. This example uses components of model `NIC_Basic` which are SR-IOV Virtual Function on a 100 Gpbs Mellanox ConnectX-6 PCI device. The VF is accessed by the node via PCI passthrough. \n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "from datetime import datetime\n",
+ "from dateutil import tz\n",
+ "\n",
+ "name='Node1'\n",
+ "nic1_name='SharedNIC1'\n",
+ "site='TACC'\n",
+ "# since all workers have a standard naming scheme, you can just change the worker\n",
+ "# to move from worker to worker\n",
+ "worker=f'{site.lower()}-w1.fabric-testbed.net'\n",
+ "cores=10\n",
+ "ram=20\n",
+ "disk=50\n",
+ "slice_name=f\"Slice Test 3.1.2-SharedNIC {site} on {worker} on {datetime.now()}\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " #Create Slice\n",
+ " print(f'Creating slice {slice_name}')\n",
+ " slice = fablib.new_slice(name=slice_name)\n",
+ "\n",
+ " # Node1\n",
+ " node1 = slice.add_node(name=name, site=site)\n",
+ " iface1 = node1.add_component(model='NIC_Basic', name=nic1_name).get_interfaces()[0]\n",
+ " \n",
+ " #Submit Slice Request\n",
+ " slice.submit()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 5: Observe the Slice's Attributes\n",
+ "\n",
+ "### Print the slice "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Print the Node List"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ "\n",
+ " print(f\"{slice.list_nodes()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Node Details"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " for node in slice.get_nodes():\n",
+ " print(f\"{node}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Interfaces"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You should see 1 interface."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " \n",
+ " print(f\"{slice.list_interfaces()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Shared NIC PCI Devices\n",
+ "\n",
+ "Run the command lspci
to see your Shared NIC PCI device(s).\n",
+ "\n",
+ "View node1's Shared NIC - you should see a `Ethernet controller: Mellanox Technologies MT28908 Family [ConnectX-6 Virtual Function]` "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "command = \"sudo dnf install -q -y pciutils && lspci\"\n",
+ "try:\n",
+ " stdout, stderr = node.execute(command)\n",
+ " print(f\"stdout: {stdout}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 6: Delete the Slice\n",
+ "\n",
+ "Please delete your slice when you are done with your experiment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " slice.delete()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/fabric_examples/acceptance_testing/Acceptance Tests 3.1.2/functional_test_3.1.2-smart-nic.ipynb b/fabric_examples/acceptance_testing/Acceptance Tests 3.1.2/functional_test_3.1.2-smart-nic.ipynb
new file mode 100644
index 00000000..20ed09ae
--- /dev/null
+++ b/fabric_examples/acceptance_testing/Acceptance Tests 3.1.2/functional_test_3.1.2-smart-nic.ipynb
@@ -0,0 +1,304 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Functional Test 3.1.2 - Smart NICs\n",
+ "\n",
+ "This Jupyter notebook will allow you to create VMs on different sites and worker nodes consistent with requirements for test 3.1.2 for testing Smart NIC attachment.\n",
+ "\n",
+ "## Step 1: Configure the Environment\n",
+ "\n",
+ "Before running this notebook, you will need to configure your environment using the [Configure Environment](../../fablib_api/configure_environment/configure_environment.ipynb) notebook. Please stop here, open and run that notebook, then return to this notebook.\n",
+ "\n",
+ "**This only needs to be done once.**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 2: Import the FABlib Library\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from fabrictestbed_extensions.fablib.fablib import FablibManager as fablib_manager\n",
+ "\n",
+ "fablib = fablib_manager()\n",
+ " \n",
+ "fablib.show_config()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 3: Check your existing slices\n",
+ "\n",
+ "Since testing can get confusing, check what slices you actually have. It may print nothing if you have no active slices."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " for slice in fablib.get_slices():\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 4: Create the Test Slice\n",
+ "\n",
+ "The following creates a node with Smart NICs. Orchestrator will auto-select appropriate workers for each test based on the requested NIC types. If you are unsure, the generated ads for each site ([in JSON format](https://github.com/fabric-testbed/aggregate-ads/tree/main/JSON)) can help.\n",
+ "\n",
+ "A node with specified NIC components (2 each as there are typically 2 NICs per worker) is created. \n",
+ "\n",
+ "Available NIC types are:\n",
+ "- NIC_ConnectX_5: 25 Gbps Dedicated Mellanox ConnectX-5 PCI Device (2 Ports) \n",
+ "- NIC_ConnectX_6: 100 Gbps Dedicated Mellanox ConnectX-6 PCI Device (2 Ports) \n",
+ "\n",
+ "**You should try different nodes and different NIC types**\n",
+ "\n",
+ "**The code to create the slice will auto-refresh until the slice is created or it fails**\n",
+ "\n",
+ "**You should change the NIC type depending on which worker you are testing.**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "from datetime import datetime\n",
+ "from dateutil import tz\n",
+ "\n",
+ "name='Node1'\n",
+ "nic1_name='SmartNIC1'\n",
+ "nic2_name='SmartNIC2'\n",
+ "\n",
+ "nic_type='NIC_ConnectX_5'\n",
+ "\n",
+ "site='TACC'\n",
+ "\n",
+ "cores=10\n",
+ "ram=20\n",
+ "disk=50\n",
+ "slice_name=f\"Slice Test 3.1.2-SmartNIC {site} on {datetime.now()}\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " #Create Slice\n",
+ " print(f'Creating slice {slice_name}')\n",
+ " slice = fablib.new_slice(name=slice_name)\n",
+ "\n",
+ " # Node1\n",
+ " node1 = slice.add_node(name=name, site=site)\n",
+ "\n",
+ " iface1 = node1.add_component(model=nic_type, name=nic1_name).get_interfaces()[0]\n",
+ " iface2 = node1.add_component(model=nic_type, name=nic2_name).get_interfaces()[0]\n",
+ " \n",
+ " #Submit Slice Request\n",
+ " slice.submit()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 5: Observe the Slice's Attributes\n",
+ "\n",
+ "### Print the slice "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Print the Node List"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ "\n",
+ " print(f\"{slice.list_nodes()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Node Details"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " for node in slice.get_nodes():\n",
+ " print(f\"{node}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Interfaces"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You should see 4 interfaces (2 cards, 2 ports)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " \n",
+ " print(f\"{slice.list_interfaces()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Smart NIC PCI Devices\n",
+ "\n",
+ "Run the command lspci
to see your Smart NIC PCI device(s).\n",
+ "\n",
+ "View node1's Shared NIC - you should see 4 of either\n",
+ "- `Ethernet controller: Mellanox Technologies MT28908 Family [ConnectX-6]` \n",
+ "- `Ethernet controller: Mellanox Technologies MT27800 Family [ConnectX-5]`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "command = \"sudo dnf install -q -y pciutils && lspci\"\n",
+ "try:\n",
+ " stdout, stderr = node.execute(command)\n",
+ " print(f\"stdout: {stdout}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 6: Delete the Slice\n",
+ "\n",
+ "Please delete your slice when you are done with your experiment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " slice.delete()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/fabric_examples/acceptance_testing/Acceptance Tests 3.2.1/functional_test_3.2.1-shared-nic.ipynb b/fabric_examples/acceptance_testing/Acceptance Tests 3.2.1/functional_test_3.2.1-shared-nic.ipynb
new file mode 100644
index 00000000..1c52a8e0
--- /dev/null
+++ b/fabric_examples/acceptance_testing/Acceptance Tests 3.2.1/functional_test_3.2.1-shared-nic.ipynb
@@ -0,0 +1,432 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Functional Test 3.2.1 - Local bridge with Shared NICs\n",
+ "\n",
+ "This Jupyter notebook will allow you to create VMs on different sites and worker nodes consistent with requirements for test 3.2.1 for testing Shared NIC with a local bridge.\n",
+ "\n",
+ "## Step 1: Configure the Environment\n",
+ "\n",
+ "Before running this notebook, you will need to configure your environment using the [Configure Environment](../../fablib_api/configure_environment/configure_environment.ipynb) notebook. Please stop here, open and run that notebook, then return to this notebook.\n",
+ "\n",
+ "**This only needs to be done once.**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 2: Import the FABlib Library\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from fabrictestbed_extensions.fablib.fablib import FablibManager as fablib_manager\n",
+ "\n",
+ "fablib = fablib_manager()\n",
+ " \n",
+ "fablib.show_config()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 3: Check your existing slices\n",
+ "\n",
+ "Since testing can get confusing, check what slices you actually have. It may print nothing if you have no active slices."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " for slice in fablib.get_slices():\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 4: Create the Test Slice\n",
+ "\n",
+ "The following creates two nodes with a shared NIC each. This should be run all worker nodes regardless of type.\n",
+ "\n",
+ "Two nodes with one NIC component each are created on different workers. This example uses components of model `NIC_Basic` which are SR-IOV Virtual Function on a 100 Gpbs Mellanox ConnectX-6 PCI device. The VF is accessed by the node via PCI passthrough. \n",
+ "\n",
+ "**Be sure to try different combinations of workers**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "from datetime import datetime\n",
+ "from dateutil import tz\n",
+ "\n",
+ "name1='Node1'\n",
+ "nic1_name='SharedNIC1'\n",
+ "\n",
+ "name2='Node2'\n",
+ "nic2_name='SharedNIC2'\n",
+ "\n",
+ "network_name='l2-bridge'\n",
+ "\n",
+ "site='TACC'\n",
+ "\n",
+ "# since all workers have a standard naming scheme, you can just change the worker\n",
+ "# to move from worker to worker\n",
+ "worker1=f'{site.lower()}-w1.fabric-testbed.net'\n",
+ "worker2=f'{site.lower()}-w2.fabric-testbed.net'\n",
+ "\n",
+ "cores=10\n",
+ "ram=20\n",
+ "disk=50\n",
+ "slice_name=f\"Slice Test 3.2.1-SharedNIC {site} {datetime.now()}\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " #Create Slice\n",
+ " print(f'Creating slice {slice_name}')\n",
+ " slice = fablib.new_slice(name=slice_name)\n",
+ "\n",
+ " # Node1\n",
+ " node1 = slice.add_node(name=name1, site=site, host=worker1, cores=cores, ram=ram, disk=disk)\n",
+ " iface1 = node1.add_component(model='NIC_Basic', name=nic1_name).get_interfaces()[0]\n",
+ " \n",
+ " node2 = slice.add_node(name=name2, site=site, host=worker2, cores=cores, ram=ram, disk=disk)\n",
+ " iface2 = node2.add_component(model='NIC_Basic', name=nic2_name).get_interfaces()[0]\n",
+ " \n",
+ " # Network\n",
+ " net1 = slice.add_l2network(name=network_name, interfaces=[iface1, iface2])\n",
+ " \n",
+ " #Submit Slice Request\n",
+ " slice.submit()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 5: Observe the Slice's Attributes\n",
+ "\n",
+ "### Print the slice "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Print the Node List"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ "\n",
+ " print(f\"{slice.list_nodes()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Node Details"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " for node in slice.get_nodes():\n",
+ " print(f\"{node}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Interfaces"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You should see 2 interfaces."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " \n",
+ " print(f\"{slice.list_interfaces()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Shared NIC PCI Devices\n",
+ "\n",
+ "Run the command lspci
to see your Shared NIC PCI device(s).\n",
+ "\n",
+ "View node1's Shared NIC - you should see a `Ethernet controller: Mellanox Technologies MT28908 Family [ConnectX-6 Virtual Function]` "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "command = \"sudo dnf install -q -y pciutils && lspci\"\n",
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ " stdout, stderr = node1.execute(command)\n",
+ " print(f\"stdout: {stdout}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 6: Configure interfaces, test reachability"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Configure IP Addresses\n",
+ "\n",
+ "### Pick a Subnet\n",
+ "\n",
+ "Create a subnet and list of available IP addresses. You can use either IPv4 or IPv6 subnets and addresses."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from ipaddress import ip_address, IPv4Address, IPv6Address, IPv4Network, IPv6Network\n",
+ "\n",
+ "try:\n",
+ " subnet = IPv4Network(\"192.168.1.0/24\")\n",
+ " available_ips = list(subnet)[1:]\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node1\n",
+ "\n",
+ "Get the node and the interface you wish to configure. You can use `node.get_interface` to get the interface that is connected to the specified network. Then `pop` an IP address from the list of available IPs and call `iface.ip_addr_add` to set the IP and subnet. \n",
+ "\n",
+ "Optionally, use the `node.execute()` method to show the results of adding the IP address."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ " node1_iface = node1.get_interface(network_name=network_name) \n",
+ " node1_addr = available_ips.pop(0)\n",
+ " node1_iface.ip_addr_add(addr=node1_addr, subnet=subnet)\n",
+ " \n",
+ " stdout, stderr = node1.execute(f'ip addr show {node1_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node2\n",
+ "\n",
+ "Repeat the steps to add the next available IP to the second node."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node2 = slice.get_node(name=name2) \n",
+ " node2_iface = node2.get_interface(network_name=network_name) \n",
+ " node2_addr = available_ips.pop(0)\n",
+ " node2_iface.ip_addr_add(addr=node2_addr, subnet=subnet)\n",
+ " \n",
+ " stdout, stderr = node2.execute(f'ip addr show {node2_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Test reachability\n",
+ "\n",
+ "Test ping between interfaces, observe successful output.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ "\n",
+ " stdout, stderr = node1.execute(f'ping -c 5 {node2_addr}')\n",
+ " print (stdout)\n",
+ " print (stderr)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 7: Delete the Slice\n",
+ "\n",
+ "Please delete your slice when you are done with your experiment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " slice.delete()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/fabric_examples/acceptance_testing/Acceptance Tests 3.2.1/functional_test_3.2.1-smart-nic.ipynb b/fabric_examples/acceptance_testing/Acceptance Tests 3.2.1/functional_test_3.2.1-smart-nic.ipynb
new file mode 100644
index 00000000..0778bda6
--- /dev/null
+++ b/fabric_examples/acceptance_testing/Acceptance Tests 3.2.1/functional_test_3.2.1-smart-nic.ipynb
@@ -0,0 +1,445 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Functional Test 3.1.2 - Local Bridge with Smart NICs\n",
+ "\n",
+ "This Jupyter notebook will allow you to create VMs on different sites and worker nodes consistent with requirements for test 3.1.2 for testing Smart NIC with a local bridge.\n",
+ "\n",
+ "## Step 1: Configure the Environment\n",
+ "\n",
+ "Before running this notebook, you will need to configure your environment using the [Configure Environment](../../fablib_api/configure_environment/configure_environment.ipynb) notebook. Please stop here, open and run that notebook, then return to this notebook.\n",
+ "\n",
+ "**This only needs to be done once.**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 2: Import the FABlib Library\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from fabrictestbed_extensions.fablib.fablib import FablibManager as fablib_manager\n",
+ "\n",
+ "fablib = fablib_manager()\n",
+ " \n",
+ "fablib.show_config()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 3: Check your existing slices\n",
+ "\n",
+ "Since testing can get confusing, check what slices you actually have. It may print nothing if you have no active slices."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " for slice in fablib.get_slices():\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 4: Create the Test Slice\n",
+ "\n",
+ "The following creates two nodes with shared NICs in the same selected site. Orchestrator will pick the appropriate worker node, if multiple workers of the same type are present (fastnet or slownet) select them explicitly by modifying the code below. If you are unsure, the generated ads for each site ([in JSON format](https://github.com/fabric-testbed/aggregate-ads/tree/main/JSON)) can help.\n",
+ "\n",
+ "Available NIC types are:\n",
+ "- NIC_ConnectX_5: 25 Gbps Dedicated Mellanox ConnectX-5 PCI Device (2 Ports) \n",
+ "- NIC_ConnectX_6: 100 Gbps Dedicated Mellanox ConnectX-6 PCI Device (2 Ports) \n",
+ "\n",
+ "**You should try different NIC types**\n",
+ "\n",
+ "**The code to create the slice will auto-refresh until the slice is created or it fails**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "from datetime import datetime\n",
+ "from dateutil import tz\n",
+ "\n",
+ "name1='Node1'\n",
+ "nic1_name='SmartNIC1'\n",
+ "\n",
+ "name2='Node2'\n",
+ "nic2_name='SmartNIC2'\n",
+ "\n",
+ "# set NIC type for both NICs, remember workers have one type or the other\n",
+ "nic_type1='NIC_ConnectX_5'\n",
+ "nic_type2='NIC_ConnectX_6'\n",
+ "\n",
+ "site='TACC'\n",
+ "\n",
+ "worker1 = None\n",
+ "worker2 = None\n",
+ "# worker selection normally is commented out so it is automatic - uncomment to select specific worker\n",
+ "# if more than one worker of fastnet or slownet type is present\n",
+ "#worker1=f'{site.lower()}-w1.fabric-testbed.net'\n",
+ "#worker2=f'{site.lower()}-w1.fabric-testbed.net'\n",
+ "\n",
+ "network_name='l2-bridge'\n",
+ "\n",
+ "cores=10\n",
+ "ram=20\n",
+ "disk=50\n",
+ "slice_name=f\"Slice Test 3.2.1-SmartNIC {site} on {datetime.now()}\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " #Create Slice\n",
+ " print(f'Creating slice {slice_name}')\n",
+ " slice = fablib.new_slice(name=slice_name)\n",
+ "\n",
+ " # Node1\n",
+ " node1 = slice.add_node(name=name1, site=site, host=worker1)\n",
+ " iface1 = node1.add_component(model=nic_type1, name=nic1_name).get_interfaces()[0]\n",
+ "\n",
+ " # Node2\n",
+ " node2 = slice.add_node(name=name2, site=site, host=worker2)\n",
+ " iface2 = node2.add_component(model=nic_type2, name=nic2_name).get_interfaces()[0]\n",
+ " \n",
+ " # Network\n",
+ " net1 = slice.add_l2network(name=network_name, interfaces=[iface1, iface2])\n",
+ " \n",
+ " # Submit Slice Request\n",
+ " slice.submit()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 5: Observe the Slice's Attributes\n",
+ "\n",
+ "### Print the slice "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Print the Node List"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ "\n",
+ " print(f\"{slice.list_nodes()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Node Details"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " for node in slice.get_nodes():\n",
+ " print(f\"{node}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Interfaces"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You should see 4 interfaces (2 cards, 2 ports)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " \n",
+ " print(f\"{slice.list_interfaces()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Smart NIC PCI Devices\n",
+ "\n",
+ "Run the command lspci
to see your Smart NIC PCI device(s).\n",
+ "\n",
+ "View node1's Shared NIC - you should see 2 of either\n",
+ "- `Ethernet controller: Mellanox Technologies MT28908 Family [ConnectX-6]` \n",
+ "- `Ethernet controller: Mellanox Technologies MT27800 Family [ConnectX-5]`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "command = \"sudo dnf install -q -y pciutils && lspci | grep Mellanox\"\n",
+ "try:\n",
+ " node1 = slice.get_node(name=name1)\n",
+ " stdout, stderr = node1.execute(command)\n",
+ " print(f\"stdout: {stdout}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 6: Configure interfaces, test reachability"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Configure IP Addresses\n",
+ "\n",
+ "### Pick a Subnet\n",
+ "\n",
+ "Create a subnet and list of available IP addresses. You can use either IPv4 or IPv6 subnets and addresses."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from ipaddress import ip_address, IPv4Address, IPv6Address, IPv4Network, IPv6Network\n",
+ "\n",
+ "try:\n",
+ " subnet = IPv4Network(\"192.168.1.0/24\")\n",
+ " available_ips = list(subnet)[1:]\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node1\n",
+ "\n",
+ "Get the node and the interface you wish to configure. You can use `node.get_interface` to get the interface that is connected to the specified network. Then `pop` an IP address from the list of available IPs and call `iface.ip_addr_add` to set the IP and subnet. \n",
+ "\n",
+ "Optionally, use the `node.execute()` method to show the results of adding the IP address."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ " node1_iface = node1.get_interface(network_name=network_name) \n",
+ " node1_addr = available_ips.pop(0)\n",
+ " node1_iface.ip_addr_add(addr=node1_addr, subnet=subnet)\n",
+ " \n",
+ " stdout, stderr = node1.execute(f'ip addr show {node1_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node2\n",
+ "\n",
+ "Repeat the steps to add the next available IP to the second node."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node2 = slice.get_node(name=name2) \n",
+ " node2_iface = node2.get_interface(network_name=network_name) \n",
+ " node2_addr = available_ips.pop(0)\n",
+ " node2_iface.ip_addr_add(addr=node2_addr, subnet=subnet)\n",
+ " \n",
+ " stdout, stderr = node2.execute(f'ip addr show {node2_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Test reachability\n",
+ "\n",
+ "Test ping between interfaces, observe successful output.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ "\n",
+ " stdout, stderr = node1.execute(f'ping -c 5 {node2_addr}')\n",
+ " print (stdout)\n",
+ " print (stderr)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 7: Delete the Slice\n",
+ "\n",
+ "Please delete your slice when you are done with your experiment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " slice.delete()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/fabric_examples/acceptance_testing/Acceptance Tests 3.2.2/functional_test_3.2.2-smart-nic.ipynb b/fabric_examples/acceptance_testing/Acceptance Tests 3.2.2/functional_test_3.2.2-smart-nic.ipynb
new file mode 100644
index 00000000..bc20d009
--- /dev/null
+++ b/fabric_examples/acceptance_testing/Acceptance Tests 3.2.2/functional_test_3.2.2-smart-nic.ipynb
@@ -0,0 +1,448 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Functional Test 3.2.2 - L2PTP Smart NICs\n",
+ "\n",
+ "This Jupyter notebook will allow you to create VMs on different sites and worker nodes consistent with requirements for test 3.2.2 for testing Smart NIC with L2PTP service.\n",
+ "\n",
+ "\n",
+ "## Step 1: Configure the Environment\n",
+ "\n",
+ "Before running this notebook, you will need to configure your environment using the [Configure Environment](../../fablib_api/configure_environment/configure_environment.ipynb) notebook. Please stop here, open and run that notebook, then return to this notebook.\n",
+ "\n",
+ "**This only needs to be done once.**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 2: Import the FABlib Library\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from fabrictestbed_extensions.fablib.fablib import FablibManager as fablib_manager\n",
+ "\n",
+ "fablib = fablib_manager()\n",
+ " \n",
+ "fablib.show_config()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 3: Check your existing slices\n",
+ "\n",
+ "Since testing can get confusing, check what slices you actually have. It may print nothing if you have no active slices."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " for slice in fablib.get_slices():\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 4: Create the Test Slice\n",
+ "\n",
+ "The following creates two nodes with Smart NICs in two selected sites. Orchestrator will pick the appropriate worker nodes, if multiple workers of the same type are present (fastnet or slownet) select them explicitly by modifying the code below. If you are unsure, the generated ads for each site ([in JSON format](https://github.com/fabric-testbed/aggregate-ads/tree/main/JSON)) can help.\n",
+ "\n",
+ "Available NIC types are:\n",
+ "- NIC_ConnectX_5: 25 Gbps Dedicated Mellanox ConnectX-5 PCI Device (2 Ports) \n",
+ "- NIC_ConnectX_6: 100 Gbps Dedicated Mellanox ConnectX-6 PCI Device (2 Ports) \n",
+ "\n",
+ "**You should try different NIC types**\n",
+ "\n",
+ "**The code to create the slice will auto-refresh until the slice is created or it fails**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "from datetime import datetime\n",
+ "from dateutil import tz\n",
+ "\n",
+ "name1='Node1'\n",
+ "nic1_name='SmartNIC1'\n",
+ "\n",
+ "name2='Node2'\n",
+ "nic2_name='SmartNIC2'\n",
+ "\n",
+ "# set NIC type for both NICs, remember workers have one type or the other\n",
+ "nic_type1='NIC_ConnectX_5'\n",
+ "nic_type2='NIC_ConnectX_5'\n",
+ "\n",
+ "site1='TACC'\n",
+ "site2='SALT'\n",
+ "\n",
+ "worker1 = None\n",
+ "worker2 = None\n",
+ "\n",
+ "# worker selection normally is commented out so it is automatic - uncomment to select specific worker\n",
+ "# if more than one worker of fastnet or slownet type is present\n",
+ "#worker1=f'{site1.lower()}-w1.fabric-testbed.net'\n",
+ "#worker2=f'{site2.lower()}-w1.fabric-testbed.net'\n",
+ "\n",
+ "network_name='l2-PTP'\n",
+ "\n",
+ "cores=10\n",
+ "ram=20\n",
+ "disk=50\n",
+ "slice_name=f\"Slice Test 3.2.2-SmartNIC across {site1}/{site2} on {datetime.now()}\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " #Create Slice\n",
+ " print(f'Creating slice {slice_name}')\n",
+ " slice = fablib.new_slice(name=slice_name)\n",
+ " \n",
+ " # Node1\n",
+ " node1 = slice.add_node(name=name1, site=site1, host=worker1)\n",
+ " iface1 = node1.add_component(model=nic_type1, name=nic1_name).get_interfaces()[0]\n",
+ "\n",
+ " # Node2\n",
+ " node2 = slice.add_node(name=name2, site=site2, host=worker2)\n",
+ " iface2 = node2.add_component(model=nic_type2, name=nic2_name).get_interfaces()[0]\n",
+ " \n",
+ " # Network\n",
+ " net1 = slice.add_l2network(name=network_name, interfaces=[iface1, iface2], type='L2PTP')\n",
+ " \n",
+ " # Submit Slice Request\n",
+ " slice.submit()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 5: Observe the Slice's Attributes\n",
+ "\n",
+ "### Print the slice "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Print the Node List"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ "\n",
+ " print(f\"{slice.list_nodes()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Node Details"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " for node in slice.get_nodes():\n",
+ " print(f\"{node}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Interfaces"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "###### You should see 6 interfaces (3 cards, 2 ports)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " \n",
+ " print(f\"{slice.list_interfaces()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Smart NIC PCI Devices\n",
+ "\n",
+ "Run the command lspci
to see your Smart NIC PCI device(s).\n",
+ "\n",
+ "View node1's Smart NIC - you should see 2 of either\n",
+ "- `Ethernet controller: Mellanox Technologies MT28908 Family [ConnectX-6]` \n",
+ "- `Ethernet controller: Mellanox Technologies MT27800 Family [ConnectX-5]`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "command = \"sudo dnf install -q -y pciutils && lspci | grep Mellanox\"\n",
+ "try:\n",
+ " node1 = slice.get_node(name=name1)\n",
+ " stdout, stderr = node1.execute(command)\n",
+ " print(f\"stdout: {stdout}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 6: Configure interfaces, test reachability"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Configure IP Addresses\n",
+ "\n",
+ "### Pick a Subnet\n",
+ "\n",
+ "Create a subnet and list of available IP addresses. You can use either IPv4 or IPv6 subnets and addresses."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from ipaddress import ip_address, IPv4Address, IPv6Address, IPv4Network, IPv6Network\n",
+ "\n",
+ "try:\n",
+ " subnet = IPv4Network(\"192.168.1.0/24\")\n",
+ " available_ips = list(subnet)[1:]\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node1\n",
+ "\n",
+ "Get the node and the interface you wish to configure. You can use `node.get_interface` to get the interface that is connected to the specified network. Then `pop` an IP address from the list of available IPs and call `iface.ip_addr_add` to set the IP and subnet. \n",
+ "\n",
+ "Optionally, use the `node.execute()` method to show the results of adding the IP address."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ " node1_iface = node1.get_interface(network_name=network_name) \n",
+ " node1_addr = available_ips.pop(0)\n",
+ " node1_iface.ip_addr_add(addr=node1_addr, subnet=subnet)\n",
+ " \n",
+ " stdout, stderr = node1.execute(f'ip addr show {node1_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node2\n",
+ "\n",
+ "Repeat the steps to add the next available IP to the second node."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node2 = slice.get_node(name=name2) \n",
+ " node2_iface = node2.get_interface(network_name=network_name) \n",
+ " node2_addr = available_ips.pop(0)\n",
+ " node2_iface.ip_addr_add(addr=node2_addr, subnet=subnet)\n",
+ " \n",
+ " stdout, stderr = node2.execute(f'ip addr show {node2_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Test reachability\n",
+ "\n",
+ "Test ping between interfaces, observe successful output.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ "\n",
+ " stdout, stderr = node1.execute(f'ping -c 5 {node2_addr}')\n",
+ " print (stdout)\n",
+ " print (stderr)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 7: Delete the Slice\n",
+ "\n",
+ "Please delete your slice when you are done with your experiment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " slice.delete()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/fabric_examples/acceptance_testing/Acceptance Tests 3.2.3/functional_test_3.2.3-shared-nic.ipynb b/fabric_examples/acceptance_testing/Acceptance Tests 3.2.3/functional_test_3.2.3-shared-nic.ipynb
new file mode 100644
index 00000000..9b91b4dc
--- /dev/null
+++ b/fabric_examples/acceptance_testing/Acceptance Tests 3.2.3/functional_test_3.2.3-shared-nic.ipynb
@@ -0,0 +1,513 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Functional Test 3.2.3 - L2STS with Shared NICs\n",
+ "\n",
+ "This Jupyter notebook will allow you to create VMs on different sites and worker nodes consistent with requirements for test 3.2.3 for testing Shared NIC with L2STS service.\n",
+ "\n",
+ "\n",
+ "## Step 1: Configure the Environment\n",
+ "\n",
+ "Before running this notebook, you will need to configure your environment using the [Configure Environment](../../fablib_api/configure_environment/configure_environment.ipynb) notebook. Please stop here, open and run that notebook, then return to this notebook.\n",
+ "\n",
+ "**This only needs to be done once.**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 2: Import the FABlib Library\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from fabrictestbed_extensions.fablib.fablib import FablibManager as fablib_manager\n",
+ "\n",
+ "fablib = fablib_manager()\n",
+ " \n",
+ "fablib.show_config()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 3: Check your existing slices\n",
+ "\n",
+ "Since testing can get confusing, check what slices you actually have. It may print nothing if you have no active slices."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " for slice in fablib.get_slices():\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 4: Create the Test Slice\n",
+ "\n",
+ "The following creates three nodes with Shared NICs in two selected sites on separate workers (STS service cannot do hairpins, so VMs on the same worker can't see each other). Worker selection should be done manually. If you are unsure, the generated ads for each site ([in JSON format](https://github.com/fabric-testbed/aggregate-ads/tree/main/JSON)) can help.\n",
+ "\n",
+ "**Do not change the NIC type, but you can change workers and sites**\n",
+ "\n",
+ "**The code to create the slice will auto-refresh until the slice is created or it fails**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "from datetime import datetime\n",
+ "from dateutil import tz\n",
+ "\n",
+ "name1='Node1'\n",
+ "nic1_name='SharedNIC1'\n",
+ "\n",
+ "name2='Node2'\n",
+ "nic2_name='SharedNIC2'\n",
+ "\n",
+ "name3='Node3'\n",
+ "nic3_name='SharedNIC3'\n",
+ "\n",
+ "# set NIC type for all NICs\n",
+ "nic_type='NIC_Basic'\n",
+ "\n",
+ "\n",
+ "site1='TACC'\n",
+ "site2='SALT'\n",
+ "\n",
+ "# Make sure all workers at the same site are different.\n",
+ "# worker1 goes with site1, workers 2 and 3 go with site 2\n",
+ "worker1=f'{site1.lower()}-w1.fabric-testbed.net'\n",
+ "worker2=f'{site2.lower()}-w1.fabric-testbed.net'\n",
+ "worker3=f'{site2.lower()}-w2.fabric-testbed.net'\n",
+ "\n",
+ "network_name='l2-STS'\n",
+ "\n",
+ "cores=10\n",
+ "ram=20\n",
+ "disk=50\n",
+ "slice_name=f\"Slice Test 3.2.3-SharedNIC across {site1}/{site2} on {datetime.now()}\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " #Create Slice\n",
+ " print(f'Creating slice {slice_name}')\n",
+ " slice = fablib.new_slice(name=slice_name)\n",
+ "\n",
+ " # Node1 and 2 goes to site1\n",
+ " # Node3 goes to site2\n",
+ " \n",
+ " # Node1\n",
+ " node1 = slice.add_node(name=name1, site=site1, host=worker1)\n",
+ " iface1 = node1.add_component(model=nic_type, name=nic1_name).get_interfaces()[0]\n",
+ "\n",
+ " # Node2\n",
+ " node2 = slice.add_node(name=name2, site=site1, host=worker2)\n",
+ " iface2 = node2.add_component(model=nic_type, name=nic2_name).get_interfaces()[0]\n",
+ " \n",
+ " # Node3\n",
+ " node3 = slice.add_node(name=name3, site=site2, host=worker3)\n",
+ " iface3 = node3.add_component(model=nic_type, name=nic3_name).get_interfaces()[0]\n",
+ " \n",
+ " # Network\n",
+ " net1 = slice.add_l2network(name=network_name, interfaces=[iface1, iface2, iface3], type='L2STS')\n",
+ " \n",
+ " # Submit Slice Request\n",
+ " slice.submit()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 5: Observe the Slice's Attributes\n",
+ "\n",
+ "### Print the slice "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Print the Node List"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ "\n",
+ " print(f\"{slice.list_nodes()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Node Details"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " for node in slice.get_nodes():\n",
+ " print(f\"{node}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Interfaces"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "###### You should see 6 interfaces (3 cards, 2 ports)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " \n",
+ " print(f\"{slice.list_interfaces()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Shared NIC PCI Devices\n",
+ "\n",
+ "Run the command lspci
to see your Shared NIC PCI device(s).\n",
+ "\n",
+ "View node1's Shared NIC - you should see one\n",
+ "- `Ethernet controller: Mellanox Technologies MT28908 Family [ConnectX-6 Virtual Function]` \n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "command = \"sudo dnf install -q -y pciutils && lspci | grep Mellanox\"\n",
+ "try:\n",
+ " node1 = slice.get_node(name=name1)\n",
+ " stdout, stderr = node1.execute(command)\n",
+ " print(f\"stdout: {stdout}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 6: Configure interfaces, test reachability"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Configure IP Addresses\n",
+ "\n",
+ "### Pick a Subnet\n",
+ "\n",
+ "Create a subnet and list of available IP addresses. You can use either IPv4 or IPv6 subnets and addresses."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from ipaddress import ip_address, IPv4Address, IPv6Address, IPv4Network, IPv6Network\n",
+ "\n",
+ "try:\n",
+ " subnet = IPv4Network(\"192.168.1.0/24\")\n",
+ " available_ips = list(subnet)[1:]\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node1\n",
+ "\n",
+ "Get the node and the interface you wish to configure. You can use `node.get_interface` to get the interface that is connected to the specified network. Then `pop` an IP address from the list of available IPs and call `iface.ip_addr_add` to set the IP and subnet. \n",
+ "\n",
+ "Optionally, use the `node.execute()` method to show the results of adding the IP address."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ " node1_iface = node1.get_interface(network_name=network_name) \n",
+ " node1_addr = available_ips.pop(0)\n",
+ " node1_iface.ip_addr_add(addr=node1_addr, subnet=subnet)\n",
+ " \n",
+ " stdout, stderr = node1.execute(f'ip addr show {node1_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node2\n",
+ "\n",
+ "Repeat the steps to add the next available IP to the second node."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node2 = slice.get_node(name=name2) \n",
+ " node2_iface = node2.get_interface(network_name=network_name) \n",
+ " node2_addr = available_ips.pop(0)\n",
+ " node2_iface.ip_addr_add(addr=node2_addr, subnet=subnet)\n",
+ " \n",
+ " stdout, stderr = node2.execute(f'ip addr show {node2_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node3\n",
+ "\n",
+ "Repeat the steps to add the next available IP to the third node."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node3 = slice.get_node(name=name1) \n",
+ " node3_iface = node1.get_interface(network_name=network_name) \n",
+ " node3_addr = available_ips.pop(0)\n",
+ " node3_iface.ip_addr_add(addr=node3_addr, subnet=subnet)\n",
+ " \n",
+ " stdout, stderr = node3.execute(f'ip addr show {node3_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Test reachability\n",
+ "\n",
+ "Test ping between interfaces, observe successful output.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ "\n",
+ " stdout, stderr = node1.execute(f'ping -c 5 {node2_addr}')\n",
+ " print (stdout)\n",
+ " print (stderr)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ "\n",
+ " stdout, stderr = node1.execute(f'ping -c 5 {node3_addr}')\n",
+ " print (stdout)\n",
+ " print (stderr)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node2 = slice.get_node(name=name2) \n",
+ "\n",
+ " stdout, stderr = node2.execute(f'ping -c 5 {node3_addr}')\n",
+ " print (stdout)\n",
+ " print (stderr)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 7: Delete the Slice\n",
+ "\n",
+ "Please delete your slice when you are done with your experiment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " slice.delete()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/fabric_examples/acceptance_testing/Acceptance Tests 3.2.3/functional_test_3.2.3-smart-nic.ipynb b/fabric_examples/acceptance_testing/Acceptance Tests 3.2.3/functional_test_3.2.3-smart-nic.ipynb
new file mode 100644
index 00000000..9cee1fda
--- /dev/null
+++ b/fabric_examples/acceptance_testing/Acceptance Tests 3.2.3/functional_test_3.2.3-smart-nic.ipynb
@@ -0,0 +1,523 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Functional Test 3.2.3 - L2STS Smart NICs\n",
+ "\n",
+ "This Jupyter notebook will allow you to create VMs on different sites and worker nodes consistent with requirements for test 3.2.3 for testing Smart NIC with L2STS service.\n",
+ "\n",
+ "\n",
+ "## Step 1: Configure the Environment\n",
+ "\n",
+ "Before running this notebook, you will need to configure your environment using the [Configure Environment](../../fablib_api/configure_environment/configure_environment.ipynb) notebook. Please stop here, open and run that notebook, then return to this notebook.\n",
+ "\n",
+ "**This only needs to be done once.**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 2: Import the FABlib Library\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from fabrictestbed_extensions.fablib.fablib import FablibManager as fablib_manager\n",
+ "\n",
+ "fablib = fablib_manager()\n",
+ " \n",
+ "fablib.show_config()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 3: Check your existing slices\n",
+ "\n",
+ "Since testing can get confusing, check what slices you actually have. It may print nothing if you have no active slices."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " for slice in fablib.get_slices():\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 4: Create the Test Slice\n",
+ "\n",
+ "The following creates three nodes with Smart NICs in two selected sites. Orchestrator will pick the appropriate worker nodes, if multiple workers of the same type are present (fastnet or slownet) select them explicitly by modifying the code below. If you are unsure, the generated ads for each site ([in JSON format](https://github.com/fabric-testbed/aggregate-ads/tree/main/JSON)) can help.\n",
+ "\n",
+ "Available NIC types are:\n",
+ "- NIC_ConnectX_5: 25 Gbps Dedicated Mellanox ConnectX-5 PCI Device (2 Ports) \n",
+ "- NIC_ConnectX_6: 100 Gbps Dedicated Mellanox ConnectX-6 PCI Device (2 Ports) \n",
+ "\n",
+ "**You should try different NIC types**\n",
+ "\n",
+ "**The code to create the slice will auto-refresh until the slice is created or it fails**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "from datetime import datetime\n",
+ "from dateutil import tz\n",
+ "\n",
+ "name1='Node1'\n",
+ "nic1_name='SmartNIC1'\n",
+ "\n",
+ "name2='Node2'\n",
+ "nic2_name='SmartNIC2'\n",
+ "\n",
+ "name3='Node3'\n",
+ "nic3_name='SmartNIC3'\n",
+ "\n",
+ "# set NIC type for both NICs, remember workers have one type or the other\n",
+ "nic_type1='NIC_ConnectX_5'\n",
+ "nic_type2='NIC_ConnectX_5'\n",
+ "nic_type3='NIC_ConnectX_5'\n",
+ "\n",
+ "site1='TACC'\n",
+ "site2='SALT'\n",
+ "\n",
+ "worker1 = None\n",
+ "worker2 = None\n",
+ "worker3 = None\n",
+ "\n",
+ "# worker selection normally is commented out so it is automatic - uncomment to select specific worker\n",
+ "# if more than one worker of fastnet or slownet type is present\n",
+ "#worker1=f'{site1.lower()}-w1.fabric-testbed.net'\n",
+ "#worker2=f'{site2.lower()}-w1.fabric-testbed.net'\n",
+ "#worker3=f'{site2.lower()}-w1.fabric-testbed.net'\n",
+ "\n",
+ "network_name='l2-STS'\n",
+ "\n",
+ "cores=10\n",
+ "ram=20\n",
+ "disk=50\n",
+ "slice_name=f\"Slice Test 3.2.3-SmartNIC across {site1}/{site2} on {datetime.now()}\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " #Create Slice\n",
+ " print(f'Creating slice {slice_name}')\n",
+ " slice = fablib.new_slice(name=slice_name)\n",
+ "\n",
+ " # Node1 goes to site1\n",
+ " # Nodes2 and 3 go to site2\n",
+ " \n",
+ " # Node1\n",
+ " node1 = slice.add_node(name=name1, site=site1, host=worker1)\n",
+ " iface1 = node1.add_component(model=nic_type1, name=nic1_name).get_interfaces()[0]\n",
+ "\n",
+ " # Node2\n",
+ " node2 = slice.add_node(name=name2, site=site2, host=worker2)\n",
+ " iface2 = node2.add_component(model=nic_type2, name=nic2_name).get_interfaces()[0]\n",
+ " \n",
+ " # Node3\n",
+ " node3 = slice.add_node(name=name3, site=site2, host=worker3)\n",
+ " iface3 = node3.add_component(model=nic_type3, name=nic3_name).get_interfaces()[0]\n",
+ " \n",
+ " # Network\n",
+ " net1 = slice.add_l2network(name=network_name, interfaces=[iface1, iface2, iface3], type='L2STS')\n",
+ " \n",
+ " # Submit Slice Request\n",
+ " slice.submit()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 5: Observe the Slice's Attributes\n",
+ "\n",
+ "### Print the slice "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Print the Node List"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ "\n",
+ " print(f\"{slice.list_nodes()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Node Details"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " for node in slice.get_nodes():\n",
+ " print(f\"{node}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Interfaces"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "###### You should see 6 interfaces (3 cards, 2 ports)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " \n",
+ " print(f\"{slice.list_interfaces()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Smart NIC PCI Devices\n",
+ "\n",
+ "Run the command lspci
to see your Smart NIC PCI device(s).\n",
+ "\n",
+ "View node1's Smart NIC - you should see 2 of either\n",
+ "- `Ethernet controller: Mellanox Technologies MT28908 Family [ConnectX-6]` \n",
+ "- `Ethernet controller: Mellanox Technologies MT27800 Family [ConnectX-5]`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "command = \"sudo dnf install -q -y pciutils && lspci | grep Mellanox\"\n",
+ "try:\n",
+ " node1 = slice.get_node(name=name1)\n",
+ " stdout, stderr = node1.execute(command)\n",
+ " print(f\"stdout: {stdout}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 6: Configure interfaces, test reachability"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Configure IP Addresses\n",
+ "\n",
+ "### Pick a Subnet\n",
+ "\n",
+ "Create a subnet and list of available IP addresses. You can use either IPv4 or IPv6 subnets and addresses."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from ipaddress import ip_address, IPv4Address, IPv6Address, IPv4Network, IPv6Network\n",
+ "\n",
+ "try:\n",
+ " subnet = IPv4Network(\"192.168.1.0/24\")\n",
+ " available_ips = list(subnet)[1:]\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node1\n",
+ "\n",
+ "Get the node and the interface you wish to configure. You can use `node.get_interface` to get the interface that is connected to the specified network. Then `pop` an IP address from the list of available IPs and call `iface.ip_addr_add` to set the IP and subnet. \n",
+ "\n",
+ "Optionally, use the `node.execute()` method to show the results of adding the IP address."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ " node1_iface = node1.get_interface(network_name=network_name) \n",
+ " node1_addr = available_ips.pop(0)\n",
+ " node1_iface.ip_addr_add(addr=node1_addr, subnet=subnet)\n",
+ " \n",
+ " stdout, stderr = node1.execute(f'ip addr show {node1_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node2\n",
+ "\n",
+ "Repeat the steps to add the next available IP to the second node."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node2 = slice.get_node(name=name2) \n",
+ " node2_iface = node2.get_interface(network_name=network_name) \n",
+ " node2_addr = available_ips.pop(0)\n",
+ " node2_iface.ip_addr_add(addr=node2_addr, subnet=subnet)\n",
+ " \n",
+ " stdout, stderr = node2.execute(f'ip addr show {node2_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node3\n",
+ "\n",
+ "Repeat the steps to add the next available IP to the third node."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node3 = slice.get_node(name=name1) \n",
+ " node3_iface = node1.get_interface(network_name=network_name) \n",
+ " node3_addr = available_ips.pop(0)\n",
+ " node3_iface.ip_addr_add(addr=node3_addr, subnet=subnet)\n",
+ " \n",
+ " stdout, stderr = node3.execute(f'ip addr show {node3_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Test reachability\n",
+ "\n",
+ "Test ping between interfaces, observe successful output.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ "\n",
+ " stdout, stderr = node1.execute(f'ping -c 5 {node2_addr}')\n",
+ " print (stdout)\n",
+ " print (stderr)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ "\n",
+ " stdout, stderr = node1.execute(f'ping -c 5 {node3_addr}')\n",
+ " print (stdout)\n",
+ " print (stderr)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node2 = slice.get_node(name=name2) \n",
+ "\n",
+ " stdout, stderr = node2.execute(f'ping -c 5 {node3_addr}')\n",
+ " print (stdout)\n",
+ " print (stderr)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 7: Delete the Slice\n",
+ "\n",
+ "Please delete your slice when you are done with your experiment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " slice.delete()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/fabric_examples/acceptance_testing/Acceptance Tests 3.2.4/functional_test_3.2.4-shared-nic.ipynb b/fabric_examples/acceptance_testing/Acceptance Tests 3.2.4/functional_test_3.2.4-shared-nic.ipynb
new file mode 100644
index 00000000..4385dae6
--- /dev/null
+++ b/fabric_examples/acceptance_testing/Acceptance Tests 3.2.4/functional_test_3.2.4-shared-nic.ipynb
@@ -0,0 +1,453 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Functional Test 3.2.4 - Shared NICs with FABNetv4\n",
+ "\n",
+ "This Jupyter notebook will allow you to create VMs on different sites and worker nodes consistent with requirements for test 3.2.4 for testing Shared NIC with FABNetv4 service. Because FABNetv4 works like a bridge, testing with just Shared NICs across all workers is sufficient.\n",
+ "\n",
+ "\n",
+ "## Step 1: Configure the Environment\n",
+ "\n",
+ "Before running this notebook, you will need to configure your environment using the [Configure Environment](../../fablib_api/configure_environment/configure_environment.ipynb) notebook. Please stop here, open and run that notebook, then return to this notebook.\n",
+ "\n",
+ "**This only needs to be done once.**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 2: Import the FABlib Library\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from fabrictestbed_extensions.fablib.fablib import FablibManager as fablib_manager\n",
+ "\n",
+ "fablib = fablib_manager()\n",
+ " \n",
+ "fablib.show_config()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 3: Check your existing slices\n",
+ "\n",
+ "Since testing can get confusing, check what slices you actually have. It may print nothing if you have no active slices."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " for slice in fablib.get_slices():\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 4: Create the Test Slice\n",
+ "\n",
+ "The following creates тшо nodes with Shared NICs in two selected sites on separate workers. Worker selection should be done manually. If you are unsure, the generated ads for each site ([in JSON format](https://github.com/fabric-testbed/aggregate-ads/tree/main/JSON)) can help.\n",
+ "\n",
+ "**Do not change the NIC type, but you can change workers and sites**\n",
+ "\n",
+ "**The code to create the slice will auto-refresh until the slice is created or it fails**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "from datetime import datetime\n",
+ "from dateutil import tz\n",
+ "\n",
+ "name1='Node1'\n",
+ "nic1_name='SharedNIC1'\n",
+ "\n",
+ "name2='Node2'\n",
+ "nic2_name='SharedNIC2'\n",
+ "\n",
+ "# set NIC type for all NICs\n",
+ "nic_type='NIC_Basic'\n",
+ "\n",
+ "\n",
+ "site1='TACC'\n",
+ "site2='SALT'\n",
+ "\n",
+ "# Make sure all workers at the same site are different.\n",
+ "# worker1 goes with site1, workers 2 and 3 go with site 2\n",
+ "worker1=f'{site1.lower()}-w1.fabric-testbed.net'\n",
+ "worker2=f'{site2.lower()}-w2.fabric-testbed.net'\n",
+ "\n",
+ "network1_name='l3-FABNetv4-1'\n",
+ "network2_name='l3-FABNetv4-2'\n",
+ "\n",
+ "cores=5\n",
+ "ram=10\n",
+ "disk=50\n",
+ "slice_name=f\"Slice Test 3.2.4-SharedNIC across {site1}/{site2} on {datetime.now()}\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " #Create Slice\n",
+ " print(f'Creating slice {slice_name}')\n",
+ " slice = fablib.new_slice(name=slice_name)\n",
+ " \n",
+ " # Node1\n",
+ " node1 = slice.add_node(name=name1, site=site1, host=worker1)\n",
+ " iface1 = node1.add_component(model=nic_type, name=nic1_name).get_interfaces()[0]\n",
+ "\n",
+ " # Node2\n",
+ " node2 = slice.add_node(name=name2, site=site2, host=worker2)\n",
+ " iface2 = node2.add_component(model=nic_type, name=nic2_name).get_interfaces()[0]\n",
+ "\n",
+ " # Networks (one in each site)\n",
+ " net1 = slice.add_l3network(name=network1_name, interfaces=[iface1], type='IPv4')\n",
+ " net2 = slice.add_l3network(name=network2_name, interfaces=[iface2], type='IPv4')\n",
+ " \n",
+ " # Submit Slice Request\n",
+ " slice.submit()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 5: Observe the Slice's Attributes\n",
+ "\n",
+ "### Print the slice "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Print the Node List"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ "\n",
+ " print(f\"{slice.list_nodes()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Node Details"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " for node in slice.get_nodes():\n",
+ " print(f\"{node}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Interfaces"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "###### You should see 6 interfaces (3 cards, 2 ports)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " \n",
+ " print(f\"{slice.list_interfaces()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Shared NIC PCI Devices\n",
+ "\n",
+ "Run the command lspci
to see your Shared NIC PCI device(s).\n",
+ "\n",
+ "View node1's Shared NIC - you should see one of\n",
+ "- `Ethernet controller: Mellanox Technologies MT28908 Family [ConnectX-6 Virtual Function]` \n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "command = \"sudo dnf install -q -y pciutils && lspci | grep Mellanox\"\n",
+ "try:\n",
+ " node1 = slice.get_node(name=name1)\n",
+ " stdout, stderr = node1.execute(command)\n",
+ " print(f\"stdout: {stdout}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 6: Configure interfaces, test reachability"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Configure IP Addresses\n",
+ "\n",
+ "### Pick a Subnet\n",
+ "\n",
+ "Create a subnet and list of available IP addresses. All objects are Python IP management objects. You can use either IPv4 or IPv6 subnets and addresses."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " network1 = slice.get_network(name=network1_name)\n",
+ " network1_available_ips = network1.get_available_ips()\n",
+ " print(f\"{network1}\")\n",
+ " \n",
+ " network2 = slice.get_network(name=network2_name)\n",
+ " network2_available_ips = network2.get_available_ips()\n",
+ " print(f\"{network2}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node1\n",
+ "\n",
+ "Get the node and the interface you wish to configure. You can use `node.get_interface` to get the interface that is connected to the specified network. Then `pop` an IP address from the list of available IPs and call `iface.ip_addr_add` to set the IP and subnet. \n",
+ "\n",
+ "Optionally, use the `node.execute()` method to show the results of adding the IP address."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ " node1_iface = node1.get_interface(network_name=network1_name) \n",
+ " node1_addr = network1_available_ips.pop(0)\n",
+ " node1_iface.ip_addr_add(addr=node1_addr, subnet=network1.get_subnet())\n",
+ " \n",
+ " node1.ip_route_add(subnet=network2.get_subnet(), gateway=network1.get_gateway())\n",
+ " \n",
+ " stdout, stderr = node1.execute(f'ip addr show {node1_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ " stdout, stderr = node1.execute(f'ip route list')\n",
+ " print (stdout)\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node2\n",
+ "\n",
+ "Repeat the steps to add the next available IP to the second node."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node2 = slice.get_node(name=name2) \n",
+ " node2_iface = node2.get_interface(network_name=network2_name) \n",
+ " node2_addr = network2_available_ips.pop(0)\n",
+ " node2_iface.ip_addr_add(addr=node2_addr, subnet=network2.get_subnet())\n",
+ " \n",
+ " node2.ip_route_add(subnet=network1.get_subnet(), gateway=network2.get_gateway())\n",
+ " \n",
+ " stdout, stderr = node2.execute(f'ip addr show {node2_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ " stdout, stderr = node2.execute(f'ip route list')\n",
+ " print (stdout)\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Test reachability\n",
+ "\n",
+ "Test ping between interfaces, observe successful output.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ "\n",
+ " stdout, stderr = node1.execute(f'ping -c 5 {node2_addr}')\n",
+ " print (stdout)\n",
+ " print (stderr)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 7: Delete the Slice\n",
+ "\n",
+ "Please delete your slice when you are done with your experiment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " slice.delete()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/fabric_examples/acceptance_testing/Acceptance Tests 3.2.5/functional_test_3.2.5-shared-nic.ipynb b/fabric_examples/acceptance_testing/Acceptance Tests 3.2.5/functional_test_3.2.5-shared-nic.ipynb
new file mode 100644
index 00000000..4bf3fa63
--- /dev/null
+++ b/fabric_examples/acceptance_testing/Acceptance Tests 3.2.5/functional_test_3.2.5-shared-nic.ipynb
@@ -0,0 +1,453 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Functional Test 3.2.5 - Shared NICs with FABNetv6\n",
+ "\n",
+ "This Jupyter notebook will allow you to create VMs on different sites and worker nodes consistent with requirements for test 3.2.5 for testing Shared NIC with FABNetv6 service. Because FABNetv6 works like a bridge, testing with just Shared NICs across all workers is sufficient.\n",
+ "\n",
+ "\n",
+ "## Step 1: Configure the Environment\n",
+ "\n",
+ "Before running this notebook, you will need to configure your environment using the [Configure Environment](../../fablib_api/configure_environment/configure_environment.ipynb) notebook. Please stop here, open and run that notebook, then return to this notebook.\n",
+ "\n",
+ "**This only needs to be done once.**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 2: Import the FABlib Library\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from fabrictestbed_extensions.fablib.fablib import FablibManager as fablib_manager\n",
+ "\n",
+ "fablib = fablib_manager()\n",
+ " \n",
+ "fablib.show_config()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 3: Check your existing slices\n",
+ "\n",
+ "Since testing can get confusing, check what slices you actually have. It may print nothing if you have no active slices."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " for slice in fablib.get_slices():\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 4: Create the Test Slice\n",
+ "\n",
+ "The following creates тшо nodes with Shared NICs in two selected sites on separate workers. Worker selection should be done manually. If you are unsure, the generated ads for each site ([in JSON format](https://github.com/fabric-testbed/aggregate-ads/tree/main/JSON)) can help.\n",
+ "\n",
+ "**Do not change the NIC type, but you can change workers and sites**\n",
+ "\n",
+ "**The code to create the slice will auto-refresh until the slice is created or it fails**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "from datetime import datetime\n",
+ "from dateutil import tz\n",
+ "\n",
+ "name1='Node1'\n",
+ "nic1_name='SharedNIC1'\n",
+ "\n",
+ "name2='Node2'\n",
+ "nic2_name='SharedNIC2'\n",
+ "\n",
+ "# set NIC type for all NICs\n",
+ "nic_type='NIC_Basic'\n",
+ "\n",
+ "\n",
+ "site1='TACC'\n",
+ "site2='SALT'\n",
+ "\n",
+ "# Make sure all workers at the same site are different.\n",
+ "# worker1 goes with site1, workers 2 and 3 go with site 2\n",
+ "worker1=f'{site1.lower()}-w1.fabric-testbed.net'\n",
+ "worker2=f'{site2.lower()}-w2.fabric-testbed.net'\n",
+ "\n",
+ "network1_name='l3-FABNetv6-1'\n",
+ "network2_name='l3-FABNetv6-2'\n",
+ "\n",
+ "cores=5\n",
+ "ram=10\n",
+ "disk=50\n",
+ "slice_name=f\"Slice Test 3.2.5-SharedNIC across {site1}/{site2} on {datetime.now()}\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " #Create Slice\n",
+ " print(f'Creating slice {slice_name}')\n",
+ " slice = fablib.new_slice(name=slice_name)\n",
+ " \n",
+ " # Node1\n",
+ " node1 = slice.add_node(name=name1, site=site1, host=worker1)\n",
+ " iface1 = node1.add_component(model=nic_type, name=nic1_name).get_interfaces()[0]\n",
+ "\n",
+ " # Node2\n",
+ " node2 = slice.add_node(name=name2, site=site2, host=worker2)\n",
+ " iface2 = node2.add_component(model=nic_type, name=nic2_name).get_interfaces()[0]\n",
+ "\n",
+ " # Networks (one in each site)\n",
+ " net1 = slice.add_l3network(name=network1_name, interfaces=[iface1], type='IPv6')\n",
+ " net2 = slice.add_l3network(name=network2_name, interfaces=[iface2], type='IPv6')\n",
+ " \n",
+ " # Submit Slice Request\n",
+ " slice.submit()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 5: Observe the Slice's Attributes\n",
+ "\n",
+ "### Print the slice "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " print(f\"{slice}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Print the Node List"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ "\n",
+ " print(f\"{slice.list_nodes()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Node Details"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " for node in slice.get_nodes():\n",
+ " print(f\"{node}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Print the Interfaces"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "###### You should see 6 interfaces (3 cards, 2 ports)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " \n",
+ " print(f\"{slice.list_interfaces()}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Shared NIC PCI Devices\n",
+ "\n",
+ "Run the command lspci
to see your Shared NIC PCI device(s).\n",
+ "\n",
+ "View node1's Shared NIC - you should see one of\n",
+ "- `Ethernet controller: Mellanox Technologies MT28908 Family [ConnectX-6 Virtual Function]` \n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "command = \"sudo dnf install -q -y pciutils && lspci | grep Mellanox\"\n",
+ "try:\n",
+ " node1 = slice.get_node(name=name1)\n",
+ " stdout, stderr = node1.execute(command)\n",
+ " print(f\"stdout: {stdout}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "## Step 6: Configure interfaces, test reachability"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Configure IP Addresses\n",
+ "\n",
+ "### Pick a Subnet\n",
+ "\n",
+ "Create a subnet and list of available IP addresses. All objects are Python IP management objects. You can use either IPv4 or IPv6 subnets and addresses."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " network1 = slice.get_network(name=network1_name)\n",
+ " network1_available_ips = network1.get_available_ips()\n",
+ " print(f\"{network1}\")\n",
+ " \n",
+ " network2 = slice.get_network(name=network2_name)\n",
+ " network2_available_ips = network2.get_available_ips()\n",
+ " print(f\"{network2}\")\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node1\n",
+ "\n",
+ "Get the node and the interface you wish to configure. You can use `node.get_interface` to get the interface that is connected to the specified network. Then `pop` an IP address from the list of available IPs and call `iface.ip_addr_add` to set the IP and subnet. \n",
+ "\n",
+ "Optionally, use the `node.execute()` method to show the results of adding the IP address."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ " node1_iface = node1.get_interface(network_name=network1_name) \n",
+ " node1_addr = network1_available_ips.pop(0)\n",
+ " node1_iface.ip_addr_add(addr=node1_addr, subnet=network1.get_subnet())\n",
+ " \n",
+ " node1.ip_route_add(subnet=network2.get_subnet(), gateway=network1.get_gateway())\n",
+ " \n",
+ " stdout, stderr = node1.execute(f'ip addr show {node1_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ " stdout, stderr = node1.execute(f'ip route list')\n",
+ " print (stdout)\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Configure Node2\n",
+ "\n",
+ "Repeat the steps to add the next available IP to the second node."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node2 = slice.get_node(name=name2) \n",
+ " node2_iface = node2.get_interface(network_name=network2_name) \n",
+ " node2_addr = network2_available_ips.pop(0)\n",
+ " node2_iface.ip_addr_add(addr=node2_addr, subnet=network2.get_subnet())\n",
+ " \n",
+ " node2.ip_route_add(subnet=network1.get_subnet(), gateway=network2.get_gateway())\n",
+ " \n",
+ " stdout, stderr = node2.execute(f'ip addr show {node2_iface.get_os_interface()}')\n",
+ " print (stdout)\n",
+ " \n",
+ " stdout, stderr = node2.execute(f'ip route list')\n",
+ " print (stdout)\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Test reachability\n",
+ "\n",
+ "Test ping between interfaces, observe successful output.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " node1 = slice.get_node(name=name1) \n",
+ "\n",
+ " stdout, stderr = node1.execute(f'ping -c 5 {node2_addr}')\n",
+ " print (stdout)\n",
+ " print (stderr)\n",
+ " \n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 7: Delete the Slice\n",
+ "\n",
+ "Please delete your slice when you are done with your experiment."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "try:\n",
+ " slice = fablib.get_slice(name=slice_name)\n",
+ " slice.delete()\n",
+ "except Exception as e:\n",
+ " print(f\"Exception: {e}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}