TricircleHowToReadCode
目录
- 1 (WIP) 逐步学习 Tricircle 源代码
- 1.1 首先体验多区域 Tricircle 网络
- 1.2 步骤 1 ~ 步骤 4:不涉及 Tricircle 的任何代码
- 1.3 步骤 5:创建 Pod 实例
- 1.4 步骤 6:在中心 Neutron 服务器上创建网络/子网
- 1.5 步骤 7:获取用于 VM 启动的镜像 ID 和 Flavor ID
- 1.6 步骤 8:启动虚拟机
- 1.7 步骤 9:验证 VM 是否连接到网络
- 1.8 步骤 10:创建外部网络和子网
- 1.9 步骤 11:在中心 Neutron 服务器上创建路由器并附加子网
- 1.10 步骤 12:在中心 Neutron 服务器上设置路由器外部网关
- 1.11 步骤 13:启动 VNC 控制台并测试连接
- 1.12 步骤 14:在中心 Neutron 服务器上创建浮动 IP
- 1.13 步骤 15:关联浮动 IP
(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'])