wingwj.github.io

Growing ⇈ !

View My GitHub Profile

庖丁解牛——StarlingX 技术详解(2. stx-fault篇)

Author: WingWJ

Date: 26th, Feb, 2019

StarlingX version: 23rd, Jan, 2019

一、引言

接着上一篇 stx-nfv 项目技术详解,这一节来看stx-fault 项目。

入题以前,先温习一下之前(已涉及到组件)的StarlingX 架构图(局部),如下图。本篇的目标在于把右上角的FM组件,完全拆解清楚。

二、stx-fault project 详解

1. 项目概述

stx-fault,诚如其名,是StarlingX 项目中用于告警及事件处理及上报的项目,功能由FM组件来承接。与之前研究stx-nfv 一样,先来看下stx-fault 实现的功能。这里摘录原文,理解准确些:

2. 业务流程解析

这里开始stx-fault 的具体解析。

2.1 开头的两点疑问

先来看看stx-fault 的包结构:

首先,这里产生一个疑问,怎么有fm-api 与fm-rest-api 两个api相关包?两者又有什么区别?——这个稍后解释,先来看看FM相关进程。

FM服务主要由两大进程提供,分别是fm-api 和 fmManager:

把这两个进程画到一张图里。如下:

看上面这张架构图,是不觉得有点奇怪。怎么fm-api 与fmManager 之间,没有交互?——这个问题作为疑问二。

下面开始整个FM的具体技术分析。计划以这两个服务启动为引子,来逐步串联起stx-fault 的核心内容,并回答开始的这两点疑问。

2.2 fm-api 服务启动

先看看fm-api 进程。这部分,其实对应于fm-rest-api包,python实现。它是一个标准的wsgi服务,基于pecan框架对外提供rest-api。启动入口在 stx-fault/fm-rest-api/fm/fm/cmd/api.py中的main() 方法。

FM对外提供的全部rest接口,可在 stx-fault/api-ref/source/api-ref-fm-v1-fault.rst 中看到。当然,直接看官网API更直观。仔细看下这些接口,其实fm-api对外只提供了一些查询类操作,以及基于告警之上的功能(如事件及抑制);接口查询直接通过调取dbapi获取(或者换句话说,从fm-db)。

通过python-fmclient 提供的CLI,能够更好地理解提供的接口,这部分和普通OpenStack其他组件一致。使用时直接输入”fm <subcommand>”:

2.3 fmManager 服务启动

接下来研究下fmManager。这里直接提供绘制好的fmManager启动流程,不关注代码细节的可以直接跳到下一节:

接着来看下fmManager服务启动的具体代码。整个函数入口在 stx-fault/fm-mgr/sources/fm_main.cpp中(C++实现),实际逻辑在 stx-fault/fm-common/sources/fmMsgServer.cpp 的 fm_server_create() 中完成:

首先,设置配置及日志文件;

接着在fm_db_util_sync_event_suppression()中,调用 /usr/bin/fm_db_sync_event_suppression.py将/etc/fm/events.yaml 读取全部告警/事件列表,来做事件抑制同步。

这里重点提一下events.yaml文件。该文件是FM中所有告警/信息的基础模板,里面记录了每个告警的类型、描述、优先级、影响等内容,是后续FM处理各个告警的基础定义。片段如下:

...

100.101:
    Type: Alarm
    Description: |-
        Platform CPU threshold exceeded; threshold x%, actual y% .
             CRITICAL @ 95%
             MAJOR    @ 90%
             MINOR    @ 80%
    Entity_Instance_ID: host=<hostname>
    Severity: [critical, major, minor]
    Proposed_Repair_Action: "Monitor and if condition persists, contact next level of support."
    Maintenance_Action:
        critical: degrade
        major: degrade
    Inhibit_Alarms:
    Alarm_Type: operational-violation
    Probable_Cause: threshold-crossed
    Service_Affecting: False
    Suppression: True
    Management_Affecting_Severity: major
    Degrade_Affecting_Severity: critical
    
   ...

回到fmManager的启动流程。在完成前期配置阶段后,启动代码会通过while循环,启动三个周期线程:

最后,开始启动服务,尝试绑定IP及端口(重试次数为5)。对应代码在 FmSocket.cpp中FmSocketServer::server_reset(),最后在FmSocketServer::run() 真正开始跑起来提供服务。至此,fmManager服务启动完毕。

2.4 告警处理

fmManager启动完毕,按“生产者-消费者”模型来看,消费者已经准备好了。接下来看一个具体的告警处理过程,即“消费”本身。

FM的业务处理从run()开始,实际在handle_socket_data() 处走不同分支来处理传入请求的:

void FmSocketServerProcessor::handle_socket_data(int fd,
		std::vector<char> &rdata, CFmDBSession &sess) {

	SFmMsgHdrT *hdr = (SFmMsgHdrT *)&(rdata[0]);

	FM_DEBUG_LOG("Processor: handler socket data, action:%u\n",hdr->action);
	switch(hdr->action) {
	case EFmCreateFault:handle_create_fault(fd,hdr,rdata, sess); break;
	case EFmDeleteFault:handle_delete_fault(fd, hdr,rdata, sess); break;
	case EFmDeleteFaults:handle_delete_faults(fd,hdr,rdata,sess); break;
	case EFmGetFault:handle_get_fault(fd,hdr,rdata); break;
	case EFmGetFaults:handle_get_faults(fd,hdr,rdata); break;
	case EFmGetFaultsById:handle_get_faults_by_id(fd,hdr,rdata); break;
	default:
		FM_ERROR_LOG("Unexpected client request, action:%u\n",hdr->action);
		break;
	}
}

继续深入,以创建一个异常为例,走上面的第一个分支,即FmSocketServerProcessor::handle_create_fault()。

该方法具体完成以下内容:

注意:入队的后续处理,就在上节fmManager启动时候的处理线程fmJobHandlerThread 中。这里两者就对应上了。

另外提一下,在event_instance_id 转换时,fm-api会调用sysinv组件的cgtsclient。流程太长了,这里不贴了。

2.5 两点疑问的答案

看完以上流程,是不发现了,告警处理似乎是直接走fmManager进程的,好像和fm-api进程没关系?

答案是,外界与fmManager通信,确实基本是直接采用socket方式来进行的。这部分功能,就是在上文提到的fm-api包里来完成的。真正处理告警的动作,是通过引入的fm_core.so封装的C方法来提供的。Fm-api对外接口如下,都是对fm_core.so的再次封装,对外提供了一个统一的告警API类FaultAPIs:

在其他模块中,如果需要与fmManager通信来上报告警,就是通过直接import fm-api,从而引入FaultAPIs类来处理的,最终通过fm_core.so封装的方法来实现。

以下用一个具体的例子,来看下其他模块是怎样集成告警功能的。以stx-nfv为例:

首先,在stx-nfv/nfv/nfv-plugins/nfv_plugins/alarm_handlers/fm.py 初始化fm_api。之后在其他告警api中,直接调用来上报/消除告警。片段如下:

所以,总结下,最开始提到的疑问一:即 fm-api包通过封装 fm_core.so来提供告警上报/消除等接口,供其他模块生成告警;而fm-rest-api,仅是用来提供rest-api,用来提供基于告警之上的事件查询及事件抑制等功能

——这里产生了一个新问题:明明相关的功能,为什么要分两个包来实现?

我查阅了一些资料,感觉这里可能是由于OpenStack社区要求的。StarlingX是风河(Wind River) 基于自研Titanium Cloud 开源而来,基于OpenStack社区开源项目要求而实现了一套rest接口,即fm-rest-api,而自研组件本身使用的还是fm-api包。

证据的话在一些细节上也有体现。比如分别查看fm-api与fm-rest-api的__init__.py文件,前者是2013年的,而后者是2018年的:

后续从了解风河产品的朋友那儿获知的消息也确定了这一点,在风河Titanium Cloud中其实是没有fm-rest-api包的,之前接口是通过sysinv统一暴露的。疑问一解决!😊

所以,开头的疑问二,为什么fm-api与fmManager之家没有交互,也就一下就想明白了。因为在最开始设计的时候,就没有fm-rest-api组件啊,当然也就没有交互了。。

至此,消费者端整个处理流程基本说完了。下面来看看和FM对接的各组件,即告警信息的生产者,都有哪些。

2.6 FM与其他组件的关联

2.6.1 FM & StarlingX

上节提到过,各组件都是通过引入fm-api包来集成告警上报能力的。所以,有个简单方法来查询各组件调用FM的方式,即直接在StarlingX项目中搜索fm-api使用的FaultAPIs类即可(先不说GUI)。得到结果如下:

2.6.1 FM & OpenStack

此外,推广一下,除了StarlingX 各组件外,其实也可以考虑FM与原有OpenStack Telemetry项目进行集成。比如,如果想在OpenStack aodh中也增加fm相关支持,只用在aodh中增加对应notifier逻辑即可。

三、小结

与上节学习stx-nfv 项目一样,本章最后,把本文中涉及的各组件画到一起。和之前的图上相比,FM部分又进行了二次拆解。

以上。第二篇stx-fault project 的分解就到这里,换个组件继续边看边写。:)