import subprocess import ipaddress import logging from uncloud import UncloudException log = logging.getLogger(__name__) class VXLANBridge(object): cmd_create_vxlan = "{sudo}ip -6 link add {vxlandev} type vxlan id {vni_dec} dstport 4789 group {multicast_address} dev {uplinkdev} ttl 5" cmd_up_dev = "{sudo}ip link set {dev} up" cmd_create_bridge="{sudo}ip link add {bridgedev} type bridge" cmd_add_to_bridge="{sudo}ip link set {vxlandev} master {bridgedev} up" cmd_add_addr="{sudo}ip addr add {ip} dev {bridgedev}" cmd_add_route_dev="{sudo}ip route add {route} dev {bridgedev}" # VXLAN ids are at maximum 24 bit - use a /104 multicast_network = ipaddress.IPv6Network("ff05::/104") max_vni = (2**24)-1 def __init__(self, vni, uplinkdev, route=None, use_sudo=False): self.config = {} if vni > self.max_vni: raise UncloudException("VNI must be in the range of 0 .. {}".format(self.max_vni)) if use_sudo: self.config['sudo'] = 'sudo ' else: self.config['sudo'] = '' self.config['vni_dec'] = vni self.config['vni_hex'] = "{:x}".format(vni) self.config['multicast_address'] = self.multicast_network[vni] self.config['route_network'] = ipaddress.IPv6Network(route) self.config['route'] = route self.config['uplinkdev'] = uplinkdev self.config['vxlandev'] = "vx{}".format(self.config['vni_hex']) self.config['bridgedev'] = "br{}".format(self.config['vni_hex']) def setup_networking(self): pass def _setup_vxlan(self): self._execute_cmd(self.cmd_create_vxlan) self._execute_cmd(self.cmd_up_dev, dev=self.config['vxlandev']) def _setup_bridge(self): self._execute_cmd(self.cmd_create_bridge) self._execute_cmd(self.cmd_up_dev, dev=self.config['bridgedev']) def _route_network(self): self._execute_cmd(self.cmd_add_route_dev) def _add_vxlan_to_bridge(self): self._execute_cmd(self.cmd_add_to_bridge) def _execute_cmd(self, cmd_string, **kwargs): cmd = cmd_string.format(**self.config, **kwargs) log.info("Executing: {}".format(cmd)) subprocess.run(cmd.split()) class ManagementBridge(VXLANBridge): pass class DNSRA(object): # VXLAN ids are at maximum 24 bit max_vni = (2**24)-1 # Command to start dnsmasq cmd_start_dnsmasq="{sudo}dnsmasq --interface={bridgedev} --bind-interfaces --dhcp-range={route},ra-only,infinite --enable-ra" def __init__(self, vni, route=None, use_sudo=False): self.config = {} if vni > self.max_vni: raise UncloudException("VNI must be in the range of 0 .. {}".format(self.max_vni)) if use_sudo: self.config['sudo'] = 'sudo ' else: self.config['sudo'] = '' #TODO: remove if not needed #self.config['vni_dec'] = vni self.config['vni_hex'] = "{:x}".format(vni) # dnsmasq only wants the network without the prefix, therefore, cut it off self.config['route'] = ipaddress.IPv6Network(route).network_address self.config['bridgedev'] = "br{}".format(self.config['vni_hex']) def _setup_dnsmasq(self): self._execute_cmd(self.cmd_start_dnsmasq) def _execute_cmd(self, cmd_string, **kwargs): cmd = cmd_string.format(**self.config, **kwargs) log.info("Executing: {}".format(cmd)) print("Executing: {}".format(cmd)) subprocess.run(cmd.split()) class Firewall(object): pass