diff --git a/pkg/apis/compute/guests.go b/pkg/apis/compute/guests.go index f0c1758884e..cdd056a73ac 100644 --- a/pkg/apis/compute/guests.go +++ b/pkg/apis/compute/guests.go @@ -763,6 +763,9 @@ type ServerChangeConfigInput struct { AutoStart bool `json:"auto_start"` Disks []DiskConfig `json:"disks"` + + SetTrafficLimits []ServerNicTrafficLimit + ResetTrafficLimits []ServerNicTrafficLimit } type ServerUpdateInput struct { diff --git a/pkg/compute/guestdrivers/base.go b/pkg/compute/guestdrivers/base.go index 0544ddc3a32..db0aac282a2 100644 --- a/pkg/compute/guestdrivers/base.go +++ b/pkg/compute/guestdrivers/base.go @@ -533,11 +533,11 @@ func (drv *SBaseGuestDriver) FetchMonitorUrl(ctx context.Context, guest *models. return influxdbUrl } -func (drv *SBaseGuestDriver) RequestResetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *models.SHost, guest *models.SGuest, input *api.ServerNicTrafficLimit) error { +func (drv *SBaseGuestDriver) RequestResetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *models.SHost, guest *models.SGuest, input []api.ServerNicTrafficLimit) error { return httperrors.ErrNotImplemented } -func (drv *SBaseGuestDriver) RequestSetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *models.SHost, guest *models.SGuest, input *api.ServerNicTrafficLimit) error { +func (drv *SBaseGuestDriver) RequestSetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *models.SHost, guest *models.SGuest, input []api.ServerNicTrafficLimit) error { return httperrors.ErrNotImplemented } @@ -545,10 +545,10 @@ func (drv *SBaseGuestDriver) SyncOsInfo(ctx context.Context, userCred mcclient.T return nil } -func (self *SBaseGuestDriver) RequestStartRescue(ctx context.Context, task taskman.ITask, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) error { +func (drv *SBaseGuestDriver) RequestStartRescue(ctx context.Context, task taskman.ITask, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) error { return httperrors.ErrNotImplemented } -func (self *SBaseGuestDriver) RequestStopRescue(ctx context.Context, task taskman.ITask, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) error { +func (drv *SBaseGuestDriver) RequestStopRescue(ctx context.Context, task taskman.ITask, body jsonutils.JSONObject, host *models.SHost, guest *models.SGuest) error { return httperrors.ErrNotImplemented } diff --git a/pkg/compute/guestdrivers/kvm.go b/pkg/compute/guestdrivers/kvm.go index 9ca3c73a7a9..86a109a8f9b 100644 --- a/pkg/compute/guestdrivers/kvm.go +++ b/pkg/compute/guestdrivers/kvm.go @@ -1150,7 +1150,7 @@ func (self *SKVMGuestDriver) FetchMonitorUrl(ctx context.Context, guest *models. return self.SVirtualizedGuestDriver.FetchMonitorUrl(ctx, guest) } -func (self *SKVMGuestDriver) RequestResetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *models.SHost, guest *models.SGuest, input *api.ServerNicTrafficLimit) error { +func (self *SKVMGuestDriver) RequestResetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *models.SHost, guest *models.SGuest, input []api.ServerNicTrafficLimit) error { url := fmt.Sprintf("%s/servers/%s/reset-nic-traffic-limit", host.ManagerUri, guest.Id) httpClient := httputils.GetDefaultClient() header := task.GetTaskRequestHeader() @@ -1162,7 +1162,7 @@ func (self *SKVMGuestDriver) RequestResetNicTrafficLimit(ctx context.Context, ta return nil } -func (self *SKVMGuestDriver) RequestSetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *models.SHost, guest *models.SGuest, input *api.ServerNicTrafficLimit) error { +func (self *SKVMGuestDriver) RequestSetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *models.SHost, guest *models.SGuest, input []api.ServerNicTrafficLimit) error { url := fmt.Sprintf("%s/servers/%s/set-nic-traffic-limit", host.ManagerUri, guest.Id) httpClient := httputils.GetDefaultClient() header := task.GetTaskRequestHeader() diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index 298977d174d..c5427ebecd3 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -2759,6 +2759,24 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T return nil, httperrors.NewInvalidStatusError("cannot change CPU/Memory spec in status %s", self.Status) } + for i := range input.ResetTrafficLimits { + input.ResetTrafficLimits[i].Mac = strings.ToLower(input.ResetTrafficLimits[i].Mac) + _, err := self.GetGuestnetworkByMac(input.ResetTrafficLimits[i].Mac) + if err != nil { + return nil, errors.Wrap(err, "get guest network by mac") + } + } + confs.Set("reset_traffic_limits", jsonutils.Marshal(input.ResetTrafficLimits)) + + for i := range input.SetTrafficLimits { + input.SetTrafficLimits[i].Mac = strings.ToLower(input.SetTrafficLimits[i].Mac) + _, err := self.GetGuestnetworkByMac(input.SetTrafficLimits[i].Mac) + if err != nil { + return nil, errors.Wrap(err, "get guest network by mac") + } + } + confs.Set("set_traffic_limits", jsonutils.Marshal(input.SetTrafficLimits)) + if addCpu < 0 { addCpu = 0 } diff --git a/pkg/compute/models/guestdrivers.go b/pkg/compute/models/guestdrivers.go index 614d683c722..31d26146e33 100644 --- a/pkg/compute/models/guestdrivers.go +++ b/pkg/compute/models/guestdrivers.go @@ -240,8 +240,8 @@ type IGuestDriver interface { QgaRequestGetNetwork(ctx context.Context, userCred mcclient.TokenCredential, body jsonutils.JSONObject, host *SHost, guest *SGuest) (jsonutils.JSONObject, error) FetchMonitorUrl(ctx context.Context, guest *SGuest) string - RequestResetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *SHost, guest *SGuest, input *api.ServerNicTrafficLimit) error - RequestSetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *SHost, guest *SGuest, input *api.ServerNicTrafficLimit) error + RequestResetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *SHost, guest *SGuest, input []api.ServerNicTrafficLimit) error + RequestSetNicTrafficLimit(ctx context.Context, task taskman.ITask, host *SHost, guest *SGuest, input []api.ServerNicTrafficLimit) error SyncOsInfo(ctx context.Context, userCred mcclient.TokenCredential, g *SGuest, extVM cloudprovider.IOSInfo) error diff --git a/pkg/compute/tasks/guest_change_config_task.go b/pkg/compute/tasks/guest_change_config_task.go index 78026e0e9a8..b22686c9189 100644 --- a/pkg/compute/tasks/guest_change_config_task.go +++ b/pkg/compute/tasks/guest_change_config_task.go @@ -335,6 +335,69 @@ func (task *GuestChangeConfigTask) OnGuestChangeCpuMemSpecCompleteFailed(ctx con func (task *GuestChangeConfigTask) OnGuestChangeCpuMemSpecFinish(ctx context.Context, guest *models.SGuest) { models.HostManager.ClearSchedDescCache(guest.HostId) + if task.Params.Contains("reset_traffic_limits") { + host, _ := guest.GetHost() + resetTraffics := []api.ServerNicTrafficLimit{} + task.Params.Unmarshal(&resetTraffics, "reset_traffic_limits") + task.SetStage("OnGuestResetNicTraffics", nil) + err := guest.GetDriver().RequestResetNicTrafficLimit(ctx, task, host, guest, resetTraffics) + if err != nil { + task.markStageFailed(ctx, guest, jsonutils.NewString(err.Error())) + } + } else { + task.OnGuestResetNicTraffics(ctx, guest, nil) + } +} + +func (task *GuestChangeConfigTask) OnGuestResetNicTraffics(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { + if task.Params.Contains("reset_traffic_limits") { + resetTraffics := []api.ServerNicTrafficLimit{} + task.Params.Unmarshal(&resetTraffics, "reset_traffic_limits") + for i := range resetTraffics { + input := resetTraffics[i] + gn, _ := guest.GetGuestnetworkByMac(input.Mac) + err := gn.UpdateNicTrafficLimit(input.RxTrafficLimit, input.TxTrafficLimit) + if err != nil { + task.markStageFailed(ctx, guest, jsonutils.NewString(fmt.Sprintf("failed update guest nic traffic limit %s", err))) + return + } + err = gn.UpdateNicTrafficUsed(0, 0) + if err != nil { + task.markStageFailed(ctx, guest, jsonutils.NewString(fmt.Sprintf("failed update guest nic traffic used %s", err))) + return + } + } + } + + if task.Params.Contains("set_traffic_limits") { + host, _ := guest.GetHost() + setTraffics := []api.ServerNicTrafficLimit{} + task.Params.Unmarshal(&setTraffics, "set_traffic_limits") + task.SetStage("OnGuestSetNicTraffics", nil) + err := guest.GetDriver().RequestSetNicTrafficLimit(ctx, task, host, guest, setTraffics) + if err != nil { + task.markStageFailed(ctx, guest, jsonutils.NewString(err.Error())) + } + } else { + task.OnGuestSetNicTraffics(ctx, guest, nil) + } +} + +func (task *GuestChangeConfigTask) OnGuestSetNicTraffics(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { + if task.Params.Contains("set_traffic_limits") { + setTraffics := []api.ServerNicTrafficLimit{} + task.Params.Unmarshal(&setTraffics, "set_traffic_limits") + for i := range setTraffics { + input := setTraffics[i] + gn, _ := guest.GetGuestnetworkByMac(input.Mac) + err := gn.UpdateNicTrafficLimit(input.RxTrafficLimit, input.TxTrafficLimit) + if err != nil { + task.markStageFailed(ctx, guest, jsonutils.NewString(fmt.Sprintf("failed update guest nic traffic limit %s", err))) + return + } + } + } + task.SetStage("OnSyncConfigComplete", nil) err := guest.StartSyncTaskWithoutSyncstatus(ctx, task.UserCred, false, task.GetTaskId()) if err != nil { @@ -343,6 +406,14 @@ func (task *GuestChangeConfigTask) OnGuestChangeCpuMemSpecFinish(ctx context.Con } } +func (task *GuestChangeConfigTask) OnGuestResetNicTrafficsFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { + task.markStageFailed(ctx, guest, data) +} + +func (task *GuestChangeConfigTask) OnGuestSetNicTrafficsFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { + task.markStageFailed(ctx, guest, data) +} + func (task *GuestChangeConfigTask) OnSyncConfigComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { guest := obj.(*models.SGuest) diff --git a/pkg/compute/tasks/guest_sync_nic_traffics_task.go b/pkg/compute/tasks/guest_sync_nic_traffics_task.go index 286cfc8f30c..5db7b175317 100644 --- a/pkg/compute/tasks/guest_sync_nic_traffics_task.go +++ b/pkg/compute/tasks/guest_sync_nic_traffics_task.go @@ -50,10 +50,10 @@ func (self *GuestResetNicTrafficsTask) OnInit(ctx context.Context, obj db.IStand self.taskFailed(ctx, guest, fmt.Sprintf("get host %s", err)) return } - input := &compute.ServerNicTrafficLimit{} - self.GetParams().Unmarshal(input) + input := compute.ServerNicTrafficLimit{} + self.GetParams().Unmarshal(&input) self.SetStage("OnResetNicTrafficLimit", nil) - err = guest.GetDriver().RequestResetNicTrafficLimit(ctx, self, host, guest, input) + err = guest.GetDriver().RequestResetNicTrafficLimit(ctx, self, host, guest, []compute.ServerNicTrafficLimit{input}) if err != nil { self.taskFailed(ctx, guest, err.Error()) } @@ -99,10 +99,10 @@ func (self *GuestSetNicTrafficsTask) OnInit(ctx context.Context, obj db.IStandal self.taskFailed(ctx, guest, fmt.Sprintf("get host %s", err)) return } - input := &compute.ServerNicTrafficLimit{} - self.GetParams().Unmarshal(input) + input := compute.ServerNicTrafficLimit{} + self.GetParams().Unmarshal(&input) self.SetStage("OnSetNicTrafficLimit", nil) - err = guest.GetDriver().RequestSetNicTrafficLimit(ctx, self, host, guest, input) + err = guest.GetDriver().RequestSetNicTrafficLimit(ctx, self, host, guest, []compute.ServerNicTrafficLimit{input}) if err != nil { self.taskFailed(ctx, guest, err.Error()) } diff --git a/pkg/hostman/guestman/guesthandlers/guesthandler.go b/pkg/hostman/guestman/guesthandlers/guesthandler.go index 53d53cc09d0..d9fb70bbdb8 100644 --- a/pkg/hostman/guestman/guesthandlers/guesthandler.go +++ b/pkg/hostman/guestman/guesthandlers/guesthandler.go @@ -835,8 +835,8 @@ func guestMemorySnapshotDelete(ctx context.Context, w http.ResponseWriter, r *ht } func guestResetNicTrafficLimit(ctx context.Context, userCred mcclient.TokenCredential, sid string, body jsonutils.JSONObject) (interface{}, error) { - input := new(computeapi.ServerNicTrafficLimit) - if err := body.Unmarshal(input); err != nil { + input := []computeapi.ServerNicTrafficLimit{} + if err := body.Unmarshal(&input); err != nil { return nil, httperrors.NewInputParameterError("failed unmarshal input %s", err) } @@ -848,8 +848,8 @@ func guestResetNicTrafficLimit(ctx context.Context, userCred mcclient.TokenCrede } func guestSetNicTrafficLimit(ctx context.Context, userCred mcclient.TokenCredential, sid string, body jsonutils.JSONObject) (interface{}, error) { - input := new(computeapi.ServerNicTrafficLimit) - if err := body.Unmarshal(input); err != nil { + input := []computeapi.ServerNicTrafficLimit{} + if err := body.Unmarshal(&input); err != nil { return nil, httperrors.NewInputParameterError("failed unmarshal input %s", err) } diff --git a/pkg/hostman/guestman/guestman.go b/pkg/hostman/guestman/guestman.go index f5fde8016c2..e9dea8d853a 100644 --- a/pkg/hostman/guestman/guestman.go +++ b/pkg/hostman/guestman/guestman.go @@ -1485,11 +1485,27 @@ func (m *SGuestManager) RequestVerifyDirtyServer(s *SKVMGuestInstance) { } } -func (m *SGuestManager) ResetGuestNicTrafficLimit(guestId string, input *compute.ServerNicTrafficLimit) error { +func (m *SGuestManager) ResetGuestNicTrafficLimit(guestId string, input []compute.ServerNicTrafficLimit) error { guest, ok := m.GetServer(guestId) if !ok { return httperrors.NewNotFoundError("guest %s not found", guestId) } + + m.TrafficLock.Lock() + defer m.TrafficLock.Unlock() + for i := range input { + if err := m.resetGuestNicTrafficLimit(guest, input[i]); err != nil { + return errors.Wrap(err, "reset guest nic traffic limit") + } + } + + if err := guest.SaveLiveDesc(guest.Desc); err != nil { + return errors.Wrap(err, "guest save desc") + } + return nil +} + +func (m *SGuestManager) resetGuestNicTrafficLimit(guest *SKVMGuestInstance, input compute.ServerNicTrafficLimit) error { var nic *desc.SGuestNetwork for i := range guest.Desc.Nics { if guest.Desc.Nics[i].Mac == input.Mac { @@ -1500,8 +1516,6 @@ func (m *SGuestManager) ResetGuestNicTrafficLimit(guestId string, input *compute if nic == nil { return httperrors.NewNotFoundError("guest nic %s not found", input.Mac) } - m.TrafficLock.Lock() - defer m.TrafficLock.Unlock() recordPath := guest.NicTrafficRecordPath() if fileutils2.Exists(recordPath) { @@ -1518,7 +1532,7 @@ func (m *SGuestManager) ResetGuestNicTrafficLimit(guestId string, input *compute } } delete(record, strconv.Itoa(int(nic.Index))) - if err = m.SaveGuestTrafficRecord(guestId, record); err != nil { + if err = m.SaveGuestTrafficRecord(guest.Id, record); err != nil { return errors.Wrap(err, "failed save guest traffic record") } } @@ -1528,17 +1542,10 @@ func (m *SGuestManager) ResetGuestNicTrafficLimit(guestId string, input *compute if input.TxTrafficLimit != nil { nic.TxTrafficLimit = *input.TxTrafficLimit } - if err := guest.SaveLiveDesc(guest.Desc); err != nil { - return errors.Wrap(err, "guest save desc") - } return nil } -func (m *SGuestManager) SetGuestNicTrafficLimit(guestId string, input *compute.ServerNicTrafficLimit) error { - guest, ok := m.GetServer(guestId) - if !ok { - return httperrors.NewNotFoundError("guest %s not found", guestId) - } +func (m *SGuestManager) setNicTrafficLimit(guest *SKVMGuestInstance, input compute.ServerNicTrafficLimit) error { var nic *desc.SGuestNetwork for i := range guest.Desc.Nics { if guest.Desc.Nics[i].Mac == input.Mac { @@ -1549,17 +1556,13 @@ func (m *SGuestManager) SetGuestNicTrafficLimit(guestId string, input *compute.S if nic == nil { return httperrors.NewNotFoundError("guest nic %s not found", input.Mac) } - m.TrafficLock.Lock() - defer m.TrafficLock.Unlock() + if input.RxTrafficLimit != nil { nic.RxTrafficLimit = *input.RxTrafficLimit } if input.TxTrafficLimit != nil { nic.TxTrafficLimit = *input.TxTrafficLimit } - if err := guest.SaveLiveDesc(guest.Desc); err != nil { - return errors.Wrap(err, "guest save desc") - } recordPath := guest.NicTrafficRecordPath() if fileutils2.Exists(recordPath) { record, err := m.GetGuestTrafficRecord(guest.Id) @@ -1574,8 +1577,30 @@ func (m *SGuestManager) SetGuestNicTrafficLimit(guestId string, input *compute.S } } } - return m.SaveGuestTrafficRecord(guestId, record) + return m.SaveGuestTrafficRecord(guest.Id, record) + } + return nil +} + +func (m *SGuestManager) SetGuestNicTrafficLimit(guestId string, input []compute.ServerNicTrafficLimit) error { + guest, ok := m.GetServer(guestId) + if !ok { + return httperrors.NewNotFoundError("guest %s not found", guestId) + } + + m.TrafficLock.Lock() + defer m.TrafficLock.Unlock() + + for i := range input { + if err := m.setNicTrafficLimit(guest, input[i]); err != nil { + return errors.Wrap(err, "set nic traffic limit") + } } + + if err := guest.SaveLiveDesc(guest.Desc); err != nil { + return errors.Wrap(err, "guest save desc") + } + return nil } diff --git a/pkg/mcclient/options/compute/servers.go b/pkg/mcclient/options/compute/servers.go index d3e08e4fab2..e110061d436 100644 --- a/pkg/mcclient/options/compute/servers.go +++ b/pkg/mcclient/options/compute/servers.go @@ -1000,6 +1000,9 @@ type ServerChangeConfigOptions struct { Disk []string `help:"Data disk description, from the 1st data disk to the last one, empty string if no change for this data disk"` InstanceType string `help:"Instance Type, e.g. S2.SMALL2 for qcloud"` + + ResetTrafficLimits []string `help:"reset traffic limits, mac,rx,tx"` + SetTrafficLimits []string `help:"set traffic limits, mac,rx,tx"` } func (o *ServerChangeConfigOptions) Params() (jsonutils.JSONObject, error) { @@ -1023,6 +1026,59 @@ func (o *ServerChangeConfigOptions) Params() (jsonutils.JSONObject, error) { if err != nil { return nil, err } + + if len(o.ResetTrafficLimits) > 0 { + // mac,rx_limit,tx_limit + // ab:bc:cd:ef:ad:fa,12312312,1231233 + resetLimits := []*jsonutils.JSONDict{} + for i := range o.ResetTrafficLimits { + resetLimit := jsonutils.NewDict() + segs := strings.Split(o.ResetTrafficLimits[i], ",") + if len(segs) != 3 { + return nil, fmt.Errorf("invalid reset traffic limit input %s", o.ResetTrafficLimits[i]) + } + resetLimit.Set("mac", jsonutils.NewString(segs[0])) + rx, err := strconv.Atoi(segs[1]) + if err != nil { + return nil, fmt.Errorf("invalid reset traffic limit input %s: %s", o.ResetTrafficLimits[i], err) + } + resetLimit.Set("rx_traffic_limit", jsonutils.NewInt(int64(rx))) + tx, err := strconv.Atoi(segs[1]) + if err != nil { + return nil, fmt.Errorf("invalid reset traffic limit input %s: %s", o.ResetTrafficLimits[i], err) + } + resetLimit.Set("tx_traffic_limit", jsonutils.NewInt(int64(tx))) + resetLimits = append(resetLimits, resetLimit) + } + params.Set("reset_traffic_limits", jsonutils.Marshal(resetLimits)) + } + + if len(o.SetTrafficLimits) > 0 { + // mac,rx_limit,tx_limit + // ab:bc:cd:ef:ad:fa,12312312,1231233 + setLimits := []*jsonutils.JSONDict{} + for i := range o.SetTrafficLimits { + setLimit := jsonutils.NewDict() + segs := strings.Split(o.SetTrafficLimits[i], ",") + if len(segs) != 3 { + return nil, fmt.Errorf("invalid reset traffic limit input %s", o.SetTrafficLimits[i]) + } + setLimit.Set("mac", jsonutils.NewString(segs[0])) + rx, err := strconv.Atoi(segs[1]) + if err != nil { + return nil, fmt.Errorf("invalid reset traffic limit input %s: %s", o.SetTrafficLimits[i], err) + } + setLimit.Set("rx_traffic_limit", jsonutils.NewInt(int64(rx))) + tx, err := strconv.Atoi(segs[1]) + if err != nil { + return nil, fmt.Errorf("invalid reset traffic limit input %s: %s", o.SetTrafficLimits[i], err) + } + setLimit.Set("tx_traffic_limit", jsonutils.NewInt(int64(tx))) + setLimits = append(setLimits, setLimit) + } + params.Set("set_traffic_limits", jsonutils.Marshal(setLimits)) + } + if params.Size() == 0 { return nil, ErrEmtptyUpdate }