From f9f661ef45c3f527af2e7e4f5edd9def1c782b85 Mon Sep 17 00:00:00 2001 From: xuezechao1 <15510726991@163.com> Date: Tue, 28 Mar 2023 15:51:18 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E4=B8=8A=E6=8A=A5entity?= =?UTF-8?q?=E5=92=8Cdao=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sermant-backend/pom.xml | 52 ++- .../huaweicloud/sermant/backend/Backend.java | 2 + .../backend/common/conf/BackendConfig.java | 174 +++++++ .../backend/common/conf/CommonConst.java | 105 +++++ .../sermant/backend/dao/DatabaseType.java | 31 ++ .../sermant/backend/dao/EventDao.java | 80 ++++ .../backend/dao/redis/EventDaoForRedis.java | 131 ++++++ .../backend/dao/redis/RedisClientImpl.java | 437 ++++++++++++++++++ .../dao/redis/RedisClusterClientImpl.java | 91 ++++ .../sermant/backend/entity/InstanceMeta.java | 2 +- .../entity/event/EventsRequestEntity.java | 73 +++ .../entity/event/QueryCacheSizeEntity.java | 39 ++ .../event/QueryResultEventInfoEntity.java | 63 +++ .../entity/heartbeat/HeartbeatMessage.java | 10 +- .../sermant/backend/util/StringUtils.java | 53 +++ ...loud.sermant.backend.webhook.WebHookClient | 2 + .../dao/redis/EventDaoForRedisTest.java | 154 ++++++ 17 files changed, 1484 insertions(+), 15 deletions(-) create mode 100644 sermant-backend/src/main/java/com/huaweicloud/sermant/backend/common/conf/BackendConfig.java create mode 100644 sermant-backend/src/main/java/com/huaweicloud/sermant/backend/common/conf/CommonConst.java create mode 100644 sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/DatabaseType.java create mode 100644 sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/EventDao.java create mode 100644 sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/EventDaoForRedis.java create mode 100644 sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/RedisClientImpl.java create mode 100644 sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/RedisClusterClientImpl.java create mode 100644 sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/EventsRequestEntity.java create mode 100644 sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/QueryCacheSizeEntity.java create mode 100644 sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/QueryResultEventInfoEntity.java create mode 100644 sermant-backend/src/main/java/com/huaweicloud/sermant/backend/util/StringUtils.java create mode 100644 sermant-backend/src/main/resources/META-INF/services/com.huaweicloud.sermant.backend.webhook.WebHookClient create mode 100644 sermant-backend/src/test/java/com/huaweicloud/sermant/backend/dao/redis/EventDaoForRedisTest.java diff --git a/sermant-backend/pom.xml b/sermant-backend/pom.xml index 5be25abe96..3678d9f7e2 100644 --- a/sermant-backend/pom.xml +++ b/sermant-backend/pom.xml @@ -18,6 +18,17 @@ 4.1.75.Final 2.5.8 1.12.1 + 3.9.1 + 1.18.12 + 1.2.83 + 2.6 + 4.13.1 + 5.6.2 + 2.28.2 + 1.7.32 + 3.9 + 4.3.1 + 2.0.9 ${project.basedir}/src/main/webapp/frontend @@ -85,50 +96,73 @@ com.google.protobuf protobuf-java - 3.9.1 + ${protobuf-java.version} org.projectlombok lombok - 1.18.12 + ${lombok.version} com.alibaba fastjson - 1.2.83 + ${fastjson.version} commons-lang commons-lang - 2.6 + ${commons-lang.version} junit junit - 4.13.1 + ${junit.version} test org.junit.jupiter junit-jupiter - 5.6.2 + ${junit-jupiter.version} test org.mockito mockito-core - 2.28.2 + ${mockito-core.version} test org.slf4j slf4j-api - 1.7.32 + ${slf4j-api.version} org.apache.commons commons-lang3 - 3.9 + ${commons-lang3.version} + + + redis.clients + jedis + ${jedis.version} + + + org.powermock + powermock-core + ${powermock.version} + test + + + org.powermock + powermock-api-mockito2 + ${powermock.version} + test + + + org.powermock + powermock-module-junit4 + ${powermock.version} + test diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/Backend.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/Backend.java index 30ae0d74ad..007928326f 100644 --- a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/Backend.java +++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/Backend.java @@ -19,6 +19,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; +import org.springframework.scheduling.annotation.EnableScheduling; /** * 启动类 @@ -29,6 +30,7 @@ */ @SpringBootApplication @ConfigurationPropertiesScan +@EnableScheduling public class Backend { public static void main(String[] args) { SpringApplication.run(Backend.class, args); diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/common/conf/BackendConfig.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/common/conf/BackendConfig.java new file mode 100644 index 0000000000..be0bb9e6f2 --- /dev/null +++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/common/conf/BackendConfig.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed 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. + */ + +package com.huaweicloud.sermant.backend.common.conf; + +import com.huaweicloud.sermant.backend.dao.DatabaseType; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +import java.util.Locale; + +/** + * 事件配置 + * + * @author xuezechao + * @since 2023-03-02 + */ +@Component +@Configuration +public class BackendConfig { + + /** + * 数据库类型 + */ + @Value("${database.type}") + private String database; + + /** + * 数据库地址 + */ + @Value("${database.address}") + private String url; + + @Value("${database.user}") + private String user; + + @Value("${database.password}") + private String password; + + @Value("${database.expire}") + private String expire; + + @Value("${webhook.eventpush.level}") + private String webhookPushEventThreshold; + + @Value("${database.version}") + private String version; + + @Value("${database.max.total}") + private String maxTotal; + + @Value("${database.max.idle}") + private String maxIdle; + + @Value("${database.timeout}") + private String timeout; + + @Value("${session.expire}") + private String sessionTimeout; + + @Value("${database.filter.thread.num}") + private String filterThreadNum; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public DatabaseType getDatabase() { + return DatabaseType.valueOf(database.toUpperCase(Locale.ROOT)); + } + + public void setDatabase(String db) { + this.database = db; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getPassword() { + return password; + } + + public String getExpire() { + return expire; + } + + public void setExpire(String expire) { + this.expire = expire; + } + + public void setWebhookPushEventThreshold(String webhookPushEventThreshold) { + this.webhookPushEventThreshold = webhookPushEventThreshold; + } + + public String getWebhookPushEventThreshold() { + return webhookPushEventThreshold; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getMaxIdle() { + return maxIdle; + } + + public void setMaxIdle(String maxIdle) { + this.maxIdle = maxIdle; + } + + public String getMaxTotal() { + return maxTotal; + } + + public void setMaxTotal(String maxTotal) { + this.maxTotal = maxTotal; + } + + public String getTimeout() { + return timeout; + } + + public void setTimeout(String timeout) { + this.timeout = timeout; + } + + public String getSessionTimeout() { + return sessionTimeout; + } + + public void setSessionTimeout(String sessionTimeout) { + this.sessionTimeout = sessionTimeout; + } + + public String getFilterThreadNum() { + return filterThreadNum; + } + + public void setFilterThreadNum(String filterThreadNum) { + this.filterThreadNum = filterThreadNum; + } +} diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/common/conf/CommonConst.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/common/conf/CommonConst.java new file mode 100644 index 0000000000..ecfd5dd71b --- /dev/null +++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/common/conf/CommonConst.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed 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. + */ + +package com.huaweicloud.sermant.backend.common.conf; + +/** + * 公共配置常量 + * + * @author xuezechao + * @since 2023-03-02 + */ +public class CommonConst { + + /** + * 钉钉webhook名称 + */ + public static final String DINGDING_WEBHOOK_NAME = "DingDing"; + + /** + * 钉钉webhook id + */ + public static final int DINGDING_WEBHOOK_ID = 1; + + /** + * 飞书webhook名称 + */ + public static final String FEISHU_WEBHOOK_NAME = "Feishu"; + + /** + * 飞书webhook id + */ + public static final int FEISHU_WEBHOOK_ID = 0; + + /** + * welink WEBHOOK名称 + */ + public static final String WELINK_WEBHOOK_NAME = "Welink"; + + /** + * welink WEBHOOK id + */ + public static final int WELINK_WEBHOOK_ID = 2; + + /** + * 默认redis地址 + */ + public static final String DEFAULT_REDIS_ADDRESS = "127.0.0.1"; + + /** + * 默认redis端口 + */ + public static final int DEFAULT_REDIS_PORT = 6379; + + /** + * redis 实例元数据key + */ + public static final String REDIS_HASH_KEY_OF_INSTANCE_META = "sermant_meta"; + + /** + * redis 事件key + */ + public static final String REDIS_EVENT_KEY = "sermant_events_hash"; + + /** + * redis 事件field集合的key + */ + public static final String REDIS_EVENT_FIELD_SET_KEY = "sermant_event_keyset"; + + /** + * redis 全匹配字符* + */ + public static final String FULL_MATCH_KEY = ".*"; + + /** + * 拼接redis key 字符 + */ + public static final String JOIN_REDIS_KEY = "_"; + + /** + * redis 地址切分字符 + */ + public static final String REDIS_ADDRESS_SPLIT = ":"; + + /** + * 前端页面事件每页展示默认数量 + */ + public static final int DEFAULT_PAGE_SIZE = 10; + + private CommonConst() { + + } +} diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/DatabaseType.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/DatabaseType.java new file mode 100644 index 0000000000..4b6cc18a59 --- /dev/null +++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/DatabaseType.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed 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. + */ + +package com.huaweicloud.sermant.backend.dao; + +/** + * 数据库类型 + * + * @since 2023-03-02 + * @author xuezechao + */ +public enum DatabaseType { + + /** + * redis 数据库 + */ + REDIS, +} diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/EventDao.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/EventDao.java new file mode 100644 index 0000000000..5292ff99b2 --- /dev/null +++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/EventDao.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed 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. + */ + +package com.huaweicloud.sermant.backend.dao; + +import com.huaweicloud.sermant.backend.entity.InstanceMeta; +import com.huaweicloud.sermant.backend.entity.event.Event; +import com.huaweicloud.sermant.backend.entity.event.EventsRequestEntity; +import com.huaweicloud.sermant.backend.entity.event.QueryCacheSizeEntity; +import com.huaweicloud.sermant.backend.entity.event.QueryResultEventInfoEntity; + +import java.util.List; + +/** + * 数据库接口 + * + * @author xuezechao + * @since 2023-03-02 + */ +public interface EventDao { + + /** + * 增加事件 + * + * @param event 事件 + * @return true/false + */ + boolean addEvent(Event event); + + /** + * 增加agent实例 + * + * @param agentInstanceMeta agent 实例 + * @return true/false + */ + boolean addInstanceMeta(InstanceMeta agentInstanceMeta); + + /** + * 事件查询 + * + * @param eventsRequestEntity 查询条件 + * @return 查询结果 + */ + List queryEvent(EventsRequestEntity eventsRequestEntity); + + /** + * 查询某页数据 + * + * @param sessionId id + * @param page 页码 + * @return 查询结果 + */ + List queryEventPage(String sessionId, int page); + + /** + * 获取查询结果数据量 + * + * @param eventsRequestEntity 查询参数 + * @return 查询结果数据量 + */ + QueryCacheSizeEntity getQueryCacheSize(EventsRequestEntity eventsRequestEntity); + + /** + * 清理过期事件 + */ + void cleanOverDueEventTimerTask(); +} diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/EventDaoForRedis.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/EventDaoForRedis.java new file mode 100644 index 0000000000..decb2dade3 --- /dev/null +++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/EventDaoForRedis.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed 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. + */ + +package com.huaweicloud.sermant.backend.dao.redis; + +import com.huaweicloud.sermant.backend.common.conf.BackendConfig; +import com.huaweicloud.sermant.backend.dao.EventDao; +import com.huaweicloud.sermant.backend.entity.InstanceMeta; +import com.huaweicloud.sermant.backend.entity.event.Event; +import com.huaweicloud.sermant.backend.entity.event.EventsRequestEntity; +import com.huaweicloud.sermant.backend.entity.event.QueryCacheSizeEntity; +import com.huaweicloud.sermant.backend.entity.event.QueryResultEventInfoEntity; + +import redis.clients.jedis.exceptions.JedisConnectionException; +import redis.clients.jedis.exceptions.JedisDataException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +/** + * redis数据库数据处理 + * + * @author xuezechao + * @since 2023-03-02 + */ +@Component +@EnableScheduling +public class EventDaoForRedis implements EventDao { + + private static final Logger LOGGER = LoggerFactory.getLogger(EventDaoForRedis.class); + + private EventDao jedis; + + /** + * 构造函数 + * + * @param backendConfig 配置 + */ + public EventDaoForRedis(BackendConfig backendConfig) { + try { + List redisAddress = Arrays.asList(backendConfig.getUrl().split(";")); + if (redisAddress.size() == 1) { + jedis = new RedisClientImpl(backendConfig); + } else if (redisAddress.size() > 1) { + jedis = new RedisClusterClientImpl(backendConfig); + } + } catch (JedisConnectionException | JedisDataException e) { + LOGGER.error(String.format(Locale.ROOT, "connect redis failed, error message: [%s]", e.getMessage())); + } + } + + /** + * 插入事件 + * + * @param event 事件 + * @return true/false + */ + @Override + public boolean addEvent(Event event) { + return jedis.addEvent(event); + } + + /** + * 插入agent实例 + * + * @param instanceMeta agent元数据 + * @return true/false + */ + @Override + public boolean addInstanceMeta(InstanceMeta instanceMeta) { + return jedis.addInstanceMeta(instanceMeta); + } + + /** + * 事件查询 + * + * @param eventsRequestEntity 查询条件 + * @return 查询结果 + */ + @Override + public List queryEvent(EventsRequestEntity eventsRequestEntity) { + return jedis.queryEvent(eventsRequestEntity); + } + + /** + * 获取某一页数据 + * + * @param page 页码 + * @return 查询数据 + */ + @Override + public List queryEventPage(String sessionId, int page) { + return jedis.queryEventPage(sessionId, page); + } + + /** + * 获取查询数据类型数量 + * + * @return 查询结果数量 + */ + @Override + public QueryCacheSizeEntity getQueryCacheSize(EventsRequestEntity eventsRequestEntity) { + return jedis.getQueryCacheSize(eventsRequestEntity); + } + + @Override + @Scheduled(fixedDelayString = "${database.fixedDelay}") + public void cleanOverDueEventTimerTask() { + jedis.cleanOverDueEventTimerTask(); + } +} diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/RedisClientImpl.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/RedisClientImpl.java new file mode 100644 index 0000000000..79b4503217 --- /dev/null +++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/RedisClientImpl.java @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed 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. + */ + +package com.huaweicloud.sermant.backend.dao.redis; + +import com.huaweicloud.sermant.backend.common.conf.CommonConst; +import com.huaweicloud.sermant.backend.common.conf.BackendConfig; +import com.huaweicloud.sermant.backend.dao.EventDao; +import com.huaweicloud.sermant.backend.entity.InstanceMeta; +import com.huaweicloud.sermant.backend.entity.event.Event; +import com.huaweicloud.sermant.backend.entity.event.EventLevel; +import com.huaweicloud.sermant.backend.entity.event.EventType; +import com.huaweicloud.sermant.backend.entity.event.EventsRequestEntity; +import com.huaweicloud.sermant.backend.entity.event.QueryCacheSizeEntity; +import com.huaweicloud.sermant.backend.entity.event.QueryResultEventInfoEntity; +import com.huaweicloud.sermant.backend.util.StringUtils; + +import com.alibaba.fastjson.JSONObject; + +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; +import redis.clients.jedis.params.ScanParams; +import redis.clients.jedis.resps.ScanResult; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.stream.Collectors; + +/** + * redis单机客户端 + * + * @author xuezechao + * @since 2023-03-02 + */ +public class RedisClientImpl implements EventDao { + + private static final int EVENT_LEVEL_INDEX = 3; + + private static final Logger LOGGER = LoggerFactory.getLogger(RedisClientImpl.class); + + private JedisPool jedisPool; + + private BackendConfig backendConfig; + + /** + * 构造redis 连接池 + * + * @param backendConfig 配置 + */ + public RedisClientImpl(BackendConfig backendConfig) { + this.backendConfig = backendConfig; + JedisPoolConfig config = new JedisPoolConfig(); + config.setMaxTotal(Integer.parseInt(backendConfig.getMaxTotal())); + config.setMaxIdle(Integer.parseInt(backendConfig.getMaxIdle())); + if (backendConfig.getVersion().compareTo("6.0") < 0) { + jedisPool = new JedisPool( + config, + Arrays.asList(backendConfig.getUrl().split(CommonConst.REDIS_ADDRESS_SPLIT)).get(0), + Integer.parseInt(Arrays.asList(backendConfig.getUrl().split(CommonConst.REDIS_ADDRESS_SPLIT)).get(1)), + Integer.parseInt(backendConfig.getTimeout()), + backendConfig.getPassword()); + } else { + jedisPool = new JedisPool( + config, + Arrays.asList(backendConfig.getUrl().split(CommonConst.REDIS_ADDRESS_SPLIT)).get(0), + Integer.parseInt(Arrays.asList(backendConfig.getUrl().split(CommonConst.REDIS_ADDRESS_SPLIT)).get(1)), + Integer.parseInt(backendConfig.getTimeout()), + backendConfig.getUser(), + backendConfig.getPassword()); + } + } + + @Override + public boolean addEvent(Event event) { + try (Jedis jedis = jedisPool.getResource()) { + String instanceMeta = jedis.hget(CommonConst.REDIS_HASH_KEY_OF_INSTANCE_META, event.getMetaHash()); + if (StringUtils.isEmpty(instanceMeta)) { + LOGGER.error("add event failed, event:{}, error message:[instance not exist]", event); + return false; + } + InstanceMeta agentInstanceMeta = JSONObject.parseObject(instanceMeta, InstanceMeta.class); + + // 获取事件字段 + String field = getEventField(agentInstanceMeta, event); + + // 检查是否有相同field + field = field + CommonConst.JOIN_REDIS_KEY + getSameFieldNum(field); + + // 写入事件 + jedis.hset(CommonConst.REDIS_EVENT_KEY, field, getEventStr(event, agentInstanceMeta)); + + // 写入类型索引 + jedis.zadd(CommonConst.REDIS_EVENT_FIELD_SET_KEY, event.getTime(), field); + if (event.getEventLevel().equals(EventLevel.EMERGENCY)) { + jedis.incrBy(EventLevel.EMERGENCY.toString(), 1); + } else if (event.getEventLevel().equals(EventLevel.IMPORTANT)) { + jedis.incrBy(EventLevel.IMPORTANT.toString(), 1); + } else { + jedis.incrBy(EventLevel.NORMAL.toString(), 1); + } + return true; + } catch (IllegalStateException e) { + LOGGER.error("add event failed, event:{}, error message:{}", event, e.getMessage()); + return false; + } + } + + /** + * 事件序列化 + * + * @param event 事件 + * @param agentInstanceMeta 事件归属的实例 + * @return 事件字符串 + */ + private String getEventStr(Event event, InstanceMeta agentInstanceMeta) { + QueryResultEventInfoEntity queryResultEventInfoEntity = new QueryResultEventInfoEntity(); + queryResultEventInfoEntity.setTime(event.getTime()); + queryResultEventInfoEntity.setScope(event.getScope()); + queryResultEventInfoEntity.setLevel(event.getEventLevel().toString().toUpperCase(Locale.ROOT)); + queryResultEventInfoEntity.setType(event.getEventType().getDescription()); + if (event.getEventType().getDescription().equals(EventType.LOG.getDescription())) { + queryResultEventInfoEntity.setInfo(event.getLogInfo()); + } else { + queryResultEventInfoEntity.setInfo(event.getEventInfo()); + } + HashMap meta = new HashMap<>(); + meta.put("service", agentInstanceMeta.getService()); + meta.put("ip", agentInstanceMeta.getNode().getIp()); + queryResultEventInfoEntity.setMeta(meta); + return JSONObject.toJSONString(queryResultEventInfoEntity); + } + + @Override + public boolean addInstanceMeta(InstanceMeta instanceMeta) { + try (Jedis jedis = jedisPool.getResource()) { + String meta = JSONObject.toJSONString(instanceMeta); + if (StringUtils.isEmpty( + jedis.hget(CommonConst.REDIS_HASH_KEY_OF_INSTANCE_META, + instanceMeta.getMetaHash()))) { + // 写入实例信息 + jedis.hset(CommonConst.REDIS_HASH_KEY_OF_INSTANCE_META, instanceMeta.getMetaHash(), meta); + } + return true; + } catch (IllegalStateException e) { + LOGGER.error("add instance meta failed, instance meta:{}, error message:{}", instanceMeta, e.getMessage()); + return false; + } + } + + /** + * 获取相同field 数量 + * + * @param field field + * @return 相同field 数量 + */ + private int getSameFieldNum(String field) { + int result = 0; + try (Jedis jedis = jedisPool.getResource()) { + ScanResult> firstScanResult = jedis.hscan( + CommonConst.REDIS_EVENT_KEY, + String.valueOf(0), + new ScanParams().match(field + "*")); + int cursor = Integer.parseInt(firstScanResult.getCursor()); + result += firstScanResult.getResult().size(); + while (cursor > 0) { + ScanResult> scanResult = jedis.hscan( + CommonConst.REDIS_EVENT_KEY, + String.valueOf(cursor), + new ScanParams().match(field + "*")); + cursor = Integer.parseInt(scanResult.getCursor()); + result += scanResult.getResult().size(); + } + return result; + } catch (IllegalStateException e) { + LOGGER.error( "query same field failed, field:{}, error message:{}", field, e.getMessage()); + return result; + } + } + + @Override + public List queryEvent(EventsRequestEntity eventsRequestEntity) { + String pattern = getPattern(eventsRequestEntity); + try (Jedis jedis = jedisPool.getResource()) { + List queryResultByTime = queryByTimeRange(CommonConst.REDIS_EVENT_FIELD_SET_KEY, + eventsRequestEntity.getStartTime(), + eventsRequestEntity.getEndTime()); + Collections.reverse(queryResultByTime); + queryResultByTime = filterQueryResult(queryResultByTime, pattern); + jedis.setex( + eventsRequestEntity.getSessionId(), + Integer.parseInt(backendConfig.getSessionTimeout()), + JSONObject.toJSONString(queryResultByTime)); + return queryEventPage(eventsRequestEntity.getSessionId(), 1); + } catch (IllegalStateException e) { + LOGGER.error("query event failed, error message:{}", e.getMessage()); + return new ArrayList<>(); + } + } + + /** + * 过滤查询结果 + * + * @param queryResultByTime 按时间查询结果 + * @param pattern 过滤规则 + * @return 过滤结果 + */ + private List filterQueryResult(List queryResultByTime, String pattern) { + List fanList = new ArrayList<>(); + if (queryResultByTime.size() <= 0) { + return fanList; + } + int threadSize = Integer.parseInt(backendConfig.getFilterThreadNum()); + int dataSize = queryResultByTime.size(); + int threadNum = 0; + if (dataSize % threadSize == 0) { + threadNum = dataSize / threadSize; + } else { + threadNum = dataSize / threadSize + 1; + } + ExecutorService exc = Executors.newFixedThreadPool(threadNum); + List>> tasks = new ArrayList<>(); + Callable> task = null; + List cutList = null; + for (int i = 0; i < threadNum; i++) { + + // 切割list + if (i == threadNum - 1) { + cutList = queryResultByTime.subList(i * threadSize, queryResultByTime.size()); + } else { + cutList = queryResultByTime.subList(i * threadSize, (i + 1) * threadSize); + } + + final List finalCutList = cutList; + task = new Callable>() { + @Override + public List call() throws Exception { + List newList = new ArrayList<>(); + for (String a : finalCutList) { + if (a.matches(pattern)) { + newList.add(a); + } + } + return newList; + } + }; + tasks.add(task); + } + + try { + List>> results = exc.invokeAll(tasks); + for (Future> result : results) { + fanList.addAll(result.get()); + } + } catch (InterruptedException | ExecutionException e) { + LOGGER.error("filter query result failed, error message:{}", e.getMessage()); + } + exc.shutdown(); + return fanList; + } + + /** + * 按时间范围查询事件 + * + * @param key 集合key + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 查询结果 + */ + public List queryByTimeRange(String key, long startTime, long endTime) { + try (Jedis jedis = jedisPool.getResource()) { + return jedis.zrangeByScore(key, startTime, endTime); + } catch (IllegalStateException e) { + LOGGER.error("query event by time failed, key:{}, startTime:{}, endTime:{}, error message:{}", + key, startTime, endTime, e.getMessage()); + return Collections.emptyList(); + } + } + + @Override + public List queryEventPage(String sessionId, int page) { + List result = new ArrayList<>(); + try (Jedis jedis = jedisPool.getResource()) { + String events = jedis.get(sessionId); + if (!StringUtils.isEmpty(events)) { + List keyList = JSONObject.parseArray(events, String.class); + int startIndex = (page - 1) * CommonConst.DEFAULT_PAGE_SIZE; + int endIndex = Math.min(startIndex + CommonConst.DEFAULT_PAGE_SIZE, keyList.size()); + for (String key : keyList.subList(startIndex, endIndex)) { + result.add(JSONObject.parseObject( + jedis.hget(CommonConst.REDIS_EVENT_KEY, key), + QueryResultEventInfoEntity.class)); + } + } + return result; + } catch (IllegalStateException e) { + LOGGER.error("query event by page failed, sessionId:{}, error message:{}", + sessionId, e.getMessage()); + return result; + } + } + + @Override + public QueryCacheSizeEntity getQueryCacheSize(EventsRequestEntity eventsRequestEntity) { + QueryCacheSizeEntity queryCacheSize = new QueryCacheSizeEntity(); + try (Jedis jedis = jedisPool.getResource()) { + queryCacheSize.setEmergencyNum(StringUtils.filterStr(jedis.get(EventLevel.EMERGENCY.toString()))); + queryCacheSize.setImportantNum(StringUtils.filterStr(jedis.get(EventLevel.IMPORTANT.toString()))); + queryCacheSize.setNormalNum(StringUtils.filterStr(jedis.get(EventLevel.NORMAL.toString()))); + queryCacheSize.setTotal(queryCacheSize.getEmergencyNum() + + queryCacheSize.getImportantNum() + queryCacheSize.getNormalNum()); + return queryCacheSize; + } catch (IllegalStateException e) { + LOGGER.error("query event size failed, sessionId:{}, error message:{}", + eventsRequestEntity.getSessionId(), e.getMessage()); + return queryCacheSize; + } + } + + /** + * 获取事件field + * + * @param agentInstanceMeta agent实例 + * @param event 事件 + * @return 事件对应field + */ + public String getEventField(InstanceMeta agentInstanceMeta, Event event) { + String field = String.join(CommonConst.JOIN_REDIS_KEY, + getField(agentInstanceMeta.getService()), + getField(agentInstanceMeta.getNode().getIp()), + getField(String.valueOf(event.getEventType().getDescription()).toLowerCase(Locale.ROOT)), + getField(event.getEventLevel().toString().toLowerCase(Locale.ROOT)), + getField(event.getScope()), + getField(agentInstanceMeta.getInstanceId()), + agentInstanceMeta.getCluster() != null ? getField(agentInstanceMeta.getCluster().getCluster()) : "", + agentInstanceMeta.getEnvironment() != null ? getField(agentInstanceMeta.getEnvironment().getEnv()) : "", + getField(agentInstanceMeta.getAz()), + getField(event.getMetaHash()), + getField(String.valueOf(event.getTime()))); + return field; + } + + private String getField(String str) { + return !StringUtils.isEmpty(str) ? str : ""; + } + + /** + * 拼接查询条件 + * + * @param event 查询条件 + * @return 事件查询模版 + */ + private String getPattern(EventsRequestEntity event) { + List patterns = new ArrayList<>(); + patterns.add(getListPattern(event.getService())); + patterns.add(getListPattern(event.getIp())); + patterns.add(getListPattern(event.getType() == null ? new ArrayList<>() : event.getType())); + patterns.add(getListPattern(event.getLevel() == null ? new ArrayList<>() : event.getLevel())); + patterns.add(getListPattern(event.getScope())); + patterns.add(CommonConst.FULL_MATCH_KEY); + return patterns.stream().map(String::valueOf).collect(Collectors.joining(CommonConst.JOIN_REDIS_KEY)); + } + + private String getListPattern(List strings) { + return strings.size() == 0 ? CommonConst.FULL_MATCH_KEY : "(" + String.join("|", strings) + ")"; + } + + /** + * 定时任务,清理过期数据 + */ + public void cleanOverDueEventTimerTask() { + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.SECOND, -Integer.parseInt(backendConfig.getExpire())); + List needCleanEvent = queryByTimeRange( + CommonConst.REDIS_EVENT_FIELD_SET_KEY, 0, calendar.getTimeInMillis()); + try (Jedis jedis = jedisPool.getResource()) { + jedis.hdel(CommonConst.REDIS_EVENT_KEY, needCleanEvent.toArray(new String[0])); + jedis.zrem(CommonConst.REDIS_EVENT_FIELD_SET_KEY, needCleanEvent.toArray(new String[0])); + cleanOverDueEventLevel(jedis, needCleanEvent); + } catch (IllegalStateException e) { + LOGGER.error("delete over dur event failed, error message:{}", e.getMessage()); + } + } + + /** + * 删除过期事件同步设置事件级别数量 + * + * @param jedis redis client + * @param needCleanEvent 需要删除的事件key + */ + private void cleanOverDueEventLevel(Jedis jedis, List needCleanEvent) { + for (String key : needCleanEvent) { + EventLevel level = EventLevel.valueOf( + key.split(CommonConst.JOIN_REDIS_KEY)[EVENT_LEVEL_INDEX].toUpperCase(Locale.ROOT)); + switch (level) { + case EMERGENCY: + jedis.decrBy(EventLevel.EMERGENCY.toString(), -1); + break; + case IMPORTANT: + jedis.decrBy(EventLevel.IMPORTANT.toString(), -1); + break; + case NORMAL: + jedis.decrBy(EventLevel.NORMAL.toString(), -1); + break; + default: + break; + } + } + } +} diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/RedisClusterClientImpl.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/RedisClusterClientImpl.java new file mode 100644 index 0000000000..1f3bc9ce50 --- /dev/null +++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/RedisClusterClientImpl.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed 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. + */ + +package com.huaweicloud.sermant.backend.dao.redis; + +import com.huaweicloud.sermant.backend.common.conf.BackendConfig; +import com.huaweicloud.sermant.backend.dao.EventDao; +import com.huaweicloud.sermant.backend.entity.InstanceMeta; +import com.huaweicloud.sermant.backend.entity.event.Event; +import com.huaweicloud.sermant.backend.entity.event.EventsRequestEntity; +import com.huaweicloud.sermant.backend.entity.event.QueryCacheSizeEntity; +import com.huaweicloud.sermant.backend.entity.event.QueryResultEventInfoEntity; + +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.JedisCluster; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * redis集群客户端 + * + * @author xuezechao + * @since 2023-03-02 + */ +public class RedisClusterClientImpl implements EventDao { + + private JedisCluster jedisCluster; + + /** + * redis 集群 + * + * @param backendConfig 事件配置 + */ + public RedisClusterClientImpl(BackendConfig backendConfig) { + Set nodes = new HashSet<>(); + String[] addressList = backendConfig.getUrl().split(";"); + for (String address : addressList) { + nodes.add(new HostAndPort( + Arrays.asList(address.split(":")).get(0), + Integer.parseInt(Arrays.asList(address.split(":")).get(1)))); + } + jedisCluster = new JedisCluster(nodes, backendConfig.getUser(), backendConfig.getPassword()); + } + + @Override + public boolean addEvent(Event event) { + return false; + } + + @Override + public boolean addInstanceMeta(InstanceMeta agentInstanceMeta) { + return false; + } + + @Override + public List queryEvent(EventsRequestEntity eventsRequestEntity) { + return Collections.emptyList(); + } + + @Override + public List queryEventPage(String sessionId, int page) { + return Collections.emptyList(); + } + + @Override + public QueryCacheSizeEntity getQueryCacheSize(EventsRequestEntity eventsRequestEntity) { + return new QueryCacheSizeEntity(); + } + + @Override + public void cleanOverDueEventTimerTask() { + + } +} diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/InstanceMeta.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/InstanceMeta.java index a21ad06766..84430a41b3 100644 --- a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/InstanceMeta.java +++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/InstanceMeta.java @@ -42,7 +42,7 @@ public class InstanceMeta { /** * 应用 */ - private String application; + private String service; /** * 节点 diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/EventsRequestEntity.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/EventsRequestEntity.java new file mode 100644 index 0000000000..9e48c58754 --- /dev/null +++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/EventsRequestEntity.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed 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. + */ + +package com.huaweicloud.sermant.backend.entity.event; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * 事件查询请求实体 + * + * @since 2023-03-02 + * @author xuezechao + */ +@Getter +@Setter +public class EventsRequestEntity { + + /** + * 应用名 + */ + private List service; + + /** + * 地址 + */ + private List ip; + + /** + * 范围 + */ + private List scope; + + /** + * 类型 + */ + private List type; + + /** + * 级别 + */ + private List level; + + /** + * 开始时间 + */ + private long startTime; + + /** + * 截止时间 + */ + private long endTime; + + /** + * session id + */ + private String sessionId; +} diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/QueryCacheSizeEntity.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/QueryCacheSizeEntity.java new file mode 100644 index 0000000000..78c7d31efb --- /dev/null +++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/QueryCacheSizeEntity.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed 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. + */ + +package com.huaweicloud.sermant.backend.entity.event; + +import lombok.Getter; +import lombok.Setter; + +/** + * 事件查询数量实体 + * + * @author xuezechao + * @since 2023-03-02 + */ +@Getter +@Setter +public class QueryCacheSizeEntity { + + int emergencyNum; + + int importantNum; + + int normalNum; + + int total; +} diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/QueryResultEventInfoEntity.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/QueryResultEventInfoEntity.java new file mode 100644 index 0000000000..8249c9324b --- /dev/null +++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/QueryResultEventInfoEntity.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed 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. + */ + +package com.huaweicloud.sermant.backend.entity.event; + +import lombok.Getter; +import lombok.Setter; + +import java.util.HashMap; + +/** + * 查询事件结果实体 + * + * @author xuezechao + * @since 2023-03-02 + */ +@Getter +@Setter +public class QueryResultEventInfoEntity { + + /** + * 元数据 + */ + HashMap meta; + + /** + * 事件上报事件 + */ + private long time; + + /** + * 事件范围 + */ + private String scope; + + /** + * 事件级别 + */ + private String level; + + /** + * 事件类型 + */ + private String type; + + /** + * 事件信息 + */ + private Object info; +} diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/heartbeat/HeartbeatMessage.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/heartbeat/HeartbeatMessage.java index c2d386bdfe..ddf9e7bc4c 100644 --- a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/heartbeat/HeartbeatMessage.java +++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/heartbeat/HeartbeatMessage.java @@ -31,7 +31,7 @@ * @since 2022-03-19 */ public class HeartbeatMessage { - private String appName; + private String service; private String appType; @@ -70,12 +70,12 @@ public void setIp(List ip) { this.ip = ip; } - public String getAppName() { - return appName; + public String getService() { + return service; } - public void setAppName(String appName) { - this.appName = appName; + public void setService(String service) { + this.service = service; } public String getAppType() { diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/util/StringUtils.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/util/StringUtils.java new file mode 100644 index 0000000000..fcbf4e4214 --- /dev/null +++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/util/StringUtils.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed 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. + */ + +package com.huaweicloud.sermant.backend.util; + +/** + * 字符串工具类 + * + * @author xuezechao + * @since 2023-03-02 + */ +public class StringUtils { + + /** + * 构造函数 + */ + private StringUtils() { + + } + + /** + * 字符串判空 + * + * @param val 字符串 + * @return 是否为空 + */ + public static boolean isEmpty(String val) { + return val == null || "".equals(val.trim()); + } + + /** + * 字符串转int 空返回0 + * + * @param val 字符串 + * @return 字符串转int值 + */ + public static int filterStr(String val) { + return val == null ? 0 : Integer.parseInt(val); + } +} diff --git a/sermant-backend/src/main/resources/META-INF/services/com.huaweicloud.sermant.backend.webhook.WebHookClient b/sermant-backend/src/main/resources/META-INF/services/com.huaweicloud.sermant.backend.webhook.WebHookClient new file mode 100644 index 0000000000..e25ff4830c --- /dev/null +++ b/sermant-backend/src/main/resources/META-INF/services/com.huaweicloud.sermant.backend.webhook.WebHookClient @@ -0,0 +1,2 @@ +com.huaweicloud.sermant.backend.webhook.feishu.FeiShuHookClient +com.huaweicloud.sermant.backend.webhook.dingding.DingDingHookClient \ No newline at end of file diff --git a/sermant-backend/src/test/java/com/huaweicloud/sermant/backend/dao/redis/EventDaoForRedisTest.java b/sermant-backend/src/test/java/com/huaweicloud/sermant/backend/dao/redis/EventDaoForRedisTest.java new file mode 100644 index 0000000000..fa15b1a19b --- /dev/null +++ b/sermant-backend/src/test/java/com/huaweicloud/sermant/backend/dao/redis/EventDaoForRedisTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed 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. + */ + +package com.huaweicloud.sermant.backend.dao.redis; + +import com.huaweicloud.sermant.backend.common.conf.CommonConst; +import com.huaweicloud.sermant.backend.common.conf.BackendConfig; +import com.huaweicloud.sermant.backend.entity.ClusterEntity; +import com.huaweicloud.sermant.backend.entity.EnvironmentEntity; +import com.huaweicloud.sermant.backend.entity.InstanceMeta; +import com.huaweicloud.sermant.backend.entity.NodeEntity; +import com.huaweicloud.sermant.backend.entity.event.Event; +import com.huaweicloud.sermant.backend.entity.event.EventInfo; +import com.huaweicloud.sermant.backend.entity.event.EventLevel; +import com.huaweicloud.sermant.backend.entity.event.EventType; +import com.huaweicloud.sermant.backend.entity.event.EventsRequestEntity; +import com.huaweicloud.sermant.backend.entity.event.LogInfo; + +import com.alibaba.fastjson.JSONObject; + +import redis.clients.jedis.Jedis; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.util.Date; + +/** + * EventDaoForRedis Tester. + * + * @author xuezechao + * @since 2023-03-14 + */ +@PrepareForTest(EventDaoForRedis.class) +@RunWith(PowerMockRunner.class) +public class EventDaoForRedisTest { + BackendConfig backendConfig = new BackendConfig(); + Jedis jedis; + Event event = new Event(); + InstanceMeta instanceMeta = new InstanceMeta(); + EventDaoForRedis eventDaoForRedis; + EventsRequestEntity eventsRequestEntity = new EventsRequestEntity(); + + @Before + public void before() throws Exception { + backendConfig.setUrl(""); + backendConfig.setPassword(""); + jedis = Mockito.mock(Jedis.class); + PowerMockito.whenNew(Jedis.class).withAnyArguments().thenReturn(jedis); + eventDaoForRedis = new EventDaoForRedis(backendConfig); + eventsRequestEntity.setStartTime(new Date().getTime()); + + event.setTime(new Date().getTime()); + event.setScope("scope"); + event.setMetaHash("hashMeat"); + event.setEventLevel(EventLevel.NORMAL); + + instanceMeta.setInstanceId("instanceId"); + instanceMeta.setService("application"); + instanceMeta.setAz("az"); + NodeEntity nodeEntity = new NodeEntity(); + nodeEntity.setIp("127.0.0.1"); + instanceMeta.setNode(nodeEntity); + EnvironmentEntity environmentEntity = new EnvironmentEntity(); + environmentEntity.setEnv("env"); + instanceMeta.setEnvironment(environmentEntity); + ClusterEntity clusterEntity = new ClusterEntity(); + clusterEntity.setCluster("cluster"); + instanceMeta.setCluster(clusterEntity); + + eventsRequestEntity.setEndTime(new Date().getTime()); + } + + @After + public void after() { + jedis.close(); + } + + @Test + public void testOtherType() { + event.setEventType(EventType.OPERATION); + EventInfo eventInfo = new EventInfo(); + eventInfo.setName("name"); + eventInfo.setDescription("description"); + event.setEventInfo(eventInfo); + setReturn(getEventField(instanceMeta, event)); + + eventDaoForRedis.addInstanceMeta(instanceMeta); + Assert.assertTrue(eventDaoForRedis.addInstanceMeta(instanceMeta)); + Assert.assertTrue(eventDaoForRedis.addEvent(event)); + } + + @Test + public void testLogType() { + event.setEventType(EventType.LOG); + LogInfo logInfo = new LogInfo(); + logInfo.setLogLevel("logLevel"); + logInfo.setLogClass("logClass"); + logInfo.setLogMessage("logMessage"); + logInfo.setLogThreadId(1); + logInfo.setLogMethod("logMethod"); + logInfo.setThrowable(new Throwable()); + event.setLogInfo(logInfo); + setReturn(getEventField(instanceMeta, event)); + + Assert.assertTrue(eventDaoForRedis.addInstanceMeta(instanceMeta)); + Assert.assertTrue(eventDaoForRedis.addEvent(event)); + } + + public String getEventField(InstanceMeta instanceMeta, Event event) { + return String.join(CommonConst.JOIN_REDIS_KEY, + instanceMeta.getInstanceId(), + instanceMeta.getService(), + instanceMeta.getNode().getIp(), + instanceMeta.getCluster().getCluster(), + instanceMeta.getEnvironment().getEnv(), + instanceMeta.getAz(), + event.getMetaHash(), + String.valueOf(event.getEventType().getType()), + event.getScope(), + String.valueOf(event.getTime())); + } + + public void setReturn(String eventField) { + Mockito.doReturn(1L).when(jedis).hset(CommonConst.REDIS_EVENT_KEY, eventField, JSONObject.toJSONString(event)); + Mockito.doReturn(1L).when(jedis).zadd(CommonConst.REDIS_EVENT_FIELD_SET_KEY, event.getTime(), eventField); + Mockito.doReturn(1L).when(jedis).hset(CommonConst.REDIS_HASH_KEY_OF_INSTANCE_META, + instanceMeta.getMetaHash(), JSONObject.toJSONString(instanceMeta)); + Mockito.doReturn(1L).when(jedis).hdel(CommonConst.REDIS_EVENT_KEY, eventField); + Mockito.doReturn(1L).when(jedis).zrem(CommonConst.REDIS_EVENT_FIELD_SET_KEY, eventField); + Mockito.doReturn(1L).when(jedis).hdel(CommonConst.REDIS_HASH_KEY_OF_INSTANCE_META, instanceMeta.getMetaHash()); + Mockito.doReturn(JSONObject.toJSONString(instanceMeta)).when(jedis).hget(CommonConst.REDIS_HASH_KEY_OF_INSTANCE_META, event.getMetaHash()); + } +}