创建虚机Port流程

1.port的创建

这我暂时不清楚,当一个虚机创建后会在br-int上添加一个端口,从neutron的角度来看,一个port已经建好了。

2.port的更新

当一个port接入到br-int上后,要做的事情就是如何让这个port和本用户的其他port,以及一些network service port(DHCP,DNS,Gateway)能通信。

具体流程是咋样的呢?

2.1.发现port

在ovs-agent里,会周期的调用rpc_loop来检查port的添加,删除,然后及时更新各个节点的流表。
在ovs-agent的内存里记录了一份本节点的所有port列表,然后周期的检查当前是否发生变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def process_network_ports(self, port_info, ovs_restarted):
#.......
#哪些端口是有变化的,哪些是新加的
devices_added_updated = (port_info.get('added', set()) |
port_info.get('updated', set()))
need_binding_devices = []
security_disabled_ports = []
if devices_added_updated:
start = time.time()
(skipped_devices, need_binding_devices,
security_disabled_ports, failed_devices['added']) = (
# 处理函数,会汇报给neutron server
self.treat_devices_added_or_updated(
devices_added_updated, ovs_restarted))

#.....
# TODO(salv-orlando): Optimize avoiding applying filters
# unnecessarily, (eg: when there are no IP address changes)
added_ports = port_info.get('added', set())
self._add_port_tag_info(need_binding_devices)

然后我们看treat_devices_added_or_updated这个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def treat_devices_added_or_updated(self, devices, ovs_restarted):
#.....
if 'port_id' in details:
LOG.info(_LI("Port %(device)s updated. Details: %(details)s"),
{'device': device, 'details': details})
details['vif_port'] = port
need_binding = self.treat_vif_port(port, details['port_id'],
details['network_id'],
details['network_type'],
details['physical_network'],
details['segmentation_id'],
details['admin_state_up'],
details['fixed_ips'],
details['device_owner'],
ovs_restarted)
#...........
self._update_port_network(details['port_id'],
details['network_id'])

这里边主要2个函数,treat_vip_port,里面主要是调用port_bound。主要是为本地port设置一些属性
因为ovs-agent只是扫描到了这个port而已,需要从neutron-server获得到这个port的详细信息,然后在本地设置这个port。
设置完毕后调用_update_port_network来向neutron-server更新换个port,从BUILD更新到ACTIVE状态。

注意后面一节代码,新添加的port,我们需要分配vlan tag,在br-int上隔离不同租户的虚拟机,这个vlan tag只在内存里,没有入库,所以重启ovs-agent后对待这个问题会有点麻烦。

后面_update_port_network在J版本到时候会调用update_device_up来更新状态。但我看在M版本里代码已经变了,是在一次loop里一起更新up和down。

1
2
3
4
if devices_up or devices_down:
devices_set = self.plugin_rpc.update_device_list(
self.context, devices_up, devices_down, self.agent_id,
self.conf.host)

2.2. update_port在ml2 plugin里的处理。

update_port_statu会层层调用到update_port_postcommit,在l2pop/mech_driver.py里
这里面干什么呢,就是和流表相关的处理了

[l2pop/mech_driver.py]
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
def update_port_postcommit(self, context):
port = context.current
orig = context.original

diff_ips = self._get_diff_ips(orig, port)
if diff_ips:
self._fixed_ips_changed(context, orig, port, diff_ips)
if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
# DVR的处理
elif (context.host != context.original_host
and context.original_status == const.PORT_STATUS_ACTIVE
and context.status == const.PORT_STATUS_DOWN):
# The port has been migrated. Send notification about port
# removal from old host.
fdb_entries = self._get_agent_fdb(
context.original_bottom_bound_segment,
orig, context.original_host)
self.L2populationAgentNotify.remove_fdb_entries(
self.rpc_ctx, fdb_entries)
elif context.status != context.original_status:
if context.status == const.PORT_STATUS_ACTIVE:
self._update_port_up(context)
elif context.status == const.PORT_STATUS_DOWN:
fdb_entries = self._get_agent_fdb(
context.bottom_bound_segment, port, context.host)
self.L2populationAgentNotify.remove_fdb_entries(
self.rpc_ctx, fdb_entries)

第一个elif是说port发生迁移,原来是ACTIVE,当前是DOWN,删除流表
第二个elif 处理状态发生变化了怎么处理,如果当前是ACTIVE的,则调用_update_port_up,否则就删除这些fdb_enties
我们先看看_update_port_up吧,猜也能猜出来,这是添加flow entry的。
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
37
38
39
40
41
42
43
44
45
def _update_port_up(self, context):
port = context.current
agent_host = context.host
session = db_api.get_session()
agent = l2pop_db.get_agent_by_host(session, agent_host)
if not agent:
LOG.warning(_LW("Unable to retrieve active L2 agent on host %s"),
agent_host)
return

network_id = port['network_id']
#当前ovs-agent上port的个数,用于判断自己是不是第一个
agent_active_ports = l2pop_db.get_agent_network_active_port_count(
session, agent_host, network_id)
agent_ip = l2pop_db.get_agent_ip(agent)
segment = context.bottom_bound_segment
if not self._validate_segment(segment, port['id'], agent):
return
# 自己新加一个port,对别的agent上的本network的影响(老port新加流表到新port)
other_fdb_entries = self._get_fdb_entries_template(
segment, agent_ip, network_id)
other_fdb_ports = other_fdb_entries[network_id]['ports']

if agent_active_ports == 1 or (l2pop_db.get_agent_uptime(agent) <
cfg.CONF.l2pop.agent_boot_time):
# First port activated on current agent in this network,
# we have to provide it with the whole list of fdb entries
agent_fdb_entries = self._create_agent_fdb(session,
agent,
segment,
network_id)

# And notify other agents to add flooding entry
other_fdb_ports[agent_ip].append(const.FLOODING_ENTRY)
# 给新创建的port添加到已有port的流表
if agent_fdb_entries[network_id]['ports'].keys():
self.L2populationAgentNotify.add_fdb_entries(
self.rpc_ctx, agent_fdb_entries, agent_host)

# Notify other agents to add fdb rule for current port
if port['device_owner'] != const.DEVICE_OWNER_DVR_INTERFACE:
other_fdb_ports[agent_ip] += self._get_port_fdb_entries(port)
# 向老port广播到新port的流表这么走
self.L2populationAgentNotify.add_fdb_entries(self.rpc_ctx,
other_fdb_entries)

前面的代码大致就分2部分,第一个部分是如果自己是本agent的第一个port,则同步所有流表过来
否则,就全部广播一下。