""" Clients for dealing with bridge/interface commands. """ from core.constants import BRCTL_BIN, ETHTOOL_BIN, IP_BIN, OVS_BIN, TC_BIN def get_net_client(use_ovs, run): """ Retrieve desired net client for running network commands. :param bool use_ovs: True for OVS bridges, False for Linux bridges :param func run: function used to run net client commands :return: net client class """ if use_ovs: return OvsNetClient(run) else: return LinuxNetClient(run) class LinuxNetClient: """ Client for creating Linux bridges and ip interfaces for nodes. """ def __init__(self, run): """ Create LinuxNetClient instance. :param run: function to run commands with """ self.run = run def set_hostname(self, name): """ Set network hostname. :param str name: name for hostname :return: nothing """ self.run(f"hostname {name}") def create_route(self, route, device): """ Create a new route for a device. :param str route: route to create :param str device: device to add route to :return: nothing """ self.run(f"{IP_BIN} route add {route} dev {device}") def device_up(self, device): """ Bring a device up. :param str device: device to bring up :return: nothing """ self.run(f"{IP_BIN} link set {device} up") def device_down(self, device): """ Bring a device down. :param str device: device to bring down :return: nothing """ self.run(f"{IP_BIN} link set {device} down") def device_name(self, device, name): """ Set a device name. :param str device: device to set name for :param str name: name to set :return: nothing """ self.run(f"{IP_BIN} link set {device} name {name}") def device_show(self, device): """ Show information for a device. :param str device: device to get information for :return: device information :rtype: str """ return self.run(f"{IP_BIN} link show {device}") def get_mac(self, device): """ Retrieve MAC address for a given device. :param str device: device to get mac for :return: MAC address :rtype: str """ return self.run(f"cat /sys/class/net/{device}/address") def get_ifindex(self, device): """ Retrieve ifindex for a given device. :param str device: device to get ifindex for :return: ifindex :rtype: str """ return self.run(f"cat /sys/class/net/{device}/ifindex") def device_ns(self, device, namespace): """ Set netns for a device. :param str device: device to setns for :param str namespace: namespace to set device to :return: nothing """ self.run(f"{IP_BIN} link set {device} netns {namespace}") def device_flush(self, device): """ Flush device addresses. :param str device: device to flush :return: nothing """ self.run(f"{IP_BIN} -6 address flush dev {device}") def device_mac(self, device, mac): """ Set MAC address for a device. :param str device: device to set mac for :param str mac: mac to set :return: nothing """ self.run(f"{IP_BIN} link set dev {device} address {mac}") def delete_device(self, device): """ Delete device. :param str device: device to delete :return: nothing """ self.run(f"{IP_BIN} link delete {device}") def delete_tc(self, device): """ Remove traffic control settings for a device. :param str device: device to remove tc :return: nothing """ self.run(f"{TC_BIN} qdisc delete dev {device} root") def checksums_off(self, interface_name): """ Turns interface checksums off. :param str interface_name: interface to update :return: nothing """ self.run(f"{ETHTOOL_BIN} -K {interface_name} rx off tx off") def create_address(self, device, address, broadcast=None): """ Create address for a device. :param str device: device to add address to :param str address: address to add :param str broadcast: broadcast address to use, default is None :return: nothing """ if broadcast is not None: self.run( f"{IP_BIN} address add {address} broadcast {broadcast} dev {device}" ) else: self.run(f"{IP_BIN} address add {address} dev {device}") def delete_address(self, device, address): """ Delete an address from a device. :param str device: targeted device :param str address: address to remove :return: nothing """ self.run(f"{IP_BIN} address delete {address} dev {device}") def create_veth(self, name, peer): """ Create a veth pair. :param str name: veth name :param str peer: peer name :return: nothing """ self.run(f"{IP_BIN} link add name {name} type veth peer name {peer}") def create_gretap(self, device, address, local, ttl, key): """ Create a GRE tap on a device. :param str device: device to add tap to :param str address: address to add tap for :param str local: local address to tie to :param int ttl: time to live value :param int key: key for tap :return: nothing """ cmd = f"{IP_BIN} 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): """ Create a Linux bridge and bring it up. :param str name: bridge name :return: nothing """ self.run(f"{BRCTL_BIN} addbr {name}") self.run(f"{BRCTL_BIN} stp {name} off") self.run(f"{BRCTL_BIN} setfd {name} 0") self.device_up(name) # turn off multicast snooping so forwarding occurs w/o IGMP joins snoop_file = "multicast_snooping" snoop = f"/sys/devices/virtual/net/{name}/bridge/{snoop_file}" self.run(f"echo 0 > /tmp/{snoop_file}", shell=True) self.run(f"cp /tmp/{snoop_file} {snoop}") def delete_bridge(self, name): """ Bring down and delete a Linux bridge. :param str name: bridge name :return: nothing """ self.device_down(name) self.run(f"{BRCTL_BIN} delbr {name}") def create_interface(self, bridge_name, interface_name): """ Create an interface associated with a Linux bridge. :param str bridge_name: bridge name :param str interface_name: interface name :return: nothing """ self.run(f"{BRCTL_BIN} addif {bridge_name} {interface_name}") self.device_up(interface_name) def delete_interface(self, bridge_name, interface_name): """ Delete an interface associated with a Linux bridge. :param str bridge_name: bridge name :param str interface_name: interface name :return: nothing """ self.run(f"{BRCTL_BIN} delif {bridge_name} {interface_name}") def existing_bridges(self, _id): """ Checks if there are any existing Linux bridges for a node. :param _id: node id to check bridges for """ output = self.run(f"{BRCTL_BIN} show") lines = output.split("\n") for line in lines[1:]: columns = line.split() name = columns[0] 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): """ Disable mac learning for a Linux bridge. :param str name: bridge name :return: nothing """ self.run(f"{BRCTL_BIN} setageing {name} 0") class OvsNetClient(LinuxNetClient): """ Client for creating OVS bridges and ip interfaces for nodes. """ def create_bridge(self, name): """ Create a OVS bridge and bring it up. :param str name: bridge name :return: nothing """ self.run(f"{OVS_BIN} add-br {name}") self.run(f"{OVS_BIN} set bridge {name} stp_enable=false") self.run(f"{OVS_BIN} set bridge {name} other_config:stp-max-age=6") self.run(f"{OVS_BIN} set bridge {name} other_config:stp-forward-delay=4") self.device_up(name) def delete_bridge(self, name): """ Bring down and delete a OVS bridge. :param str name: bridge name :return: nothing """ self.device_down(name) self.run(f"{OVS_BIN} del-br {name}") def create_interface(self, bridge_name, interface_name): """ Create an interface associated with a network bridge. :param str bridge_name: bridge name :param str interface_name: interface name :return: nothing """ self.run(f"{OVS_BIN} add-port {bridge_name} {interface_name}") self.device_up(interface_name) def delete_interface(self, bridge_name, interface_name): """ Delete an interface associated with a OVS bridge. :param str bridge_name: bridge name :param str interface_name: interface name :return: nothing """ self.run(f"{OVS_BIN} del-port {bridge_name} {interface_name}") def existing_bridges(self, _id): """ Checks if there are any existing OVS bridges for a node. :param _id: node id to check bridges for """ output = self.run(f"{OVS_BIN} 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): """ Disable mac learning for a OVS bridge. :param str name: bridge name :return: nothing """ self.run(f"{OVS_BIN} set bridge {name} other_config:mac-aging-time=0")