作者:张华 发表于:2013-08-17
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明
( http://blog.csdn.net/quqi99)
这两天
评审了
社区的如下两个patch, 值。前一个涉及ML2插件中在一个host中同时为tenant启动GRE和VXLAN遂道的数据隔离性问题(客观上它通过配置SDN的MAC地址学习功能也解决了GRE广播带来的性能问题),后一个patch涉及到OVS插件中一个host启动GRE agent一个host启动VXLAN agent由于使用了同一张表带来的数据库事务问题。这两个patch实际上是关联的,非常地
有意思,深入这两个patch背后的讨论我相信能将Neutron中的OVS插件与ML2插件的本质弄得非常清楚。
https://review.openstack.org/#/c/41239/
https://review.openstack.org/#/c/27818/
故事由来:
在老的ovs plugin里使用了一张表叫ovs_tunnel_endpoints,它有两个字段:ip_address与id. tunnel_sync方法(在ovs_neutron_plugin.py文件中)会往数据表ovs_tunnel_endpoints中插一条记录,id则由_generate_tunnel_id方法产生。
tunnel_ip = kwargs.get('tunnel_ip')
tunnel = ovs_db_v2.add_tunnel_endpoint(tunnel_ip)
tunnels = ovs_db_v2.get_tunnel_endpoints()
self.notifier.tunnel_update(rpc_context, tunnel.ip_address,
tunnel.id, self.tunnel_type)
def _generate_tunnel_id(session):
max_tunnel_id = session.query(
func.max(ovs_models_v2.TunnelEndpoint.id)).scalar() or 0
return max_tunnel_id + 1
def add_tunnel_endpoint(ip):
session = db.get_session()
try:
tunnel = (session.query(ovs_models_v2.TunnelEndpoint).
filter_by(ip_address=ip).with_lockmode('update').one())
except exc.NoResultFound:
tunnel_id = _generate_tunnel_id(session)
tunnel = ovs_models_v2.TunnelEndpoint(ip, tunnel_id)
session.add(tunnel)
session.flush()
return tunnel
如果一个host运行了gre agent, 同时另一个host也运行了vxlan agent,(老的ovs plugin因为只是一层插件结构在一台host上不能同时运行gre agent与vxlan agent),上述的add_tunnel_endpoint方法可能造成id相同从而抛出主键重复的异常。
正确的做法应该对上述add_tunnel_endpoint方法改造成成乐观锁,添加重试次数。 代码见:https://review.openstack.org/#/c/27818/7/neutron/plugins/openvswitch/ovs_db_v2.py
我 评审时最初是这样想的:
只有在老的ovs plugin中才会有这个问题,因为gre与vxlan同时共用了一张同名的表ovs_tunnel_endpoints,当在不同的host上同时运行gre与vxlan agent注册遂道端点时就会发生这样的问题。
但是在新的ml2插件中,由于两层插件机制的引入可以在一台host上同时运行gre与vxlan,即使这样也不会出现上的问题,因为它的表名是不同的,一个是ml2_vxlan_endpoints,一个是ml2_gre_endpoints,所以它们都不需要id这个属性。
mysql> desc ml2_vxlan_endpoints;
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| ip_address | varchar(64) | YES | | NULL | |
| udp_port | int(11) | NO | PRI | NULL | auto_increment |
+------------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
mysql> desc ml2_gre_endpoints;
+------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| ip_address | varchar(64) | NO | PRI | | |
+------------+-------------+------+-----+---------+-------+
1 row in set (0.00 sec)
所以在对https://review.openstack.org/#/c/27818/6/neutron/plugins/openvswitch/ovs_db_v2.py进行 评审的时候,最初我给出的意见是应该对id采用不重复的uuid而不是用乐观锁 ( the incorrect method _generate_tunnel_id should be the root reason for this issue. the id returning by _generate_tunnel_id will be used to insert the id field of the table ovs_tunnel_endpoints. running the ovs plugin with tunneling in a multi-agent environment. means ovs plugin use only one same table ovs_tunnel_endpoints to store tunnel endpoint info for both GRE and VXLAN, thus above problem will cause this issue. so I prefer to change the id filed of the table ovs_tunnel_endpoints to use uuid.)
但是这样可能会造成当一个agent重启之后,在数据库又插入一条记录,这样agent在收到通知之后又创建一个重复的port (I dindn't figured out that case before, but you're right, using tunnel_ip in existing installation wouldn't work properly: if an agent gets rebooted, it would properly setup new tunnel ports using tunnel_ip to name them, but other running agent would recieve a tunnel_update with the ip_address, and set-up a new port to rebooted agent in addition to the existing one...)
所以OVS插件的这个问题最后加乐观锁解决,而对于ML2插件因为表不同永远不会出现此类问题。