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
20def 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 | def treat_devices_added_or_updated(self, devices, ovs_restarted): | 
这里边主要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
4if 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里
这里面干什么呢,就是和流表相关的处理了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
27def 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
45def _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,则同步所有流表过来
否则,就全部广播一下。