Notice: Constant WP_DEBUG already defined in /var/www/html/wordpress/wp-content/plugins/changyan/sohuchangyan.php on line 12

Notice: Constant WP_DEBUG_LOG already defined in /var/www/html/wordpress/wp-content/plugins/changyan/sohuchangyan.php on line 13

Notice: Constant WP_DEBUG_DISPLAY already defined in /var/www/html/wordpress/wp-content/plugins/changyan/sohuchangyan.php on line 14
OpenStack对接ODL之ODL-Networking详解【zoues.com】 – zoues

LOADING

Follow me

OpenStack对接ODL之ODL-Networking详解【zoues.com】
六月 23, 2017|ITPaaSSDN

OpenStack对接ODL之ODL-Networking详解【zoues.com】

OpenStack对接ODL之ODL-Networking详解【zoues.com】

作者简介:蒋暕青@上海宽带技术及应用工程研究中心,SDN技术实践者,对OpenStack有深入了解

OpenStack支持多种SDN控制器的对接,有一个对接方案是将一个mechanism driver的python文件放在neutron ml2目录下面,使得OpenStack对neutron的操作可以转发给SDN控制器。但是这样的处理方式存在许多问题,后来发展的driver被命名为xxx-networking项目,主要是为了更好地对接HA部署的OpenStack,因为Neutron server分布在多个物理机上的时候会出现更加复杂的情况,而导致SDN控制器接受到错误的消息,这篇文章以ODL-Networking为例介绍ODL是如何实现,自研控制器的对接也可以参考ODL的设计思想。

OpenStack对接ODL之ODL-Networking详解

ODL的ODL-Networking项目位于下面的链接:https://github.com/openstack/networking-odl。

ODL-Networking 介绍

OpenStack networking-odl是一个二进制文件的plugin,它的作用是集成OpenStack的Neutron API和OpenDaylight SDN控制器。networking-odl有ML2 driver和L3 plugin的模块,可以支持OpenStack Neutron L2和L3的API,再转发数据到OpenDaylight控制器上。

ODL驱动架构v1

ODL-Networking分为v1 driver和v2 driver,v1 driver会把OpenStack上的网络更改同步到ODL控制器上。举个例子,用户创建了一个网络,首先这个操作会被写进OpenStack数据库中,然后ODL driver会发送一个POST请求到ODL控制器 。虽然这个操作看起来很简单,但是却有一些隐含的问题:

  • ODL并不是实时反应的,即使REST call成功给到了ODL,ODL也不一定可以马上响应;
  • 在负载很大的时候(可能是odl或者openstack),两边的同步可能会存在瓶颈;
  • 在遇到odl与openstack同步失败时,v1 driver在下次的call REST API时会尝试去“full sync”整个neutron DB,所以会导致下次的REST API call花费很长的时间;
  • 在多个rest api call竞争的情况下可能会出问题:
    1.如,创建subnet时再创建port的情况,neutron的HA部署的时候,这2个创建的消息会同步的通过v1 driver发给ODL,导致创建port失败;
    2.在前面的“full sync”操作的时候,数据库如果正在删除资源,那么此时同步,会把下一时刻被删除的资源,同步到odl这边,形成openstack和odl的资源不同步。

ODL驱动架构v2

面对前面的这些问题,社区又重新设计了v2 driver:

  • v2 driver最重要的机制是引入了journaling(记录)机制,v2 driver会把openstack传给open daylight的数据记录在一个journal表中,journal表使用一堆队列来构成的。而不是直接把openstack的数据传递给odl;
  • v2 driver中会起一个journal的线程,它会周期性地检查journal表中是否有新的数据,然后处理新的数据,另外的,openstack neutron的postcommmit的操作也会触发这个线程工作;
  • 用户在创建一个网络资源的时候,这条数据会一开始存在neutron DB中,v2 driver再把这条数据存在“journal entry”里面,这个时候触发journal线程来处理这条数据;
  • 数据在pre-commit的时候就已经被记录在journal entry中了,以防止这个commit失败的时候,journal entry也可以马上中止,实现消息的同步。

Journal Entry架构

  • journal entry一开始创建的时候,状态置为“pending”等待状态,在这种状态下,entry在等待线程来处理,很多线程在处理entry的时候,只有一个线程可以“选中”这个entry;
  • 当这个entry被选中时,状态会被置为“processing”状态,并且被lock住,防止其他的线程来处理这个entry;
  • 当线程开始处理的时候,它先检查这个entry的依赖关系,如果这个entry依赖其他的entry,那么这个线程会把这个entry放回原位,然后再处理下一个entry;
  • 当这个entry没有其他依赖关系的时候,这个线程会吧entry里面的消息传递给opendaylight,具体是PUT,POST,DELETE这些操作,当这个REST call成功的时候,那么把这个entry的状态置为“completed”;
  • 当出现错误的时候,线程首先判断这个是“expected”意料之中的错误(如网络连通性等问题)还是“unexpected”意料之外的错误。对于那些意料之外的错误,会引入一个计数器,并对这个计数器设置一个数字,这个entry会被再次处理,处理的次数是这个计数器的数字。对于那些意料之中的错误,不会改变计数器的数字,当entry再次处理,直到计数器数字的次数后,这个entry就被设置为“failed”状态。否则,这个entry重新被标记为“pending”等待,等待下次被处理。

Entry依赖检查机制

以下是社区Entry依赖检查机制BP,Entry依赖检查机制将会被提前到entry创建的时候进行(https://blueprints.launchpad.net/networking-odl/+spec/dep-validations-on-create)。当前v2 driver的Entry依赖检查机制发生在entry被处理的时候,但是将会被改为entry被创建的时候就进行依赖检查机制 。当原先Entry依赖检查机制发生在entry被处理的时候,如果依赖检查机制fail,那么这个entry就会被送回队列,这样的处理方式可能会有以下几个问题:

  • entry依赖检查机制fail但是不知道哪里出了问题;
  • 很难去debug;
  • 当循环依赖的情况发生的话很难定位问题;
  • 一个entry可能会被反复进行依赖检查,导致CPU浪费;
  • 过多地检查依赖关系的话会导致数据库压力剧增。

因此,Entry依赖检查机制将会被提前到entry创建时就进行,在entry被创建的时候,journal就会检查这个entry是否有依赖其他的entry,如果有的话,那么把这个entry放在一个链表中;这样的话,journal在选择entry的时候就会只去选择没有其他依赖的entry,当entry被处理后,无论是成功还是失败,依赖这个entry的链表都会被清空,这样可以使得后面的entry不会依赖到它 。

下面是journal依赖的表,一个entry如果有依赖的话,那么下面的表中的行列就是这个entry的外键,entry被删除的时候,这些外键也需要被同时删除。entry在被处理的时候,这个entry所依赖的parent被存入parent_id中,依赖这个entry的被存入dependent_id中,在journal处理这个entry的时候,必须要保证这个entry没有与parent有依赖关系(当parent被处理完之后会自动断开所有依赖),这样的话就保证了不会存在parent没处理完,而直接去处理这个entry的情况。

1
2
3
4
5
6
++
| odl_journal_dependency |
++
| parent_id              |
| dependent_id           |
++

Entry恢复机制

以下是社区的Entry恢复机制BP(https://docs.openstack.org/developer/networking-odl/specs/completed/ocata/journal-recovery.html),Journal entry在处理失败的情况,entry需要再被处理 ,entry可能由于以下几个原因处理失败

  • 在最大尝试次数条件下,一直处理失败;
  • ODL中的数据和OpenStack Neutron数据库不一致;
  • Neutron和ODL中的数据同步失败;
    目前失败的entry就一直会存在journal表中,这样会影响journal表的性能,大量的失败的entry存在journal表中的话对性能的影响很大。

现在需要解决的问题是如何处理entry中标记为fail的entry,下面是社区BP的流程图:

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
46
47
48
49
 
++
| For each entry  |
| in failed state |
+++
        |
+v+
| Query resource |
| on ODL (REST)  |
++++
      |     |                          ++
   Resource |                          | Determine |
   exists   +Resource doesn't exist–> operation |
      |                                | type      |
+v+                          +++
| Determine |                                |
| operation |                                |
| type      |                                |
+++                                |
      |              ++          |
      +Create> Mark entry <Delete+
      |              | completed  |          |
      |              +^+       Create/
      |                         |         Update
      |                         |            |
      |          ++ |      +v+
      +Delete> Mark entry | |      | Determine |
      |          | pending    | |      | parent    |
      |          +^+ |      | relation  |
      |                    |    |      +++
+v+             |    |            |
| Compare to +Different+    |            |
| resource   |                  |            |
| in DB      +Same+            |
++                               |
                                             |
++                        |
| Create entry for  <Has no parent+
| resource creation |                        |
+^+                  Has a parent
         |                                   |
         |                         +v+
         +Parent exists+ Query parent  |
                                   | on ODL (REST) |
                                   +++
++                         |
| Create entry for <Parent doesn't exist–+
| parent creation  |
++

entry中标记为fail状态的entry不能对后面进来的entry产生影响
BP的实现可以分为两个部分,第一个部分,当我们检查到entry处于fail状态的时候,这个entry的动作可能是create或者update操作,如果这个资源再ODL中并没有存在、生效的话,那么我们就创建一个新的”create resource”这样的entry出来,其中,处理这个fail的entry是由一个专门的线程来处理,称为”maintenance thread”。

代码分析

v2的driver相比以前只有一个python文件来说,代码量大了许多,主要内容在networking_odl目录下,其中bgpvpn,fwaas,lbaas,qos,sfc这几个高级功能都是针对ODL的北向接口开发的,因此参考意义不大。主要的内容还是journal机制的实现以及l2和l3的实现:

1
2
3
4
5
6
7
L2_RESOURCES = {ODL_SG: ODL_SGS,
                ODL_SG_RULE: ODL_SG_RULES,
                ODL_NETWORK: ODL_NETWORKS,
                ODL_SUBNET: ODL_SUBNETS,
                ODL_PORT: ODL_PORTS}
L3_RESOURCES = {ODL_ROUTER: ODL_ROUTERS,
                ODL_FLOATINGIP: ODL_FLOATINGIPS}

在判断pending还是processing的时候,通过session.query去数据库中查找:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def check_for_pending_or_processing_ops(session, object_uuid, seqnum=None,
                                        operation=None):
    q = session.query(models.OpendaylightJournal).filter(
        or_(models.OpendaylightJournal.state == odl_const.PENDING,
            models.OpendaylightJournal.state == odl_const.PROCESSING),
        models.OpendaylightJournal.object_uuid == object_uuid)
 
    if seqnum is not None:
        q = q.filter(models.OpendaylightJournal.seqnum < seqnum)
 
    if operation:
        if isinstance(operation, (list, tuple)):
            q = q.filter(models.OpendaylightJournal.operation.in_(operation))
        else:
            q = q.filter(models.OpendaylightJournal.operation == operation)
    return session.query(q.exists()).scalar()

上述的2个机制分别位于2个类中,实现了描述的功能。Journal Entry架构:class OpendaylightJournalThread(object):
Entry恢复机制:class MaintenanceThread(object):
而L2和L3 的转发分别位于以下2个类中:

1
2
3
4
5
6
7
8
class OpenDaylightL2gwDriver(service_drivers.L2gwDriver):
 
class OpenDaylightL3RouterPlugin(
        common_db_mixin.CommonDbMixin,
        extraroute_db.ExtraRoute_db_mixin,
        l3_dvr_db.L3_NAT_with_dvr_db_mixin,
        l3_gwmode_db.L3_NAT_db_mixin,
        l3_agentschedulers_db.L3AgentSchedulerDbMixin):

在性能方面,ODL-Networking对对entry做cache缓存,有timeout和value属性,应该会有不小的性能提升;
class CacheEntry(collections.namedtuple(‘CacheEntry’, [‘timeout’, ‘values’])):为了更好的支持ODL增加的北向接口,ODL-Networking还特定会起一个start_odl_websocket_thread,位于class OpendaylightWebsocketClient(object)类中。

参考文献

https://docs.openstack.org/developer/networking-odl/

no comments
Share