PXE系列文章(14)- 带外管理Driver设计与实现
通过前面13篇文章,我们已经完全准备好了PXE的装机环境, 按PXE系列文章(9)- 优化 PXE 装机流程系统架构,我们需要实现带外管理的Driver以及带内执行逻辑的Agent.
- Driver 负责带外功能的实现,功能包括
开机
、关机
、重启
、PXE启动
、Disk启动
、Ping ILO Host
等等功能、主要是基于ipmitool
命令来实现, 也提供了可插拔的机制来实现其他带外管理的能力,比如 iDRAC、Redfish、SNMP等,目前我们使用ipmitool
能满足使用需求, 所以从实现上来讲仅实现ipmitool
的支持就足够了。
对于系统技术选型,前面也分析过,方案如下
- API --- Java
- Scheduler --- Java
- Driver --- Python
- Agent --- Python
对于API/Scheduler,我们使用Java是因为Java在作为Http Server以及流程调度这块足够健壮和严谨, 当然其他语言也一样可以实现,比如PHP/Python/NodeJS,但相对来说Java在HTTP相关部分有成熟、易于使用和便于维护的优势, 同时良好的多线程机制以及异步处理框架方便我们来抽象业务逻辑,以便构建成熟稳定可靠的集群。这话不绝对,其他任何语言都可以实现,可能这部分有个人的感性因素,可以多多沟通讨论,PHP/Python也一样可以实现易读易维护的系统。
对于Driver/Agent, 更多是和系统层面交互,以便完成系统API调用、Shell 命令执行等逻辑,我比较偏向于这块用Python来实现,有足够多优秀的系统操作Lib库。如果要让我们使用Java来操作这些逻辑,我估计大多数人都要崩溃,成本太高,而且性能不好, 哪怕你用JNI实现,研发实现的成本将大大的增加。
除了API是使用HTTP API对外提供服务,其他组建之间都将使用MQ来完成具体任务的下发和反馈。
本节我们优先实现Driver组建,相对来说Driver是比较轻量,接收Scheduler下发的带外管理指令,执行对应指令逻辑并反馈是否执行成功。
设计的Driver内部结构图如下:
- Driver Context 负责管理服务环境,包括消息总线的管理,任务的执行流程控制。
- 消息总线, 因为我们使用rabbitmq来作为消息队列服务,在选型时,我们详细调研了下,使用官方推荐pika库来处理消息的接收和发布。
- 生命周期指令集,主要适用于控制物理机的生命周期,开机/关机/重启/PXE启动等等。
具体实现的代码可以详细参见我们发布到github的仓库 suzaku-driver
需要调试的话, 需要准备一个能访问的RabbitMQ的服务,suzaku-driver
的配置信息在 (https://github.com/veryplay/suzaku-parent/blob/master/suzaku-driver/suzaku_driver/cfg/config-dev.yml)路径下,将MQ的信息更改为自己的调试环境的RabbitMQ的信息
然后就可以在根目录下执行 make develop
来构建测试环境,建议使用 virtualenv
来构建python环境, 而影响全局库, virtualenv
对 python
研发还是很重要的,打算写一篇介绍 virtualenv
的文章。
Makefile中包含对应平台的包的依赖,目前考虑的主要是Ubuntu和CentOS,使用 make centos
和 make ubuntu
安装对应依赖。
源码目录调试,执行 python cmd/server.py
如果执行过 make install
则直接运行 suzaku-driver
命令就好了, 默认启用的是 dev 环境。
如果启动后显示下面这些内容, 说明 suzaku-driver
已经正常工作了,可以发送MQ消息来验证下driver的工作机制。
python suzaku_driver/cmd/server.py
=============================================================================================
--..,_ _,.--.
`'.'. .'`__ o `;__.
'.'. .'.'` '---'` ` SUZAKU Driver Service
'.`'--....--'`.'
`'--....--'`
=============================================================================================
2019-07-11 09:09:23,071 server.py[line:29] INFO {'version': 1, 'root': {'level': 'INFO', 'handlers': ['console', 'info_file_handler']}, 'formatters': {'simple': {'format': '%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s'}}, 'disable_existing_loggers': False, 'handlers': {'console': {'formatter': 'simple', 'class': 'logging.StreamHandler', 'stream': 'ext://sys.stdout', 'level': 'DEBUG'}, 'info_file_handler': {'formatter': 'simple', 'backupCount': 100, 'level': 'INFO', 'encoding': 'utf8', 'class': 'logging.handlers.RotatingFileHandler', 'maxBytes': 104857600, 'filename': '/root/logs/suzaku-driver.log'}}}
2019-07-11 09:09:23,101 loader.py[line:31] INFO load config from /root/suzaku-parent/suzaku-driver/suzaku_driver/cfg/config-dev.yml
2019-07-11 09:09:23,101 server.py[line:33] INFO {'heart': {'period': 10}, 'mq': {'username': 'admin', 'vhost': '/suzaku', 'host': '192.168.56.10', 'password': 'admin', 'exchange': 'SUZAKU_SCHEDULER', 'receive_routing_key': 'dev', 'port': 5672}, 'env': 'dev'}
2019-07-11 09:09:23,101 engine.py[line:42] INFO use heart period : 10
2019-07-11 09:09:23,102 lifecycle.py[line:33] INFO Engine is starting
2019-07-11 09:09:23,102 lifecycle.py[line:33] INFO Broker is starting
2019-07-11 09:09:23,102 rabbitmq.py[line:112] INFO Connecting to <ConnectionParameters host=192.168.56.10 port=5672 virtual_host=/suzaku ssl=False>
2019-07-11 09:09:23,103 rabbitmq.py[line:112] INFO Connecting to <ConnectionParameters host=192.168.56.10 port=5672 virtual_host=/suzaku ssl=False>
2019-07-11 09:09:23,103 lifecycle.py[line:36] INFO Broker is started
2019-07-11 09:09:23,104 base_connection.py[line:237] INFO Pika version 0.13.1 connecting to 192.168.56.10:5672
2019-07-11 09:09:23,104 base_connection.py[line:237] INFO Pika version 0.13.1 connecting to 192.168.56.10:5672
2019-07-11 09:09:23,104 engine.py[line:129] INFO trigger a heart period 10.
2019-07-11 09:09:23,105 lifecycle.py[line:36] INFO Engine is started
2019-07-11 09:09:23,109 blocking_connection.py[line:1201] INFO Created channel=1
2019-07-11 09:09:23,111 blocking_connection.py[line:1201] INFO Created channel=1
2019-07-11 09:09:23,111 rabbitmq.py[line:125] INFO Declaring exchange : SUZAKU_SCHEDULER
2019-07-11 09:09:23,112 rabbitmq.py[line:125] INFO Declaring exchange : SUZAKU_SCHEDULER
2019-07-11 09:09:23,112 rabbitmq.py[line:139] INFO Declaring queue SUZAKU_SCHEDULER
2019-07-11 09:09:23,113 rabbitmq.py[line:139] INFO Declaring queue dev
2019-07-11 09:09:23,113 rabbitmq.py[line:155] INFO Binding SUZAKU_SCHEDULER to SUZAKU_SCHEDULER with SUZAKU_SCHEDULER
2019-07-11 09:09:23,114 rabbitmq.py[line:155] INFO Binding SUZAKU_SCHEDULER to dev with dev
2019-07-11 09:09:23,115 rabbitmq.py[line:268] INFO initial tx connect completely.
2019-07-11 09:09:23,116 rabbitmq.py[line:238] INFO begin to subscribe message
2019-07-11 09:09:23,215 rabbitmq.py[line:291] INFO deliver message to SUZAKU_SCHEDULER SUZAKU_SCHEDULER {"action": "Heart", "status": "OK", "sn": "dev"}
访问 http://192.168.56.10:15672
的 RabbitMQ 服务,因为 suzaku-driver
工作环境在 dev
, 调度下发指令的队列名也即使用 dev
作为driver的监听消息的队列, 如图所是:
在 https://github.com/veryplay/suzaku-parent/blob/master/suzaku-driver/command.md
文件中, 有所有支持的指令的模板文档, 那其中的 PingHost
指令来说,PingHost
是用于验证 ilo ip
的可达性,以验证带外管理网络可正常访问。
我们 ping
我们的PXE Server的IP, 看看执行结果,整理指令内容如下:
{
"sn" : "0",
"action" : "PingHost",
"ip" : "192.168.56.10"
}
sn在Driver操作部分是可选参数,不过尽量正确填写,以便跟踪对具体资源的操作行为记录。
将指令在 dev
队列 Publish message
部分输入上述内容:
看到 `suzaku-driver的日志如下:
2019-07-11 09:28:19,772 rabbitmq.py[line:191] INFO Received message # 1 from None: {
"sn" : "0",
"action" : "PingHost",
"ip" : "192.168.56.10"
}
2019-07-11 09:28:19,772 rabbitmq.py[line:201] INFO Acknowledging message 1
2019-07-11 09:28:19,845 executor.py[line:144] INFO ready to run ping -c 3 192.168.56.10
2019-07-11 09:28:21,919 executor.py[line:172] INFO execute `ping -c 3 192.168.56.10` completely, exit code : 0, stdout :
PING 192.168.56.10 (192.168.56.10) 56(84) bytes of data.
64 bytes from 192.168.56.10: icmp_seq=1 ttl=64 time=0.154 ms
64 bytes from 192.168.56.10: icmp_seq=2 ttl=64 time=0.139 ms
64 bytes from 192.168.56.10: icmp_seq=3 ttl=64 time=0.163 ms
--- 192.168.56.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 62ms
rtt min/avg/max/mdev = 0.139/0.152/0.163/0.009 ms
2019-07-11 09:28:21,919 ping.py[line:35] INFO ping 192.168.56.10 successfully
2019-07-11 09:28:21,919 common.py[line:72] INFO run PingHost success.
2019-07-11 09:28:22,015 rabbitmq.py[line:291] INFO deliver message to SUZAKU_SCHEDULER SUZAKU_SCHEDULER {"action": "PingHost", "status": "OK", "message": "run PingHost success.", "sn": "0", "ip": "192.168.56.10"}
显示 suzaku-driver
成功收到我们的 PingHost
指令,执行过程是发送数次 ping
指令,成功执行之后,给Scheduler反馈
{"action": "PingHost", "status": "OK", "message": "run PingHost success.", "sn": "0", "ip": "192.168.56.10"}
目前 Driver
lifecycle 抽象还未实现,后续逐步完善这块的抽象,以便可以接入其他带外管理工具,比如 iDRAC
、redfish
等。
如果还有任何问题欢迎通过 github
或者 电子邮箱: mailto:linuxcoming@qq.com 沟通交流.