mirror of
https://github.com/kubesphere/kubekey.git
synced 2025-12-25 17:12:50 +00:00
- Updated the Connector interface to return both stdout and stderr for command execution. - Modified implementations in local, kubernetes, and ssh connectors to support the new return values. - Improved documentation for the Connector interface methods for clarity. - Added error handling for stderr in command execution across connectors. - Introduced new utility functions for IP parsing and checking localhost IPs. Signed-off-by: joyceliu <joyceliu@yunify.com>
262 lines
7.3 KiB
Go
262 lines
7.3 KiB
Go
package _const
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"math"
|
|
"math/big"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// ===========================================================================
|
|
// ============================= ParseIP =================================
|
|
// ===========================================================================
|
|
|
|
// ParseIP parses a CIDR, an IP range string (e.g., "xxx-xxx"), or a single IP into a slice of actual IPs.
|
|
// Supports both IPv4 and IPv6.
|
|
func ParseIP(ip string) []string {
|
|
var availableIPs []string
|
|
|
|
ip = strings.TrimRight(ip, "/")
|
|
if strings.Contains(ip, "/") {
|
|
// Handle single IP case in CIDR format
|
|
if strings.HasSuffix(ip, "/32") || strings.HasSuffix(ip, "/128") {
|
|
availableIPs = append(availableIPs, strings.Split(ip, "/")[0])
|
|
} else {
|
|
availableIPs = getAvailableIP(ip)
|
|
}
|
|
} else if strings.Contains(ip, "-") {
|
|
ipRange := strings.SplitN(ip, "-", 2)
|
|
if len(ipRange) == 2 {
|
|
availableIPs = getAvailableIPRange(ipRange[0], ipRange[1])
|
|
}
|
|
} else {
|
|
availableIPs = append(availableIPs, ip)
|
|
}
|
|
|
|
return availableIPs
|
|
}
|
|
|
|
// getAvailableIPRange generates all IPs between the start and end IP addresses (inclusive).
|
|
// Supports both IPv4 and IPv6.
|
|
func getAvailableIPRange(ipStart, ipEnd string) []string {
|
|
var availableIPs []string
|
|
|
|
startIP := net.ParseIP(ipStart)
|
|
endIP := net.ParseIP(ipEnd)
|
|
if startIP == nil || endIP == nil {
|
|
return availableIPs
|
|
}
|
|
|
|
// Determine if IPv4 or IPv6
|
|
if startIP.To4() != nil && endIP.To4() != nil {
|
|
startIP = startIP.To4()
|
|
endIP = endIP.To4()
|
|
startIPNum := ip4ToInt(startIP)
|
|
endIPNum := ip4ToInt(endIP)
|
|
for ipNum := new(big.Int).Set(startIPNum); ipNum.Cmp(endIPNum) <= 0; ipNum.Add(ipNum, big.NewInt(1)) {
|
|
availableIPs = append(availableIPs, intToIP4(ipNum).String())
|
|
}
|
|
} else if startIP.To16() != nil && endIP.To16() != nil {
|
|
startIP = startIP.To16()
|
|
endIP = endIP.To16()
|
|
startIPNum := ip6ToInt(startIP)
|
|
endIPNum := ip6ToInt(endIP)
|
|
for ipNum := new(big.Int).Set(startIPNum); ipNum.Cmp(endIPNum) <= 0; ipNum.Add(ipNum, big.NewInt(1)) {
|
|
availableIPs = append(availableIPs, intToIP6(ipNum).String())
|
|
}
|
|
}
|
|
|
|
return availableIPs
|
|
}
|
|
|
|
// getAvailableIP calculates all available IPs in a given CIDR.
|
|
// Supports both IPv4 and IPv6.
|
|
func getAvailableIP(ipAndMask string) []string {
|
|
var availableIPs []string
|
|
|
|
ipAndMask = strings.TrimSpace(ipAndMask)
|
|
ipAndMask = iPAddressToCIDR(ipAndMask)
|
|
|
|
_, ipnet, err := net.ParseCIDR(ipAndMask)
|
|
if err != nil || ipnet == nil {
|
|
return availableIPs
|
|
}
|
|
|
|
firstIP, lastIP := networkRange(ipnet)
|
|
if firstIP == nil || lastIP == nil {
|
|
return availableIPs
|
|
}
|
|
|
|
// IPv4
|
|
if firstIP.To4() != nil {
|
|
startIPNum := ip4ToInt(firstIP)
|
|
endIPNum := ip4ToInt(lastIP)
|
|
// Exclude network and broadcast addresses if possible
|
|
for ipNum := new(big.Int).Add(startIPNum, big.NewInt(1)); ipNum.Cmp(endIPNum) < 0; ipNum.Add(ipNum, big.NewInt(1)) {
|
|
availableIPs = append(availableIPs, intToIP4(ipNum).String())
|
|
}
|
|
} else if firstIP.To16() != nil {
|
|
// IPv6: no broadcast, so include all except network address
|
|
startIPNum := ip6ToInt(firstIP)
|
|
endIPNum := ip6ToInt(lastIP)
|
|
for ipNum := new(big.Int).Set(startIPNum); ipNum.Cmp(endIPNum) <= 0; ipNum.Add(ipNum, big.NewInt(1)) {
|
|
availableIPs = append(availableIPs, intToIP6(ipNum).String())
|
|
}
|
|
}
|
|
|
|
return availableIPs
|
|
}
|
|
|
|
// ip4ToInt converts an IPv4 address to a big.Int.
|
|
func ip4ToInt(ip net.IP) *big.Int {
|
|
ip = ip.To4()
|
|
if ip == nil {
|
|
return big.NewInt(0)
|
|
}
|
|
return big.NewInt(0).SetUint64(uint64(binary.BigEndian.Uint32(ip)))
|
|
}
|
|
|
|
// intToIP4 converts a big.Int to an IPv4 address.
|
|
func intToIP4(n *big.Int) net.IP {
|
|
maxIPv4 := big.NewInt(int64(math.MaxUint32))
|
|
if n.Cmp(maxIPv4) > 0 {
|
|
n = maxIPv4
|
|
}
|
|
|
|
// Get 4-byte representation
|
|
b := n.Bytes()
|
|
if len(b) < 4 {
|
|
// Pad with leading zeros if necessary
|
|
padded := make([]byte, 4)
|
|
copy(padded[4-len(b):], b)
|
|
b = padded
|
|
} else if len(b) > 4 {
|
|
// Trim to last 4 bytes if it's longer (shouldn't happen due to clamp)
|
|
b = b[len(b)-4:]
|
|
}
|
|
|
|
return net.IP(b)
|
|
}
|
|
|
|
// ip6ToInt converts an IPv6 address to a big.Int.
|
|
func ip6ToInt(ip net.IP) *big.Int {
|
|
ip = ip.To16()
|
|
if ip == nil {
|
|
return big.NewInt(0)
|
|
}
|
|
return big.NewInt(0).SetBytes(ip)
|
|
}
|
|
|
|
// intToIP6 converts a big.Int to an IPv6 address.
|
|
func intToIP6(n *big.Int) net.IP {
|
|
b := n.Bytes()
|
|
if len(b) < 16 {
|
|
pad := make([]byte, 0, 16-len(b))
|
|
b = append(pad, b...)
|
|
}
|
|
return net.IP(b)
|
|
}
|
|
|
|
// iPAddressToCIDR converts an IP address with a subnet mask to CIDR format.
|
|
// Only supports IPv4 mask notation (e.g., "192.168.1.1/255.255.255.0").
|
|
func iPAddressToCIDR(ipAddress string) string {
|
|
if strings.Contains(ipAddress, "/") {
|
|
parts := strings.Split(ipAddress, "/")
|
|
ip := parts[0]
|
|
mask := parts[1]
|
|
if strings.Contains(mask, ".") {
|
|
mask = iPMaskStringToCIDR(mask)
|
|
}
|
|
return ip + "/" + mask
|
|
}
|
|
return ipAddress
|
|
}
|
|
|
|
// iPMaskStringToCIDR converts a subnet mask string (e.g., "255.255.255.0") to a CIDR prefix length.
|
|
func iPMaskStringToCIDR(netmask string) string {
|
|
parts := strings.Split(netmask, ".")
|
|
if len(parts) != 4 {
|
|
return "0"
|
|
}
|
|
maskBytes := make([]byte, 4)
|
|
for i, part := range parts {
|
|
val, _ := strconv.Atoi(part)
|
|
maskBytes[i] = byte(val)
|
|
}
|
|
mask := net.IPv4Mask(maskBytes[0], maskBytes[1], maskBytes[2], maskBytes[3])
|
|
ones, _ := mask.Size()
|
|
return strconv.Itoa(ones)
|
|
}
|
|
|
|
// networkRange calculates the first and last IP in a given network.
|
|
// Supports both IPv4 and IPv6.
|
|
func networkRange(network *net.IPNet) (net.IP, net.IP) {
|
|
netIP := network.IP
|
|
mask := network.Mask
|
|
if netIP == nil || mask == nil {
|
|
return nil, nil
|
|
}
|
|
ipLen := len(netIP)
|
|
if ipLen == net.IPv4len {
|
|
netIP = netIP.To4()
|
|
} else if ipLen == net.IPv6len {
|
|
netIP = netIP.To16()
|
|
}
|
|
if netIP == nil {
|
|
return nil, nil
|
|
}
|
|
startIP := make(net.IP, len(netIP))
|
|
copy(startIP, netIP.Mask(mask))
|
|
endIP := make(net.IP, len(startIP))
|
|
for i := range startIP {
|
|
endIP[i] = startIP[i] | ^mask[i]
|
|
}
|
|
return startIP, endIP
|
|
}
|
|
|
|
// ===========================================================================
|
|
// ============================= IsLocalhostIP ===========================
|
|
// ===========================================================================
|
|
|
|
// IsLocalhostIP checks if the given IP address string (ipStr) is bound to any local network interface.
|
|
// It returns true if the IP is found on any interface, false otherwise.
|
|
// This function parses the input string as an IP address, iterates over all network interfaces on the host,
|
|
// and checks if any of the interface addresses match the target IP.
|
|
func IsLocalhostIP(ipStr string) bool {
|
|
targetIP := net.ParseIP(ipStr)
|
|
if targetIP == nil {
|
|
// The input string is not a valid IP address.
|
|
return false
|
|
}
|
|
ifaces, err := net.Interfaces()
|
|
if err != nil {
|
|
// Failed to retrieve network interfaces.
|
|
return false
|
|
}
|
|
for _, iface := range ifaces {
|
|
addrs, err := iface.Addrs()
|
|
if err != nil {
|
|
// Skip this interface if its addresses cannot be retrieved.
|
|
continue
|
|
}
|
|
for _, addr := range addrs {
|
|
switch v := addr.(type) {
|
|
case *net.IPNet:
|
|
// Check if the IP address of this interface matches the target IP.
|
|
if v.IP.Equal(targetIP) {
|
|
return true
|
|
}
|
|
case *net.IPAddr:
|
|
// Check if the IP address of this interface matches the target IP.
|
|
if v.IP.Equal(targetIP) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// The target IP was not found on any local network interface.
|
|
return false
|
|
}
|