与 l3 agent 有关的 namespace 有三种:qrouter-、sant-、fip-
neutron/agent/l3/namespace.py
namespace 的基类
def __init__(self, name, agent_conf, driver, use_ipv6):
self.name = name
self.ip_wrapper_root = ip_lib.IPWrapper()
self.agent_conf = agent_conf
self.driver = driver
self.use_ipv6 = use_ipv6
- 调用
IPWrapper.ensure_namespace
创建 namespace - 执行
sysctl -w net.ipv4.ip_forward=1
- 若使用 ipv6,则执行
sysctl -w net.ipv6.conf.all.forwarding=1
调用 IPWrapper
删除掉 namespace
调用 IPWrapper
检查该 namespace 是否存在
qrouter- 类型的 namespace
def __init__(self, router_id, agent_conf, driver, use_ipv6):
self.router_id = router_id
name = self._get_ns_name(router_id)
super(RouterNamespace, self).__init__(
name, agent_conf, driver, use_ipv6)
@classmethod
def _get_ns_name(cls, router_id):
return build_ns_name(NS_PREFIX, router_id)
调用 buil_ns_name
创建一个 namespace name
- 调用
IPWrapper.get_devices
获取当前 namespace 下的所有网络设备(lo 除外) - 删除 namespace 下的网络设备:
- 调用 interface driver 删除
qr-
设备 - 调用
IPWrapper.del_veth
删除rfp-
veth 设备 - 调用 interface dirver 删除
qg-
设备 - 调用
Namespace.delete
删除该 namespace
def __init__(self, router_id, agent_conf, driver, use_ipv6):
self.router_id = router_id
name = self.get_snat_ns_name(router_id)
super(SnatNamespace, self).__init__(
name, agent_conf, driver, use_ipv6)
@classmethod
def get_snat_ns_name(cls, router_id):
return namespaces.build_ns_name(SNAT_NS_PREFIX, router_id)
获取 namespace 的名称
- 调用
Namespace.create
完成 namespace 的创建 - 在该 namespace 下执行如下命令:
sysctl -w net.ipv4.ip_nonlocal_bind=0
/proc/sys/net/ipv4/ip_nonlocal_bind
如果你希望你的应用程序能够绑定到不属于本地网卡的地址上时,设置这个选项。如果你的机器没有专线连接(甚至是动态连接)时非常有用,即使你的连接断开,你的服务也可以启动并绑定在一个指定的地址上。
- 调用
IPWrapper.get_devices
获取当前 namespace 下的所有网络设备(lo 除外) - 删除 namespace 下的网络设备:
- 调用 interface driver 删除
sg-
设备 - 调用 interface dirver 删除
qg-
设备 - 调用
Namespace.delete
删除该 namespace
def __init__(self, ext_net_id, agent_conf, driver, use_ipv6):
name = self._get_ns_name(ext_net_id)
super(FipNamespace, self).__init__(
name, agent_conf, driver, use_ipv6)
self._ext_net_id = ext_net_id
self.agent_conf = agent_conf
self.driver = driver
self.use_ipv6 = use_ipv6
self.agent_gateway_port = None
self._subscribers = set()
path = os.path.join(agent_conf.state_path, 'fip-priorities')
self._rule_priorities = frpa.FipRulePriorityAllocator(path,
FIP_PR_START,
FIP_PR_END)
self._iptables_manager = iptables_manager.IptablesManager(
namespace=self.get_name(),
use_ipv6=self.use_ipv6)
path = os.path.join(agent_conf.state_path, 'fip-linklocal-networks')
self.local_subnets = lla.LinkLocalAllocator(
path, constants.DVR_FIP_LL_CIDR)
self.destroyed = False
- 调用
_get_ns_name
获取 namespace 的名称 ext_net_id
表示 external network 的 id- 获取 fip-priorities 的文件地址(/opt/stack/data/neutron/fip-priorities)
- 构造
FipRulePriorityAllocator
的实例用于记录 floating ip 的分配 - 构造该 namesapce 的
IptablesManager
实例 - 确定
fip-linklocal-networks
路径 (/opt/stack/data/neutron/fip-linklocal-networks) - 实例化
LinkLocalAllocator
用于为 fpr- 分配 ip 地址
@classmethod
def _get_ns_name(cls, ext_net_id):
return namespaces.build_ns_name(FIP_NS_PREFIX, ext_net_id)
获取 namespace 的全名
获取自身 namespace 的名称
获取 fg- 网络设备的名称(floating ip gateway)
获取 fpr- 网络设备的名称
获取 rfp- 网络设备的名称
def has_subscribers(self):
return len(self._subscribers) != 0
def subscribe(self, external_net_id):
is_first = not self.has_subscribers()
self._subscribers.add(external_net_id)
return is_first
def unsubscribe(self, external_net_id):
self._subscribers.discard(external_net_id)
return not self.has_subscribers()
def allocate_rule_priority(self, floating_ip):
return self._rule_priorities.allocate(floating_ip)
为 floating ip 分配一个优先级(这个优先级是指 ip rule 的优先级)
def deallocate_rule_priority(self, floating_ip):
self._rule_priorities.release(floating_ip)
回收分配出去的 ip rule 优先级
@contextlib.contextmanager
def _fip_port_lock(self, interface_name):
# Use a namespace and port-specific lock semaphore to allow for
# concurrency
lock_name = 'port-lock-' + self.name + '-' + interface_name
with lockutils.lock(lock_name, common_utils.SYNCHRONIZED_PREFIX):
try:
yield
except Exception:
with excutils.save_and_reraise_exception():
LOG.error(_LE('DVR: FIP namespace config failure '
'for interface %s'), interface_name)
产生一个锁
- 调用
get_ext_device_name
获取 agent gateway port 的名称 - 调用
_fip_port_lock
生成锁 - 调用
subscribe
判断是否应该创建 agent gateway port - 若需要创建,则调用
_create_gateway_port
创建 agent gateway port - 若不需要创建,则调用
_update_gateway_port
更新 agent gateway port
- 调用
get_name
获取当前 namespace 的名称 - 调用 interface driver plug 创建网络设备 fg-
- 调用
IPWrapper.get_devices
获取当前 namespace 下的所有设备 - 若该 namespace 下存在不同于该 agent gateway port 的 fg- 设备,则调用 interface driver unplug 删除这些 fg- 设备(这表明:在每个 fip- namesapce 中,只能存在一个 fg- 设备用来当做 agent gateway)
- 调用 interface driver init_l3 方法设备刚才创建的 agent gateway port 的 ip 地址
- 执行
sysctl -w net.ipv4.conf.fg-xxxxx.proxy_arp=1
让该 fg- 设备可以执行 arp proxy 功能
- 调用
_check_for_gateway_ip_change
判断 agent gateway port 的 ip 是否发生了变化 - 若发生了变化,则调用
_update_gateway_route
更新关于该 agent gateway 的 route
根据更新数据,判断 agent gateway port 的 ip 是否发生了变化
- 调用
get_name
获取该 namespace 名称 - 调用
IPDevice
描述该 agent gateway port - 调用
ip_lib.send_ip_addr_adv_notif
来 arp 通知,告诉周围设备 agent gateway ip 地址的变更 - 若该 agent gateway port 所在的 subnet 存在 gateway,则:
- 若 agent gateway port 的 gateway ip 不在 subnet 内,则调用
IPDevice.route.add_route
增加 route 的记录 - 若 agent gateway port 的 gateway ip 在 subnet 内,则调用
_add_default_gateway_for_fip
增加 route 的记录 - 若该 agent gateway port 所在的 subnet 不存在 gateway,则:
- 则删除之前关于该 port 的 gateway route 的记录
- 若未指明 ip route 的 table,则调用
get_fip_table_indexes
获取非 'local', 'default', 'main' 的 ip route table,并在这些所有的 table 中,都增加一个 gateway ip 的 route 记录 - 若指明了 ip route 的 table,则在该 table 中增加一个 gateway ip 的 route 记录
- 执行 ip rule list,获取所有的 ip route table
- 获取非 'local', 'default', 'main' 的 table
- 调用
Namespace.create
完成 namespace 的创建 - 调用
ip_lib.set_ip_nonlocal_bind
执行sysctl -w net.ipv4.ip_nonlocal_bind=1
- 增加一个 iptabels 规则如下:
-A neutron-l3-agent-PREROUTING -j CT --notrack
调用 _detele
实现
- 调用
IPWrapper.get_devices
获取当前 namespace 下的所有网络设备(lo 除外) - 删除 namespace 下的网络设备:
- 调用
IPWrapper.del_veth
删除rfp-
veth 设备 - 调用 interface dirver 删除
fg-
设备 - 调用
Namespace.delete
删除该 namespace
- 调用
get_rtr_ext_device_name
获取 rfp- 设备的名称 - 调用
get_int_device_name
获取 fpr- 设备的名称 - 调用
get_name
获取该 namespace 的 name - 为 fpr- 和 rfp- 设备分配一对 ip 地址
- 利用
IPDevice
来描述这对设备 - 调用
IPWrapper.add_veth
创建这对设备 - 为这对设备设定 mtu
- 启动这对设备
- 调用
_add_cidr_to_device
为这对设备各自增加 ip 地址 - 调用
_add_rtr_ext_route_rule_to_route_table
增加 fip- 中的 ip rule, - 为 rfp- 设备增加 gateway route:
[root@node1 ~]# ip netns exec qrouter-61eaacf9-d4e0-4202-bac9-321da0ceaa69 ip route show table 16
default via 169.254.106.115 dev rfp-61eaacf9-d
为设备增加 Ip 地址
- 调用
_update_gateway_route
更新 route 数据 - 通过
IPRule
增加一条 rule:2852022899: from all iif fpr-61eaacf9-d lookup 2852022899
[root@node1 ~]# ip netns exec fip-6c9852c3-7a5b-4b9c-a279-b9098257a0dd ip route list table 2852022899
default via 172.16.100.1 dev fg-efbf14c6-dc
scan system for any existing fip ports
neutron/agent/l3/namespace.py
构造 namespace 名称
从一个 namespace 名称中获得前缀(该 namespace 的类型)
从一个 namespace 名称中获得 id
修饰器,判断一个 namespace 是否存在,若存在,则调用 f 方法