diff --git a/collector/src/main/java/org/dromara/hertzbeat/collector/collect/smtp/SmtpCollectImpl.java b/collector/src/main/java/org/dromara/hertzbeat/collector/collect/smtp/SmtpCollectImpl.java new file mode 100644 index 00000000000..46d3df8343d --- /dev/null +++ b/collector/src/main/java/org/dromara/hertzbeat/collector/collect/smtp/SmtpCollectImpl.java @@ -0,0 +1,120 @@ +package org.dromara.hertzbeat.collector.collect.smtp; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.net.smtp.SMTP; +import org.dromara.hertzbeat.collector.collect.AbstractCollect; +import org.dromara.hertzbeat.collector.dispatch.DispatchConstants; +import org.dromara.hertzbeat.collector.util.CollectUtil; +import org.dromara.hertzbeat.common.constants.CollectorConstants; +import org.dromara.hertzbeat.common.constants.CommonConstants; +import org.dromara.hertzbeat.common.entity.job.Metrics; +import org.dromara.hertzbeat.common.entity.job.protocol.SmtpProtocol; +import org.dromara.hertzbeat.common.entity.message.CollectRep; +import org.dromara.hertzbeat.common.util.CommonUtil; + +import java.io.IOException; +import java.net.SocketException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * @author dongfeng + */ +@Slf4j +public class SmtpCollectImpl extends AbstractCollect { + public SmtpCollectImpl() { + } + + @Override + public void collect(CollectRep.MetricsData.Builder builder, long monitorId, String app, Metrics metrics) { + long startTime = System.currentTimeMillis(); + if (metrics == null || metrics.getSmtp() == null) { + builder.setCode(CollectRep.Code.FAIL); + builder.setMsg("Smtp collect must has Smtp params"); + return; + } + SmtpProtocol smtpProtocol = metrics.getSmtp(); + String host = smtpProtocol.getHost(); + String port = smtpProtocol.getPort(); + int timeout = CollectUtil.getTimeout(smtpProtocol.getTimeout()); + SMTP smtp = null; + try { + smtp = new SMTP(); + smtp.setConnectTimeout(timeout); + smtp.setCharset(StandardCharsets.UTF_8); + smtp.connect(host, Integer.parseInt(port)); + if (smtp.isConnected()) { + long responseTime = System.currentTimeMillis() - startTime; + List aliasFields = metrics.getAliasFields(); + + Map resultMap = execCmdAndParseResult(smtp, smtpProtocol.getCmd(), smtpProtocol); + resultMap.put(CollectorConstants.RESPONSE_TIME, Long.toString(responseTime)); + if (resultMap.size() < aliasFields.size()) { + log.error("smtp response data not enough: {}", resultMap); + builder.setCode(CollectRep.Code.FAIL); + builder.setMsg("The cmd execution results do not match the expected number of metrics."); + return; + } + CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder(); + for (String field : aliasFields) { + String fieldValue = resultMap.get(field); + valueRowBuilder.addColumns(Objects.requireNonNullElse(fieldValue, CommonConstants.NULL_VALUE)); + } + builder.addValues(valueRowBuilder.build()); + } else { + builder.setCode(CollectRep.Code.UN_CONNECTABLE); + builder.setMsg("Peer connect failed,Timeout " + timeout + "ms"); + return; + } + smtp.disconnect(); + } catch (SocketException socketException) { + String errorMsg = CommonUtil.getMessageFromThrowable(socketException); + log.debug(errorMsg); + builder.setCode(CollectRep.Code.UN_CONNECTABLE); + builder.setMsg("The peer refused to connect: service port does not listening or firewall: " + errorMsg); + } catch (IOException ioException) { + String errorMsg = CommonUtil.getMessageFromThrowable(ioException); + log.info(errorMsg); + builder.setCode(CollectRep.Code.UN_CONNECTABLE); + builder.setMsg("Peer connect failed: " + errorMsg); + } catch (Exception e) { + String errorMsg = CommonUtil.getMessageFromThrowable(e); + log.warn(errorMsg, e); + builder.setCode(CollectRep.Code.FAIL); + builder.setMsg(errorMsg); + } finally { + if (smtp != null) { + try { + smtp.disconnect(); + } catch (Exception e) { + log.warn(e.getMessage()); + } + } + } + } + + @Override + public String supportProtocol() { + return DispatchConstants.PROTOCOL_SMTP; + } + + private static Map execCmdAndParseResult(SMTP smtp, String cmd, SmtpProtocol smtpProtocol) throws IOException { + Map result = new HashMap<>(8); + // 存入smtp连接的响应 + result.put("smtpBanner", smtp.getReplyString()); + smtp.helo(smtpProtocol.getEmail()); + // 获取helo的响应 + String replyString = smtp.getReplyString(); + result.put("heloInfo", replyString); + String[] lines = replyString.split("\n"); + for (String line : lines) { + if (line.startsWith("250")) { + result.put("response", "OK"); + } + } + return result; + } +} diff --git a/collector/src/main/java/org/dromara/hertzbeat/collector/dispatch/DispatchConstants.java b/collector/src/main/java/org/dromara/hertzbeat/collector/dispatch/DispatchConstants.java index 19ea65a9d6b..820cc2ed49e 100644 --- a/collector/src/main/java/org/dromara/hertzbeat/collector/dispatch/DispatchConstants.java +++ b/collector/src/main/java/org/dromara/hertzbeat/collector/dispatch/DispatchConstants.java @@ -35,6 +35,10 @@ public interface DispatchConstants { * protocol telnet */ String PROTOCOL_TELNET = "telnet"; + /** + * protocol smtp + */ + String PROTOCOL_SMTP = "smtp"; /** * protocol udp */ diff --git a/collector/src/main/resources/META-INF/services/org.dromara.hertzbeat.collector.collect.AbstractCollect b/collector/src/main/resources/META-INF/services/org.dromara.hertzbeat.collector.collect.AbstractCollect index 23332b2feda..2b8a22393b1 100644 --- a/collector/src/main/resources/META-INF/services/org.dromara.hertzbeat.collector.collect.AbstractCollect +++ b/collector/src/main/resources/META-INF/services/org.dromara.hertzbeat.collector.collect.AbstractCollect @@ -8,6 +8,7 @@ org.dromara.hertzbeat.collector.collect.mongodb.MongodbSingleCollectImpl org.dromara.hertzbeat.collector.collect.snmp.SnmpCollectImpl org.dromara.hertzbeat.collector.collect.ssh.SshCollectImpl org.dromara.hertzbeat.collector.collect.telnet.TelnetCollectImpl +org.dromara.hertzbeat.collector.collect.smtp.SmtpCollectImpl org.dromara.hertzbeat.collector.collect.ftp.FtpCollectImpl org.dromara.hertzbeat.collector.collect.mq.RocketmqSingleCollectImpl org.dromara.hertzbeat.collector.collect.udp.UdpCollectImpl diff --git a/common/src/main/java/org/dromara/hertzbeat/common/entity/job/Metrics.java b/common/src/main/java/org/dromara/hertzbeat/common/entity/job/Metrics.java index aeb1904417f..1edf9669995 100644 --- a/common/src/main/java/org/dromara/hertzbeat/common/entity/job/Metrics.java +++ b/common/src/main/java/org/dromara/hertzbeat/common/entity/job/Metrics.java @@ -115,6 +115,11 @@ public class Metrics { * 使用telnet协议的监控配置信息 */ private TelnetProtocol telnet; + /** + * Monitoring configuration information using the public smtp protocol + * 使用smtp协议的监控配置信息 + */ + private SmtpProtocol smtp; /** * Use udp implemented by socket for service port detection configuration information * 使用socket实现的udp进行服务端口探测配置信息 diff --git a/common/src/main/java/org/dromara/hertzbeat/common/entity/job/protocol/SmtpProtocol.java b/common/src/main/java/org/dromara/hertzbeat/common/entity/job/protocol/SmtpProtocol.java new file mode 100644 index 00000000000..7e22bff1f19 --- /dev/null +++ b/common/src/main/java/org/dromara/hertzbeat/common/entity/job/protocol/SmtpProtocol.java @@ -0,0 +1,40 @@ +package org.dromara.hertzbeat.common.entity.job.protocol; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author dongfeng + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SmtpProtocol { + /** + * email主机ip或域名 + */ + private String host; + + /** + * email主机端口 + */ + private String port; + + /** + * 超时时间 + */ + private String timeout; + + /** + * helo命令的测试者(email) + */ + private String email; + + /** + * 发送的命令 + */ + private String cmd; +} diff --git a/manager/src/main/resources/define/app-smtp.yml b/manager/src/main/resources/define/app-smtp.yml new file mode 100644 index 00000000000..60122691393 --- /dev/null +++ b/manager/src/main/resources/define/app-smtp.yml @@ -0,0 +1,172 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The monitoring type category:service-application service monitoring db-database monitoring mid-middleware custom-custom monitoring os-operating system monitoring +# 监控类型所属类别:service-应用服务 program-应用程序 db-数据库 custom-自定义 os-操作系统 bigdata-大数据 mid-中间件 webserver-web服务器 cache-缓存 cn-云原生 network-网络监控等等 +category: service +# The monitoring type eg: linux windows tomcat mysql aws... +# 监控类型 eg: linux windows tomcat mysql aws... +app: smtp +# 监控类型国际化名称 +name: + zh-CN: SMTP监控 + en-US: SMTP MONITORS +# The description and help of this monitoring type +# 监控类型的帮助描述信息 +help: + zh-CN: HertzBeat 对 SMTP 服务的(Banner,helo响应)相关指标进行监测。 + en-US: HertzBeat monitors the banner and hello response related indicators of the SMTP service. + zh-TW: HertzBeat對SMTP服務的(Banner,helo響應)相關名額進行監測。 +# 监控所需输入参数定义(根据定义渲染页面UI) +# Input params define for monitoring(render web ui by the definition) +params: + # field-param field key + # field-字段名称标识符 + - field: host + # name-param field display i18n name + # name-参数字段显示名称 + name: + zh-CN: SMTP的Host + en-US: Target Host + # type-param field type(most mapping the html input type) + # type-字段类型,样式(大部分映射input标签type属性) + type: host + # required-true or false + # 是否是必输项 true-必填 false-可选 + required: true + # field-param field key + # field-字段名称标识符 + - field: port + # name-param field display i18n name + # name-参数字段显示名称 + name: + zh-CN: 端口 + en-US: Port + # type-param field type(most mapping the html input type) + # type-字段类型,样式(大部分映射input标签type属性) + type: number + # when type is number, range is required + # 当type为number时,用range表示范围 + range: '[0,65535]' + # required-true or false + # 是否是必输项 true-必填 false-可选 + required: true + # default value 25 + # 默认值 25 + defaultValue: 25 + # field-param field key + # field-字段名称标识符 + - field: timeout + # name-param field display i18n name + # name-参数字段显示名称 + name: + zh-CN: 连接超时时间(ms) + en-US: Connect Timeout(ms) + # type-param field type(most mapping the html input type) + # type-字段类型,样式(大部分映射input标签type属性) + type: number + # when type is number, range is required + # 当type为number时,用range表示范围 + range: '[0,100000]' + # required-true or false + # 是否是必输项 true-必填 false-可选 + required: true + # default value 6000 + # 默认值 6000 + defaultValue: 6000 + # field-param field key + # field-字段名称标识符 + - field: email + name: + zh-CN: email + en-US: email + type: text + # required-true or false + # 是否是必输项 true-必填 false-可选 + required: true +# collect metrics config list +# 采集指标配置列表 +metrics: + # metrics - summary + # 监控指标 - summary + - name: summary + i18n: + zh-CN: 概要 + en-US: Summary + # metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel + # priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue + # 指标采集调度优先级(0->127)->(优先级高->低) 优先级低的指标会等优先级高的指标采集完成后才会被调度, 相同优先级的指标会并行调度采集 + # 优先级为0的指标为可用性指标,即它会被首先调度,采集成功才会继续调度其它指标,采集失败则中断调度 + priority: 0 + # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 label-是否是指标标签字段 unit:指标单位 + # field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field + # field-指标名称, type-指标类型(0-number数字,1-string字符串), unit-指标单位('%','ms','MB'), label-是否是指标标签字段 + fields: + - field: responseTime + type: 0 + unit: ms + i18n: + zh-CN: 响应时间 + en-US: ResponseTime + - field: response + type: 1 + i18n: + zh-CN: 响应状态 + en-US: Response + - field: smtpBanner + type: 1 + i18n: + zh-CN: SMTP的Banner + en-US: SMTPBanner + - field: heloInfo + type: 1 + i18n: + zh-CN: Helo日志 + en-US: HeloInfo +# - field: emailMaxSize +# type: 0 +# unit: byte +# i18n: +# zh-CN: 最大邮件大小 +# en-US: EmailMaxSize +# - field: serverInfo +# type: 1 +# i18n: +# zh-CN: 服务器相关信息 +# en-US: ServerInfo +# - field: heloInfo +# type: 1 +# i18n: +# zh-CN: Helo日志 +# en-US: HeloInfo + + # the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk + # 采集协议, 目前支持sql, ssh, http, telnet, wmi, snmp, sdk + protocol: smtp + # Specific collection configuration when protocol is telnet protocol + # 当protocol为telnet协议时具体的采集配置 + smtp: + # telnet host + # 远程登录主机 + host: ^_^host^_^ + # port + # 端口 + port: ^_^port^_^ + # timeout + # 超时时间 + timeout: ^_^timeout^_^ + # email + # email(helo命令所需) + email: ^_^email^_^