diff --git a/internal/utils/ip_utils.go b/internal/utils/ip_utils.go index ebb7fcb4..f306adc8 100644 --- a/internal/utils/ip_utils.go +++ b/internal/utils/ip_utils.go @@ -1,14 +1,17 @@ package utils import ( + "bytes" "encoding/binary" + "errors" + "github.com/iwind/TeaGo/types" "math/big" "net" "regexp" "strings" ) -// 将IP转换为整型 +// IP2Long 将IP转换为整型 func IP2Long(ip string) uint64 { s := net.ParseIP(ip) if len(s) != 16 { @@ -23,7 +26,7 @@ func IP2Long(ip string) uint64 { return uint64(binary.BigEndian.Uint32(s.To4())) } -// 判断是否为IPv4 +// IsIPv4 判断是否为IPv4 func IsIPv4(ip string) bool { if !regexp.MustCompile(`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$`).MatchString(ip) { return false @@ -34,10 +37,100 @@ func IsIPv4(ip string) bool { return true } -// 判断是否为IPv6 +// IsIPv6 判断是否为IPv6 func IsIPv6(ip string) bool { if !strings.Contains(ip, ":") { return false } return len(net.ParseIP(ip)) == net.IPv6len } + +// ExtractIP 分解IP +// 只支持D段掩码的CIDR +// 最多只记录255个值 +func ExtractIP(ipStrings string) ([]string, error) { + ipStrings = strings.ReplaceAll(ipStrings, " ", "") + + // CIDR + if strings.Contains(ipStrings, "/") { + _, cidrNet, err := net.ParseCIDR(ipStrings) + if err != nil { + return nil, err + } + + var index = strings.Index(ipStrings, "/") + var ipFrom = ipStrings[:index] + var bits = types.Int(ipStrings[index+1:]) + if bits < 24 { + return nil, errors.New("CIDR bits should be greater than 24") + } + + var ipv4 = net.ParseIP(ipFrom).To4() + if len(ipv4) == 0 { + return nil, errors.New("support IPv4 only") + } + + var result = []string{} + ipv4[3] = 0 // 从0开始 + for i := 0; i <= 255; i++ { + if cidrNet.Contains(ipv4) { + result = append(result, ipv4.String()) + } + ipv4 = NextIP(ipv4) + } + return result, nil + } + + // IP Range + if strings.Contains(ipStrings, "-") { + var index = strings.Index(ipStrings, "-") + var ipFromString = ipStrings[:index] + var ipToString = ipStrings[index+1:] + + var ipFrom = net.ParseIP(ipFromString).To4() + if len(ipFrom) == 0 { + return nil, errors.New("invalid ip '" + ipFromString + "'") + } + + var ipTo = net.ParseIP(ipToString).To4() + if len(ipTo) == 0 { + return nil, errors.New("invalid ip '" + ipToString + "'") + } + + if bytes.Compare(ipFrom, ipTo) > 0 { + ipFrom, ipTo = ipTo, ipFrom + } + + var result = []string{} + for i := 0; i < 255; i++ { + if bytes.Compare(ipFrom, ipTo) > 0 { + break + } + result = append(result, ipFrom.String()) + ipFrom = NextIP(ipFrom) + } + return result, nil + } + + return []string{ipStrings}, nil +} + +// NextIP IP增加1 +func NextIP(prevIP net.IP) net.IP { + var ip = make(net.IP, len(prevIP)) + copy(ip, prevIP) + var index = len(ip) - 1 + for { + if ip[index] == 255 { + ip[index] = 0 + index-- + if index < 0 { + break + } + } else { + ip[index]++ + break + } + } + return ip +} diff --git a/internal/utils/ip_utils_test.go b/internal/utils/ip_utils_test.go index 8c891ce9..e3bda6ac 100644 --- a/internal/utils/ip_utils_test.go +++ b/internal/utils/ip_utils_test.go @@ -1,6 +1,7 @@ package utils import ( + "net" "testing" ) @@ -83,3 +84,27 @@ func TestIsIPv6(t *testing.T) { } } } + +func TestExtractIP(t *testing.T) { + t.Log(ExtractIP("192.168.1.100")) +} + +func TestExtractIP_CIDR(t *testing.T) { + t.Log(ExtractIP("192.168.2.100/24")) +} + +func TestExtractIP_Range(t *testing.T) { + t.Log(ExtractIP("192.168.2.100 - 192.168.4.2")) +} + +func TestNextIP(t *testing.T) { + for _, ip := range []string{"192.168.1.1", "0.0.0.0", "255.255.255.255", "192.168.2.255", "192.168.255.255"} { + t.Log(ip+":", NextIP(net.ParseIP(ip).To4())) + } +} + +func TestNextIP_Copy(t *testing.T) { + var ip = net.ParseIP("192.168.1.100") + var nextIP = NextIP(ip) + t.Log(ip, nextIP) +} \ No newline at end of file diff --git a/internal/web/actions/default/clusters/cluster/createNode.go b/internal/web/actions/default/clusters/cluster/createNode.go index e072a5c9..4e66fa31 100644 --- a/internal/web/actions/default/clusters/cluster/createNode.go +++ b/internal/web/actions/default/clusters/cluster/createNode.go @@ -3,6 +3,7 @@ package cluster import ( "encoding/json" "github.com/TeaOSLab/EdgeAdmin/internal/oplogs" + "github.com/TeaOSLab/EdgeAdmin/internal/utils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" @@ -167,9 +168,13 @@ func (this *CreateNodeAction) RunPost(params struct { nodeId := createResp.NodeId // IP地址 + var resultIPAddresses = []string{} for _, addr := range ipAddresses { + var resultAddrIds = []int64{} + addrId := addr.GetInt64("id") if addrId > 0 { + resultAddrIds = append(resultAddrIds, addrId) _, err = this.RPC().NodeIPAddressRPC().UpdateNodeIPAddressNodeId(this.AdminContext(), &pb.UpdateNodeIPAddressNodeIdRequest{ NodeIPAddressId: addrId, NodeId: nodeId, @@ -178,20 +183,50 @@ func (this *CreateNodeAction) RunPost(params struct { this.ErrorPage(err) return } + + resultIPAddresses = append(resultIPAddresses, addr.GetString("ip")) } else { - createResp, err := this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{ - NodeId: nodeId, - Role: nodeconfigs.NodeRoleNode, - Name: addr.GetString("name"), - Ip: addr.GetString("ip"), - CanAccess: addr.GetBool("canAccess"), - IsUp: addr.GetBool("isUp"), - }) + var ipStrings = addr.GetString("ip") + result, err := utils.ExtractIP(ipStrings) if err != nil { - this.ErrorPage(err) - return + this.Fail("节点创建成功,但是保存IP失败:" + err.Error()) + } + + resultIPAddresses = append(resultIPAddresses, result...) + + if len(result) == 1 { + // 单个创建 + createResp, err := this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{ + NodeId: nodeId, + Role: nodeconfigs.NodeRoleNode, + Name: addr.GetString("name"), + Ip: result[0], + CanAccess: addr.GetBool("canAccess"), + IsUp: addr.GetBool("isUp"), + }) + if err != nil { + this.ErrorPage(err) + return + } + addrId = createResp.NodeIPAddressId + resultAddrIds = append(resultAddrIds, addrId) + } else if len(result) > 1 { + // 批量创建 + createResp, err := this.RPC().NodeIPAddressRPC().CreateNodeIPAddresses(this.AdminContext(), &pb.CreateNodeIPAddressesRequest{ + NodeId: nodeId, + Role: nodeconfigs.NodeRoleNode, + Name: addr.GetString("name"), + IpList: result, + CanAccess: addr.GetBool("canAccess"), + IsUp: addr.GetBool("isUp"), + GroupValue: ipStrings, + }) + if err != nil { + this.ErrorPage(err) + return + } + resultAddrIds = append(resultAddrIds, createResp.NodeIPAddressIds...) } - addrId = createResp.NodeIPAddressId } // 阈值 @@ -202,13 +237,16 @@ func (this *CreateNodeAction) RunPost(params struct { this.ErrorPage(err) return } - _, err = this.RPC().NodeIPAddressThresholdRPC().UpdateAllNodeIPAddressThresholds(this.AdminContext(), &pb.UpdateAllNodeIPAddressThresholdsRequest{ - NodeIPAddressId: addrId, - NodeIPAddressThresholdsJSON: thresholdsJSON, - }) - if err != nil { - this.ErrorPage(err) - return + + for _, addrId := range resultAddrIds { + _, err = this.RPC().NodeIPAddressThresholdRPC().UpdateAllNodeIPAddressThresholds(this.AdminContext(), &pb.UpdateAllNodeIPAddressThresholdsRequest{ + NodeIPAddressId: addrId, + NodeIPAddressThresholdsJSON: thresholdsJSON, + }) + if err != nil { + this.ErrorPage(err) + return + } } } } @@ -224,11 +262,6 @@ func (this *CreateNodeAction) RunPost(params struct { return } if nodeResp.Node != nil { - var addresses = []string{} - for _, addrMap := range ipAddresses { - addresses = append(addresses, addrMap.GetString("ip")) - } - var grantMap maps.Map = nil grantId := params.GrantId if grantId > 0 { @@ -253,7 +286,7 @@ func (this *CreateNodeAction) RunPost(params struct { "name": nodeResp.Node.Name, "uniqueId": nodeResp.Node.UniqueId, "secret": nodeResp.Node.Secret, - "addresses": addresses, + "addresses": resultIPAddresses, "login": maps.Map{ "id": 0, "name": "SSH", diff --git a/internal/web/actions/default/nodes/ipAddresses/createPopup.go b/internal/web/actions/default/nodes/ipAddresses/createPopup.go index c58bfa4f..d4f8c678 100644 --- a/internal/web/actions/default/nodes/ipAddresses/createPopup.go +++ b/internal/web/actions/default/nodes/ipAddresses/createPopup.go @@ -3,6 +3,7 @@ package ipAddresses import ( "encoding/json" teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" + "github.com/TeaOSLab/EdgeAdmin/internal/utils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/iwind/TeaGo/actions" @@ -39,9 +40,15 @@ func (this *CreatePopupAction) RunPost(params struct { Field("ip", params.IP). Require("请输入IP地址") - ip := net.ParseIP(params.IP) - if len(ip) == 0 { - this.FailField("ip", "请输入正确的IP") + result, err := utils.ExtractIP(params.IP) + if err != nil { + this.Fail("IP格式错误'" + params.IP + "'") + } + + for _, ip := range result { + if len(net.ParseIP(ip)) == 0 { + this.FailField("ip", "请输入正确的IP") + } } // 阈值设置 diff --git a/internal/web/actions/default/nodes/ipAddresses/ipaddressutils/utils.go b/internal/web/actions/default/nodes/ipAddresses/ipaddressutils/utils.go index 30b8d073..d64c075a 100644 --- a/internal/web/actions/default/nodes/ipAddresses/ipaddressutils/utils.go +++ b/internal/web/actions/default/nodes/ipAddresses/ipaddressutils/utils.go @@ -2,6 +2,7 @@ package ipaddressutils import ( "encoding/json" + "github.com/TeaOSLab/EdgeAdmin/internal/utils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" @@ -16,8 +17,11 @@ func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64, return err } for _, addr := range addresses { + var resultAddrIds = []int64{} addrId := addr.GetInt64("id") if addrId > 0 { + resultAddrIds = append(resultAddrIds, addrId) + var isOn = false if !addr.Has("isOn") { // 兼容老版本 isOn = true @@ -37,18 +41,40 @@ func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64, return err } } else { - createResp, err := parentAction.RPC().NodeIPAddressRPC().CreateNodeIPAddress(parentAction.AdminContext(), &pb.CreateNodeIPAddressRequest{ - NodeId: nodeId, - Role: role, - Name: addr.GetString("name"), - Ip: addr.GetString("ip"), - CanAccess: addr.GetBool("canAccess"), - IsUp: addr.GetBool("isUp"), - }) - if err != nil { - return err + var ipStrings = addr.GetString("ip") + result, _ := utils.ExtractIP(ipStrings) + + if len(result) == 1 { + // 单个创建 + createResp, err := parentAction.RPC().NodeIPAddressRPC().CreateNodeIPAddress(parentAction.AdminContext(), &pb.CreateNodeIPAddressRequest{ + NodeId: nodeId, + Role: role, + Name: addr.GetString("name"), + Ip: result[0], + CanAccess: addr.GetBool("canAccess"), + IsUp: addr.GetBool("isUp"), + }) + if err != nil { + return err + } + addrId = createResp.NodeIPAddressId + resultAddrIds = append(resultAddrIds, addrId) + } else if len(result) > 1 { + // 批量创建 + createResp, err := parentAction.RPC().NodeIPAddressRPC().CreateNodeIPAddresses(parentAction.AdminContext(), &pb.CreateNodeIPAddressesRequest{ + NodeId: nodeId, + Role: role, + Name: addr.GetString("name"), + IpList: result, + CanAccess: addr.GetBool("canAccess"), + IsUp: addr.GetBool("isUp"), + GroupValue: ipStrings, + }) + if err != nil { + return err + } + resultAddrIds = append(resultAddrIds, createResp.NodeIPAddressIds...) } - addrId = createResp.NodeIPAddressId } // 保存阈值 @@ -58,12 +84,15 @@ func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64, if err != nil { return err } - _, err = parentAction.RPC().NodeIPAddressThresholdRPC().UpdateAllNodeIPAddressThresholds(parentAction.AdminContext(), &pb.UpdateAllNodeIPAddressThresholdsRequest{ - NodeIPAddressId: addrId, - NodeIPAddressThresholdsJSON: thresholdsJSON, - }) - if err != nil { - return err + + for _, addrId := range resultAddrIds { + _, err = parentAction.RPC().NodeIPAddressThresholdRPC().UpdateAllNodeIPAddressThresholds(parentAction.AdminContext(), &pb.UpdateAllNodeIPAddressThresholdsRequest{ + NodeIPAddressId: addrId, + NodeIPAddressThresholdsJSON: thresholdsJSON, + }) + if err != nil { + return err + } } } } diff --git a/internal/web/actions/default/nodes/ipAddresses/updatePopup.go b/internal/web/actions/default/nodes/ipAddresses/updatePopup.go index bdfcadd6..ec245ec3 100644 --- a/internal/web/actions/default/nodes/ipAddresses/updatePopup.go +++ b/internal/web/actions/default/nodes/ipAddresses/updatePopup.go @@ -3,6 +3,7 @@ package ipAddresses import ( "encoding/json" teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" + "github.com/TeaOSLab/EdgeAdmin/internal/utils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" @@ -57,9 +58,22 @@ func (this *UpdatePopupAction) RunPost(params struct { } } - ip := net.ParseIP(params.IP) - if len(ip) == 0 { - this.Fail("请输入正确的IP") + if params.AddressId > 0 { + ip := net.ParseIP(params.IP) + if len(ip) == 0 { + this.Fail("请输入正确的IP") + } + } else { + result, err := utils.ExtractIP(params.IP) + if err != nil { + this.Fail("IP格式错误'" + params.IP + "'") + } + + for _, ip := range result { + if len(net.ParseIP(ip)) == 0 { + this.FailField("ip", "请输入正确的IP") + } + } } var thresholds = []*nodeconfigs.IPAddressThresholdConfig{} diff --git a/internal/web/actions/default/ns/clusters/cluster/createNode.go b/internal/web/actions/default/ns/clusters/cluster/createNode.go index 1a6fd24f..1062deb1 100644 --- a/internal/web/actions/default/ns/clusters/cluster/createNode.go +++ b/internal/web/actions/default/ns/clusters/cluster/createNode.go @@ -3,6 +3,7 @@ package cluster import ( "encoding/json" "github.com/TeaOSLab/EdgeAdmin/internal/oplogs" + "github.com/TeaOSLab/EdgeAdmin/internal/utils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" @@ -87,14 +88,31 @@ func (this *CreateNodeAction) RunPost(params struct { NodeId: nodeId, }) } else { - _, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{ - NodeId: nodeId, - Role: nodeconfigs.NodeRoleDNS, - Name: addrMap.GetString("name"), - Ip: addrMap.GetString("ip"), - CanAccess: addrMap.GetBool("canAccess"), - IsUp: addrMap.GetBool("isUp"), - }) + var ipStrings = addrMap.GetString("ip") + result, _ := utils.ExtractIP(ipStrings) + + if len(result) == 1 { + // 单个创建 + _, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{ + NodeId: nodeId, + Role: nodeconfigs.NodeRoleDNS, + Name: addrMap.GetString("name"), + Ip: result[0], + CanAccess: addrMap.GetBool("canAccess"), + IsUp: addrMap.GetBool("isUp"), + }) + } else if len(result) > 1 { + // 批量创建 + _, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddresses(this.AdminContext(), &pb.CreateNodeIPAddressesRequest{ + NodeId: nodeId, + Role: nodeconfigs.NodeRoleDNS, + Name: addrMap.GetString("name"), + IpList: result, + CanAccess: addrMap.GetBool("canAccess"), + IsUp: addrMap.GetBool("isUp"), + GroupValue: ipStrings, + }) + } } if err != nil { this.ErrorPage(err) diff --git a/web/views/@default/nodes/ipAddresses/createPopup.html b/web/views/@default/nodes/ipAddresses/createPopup.html index f701f3f0..aa954e2b 100644 --- a/web/views/@default/nodes/ipAddresses/createPopup.html +++ b/web/views/@default/nodes/ipAddresses/createPopup.html @@ -8,6 +8,7 @@ IP地址 * +

支持单个IP、CIDR格式的一组IP(位数不能小于24)、IP范围(类似于x.x.x.x-x.x.x.x,最多只取256个)等。

diff --git a/web/views/@default/nodes/ipAddresses/updatePopup.html b/web/views/@default/nodes/ipAddresses/updatePopup.html index 611c3da4..0829d1eb 100644 --- a/web/views/@default/nodes/ipAddresses/updatePopup.html +++ b/web/views/@default/nodes/ipAddresses/updatePopup.html @@ -9,6 +9,8 @@ IP地址 * +

只支持单个IP。

+

支持单个IP、CIDR格式的一组IP(位数不能小于24)、IP范围(类似于x.x.x.x-x.x.x.x,最多只取256个)等。