diff --git a/conf/i18n/globalErrorCodeMapping/global-error-en_US.json b/conf/i18n/globalErrorCodeMapping/global-error-en_US.json index df3c4c42bdf..f5828af0310 100644 --- a/conf/i18n/globalErrorCodeMapping/global-error-en_US.json +++ b/conf/i18n/globalErrorCodeMapping/global-error-en_US.json @@ -4734,5 +4734,11 @@ "ORG_ZSTACK_AI_10162": "VM[name:%s, uuid:%s] must be running to mount/unmount model. Current state: %s", "ORG_ZSTACK_AI_10163": "VM[name:%s, uuid:%s] is not running on any host", "ORG_ZSTACK_AI_10164": "Model[name:%s, uuid:%s] is not shared to the account that owns this VM (or to public). Mount requires the model to be accessible under the same sharing rules as the VM.", - "ORG_ZSTACK_AI_10165": "ModelCenter[uuid:%s] not found for Model[name:%s, uuid:%s]" + "ORG_ZSTACK_AI_10165": "ModelCenter[uuid:%s] not found for Model[name:%s, uuid:%s]", + "ORG_ZSTACK_NETWORK_ZNS_10043": "failed to allocate DHCPv4 server IP for ZNS L3[uuid:%s]", + "ORG_ZSTACK_NETWORK_ZNS_10044": "cannot enable DHCP: L3[uuid:%s] has no eligible IpRange (SLAAC excluded)", + "ORG_ZSTACK_NETWORK_ZNS_10045": "cannot enable DHCP: L3[uuid:%s] has no Cloud-reserved DHCPv4 server IP", + "ORG_ZSTACK_NETWORK_ZNS_10046": "DHCP service already exists on segment[uuid:%s] but GET returned empty", + "ORG_ZSTACK_NETWORK_ZNS_10047": "cannot update DHCP: L3[uuid:%s] has no Cloud-reserved DHCPv4 server IP", + "ORG_ZSTACK_NETWORK_ZNS_10048": "failed to allocate DHCPv4 server IP for ZNS L3[uuid:%s]" } diff --git a/conf/i18n/globalErrorCodeMapping/global-error-zh_CN.json b/conf/i18n/globalErrorCodeMapping/global-error-zh_CN.json index 1400aefd860..8e9b89ac4b3 100644 --- a/conf/i18n/globalErrorCodeMapping/global-error-zh_CN.json +++ b/conf/i18n/globalErrorCodeMapping/global-error-zh_CN.json @@ -4741,5 +4741,11 @@ "ORG_ZSTACK_AI_10162": "虚拟机「%s」(UUID: %s) 必须处于运行状态才能挂载/卸载模型,当前状态: %s", "ORG_ZSTACK_AI_10163": "虚拟机「%s」(UUID: %s) 未运行在任何主机上", "ORG_ZSTACK_AI_10164": "模型「%s」(UUID: %s) 与该虚拟机所属账户的共享规则不匹配,无法挂载。\n请确认模型已共享给该账户或设为公开。", - "ORG_ZSTACK_AI_10165": "模型中心 (UUID: %s) 未找到(关联模型: 「%s」UUID: %s)。\n请检查模型中心是否已被删除。" + "ORG_ZSTACK_AI_10165": "模型中心 (UUID: %s) 未找到(关联模型: 「%s」UUID: %s)。\n请检查模型中心是否已被删除。", + "ORG_ZSTACK_NETWORK_ZNS_10043": "为 ZNS 三层网络[uuid:%s]分配 DHCPv4 服务 IP 失败", + "ORG_ZSTACK_NETWORK_ZNS_10044": "无法启用 DHCP:三层网络[uuid:%s]没有可用的 IP 范围(不包含 SLAAC)", + "ORG_ZSTACK_NETWORK_ZNS_10045": "无法启用 DHCP:三层网络[uuid:%s]没有云端预留的 DHCPv4 服务 IP", + "ORG_ZSTACK_NETWORK_ZNS_10046": "网段[uuid:%s]上已存在 DHCP 服务,但 GET 返回为空", + "ORG_ZSTACK_NETWORK_ZNS_10047": "无法更新 DHCP:三层网络[uuid:%s]没有云端预留的 DHCPv4 服务 IP", + "ORG_ZSTACK_NETWORK_ZNS_10048": "为 ZNS 三层网络[uuid:%s]分配 DHCPv4 服务 IP 失败" } diff --git a/header/src/main/java/org/zstack/header/network/service/APIAttachNetworkServiceToL3NetworkMsg.java b/header/src/main/java/org/zstack/header/network/service/APIAttachNetworkServiceToL3NetworkMsg.java index c51ba7f5423..1753638d0d1 100755 --- a/header/src/main/java/org/zstack/header/network/service/APIAttachNetworkServiceToL3NetworkMsg.java +++ b/header/src/main/java/org/zstack/header/network/service/APIAttachNetworkServiceToL3NetworkMsg.java @@ -8,6 +8,7 @@ import org.zstack.header.network.l3.L3NetworkConstant; import org.zstack.header.network.l3.L3NetworkMessage; import org.zstack.header.network.l3.L3NetworkVO; +import org.zstack.header.rest.APINoSee; import org.zstack.header.rest.RestRequest; import java.util.*; @@ -66,6 +67,8 @@ public class APIAttachNetworkServiceToL3NetworkMsg extends APIMessage implements */ @APIParam private Map> networkServices; + @APINoSee + private transient boolean skipAttach; @Override public String getL3NetworkUuid() { @@ -83,6 +86,14 @@ public void setNetworkServices(Map> networkServices) { public void setL3NetworkUuid(String l3NetworkUuid) { this.l3NetworkUuid = l3NetworkUuid; } + + public boolean isSkipAttach() { + return skipAttach; + } + + public void setSkipAttach(boolean skipAttach) { + this.skipAttach = skipAttach; + } public static APIAttachNetworkServiceToL3NetworkMsg __example__() { APIAttachNetworkServiceToL3NetworkMsg msg = new APIAttachNetworkServiceToL3NetworkMsg(); diff --git a/header/src/main/java/org/zstack/header/network/service/NetworkServiceAttachExtensionPoint.java b/header/src/main/java/org/zstack/header/network/service/NetworkServiceAttachExtensionPoint.java new file mode 100644 index 00000000000..1b61aac5fad --- /dev/null +++ b/header/src/main/java/org/zstack/header/network/service/NetworkServiceAttachExtensionPoint.java @@ -0,0 +1,5 @@ +package org.zstack.header.network.service; + +public interface NetworkServiceAttachExtensionPoint { + boolean skipAttachNetworkService(APIAttachNetworkServiceToL3NetworkMsg msg); +} diff --git a/header/src/main/java/org/zstack/header/network/service/NetworkServiceProviderType.java b/header/src/main/java/org/zstack/header/network/service/NetworkServiceProviderType.java index 852bd09c401..a5ae83ce72f 100755 --- a/header/src/main/java/org/zstack/header/network/service/NetworkServiceProviderType.java +++ b/header/src/main/java/org/zstack/header/network/service/NetworkServiceProviderType.java @@ -10,6 +10,7 @@ public class NetworkServiceProviderType { private final String typeName; private boolean createDhcpNameSpace = true; private boolean allocateDhcpServerIp = true; + private boolean allocateDhcpv6ServerIp = true; public NetworkServiceProviderType(String typeName) { this.typeName = typeName; @@ -55,6 +56,14 @@ public void setAllocateDhcpServerIp(boolean allocateDhcpServerIp) { this.allocateDhcpServerIp = allocateDhcpServerIp; } + public boolean isAllocateDhcpv6ServerIp() { + return allocateDhcpv6ServerIp; + } + + public void setAllocateDhcpv6ServerIp(boolean allocateDhcpv6ServerIp) { + this.allocateDhcpv6ServerIp = allocateDhcpv6ServerIp; + } + @Override public int hashCode() { return typeName.hashCode(); diff --git a/network/src/main/java/org/zstack/network/l3/L3BasicNetwork.java b/network/src/main/java/org/zstack/network/l3/L3BasicNetwork.java index 1e4f046423d..43849b41e34 100755 --- a/network/src/main/java/org/zstack/network/l3/L3BasicNetwork.java +++ b/network/src/main/java/org/zstack/network/l3/L3BasicNetwork.java @@ -1560,6 +1560,13 @@ public void fail(ErrorCode errorCode) { private void handle(APIAttachNetworkServiceToL3NetworkMsg msg) { APIAttachNetworkServiceToL3NetworkEvent evt = new APIAttachNetworkServiceToL3NetworkEvent(msg.getId()); + if (msg.isSkipAttach()) { + self = dbf.reload(self); + evt.setInventory(L3NetworkInventory.valueOf(self)); + bus.publish(evt); + return; + } + AttachNetworkServiceToL3Msg amsg = new AttachNetworkServiceToL3Msg(); amsg.setL3NetworkUuid(msg.getL3NetworkUuid()); amsg.setNetworkServices(msg.getNetworkServices()); diff --git a/network/src/main/java/org/zstack/network/service/DhcpExtension.java b/network/src/main/java/org/zstack/network/service/DhcpExtension.java index 8e37da8b1e9..ff66b997243 100755 --- a/network/src/main/java/org/zstack/network/service/DhcpExtension.java +++ b/network/src/main/java/org/zstack/network/service/DhcpExtension.java @@ -437,6 +437,7 @@ public void enableNetworkService(L3NetworkVO l3VO, NetworkServiceProviderType pr SdnControllerEnableDHCPMsg msg = new SdnControllerEnableDHCPMsg(); msg.setL3NetworkUuid(l3VO.getUuid()); msg.setSdnControllerUuid(sdnControllerUuid); + msg.setSystemTags(systemTags); bus.makeTargetServiceIdByResourceUuid(msg, SdnControllerConstant.SERVICE_ID, sdnControllerUuid); bus.send(msg, new CloudBusCallBack(completion) { @Override diff --git a/network/src/main/java/org/zstack/network/service/MtuGetter.java b/network/src/main/java/org/zstack/network/service/MtuGetter.java index 9635ca3f0f5..b53da773b90 100644 --- a/network/src/main/java/org/zstack/network/service/MtuGetter.java +++ b/network/src/main/java/org/zstack/network/service/MtuGetter.java @@ -3,6 +3,7 @@ import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Configurable; +import org.zstack.core.Platform; import org.zstack.core.componentloader.PluginRegistry; import org.zstack.core.db.Q; import org.zstack.header.network.l2.*; @@ -28,6 +29,13 @@ public class MtuGetter { @Autowired private PluginRegistry pluginRgty; + private PluginRegistry getPluginRegistry() { + if (pluginRgty == null) { + pluginRgty = Platform.getComponentLoader().getComponent(PluginRegistry.class); + } + return pluginRgty; + } + public Integer getMtu(String l3NetworkUuid) { String l2NetworkUuid = Q.New(L3NetworkVO.class).select(L3NetworkVO_.l2NetworkUuid).eq(L3NetworkVO_.uuid, l3NetworkUuid).findValue(); @@ -43,7 +51,7 @@ public Integer getMtu(String l3NetworkUuid) { } L2NetworkVO l2VO = Q.New(L2NetworkVO.class).eq(L2NetworkVO_.uuid, l2NetworkUuid).find(); - for (L2NetworkDefaultMtu e : pluginRgty.getExtensionList(L2NetworkDefaultMtu.class)) { + for (L2NetworkDefaultMtu e : getPluginRegistry().getExtensionList(L2NetworkDefaultMtu.class)) { if (l2VO.getType().equals(e.getL2NetworkType())) { mtu = e.getDefaultMtu(L2NetworkInventory.valueOf(l2VO)); } @@ -82,7 +90,7 @@ public Integer getL2Mtu(L2NetworkInventory l2Inv) { } /* compare to default mtu */ - for (L2NetworkDefaultMtu e : pluginRgty.getExtensionList(L2NetworkDefaultMtu.class)) { + for (L2NetworkDefaultMtu e : getPluginRegistry().getExtensionList(L2NetworkDefaultMtu.class)) { if (l2Inv.getType().equals(e.getL2NetworkType())) { Integer l2mtu = e.getDefaultMtu(l2Inv); if (mtu == null) { diff --git a/network/src/main/java/org/zstack/network/service/NetworkServiceApiInterceptor.java b/network/src/main/java/org/zstack/network/service/NetworkServiceApiInterceptor.java index dad5d87781a..9b95e53ee9a 100755 --- a/network/src/main/java/org/zstack/network/service/NetworkServiceApiInterceptor.java +++ b/network/src/main/java/org/zstack/network/service/NetworkServiceApiInterceptor.java @@ -1,6 +1,7 @@ package org.zstack.network.service; import org.springframework.beans.factory.annotation.Autowired; +import org.zstack.core.componentloader.PluginRegistry; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.db.SimpleQuery; import org.zstack.core.db.SimpleQuery.Op; @@ -29,16 +30,22 @@ /** */ -public class NetworkServiceApiInterceptor implements ApiMessageInterceptor { - private final static CLogger logger = Utils.getLogger(NetworkServiceApiInterceptor.class); - @Autowired +public class NetworkServiceApiInterceptor implements ApiMessageInterceptor { + private final static CLogger logger = Utils.getLogger(NetworkServiceApiInterceptor.class); + @Autowired private DatabaseFacade dbf; + @Autowired + private PluginRegistry pluginRgty; @Override public APIMessage intercept(APIMessage msg) throws ApiMessageInterceptionException { if (msg instanceof APIAttachNetworkServiceToL3NetworkMsg) { APIAttachNetworkServiceToL3NetworkMsg attachMsg = (APIAttachNetworkServiceToL3NetworkMsg)msg; attachMsg.setNetworkServices(convertNetworkProviderTypeToUuid(attachMsg.getNetworkServices())); + if (skipAttachNetworkService(attachMsg)) { + attachMsg.setSkipAttach(true); + return msg; + } validate(attachMsg); } else if (msg instanceof APIDetachNetworkServiceFromL3NetworkMsg) { APIDetachNetworkServiceFromL3NetworkMsg detachMsg = (APIDetachNetworkServiceFromL3NetworkMsg)msg; @@ -59,6 +66,15 @@ public APIMessage intercept(APIMessage msg) throws ApiMessageInterceptionExcepti return msg; } + private boolean skipAttachNetworkService(APIAttachNetworkServiceToL3NetworkMsg msg) { + for (NetworkServiceAttachExtensionPoint ext : pluginRgty.getExtensionList(NetworkServiceAttachExtensionPoint.class)) { + if (ext.skipAttachNetworkService(msg)) { + return true; + } + } + return false; + } + private void validate(APIAttachNetworkServiceToL3NetworkMsg msg) { if (msg.getNetworkServices().isEmpty()) { throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_10006, "networkServices cannot be empty")); @@ -157,11 +173,11 @@ public String call(String type) { } private Map> convertNetworkProviderTypeToUuid(Map> map){ - if (map.isEmpty()) { + Map> mapNew = normalizeNetworkServices(map); + if (mapNew.isEmpty()) { throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_10012, "networkServices cannot be empty")); } - Map> mapNew = new HashMap<>(map); List networkServiceProviderVOs = Q.New(NetworkServiceProviderVO.class).list(); for (NetworkServiceProviderVO vo :networkServiceProviderVOs) { @@ -172,4 +188,39 @@ private Map> convertNetworkProviderTypeToUuid(Map> normalizeNetworkServices(Map> map) { + Map> ret = new LinkedHashMap<>(); + if (map == null) { + return ret; + } + + for (Map.Entry> entry : map.entrySet()) { + if (entry.getKey() == null) { + continue; + } + + String provider = entry.getKey().trim(); + if (provider.isEmpty()) { + continue; + } + + List services = new ArrayList<>(); + if (entry.getValue() != null) { + for (String service : entry.getValue()) { + if (service == null) { + continue; + } + + String normalized = service.trim(); + if (!normalized.isEmpty()) { + services.add(normalized); + } + } + } + + ret.computeIfAbsent(provider, k -> new ArrayList<>()).addAll(services); + } + return ret; + } } diff --git a/plugin/flatNetworkProvider/src/main/java/org/zstack/network/service/flat/FlatDhcpBackend.java b/plugin/flatNetworkProvider/src/main/java/org/zstack/network/service/flat/FlatDhcpBackend.java index 5823bb2b9ee..db72febcf4d 100755 --- a/plugin/flatNetworkProvider/src/main/java/org/zstack/network/service/flat/FlatDhcpBackend.java +++ b/plugin/flatNetworkProvider/src/main/java/org/zstack/network/service/flat/FlatDhcpBackend.java @@ -996,7 +996,7 @@ public static Map getExistingDhcpServerIp(String l3Uuid, int ipV @Deferred private String allocateDhcpIp(String l3Uuid, int ipVersion, boolean allocate_ip, String requiredIp, String excludedIp) { - if (!isAllocateDhcpServerIp(l3Uuid)) { + if (!isAllocateDhcpServerIp(l3Uuid, ipVersion)) { return null; } @@ -1276,6 +1276,17 @@ private boolean isAllocateDhcpServerIp(String l3Uuid) { return type.isAllocateDhcpServerIp(); } + private boolean isAllocateDhcpServerIp(String l3Uuid, int ipVersion) { + String providerType = new NetworkProviderFinder().getNetworkProviderTypeByNetworkServiceType(l3Uuid, NetworkServiceType.DHCP.toString()); + if (providerType == null) { + return false; + } + + NetworkServiceProviderType type = NetworkServiceProviderType.valueOf(providerType); + return type.isAllocateDhcpServerIp() + && (ipVersion != IPv6Constants.IPv6 || type.isAllocateDhcpv6ServerIp()); + } + private boolean isCreateDhcpNameSpace(String l3Uuid) { String providerType = new NetworkProviderFinder().getNetworkProviderTypeByNetworkServiceType(l3Uuid, NetworkServiceType.DHCP.toString()); if (providerType == null) { @@ -2490,7 +2501,10 @@ private void validate(APIDetachNetworkServiceFromL3NetworkMsg msg) { } private void validate(APIChangeL3NetworkDhcpIpAddressMsg msg) { - if (!isAllocateDhcpServerIp(msg.getL3NetworkUuid())) { + if ((msg.getDhcpServerIp() != null && !isAllocateDhcpServerIp(msg.getL3NetworkUuid(), IPv6Constants.IPv4)) + || (msg.getDhcpv6ServerIp() != null && !isAllocateDhcpServerIp(msg.getL3NetworkUuid(), IPv6Constants.IPv6)) + || (msg.getDhcpServerIp() == null && msg.getDhcpv6ServerIp() == null + && !isAllocateDhcpServerIp(msg.getL3NetworkUuid()))) { throw new ApiMessageInterceptionException(argerr(ORG_ZSTACK_NETWORK_SERVICE_FLAT_10044, "could change dhcp server ip, because flat dhcp is not enabled")); } diff --git a/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java b/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java index e406468f95a..24ae483e3b4 100644 --- a/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java +++ b/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java @@ -12124,6 +12124,12 @@ public class CloudOperationsErrorCode { // 10040 ZNS did not return Cloud allocated IP version // 10041 VM NIC MAC update is not supported for ZNS NICs // 10042 L3Network not found while enabling ZNS DHCP + // 10043 failed to allocate DHCPv4 server IP in ZNS DHCP helper + // 10044 ZNS DHCP enable has no eligible IpRange + // 10045 ZNS DHCP enable has no Cloud-reserved DHCPv4 server IP + // 10046 ZNS DHCP 409 fallback GET returned empty + // 10047 ZNS DHCP update has no Cloud-reserved DHCPv4 server IP + // 10048 failed to allocate DHCPv4 server IP in ZNS DHCP backend public static final String ORG_ZSTACK_NETWORK_ZNS_10035 = "ORG_ZSTACK_NETWORK_ZNS_10035"; public static final String ORG_ZSTACK_NETWORK_ZNS_10036 = "ORG_ZSTACK_NETWORK_ZNS_10036"; public static final String ORG_ZSTACK_NETWORK_ZNS_10037 = "ORG_ZSTACK_NETWORK_ZNS_10037"; @@ -12132,6 +12138,12 @@ public class CloudOperationsErrorCode { public static final String ORG_ZSTACK_NETWORK_ZNS_10040 = "ORG_ZSTACK_NETWORK_ZNS_10040"; public static final String ORG_ZSTACK_NETWORK_ZNS_10041 = "ORG_ZSTACK_NETWORK_ZNS_10041"; public static final String ORG_ZSTACK_NETWORK_ZNS_10042 = "ORG_ZSTACK_NETWORK_ZNS_10042"; + public static final String ORG_ZSTACK_NETWORK_ZNS_10043 = "ORG_ZSTACK_NETWORK_ZNS_10043"; + public static final String ORG_ZSTACK_NETWORK_ZNS_10044 = "ORG_ZSTACK_NETWORK_ZNS_10044"; + public static final String ORG_ZSTACK_NETWORK_ZNS_10045 = "ORG_ZSTACK_NETWORK_ZNS_10045"; + public static final String ORG_ZSTACK_NETWORK_ZNS_10046 = "ORG_ZSTACK_NETWORK_ZNS_10046"; + public static final String ORG_ZSTACK_NETWORK_ZNS_10047 = "ORG_ZSTACK_NETWORK_ZNS_10047"; + public static final String ORG_ZSTACK_NETWORK_ZNS_10048 = "ORG_ZSTACK_NETWORK_ZNS_10048"; public static final String ORG_ZSTACK_PREMIUM_EXTERNALSERVICE_MARKETPLACE_10000 = "ORG_ZSTACK_PREMIUM_EXTERNALSERVICE_MARKETPLACE_10000";