Skip to content

Instantly share code, notes, and snippets.

@voluntas
Last active May 12, 2025 13:39
Show Gist options
  • Save voluntas/7b0b27af5617de4fee7127f068b4cca0 to your computer and use it in GitHub Desktop.
Save voluntas/7b0b27af5617de4fee7127f068b4cca0 to your computer and use it in GitHub Desktop.
import asyncio
import errno # Added for e.code == errno.ENOENT
import os
import sys
import time
import traceback
from pyroute2 import (
AsyncIPRoute, # IPRoute を AsyncIPRoute に変更
NetlinkError,
netns,
)
from pyroute2.netlink.rtnl import TC_H_ROOT
# --- 設定値 ---
PRIV_BR = "br-priv"
PUB_BR = "br-pub"
PRIV_NET = "10.0.1"
PUB_NET = "10.0.2"
SUBNET_MASK = "/24"
NUM_SERVERS = 3
PUB_NS = "ns-pub"
VETH_PUB_NS = "veth-ns-pub"
VETH_PUB_BR = "veth-br-pub"
# --- ユーティリティ関数 ---
def namespace_exists(ns_name):
"""名前空間が存在するかチェックする"""
try:
# /var/run/netns ディレクトリが存在しない場合、listnetns() はエラーを出す可能性がある
# pyroute2 の listnetns() は内部で os.listdir('/var/run/netns') を呼び出すため、
# FileNotFoundError をキャッチする。
existing_namespaces = netns.listnetns()
return ns_name in existing_namespaces
except FileNotFoundError:
# /var/run/netns が存在しない場合は、どの名前空間も存在しないとみなす
print(
f" Info: Namespace directory /var/run/netns not found. Assuming namespace {ns_name} does not exist.",
file=sys.stderr,
)
return False
except Exception as e:
# その他のエラーが発生した場合 (例: パーミッションエラー)
print(
f" Warning: Error checking namespace existence for {ns_name} via list_ns(): {e}",
file=sys.stderr,
)
# エラー時は存在しないと仮定し、作成を試みるようにする
return False
def create_namespace(ns_name):
"""名前空間を作成する"""
if not namespace_exists(ns_name):
try:
netns.create(ns_name) # netns.create を使用
print(f" Namespace {ns_name} created via netns.create().")
except NetlinkError as e:
if e.code == errno.EEXIST:
# namespace_exists() のチェックと netns.create() の呼び出しの間に作成された場合
print(
f" Namespace {ns_name} already exists (EEXIST from netns.create(), likely race)."
)
else:
print(
f" Error creating namespace {ns_name} with netns.create(): {e}",
file=sys.stderr,
)
raise
else:
print(f" Namespace {ns_name} already exists (pre-checked).")
def remove_namespace(ns_name):
"""名前空間を削除する"""
if not namespace_exists(ns_name):
print(f" Namespace {ns_name} does not exist, no need to remove.")
return False
try:
netns.remove(ns_name) # netns.remove を使用
print(f" Namespace {ns_name} successfully deleted via netns.remove().")
return True
except NetlinkError as e_pyroute_netns:
print(
f" netns.remove() for {ns_name} failed: {e_pyroute_netns}. Trying 'ip netns del'.",
file=sys.stderr,
)
return False
except Exception as e_generic: # 他の予期せぬエラー
print(
f" Error during removal of namespace {ns_name} with netns.remove(): {e_generic}",
file=sys.stderr,
)
async def create_network():
"""
ネットワーク構成を作成する
"""
print("Setting up 3-node server private and public network simulation...")
try:
async with AsyncIPRoute() as ipr: # async with に変更
# 1. ブリッジを作成
print(f"1. Creating and bringing up bridges {PRIV_BR} and {PUB_BR}...")
for br_name in [PRIV_BR, PUB_BR]:
# link_lookup を await で呼び出す
if not await ipr.link_lookup(ifname=br_name):
try:
# link を await で呼び出す
await ipr.link("add", ifname=br_name, kind="bridge")
print(f" Bridge {br_name} created.")
except Exception as e:
print(
f"[ERROR] Failed to create bridge {br_name}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
try:
# link_lookup を await で呼び出す
br_indices = await ipr.link_lookup(ifname=br_name)
if br_indices:
# link を await で呼び出す
await ipr.link("set", index=br_indices[0], state="up")
else:
raise Exception(
f"Bridge {br_name} not found after creation/check for bringing up"
)
except Exception as e:
print(
f"[ERROR] Failed to bring up bridge {br_name}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
print(" Bridges created and are up.")
# 2. サーバーノード用のネットワーク名前空間を作成
print(
f"2. Creating {NUM_SERVERS} server namespaces and connecting them to both bridges..."
)
for i in range(1, NUM_SERVERS + 1):
ns_name = f"ns{i}"
veth_ns_priv = f"veth-ns{i}-priv"
veth_br_priv = f"veth-br{i}-priv"
priv_ip_str = f"{PRIV_NET}.{i}"
priv_ip_full = f"{priv_ip_str}{SUBNET_MASK}"
veth_ns_pub = f"veth-ns{i}-pub"
veth_br_pub = f"veth-br{i}-pub"
pub_ip_str = f"{PUB_NET}.{i}"
pub_ip_full = f"{pub_ip_str}{SUBNET_MASK}"
print(f" - Creating namespace {ns_name}...")
try:
# create_namespace は同期のまま(必要なら後で修正)
create_namespace(ns_name)
except Exception as e:
print(
f"[ERROR] Failed to create namespace {ns_name}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
print(f" Waiting for namespace {ns_name} to be ready...")
# === プライベート通信用vethの設定 ===
print(f" Configuring private network for {ns_name}...")
# link_lookup を await で呼び出す
if not await ipr.link_lookup(ifname=veth_ns_priv) and not await ipr.link_lookup(
ifname=veth_br_priv
):
try:
# link を await で呼び出す
await ipr.link(
"add", ifname=veth_ns_priv, peer=veth_br_priv, kind="veth"
)
# link_lookup を await で呼び出す
veth_ns_priv_idx = (await ipr.link_lookup(ifname=veth_ns_priv))[0]
# link_lookup を await で呼び出す
veth_br_priv_idx = (await ipr.link_lookup(ifname=veth_br_priv))[0]
# link を await で呼び出す
await ipr.link("set", index=veth_ns_priv_idx, state="up")
# link を await で呼び出す
await ipr.link("set", index=veth_br_priv_idx, state="up")
except Exception as e:
print(
f"[ERROR] Failed to create veth pair {veth_ns_priv}<->{veth_br_priv}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
try:
print(f" Moving {veth_ns_priv} to namespace {ns_name}...")
# link_lookup を await で呼び出す
veth_ns_priv_indices_root = await ipr.link_lookup(ifname=veth_ns_priv)
if veth_ns_priv_indices_root:
# link を await で呼び出す
await ipr.link(
"set", index=veth_ns_priv_indices_root[0], net_ns_fd=ns_name
)
print(
f" Successfully moved {veth_ns_priv} to namespace {ns_name}"
)
else:
# AsyncIPRoute に変更, async with に変更
async with AsyncIPRoute(netns=ns_name) as ipr_check_ns:
# link_lookup を await で呼び出す
if not await ipr_check_ns.link_lookup(ifname=veth_ns_priv):
raise Exception(
f"{veth_ns_priv} not found in root or target namespace {ns_name}"
)
print(
f" {veth_ns_priv} already in namespace {ns_name}."
)
except Exception as e:
print(
f"[ERROR] Failed to move or verify {veth_ns_priv} in namespace {ns_name}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
try:
# link_lookup を await で呼び出す
veth_br_priv_indices = await ipr.link_lookup(ifname=veth_br_priv)
# link_lookup を await で呼び出す
priv_br_indices_for_master = await ipr.link_lookup(ifname=PRIV_BR)
if veth_br_priv_indices and priv_br_indices_for_master:
# link を await で呼び出す
await ipr.link(
"set",
index=veth_br_priv_indices[0],
master=priv_br_indices_for_master[0],
)
# link を await で呼び出す
await ipr.link("set", index=veth_br_priv_indices[0], state="up")
else:
missing = []
if not veth_br_priv_indices:
missing.append(veth_br_priv)
if not priv_br_indices_for_master:
missing.append(PRIV_BR)
raise Exception(
f"Required interface(s) not found for connecting {veth_br_priv} to {PRIV_BR}: {', '.join(missing)}"
)
except Exception as e:
print(
f"[ERROR] Failed to connect {veth_br_priv} to bridge {PRIV_BR}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
try:
print(
f" Configuring private IP for {veth_ns_priv} in {ns_name}..."
)
# AsyncIPRoute に変更, async with に変更
async with AsyncIPRoute(netns=ns_name) as ipr_ns:
# link_lookup を await で呼び出す
veth_indices_in_ns = await ipr_ns.link_lookup(ifname=veth_ns_priv)
if veth_indices_in_ns:
idx_veth_ns_priv = veth_indices_in_ns[0]
# addr を await で呼び出す
await ipr_ns.addr(
"add",
index=idx_veth_ns_priv,
address=priv_ip_str,
mask=int(SUBNET_MASK[1:]),
)
# link を await で呼び出す
await ipr_ns.link("set", index=idx_veth_ns_priv, state="up")
print(
f" Configured private {veth_ns_priv} in {ns_name} with IP {priv_ip_full}"
)
else:
print(
f"Warning: Interface {veth_ns_priv} not found in namespace {ns_name} for IP config."
)
except Exception as e:
print(
f"[ERROR] Failed to configure private IP for {veth_ns_priv} in namespace {ns_name}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
# === パブリック通信用vethの設定 === (Similar to private, with pub variables)
print(f" Configuring public network for {ns_name}...")
# link_lookup を await で呼び出す
if not await ipr.link_lookup(ifname=veth_ns_pub) and not await ipr.link_lookup(
ifname=veth_br_pub
):
try:
# link を await で呼び出す
await ipr.link(
"add", ifname=veth_ns_pub, peer=veth_br_pub, kind="veth"
)
# link_lookup を await で呼び出す
veth_ns_pub_idx = (await ipr.link_lookup(ifname=veth_ns_pub))[0]
# link_lookup を await で呼び出す
veth_br_pub_idx = (await ipr.link_lookup(ifname=veth_br_pub))[0]
# link を await で呼び出す
await ipr.link("set", index=veth_ns_pub_idx, state="up")
# link を await で呼び出す
await ipr.link("set", index=veth_br_pub_idx, state="up")
except Exception as e:
print(
f"[ERROR] Failed to create veth pair {veth_ns_pub}<->{veth_br_pub}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
try:
print(f" Moving {veth_ns_pub} to namespace {ns_name}...")
# link_lookup を await で呼び出す
veth_ns_pub_indices_root = await ipr.link_lookup(ifname=veth_ns_pub)
if veth_ns_pub_indices_root:
# link を await で呼び出す
await ipr.link(
"set", index=veth_ns_pub_indices_root[0], net_ns_fd=ns_name
)
print(
f" Successfully moved {veth_ns_pub} to namespace {ns_name}"
)
else:
# AsyncIPRoute に変更, async with に変更
async with AsyncIPRoute(netns=ns_name) as ipr_check_ns:
# link_lookup を await で呼び出す
if not await ipr_check_ns.link_lookup(ifname=veth_ns_pub):
raise Exception(
f"{veth_ns_pub} not found in root or target namespace {ns_name}"
)
print(f" {veth_ns_pub} already in namespace {ns_name}.")
except Exception as e:
print(
f"[ERROR] Failed to move or verify {veth_ns_pub} in namespace {ns_name}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
try:
# link_lookup を await で呼び出す
veth_br_pub_indices = await ipr.link_lookup(ifname=veth_br_pub)
# link_lookup を await で呼び出す
pub_br_indices_for_master = await ipr.link_lookup(ifname=PUB_BR)
if veth_br_pub_indices and pub_br_indices_for_master:
# link を await で呼び出す
await ipr.link(
"set",
index=veth_br_pub_indices[0],
master=pub_br_indices_for_master[0],
)
# link を await で呼び出す
await ipr.link("set", index=veth_br_pub_indices[0], state="up")
else:
missing = []
if not veth_br_pub_indices:
missing.append(veth_br_pub)
if not pub_br_indices_for_master:
missing.append(PUB_BR)
raise Exception(
f"Required interface(s) not found for connecting {veth_br_pub} to {PUB_BR}: {', '.join(missing)}"
)
except Exception as e:
print(
f"[ERROR] Failed to connect {veth_br_pub} to bridge {PUB_BR}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
try:
print(
f" Configuring public IP for {veth_ns_pub} in {ns_name}..."
)
# AsyncIPRoute に変更, async with に変更
async with AsyncIPRoute(netns=ns_name) as ipr_ns:
# link_lookup を await で呼び出す
veth_indices_in_ns_pub = await ipr_ns.link_lookup(ifname=veth_ns_pub)
if veth_indices_in_ns_pub:
idx_veth_ns_pub = veth_indices_in_ns_pub[0]
# addr を await で呼び出す
await ipr_ns.addr(
"add",
index=idx_veth_ns_pub,
address=pub_ip_str,
mask=int(SUBNET_MASK[1:]),
)
# link を await で呼び出す
await ipr_ns.link("set", index=idx_veth_ns_pub, state="up")
print(
f" Configured public {veth_ns_pub} in {ns_name} with IP {pub_ip_full}"
)
else:
print(
f"Warning: Interface {veth_ns_pub} not found in namespace {ns_name} for IP config."
)
except Exception as e:
print(
f"[ERROR] Failed to configure public IP for {veth_ns_pub} in namespace {ns_name}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
print(
f" Namespace {ns_name} configured. Private IP: {priv_ip_full}, Public IP: {pub_ip_full}."
)
# 3. パブリック用のネットワーク名前空間を作成 (ns-pub)
print(
"3. Creating public namespace (ns-pub) and connecting it to the public bridge..."
)
pub_ns_name = PUB_NS
pub_ns_ip_str = f"{PUB_NET}.100"
pub_ns_ip_full = f"{pub_ns_ip_str}{SUBNET_MASK}"
print(f" - Creating namespace {pub_ns_name}...")
# create_namespace は同期のまま
create_namespace(pub_ns_name)
print(f" Waiting for namespace {pub_ns_name} to be ready...")
print(f" Creating veth pair {VETH_PUB_NS} <--> {VETH_PUB_BR}...")
# link_lookup を await で呼び出す
if not await ipr.link_lookup(ifname=VETH_PUB_NS) and not await ipr.link_lookup(
ifname=VETH_PUB_BR
):
try:
# link を await で呼び出す
await ipr.link("add", ifname=VETH_PUB_NS, peer=VETH_PUB_BR, kind="veth")
# link_lookup を await で呼び出す
veth_pub_ns_idx = (await ipr.link_lookup(ifname=VETH_PUB_NS))[0]
# link_lookup を await で呼び出す
veth_pub_br_idx = (await ipr.link_lookup(ifname=VETH_PUB_BR))[0]
# link を await で呼び出す
await ipr.link("set", index=veth_pub_ns_idx, state="up")
# link を await で呼び出す
await ipr.link("set", index=veth_pub_br_idx, state="up")
except Exception as e:
print(
f"[ERROR] Failed to create veth pair {VETH_PUB_NS}<->{VETH_PUB_BR}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
try:
print(f" Moving {VETH_PUB_NS} to namespace {pub_ns_name}...")
# link_lookup を await で呼び出す
veth_pub_ns_indices_root = await ipr.link_lookup(ifname=VETH_PUB_NS)
if veth_pub_ns_indices_root:
# link を await で呼び出す
await ipr.link(
"set", index=veth_pub_ns_indices_root[0], net_ns_fd=pub_ns_name
)
print(
f" Successfully moved {VETH_PUB_NS} to namespace {pub_ns_name}"
)
else:
# AsyncIPRoute に変更, async with に変更
async with AsyncIPRoute(netns=pub_ns_name) as ipr_check_ns:
# link_lookup を await で呼び出す
if not await ipr_check_ns.link_lookup(ifname=VETH_PUB_NS):
raise Exception(
f"{VETH_PUB_NS} not found in root or target namespace {pub_ns_name}"
)
print(f" {VETH_PUB_NS} already in namespace {pub_ns_name}.")
except Exception as e:
print(
f"[ERROR] Failed to move or verify {VETH_PUB_NS} in namespace {pub_ns_name}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
try:
# link_lookup を await で呼び出す
veth_pub_br_indices = await ipr.link_lookup(ifname=VETH_PUB_BR)
# link_lookup を await で呼び出す
pub_br_indices_for_master_pub = await ipr.link_lookup(ifname=PUB_BR)
if veth_pub_br_indices and pub_br_indices_for_master_pub:
# link を await で呼び出す
await ipr.link(
"set",
index=veth_pub_br_indices[0],
master=pub_br_indices_for_master_pub[0],
)
# link を await で呼び出す
await ipr.link("set", index=veth_pub_br_indices[0], state="up")
else:
missing = []
if not veth_pub_br_indices:
missing.append(VETH_PUB_BR)
if not pub_br_indices_for_master_pub:
missing.append(PUB_BR)
raise Exception(
f"Required interface(s) not found for connecting {VETH_PUB_BR} to {PUB_BR}: {', '.join(missing)}"
)
except Exception as e:
print(
f"[ERROR] Failed to connect {VETH_PUB_BR} to bridge {PUB_BR}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
print(f" Configuring IP for {VETH_PUB_NS} in {pub_ns_name}...")
try:
# AsyncIPRoute に変更, async with に変更
async with AsyncIPRoute(netns=pub_ns_name) as ipr_ns_pub:
# link_lookup を await で呼び出す
veth_indices_in_ns_pub_final = await ipr_ns_pub.link_lookup(
ifname=VETH_PUB_NS
)
if veth_indices_in_ns_pub_final:
idx_veth_ns_pub_final = veth_indices_in_ns_pub_final[0]
# addr を await で呼び出す
await ipr_ns_pub.addr(
"add",
index=idx_veth_ns_pub_final,
address=pub_ns_ip_str,
mask=int(SUBNET_MASK[1:]),
)
# link を await で呼び出す
await ipr_ns_pub.link("set", index=idx_veth_ns_pub_final, state="up")
print(
f" Configured {VETH_PUB_NS} with IP {pub_ns_ip_full} in {pub_ns_name}"
)
else:
print(
f"Warning: Interface {VETH_PUB_NS} not found in namespace {pub_ns_name} for IP config."
)
except Exception as e:
print(
f"[ERROR] Failed to configure public IP for {VETH_PUB_NS} in namespace {pub_ns_name}: {e}",
file=sys.stderr,
)
traceback.print_exc()
raise
print(
f" Public namespace {pub_ns_name} configured with IP {pub_ns_ip_full}."
)
print("\nSetup complete.")
print(
f"Server namespaces: {', '.join([f'ns{i}' for i in range(1, NUM_SERVERS + 1)])}"
)
print(f"Public namespace : {PUB_NS}")
print(f"Private network : {PRIV_NET}.0/24 via bridge {PRIV_BR}")
print(f"Public network : {PUB_NET}.0/24 via bridge {PUB_BR}")
except NetlinkError as e:
print(f"\nError during network setup: {e}", file=sys.stderr)
print("Stack trace:", file=sys.stderr)
traceback.print_exc()
print("Attempting cleanup...", file=sys.stderr)
# cleanup_network も非同期にする必要あり
await cleanup_network()
sys.exit(1)
except Exception as e:
print(f"\nAn unexpected error occurred: {e}", file=sys.stderr)
print("Stack trace:", file=sys.stderr)
traceback.print_exc()
print("Attempting cleanup...", file=sys.stderr)
# cleanup_network も非同期にする必要あり
await cleanup_network()
sys.exit(1)
async def cleanup_network():
"""
作成したネットワーク構成を削除する (非同期版)
"""
print("\nCleaning up network simulation...")
try:
namespaces_to_delete = [f"ns{i}" for i in range(1, NUM_SERVERS + 1)] + [PUB_NS]
for ns_name in namespaces_to_delete:
print(f" - Deleting namespace {ns_name}...")
# remove_namespace は同期のまま
if remove_namespace(ns_name):
print(f" Namespace {ns_name} deleted.")
async with AsyncIPRoute() as ipr: # async with に変更
print(" - Deleting veth interfaces (peers in root namespace)...")
veth_peers_in_root_ns = []
for i in range(1, NUM_SERVERS + 1):
veth_peers_in_root_ns.append(f"veth-br{i}-priv")
veth_peers_in_root_ns.append(f"veth-br{i}-pub")
veth_peers_in_root_ns.append(VETH_PUB_BR)
for veth_ifname in veth_peers_in_root_ns:
try:
# link_lookup を await で呼び出す
indices = await ipr.link_lookup(ifname=veth_ifname)
if indices:
print(f" - Deleting veth interface {veth_ifname}...")
# link を await で呼び出す
await ipr.link("del", index=indices[0])
except NetlinkError as e:
if e.code == errno.ENOENT:
print(
f" Veth {veth_ifname} already deleted or not found (ENOENT)."
)
else:
print(
f" Warning: Could not delete veth {veth_ifname}: {e}",
file=sys.stderr,
)
except Exception as e:
print(
f" Warning: Could not delete veth {veth_ifname}: {e}",
file=sys.stderr,
)
print(
" - Searching for and deleting any other remaining veth interfaces in root ns..."
)
try:
# get_links を await で呼び出す
async for link in await ipr.get_links():
ifname = link.get_attr("IFLA_IFNAME")
kind = None
link_info = link.get_attr("IFLA_LINKINFO")
if link_info:
info_data = link_info.get_attr("IFLA_INFO_DATA")
if info_data:
kind = info_data.get_attr("IFLA_INFO_KIND")
if ifname and kind == "veth":
try:
print(
f" - Deleting remaining veth interface {ifname} (index {link['index']})..."
)
# link を await で呼び出す
await ipr.link("del", ifname=ifname)
except NetlinkError as e_del:
if e_del.code == errno.ENOENT:
print(
f" Veth {ifname} disappeared before deletion (ENOENT)."
)
else:
print(
f" Warning: Could not delete veth {ifname}: {e_del}",
file=sys.stderr,
)
except Exception as e_del_other:
print(
f" Warning: Could not delete veth {ifname}: {e_del_other}",
file=sys.stderr,
)
except Exception as e_get_links:
print(
f"[ERROR] Error while listing links for remaining veth cleanup: {e_get_links}",
file=sys.stderr,
)
bridges_to_delete = [PRIV_BR, PUB_BR]
for br_name in bridges_to_delete:
try:
# link_lookup を await で呼び出す
br_indices = await ipr.link_lookup(ifname=br_name)
if br_indices:
print(f" - Setting bridge {br_name} down...")
# link を await で呼び出す
await ipr.link("set", index=br_indices[0], state="down")
print(f" - Deleting bridge {br_name}...")
# link を await で呼び出す
await ipr.link("del", index=br_indices[0])
print(f" Bridge {br_name} deleted.")
except NetlinkError as e_br:
if e_br.code == errno.ENOENT:
print(
f" Bridge {br_name} not found (ENOENT) during cleanup steps."
)
else:
print(
f" Warning: Error during cleanup of bridge {br_name}: {e_br}",
file=sys.stderr,
)
except Exception as e_br_other:
print(
f" Warning: Error during cleanup of bridge {br_name}: {e_br_other}",
file=sys.stderr,
)
print("Cleanup complete.")
except NetlinkError as e:
print(f"\nError during cleanup: {e}", file=sys.stderr)
except Exception as e:
print(f"\nAn unexpected error occurred during cleanup: {e}", file=sys.stderr)
async def add_delay_to_interface(ns_name, if_name, delay_ms):
"""
指定された名前空間のインターフェースに netem 遅延を追加する (非同期版)
Equivalent to: ip netns exec <ns_name> tc qdisc add dev <if_name> root netem delay <delay_ms>ms
"""
print(f"\nAdding {delay_ms}ms delay to {if_name} in namespace {ns_name}...")
try:
# AsyncIPRoute に変更, async with に変更
async with AsyncIPRoute(netns=ns_name) as ipr_ns:
# link_lookup を await で呼び出す
indices = await ipr_ns.link_lookup(ifname=if_name)
if not indices:
print(f"[ERROR] Interface {if_name} not found in namespace {ns_name}", file=sys.stderr)
return
if_index = indices[0]
# 遅延をミリ秒からマイクロ秒に変換
delay_us = delay_ms * 1000
# tc qdisc add dev <if_index> handle ffff: root netem delay <delay_us>
# handle 0xffff0000 は root qdisc を意味します。
# pyroute2 v0.7.0 以降では、キーワード引数でオプションを渡すことが推奨されます。
# tc を await で呼び出す
await ipr_ns.tc(
command='add',
kind='netem',
index=if_index,
handle=TC_H_ROOT, # ROOT handle
# netem 固有のオプションはキーワード引数として渡す
delay=delay_us
# 他の netem オプション (例: jitter, loss) もここに追加可能
# jitter=5000, # 5ms jitter
# loss=10.0, # 10% packet loss
)
print(f" Successfully added {delay_ms}ms delay to {if_name} (index {if_index}) in {ns_name}.")
except NetlinkError as e:
# 例えば、qdisc がすでに存在する場合 (errno.EEXIST)
if e.code == errno.EEXIST:
print(f" Warning: qdisc already exists on {if_name} in {ns_name}. Trying 'replace'.", file=sys.stderr)
try:
# 既存の qdisc を置き換える試み
# AsyncIPRoute に変更, async with に変更
async with AsyncIPRoute(netns=ns_name) as ipr_replace:
# tc を await で呼び出す
await ipr_replace.tc(
command='replace', # 'add' の代わりに 'replace'
kind='netem',
index=if_index,
handle=TC_H_ROOT,
delay=delay_us
)
print(f" Successfully replaced existing qdisc on {if_name} in {ns_name} with {delay_ms}ms delay.")
except Exception as e_replace:
print(f"[ERROR] Failed to replace qdisc on {if_name} in {ns_name}: {e_replace}", file=sys.stderr)
traceback.print_exc()
else:
print(f"[ERROR] Failed to add/replace qdisc on {if_name} in {ns_name}: {e}", file=sys.stderr)
traceback.print_exc()
except Exception as e:
print(f"[ERROR] An unexpected error occurred while adding delay to {if_name} in {ns_name}: {e}", file=sys.stderr)
traceback.print_exc()
async def delete_delay_from_interface(ns_name, if_name):
"""
指定された名前空間のインターフェースから netem 遅延を削除する (非同期版)
Equivalent to: ip netns exec <ns_name> tc qdisc del dev <if_name> root netem
"""
print(f"\nRemoving delay from {if_name} in namespace {ns_name}...")
try:
# AsyncIPRoute に変更, async with に変更
async with AsyncIPRoute(netns=ns_name) as ipr_ns:
# link_lookup を await で呼び出す
indices = await ipr_ns.link_lookup(ifname=if_name)
if not indices:
print(f"[ERROR] Interface {if_name} not found in namespace {ns_name}", file=sys.stderr)
return
if_index = indices[0]
# 既存の qdisc を取得
# get_qdiscs を await で呼び出す
qdiscs = await ipr_ns.get_qdiscs(index=if_index)
netem_exists = False
for q in qdiscs:
# root ハンドル (0xffff0000) かつ kind が netem かチェック
if q.get('handle') == 0xffff0000 and q.get_attr('TCA_KIND') == 'netem':
netem_exists = True
break
if netem_exists:
# netem qdisc が存在する場合のみ削除を実行
print(f" Found existing netem qdisc on {if_name}. Attempting removal...")
# tc を await で呼び出す
await ipr_ns.tc(
command='del',
kind='netem',
index=if_index,
handle=0xffff0000
)
print(f" Successfully removed delay from {if_name} in {ns_name}.")
else:
# netem qdisc が存在しない場合は何もしない
print(f" No root netem qdisc found on {if_name} in {ns_name}. No removal needed.")
except NetlinkError as e:
# 削除中に予期せぬ Netlink エラーが発生した場合
print(f"[ERROR] Failed to delete delay from {if_name} in {ns_name}: {e}", file=sys.stderr)
# --- メイン処理 ---
# main ブロックを async 関数でラップし、asyncio.run で実行
async def main():
if os.geteuid() != 0:
print("This script requires root privileges. Run with sudo.", file=sys.stderr)
sys.exit(1)
if len(sys.argv) != 2 or sys.argv[1] not in ["create", "cleanup", "add_delay", "delete_delay"]:
print("Usage: sudo python3 main.py [create|cleanup|add_delay|delete_delay]", file=sys.stderr)
sys.exit(1)
action = sys.argv[1]
if action == "create":
await create_network()
elif action == "add_delay":
target_namespace = "ns2"
target_interface = "veth-ns2-pub"
delay_milliseconds = 100
await add_delay_to_interface(target_namespace, target_interface, delay_milliseconds)
elif action == "delete_delay":
target_namespace = "ns2"
target_interface = "veth-ns2-pub"
await delete_delay_from_interface(target_namespace, target_interface)
elif action == "cleanup":
await cleanup_network()
if action == "create":
print("\n--- Verification Commands ---")
print(" # List all network namespaces")
print(" sudo ip netns list")
print("\n # Show details of the private bridge")
print(f" sudo ip a show dev {PRIV_BR}")
print("\n # Show details of the public bridge")
print(f" sudo ip a show dev {PUB_BR}")
print(
"\n # Show IP addresses in server namespace ns1 (replace ns1 with ns2, ns3 etc. as needed)"
)
print(" sudo ip netns exec ns1 ip a")
print("\n # Show IP addresses in the public namespace")
print(f" sudo ip netns exec {PUB_NS} ip a")
print(
f"\n # Ping from ns1 to ns2 on the private network (assumes ns2 is {PRIV_NET}.2)"
)
print(f" sudo ip netns exec ns1 ping -c 3 {PRIV_NET}.2")
print(
f"\n # Ping from the public namespace to ns1 on the public network (assumes ns1 public IP is {PUB_NET}.1)"
)
print(f" sudo ip netns exec {PUB_NS} ping -c 3 {PUB_NET}.1")
if __name__ == "__main__":
asyncio.run(main()) # asyncio.run で main() を実行
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment