mfd-network-adapter 14.4.1
Module which handle operations on network adapters installed on different operating systems
Requires Python: <3.14,>=3.10

> [!IMPORTANT]  
> This project is under development. All source code and features on the main branch are for the purpose of testing or evaluation and not production ready.


# MFD Network Adapter

## Table of contents
1. [Usage](#usage)
2. [Exceptions](#exceptions-raised-by-mfd-network-adapter-module)
3. [Classes](#classes)
   * [NetworkAdapterOwner](#networkadapterowner)
     * [Methods](#methods)
     * [Available features](#available-features)
       * [VLAN](#vlan)
       * [VxLAN](#vxlan)
       * [GRE](#gre)
       * [GTP](#gtp)
       * [ARP](#arp)
       * [CPU](#cpu)
       * [DCB](#dcb)
       * [Driver](#driver)
       * [Firewall](#firewall)
       * [Interrupt](#interrupt)
       * [IP](#ip)
       * [Route](#route)
       * [Network Manager](#network-manager---nmcli)
       * [Virtualization](#virtualization)
       * [Queue](#queue)
       * [Utils](#utils)
       * [IPTables](#iptables)
       * [LinkAggregation](#link-aggregation-owner)
       * [MAC](#mac)
   * [NetworkInterface](#networkinterface)
     * [Common fields](#common-fields-of-networkinterface-)
     * [Linux fields](#additional-fields-of-linux-network-interface-)
     * [Windows fields](#additional-fields-of-windows-networkinterface-)
     * [Skipped interfaces](#what-type-of-interfaces-are-intentionally-skipped--linux-)
     * [Unsupported interfaces](#unsupported-interfaces--linux--)
     * [When to refresh interfaces?](#what-kind-of-actions-should-force-users-to-re-create-list-of-interfaces)
     * [Methods](#methods-1)
     * [Linux Methods](#additional-methods---linux)
     * [ESXi Methods](#additional-methods---esxi)
     * [Features](#features)
       * [Link](#link)
       * [IP](#ip-1)
       * [MTU](#mtu)
       * [Driver](#driver-1)
       * [Utils](#utils-1)
       * [Memory](#memory)
       * [NUMA](#numa)
       * [InterFrame](#interframe)
       * [RSS](#rss)
       * [Stats](#stats)
       * [LLDP](#lldp)
       * [Wol](#wol)
       * [Queue](#queue-1)
       * [Virtualization](#virtualization-1)
       * [StatChecker](#statchecker)
       * [Buffers](#buffers)
       * [DMA](#dma)
       * [Capture](#capture)
       * [VLAN](#vlan-1)
       * [Offload](#offload)
       * [ENS](#ens)
       * [NIC Team](#nic-team)
       * [MAC](#mac-1)
     * [Data structures](#networkinterface-data-structures-)
4. [Common Data structures](#common-data-structures)
5. [OS supported](#os-supported-)
6. [Static API](#static-api)
7. [Poolmon](#poolmon)
8. [Issue reporting](#issue-reporting)


## Usage
- More detailed examples:
  - [linux example](examples/linux_example.py)
  - [interface refresh example](examples/interface_refresh_example.py)

```python
from mfd_connect import SSHConnection
from mfd_network_adapter.network_adapter_owner.linux import NetworkAdapterOwner

connection = SSHConnection(ip='10.10.10.10', username='***', password='***')
owner = NetworkAdapterOwner(connection=connection)
interfaces = owner.get_interfaces()
```
## Exceptions raised by MFD-Network-Adapter module
- related to module:  `NetworkAdapterModuleException`
- related to Network Interface:  `InterfaceNameNotFound`, `IPException`, `IPAddressesNotFound`, `NetworkQueuesException`, `RDMADeviceNotFound`, `NumaNodeException`, `DriverInfoNotFound`, `FirmwareVersionNotFound`, `VirtualFunctionNotFoundException`, `HypervisorNotSupportedException`, `NetworkAdapterConfigurationException`, `NetworkInterfaceNotSupported`
- related to NetworkInterface's features: `VirtualizationFeatureException`
- 
## Classes

## `NetworkAdapterOwner`
Gathers system-level features related to networking. 
It implements methods for detecting and filtering NICs of the system. 

#### Class diagram:

```mermaid
classDiagram

NetworkAdapterOwner <|-- ESXiNetworkAdapterOwner
NetworkAdapterOwner <|-- FreeBSDNetworkAdapterOwner
LinuxNetworkAdapterOwner
LinuxNetworkAdapterOwner <|-- IPULinuxNetworkAdapterOwner
NetworkAdapterOwner <|-- LinuxNetworkAdapterOwner
NetworkAdapterOwner <|-- WindowsNetworkAdapterOwner
```

#### Methods:

- `get_interfaces`:  returns list of all detected Network Interfaces on the system.

To filter out specific Network Interfaces you can use following combinations of filters:
1) `pci_address`
2) (`pci_device`|`family`|`speed`) + `interface_indexes`
3) (`pci_device`|`family`|`speed`|`family`+`speed`) + (`random_interface`|`all_interfaces`)
4) (`random_interface`|`all_interfaces`) 
5) `interface_names`
6) `mac_address`

- `family`:
  - key of `DEVICE_IDS` from `mfd-const` e.g. `CPK`, `FVL`
  - or `Family` Enum member from `mfd-const` e.g. `Family.FVL`, `Family.CPK`

- `speed`:
  - key of `SPEED_IDS` from `mfd-const` e.g. `@40G`, `@100G`
    - available `speed` str formats: `@40G`, `@40g`, `40`, `40G`, `40g`, `40giga`, `40Giga`, `40GIGA`, `40Gb`
    - basically it needs to match speed number with such regex: `pattern = r"@{0,1}(?P<speed>\d+)\D*"`
  - or `Speed` Enum member from `mfd-const` e.g. `Speed.G40`, `Speed.G100`

* source code:
```python
def get_interfaces(
        self,
        *,
        pci_address: Optional[PCIAddress] = None,
        pci_device: Optional[PCIDevice] = None,
        family: Optional[Union[str, Family]] = None,
        speed: Optional[Union[str, Speed]] = None,
        interface_indexes: Optional[List[int]] = None,
        interface_names: Optional[List[str]] = None,
        random_interface: Optional[bool] = None,
        all_interfaces: Optional[bool] = None,
        namespace: Optional[str] = None,
        mac_address:  MACAddress | None = None
    ) -> List["NetworkInterface"]
```

* `Sorted Interfaces`: To get interfaces in the sorted order use sorted() built-in function to list of interfaces.
```python
list_of_interfaces = owner.get_interfaces()
sorted_list_of_interfaces = sorted(interfaces)
```

- `get_interface`:  returns single interface of network adapter.

Expected combinations are:
1) interface_name
2) pci_address
3) pci_device / family / speed + interface_index
4) mac_address

* source code:
```python
def get_interface(
        self,
        *,
        pci_address: Optional[PCIAddress] = None,
        pci_device: Optional[PCIDevice] = None,
        family: Optional[Union[str, Family]] = None,
        speed: Optional[Union[str, Speed]] = None,
        interface_index: Optional[int] = None,
        interface_name: Optional[str] = None,
        namespace: Optional[str] = None,
        mac_address:  MACAddress | None = None
    ) -> "NetworkInterface"
```

- `is_management_interface(ip: IPv4Interface)`: Validate if passed IP address is used by management interface.

[L]
- `load_driver_file(driver_filepath: 'Path', params: Optional[Dict])`: load file with driver to kernel using insmod, available usege of parameters to insmod

[L]
- `load_driver_module(driver_name: str, params: Optional[Dict])`: load module with driver to kernel using modprobe, available usege of parameters to modprobe

[L]
- `unload_driver_module(driver_name: str)`: unload driver using modprobe -r

[L]
- `reload_driver_module(driver_name: str, reload_time: float = 5, params: Optional[Dict])`: unload and load driver module with `reload_time` inactivity

[L]
- `create_vfs(interface_name: str, vfs_count: int)`: assign specified number of Virtual Functions to the Physical Function

[L]
- `delete_vfs(interface_name: str)`: delete all Virtual Functions assigned to the Physical Function.

- `get_pci_addresses_by_pci_device(self, pci_device: PCIDevice, namespace: Optional[str] = None) -> List[PCIAddress]`: Translate PCI Device to PCI Addresses.

- `get_pci_device_by_pci_address(self, pci_address: PCIAddress, namespace: Optional[str] = None) -> PCIDevice`: Translate PCI Address to PCI Device.

[W]
-`get_log_cpu_no(self) -> int`: Get the number of logical cpus.

[ESXi]
- `wait_for_interfaces_up(self, interfaces: list["NetworkInterface"], timeout: int = 30) -> None`: Wait for all interfaces become up.

[FreeBSD]
- `create_vfs(interface_name: str, vfs_count: int, config_dir: "Path | str", config_name: str = None)`: assign specified number of Virtual Functions to the Physical Function.

- `delete_vfs(interface_name: str, config_dir: "Path | str", remove_conf: bool, config_name: str = None)`: delete all Virtual Functions assigned to the Physical Function.

  - `add_vfs_to_config_file(
      self,
      interface_name: str,
      vfs_count: int,
      passthrough: bool = False,
      max_vlan_allowed: int | None = None,
      max_mac_filters: int | None = None,
      allow_promiscuous: bool = False,
      num_queues: int | None = None,
      mdd_auto_reset_vf: bool = False,
      config_dir: "Path | str" = "/home/user",
      mirror_src_vsi: int | None = None,
      config_name: str | None = None,
      mac_addr: tuple[str, ...] | None = None,
      allow_set_mac: bool = False,
      mac_anti_spoof: bool = True,
      **kwargs, 
        ) -> None:
  `: Add specified number of Virtual Functions to the conf file for the Physical Function. 

### Available features

#### VLAN
[Linux]
```python
create_vlan(        
    self,
    vlan_id: int,
    interface_name: str,
    vlan_name: Optional[str] = None,
    protocol: Optional[str] = None,
    reorder: bool = True,
    namespace_name: Optional[str] = None,
) -> ConnectionCompletedProcess
```
[Windows]
```python
create_vlan(
    self,
    vlan_id: int,
    method: str,
    interface_name: Optional[str] = None,
    interface_index: Optional[str] = None,
    nic_team_name: Optional[str] = None,
) -> ConnectionCompletedProcess
```
[FreeBSD]
```python
create_vlan(self, vlan_id: int, interface_name: str) -> ConnectionCompletedProcess
```

[Linux]
```python
remove_vlan(
    self,
    vlan_name: Optional[str] = None,
    vlan_id: Optional[int] = None,
    interface_name: Optional[str] = None,
    namespace_name: Optional[str] = None,
) -> ConnectionCompletedProcess:
```
[Windows]
```python
remove_vlan(self, vlan_id: int, method: str, interface_name: str, interface_index: Optional[str]) -> ConnectionCompletedProcess
```
[FreeBSD]
```python
remove_vlan(self, vlan_id: int) -> ConnectionCompletedProcess
```

[Linux]
```python
remove_all_vlans(self) -> None
```

[Linux]
```python
create_macvlan(self, interface_name: str, mac: MACAddress, macvlan_name: str) -> ConnectionCompletedProcess
```

[Linux]
```python
set_ingress_egress_map(self, interface_name: str, priority_map: str, direction: str, verify: bool = True) -> None
```

[Windows]
```python
list_vlan_ids(self, interface_name: str) -> list - Returns list of all VLAN IDs on provided interface.
```
[Windows]
```python
modify_vlan(self, vlan_id: int, nic_team_name: str, new_vlan_id: int, new_vlan_name: str) -> ConnectionCompletedProcess
```
#### VxLAN

[Linux] Create a VxLAN Tunnel.

```python
create_setup_vxlan(vxlan_name: str, ip_addr: Union[IPv4Interface, IPv6Interface], vni: int, group_addr: Union[IPv4Interface, IPv6Interface], interface_name: str, dstport: int, namespace_name: str | None = None) -> None
```

[Linux] Delete a VxLAN Tunnel.

```python
delete_vxlan(vxlan_name: str, namespace_name: str | None = None) -> None
```

[FreeBSD] Create a VxLAN Tunnel.

```python
create_setup_vxlan(local_ip_addr: Union[IPv4Interface, IPv6Interface], vni: int, group_addr: Union[IPv4Interface, IPv6Interface], interface_name: str, vxlan_ip_addr: Union[IPv4Interface, IPv6Interface]) -> Union[str, None]:
```

[FreeBSD] Delete a VxLAN Tunnel.

```python
delete_vxlan(vxlan_name: str) -> None:
```

#### GRE

[Linux] Create a GRE Tunnel.

```python
create_setup_gre(gre_tunnel_name: str, local_ip_addr: IPv4Interface | IPv6Interface, remote_ip_addr: IPv4Interface | IPv6Interface, interface_name: str, key_id: int, namespace_name: str | None = None) -> None
```

[Linux] Delete a GRE Tunnel.

```python
delete_gre(gre_tunnel_name: str, namespace_name: str | None = None) -> None
```

#### GTP
[Linux] Create a GTP Tunnel.
```python
create_setup_gtp_tunnel(tunnel_name: str, namespace_name: str | None = None, role: str = "sgsn") -> None
```
[Linux] Delete a GTP Tunnel.
```python
delete_gtp_tunnel(tunnel_name: str, namespace_name: str | None = None) -> None
```

### ARP

[FreeBSD]

```python
get_arp_table(self, ip_ver: IPVersion = IPVersion.V4) -> Dict[Union[IPv4Interface, IPv6Interface], MACAddress]
```

[Linux] [Windows]

```python
get_arp_table(
    self, ip_ver: IPVersion = IPVersion.V4, allowed_states: Optional[List[str]] = None
) -> Dict[Union[IPv4Interface, IPv6Interface], MACAddress]
```

[FreeBSD]

```python
add_arp_entry(self, ip: Union[IPv4Interface, IPv6Interface], mac: MACAddress) -> "ConnectionCompletedProcess"
```

[Linux]

```python
add_arp_entry(
    self,
    interface: "LinuxNetworkInterface",
    ip: Union[IPv4Interface, IPv6Interface],
    mac: MACAddress,
) -> "ConnectionCompletedProcess"
```

[Windows]

```python
add_arp_entry(
    self, interface: "WindowsNetworkInterface", ip: Union[IPv4Interface, IPv6Interface], mac: MACAddress
) -> "ConnectionCompletedProcess"
```

[FreeBSD]

```python
del_arp_entry(self, ip: Union[IPv4Interface, IPv6Interface]) -> "ConnectionCompletedProcess"
```

[Linux]

```python
del_arp_entry(
    self, interface: "LinuxNetworkInterface", ip: Union[IPv4Interface, IPv6Interface], mac: MACAddress
) -> "ConnectionCompletedProcess"
```

[Windows]

```python
del_arp_entry(self, interface: "WindowsNetworkInterface", ip: Union[IPv4Interface, IPv6Interface]) -> "ConnectionCompletedProcess"
```

[ESXi]
```python
del_arp_entry(self, ip: IPv4Interface | IPv6Interface) -> "ConnectionCompletedProcess": - Delete an entry from ARP table
```

[Linux]

```python
send_arp(self, interface: "LinuxNetworkInterface", destination: IPv4Interface, count: int = 1) -> "ConnectionCompletedProcess"
```

[FreeBSD]

```python
send_arp(self, interface: "FreeBSDNetworkInterface", destination: IPv4Interface, count: int = 1) -> "ConnectionCompletedProcess"
```

[Windows] [arp-ping.exe](https://www.elifulkerson.com/projects/arp-ping.php) is required to run this method.

```python
send_arp(
    self,
    interface: "WindowsNetworkInterface",
    destination: IPv4Interface,
    arp_ping_path: Union[str, Path],
    count: int = 1,
) -> "ConnectionCompletedProcess"
```

[Linux]

```python
flush_arp_table(self, interface: "LinuxNetworkInterface") -> "ConnectionCompletedProcess"
```

[Linux]

```python
delete_permanent_arp_table(
    self, interface: "LinuxNetworkInterface", ip_ver: IPVersion = IPVersion.V4
) -> None
```

[Linux]

```python
set_arp_response(self, interface: "LinuxNetworkInterface", state: State) -> "ConnectionCompletedProcess"
```

[Linux]

```python
check_arp_response_state(self, interface: "LinuxNetworkInterface") -> State
```

[Windows]

**Methods**
- `read_arp_table() -> str`

  Reads all lines in arp table.

  **Parameters:**
  * `None`
  
  **Returns:**
  * `str` - command output (ConnectionCompletedProcess.stdout)

- `read_ndp_neighbors(self, ip: "IPv4Interface | IPv6Interface") -> str`

  Reads neighbor discovery table (ND Table).

  **Parameters:**

  * `ip`: IP address of entry (IPv4 or IPv6)
    **Returns:**
  * `str` - command output (ConnectionCompletedProcess.stdout)


### Bonding
[Linux] Load bonding module
```python
load(self, mode: str = "active-backup", miimon: int = 100, max_bonds: int = 1) -> None
```

[Linux] Get list of bond interfaces
```python
get_bond_interfaces(self) -> list[str]
```

[Linux] Attach network interface to bonding interface using ifenslave command
```python
connect_interface_to_bond(self, network_interface: str | LinuxNetworkInterface, bonding_interface: str | LinuxNetworkInterface) -> None
```

[Linux] Detach network interface from bonding interface using ifenslave command
```python
disconnect_interface_from_bond(self, network_interface: str | LinuxNetworkInterface, bonding_interface: str | LinuxNetworkInterface) -> None
```

[Linux] Attach network interface to bonding interface using alternative commands
```python
connect_interface_to_bond_alternative(self, network_interface: str | LinuxNetworkInterface, bonding_interface: str | LinuxNetworkInterface, mode: str = None, miimon: int = None) -> None
```

[Linux] Detach network interface from bonding interface using alternative commands
```python
disconnect_interface_from_bond_alternative(self, network_interface: str | LinuxNetworkInterface, bonding_interface: str | LinuxNetworkInterface) -> None
```

[Linux] Create bond interface
```python
create_bond_interface(self, bonding_interface: str | LinuxNetworkInterface) -> None
```

[Linux] Set bonding params
```python
set_bonding_params(self, bonding_interface: str | LinuxNetworkInterface, params: dict[BondingParams, str]) -> None:
```

[Linux] Set active child
```python
set_active_child(self, bonding_interface: str | LinuxNetworkInterface, network_interface: str | LinuxNetworkInterface) -> None
```

[Linux] Get active child
```python
get_active_child(self, bonding_interface: str | LinuxNetworkInterface) -> str
```

[Linux] Get bonding mode
```python
get_bonding_mode(self, bonding_interface: str | LinuxNetworkInterface) -> str
```

[Linux] Delete bond interface
```python
delete_bond_interface(self, bonding_interface: str | LinuxNetworkInterface, child_interfaces: list[str | LinuxNetworkInterface]) -> None
```

[Linux] Verify if provided network_interface is active child
```python
verify_active_child(self, bonding_interface: str | LinuxNetworkInterface, network_interface: str | LinuxNetworkInterface) -> bool
```

[Linux] Get children
```python
get_children(self, bonding_interface: str | LinuxNetworkInterface) -> list[str]
```

### CPU

[ESXi] Initiate ESXi performance statistic gathering. Samples are collected every 2 seconds.
```python
start_cpu_usage_measure(self, file_path: str = "cpu.csv") -> "RemoteProcess"
```

[ESXi] Ensure statistic collection process termination.
```pyhton
stop_cpu_measurement(self, process: "RemoteProcess") -> bool
```

[ESXi] Extract from esxtop batch file data regarding particular VM vCPU usage.
```pyhton
parse_cpu_measurement_output(self, name_vm: str, file_path: str) -> int
```

### DCB

DCB feature is an object of mfd-dcb, so it provides you all the API that mfd-dcb does.

[API](https://github.com/intel/mfd-dcb)

### Driver

[Linux] Load driver by module name using modprobe.
```python
load_module(*, module_name: str, params: Optional[str] = None) -> "ConnectionCompletedProcess"
```

[Linux] Load driver file using insmod.
```python
load_module_file( *, module_filepath: "Path", params: Optional[str] = None) -> "ConnectionCompletedProcess"
```

[Linux] Unload driver from kernel via modprobe.
```python
unload_module(*, module_name: str, params: Optional[str] = None, with_dependencies: bool = False) -> "ConnectionCompletedProcess"
```

[Linux] Reload module using modprobe.
```python
reload_module(*, module_name: str, reload_time: float = 5, params: Optional[str] = None, with_dependencies: bool = False) -> None
```

[ESXi] Load module with configuration parameters.
```python
load_module(*, module_name: str, params: str = None) -> "ConnectionCompletedProcess"
```

[ESXi] Load module with configuration parameters.
```python
unload_module(module_name: str) -> "ConnectionCompletedProcess"
```

[ESXi] Reload module in system.
```python
reload_module(*, module_name: str, reload_time: float = 5, params: str = None) -> None
```

[ESXi] Get module params.
```python
get_module_params(module_name: str) -> str
```

[ESXi] Get module params as dictionary, e.g.: {"vmdq": "1,1,0,0"}.
```python
get_module_params_as_dict(module_name: str) -> Dict[str, str]
```

[ESXi] Prepare string for module settings in format required to reload module command.
```python
prepare_module_param_options(module_name: str, param: str, values: List[str]) -> str)
```

[ESXi] Prepare a string with multiple param options for the reload module command.
```python
prepare_multiple_param_options(self, *, param_dict: Dict, module_name: str) -> str
```

[ESXi] Prepare values for interfaces which share same driver needed for driver reload with them. 
Value of param will be prepared for update for all interfaces using <driver_name>.
```python
prepare_values_sharing_same_driver(*, driver_name: str, param: str, value: int) -> str
```

[Linux] Enable or disable SRIOV drivers auto probe.
```python
set_sriov_driver_autoprobe(self, state: bool) -> None
```

[Windows]

`change_state_family_interfaces(*, driver_filename: str, enable: State.ENABLED) -> None`

Change state of all interfaces with the same driver - belong to the same NIC family.

**Parameters:**

- `driver_filename (str)`: driver filename to be used for changing state, e.g. 'v40e65.sys'

- `enable (bool):` `State.ENABLED` if enable NICs, `State.DISABLED` otherwise

**Returns:**

- `None`

[Esxi] Wait for all interfaces become loaded.
```python
wait_for_all_interfaces_load(self, driver_name: str) -> None
```

### Firewall

[Windows]
 - The structure created for firewall purpose to structurized possible options for Inbound and OutBound actions:
```python
class DefInOutBoundActions(Enum):
    """Available def_in_bound and def_out_bound actions for set firewall feature on Windows Owner."""

    NOTCONFIGURED = "NotConfigured"
    ALLOW = "Allow"
    BLOCK = "Block"
```


**Methods**
- `set_firewall_default_action(profile: list[str] = ["Domain", "Public", "Private"], def_inbound_action: DefInOutBoundActions = DefInOutBoundActions.ALLOW, 
def_outbound_action: DefInOutBoundActions = DefInOutBoundActions.ALLOW) -> str`

  Sets firewall default Inbound and Outbound action settings on given profile(s).

  **Parameters:**
  * `profile`: FW profile to set. default: ['Domain', 'Public', 'Private']
    `def_inbound_action`: Default Inbound Action. Possible values are stored in DefInOutBoundActions structure: NOTCONFIGURED, ALLOW, BLOCK.
    `def_outbound_action`: Default Outbound Action. Possible values: NotConfigured, Allow, Block
  
  **Returns:**
  * `str` - command output (ConnectionCompletedProcess.stdout)

- `set_firewall_profile(profile: list[str] = ("Domain", "Public", "Private"), enabled: bool | State = State.ENABLED) -> str`

  Enables or Disable the firewall on given profile(s).

  **Parameters:**

  * `profile`: FW profile to set. default: `['Domain', 'Public', 'Private']`
  * `enabled`: State.ENABLED or True for on, State.DISABLED or False for off
    **Returns:**
  * `str` - command output (ConnectionCompletedProcess.stdout)

### Interrupt

[ESXi] Set Interrupt moderation rate.
```python
set_interrupt_moderation_rate(self, *, driver_name: str, rxvalue: Optional[int] = None, txvalue: Optional[int] = None) -> None
```

### IP

[L] Create bridge.
```python
create_bridge(self, bridge_name: str) -> None
```

[L] Add interface to bridge.
```python
add_to_bridge(self, bridge_name: str, interface_name: str) -> None
```

[L] Create namespace.
```python
create_namespace(self, namespace_name: str) -> None
```

[L] Delete namespace.
```python
delete_namespace(self, namespace_name: str) -> None
```

[L] Add interface to namespace.
```python
add_to_namespace(self, namespace_name: str, interface_name: str) -> None
```

[L] Add virtual link (interface/device with type - e.g. bridge, vlan)
```python
add_virtual_link(self, device_name: str, device_type: str, namespace: Optional[str] = None) -> None
```

[L] Delete virtual link (interface/device)
```python
delete_virtual_link(self, device_name: str, namespace: Optional[str] = None) -> None
```

[L] Add Virtual Ethernet interface
```python
create_veth_interface(self, interface_name: str, peer_name: str, namespace: Optional[str] = None) -> None
```

[L] Kill processes used in namespace
```python
kill_namespace_processes(self, namespace: str) -> None
```

[OS Agnostic] Remove conflicting IPs with tested interface
```python
remove_conflicting_ip(tested_interface: "NetworkInterface", all_interfaces: list["NetworkInterface"] | None = None) -> None:
```

[OS Agnostic] Remove duplicated IPs in system
```python
remove_duplicate_ip(ip_to_compare: IPv4Interface | IPv6Interface, interface_to_skip: "NetworkInterface | None" = None, all_interfaces: list["NetworkInterface"] | None = None) -> None
```

[L] Get output from ip link show bridge
```python
get_ip_link_show_bridge_output(self) -> str:
```

[L] Get list of net namespaces
```python
get_namespaces(self) -> list[str]:
```

[L] Delete all net namespaces
```python
delete_all_namespaces(self) -> None:
```

[L] Rename an interface
```python
rename_interface(self, current_name: str, new_name: str, namespace: str | None = None) -> None:
```
___
### Route
[Linux] Add ip route
```python
add_route(self, ip_network: "IPv4Interface", device: str, namespace: Optional[str] = None)
```

[Linux] Add ip route via remote address
```python
add_route_via_remote(
        self,
        ip_network: "IPv4Interface",
        remote_ip: "IPv4Address",
        device: str,
        set_onlink: bool = False,
        namespace: Optional[str] = None,
    ):
```
[Linux] Add default ip route
```python
add_default_route(self, remote_ip: "IPv4Address", device: str, namespace: Optional[str] = None) -> None:
```

[Linux] Change ip route
```python
change_route(
        self, ip_network: "IPv4Interface", remote_ip: "IPv4Address", device: str, namespace: Optional[str] = None
    ) -> None:
```

[Linux] Delete ip route
```python
delete_route(self, ip_network: "IPv4Interface", device: str, namespace: Optional[str] = None) -> None:
 ```

[Linux] Clear routing table for interface
```python
clear_routing_table(self, device: str, namespace: str | None = None) -> None:
```

### Network Manager - nmcli

[Linux] Set managed state
```python
set_managed(self, device: str, state: State) -> None:
```

[Linux] Remove device - set managed state to `no`
```python
remove_device(self, device: str) -> None:
```

[Linux] Get managed state
```python
get_managed_state(self, device: str) -> State:
```

[Linux] Verify managed state
```python
verify_managed(self, device: str, expected_state: State) -> bool:
```

[Linux] Prepare configuration file of interface for network manager
```python
prepare_adapter_config_file_for_network_manager(self, interface_name: str) -> None:
```
___
### Virtualization

[Linux] Create mediated device
```python
create_mdev(self, mdev_uuid: Union[str, "UUID"], pci_address: "PCIAddress", driver_name: str) -> None:
```

[Linux] Remove mediated device
```python
remove_mdev(self, mdev_uuid: Union[str, "UUID"]) -> None:
```

[Linux] Enable mediated device
```python
enable_mdev(self, mdev_uuid: Union[str, "UUID"]) -> None:
```

[Linux] Disable mediated device
```python
disable_mdev(self, mdev_uuid: Union[str, "UUID"]) -> None:
```

[Linux] Get list of all mdevs
```python
get_all_mdev_uuids(self) -> list[str]:
```

[Linux] Get PCI address of the PF that mdev is created on
```python
get_pci_address_of_mdev_pf(self, mdev_uuid: Union[str, "UUID"]) -> PCIAddress:
```

[Linux] Assign queue pairs to the mdev
```python
assign_queue_pairs(self, mdev_uuid: Union[str, "UUID"], queue_pairs: dict[str, int]) -> None:
```

[Linux] Set VMDQ (Virtual Machine Device Queues) parameter for driver.
```python
set_vmdq(driver_name: str, value: int, reload_time: float = 5) -> None
```

[ESXi] Set VMDQ (Virtual Machine Device Queues) parameter for all interfaces sharing <driver_name>.
```python
set_vmdq(driver_name: str, value: int, reload_time: float = 5) -> None
```

[ESXi] Set VMDQ (Virtual Machine Device Queues) parameter on provided interface only.
```python
set_vmdq_on_interface(*, interface: "ESXiNetworkInterface", value: int, reload_time: float = 10) -> None
```

[ESXi] Set NumQPsPerVF parameter for all interfaces sharing <driver_name>.
```python
set_num_queue_pairs_per_vf(*, driver_name: str, value: int, reload_time: float = 10) -> None
```

[ESXi]  Verify whether VMDQ is set as expected.
```python
verify_vmdq(interface: "NetworkInterface", desired_value: int) -> None
```

[ESXi]  Get the list of VFs (IDs) of specified physical device used by VM.
```python
get_vm_vf_ids(self, vm_name: str, interface: "ESXiNetworkInterface") -> list[int]
```

[Linux] Set number of MSI-X vectors for PF interface.
```python
set_msix_vectors_count(self, count: int, method: MethodType = MethodType.DEVLINK) -> None
```

[Linux] Get number of MSI-X vectors of PF interface.
```python
get_msix_vectors_count(self, method: MethodType = MethodType.DEVLINK) -> int
```

### Queue

[L] Get number of queues from proc interrupts
```python
get_queue_number_from_proc_interrupts(self, interface_name: str) -> str:
```

### Utils

[OS Agnostic] Check if port is used.

```python
is_port_used(self, port_num: int) -> bool
```

[L] Get bridge interfaces
```python
get_bridge_interfaces(self, all_interfaces: list["NetworkInterface"] | None = None) -> list[NetworkInterface]:
```

[OS Agnostic] Get interfaces on the same bus as passed interface
```python
get_same_pci_bus_interfaces(self, interface: "NetworkInterface") -> list["NetworkInterface"]:
```


[L] Capture some meminfo results
```python
get_memory_values(self) -> dict[str, int]
```

[W] Get memory value based on poolmons values: available, paged and non-paged memor
```python
get_memory_values(self, poolmon_dir_path: "Path | str", *, cleanup_logs: bool = True) -> dict[str, str | int]:
```

[FreeBSD]

- Convert ConfigParser string to FreeBSD Virtual Function config format.  
example input string:  
  [VF-0]
passthrough = true  
max-vlan-allowed = 1  
max-mac-filters = 1  
allow-set-mac = true  
mac-addr = 00:00:00:00:00:00  
allow-promisc = true  
num-queues = 4  
mdd-auto-reset-vf = true  
mac-anti-spoof = false  
mirror-src-vsi = 3  
example output string:  
DEFAULT {  
}  
VF-0 { 
passthrough : true  
max-vlan-allowed : 16  
max-mac-filters : 16  
allow-set-mac : true  
mac-addr : 00:00:00:00:00:00  
allow-promisc : true  
num-queues : 4  
mdd-auto-reset-vf : true  
mac-anti-spoof : true  
mirror-src-vsi : 4  
}  

```python
convert_to_vf_config_format(config: str) -> str
```

- Update num_vfs in config string. 
```python
update_num_vfs_in_config(config: str, vfs_num: int) -> str
```

### IPTables

[L] Set snat rule in iptables
```python
set_snat_rule(self, source_interface_ip: "IPv4Address", destination_ip: "IPv4Address", new_source_ip: "IPv4Address")  -> None:
```

[L] Set dnat rule in iptables
```python
set_dnat_rule(self, original_destination_ip: "IPv4Address", new_destination_ip: "IPv4Address") -> None:
```

### DDP
[ESXi] Load DDP Package
```python
load_ddp_package(self, vmnic: str, package_name: str, force: bool = False, expect_error: bool = False) -> None:
```

[ESXi] Rollback loaded DDP package
```python
rollback_ddp_package(self, vmnic: str, force: bool = False) -> None:
```

[ESXi] List currently loaded ddp packages
```python
list_ddp_packages(self, csv_format: bool = False) -> str:
```

[ESXi] Check if DDP is loaded
```python
is_ddp_loaded(self, vmnic: str, package_name: str | None = "default package") -> bool:
```

## Link Aggregation Owner

**Methods**

[Windows]
`create_nic_team(        
        interfaces: "list[WindowsNetworkInterface] | WindowsNetworkInterface",
        team_name: str,
        *,
        teaming_mode: TeamingMode = TeamingMode.SWITCHINDEPENDENT,
        lb_algorithm: LoadBalancingAlgorithm = LoadBalancingAlgorithm.DYNAMIC,
    ) -> None`
Create NIC team.

**Parameters**:

`interfaces`: interface or list of interfaces to be added to the new NIC team
`team_name`: name of the NIC team
`teaming_mode`: team operating mode: {LACP, Static, SwitchIndependent}
`lb_algorithm`: load balancing algorithm: {Dynamic, TransportPorts, IPAddresses, MacAddresses, HyperVPort}

**Raises:**

`NICTeamFeatureProcessException`: if the return code is not expected

`wait_for_nic_team_status_up(team_name: str, *, count: int = 4, tout: int = 10) -> bool`
Wait for NIC team status change to up.

**Parameters:**

`team_name`: name of the NIC team
`count`: number of checks
`tout`: timeout in seconds between subsequent tries

**Raises:**

`NICTeamFeatureException:` if NIC team does not exist

**Returns:**

`True` if NIC team status is up, `False` otherwise


`get_nic_teams()-> dict[str, dict[str, str]]`
Get a dictionary of all existing NIC teams on host with name as a key and value is a dictionary of other field-value pairs.

**Returns:**

dictionary of existing NIC teams, e.g. {"TeamBlue": {"Name": TeamBlue, "Members": Ethernet 9 ...}}

**Raises:**

`NICTeamFeatureException:` if the return code is not expected

`remove_nic_team(team_name: str) -> None`
Remove specified NIC team from the host.

**Parameters:**

`team_name:` name of the NIC team

**Raises:**

`NICTeamFeatureException:` if the return code is not expected

**Returns:**

Nothing

`get_nic_team_interfaces(team_name: str) -> list[str]`
Get list of network interfaces which are members of the specified NIC team.

**Parameters:**

`team_name:` name of the NIC team

**Raises:**

`NICTeamFeatureException:` if the return code is not expected

**Returns:**

team members of specified NIC team, e.g. `['Ethernet 1', 'Ethernet 2']`

### MAC
MAC Feature

[Linux, Windows, FreeBSD]
- `set_mac(interface_name: str, mac: MACAddress) -> None` : Set MAC address for the interface.

[Linux]
- `delete_mac(interface_name: str, mac: MACAddress) -> None` : Delete MAC address from the interface.
- `get_default_mac(interface_name: str) -> MACAddress` : Get permanent HW MAC address of the interface.

## `NetworkInterface`

Class reflecting single Network Interface. List of supported NICs Types varies between OSes. 
To check what Interface Types are supported go to [mfd-typing.network_interface](https://github.com/intel/mfd-typing/blob/main/mfd_typing/network_interface.py#L19).
It offers plenty of features which are described in the section below. 
Every feature is kept in separate folder, e.g. `ip`, `link`, `mtu`. 
Feature code is divided into separate files - one per OS supported: `esxi.py`, `freebsd.py`, `linux.py`, `windows.py`).

#### Common fields of NetworkInterface: 

- `name` - Interface name
- `mac_address` - MAC Address
- `pci_address` - PCI Address
- `interface_type` - One of multiple available types (`InterfaceType` enum from `mfd-typing`): 
  - `GENERIC` : default
  - `ETH_CONTROLLER` : network controller listed on pci (default for network device without loaded driver)
  - `VIRTUAL_DEVICE` : interface located in path ../devices/virtual/net/ (bridge, macvlan, loopback)
  - `PF` : regular physical interface; located on PCI bus (../devices/pci0000/..) (eth)
  - `VF` : virtual inteface (SRIOV); described as 'Virtual Interface' in lspci detailed info
  - `VPORT` : IPU-specific interface which shares PCI Address with other interfaces (extra VSI Info stored in `VsiInfo`)
  - `VMNIC` : ESXi-specific interface or Windows Hyper-V interface (VNIC associated with SR-IOV interface), not used in Linux 
  - `VMBUS` : Hyper-V specific for Linux Guests (https://docs.kernel.org/virt/hyperv/vmbus.html)
  - `MANAGEMENT` : interface having IPv4 Address in range of management network (`from mfd_const.mfd_const import MANAGEMENT_NETWORK`)
  - `VLAN` : virtual device which is assigned to 802.1Q VLAN (extra VLAN details stored in `VlanInterfaceInfo`)
  - `CLUSTER_MANAGEMENT` : Windows ASHCI cluster management interface type
  - `CLUSTER_STORAGE` : Windows ASHCI storage / compute interfaces in cluster nodes, marked as `vSMB` in system
  - `BTS` : Linux: BTS shares PCI bus, device ID and index, we will mark it based on name starting with `nac`
- `installed` - Boolean flag telling us whether there is driver loaded for particular interface or not
- `branding_string` - Friendly name of network adapter
- `vlan_info` - `VlanInterfaceInfo` structure from `mfd-typing` holds details like: `vlan_id` and `parent` (name of parent interface)
- `switch_info` - `SwitchInfo` structure from `data_structures` holds details like: `switch` (MFD-Switchmanagement object) and `port` (literal representing switch port name of connected interface)
- `speed` - Python's property field that returns Speed Enum member (from mfd-const) based on PCI Device
- `family` - Python's property field that returns Family Enum member (from mfd-const) based on PCI Device

#### Additional Fields of Linux Network Interface:
- `namespace` - Linux network namespace name
- `vsi_info` - VPORT specific attribute which holds info about VSI Info (`fn_id`, `host_id`, `is_vf`, `vsi_id`, `vport_id`, `is_created`, `is_enabled`)

#### Additional Fields of Windows NetworkInterface:
- `description` - Description
- `index` - Index info
- `manufacturer` - Manufacturer name
- `net_connection_status` - Status of connection
- `pnp_device_id` - PnP Device ID
- `product_name` - Product Name
- `service_name` - Service Name
- `guid` - GUID
- `win32_speed` - Advertised speed of the interface
- `cluster_info` - Structure of Cluster Info (holds information about `node` & `network`), e.g.: 
  ```python
  from mfd_typing.network_interface import ClusterInfo
  
  ClusterInfo(node="NODE-1", network="Cluster Network 2")
  ```

#### What type of interfaces are intentionally skipped? (Linux)
- :x: Loopback interface
- :x: 40G FCoE
- :x: Tunnel interface

#### Unsupported interfaces (Linux):
- :question: USB ??? -> they might be listed on sys/class/net but is not supported

Network Interface object shall remain stateless.
It means that any action which significantly affect the lifecycle of Interface object like:
- driver reload 
- host reboot
- vlan created/deleted
- namespace created/deleted
- VFs created/deleted

should trigger the re-creation process.

> :warning: It is forbidden to modify its core properties like name, pci_address (all stored as InterfaceInfo dataclass).

#### What kind of actions should force users to re-create list of interfaces?
- adding/removing interface to/from namespace
- adding/removing interface to/from vlan
- loading/unloading driver
- binding/unbinding driver
- creating/destroying virtual interfaces
- attaching/deattaching interfaces to/from VM
- flashing MAC Address (adding alternate MAC Address)
- renaming interface


#### Class diagram:

```mermaid
classDiagram

NetworkInterface <|-- NetworkInterfaceBase
NetworkInterface <|-- ESXiNetworkInterface
NetworkInterfaceBase <|-- FreeBSDNetworkInterface
NetworkInterfaceBase <|-- LinuxNetworkInterface
NetworkInterfaceBase <|-- WindowsNetworkInterface

```

* source code:

```
class NetworkInterface(ABC):
    def __init__(
      self,
      *,
      connection: "Connection",
      interface_info: "InterfaceInfo",
      topology: "NetworkInterfaceModelBase | None" = None,
    )
 ```

#### Methods
- `get_branding_string() -> str` - Get branding string. Raise `BrandingStringException` if branding string not found.

- `get_stats() -> Dict` - Get specific Network Interface statistic or get all the statistics.

- `get_mac_address()` - Get MAC Address of interface

- `get_ring_settings() -> "RingBufferSettings"` - Get ring buffer settings.

- `set_ring_settings(settings: "RingBuffer") -> None` - Set ring buffer settings.

- `get_firmware_version() -> str` - Get firmware version of Adapter.

- `get_driver_info() -> 'DriverInfo'` - Get information about driver name and driver version of Network Adapter. Raises `AdapterDriverInfoNotFound` if driver info is incomplete or absent.

- `get_number_of_ports() -> int'` - Get number of ports in tested adapter.

- `reload_adapter_devlink() -> None` - Reload adapter via devlink.

- `restart() -> None` - Restart interface.

#### Additional methods - Linux

- `get_device_string() -> str` - Get device string. Raise `DeviceStringException` if device string not found. Linux's implementation only.

- `get_rdma_device_name()` - Get RDMA device name for Network Interface, requires `ibv_devices` tool available on machine.

- `get_network_queues() -> Dict` - Get network queues. Raise `NetworkQueuesException` if failed. Linux's implementation only.

- `set_network_queues(rx: int, tx: int, optional: int, combined: int) -> None` - Set network queues. Raise `NetworkQueuesException` if no values provided or failed. Linux's implementation only.

- `get_numa_node() -> int` - Get the Non-Uniform