def create_virtual_server( ns_client, client_ctx_traf, client_ctx_comm, ns_server, server_ctx_traf, server_ctx_comm ) -> subprocess.Popen: """ Create a server process in separate network namespace, connect it by virtual links and start it. Returns the process of the created server. """ server_ctx_comm_ = ContextLocal(server_ctx_traf.iface + "_comm", inet=server_ctx_comm.inet, mask=server_ctx_comm.mask) client_ctx_comm_ = ContextLocal(client_ctx_traf.iface + "_comm", inet=client_ctx_comm.inet, mask=client_ctx_comm.mask) create_virtual_link(ns_client, client_ctx_traf, ns_server, server_ctx_traf) create_virtual_link(ns_client, client_ctx_comm_, ns_server, server_ctx_comm_) to_run = [ "python3", "-m", "xdp_test_harness.server", pickle.dumps(ContextServer( server_ctx_traf, server_ctx_comm, ), 0).decode() ] if hasattr(ns_server, "netns"): server_process = pyroute2.NSPopen(ns_server.netns, to_run) else: server_process = subprocess.Popen(to_run) return server_process
def clean_traffic(iface: str, netns: pyroute2.NetNS = None, restore_on_exit: bool = True): sysctl_state = [] MILLISECONDS_IN_HOUR = 1000 * 60 * 60 for (folder, setting, value) in [ ("conf", "autoconf", 0), ("conf", "accept_ra", 0), ("conf", "accept_dad", 0), ("conf", "mldv1_unsolicited_report_interval", MILLISECONDS_IN_HOUR), ("conf", "mldv2_unsolicited_report_interval", MILLISECONDS_IN_HOUR), ("neigh", "mcast_solicit", 0), ]: if netns: # No need to remember previous setting of network namespace, # since it is going to be destroyed anyway. pyroute2.NSPopen(netns.netns, [ "sysctl", "-w", "net.ipv6." + folder + "." + iface + "." + setting + "=" + str(value) ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).wait() else: try: previous = subprocess.check_output([ "sysctl", "net.ipv6." + folder + "." + iface + "." + setting ]) sysctl_state.append(previous) except: pass subprocess.run([ "sysctl", "-w", "net.ipv6." + folder + "." + iface + "." + setting + "=" + str(value) ], capture_output=True) atexit.register(restore_traffic, sysctl_state)
def plug_vip(self, vip, subnet_cidr, gateway, mac_address, mtu=None, vrrp_ip=None, host_routes=None): # Validate vip and subnet_cidr, calculate broadcast address and netmask try: render_host_routes = [] ip = ipaddress.ip_address( vip if isinstance(vip, six.text_type) else six.u(vip)) network = ipaddress.ip_network( subnet_cidr if isinstance(subnet_cidr, six.text_type) else six.u(subnet_cidr)) vip = ip.exploded broadcast = network.broadcast_address.exploded netmask = (network.prefixlen if ip.version == 6 else network.netmask.exploded) vrrp_version = None if vrrp_ip: vrrp_ip_obj = ipaddress.ip_address( vrrp_ip if isinstance(vrrp_ip, six.text_type) else six.u(vrrp_ip) ) vrrp_version = vrrp_ip_obj.version if host_routes: for hr in host_routes: network = ipaddress.ip_network( hr['destination'] if isinstance( hr['destination'], six.text_type) else six.u(hr['destination'])) render_host_routes.append({'network': network, 'gw': hr['nexthop']}) except ValueError: return webob.Response(json=dict(message="Invalid VIP"), status=400) # Check if the interface is already in the network namespace # Do not attempt to re-plug the VIP if it is already in the # network namespace if self._netns_interface_exists(mac_address): return webob.Response( json=dict(message="Interface already exists"), status=409) # Check that the interface has been fully plugged self._interface_by_mac(mac_address) # Always put the VIP interface as eth1 primary_interface = consts.NETNS_PRIMARY_INTERFACE secondary_interface = "{interface}:0".format( interface=primary_interface) interface_file_path = self._osutils.get_network_interface_file( primary_interface) self._osutils.create_netns_dir() self._osutils.write_interfaces_file() self._osutils.write_vip_interface_file( interface_file_path=interface_file_path, primary_interface=primary_interface, vip=vip, ip=ip, broadcast=broadcast, netmask=netmask, gateway=gateway, mtu=mtu, vrrp_ip=vrrp_ip, vrrp_version=vrrp_version, render_host_routes=render_host_routes) # Update the list of interfaces to add to the namespace # This is used in the amphora reboot case to re-establish the namespace self._update_plugged_interfaces_file(primary_interface, mac_address) # Create the namespace netns = pyroute2.NetNS(consts.AMPHORA_NAMESPACE, flags=os.O_CREAT) netns.close() # Load sysctl in new namespace sysctl = pyroute2.NSPopen(consts.AMPHORA_NAMESPACE, [consts.SYSCTL_CMD, '--system'], stdout=subprocess.PIPE) sysctl.communicate() sysctl.wait() sysctl.release() cmd_list = [['modprobe', 'ip_vs'], [consts.SYSCTL_CMD, '-w', 'net.ipv4.vs.conntrack=1']] if ip.version == 4: # For lvs function, enable ip_vs kernel module, enable ip_forward # conntrack in amphora network namespace. cmd_list.append([consts.SYSCTL_CMD, '-w', 'net.ipv4.ip_forward=1']) elif ip.version == 6: cmd_list.append([consts.SYSCTL_CMD, '-w', 'net.ipv6.conf.all.forwarding=1']) for cmd in cmd_list: ns_exec = pyroute2.NSPopen(consts.AMPHORA_NAMESPACE, cmd, stdout=subprocess.PIPE) ns_exec.wait() ns_exec.release() with pyroute2.IPRoute() as ipr: # Move the interfaces into the namespace idx = ipr.link_lookup(address=mac_address)[0] ipr.link('set', index=idx, net_ns_fd=consts.AMPHORA_NAMESPACE, IFLA_IFNAME=primary_interface) # In an ha amphora, keepalived should bring the VIP interface up if (CONF.controller_worker.loadbalancer_topology == consts.TOPOLOGY_ACTIVE_STANDBY): secondary_interface = None # bring interfaces up self._osutils.bring_interfaces_up( ip, primary_interface, secondary_interface) return webob.Response(json=dict( message="OK", details="VIP {vip} plugged on interface {interface}".format( vip=vip, interface=primary_interface)), status=202)
def plug_vip(self, vip, subnet_cidr, gateway, mac_address, mtu=None, vrrp_ip=None, host_routes=None): # Validate vip and subnet_cidr, calculate broadcast address and netmask try: render_host_routes = [] ip = ipaddress.ip_address( vip if isinstance(vip, six.text_type) else six.u(vip)) network = ipaddress.ip_network(subnet_cidr if isinstance( subnet_cidr, six.text_type) else six.u(subnet_cidr)) vip = ip.exploded broadcast = network.broadcast_address.exploded netmask = (network.prefixlen if ip.version is 6 else network.netmask.exploded) vrrp_version = None if vrrp_ip: vrrp_ip_obj = ipaddress.ip_address(vrrp_ip if isinstance( vrrp_ip, six.text_type) else six.u(vrrp_ip)) vrrp_version = vrrp_ip_obj.version if host_routes: for hr in host_routes: network = ipaddress.ip_network( hr['destination'] if isinstance( hr['destination'], six.text_type ) else six.u(hr['destination'])) render_host_routes.append({ 'network': network, 'gw': hr['nexthop'] }) except ValueError: return webob.Response(json=dict(message="Invalid VIP"), status=400) # Check if the interface is already in the network namespace # Do not attempt to re-plug the VIP if it is already in the # network namespace if self._netns_interface_exists(mac_address): return webob.Response( json=dict(message="Interface already exists"), status=409) # This is the interface prior to moving into the netns default_netns_interface = self._interface_by_mac(mac_address) # Always put the VIP interface as eth1 primary_interface = consts.NETNS_PRIMARY_INTERFACE secondary_interface = "{interface}:0".format( interface=primary_interface) interface_file_path = self._osutils.get_network_interface_file( primary_interface) self._osutils.create_netns_dir() self._osutils.write_interfaces_file() self._osutils.write_vip_interface_file( interface_file_path=interface_file_path, primary_interface=primary_interface, vip=vip, ip=ip, broadcast=broadcast, netmask=netmask, gateway=gateway, mtu=mtu, vrrp_ip=vrrp_ip, vrrp_version=vrrp_version, render_host_routes=render_host_routes) # Update the list of interfaces to add to the namespace # This is used in the amphora reboot case to re-establish the namespace self._update_plugged_interfaces_file(primary_interface, mac_address) # Create the namespace netns = pyroute2.NetNS(consts.AMPHORA_NAMESPACE, flags=os.O_CREAT) netns.close() # Load sysctl in new namespace sysctl = pyroute2.NSPopen(consts.AMPHORA_NAMESPACE, [consts.SYSCTL_CMD, '--system'], stdout=subprocess.PIPE) sysctl.communicate() sysctl.wait() sysctl.release() with pyroute2.IPRoute() as ipr: # Move the interfaces into the namespace idx = ipr.link_lookup(ifname=default_netns_interface)[0] ipr.link('set', index=idx, net_ns_fd=consts.AMPHORA_NAMESPACE, IFLA_IFNAME=primary_interface) # bring interfaces up self._osutils.bring_interfaces_up(ip, primary_interface, secondary_interface) return webob.Response(json=dict( message="OK", details="VIP {vip} plugged on interface {interface}".format( vip=vip, interface=primary_interface)), status=202)