From 8e6e30d57fd42674e02e0ee8eb235445facc3e16 Mon Sep 17 00:00:00 2001 From: wanyaoqi <18528551+wanyaoqi@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:22:22 +0800 Subject: [PATCH] fix(region,baremetal): ipv6 baremetal register support (#23422) --- pkg/baremetal/handler/handlers.go | 9 +++- pkg/baremetal/manager.go | 68 ++++++++++++++++++++++----- pkg/baremetal/tasks/bm_register.go | 25 +++++++--- pkg/baremetal/tasks/cdrom.go | 9 +++- pkg/cloudcommon/agent/agent.go | 13 ++++- pkg/compute/misc/handler.go | 9 ++-- pkg/compute/models/baremetalagents.go | 2 +- pkg/compute/models/hosts.go | 5 +- pkg/compute/models/networks.go | 32 +++++++++++-- 9 files changed, 140 insertions(+), 32 deletions(-) diff --git a/pkg/baremetal/handler/handlers.go b/pkg/baremetal/handler/handlers.go index 88473c6d2f..3d185d65aa 100644 --- a/pkg/baremetal/handler/handlers.go +++ b/pkg/baremetal/handler/handlers.go @@ -16,7 +16,9 @@ package handler import ( "context" + "fmt" "net/http" + "strings" "time" "yunion.io/x/jsonutils" @@ -193,7 +195,12 @@ func handleBaremetalValidateIPMI() appsrv.FilterHandler { if err := body.Unmarshal(input); err != nil { return nil, errors.Wrapf(err, "unmarshal validate ipmi request: %s", body) } - redfishCli := redfish.NewRedfishDriver(ctx, "https://"+input.Ip, input.Username, input.Password, false) + + var endpoint = "https://" + input.Ip + if strings.Contains(input.Ip, ":") { + endpoint = fmt.Sprintf("https://[%s]", input.Ip) + } + redfishCli := redfish.NewRedfishDriver(ctx, endpoint, input.Username, input.Password, false) resp := &baremetalapi.ValidateIPMIResponse{} if redfishCli == nil { resp.IsRedfishSupported = false diff --git a/pkg/baremetal/manager.go b/pkg/baremetal/manager.go index d428bcc101..582b33c8a2 100644 --- a/pkg/baremetal/manager.go +++ b/pkg/baremetal/manager.go @@ -321,7 +321,12 @@ func (m *SBaremetalManager) RegisterBaremetal(ctx context.Context, userCred mccl return } - input.IpAddr, err = m.fetchIpmiIp(sshCli) + isIpv6Addr := false + if strings.Contains(input.RemoteIp, ":") { + isIpv6Addr = true + } + input.IpAddr, err = m.fetchIpmiIp(sshCli, isIpv6Addr) + log.Infof("find ipmi addr %s", input.IpAddr) if input.isTimeout() { return @@ -375,17 +380,48 @@ func (m *SBaremetalManager) RegisterBaremetal(ctx context.Context, userCred mccl registerTask.DoPrepare(ctx, sshCli, registered) } -func (m *SBaremetalManager) fetchIpmiIp(sshCli *ssh.Client) (string, error) { - res, err := sshCli.RawRun(`/usr/bin/ipmitool lan print | grep "IP Address "`) - if err != nil { - return "", err - } - if len(res) == 1 { - segs := strings.Fields(res[0]) - if len(segs) == 4 { - return strings.TrimSpace(segs[3]), nil +func (m *SBaremetalManager) fetchIpmiIp(sshCli *ssh.Client, isIpv6Addr bool) (string, error) { + if !isIpv6Addr { + res, err := sshCli.RawRun(`/usr/bin/ipmitool lan print | grep "IP Address "`) + if err != nil { + return "", err + } + if len(res) == 1 { + segs := strings.Fields(res[0]) + if len(segs) == 4 { + return strings.TrimSpace(segs[3]), nil + } + } + } else { + res, err := sshCli.Run(`/usr/bin/ipmitool lan6 print`) + if err != nil { + return "", err + } + for i, line := range res { + if strings.HasPrefix(line, "IPv6 Static Address") || strings.HasPrefix(line, "IPv6 Dynamic Address") { + if len(res)-i > 3 { + enabledSegs := strings.Fields(res[i+1]) + log.Infof("enabled segs %#v", enabledSegs) + if len(enabledSegs) != 2 || enabledSegs[0] != "Enabled:" || enabledSegs[1] != "yes" { + continue + } + statusSegs := strings.Fields(res[i+3]) + log.Infof("status segs %#v", statusSegs) + if len(enabledSegs) != 2 || statusSegs[0] != "Status:" || statusSegs[1] != "active" { + continue + } + addrSegs := strings.Fields(res[i+2]) + if len(addrSegs) != 2 || addrSegs[0] != "Address:" { + continue + } + ipv6Addr := strings.Split(addrSegs[1], "/") + + return ipv6Addr[0], nil + } + } } } + return "", fmt.Errorf("Failed to find ipmi ip address") } @@ -394,6 +430,11 @@ func (m *SBaremetalManager) checkNetworkFromIp(ip string) (string, error) { params.Set("ip", jsonutils.NewString(ip)) params.Set("scope", jsonutils.NewString("system")) params.Set("is_classic", jsonutils.JSONTrue) + params.Set("provider", jsonutils.NewString(api.CLOUD_PROVIDER_ONECLOUD)) + params.Set("limit", jsonutils.NewInt(0)) + // use default vpc + params.Set("vpc", jsonutils.NewString(api.DEFAULT_VPC_ID)) + res, err := modules.Networks.List(m.GetClientSession(), params) if err != nil { return "", fmt.Errorf("Fetch network by ip %s failed: %s", ip, err) @@ -1793,8 +1834,11 @@ func (b *SBaremetalInstance) GetRedfishCli(ctx context.Context) redfish.IRedfish return nil } conf := b.GetIPMIConfig() - return redfish.NewRedfishDriver(ctx, "https://"+conf.IpAddr, - conf.Username, conf.Password, false) + var endpoint = "https://" + conf.IpAddr + if strings.Contains(conf.IpAddr, ":") { + endpoint = fmt.Sprintf("https://[%s]", conf.IpAddr) + } + return redfish.NewRedfishDriver(ctx, endpoint, conf.Username, conf.Password, false) } func (b *SBaremetalInstance) GetIPMILanChannel() uint8 { diff --git a/pkg/baremetal/tasks/bm_register.go b/pkg/baremetal/tasks/bm_register.go index 7801bffe21..9b9c1c2712 100644 --- a/pkg/baremetal/tasks/bm_register.go +++ b/pkg/baremetal/tasks/bm_register.go @@ -84,12 +84,20 @@ func (s *sBaremetalRegisterTask) getSession() *mcclient.ClientSession { } func (s *sBaremetalRegisterTask) getAccessDevMacAddr(ip string) (string, error) { - nicsRet, err := s.SshCli.Run("/sbin/ip -o -4 addr show") - if err != nil { - return "", fmt.Errorf("Failed get access nic %s", err) - } - var dev string + var nicsRet []string + var err error + if strings.Contains(ip, ":") { // ipv6 + nicsRet, err = s.SshCli.Run("/sbin/ip -o -6 addr show") + if err != nil { + return "", fmt.Errorf("Failed get access nic %s", err) + } + } else { // ipv4 + nicsRet, err = s.SshCli.Run("/sbin/ip -o -4 addr show") + if err != nil { + return "", fmt.Errorf("Failed get access nic %s", err) + } + } for i := 0; i < len(nicsRet); i++ { if strings.Contains(nicsRet[i], ip+"/") { segs := strings.Split(nicsRet[i], " ") @@ -102,6 +110,7 @@ func (s *sBaremetalRegisterTask) getAccessDevMacAddr(ip string) (string, error) if len(dev) == 0 { return "", fmt.Errorf("Can't get access dev") } + log.Infof("Access dev is %s", dev) macRet, err := s.SshCli.Run("/sbin/ip a show " + dev) if err != nil || len(macRet) < 2 { @@ -205,7 +214,11 @@ func (s *sBaremetalRegisterTask) UpdateBaremetal(ctx context.Context) (string, e } func (s *sBaremetalRegisterTask) doRedfishProbe(ctx context.Context) (redfishSupport bool, cdromBoot bool) { - redfishCli := redfish.NewRedfishDriver(ctx, "https://"+s.IpmiIpAddr, s.IpmiUsername, s.IpmiPassword, false) + var endpoint = "https://" + s.IpmiIpAddr + if strings.Contains(s.IpmiIpAddr, ":") { + endpoint = fmt.Sprintf("https://[%s]", s.IpmiIpAddr) + } + redfishCli := redfish.NewRedfishDriver(ctx, endpoint, s.IpmiUsername, s.IpmiPassword, false) if redfishCli != nil { _, cdInfo, _ := redfishCli.GetVirtualCdromInfo(ctx) redfishSupport = true diff --git a/pkg/baremetal/tasks/cdrom.go b/pkg/baremetal/tasks/cdrom.go index 881df4cd43..0dff90af8f 100644 --- a/pkg/baremetal/tasks/cdrom.go +++ b/pkg/baremetal/tasks/cdrom.go @@ -16,7 +16,9 @@ package tasks import ( "context" + "fmt" "path/filepath" + "strings" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -78,7 +80,12 @@ func (self *SBaremetalCdromTask) getRedfishApi(ctx context.Context) (redfish.IRe if !ipmiInfo.CdromBoot { return nil, errors.Error("mount Virtual Cdrom not supported") } - redfishCli := redfish.NewRedfishDriver(ctx, "https://"+ipmiInfo.IpAddr, ipmiInfo.Username, ipmiInfo.Password, false) + + var endpoint = "https://" + ipmiInfo.IpAddr + if strings.Contains(ipmiInfo.IpAddr, ":") { + endpoint = fmt.Sprintf("https://[%s]", ipmiInfo.IpAddr) + } + redfishCli := redfish.NewRedfishDriver(ctx, endpoint, ipmiInfo.Username, ipmiInfo.Password, false) if redfishCli != nil { return redfishCli, nil } else { diff --git a/pkg/cloudcommon/agent/agent.go b/pkg/cloudcommon/agent/agent.go index d2c140eea3..9bbe459452 100644 --- a/pkg/cloudcommon/agent/agent.go +++ b/pkg/cloudcommon/agent/agent.go @@ -17,6 +17,7 @@ package agent import ( "fmt" "net" + "strings" "time" "yunion.io/x/jsonutils" @@ -82,7 +83,14 @@ func (agent *SBaseAgent) GetListenIPs() []net.IP { func (agent *SBaseAgent) FindListenIP(listenAddr string) (net.IP, error) { ips := agent.GetListenIPs() if listenAddr == "" { - return ips[0], nil + for i := range ips { + ipstr := ips[i].String() + if strings.HasPrefix(ipstr, netutils2.SECRET_PREFIX) { + continue + } + return ips[i], nil + } + return nil, fmt.Errorf("Not Address on Interface %#v", agent.ListenInterface) } if listenAddr == "0.0.0.0" { return net.ParseIP(listenAddr), nil @@ -287,6 +295,9 @@ func (agent *SBaseAgent) GetManagerUri() string { if agent.IAgent().GetEnableSsl() { proto = "https" } + if accessIP.To4() == nil { // ipv6 addr + return fmt.Sprintf("%s://[%s]:%d", proto, accessIP, agent.IAgent().GetPort()) + } return fmt.Sprintf("%s://%s:%d", proto, accessIP, agent.IAgent().GetPort()) } diff --git a/pkg/compute/misc/handler.go b/pkg/compute/misc/handler.go index 2c96d9d7a8..492701ffc0 100644 --- a/pkg/compute/misc/handler.go +++ b/pkg/compute/misc/handler.go @@ -20,6 +20,7 @@ import ( "net" "net/http" + "yunion.io/x/log" "yunion.io/x/pkg/tristate" "yunion.io/x/onecloud/pkg/apis/compute" @@ -52,9 +53,10 @@ func getBmAgentUrl(ctx context.Context, w http.ResponseWriter, r *http.Request) return } } - - n, _ := models.NetworkManager.GetOnPremiseNetworkOfIP(ipAddr, "", tristate.None) + log.Infof("getBmAgentUrl request ipaddr %s", ipAddr) + n, err := models.NetworkManager.GetOnPremiseNetworkOfIP(ipAddr, "", tristate.None) if n == nil { + log.Errorf("failed get network of ip %s: %s", ipAddr, err) httperrors.NotFoundError(ctx, w, "Network not found") return } @@ -67,7 +69,8 @@ func getBmAgentUrl(ctx context.Context, w http.ResponseWriter, r *http.Request) return } - fmt.Fprintf(w, "%s", bmAgent.ManagerUri) + ret := fmt.Sprintf("%s %s", bmAgent.ManagerUri, ipAddr) + fmt.Fprintf(w, "%s", ret) } func getBmPrepareScript(ctx context.Context, w http.ResponseWriter, r *http.Request) { diff --git a/pkg/compute/models/baremetalagents.go b/pkg/compute/models/baremetalagents.go index 7d533c0191..1b336ca59f 100644 --- a/pkg/compute/models/baremetalagents.go +++ b/pkg/compute/models/baremetalagents.go @@ -44,7 +44,7 @@ type SBaremetalagent struct { SZoneResourceBase `width:"128" charset:"ascii" nullable:"false" list:"admin" update:"admin" create:"admin_required"` Status string `width:"36" charset:"ascii" nullable:"false" default:"disable" create:"optional"` - AccessIp string `width:"16" charset:"ascii" nullable:"false" list:"admin" update:"admin" create:"admin_required"` + AccessIp string `width:"64" charset:"ascii" nullable:"false" list:"admin" update:"admin" create:"admin_required"` ManagerUri string `width:"256" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_required"` // ZoneId string `width:"128" charset:"ascii" nullable:"false" list:"admin" update:"admin" create:"admin_required"` diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index b99578dc47..6666d15a09 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -4913,8 +4913,8 @@ func fetchIpmiInfo(data api.HostIpmiAttributes, hostId string) (types.SIPMIInfo, info.Password = data.IpmiPassword } } - if len(data.IpmiIpAddr) > 0 && !regutils.MatchIP4Addr(data.IpmiIpAddr) { - msg := fmt.Sprintf("ipmi_ip_addr: %v not valid ipv4 address", data.IpmiIpAddr) + if len(data.IpmiIpAddr) > 0 && !regutils.MatchIP4Addr(data.IpmiIpAddr) && !regutils.MatchIP6Addr(data.IpmiIpAddr) { + msg := fmt.Sprintf("ipmi_ip_addr: %v not valid address", data.IpmiIpAddr) log.Errorf("%s", msg) return info, errors.Wrap(httperrors.ErrInvalidFormat, msg) } @@ -5610,6 +5610,7 @@ func (hh *SHost) PerformInitialize( guest.ProjectId = userCred.GetProjectId() guest.DomainId = userCred.GetProjectDomainId() guest.Status = api.VM_RUNNING + guest.PowerStates = api.VM_POWER_STATES_ON guest.OsType = "Linux" guest.SetModelManager(GuestManager, guest) err = GuestManager.TableSpec().Insert(ctx, guest) diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index 767a86deb8..1174862fb5 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -921,17 +921,39 @@ func (manager *SNetworkManager) fetchAllOnpremiseNetworks(serverType string, isP } func (manager *SNetworkManager) GetOnPremiseNetworkOfIP(ipAddr string, serverType string, isPublic tristate.TriState) (*SNetwork, error) { - address, err := netutils.NewIPV4Addr(ipAddr) - if err != nil { - return nil, errors.Wrap(err, "NewIPV4Addr") + var addr4 netutils.IPV4Addr + var addr6 netutils.IPV6Addr + var isIpv6Addr = false + var err error + if strings.Contains(ipAddr, ":") { + isIpv6Addr = true } + + if isIpv6Addr { + addr6, err = netutils.NewIPV6Addr(ipAddr) + if err != nil { + return nil, errors.Wrap(err, "NewIPV6Addr") + } + } else { + addr4, err = netutils.NewIPV4Addr(ipAddr) + if err != nil { + return nil, errors.Wrap(err, "NewIPV4Addr") + } + } + nets, err := manager.fetchAllOnpremiseNetworks(serverType, isPublic) if err != nil { return nil, errors.Wrap(err, "fetchAllOnpremiseNetworks") } for _, n := range nets { - if n.IsAddressInRange(address) { - return &n, nil + if isIpv6Addr { + if n.IsAddress6InRange(addr6) { + return &n, nil + } + } else { + if n.IsAddressInRange(addr4) { + return &n, nil + } } } return nil, sql.ErrNoRows