vdsm的network模块中的dhcp-monitor
2024-02-26 15:13:18

dhcp_monitor 是 VDSM 的 network 模块中的一部分,这里单独记录下源码分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def initialize_monitor(cif):
global _monitor_instance
try:
monitor = Monitor.instance()
monitor.add_handler(lambda event: _dhcp_event_handler(cif, event))
monitor.start()
except Exception as e:
_monitor_instance = None
raise e
Monitor 的 add_handler 接收了一个 event 作为 handler,然后在 monitor 的 init 函数中会通过 serve_forever 去执行这个方法
def handle_event(self, event):
for handler in self._handlers:
handler(event)

def serve_forever(self):
for event in self._nl_monitor:
self.handle_event(event)

对应到前面,执行这个 _dhcp_event_handler,其中 address.address, address.prefixlen 对应 ip address 和 network mask。这里的 address 和 iface 是从 event 中读取的,EventField.IFACE 和 EventField.ADDRESS 是常量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class EventField(object):
class Scope(object):
KEY = 'scope'
GLOBAL = 'global'

class Event(object):
KEY = 'event'
NEW_ADDR = 'new_addr'

class Family(object):
KEY = 'family'
IPV4 = 'inet'
IPV6 = 'inet6'

ADDRESS = 'address'
IFACE = 'label'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def _dhcp_event_handler(cif, event):
if not _is_valid_event(event):
return

pool = MonitoredItemPool.instance()

iface = event[EventField.IFACE]
family = _get_event_family(event.get(EventField.Family.KEY))
address = IPAddressData(event[EventField.ADDRESS], iface)
item = (iface, family)

if not pool.is_item_in_pool(item):
logging.warning(
'Nic %s is not configured for IPv%s monitoring.', iface, family
)
return

if family == 4:
add_dynamic_source_route_rules(
iface, address.address, address.prefixlen
)

cif.notify('|net|host_conn|no_id')
pool.remove(item)

然后 add_dynamic_source_route_rules -> _setup_desired_state -> setup -> state_apply, state_apply 是 libnmstate 的方法,也就是设置网络的期望状态(声明式网络管理器API),也就是说,这里提交了该物理机期望的网络状态,提交给 nmstate,由 nmstate 去设置。

1
2
3
4
5
6
7
8
9
def add_dynamic_source_route_rules(next_hop, ipaddr, mask):
if not _should_add_source_route_rules(next_hop):
return

_setup_desired_state(
generate_dynamic_source_route_rule_state(
next_hop, ipaddr, mask
).state()
)

generate_dynamic_source_route_rule_state 用于生成网络配置信息。

1
2
3
def generate_dynamic_source_route_rule_state(next_hop, ipaddr, mask):
helper = SourceRouteHelper(next_hop, ipaddr, mask, None)
return NetworkingState(route_rules_state=helper.rules_state())

查看helper ,目前只支持 IPv4 的 source routing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# FIXME: Currently we are supporting only IPv4 source routing
class SourceRouteHelper(object):
def __init__(self, next_hop, ipaddr, mask, gateway):
self._next_hop = next_hop
self._ipaddr = ipaddr
self._mask = mask
self._gateway = gateway

self._table_id = generate_table_id(next_hop) if next_hop else None
self._network = self._parse_network()

def _parse_network(self):
if not self._ipaddr or not self._mask:
return None

return str(
ipaddress.ip_interface(f'{self._ipaddr}/{self._mask}').network
)

最后调用 NetworkingState 生成了一个 networkingstate 对象,里面设置了 ip,mask,gateway。
此时 NetworkingState 对象的其它方法就可以通过 network api 去调用,去修改 vdsm host 网络的信息。比如
lib/vdsm/network/nmstate/state.py 中的 update_mtu() 方法,在 lib/vdsm/network/nmstate/api.py 中的 generate_state 接口调用,而 generate_state 在 lib/vdsm/network/netswitch/configurator.py 的 _setup_nmstate 中调用,一直到最上层,就是 setupNetworks 接口。

Cif 的 notify 方法位置:
lib/vdsm/clientIF.py 中的 notify

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def notify(self, event_id, params=None):
"""
Send notification using provided subscription id as
event_id and a dictionary as event body. Before sending
there is notify_time added on top level to the dictionary.

Please consult event-schema.yml in order to build an appropriate event.
https://github.com/oVirt/vdsm/blob/master/lib/api/vdsm-events.yml

Args:
event_id (string): unique event name
params (dict): event content
"""
if not params:
params = {}

if not self.ready:
self.log.warning('Not ready yet, ignoring event %r args=%r',
event_id, params)
return

json_binding = self.servers['jsonrpc']

def _send_notification(message):
json_binding.reactor.server.send(
message, config.get('addresses', 'event_queue'))

try:
notification = Notification(event_id, _send_notification,
json_binding.bridge.event_schema)
notification.emit(params)
self.log.debug("Sending notification %s with params %s ",
event_id, params)
except KeyError:
self.log.warning("Attempt to send an event when jsonrpc binding"
" not available")

跟网络相关的,很多地方使用 event.get() 获取的数据,_event 是从 _nl_monitor 中获取的

1
2
3
4
5
6
7
8
9
10
11
lib/vdsm/network/dhcp_monitor.py 
def add_handler(self, handler):
self._handlers.append(handler)

def handle_event(self, event):
for handler in self._handlers:
handler(event)

def serve_forever(self):
for event in self._nl_monitor:
self.handle_event(event)

nl_monitor 实际上是 lib/vdsm/network/netlink/monitor.py 中的 Monitor,而这个 Monitor 实际上是 netlink 的 monitor,跟 libnl.GROUPS 对应。netlink 参考 Netlink 和 libnl 基础