跳转到: 导航, 搜索

TricircleHowToReadCode

(WIP) 逐步学习 Tricircle 源代码

首先体验多区域 Tricircle 网络

请遵循安装指南: https://github.com/openstack/tricircle/blob/master/doc/source/multi-pod-installation-devstack.rst

请注意:源代码中的中心 Neutron 有时称为“top”,本地 Neutron 称为“bottom”。

安装指南中涉及的源代码如下所示

步骤 1 ~ 步骤 4:不涉及 Tricircle 的任何代码

步骤 5:创建 Pod 实例

已发出命令

 curl -X POST http://127.0.0.1:19999/v1.0/pods -H "Content-Type: application/json" \
     -H "X-Auth-Token: $token" -d '{"pod": {"region_name":  "CentralRegion"}}'
 curl -X POST http://127.0.0.1:19999/v1.0/pods -H "Content-Type: application/json" \
     -H "X-Auth-Token: $token" -d '{"pod": {"region_name":  "RegionOne", "az_name": "az1"}}'


  • 对于版本为 'v1.0',RESTful API 请求将从 RootController 路由到 V1Controller
 tricircle/tricircle/api/controllers/root.py
 ...
 class RootController(object):
      ...
     @expose()
     def _lookup(self, version, *remainder):
         if version == 'v1.0':
             return V1Controller(), remainder
  • 在 V1Controller 中,由于请求是创建 Pod,因此请求将转发到 PodsController 中的 post 函数。Post 函数将在 pod 表中创建 pod 和 az 映射记录
 tricircle/tricircle/api/controllers/pod.py
 ...
 class PodsController(rest.RestController):
   ...
   @expose(generic=True, template='json')
   def post(self, **kw):
       context = t_context.extract_context_from_environ()
       ...
       new_pod = core.create_resource(
                   context, models.Pod,
                   {'pod_id': _uuid,
                    'region_name': region_name,
                    'pod_az_name': pod_az_name,
                    'dc_name': dc_name,
                    'az_name': az_name})

步骤 6:在中心 Neutron 服务器上创建网络/子网

因为中心 Neutron 的核心插件配置如下,所以会调用 central_plugin.py

   core_plugin = tricircle.network.central_plugin.TricirclePlugin

已发出命令

 neutron --os-region-name=CentralRegion net-create net1
 neutron --os-region-name=CentralRegion subnet-create net1 10.0.1.0/24
  • 创建 net1 时,请求由 create_network 处理
 tricircle/tricircle/network/central_plugin.py
 ...
 class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
                     security_groups.TricircleSecurityGroupMixin,
                     external_net_db.External_net_db_mixin,
                     portbindings_db.PortBindingMixin,
                     extradhcpopt_db.ExtraDhcpOptMixin,
                     l3_db.L3_NAT_dbonly_mixin,
                     l3_attrs_db.ExtraAttributesMixin):
  ...
  def create_network(self, context, network):
       net_data = network[attributes.NETWORK]
       ...
  • 在 net1 中创建 10.0.1.0/24 子网时,请求由 create_subnet 处理
 tricircle/tricircle/network/central_plugin.py
 ...
 class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
                     security_groups.TricircleSecurityGroupMixin,
                     external_net_db.External_net_db_mixin,
                     portbindings_db.PortBindingMixin,
                     extradhcpopt_db.ExtraDhcpOptMixin,
                     l3_db.L3_NAT_dbonly_mixin,
                     l3_attrs_db.ExtraAttributesMixin):
 ...
 def create_subnet(self, context, subnet):
       subnet_data = subnet['subnet']
       ...

步骤 7:获取用于 VM 启动的镜像 ID 和 Flavor ID

已发出命令

 glance --os-region-name=RegionOne image-list
 nova --os-region-name=RegionOne flavor-list

此步骤不涉及 Tricircle 的任何源代码。

步骤 8:启动虚拟机

已发出命令

 nova --os-region-name=RegionOne boot --flavor 1 --image $image1_id --nic net-id=$net1_id vm1

请注意,RegionOne 中的 Nova 配置为使用 RegionOne 中的本地 Neutron,而 RegionTwo 的 Nova 将使用 RegionTwo 中的本地 Neutron。因此,在不同区域启动实例时,将调用不同的本地 Neutron。

在 RegionOne 本地 Neutron 的配置中,核心插件配置如下

 core_plugin = tricircle.network.local_plugin.TricirclePlugin
  • 当在 RegionOne Nova 中执行 nova boot 时,Nova 会将带有网络 ID 的网络查询发送到本地 Neutron,本地插件会将网络查询请求发送到中心 Neutron
 tricircle/tricircle/network/local_plugin.py
 ...
 def get_networks(self, context, filters=None, fields=None,
                    sorts=None, limit=None, marker=None, page_reverse=False):
 ...
 t_networks = raw_client.list_networks(**params)['networks']
  • raw_client.list_networks 用于查询中心 Neutron 中的网络,如果网络存在于中心 Neutron 中,本地插件将使用与中心 Neutron 中相同的 uuid 创建网络/子网
 tricircle/tricircle/network/local_plugin.py
 ...
 def get_networks(self, context, filters=None, fields=None,
                    sorts=None, limit=None, marker=None, page_reverse=False):
       ...
               b_network = self.core_plugin.create_network(
                   context, {'network': network})
               subnet_ids = self._ensure_subnet(context, network)
  • 稍后,RegionOne 中的 nova 将发出请求在 RegionOne 本地 Neutron 中创建端口,创建过程首先在中心 Neutron 中创建端口,然后在本地 Neutron 中使用相同的 uuid 创建端口和安全组。
 tricircle/tricircle/network/local_plugin.py
 ...
 def create_port(self, context, port):
 ...
           self._adapt_port_body_for_client(port['port'])
           t_port = raw_client.create_port(port)['port']
  ....
   self._handle_security_group(t_ctx, context, t_port)
   b_port = self.core_plugin.create_port(context, {'port': t_port})
  • 接下来,RegionOne 中的 nova 将发出请求在 RegionOne 本地 Neutron 中更新端口绑定,本地 Neutron 插件中的绑定过程会将端口更新转发到中心 Neutron,然后更新本地端口。
 tricircle/tricircle/network/local_plugin.py
 ...
 def update_port(self, context, _id, port):
 ...
           t_ctx = t_context.get_context_from_neutron_context(context)
           self.neutron_handle.handle_update(t_ctx, 'port', _id,
                                             {'port': update_dict})
       return self.core_plugin.update_port(context, _id, port)
  • 一旦中心 Neutron 收到端口更新请求,就需要创建路由表中资源路由条目。
 tricircle/tricircle/network/central_plugin.py
 ...
 def update_port(self, context, port_id, port):
       t_ctx = t_context.get_context_from_neutron_context(context)
       top_port = super(TricirclePlugin, self).get_port(context, port_id)
       ...
          for resource_id, resource_type in entries:
               if db_api.get_bottom_id_by_top_id_region_name(
                       t_ctx, resource_id, pod['region_name'], resource_type):
                   continue
               db_api.create_resource_mapping(t_ctx, resource_id, resource_id,
                                              pod['pod_id'], res['tenant_id'],
                                              resource_type)
  • 如果网络连接到路由器上的接口,则启动异步作业以(如果 RegionOne 中不存在则创建)设置 RegionOne 中的路由器。
 tricircle/tricircle/network/central_plugin.py
 ...
 def update_port(self, context, port_id, port):
      ...
      interfaces = super(TricirclePlugin, self).get_ports(
               context,
               {'network_id': [res['network_id']],
                'device_owner': [constants.DEVICE_OWNER_ROUTER_INTF]})
           interfaces = [inf for inf in interfaces if inf['device_id']]
           if interfaces:
               # request may be come from service, we use an admin context
               # to run the xjob
               admin_context = t_context.get_admin_context()
               self.xjob_handler.setup_bottom_router(
                   admin_context, res['network_id'],
                   interfaces[0]['device_id'], pod['pod_id'])
  • 如果网络连接到路由器上的接口,xjob_handler.setup_bottom_router 将注册异步作业并发送一条 RPC 消息到 xmanager.py 以触发异步作业,该作业将在 RegionOne 中设置路由器。
  tricircle/tricircle/xjob/xmanager.py
  ...
   @_job_handle(constants.JT_ROUTER_SETUP)
   def setup_bottom_router(self, ctx, payload):
       (b_pod_id,
        t_router_id, t_net_id) = payload[constants.JT_ROUTER_SETUP].split('#')
        ...
  • 除了 setup_bottom_router 异步作业之外,在 central_plugin.py 的 update_port 函数中,还需要检查其他资源,例如是否在 RegionOne 中创建或更新这些资源,例如安全组。
 tricircle/tricircle/network/central_plugin.py
 ...
 def update_port(self, context, port_id, port):
      ...
      self.xjob_handler.configure_security_group_rules(t_ctx,
                                                            res['tenant_id'])

xjob_handler.configure_security_group_rules 将注册安全组规则异步作业,然后发送 RPC 消息以触发 xmanager.py 中的异步作业。

  • 您可以在 xmanager.py 中找到 configure_security_group_rules 作业。
    tricircle/tricircle/xjob/xmanager.py
  ...
  @_job_handle(constants.JT_SEG_RULE_SETUP)
   def configure_security_group_rules(self, ctx, payload):
       project_id = payload[constants.JT_SEG_RULE_SETUP]
       top_client = self._get_client()
       sg_filters = [{'key': 'tenant_id', 'comparator': 'eq',
                      'value': project_id}] 

路由器设置和安全组规则配置(以及稍后将包含的其他资源)作为 XJOB 进程中的异步作业处理,原因是为了增强实例启动体验。如果所有这些资源都在 local_plugin 中创建,那么将花费很长时间,并且可能导致启动超时、糟糕的用户体验和低可靠性。同时,通过异步作业重试和可靠性考虑设计,确保了网络自动化过程的可靠性。

如果您在 RegionTwo 中启动 VM2,例如

  nova --os-region-name=RegionTwo boot --flavor 1 --image $image2_id --nic net-id=$net2_id vm2

那么 RegionTwo 本地 Neutron 下的本地插件将被调用,工作流程与 RegionOne 中的流程相同。中心 Neutron 中的异步作业将在 RegionTwo 中设置路由器(如果网络连接到路由器上)和安全组规则等资源。请注意,如果在 VM2 启动期间使用了跨 OpenStack L2 网络,那么资源设置可能会发生在 RegionOne 和 RegionTwo 中的异步作业中。这是网络自动化协调多个 OpenStack 网络资源的方式。

步骤 9:验证 VM 是否连接到网络

Tricircle 不涉及此步骤。

步骤 10:创建外部网络和子网

已发出命令

 curl -X POST http://127.0.0.1:20001/v2.0/networks -H "Content-Type: application/json" \
    -H "X-Auth-Token: $token" \
    -d '{"network": {"name": "ext-net", "admin_state_up": true, "router:external": true,  "provider:network_type": "vlan", "provider:physical_network": "extern", "availability_zone_hints": ["RegionTwo"]}}'
 neutron --os-region-name=CentralRegion subnet-create --name ext-subnet --disable-dhcp ext-net 163.3.124.0/24
  • 对于外部网络,它将由云管理员创建,并且需要指定外部网络将驻留在哪个区域。create_network 将检查网络创建是否为外部网络,如果是,则将外部网络创建请求转发到相应的区域。因为指定了 RegionTwo,所以请求将转发到 RegionTwo 本地 Neutron。
 tricircle/tricircle/network/central_plugin.py
 ...
 def create_network(self, context, network):
       net_data = network[attributes.NETWORK]
       tenant_id = net_data['tenant_id']
       is_external = self._ensure_az_set_for_external_network(context,
                                                              net_data)
       ...
            if is_external:
               self._fill_provider_info(res, net_data)
               self._create_bottom_external_network(
                   context, net_data, res['id'])

步骤 11:在中心 Neutron 服务器上创建路由器并附加子网

已发出命令

 neutron --os-region-name=CentralRegion router-create router
 neutron --os-region-name=CentralRegion router-interface-add router $subnet1_id
 neutron --os-region-name=CentralRegion router-interface-add router $subnet2_id

请注意,在步骤 6 中,虽然有代码可以在 update_port 中设置路由器,但由于网络在步骤 6 中没有连接到路由器上的接口,因此未调用路由器的异步作业。现在执行 router-interface-add 时,central plugin 中的 add_router_interface 代码将被调用,并启动异步作业以设置 RegionOne 和/或 RegionTwo 中的路由器,如果需要,将创建桥接网络

tricircle/tricircle/network/central_plugin.py
 ...
 def add_router_interface(self, context, router_id, interface_info):
       t_ctx = t_context.get_context_from_neutron_context(context)
       router = self._get_router(context, router_id)
       ....
       if len(b_pods) == 1:
               self.xjob_handler.setup_bottom_router(
                   t_ctx, net_id, router_id, b_pods[0]['pod_id'])
           else:
               self.xjob_handler.setup_bottom_router(
                   t_ctx, net_id, router_id, t_constants.POD_NOT_SPECIFIED)

步骤 12:在中心 Neutron 服务器上设置路由器外部网关

已发出命令

  neutron --os-region-name=CentralRegion router-gateway-set router ext-net

将调用 central_plugin 中的 update_router,并且首先在中心 Neutron 中设置路由器网关,然后在驻留 ext-net 的 RegionTwo 中设置路由器网关,通过异步作业也会设置 Neutron 中跨 L3 网络的额外路由“目标地址,下一跳”。

 tricircle/tricircle/network/central_plugin.py
 ...
 def update_router(self, context, router_id, router):
       router_data = copy.deepcopy(router['router'])
       ...
       if is_add:
           ret = super(TricirclePlugin, self).update_router(
               context, router_id, router)
           router_data[l3.EXTERNAL_GW_INFO].update(ret[l3.EXTERNAL_GW_INFO])
           self._add_router_gateway(context, router_id, router_data)
       ...
       t_ctx = t_context.get_context_from_neutron_context(context)
       self.xjob_handler.configure_extra_routes(t_ctx, router_id)

步骤 13:启动 VNC 控制台并测试连接

此步骤不调用 Tricircle 的任何代码。

步骤 14:在中心 Neutron 服务器上创建浮动 IP

已发出命令

 neutron --os-region-name=CentralRegion floatingip-create ext-net

将调用 central_plugin 中的 create_floatingip,并且仅在中心 Neutron 中创建浮动 IP。

 tricircle/tricircle/network/central_plugin.py
 ...
 def create_floatingip(self, context, floatingip):
       # create bottom fip when associating fixed ip
       return super(TricirclePlugin, self).create_floatingip(
           context, floatingip,
           initial_status=constants.FLOATINGIP_STATUS_DOWN)
 ...

步骤 15:关联浮动 IP

已发出命令

 neutron --os-region-name=CentralRegion floatingip-associate $floatingip_id $port_id

将调用 central_plugin 中的 update_floatingip,并且首先在中心 Neutron 中关联浮动 IP,然后调用异步作业以在驻留外部网络的 RegionTwo 中关联浮动 IP,如果与浮动 IP 关联的端口不在同一区域,则可能会创建影子端口。

 tricircle/tricircle/network/central_plugin.py
 ...
def update_floatingip(self, context, _id, floatingip):
...
       org_floatingip_dict = self._make_floatingip_dict(
           self._get_floatingip(context, _id))
       res = super(TricirclePlugin, self).update_floatingip(
           context, _id, floatingip)
       try:
           if floatingip['floatingip']['port_id']:
               self._associate_floatingip(context, _id, floatingip)
 ...
 def _associate_floatingip(self, context, _id, floatingip):
       t_ctx = t_context.get_context_from_neutron_context(context)
       .....
       self.xjob_handler.setup_bottom_router(
           t_ctx, net_id, floatingip_db['router_id'], int_net_pod['pod_id'])