388 lines
11 KiB
Python
388 lines
11 KiB
Python
"""
|
|
Clients for dealing with bridge/interface commands.
|
|
"""
|
|
from typing import Callable
|
|
|
|
import netaddr
|
|
|
|
from core.executables import ETHTOOL, IP, OVS_VSCTL, SYSCTL, TC
|
|
|
|
|
|
class LinuxNetClient:
|
|
"""
|
|
Client for creating Linux bridges and ip interfaces for nodes.
|
|
"""
|
|
|
|
def __init__(self, run: Callable[..., str]) -> None:
|
|
"""
|
|
Create LinuxNetClient instance.
|
|
|
|
:param run: function to run commands with
|
|
"""
|
|
self.run: Callable[..., str] = run
|
|
|
|
def set_hostname(self, name: str) -> None:
|
|
"""
|
|
Set network hostname.
|
|
|
|
:param name: name for hostname
|
|
:return: nothing
|
|
"""
|
|
self.run(f"hostname {name}")
|
|
|
|
def create_route(self, route: str, device: str) -> None:
|
|
"""
|
|
Create a new route for a device.
|
|
|
|
:param route: route to create
|
|
:param device: device to add route to
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{IP} route add {route} dev {device}")
|
|
|
|
def device_up(self, device: str) -> None:
|
|
"""
|
|
Bring a device up.
|
|
|
|
:param device: device to bring up
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{IP} link set {device} up")
|
|
|
|
def device_down(self, device: str) -> None:
|
|
"""
|
|
Bring a device down.
|
|
|
|
:param device: device to bring down
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{IP} link set {device} down")
|
|
|
|
def device_name(self, device: str, name: str) -> None:
|
|
"""
|
|
Set a device name.
|
|
|
|
:param device: device to set name for
|
|
:param name: name to set
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{IP} link set {device} name {name}")
|
|
|
|
def device_show(self, device: str) -> str:
|
|
"""
|
|
Show link information for a device.
|
|
|
|
:param device: device to get information for
|
|
:return: device information
|
|
"""
|
|
return self.run(f"{IP} link show {device}")
|
|
|
|
def address_show(self, device: str) -> str:
|
|
"""
|
|
Show address information for a device.
|
|
|
|
:param device: device name
|
|
:return: address information
|
|
"""
|
|
return self.run(f"{IP} address show {device}")
|
|
|
|
def get_mac(self, device: str) -> str:
|
|
"""
|
|
Retrieve MAC address for a given device.
|
|
|
|
:param device: device to get mac for
|
|
:return: MAC address
|
|
"""
|
|
return self.run(f"cat /sys/class/net/{device}/address")
|
|
|
|
def get_ifindex(self, device: str) -> str:
|
|
"""
|
|
Retrieve ifindex for a given device.
|
|
|
|
:param device: device to get ifindex for
|
|
:return: ifindex
|
|
"""
|
|
return self.run(f"cat /sys/class/net/{device}/ifindex")
|
|
|
|
def device_ns(self, device: str, namespace: str) -> None:
|
|
"""
|
|
Set netns for a device.
|
|
|
|
:param device: device to setns for
|
|
:param namespace: namespace to set device to
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{IP} link set {device} netns {namespace}")
|
|
|
|
def device_flush(self, device: str) -> None:
|
|
"""
|
|
Flush device addresses.
|
|
|
|
:param device: device to flush
|
|
:return: nothing
|
|
"""
|
|
self.run(
|
|
f"[ -e /sys/class/net/{device} ] && "
|
|
f"{IP} address flush dev {device} || true",
|
|
shell=True,
|
|
)
|
|
|
|
def device_mac(self, device: str, mac: str) -> None:
|
|
"""
|
|
Set MAC address for a device.
|
|
|
|
:param device: device to set mac for
|
|
:param mac: mac to set
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{IP} link set dev {device} address {mac}")
|
|
|
|
def delete_device(self, device: str) -> None:
|
|
"""
|
|
Delete device.
|
|
|
|
:param device: device to delete
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{IP} link delete {device}")
|
|
|
|
def delete_tc(self, device: str) -> None:
|
|
"""
|
|
Remove traffic control settings for a device.
|
|
|
|
:param device: device to remove tc
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{TC} qdisc delete dev {device} root")
|
|
|
|
def checksums_off(self, iface_name: str) -> None:
|
|
"""
|
|
Turns interface checksums off.
|
|
|
|
:param iface_name: interface to update
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{ETHTOOL} -K {iface_name} rx off tx off")
|
|
|
|
def create_address(self, device: str, address: str, broadcast: str = None) -> None:
|
|
"""
|
|
Create address for a device.
|
|
|
|
:param device: device to add address to
|
|
:param address: address to add
|
|
:param broadcast: broadcast address to use, default is None
|
|
:return: nothing
|
|
"""
|
|
if broadcast is not None:
|
|
self.run(f"{IP} address add {address} broadcast {broadcast} dev {device}")
|
|
else:
|
|
self.run(f"{IP} address add {address} dev {device}")
|
|
if netaddr.valid_ipv6(address.split("/")[0]):
|
|
# IPv6 addresses are removed by default on interface down.
|
|
# Make sure that the IPv6 address we add is not removed
|
|
self.run(f"{SYSCTL} -w net.ipv6.conf.{device}.keep_addr_on_down=1")
|
|
|
|
def delete_address(self, device: str, address: str) -> None:
|
|
"""
|
|
Delete an address from a device.
|
|
|
|
:param device: targeted device
|
|
:param address: address to remove
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{IP} address delete {address} dev {device}")
|
|
|
|
def create_veth(self, name: str, peer: str) -> None:
|
|
"""
|
|
Create a veth pair.
|
|
|
|
:param name: veth name
|
|
:param peer: peer name
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{IP} link add name {name} type veth peer name {peer}")
|
|
|
|
def create_gretap(
|
|
self, device: str, address: str, local: str, ttl: int, key: int
|
|
) -> None:
|
|
"""
|
|
Create a GRE tap on a device.
|
|
|
|
:param device: device to add tap to
|
|
:param address: address to add tap for
|
|
:param local: local address to tie to
|
|
:param ttl: time to live value
|
|
:param key: key for tap
|
|
:return: nothing
|
|
"""
|
|
cmd = f"{IP} link add {device} type gretap remote {address}"
|
|
if local is not None:
|
|
cmd += f" local {local}"
|
|
if ttl is not None:
|
|
cmd += f" ttl {ttl}"
|
|
if key is not None:
|
|
cmd += f" key {key}"
|
|
self.run(cmd)
|
|
|
|
def create_bridge(self, name: str) -> None:
|
|
"""
|
|
Create a Linux bridge and bring it up.
|
|
|
|
:param name: bridge name
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{IP} link add name {name} type bridge")
|
|
self.run(f"{IP} link set {name} type bridge stp_state 0")
|
|
self.run(f"{IP} link set {name} type bridge forward_delay 0")
|
|
self.run(f"{IP} link set {name} type bridge mcast_snooping 0")
|
|
self.run(f"{IP} link set {name} type bridge group_fwd_mask 65528")
|
|
self.device_up(name)
|
|
|
|
def delete_bridge(self, name: str) -> None:
|
|
"""
|
|
Bring down and delete a Linux bridge.
|
|
|
|
:param name: bridge name
|
|
:return: nothing
|
|
"""
|
|
self.device_down(name)
|
|
self.run(f"{IP} link delete {name} type bridge")
|
|
|
|
def set_iface_master(self, bridge_name: str, iface_name: str) -> None:
|
|
"""
|
|
Assign interface master to a Linux bridge.
|
|
|
|
:param bridge_name: bridge name
|
|
:param iface_name: interface name
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{IP} link set dev {iface_name} master {bridge_name}")
|
|
self.device_up(iface_name)
|
|
|
|
def delete_iface(self, bridge_name: str, iface_name: str) -> None:
|
|
"""
|
|
Delete an interface associated with a Linux bridge.
|
|
|
|
:param bridge_name: bridge name
|
|
:param iface_name: interface name
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{IP} link set dev {iface_name} nomaster")
|
|
|
|
def existing_bridges(self, _id: int) -> bool:
|
|
"""
|
|
Checks if there are any existing Linux bridges for a node.
|
|
|
|
:param _id: node id to check bridges for
|
|
:return: True if there are existing bridges, False otherwise
|
|
"""
|
|
output = self.run(f"{IP} -o link show type bridge")
|
|
lines = output.split("\n")
|
|
for line in lines:
|
|
values = line.split(":")
|
|
if not len(values) >= 2:
|
|
continue
|
|
name = values[1]
|
|
fields = name.split(".")
|
|
if len(fields) != 3:
|
|
continue
|
|
if fields[0] == "b" and fields[1] == _id:
|
|
return True
|
|
return False
|
|
|
|
def disable_mac_learning(self, name: str) -> None:
|
|
"""
|
|
Disable mac learning for a Linux bridge.
|
|
|
|
:param name: bridge name
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{IP} link set {name} type bridge ageing_time 0")
|
|
|
|
|
|
class OvsNetClient(LinuxNetClient):
|
|
"""
|
|
Client for creating OVS bridges and ip interfaces for nodes.
|
|
"""
|
|
|
|
def create_bridge(self, name: str) -> None:
|
|
"""
|
|
Create a OVS bridge and bring it up.
|
|
|
|
:param name: bridge name
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{OVS_VSCTL} add-br {name}")
|
|
self.run(f"{OVS_VSCTL} set bridge {name} stp_enable=false")
|
|
self.run(f"{OVS_VSCTL} set bridge {name} other_config:stp-max-age=6")
|
|
self.run(f"{OVS_VSCTL} set bridge {name} other_config:stp-forward-delay=4")
|
|
self.device_up(name)
|
|
|
|
def delete_bridge(self, name: str) -> None:
|
|
"""
|
|
Bring down and delete a OVS bridge.
|
|
|
|
:param name: bridge name
|
|
:return: nothing
|
|
"""
|
|
self.device_down(name)
|
|
self.run(f"{OVS_VSCTL} del-br {name}")
|
|
|
|
def set_iface_master(self, bridge_name: str, iface_name: str) -> None:
|
|
"""
|
|
Create an interface associated with a network bridge.
|
|
|
|
:param bridge_name: bridge name
|
|
:param iface_name: interface name
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{OVS_VSCTL} add-port {bridge_name} {iface_name}")
|
|
self.device_up(iface_name)
|
|
|
|
def delete_iface(self, bridge_name: str, iface_name: str) -> None:
|
|
"""
|
|
Delete an interface associated with a OVS bridge.
|
|
|
|
:param bridge_name: bridge name
|
|
:param iface_name: interface name
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{OVS_VSCTL} del-port {bridge_name} {iface_name}")
|
|
|
|
def existing_bridges(self, _id: int) -> bool:
|
|
"""
|
|
Checks if there are any existing OVS bridges for a node.
|
|
|
|
:param _id: node id to check bridges for
|
|
:return: True if there are existing bridges, False otherwise
|
|
"""
|
|
output = self.run(f"{OVS_VSCTL} list-br")
|
|
if output:
|
|
for line in output.split("\n"):
|
|
fields = line.split(".")
|
|
if fields[0] == "b" and fields[1] == _id:
|
|
return True
|
|
return False
|
|
|
|
def disable_mac_learning(self, name: str) -> None:
|
|
"""
|
|
Disable mac learning for a OVS bridge.
|
|
|
|
:param name: bridge name
|
|
:return: nothing
|
|
"""
|
|
self.run(f"{OVS_VSCTL} set bridge {name} other_config:mac-aging-time=0")
|
|
|
|
|
|
def get_net_client(use_ovs: bool, run: Callable[..., str]) -> LinuxNetClient:
|
|
"""
|
|
Retrieve desired net client for running network commands.
|
|
|
|
:param use_ovs: True for OVS bridges, False for Linux bridges
|
|
:param run: function used to run net client commands
|
|
:return: net client class
|
|
"""
|
|
if use_ovs:
|
|
return OvsNetClient(run)
|
|
else:
|
|
return LinuxNetClient(run)
|