def test_setup_ns(setup_networking): config.get_config(config_dict=yaml.safe_load("""namespaces: - name: test-ns-5 interfaces: - name: eth0 mac: "fa:16:3e:31:c8:d8" - name: eth1 mac: "fa:18:3e:31:c8:44" """)) setup_namespaces()
def delete_namespace(namespace): namespace = util.process_namespace(namespace) if namespace not in list_namespaces().get_json(): raise exceptions.ServerError(f"Invalid namespace {namespace}") interfaces: List = list_interfaces(namespace).get_json() untagged: List = [ interface for interface in interfaces if "." not in interface and interface != "lo" ] if untagged: raise exceptions.ServerError( "Unable to delete namespace with untagged interfaces %s in it." " Move them to another interface first." % ", ".join(untagged) ) if app.simulate: cfg = get_config() if namespace not in cfg.namespaces: return abort(404) del cfg.namespaces[namespace] return jsonify({}) util.delete_namespace(namespace) return jsonify({})
def add_namespace(namespace): namespace = util.process_namespace(namespace) if app.simulate: cfg = get_config() if namespace in cfg.namespaces: return abort(409) cfg.namespaces[namespace] = Namespace(namespace, interfaces=[{ "ifindex": 1, "ifname": "lo", "flags": ["LOOPBACK"], "mtu": 65536, "qdisc": "noop", "operstate": "DOWN", "linkmode": "DEFAULT", "group": "default", "txqlen": 1000, "link_type": "loopback", "address": "00:00:00:00:00:00", "broadcast": "00:00:00:00:00:00" }]) return jsonify({}) create_namespace(namespace) return jsonify({})
def move_interface(namespace, interface): namespace = util.process_namespace(namespace) parts = interface.split(".") if len(parts) not in range(1, 4): raise exceptions.ServerError(f"Only untagged, single and double tagged interfaces are supported") if app.simulate: cfg = get_config() if namespace not in cfg.namespaces: abort(404) all_interfaces = [intf.name for intf in cfg.namespaces[namespace].interfaces] else: all_interfaces = util.list_interfaces(namespace) if interface not in all_interfaces: raise exceptions.ServerError(f"Interface {interface} does not exist in {namespace}") new_namespace_key: str = "destination_namespace" if new_namespace_key not in request.json: raise exceptions.ServerError(f"Invalid request: request should contain \"destination_namespace\"") new_namespace = request.json[new_namespace_key] if app.simulate: old_ns: Namespace = cfg.namespaces[namespace] new_ns: Namespace = cfg.namespaces[new_namespace] iface: Interface = old_ns.get_interface(interface) old_ns.interfaces.remove(iface) new_ns.interfaces.append(iface) else: util.move_interface(new_namespace, interface, interface, old_namespace=namespace) return jsonify({})
def list_interfaces(namespace): namespace = util.process_namespace(namespace, allow_none=True) if app.simulate: cfg = get_config() if namespace not in cfg.namespaces: return abort(404) return jsonify([intf.name for intf in cfg.namespaces[namespace].interfaces]) return jsonify(util.list_interfaces(namespace))
def list_interfaces(namespace): if namespace is None or len(namespace) == 0: raise exceptions.ServerError(f"Invalid namespace {namespace}") if app.simulate: cfg = get_config() if namespace not in cfg.namespaces: return abort(404) return jsonify( [intf.name for intf in cfg.namespaces[namespace].interfaces]) return jsonify(util.list_interfaces(namespace))
def get_routing_table_from_ns(namespace): if app.simulate: cfg = get_config() if namespace not in cfg.namespaces: abort(404) ns = cfg.namespaces[namespace] return jsonify([route.to_json() for route in ns.routes]) else: try: routes = util.list_routes(namespace) return jsonify(routes) except subprocess.CalledProcessError as e: LOGGER.exception("status: %s, out: %s, err: %s", e.returncode, e.stdout, e.stderr) return jsonify({})
def test_load_config(tmp_path): cfg = """ namespaces: - name: LOC1 interfaces: - name: eth0 mac: "fa:16:3e:31:c8:d8" - name: LOC2 interfaces: - name: eth0 mac: "fa:16:3e:2c:7d:c1" - name: LOC3 interfaces: - name: eth0 mac: "fa:16:3e:81:7d:13" - name: LOC4 interfaces: - name: eth0 mac: "fa:16:3e:33:c6:e3" - name: LOC5 interfaces: - name: eth0 mac: "fa:16:3e:ff:92:8a" - name: LOC6 interfaces: - name: eth0 mac: "fa:16:3e:15:08:dc" - name: LOC7 interfaces: - name: eth0 mac: "fa:16:3e:71:04:b4" - name: LOC8 interfaces: - name: eth0 mac: "fa:16:3e:ed:14:0e" connection_config: timeout_dns_lookup_in_ms: 1000 duration_bandwidth_test_in_sec: 5 hostname_for_dns_lookup: "inmanta.com" iperf3_server: "10.0.0.36" """ p = tmp_path / "config.yaml" p.write_text(cfg) cfg = config.get_config(str(p)) assert len(cfg.namespaces) == 8
def set_interface_state(namespace, interface): namespace = util.process_namespace(namespace) if app.simulate: cfg = get_config() if namespace not in cfg.namespaces: abort(404) ns = cfg.namespaces[namespace] intf = ns.get_interface(interface) if intf is None: abort(404) intf.set_state(request.json) return jsonify({"interface": intf.get_state()}) else: util.set_interface_state(namespace, interface, request.json) return jsonify(util.get_interface_state(namespace, interface))
def create_sub_interface(namespace, interface): if namespace is None or len(namespace) == 0: raise exceptions.ServerError(f"Invalid namespace {namespace}") parts = interface.split(".") if len(parts) not in [2, 3]: raise exceptions.ServerError( f"Only single and double tagged interfaces are supported") if app.simulate: cfg = get_config() if namespace not in cfg.namespaces: abort(404) all_interfaces = [ intf.name for intf in cfg.namespaces[namespace].interfaces ] else: all_interfaces = util.list_interfaces(namespace) if interface in all_interfaces: raise exceptions.ServerError( f"Interface {interface} already exists in {namespace}") base_interface = parts.pop(0) if base_interface not in all_interfaces: raise exceptions.ServerError( f"Base interface {base_interface} does not exist") outer = int(parts.pop(0)) inner = 0 if len(parts): inner = int(parts[0]) if app.simulate: ns = cfg.namespaces[namespace] base_intf = ns.get_interface(base_interface) intf = config.Interface(name=interface, mac=base_intf.mac) ns.interfaces.append(intf) return jsonify({"interface": intf.get_state()}) else: util.create_sub_interface(namespace, base_interface, outer, inner) return jsonify(util.get_interface_state(namespace, interface))
def setup_namespaces(): """ Make sure all the namespaces from the config file are created and the correct interfaces are defined """ mac_lookup = {x["mac"]: x for x in util.list_all_interfaces()} cfg = get_config() for ns in cfg.namespaces.values(): for intf in ns.interfaces: if intf.mac in mac_lookup: old_namespace: Optional[str] = mac_lookup[intf.mac]["namespace"] new_name: str = mac_lookup[intf.mac]["name"] util.ensure_namespace(ns.name) util.move_interface(ns.name, new_name, intf.name, old_namespace=old_namespace) LOGGER.debug( "Moved interface %s with mac %s to namespace %s and name %s", new_name if old_namespace is None else "%s from %s" % (new_name, old_namespace), intf.mac, ns.name, intf.name, )
def set_interface_state(namespace, interface): if namespace is None or len(namespace) == 0: raise exceptions.ServerError(f"Invalid namespace {namespace}") if app.simulate: cfg = get_config() if namespace not in cfg.namespaces: abort(404) ns = cfg.namespaces[namespace] intf = ns.get_interface(interface) if intf is None: abort(404) intf.set_state(request.json) return jsonify({"interface": intf.get_state()}) else: util.set_interface_state(namespace, interface, request.json) return jsonify(util.get_interface_state(namespace, interface))
def get_interface_state(namespace, interface): namespace = util.process_namespace(namespace, allow_none=True) if app.simulate: cfg = get_config() if namespace not in cfg.namespaces: abort(404) ns = cfg.namespaces[namespace] intf = ns.get_interface(interface) if intf is None: abort(404) return jsonify({"interface": intf.get_state()}) else: try: return jsonify(util.get_interface_state(namespace, interface)) except Exception: LOGGER.exception("Failed to get the interface state") abort(404)
def get_interface_state(namespace, interface): if namespace is None or len(namespace) == 0: raise exceptions.ServerError(f"Invalid namespace {namespace}") if app.simulate: cfg = get_config() if namespace not in cfg.namespaces: abort(404) ns = cfg.namespaces[namespace] intf = ns.get_interface(interface) if intf is None: abort(404) return jsonify({"interface": intf.get_state()}) else: try: return jsonify(util.get_interface_state(namespace, interface)) except Exception: LOGGER.exception("Failed to get the interface state") abort(404)
def add_route_from_ns(namespace): namespace = util.process_namespace(namespace, allow_none=True) subnet = request.get_json(force=True).get("subnet") gateway = request.get_json(force=True).get("gateway", None) interface = request.get_json(force=True).get("interface", None) if gateway is None and interface is None: raise exceptions.ServerError("gateway or interface should be set.") if app.simulate: cfg = get_config() if namespace not in cfg.namespaces: abort(404) ns = cfg.namespaces[namespace] route = Route(subnet, gateway, interface) if route in ns.routes: abort(409) else: ns.routes.append(route) return jsonify([route.to_json() for route in ns.routes]) else: try: util.run_in_ns( namespace, [ "ip", "route", "add", subnet, *(["via", gateway] if gateway is not None else []), *(["dev", interface] if interface is not None else []), ], ) routes = util.list_routes(namespace) return jsonify(routes) except subprocess.CalledProcessError as e: LOGGER.exception("status: %s, out: %s, err: %s", e.returncode, e.stdout, e.stderr) return jsonify({})
def add_namespace(namespace): if namespace is None or len(namespace) == 0: raise exceptions.ServerError(f"Invalid namespace {namespace}") if app.simulate: cfg = get_config() if namespace in cfg.namespaces: return abort(409) cfg.namespaces[namespace] = Namespace(namespace, interfaces=[{ "ifindex": 1, "ifname": "lo", "flags": ["LOOPBACK"], "mtu": 65536, "qdisc": "noop", "operstate": "DOWN", "linkmode": "DEFAULT", "group": "default", "txqlen": 1000, "link_type": "loopback", "address": "00:00:00:00:00:00", "broadcast": "00:00:00:00:00:00" }]) return jsonify({}) create_namespace(namespace) return jsonify({})
def delete_sub_interface(namespace, interface): namespace = util.process_namespace(namespace) parts = interface.split(".") if len(parts) not in [2, 3]: raise exceptions.ServerError(f"Only single and double tagged interfaces are supported") if app.simulate: cfg = get_config() if namespace not in cfg.namespaces: abort(404) all_interfaces = [intf.name for intf in cfg.namespaces[namespace].interfaces] else: all_interfaces = util.list_interfaces(namespace) if interface not in all_interfaces: raise exceptions.ServerError(f"Interface {interface} dot exist in {namespace}") base_interface = parts.pop(0) if base_interface not in all_interfaces: raise exceptions.ServerError(f"Base interface {base_interface} does not exist") outer = int(parts.pop(0)) inner = 0 if len(parts): inner = int(parts[0]) if app.simulate: ns = cfg.namespaces[namespace] intf = ns.get_interface(interface) ns.interfaces.remove(intf) else: util.delete_sub_interface(namespace, base_interface, outer, inner) return jsonify({})
def list_namespaces(): if app.simulate: cfg = get_config() return jsonify([ns for ns in cfg.namespaces.keys()]) return jsonify(util.list_net_ns())
def main(config, simulate): cfg = get_config(config) if not simulate: setup_namespaces() app.simulate = simulate app.run(host=cfg.host, port=cfg.port)