From 643f52f3abd4ff84176d6e08642eed4f6ba47686 Mon Sep 17 00:00:00 2001 From: "pengchao.liu" Date: Fri, 21 Aug 2026 11:08:26 +0800 Subject: [PATCH] [network]: correct policy schedule behavior Expired schedules could not be rebound, and API responses could only report whether a future active minute remained. This prevented callers from distinguishing current effectiveness from final expiration. Allow Set to bind owned schedules regardless of expiry, expose the current effective state, and align description limits with storage. Resolves: ZSTAC-87802 Change-Id: I5c69ca4ef0d9c1435946a613badf70f39b189fdc --- conf/db/upgrade/V5.5.38__schema.sql | 2 +- ...ateNetworkSecurityPolicyScheduleEvent.java | 7 ++ ...reateNetworkSecurityPolicyScheduleMsg.java | 21 +++++- ...eteNetworkSecurityPolicyScheduleEvent.java | 4 ++ ...eleteNetworkSecurityPolicyScheduleMsg.java | 7 ++ ...PIGetNetworkSecurityPolicyScheduleMsg.java | 7 ++ ...kSecurityPolicyScheduleMsgDoc_zh_cn.groovy | 34 +++++++++- ...GetNetworkSecurityPolicyScheduleReply.java | 9 +++ ...SetNetworkSecurityPolicyScheduleEvent.java | 4 ++ ...PISetNetworkSecurityPolicyScheduleMsg.java | 10 +++ ...ateNetworkSecurityPolicyScheduleEvent.java | 7 ++ ...pdateNetworkSecurityPolicyScheduleMsg.java | 18 ++++- ...kSecurityPolicyScheduleApiInterceptor.java | 18 +---- ...etworkSecurityPolicyScheduleInventory.java | 36 +++++++++- ...ityPolicyScheduleInventoryDoc_zh_cn.groovy | 6 ++ ...workSecurityPolicyScheduleManagerImpl.java | 9 --- .../NetworkSecurityPolicyScheduleVO.java | 2 +- ...teNetworkSecurityPolicyScheduleAction.java | 2 +- ...etworkSecurityPolicyScheduleInventory.java | 8 +++ ...teNetworkSecurityPolicyScheduleAction.java | 2 +- ...etworkSecurityPolicyScheduleApiCase.groovy | 50 +++++++++++--- ...ecurityGroupScheduledActivationCase.groovy | 66 +++++++++++++++++++ .../java/org/zstack/testlib/ApiHelper.groovy | 62 ++++++++--------- 23 files changed, 316 insertions(+), 75 deletions(-) diff --git a/conf/db/upgrade/V5.5.38__schema.sql b/conf/db/upgrade/V5.5.38__schema.sql index 64c997dc430..16f092dee8a 100644 --- a/conf/db/upgrade/V5.5.38__schema.sql +++ b/conf/db/upgrade/V5.5.38__schema.sql @@ -1,7 +1,7 @@ CREATE TABLE IF NOT EXISTS `NetworkSecurityPolicyScheduleVO` ( `uuid` varchar(32) NOT NULL, `name` varchar(255) NOT NULL, - `description` varchar(2048) DEFAULT NULL, + `description` varchar(255) DEFAULT NULL, `resourceType` varchar(32) NOT NULL, `resourceUuid` varchar(32) NOT NULL, `timeType` varchar(32) NOT NULL, diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APICreateNetworkSecurityPolicyScheduleEvent.java b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APICreateNetworkSecurityPolicyScheduleEvent.java index a5f3d8e309e..401abce2850 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APICreateNetworkSecurityPolicyScheduleEvent.java +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APICreateNetworkSecurityPolicyScheduleEvent.java @@ -21,4 +21,11 @@ public NetworkSecurityPolicyScheduleInventory getInventory() { public void setInventory(NetworkSecurityPolicyScheduleInventory inventory) { this.inventory = inventory; } + + public static APICreateNetworkSecurityPolicyScheduleEvent __example__() { + APICreateNetworkSecurityPolicyScheduleEvent event = + new APICreateNetworkSecurityPolicyScheduleEvent(); + event.setInventory(NetworkSecurityPolicyScheduleInventory.__example__()); + return event; + } } diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APICreateNetworkSecurityPolicyScheduleMsg.java b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APICreateNetworkSecurityPolicyScheduleMsg.java index bab05b803cf..c004ac3c130 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APICreateNetworkSecurityPolicyScheduleMsg.java +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APICreateNetworkSecurityPolicyScheduleMsg.java @@ -7,6 +7,7 @@ import org.zstack.header.rest.RestRequest; import org.zstack.header.vo.ResourceVO; +import java.util.Arrays; import java.util.List; @Action(category = NetworkSecurityPolicyScheduleConstant.ACTION_CATEGORY) @@ -20,7 +21,7 @@ public class APICreateNetworkSecurityPolicyScheduleMsg extends APIMessage { @APIParam(maxLength = 255, emptyString = false) private String name; - @APIParam(required = false, maxLength = 2048) + @APIParam(required = false, maxLength = 255) private String description; @APIParam(validValues = { @@ -140,4 +141,22 @@ public List getWeekDays() { public void setWeekDays(List weekDays) { this.weekDays = weekDays; } + + public static APICreateNetworkSecurityPolicyScheduleMsg __example__() { + APICreateNetworkSecurityPolicyScheduleMsg msg = + new APICreateNetworkSecurityPolicyScheduleMsg(); + msg.setName("office-hours"); + msg.setDescription("Weekday office hours"); + msg.setResourceType( + NetworkSecurityPolicyScheduleConstant.SECURITY_GROUP_RESOURCE_TYPE); + msg.setResourceUuid(uuid()); + msg.setTimeType("UTC"); + msg.setRepeatType("Weekly"); + msg.setStartDate("2026-01-01"); + msg.setEndDate("2026-12-31"); + msg.setStartTime("09:00"); + msg.setEndTime("18:00"); + msg.setWeekDays(Arrays.asList(1, 2, 3, 4, 5)); + return msg; + } } diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIDeleteNetworkSecurityPolicyScheduleEvent.java b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIDeleteNetworkSecurityPolicyScheduleEvent.java index c61e31fa001..0bce7d8b8ab 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIDeleteNetworkSecurityPolicyScheduleEvent.java +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIDeleteNetworkSecurityPolicyScheduleEvent.java @@ -11,4 +11,8 @@ public APIDeleteNetworkSecurityPolicyScheduleEvent() { public APIDeleteNetworkSecurityPolicyScheduleEvent(String apiId) { super(apiId); } + + public static APIDeleteNetworkSecurityPolicyScheduleEvent __example__() { + return new APIDeleteNetworkSecurityPolicyScheduleEvent(); + } } diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIDeleteNetworkSecurityPolicyScheduleMsg.java b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIDeleteNetworkSecurityPolicyScheduleMsg.java index c06bff359af..9aa9ffcf932 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIDeleteNetworkSecurityPolicyScheduleMsg.java +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIDeleteNetworkSecurityPolicyScheduleMsg.java @@ -26,4 +26,11 @@ public String getUuid() { public void setUuid(String uuid) { this.uuid = uuid; } + + public static APIDeleteNetworkSecurityPolicyScheduleMsg __example__() { + APIDeleteNetworkSecurityPolicyScheduleMsg msg = + new APIDeleteNetworkSecurityPolicyScheduleMsg(); + msg.setUuid(uuid()); + return msg; + } } diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIGetNetworkSecurityPolicyScheduleMsg.java b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIGetNetworkSecurityPolicyScheduleMsg.java index ba7e0a342b8..a6d0121dd04 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIGetNetworkSecurityPolicyScheduleMsg.java +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIGetNetworkSecurityPolicyScheduleMsg.java @@ -24,4 +24,11 @@ public String getResourceUuid() { public void setResourceUuid(String resourceUuid) { this.resourceUuid = resourceUuid; } + + public static APIGetNetworkSecurityPolicyScheduleMsg __example__() { + APIGetNetworkSecurityPolicyScheduleMsg msg = + new APIGetNetworkSecurityPolicyScheduleMsg(); + msg.setResourceUuid(uuid()); + return msg; + } } diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIGetNetworkSecurityPolicyScheduleMsgDoc_zh_cn.groovy b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIGetNetworkSecurityPolicyScheduleMsgDoc_zh_cn.groovy index d73bfaab49c..a7dfed4fcfd 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIGetNetworkSecurityPolicyScheduleMsgDoc_zh_cn.groovy +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIGetNetworkSecurityPolicyScheduleMsgDoc_zh_cn.groovy @@ -1,7 +1,6 @@ package org.zstack.networksecuritypolicyschedule import org.zstack.networksecuritypolicyschedule.APIGetNetworkSecurityPolicyScheduleReply -import org.zstack.header.message.APISyncCallMessage doc { title "GetNetworkSecurityPolicySchedule" @@ -18,9 +17,38 @@ doc { clz APIGetNetworkSecurityPolicyScheduleMsg.class - desc """null""" + desc """""" - params APISyncCallMessage.class + params { + + column { + name "resourceUuid" + enclosedIn "" + desc "资源UUID" + location "query" + type "String" + optional false + since "5.5.38" + } + column { + name "systemTags" + enclosedIn "" + desc "系统标签" + location "query" + type "List" + optional true + since "5.5.38" + } + column { + name "userTags" + enclosedIn "" + desc "用户标签" + location "query" + type "List" + optional true + since "5.5.38" + } + } } response { diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIGetNetworkSecurityPolicyScheduleReply.java b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIGetNetworkSecurityPolicyScheduleReply.java index ba9c80c04f0..f3e21a458e1 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIGetNetworkSecurityPolicyScheduleReply.java +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIGetNetworkSecurityPolicyScheduleReply.java @@ -3,6 +3,7 @@ import org.zstack.header.message.APIReply; import org.zstack.header.rest.RestResponse; +import java.util.Collections; import java.util.List; @RestResponse(allTo = "inventories") @@ -16,4 +17,12 @@ public List getInventories() { public void setInventories(List inventories) { this.inventories = inventories; } + + public static APIGetNetworkSecurityPolicyScheduleReply __example__() { + APIGetNetworkSecurityPolicyScheduleReply reply = + new APIGetNetworkSecurityPolicyScheduleReply(); + reply.setInventories(Collections.singletonList( + NetworkSecurityPolicyScheduleInventory.__example__())); + return reply; + } } diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APISetNetworkSecurityPolicyScheduleEvent.java b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APISetNetworkSecurityPolicyScheduleEvent.java index 54ed1548e24..3b4c6f6ac06 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APISetNetworkSecurityPolicyScheduleEvent.java +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APISetNetworkSecurityPolicyScheduleEvent.java @@ -11,4 +11,8 @@ public APISetNetworkSecurityPolicyScheduleEvent() { public APISetNetworkSecurityPolicyScheduleEvent(String apiId) { super(apiId); } + + public static APISetNetworkSecurityPolicyScheduleEvent __example__() { + return new APISetNetworkSecurityPolicyScheduleEvent(); + } } diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APISetNetworkSecurityPolicyScheduleMsg.java b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APISetNetworkSecurityPolicyScheduleMsg.java index ddf12a2d334..9c8e235aa3a 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APISetNetworkSecurityPolicyScheduleMsg.java +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APISetNetworkSecurityPolicyScheduleMsg.java @@ -50,4 +50,14 @@ public String getResourceUuid() { public void setResourceUuid(String resourceUuid) { this.resourceUuid = resourceUuid; } + + public static APISetNetworkSecurityPolicyScheduleMsg __example__() { + APISetNetworkSecurityPolicyScheduleMsg msg = + new APISetNetworkSecurityPolicyScheduleMsg(); + msg.setScheduleUuid(uuid()); + msg.setResourceType( + NetworkSecurityPolicyScheduleConstant.SECURITY_GROUP_RESOURCE_TYPE); + msg.setResourceUuid(uuid()); + return msg; + } } diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIUpdateNetworkSecurityPolicyScheduleEvent.java b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIUpdateNetworkSecurityPolicyScheduleEvent.java index fdf1ad3e1f0..e2d569e2885 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIUpdateNetworkSecurityPolicyScheduleEvent.java +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIUpdateNetworkSecurityPolicyScheduleEvent.java @@ -21,4 +21,11 @@ public NetworkSecurityPolicyScheduleInventory getInventory() { public void setInventory(NetworkSecurityPolicyScheduleInventory inventory) { this.inventory = inventory; } + + public static APIUpdateNetworkSecurityPolicyScheduleEvent __example__() { + APIUpdateNetworkSecurityPolicyScheduleEvent event = + new APIUpdateNetworkSecurityPolicyScheduleEvent(); + event.setInventory(NetworkSecurityPolicyScheduleInventory.__example__()); + return event; + } } diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIUpdateNetworkSecurityPolicyScheduleMsg.java b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIUpdateNetworkSecurityPolicyScheduleMsg.java index 332b2992c4f..12efd58a538 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIUpdateNetworkSecurityPolicyScheduleMsg.java +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/APIUpdateNetworkSecurityPolicyScheduleMsg.java @@ -6,6 +6,7 @@ import org.zstack.header.message.APIParam; import org.zstack.header.rest.RestRequest; +import java.util.Arrays; import java.util.List; @Action(category = NetworkSecurityPolicyScheduleConstant.ACTION_CATEGORY) @@ -22,7 +23,7 @@ public class APIUpdateNetworkSecurityPolicyScheduleMsg extends APIMessage { @APIParam(maxLength = 255, emptyString = false) private String name; - @APIParam(required = false, maxLength = 2048) + @APIParam(required = false, maxLength = 255) private String description; @APIParam(validValues = {"Local", "UTC"}) @@ -126,4 +127,19 @@ public void setWeekDays(List weekDays) { this.weekDays = weekDays; } + public static APIUpdateNetworkSecurityPolicyScheduleMsg __example__() { + APIUpdateNetworkSecurityPolicyScheduleMsg msg = + new APIUpdateNetworkSecurityPolicyScheduleMsg(); + msg.setUuid(uuid()); + msg.setName("office-hours"); + msg.setDescription("Weekday office hours"); + msg.setTimeType("UTC"); + msg.setRepeatType("Weekly"); + msg.setStartDate("2026-01-01"); + msg.setEndDate("2026-12-31"); + msg.setStartTime("09:00"); + msg.setEndTime("18:00"); + msg.setWeekDays(Arrays.asList(1, 2, 3, 4, 5)); + return msg; + } } diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleApiInterceptor.java b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleApiInterceptor.java index 78c42babccb..63d05e37f56 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleApiInterceptor.java +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleApiInterceptor.java @@ -18,7 +18,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Objects; import java.util.Set; import static org.zstack.core.Platform.argerr; @@ -138,18 +137,7 @@ private void validate(APISetNetworkSecurityPolicyScheduleMsg msg) { if (msg.getScheduleUuid() == null) { return; } - NetworkSecurityPolicyScheduleVO schedule = validateScheduleOwner( - msg.getScheduleUuid(), msg.getResourceType(), msg.getResourceUuid()); - if (!Objects.equals( - scheduleUuidOf(msg.getResourceType(), msg.getResourceUuid()), - msg.getScheduleUuid()) - && !NetworkSecurityPolicyScheduleTime.valueOf(schedule) - .hasRemainingSchedule(scheduleFacade.now())) { - throw new ApiMessageInterceptionException(argerr( - ORG_ZSTACK_NETWORKSECURITYPOLICYSCHEDULE_10007, - "network security policy schedule[uuid:%s] has no remaining active minute", - msg.getScheduleUuid())); - } + validateScheduleOwner(msg.getScheduleUuid(), msg.getResourceType(), msg.getResourceUuid()); } private NetworkSecurityPolicyScheduleTime validateTime( @@ -323,10 +311,6 @@ private void validateScheduleOwner(NetworkSecurityPolicyScheduleVO schedule, } } - private String scheduleUuidOf(String resourceType, String resourceUuid) { - return resourceBackend(resourceType).getScheduleUuid(resourceUuid); - } - private NetworkSecurityPolicyScheduleResourceBackend resourceBackend(String resourceType) { NetworkSecurityPolicyScheduleResourceBackend backend = backendRegistry.getBackend(resourceType); if (backend == null) { diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleInventory.java b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleInventory.java index 8e7c3c52a0b..d123420942a 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleInventory.java +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleInventory.java @@ -25,6 +25,7 @@ public class NetworkSecurityPolicyScheduleInventory { private String startTime; private String endTime; private List weekDays; + private boolean effective; private boolean expired; private Timestamp createDate; private Timestamp lastOpDate; @@ -33,6 +34,29 @@ public static NetworkSecurityPolicyScheduleInventory valueOf(NetworkSecurityPoli return valueOf(vo, Instant.now()); } + public static NetworkSecurityPolicyScheduleInventory __example__() { + NetworkSecurityPolicyScheduleInventory inventory = + new NetworkSecurityPolicyScheduleInventory(); + inventory.setUuid("4c4aa4f9b7254d76b48ad99f6a20c9ee"); + inventory.setName("office-hours"); + inventory.setDescription("Weekday office hours"); + inventory.setResourceType( + NetworkSecurityPolicyScheduleConstant.SECURITY_GROUP_RESOURCE_TYPE); + inventory.setResourceUuid("f1a72f89f9624c92a84cbd07347be003"); + inventory.setTimeType("UTC"); + inventory.setRepeatType("Weekly"); + inventory.setStartDate("2026-01-01"); + inventory.setEndDate("2026-12-31"); + inventory.setStartTime("09:00"); + inventory.setEndTime("18:00"); + inventory.setWeekDays(Arrays.asList(1, 2, 3, 4, 5)); + inventory.setEffective(true); + inventory.setExpired(false); + inventory.setCreateDate(Timestamp.valueOf("2026-01-01 00:00:00")); + inventory.setLastOpDate(Timestamp.valueOf("2026-01-01 00:00:00")); + return inventory; + } + public static NetworkSecurityPolicyScheduleInventory valueOf(NetworkSecurityPolicyScheduleVO vo, Instant now) { NetworkSecurityPolicyScheduleInventory inventory = new NetworkSecurityPolicyScheduleInventory(); inventory.uuid = vo.getUuid(); @@ -49,7 +73,9 @@ public static NetworkSecurityPolicyScheduleInventory valueOf(NetworkSecurityPoli inventory.endTime = vo.getEndTime().toLocalTime() .truncatedTo(ChronoUnit.MINUTES).toString(); inventory.weekDays = toWeekDays(vo.getWeekDays()); - inventory.expired = !NetworkSecurityPolicyScheduleTime.valueOf(vo).hasRemainingSchedule(now); + NetworkSecurityPolicyScheduleTime time = NetworkSecurityPolicyScheduleTime.valueOf(vo); + inventory.effective = time.isInSchedule(now); + inventory.expired = !time.hasRemainingSchedule(now); inventory.createDate = vo.getCreateDate(); inventory.lastOpDate = vo.getLastOpDate(); return inventory; @@ -175,6 +201,14 @@ public void setWeekDays(List weekDays) { this.weekDays = weekDays; } + public boolean isEffective() { + return effective; + } + + public void setEffective(boolean effective) { + this.effective = effective; + } + public boolean isExpired() { return expired; } diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleInventoryDoc_zh_cn.groovy b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleInventoryDoc_zh_cn.groovy index 74e1889a58c..e1922a9faf6 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleInventoryDoc_zh_cn.groovy +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleInventoryDoc_zh_cn.groovy @@ -77,6 +77,12 @@ doc { type "List" since "5.5.38" } + field { + name "effective" + desc "当前分钟是否处于生效时间段" + type "boolean" + since "5.5.38" + } field { name "expired" desc "从当前分钟起是否已不存在未来生效时间" diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleManagerImpl.java b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleManagerImpl.java index 03d08228d9f..ce038322811 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleManagerImpl.java +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleManagerImpl.java @@ -45,7 +45,6 @@ import static org.zstack.core.Platform.err; import static org.zstack.core.Platform.operr; import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.ORG_ZSTACK_NETWORKSECURITYPOLICYSCHEDULE_10006; -import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.ORG_ZSTACK_NETWORKSECURITYPOLICYSCHEDULE_10007; import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.ORG_ZSTACK_NETWORKSECURITYPOLICYSCHEDULE_10010; import static org.zstack.utils.clouderrorcode.CloudOperationsErrorCode.ORG_ZSTACK_NETWORKSECURITYPOLICYSCHEDULE_10011; @@ -347,14 +346,6 @@ public void run(FlowTrigger trigger, Map data) { msg.getResourceType(), msg.getResourceUuid())); return; } - if (!NetworkSecurityPolicyScheduleTime.valueOf(vo) - .hasRemainingSchedule(scheduleFacade.now())) { - trigger.fail(argerr( - ORG_ZSTACK_NETWORKSECURITYPOLICYSCHEDULE_10007, - "network security policy schedule[uuid:%s] has no remaining active minute", - msg.getScheduleUuid())); - return; - } trigger.next(); } }); diff --git a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleVO.java b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleVO.java index cd9692a438d..fbe9d4b256c 100644 --- a/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleVO.java +++ b/plugin/networkSecurityPolicySchedule/src/main/java/org/zstack/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleVO.java @@ -24,7 +24,7 @@ public class NetworkSecurityPolicyScheduleVO { @Column private String name; - @Column(length = 2048) + @Column private String description; @Column diff --git a/sdk/src/main/java/org/zstack/sdk/CreateNetworkSecurityPolicyScheduleAction.java b/sdk/src/main/java/org/zstack/sdk/CreateNetworkSecurityPolicyScheduleAction.java index cdaf6aecd19..5f47a8dfc9c 100644 --- a/sdk/src/main/java/org/zstack/sdk/CreateNetworkSecurityPolicyScheduleAction.java +++ b/sdk/src/main/java/org/zstack/sdk/CreateNetworkSecurityPolicyScheduleAction.java @@ -28,7 +28,7 @@ public Result throwExceptionIfError() { @Param(required = true, maxLength = 255, nonempty = false, nullElements = false, emptyString = false, noTrim = false) public java.lang.String name; - @Param(required = false, maxLength = 2048, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + @Param(required = false, maxLength = 255, nonempty = false, nullElements = false, emptyString = true, noTrim = false) public java.lang.String description; @Param(required = true, validValues = {"SecurityGroup","VpcFirewallRuleSet"}, nonempty = false, nullElements = false, emptyString = true, noTrim = false) diff --git a/sdk/src/main/java/org/zstack/sdk/NetworkSecurityPolicyScheduleInventory.java b/sdk/src/main/java/org/zstack/sdk/NetworkSecurityPolicyScheduleInventory.java index d08aaba1cc9..eb2d8300a11 100644 --- a/sdk/src/main/java/org/zstack/sdk/NetworkSecurityPolicyScheduleInventory.java +++ b/sdk/src/main/java/org/zstack/sdk/NetworkSecurityPolicyScheduleInventory.java @@ -100,6 +100,14 @@ public java.util.List getWeekDays() { return this.weekDays; } + public boolean effective; + public void setEffective(boolean effective) { + this.effective = effective; + } + public boolean getEffective() { + return this.effective; + } + public boolean expired; public void setExpired(boolean expired) { this.expired = expired; diff --git a/sdk/src/main/java/org/zstack/sdk/UpdateNetworkSecurityPolicyScheduleAction.java b/sdk/src/main/java/org/zstack/sdk/UpdateNetworkSecurityPolicyScheduleAction.java index 427c85e0463..1a496ed9075 100644 --- a/sdk/src/main/java/org/zstack/sdk/UpdateNetworkSecurityPolicyScheduleAction.java +++ b/sdk/src/main/java/org/zstack/sdk/UpdateNetworkSecurityPolicyScheduleAction.java @@ -31,7 +31,7 @@ public Result throwExceptionIfError() { @Param(required = true, maxLength = 255, nonempty = false, nullElements = false, emptyString = false, noTrim = false) public java.lang.String name; - @Param(required = false, maxLength = 2048, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + @Param(required = false, maxLength = 255, nonempty = false, nullElements = false, emptyString = true, noTrim = false) public java.lang.String description; @Param(required = true, validValues = {"Local","UTC"}, nonempty = false, nullElements = false, emptyString = true, noTrim = false) diff --git a/test/src/test/groovy/org/zstack/test/integration/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleApiCase.groovy b/test/src/test/groovy/org/zstack/test/integration/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleApiCase.groovy index aa777bca57c..bb276d538de 100644 --- a/test/src/test/groovy/org/zstack/test/integration/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleApiCase.groovy +++ b/test/src/test/groovy/org/zstack/test/integration/networksecuritypolicyschedule/NetworkSecurityPolicyScheduleApiCase.groovy @@ -75,6 +75,7 @@ class NetworkSecurityPolicyScheduleApiCase extends SubCase { String resourceType, String resourceUuid, Map overrides = [:], String sessionId = null) { Map fields = [ name: "office-hours", + description: null, resourceType: resourceType, resourceUuid: resourceUuid, timeType: "UTC", @@ -88,6 +89,7 @@ class NetworkSecurityPolicyScheduleApiCase extends SubCase { fields.putAll(overrides) return new CreateNetworkSecurityPolicyScheduleAction( name: fields.name, + description: fields.description, resourceType: fields.resourceType, resourceUuid: fields.resourceUuid, timeType: fields.timeType, @@ -216,7 +218,9 @@ class NetworkSecurityPolicyScheduleApiCase extends SubCase { weekDays = [3] } as NetworkSecurityPolicyScheduleInventory - assert !upcoming.expired && !active.expired && !laterWeekly.expired + assert !upcoming.effective && !upcoming.expired : "upcoming status mismatch" + assert active.effective && !active.expired : "active status mismatch" + assert !laterWeekly.effective && !laterWeekly.expired : "later weekly status mismatch" NetworkSecurityPolicyScheduleInventory expired = updateNetworkSecurityPolicySchedule { uuid = upcoming.uuid @@ -228,21 +232,25 @@ class NetworkSecurityPolicyScheduleApiCase extends SubCase { startTime = "06:00" endTime = "07:00" } as NetworkSecurityPolicyScheduleInventory - assert expired.expired + assert !expired.effective && expired.expired : "expired status mismatch" Map queried = getSchedules(securityGroup.uuid).collectEntries { [(it.uuid): it] } - assert queried[upcoming.uuid].expired && - !queried[active.uuid].expired && - !queried[laterWeekly.uuid].expired + assert !queried[upcoming.uuid].effective && queried[upcoming.uuid].expired : + "queried expired status mismatch" + assert queried[active.uuid].effective && !queried[active.uuid].expired : + "queried active status mismatch" + assert !queried[laterWeekly.uuid].effective && !queried[laterWeekly.uuid].expired : + "queried future status mismatch" scheduleFacade.setClock(Clock.fixed( Instant.parse("2026-08-06T00:00:00Z"), ZoneOffset.UTC)) List allExpired = getSchedules(securityGroup.uuid) - assert allExpired.size() == 3 && allExpired.every { it.expired } + assert allExpired.size() == 3 && allExpired.every { it.expired && !it.effective } : + "all schedules should be expired and ineffective" scheduleFacade.setClock(Clock.fixed( Instant.parse("2026-07-29T08:00:00Z"), ZoneOffset.UTC)) @@ -448,8 +456,33 @@ class NetworkSecurityPolicyScheduleApiCase extends SubCase { unsetSchedule(securityGroup.uuid) SetNetworkSecurityPolicyScheduleAction.Result expiredSet = setScheduleResult( schedule.uuid, "SecurityGroup", securityGroup.uuid) - assert expiredSet.error != null && - scheduleUuidOf(securityGroup.uuid) == null + assert expiredSet.error == null && scheduleUuidOf(securityGroup.uuid) == schedule.uuid : + "expired schedule should remain bindable" + unsetSchedule(securityGroup.uuid) + } + + void testDescriptionLength() { + SecurityGroupInventory securityGroup = createSecurityGroup { + name = "schedule-description-length-sg" + ipVersion = 4 + } as SecurityGroupInventory + String accepted = "a" * 255 + + CreateNetworkSecurityPolicyScheduleAction.Result created = createScheduleResult( + "SecurityGroup", securityGroup.uuid, [description: accepted]) + assert created.error == null && created.value.inventory.description == accepted : + "255-character description should be accepted" + + expect(ApiException.class) { + createScheduleResult("SecurityGroup", securityGroup.uuid, [description: "a" * 256]) + } + expect(ApiException.class) { + updateScheduleResult(created.value.inventory, [description: "a" * 256]) + } + + deleteNetworkSecurityPolicySchedule { + uuid = created.value.inventory.uuid + } } void testMultipleSchedulesPerResource() { @@ -651,6 +684,7 @@ class NetworkSecurityPolicyScheduleApiCase extends SubCase { testExpiredStatus() testSecurityGroupScheduleLifecycle() testCreateApiValidation() + testDescriptionLength() testRejectUninstalledResourceBackend() testUpdateApiValidation() testMultipleSchedulesPerResource() diff --git a/test/src/test/groovy/org/zstack/test/integration/networksecuritypolicyschedule/SecurityGroupScheduledActivationCase.groovy b/test/src/test/groovy/org/zstack/test/integration/networksecuritypolicyschedule/SecurityGroupScheduledActivationCase.groovy index b65eaa3aee1..565f93cd5b2 100644 --- a/test/src/test/groovy/org/zstack/test/integration/networksecuritypolicyschedule/SecurityGroupScheduledActivationCase.groovy +++ b/test/src/test/groovy/org/zstack/test/integration/networksecuritypolicyschedule/SecurityGroupScheduledActivationCase.groovy @@ -1069,6 +1069,71 @@ class SecurityGroupScheduledActivationCase extends SubCase { } } + void testBindExpiredSchedule() { + scanTask.stop() + scheduleFacade.setClock(Clock.fixed( + Instant.parse("2026-07-30T08:00:00Z"), ZoneOffset.UTC)) + L3NetworkInventory l3 = env.inventoryByName("l3") as L3NetworkInventory + VmInstanceInventory vm = env.inventoryByName("vm3") as VmInstanceInventory + String existingPort = "16600" + String addedPort = "16601" + SecurityGroupInventory securityGroup = createSecurityGroupWithRule( + "expired-schedule-security-group", existingPort, l3) + List commands = + recordVmNicRuleCommands([vm.vmNics[0].uuid]) + addVmNicsAndWaitForRuleApply( + securityGroup.uuid, [vm.vmNics[0].uuid], commands, 1) + + NetworkSecurityPolicyScheduleInventory schedule = createNetworkSecurityPolicySchedule { + name = "expired-security-group-schedule" + resourceType = "SecurityGroup" + resourceUuid = securityGroup.uuid + timeType = "UTC" + repeatType = "Once" + startDate = "2026-07-30" + endDate = "2026-07-30" + startTime = "09:00" + endTime = "10:00" + } as NetworkSecurityPolicyScheduleInventory + + commands.clear() + scheduleFacade.setClock(Clock.fixed( + Instant.parse("2026-07-30T10:00:00Z"), ZoneOffset.UTC)) + setSchedule(schedule, securityGroup.uuid) + retryInSecs { + assert commands.size() == 1 : "expired bind refresh count: ${commands.size()}" + assert !containsRule(commands.last(), securityGroup.uuid, existingPort) : + "expired bind retained port ${existingPort}" + } + + commands.clear() + addSecurityGroupRule { + securityGroupUuid = securityGroup.uuid + delegate.rules = [new SecurityGroupRuleAO( + type: "Ingress", + ipVersion: 4, + protocol: "TCP", + dstPortRange: addedPort, + srcIpRange: "10.0.0.0/24", + state: "Enabled" + )] + } + retryInSecs { + assert commands.size() == 1 : "expired rule refresh count: ${commands.size()}" + assert !containsRule(commands.last(), securityGroup.uuid, existingPort) : + "expired schedule restored port ${existingPort}" + assert !containsRule(commands.last(), securityGroup.uuid, addedPort) : + "expired schedule enabled port ${addedPort}" + } + + deleteNetworkSecurityPolicySchedule { + uuid = schedule.uuid + } + deleteSecurityGroup { + uuid = securityGroup.uuid + } + } + @Override void test() { env.create { @@ -1085,6 +1150,7 @@ class SecurityGroupScheduledActivationCase extends SubCase { testConcurrentStateChange() testHostRefreshFailure() testSdnRuleCalculation() + testBindExpiredSchedule() } } } diff --git a/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy b/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy index 2ce3a3b8d54..37468fde7ea 100644 --- a/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy +++ b/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy @@ -21,20 +21,20 @@ abstract class ApiHelper { c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() - + if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) @@ -10715,20 +10715,20 @@ abstract class ApiHelper { c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() - + if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) @@ -16250,20 +16250,20 @@ abstract class ApiHelper { c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() - + if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) @@ -24485,20 +24485,20 @@ abstract class ApiHelper { c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() - + if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) @@ -24539,20 +24539,20 @@ abstract class ApiHelper { c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() - + if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) @@ -40830,20 +40830,20 @@ abstract class ApiHelper { c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() - + if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) @@ -47121,20 +47121,20 @@ abstract class ApiHelper { c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() - + if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) @@ -47337,20 +47337,20 @@ abstract class ApiHelper { c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() - + if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) @@ -48687,7 +48687,7 @@ abstract class ApiHelper { c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() - + if (System.getProperty("apipath") != null) { if (a.apiId == null) { @@ -52177,20 +52177,20 @@ abstract class ApiHelper { c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() - + if (System.getProperty("apipath") != null) { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call()) @@ -56370,7 +56370,7 @@ abstract class ApiHelper { c.resolveStrategy = Closure.OWNER_FIRST c.delegate = a c() - + a.conditions = a.conditions.collect { it.toString() } @@ -56378,14 +56378,14 @@ abstract class ApiHelper { if (a.apiId == null) { a.apiId = Platform.uuid } - + def tracker = new ApiPathTracker(a.apiId) def out = errorOut(a.call()) def path = tracker.getApiPath() if (!path.isEmpty()) { Test.apiPaths[a.class.name] = path.join(" --->\n") } - + return out } else { return errorOut(a.call())