在进行云计算网络功能开发时,经常需要在现有网络基础上进行网络功能定制或者开发新的插件。此时,就需要我们对Neutron的设计和功能有足够的认识。下面以Neutorn的l3-agent插件为例,讲解第三方服务如何实现对l3-agent服务状态的监控。
l3-agent是Neutron家族众多Agent中的一员。从名称就可以推知,该Agent肯定与三层路由有着密不可分的关系。实际情况也的确如此。l3-agent是一个进程,同时它也是一个RPC Consumer,接收来自neutron-server的RPC消息。它向租户提供路由和NAT功能。l3扩展包含两种资源:
-
router:在不同内部子网中转发数据包,通过指定内部网关做NAT。每个子网对应router上的一个端口,这个端口的IP就是该子网的网关地址。
-
floating ip:代表一个外部网络的IP,映射到内部网络的端口上。当网络的router:external属性为True时,floating ip才能定义。
整个Neutron架构如下所示:
从上图可知,l3-agent通过MQ与neutron-server进行通信。所以如果想监听l3-agent相关操作,理论上只需要订阅l3-agent相关主题即可。
通过具体分析l3-agent源码,我们可以找出所有l3-agent相关的主题。
-
l3-agent启动代码
代码位置neutron/agent/l3_agent.py
def main(manager='neutron.agent.l3.agent.L3NATAgentWithStateReport'):
register_opts(cfg.CONF)
common_config.init(sys.argv[1:])
config.setup_logging()
config.setup_privsep()
server = neutron_service.Service.create(
binary='neutron-l3-agent',
topic=topics.L3_AGENT,
report_interval=cfg.CONF.AGENT.report_interval,
manager=manager)
service.launch(cfg.CONF, server).wait()
从上可知,l3-agent实际服务是由neutron.agent.l3.agent.L3NATAgentWithStateReport类提供的。
-
L3NATAgentWithStateReport关键代码
class L3NATAgentWithStateReport(L3NATAgent):
def __init__(self, host, conf=None):
super(L3NATAgentWithStateReport, self).__init__(host=host, conf=conf)
self.state_rpc = agent_rpc.PluginReportStateAPI(topics.REPORTS)
self.agent_state = {
'binary': 'neutron-l3-agent',
'host': host,
'availability_zone': self.conf.AGENT.availability_zone,
'topic': topics.L3_AGENT,
'configurations': {
'agent_mode': self.conf.agent_mode,
'handle_internal_only_routers':
self.conf.handle_internal_only_routers,
'external_network_bridge': self.conf.external_network_bridge,
'gateway_external_network_id':
self.conf.gateway_external_network_id,
'interface_driver': self.conf.interface_driver,
'log_agent_heartbeats': self.conf.AGENT.log_agent_heartbeats},
'start_flag': True,
'agent_type': lib_const.AGENT_TYPE_L3}
report_interval = self.conf.AGENT.report_interval
if report_interval:
self.heartbeat = loopingcall.FixedIntervalLoopingCall(
self._report_state)
self.heartbeat.start(interval=report_interval)
从方法名称就可知道该Agent必然与NAT有关系。这里我们不再详细分析每一个方法。从init方法可知,l3-agent上报自身的当前状态是通过定时任务实现。loopingcall.FixedIntervalLoopingCall会周期性回调self._report_state方法。
-
_report_state方法
def _report_state(self):
num_ex_gw_ports = 0
num_interfaces = 0
num_floating_ips = 0
router_infos = self.router_info.values()
num_routers = len(router_infos)
for ri in router_infos:
ex_gw_port = ri.get_ex_gw_port()
if ex_gw_port:
num_ex_gw_ports += 1
num_interfaces += len(ri.router.get(lib_const.INTERFACE_KEY,
[]))
num_floating_ips += len(ri.router.get(lib_const.FLOATINGIP_KEY,
[]))
configurations = self.agent_state['configurations']
configurations['routers'] = num_routers
configurations['ex_gw_ports'] = num_ex_gw_ports
configurations['interfaces'] = num_interfaces
configurations['floating_ips'] = num_floating_ips
try:
agent_status = self.state_rpc.report_state(self.context,
self.agent_state,
True)
if agent_status == agent_consts.AGENT_REVIVED:
LOG.info('Agent has just been revived. '
'Doing a full sync.')
self.fullsync = True
self.agent_state.pop('start_flag', None)
except AttributeError:
# This means the server does not support report_state
LOG.warning("Neutron server does not support state report. "
"State report for this agent will be disabled.")
self.heartbeat.stop()
return
except Exception:
LOG.exception("Failed reporting state!")
其中,self.state_rpc.report_state是一个消息生产者,作用是向订阅的队列发送agent状态。该agent状态最终被neutron-server接收,并入库到neutron数据库的agents表。agents表有一个configurations字段,保存的信息就是agent_state对象中的configurations的json对象。
-
编写第三方服务server.py监听agent状态
#!/usr/bin/python
from oslo_config import cfg
import oslo_messaging
import sys
import time
class TestEndpoint(object):
def report_agent(self, ctx, **kwargs):
print "receive client access"
return 'alive'
transport_url = 'rabbit://guest:guest@192.168.90.26:5672/'
server = sys.argv[1]
oslo_messaging.set_transport_defaults(control_exchange='neutron')
transport = oslo_messaging.get_transport(cfg.CONF,transport_url)
target = oslo_messaging.Target(topic='q-reports-plugin', server=server, fanout=False, version='1.0')
endpoints = [
TestEndpoint(),
]
server = oslo_messaging.get_rpc_server(transport, target, endpoints)
try:
server.start()
while True:
time.sleep(1)
except KeyboardInterrupt:
print("Stopping server")
server.stop()
server.wait()
其中,q-reports-plugin是l3-agent上报的topic,Neutron默认的交换器配置是'neutron'。
-
启动服务,即可监听到l3-agent上报到neutron-server的消息
python server.py myserver
上述服务仅仅作为学习测试之用。从代码的配置可知,l3-agent状态上报是非fanout模式。在这种模式下,如果同一队列存在多个消费者时,RabbitMQ会以轮询的方式调用消费者。在实际生产过程中,需要注意到此点,否者冒然通过以上方式监听q-reports-plugin队列,会影响到Neutron本身的业务逻辑。
小结
在实际生产过程中使用第三方服务监听l3-agent相关消息队列时,需要注意消息队列使用的模式,否者很可能影响到Neutron的正常业务。另外,在获取transport对象之前,一定需要把Neutron的默认交换器配置为neutron,否则监听消息一定失败。
如果对云计算感兴趣,可以关注我的微信公众号: