From cf71a29b673722d993f84fec5544aed179862607 Mon Sep 17 00:00:00 2001 From: wanyaoqi <18528551+wanyaoqi@users.noreply.github.com> Date: Fri, 30 May 2025 10:29:38 +0800 Subject: [PATCH] feat(region,host): qga deploy network config on attach nics (#22547) --- cmd/climc/shell/compute/servernetworks.go | 6 +- pkg/apis/compute/guests.go | 2 +- pkg/compute/tasks/guest_sync_task.go | 2 +- pkg/hostman/guestman/guest-agent.go | 14 ++++- pkg/hostman/guestman/guesttasks.go | 74 ++++++++++++++++++++++- pkg/hostman/monitor/qga/qga.go | 37 ++++++++++++ pkg/mcclient/options/compute/servers.go | 4 +- 7 files changed, 131 insertions(+), 8 deletions(-) diff --git a/cmd/climc/shell/compute/servernetworks.go b/cmd/climc/shell/compute/servernetworks.go index d77dd45f01..7545f45475 100644 --- a/cmd/climc/shell/compute/servernetworks.go +++ b/cmd/climc/shell/compute/servernetworks.go @@ -155,8 +155,9 @@ func init() { }) type ServerAttachNetworkOptions struct { - SERVER string `help:"ID or Name of server"` - NETDESC []string `help:"Network description"` + SERVER string `help:"ID or Name of server"` + DisableSyncConfig bool `help:"Disable sync config"` + NETDESC []string `help:"Network description"` } R(&ServerAttachNetworkOptions{}, "server-attach-network", "Attach a server to a virtual network", func(s *mcclient.ClientSession, args *ServerAttachNetworkOptions) error { input := compute.AttachNetworkInput{} @@ -167,6 +168,7 @@ func init() { } input.Nets = append(input.Nets, conf) } + input.DisableSyncConfig = &args.DisableSyncConfig params := jsonutils.Marshal(input) srv, err := modules.Servers.PerformAction(s, args.SERVER, "attachnetwork", params) if err != nil { diff --git a/pkg/apis/compute/guests.go b/pkg/apis/compute/guests.go index 5f929ede26..849b316204 100644 --- a/pkg/apis/compute/guests.go +++ b/pkg/apis/compute/guests.go @@ -1175,7 +1175,7 @@ type ServerQgaGetNetworkInput struct { } type ServerQgaTimeoutInput struct { - // qga execute timeout millisecond + // qga execute timeout second Timeout int } diff --git a/pkg/compute/tasks/guest_sync_task.go b/pkg/compute/tasks/guest_sync_task.go index c723fe9d43..76c2f89e16 100644 --- a/pkg/compute/tasks/guest_sync_task.go +++ b/pkg/compute/tasks/guest_sync_task.go @@ -96,7 +96,7 @@ func (self *GuestSyncConfTask) StartRestartNetworkTask(ctx context.Context, gues // try use qga restart network err = func() error { host, _ := guest.GetHost() - err = guest.GetDriver().QgaRequestGuestPing(ctx, self.GetTaskRequestHeader(), host, guest, false, &api.ServerQgaTimeoutInput{1000}) + err = guest.GetDriver().QgaRequestGuestPing(ctx, self.GetTaskRequestHeader(), host, guest, false, &api.ServerQgaTimeoutInput{1}) if err != nil { return errors.Wrap(err, "qga guest-ping") } diff --git a/pkg/hostman/guestman/guest-agent.go b/pkg/hostman/guestman/guest-agent.go index 74c67901ef..d0d2e78edf 100644 --- a/pkg/hostman/guestman/guest-agent.go +++ b/pkg/hostman/guestman/guest-agent.go @@ -20,6 +20,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" + "yunion.io/x/onecloud/pkg/hostman/guestman/desc" deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis" "yunion.io/x/onecloud/pkg/hostman/monitor" "yunion.io/x/onecloud/pkg/httperrors" @@ -107,7 +108,6 @@ func (m *SGuestManager) QgaSetNetwork(ctx context.Context, params interface{}) ( Gateway: input.Gateway, } - //func (m *SGuestManager) QgaSetNetwork(netmod *monitor.NetworkModify, sid string, execTimeout int) (string, error) { guest, err := m.checkAndInitGuestQga(input.Sid) if err != nil { return nil, err @@ -144,3 +144,15 @@ func (m *SGuestManager) QgaGetOsInfo(sid string) (jsonutils.JSONObject, error) { } return jsonutils.Marshal(res), nil } + +func (guest *SKVMGuestInstance) QgaAddNicsConfigure(addNics []*desc.SGuestNetwork) error { + if guest.guestAgent == nil { + if err := guest.InitQga(); err != nil { + return errors.Wrap(err, "init qga") + } + } + if err := guest.guestAgent.GuestPing(1); err != nil { + return errors.Wrap(err, "Qga ping") + } + return guest.guestAgent.QgaDeployNics(deployapi.GuestNicsToServerNics(addNics)) +} diff --git a/pkg/hostman/guestman/guesttasks.go b/pkg/hostman/guestman/guesttasks.go index 407f0449aa..74cdb831c6 100644 --- a/pkg/hostman/guestman/guesttasks.go +++ b/pkg/hostman/guestman/guesttasks.go @@ -615,11 +615,34 @@ type SGuestNetworkSyncTask struct { addNics []*desc.SGuestNetwork errors []error + addNicMacs []string + addNicConfs []*monitor.NetworkModify + callback func(...error) } func (n *SGuestNetworkSyncTask) Start(callback func(...error)) { n.callback = callback + if len(n.addNics) > 0 { + nics := make([]*desc.SGuestNetwork, 0) + nics = append(nics, n.guest.Desc.Nics...) + nics = append(nics, n.addNics...) + if err := n.guest.QgaAddNicsConfigure(nics); err != nil { + log.Errorf("QgaAddNicsConfigure failed %s", err) + } else { + addNicMacs := make([]string, 0) + addNicConfs := make([]*monitor.NetworkModify, 0) + for i := range n.addNics { + addNicMacs = append(addNicMacs, n.addNics[i].Mac) + addNicConfs = append(addNicConfs, &monitor.NetworkModify{ + Ipmask: fmt.Sprintf("%s/%d", n.addNics[i].Ip, n.addNics[i].Masklen), + Gateway: n.addNics[i].Gateway, + }) + } + n.addNicMacs = addNicMacs + n.addNicConfs = addNicConfs + } + } n.syncNetworkConf() } @@ -633,10 +656,59 @@ func (n *SGuestNetworkSyncTask) syncNetworkConf() { n.addNics = n.addNics[:len(n.addNics)-1] n.addNic(nic) } else { + if len(n.addNicMacs) > 0 { + // try restart added nics, wait for added nic ready + time.Sleep(3 * time.Second) + if err := n.qgaRestartAddedNics(); err != nil { + log.Errorf("failed qgaRestartAddedNics") + } + } + n.callback(n.errors...) } } +func (n *SGuestNetworkSyncTask) qgaRestartAddedNics() error { + err := n.qgaGetAddedNicDevs() + if err != nil { + return err + } + for i := range n.addNicConfs { + if n.addNicConfs[i].Device != "" { + err = n.guest.guestAgent.QgaRestartNetwork(n.addNicConfs[i]) + if err != nil { + log.Errorf("Failed QgaRestartNetwork %s %s", n.addNicConfs[i].Device, err) + } + } + } + return nil +} + +func (n *SGuestNetworkSyncTask) qgaGetAddedNicDevs() error { + data, err := n.guest.guestAgent.QgaGetNetwork() + if err != nil { + return errors.Wrap(err, "QgaGetNetwork") + } + var parsedData []api.IfnameDetail + ifnames, err := jsonutils.Parse(data) + if err != nil { + return errors.Wrapf(err, "parse qga network output %s", data) + } + err = ifnames.Unmarshal(&parsedData) + if err != nil { + return errors.Wrap(err, "unmarshal ifnames") + } + for i := range n.addNicMacs { + for j := range parsedData { + if n.addNicMacs[i] == parsedData[j].HardwareAddress { + n.addNicConfs[i].Device = parsedData[j].Name + break + } + } + } + return nil +} + func (n *SGuestNetworkSyncTask) removeNic(nic *desc.SGuestNetwork) { callback := func(res string) { if len(res) > 0 && !strings.Contains(res, "not found") { @@ -794,7 +866,7 @@ func (n *SGuestNetworkSyncTask) onDeviceAdd(nic *desc.SGuestNetwork) { func NewGuestNetworkSyncTask( guest *SKVMGuestInstance, delNics, addNics []*desc.SGuestNetwork, ) *SGuestNetworkSyncTask { - return &SGuestNetworkSyncTask{guest, delNics, addNics, make([]error, 0), nil} + return &SGuestNetworkSyncTask{guest, delNics, addNics, make([]error, 0), nil, nil, nil} } /** diff --git a/pkg/hostman/monitor/qga/qga.go b/pkg/hostman/monitor/qga/qga.go index e76f88a29a..6748ea7aeb 100644 --- a/pkg/hostman/monitor/qga/qga.go +++ b/pkg/hostman/monitor/qga/qga.go @@ -598,6 +598,11 @@ if systemctl is-active --quiet network.service; then exit 0 fi +if command -v netplan &>/dev/null; then + netplan try --timeout 0 + exit 0 +fi + if command -v ip &> /dev/null; then ip link set $DEV down && ip link set $DEV up exit 0 @@ -653,6 +658,38 @@ func (qga *QemuGuestAgent) QgaSetNetwork(qgaNetMod *monitor.NetworkModify, guest } } +func (qga *QemuGuestAgent) QgaRestartNetwork(qgaNetMod *monitor.NetworkModify) error { + //Getting information about the operating system + resOsInfo, err := qga.QgaGuestGetOsInfo() + if err != nil { + return errors.Wrap(err, "get os info") + } + + //Judgement based on id, currently only windows and other systems are judged + switch resOsInfo.Id { + case "mswindows": + return qga.QgaSetWindowsNetwork(qgaNetMod) + default: + return qga.QgaRestartLinuxNetwork(qgaNetMod) + } +} + +func (qga *QemuGuestAgent) QgaDeployNics(guestNics []*types.SServerNic) error { + //Getting information about the operating system + resOsInfo, err := qga.QgaGuestGetOsInfo() + if err != nil { + return errors.Wrap(err, "get os info") + } + + if resOsInfo.Id == "mswindows" { + return nil + } + if err := qga.qgaDeployNetworkConfigure(guestNics); err != nil { + return errors.Wrap(err, "qgaDeployNetworkConfigure") + } + return nil +} + /* # @username: the user account whose password to change # @password: the new password entry string, base64 encoded diff --git a/pkg/mcclient/options/compute/servers.go b/pkg/mcclient/options/compute/servers.go index 60cb0789a3..4fa97c6abb 100644 --- a/pkg/mcclient/options/compute/servers.go +++ b/pkg/mcclient/options/compute/servers.go @@ -866,7 +866,7 @@ type ServerQgaCommand struct { ServerIdOptions COMMAND string `help:"qga command"` - Timeout int `help:"qga command execute timeout (ms)"` + Timeout int `help:"qga command execute timeout (s)"` } func (o *ServerQgaCommand) Params() (jsonutils.JSONObject, error) { @@ -876,7 +876,7 @@ func (o *ServerQgaCommand) Params() (jsonutils.JSONObject, error) { type ServerQgaPing struct { ServerIdOptions - Timeout int `help:"qga command execute timeout (ms)"` + Timeout int `help:"qga command execute timeout (s)"` } func (o *ServerQgaPing) Params() (jsonutils.JSONObject, error) {