From 98506c19296c5c731cf307d9695da5aca65b7a66 Mon Sep 17 00:00:00 2001 From: TangBin Date: Thu, 22 Aug 2019 12:10:41 +0800 Subject: [PATCH 01/13] huawei & aws bugfix --- pkg/compute/guestdrivers/managedvirtual.go | 3 ++- pkg/multicloud/huawei/instance.go | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/pkg/compute/guestdrivers/managedvirtual.go b/pkg/compute/guestdrivers/managedvirtual.go index c380cea5d5..943b4733df 100644 --- a/pkg/compute/guestdrivers/managedvirtual.go +++ b/pkg/compute/guestdrivers/managedvirtual.go @@ -722,7 +722,8 @@ func (self *SManagedVirtualizedGuestDriver) RequestChangeVmConfig(ctx context.Co return true } } else { - if iVM.GetVcpuCount() == int(vcpuCount) && iVM.GetVmemSizeMB() == int(vmemSize) { + // aws 目前取不到内存。返回值永远为0 + if iVM.GetVcpuCount() == int(vcpuCount) && (iVM.GetVmemSizeMB() == int(vmemSize) || iVM.GetVmemSizeMB() == 0) { return true } } diff --git a/pkg/multicloud/huawei/instance.go b/pkg/multicloud/huawei/instance.go index c20d48aecb..7aa9573462 100644 --- a/pkg/multicloud/huawei/instance.go +++ b/pkg/multicloud/huawei/instance.go @@ -788,19 +788,21 @@ func (self *SRegion) CreateInstance(name string, imageId string, instanceType st // https://support.huaweicloud.com/api-ecs/zh-cn_topic_0020212668.html#ZH-CN_TOPIC_0020212668__table761103195216 params.KeyName = keypair - if len(keypair) > 0 { - udata, err := updateUserData(userData, "root", "", publicKey) + params.AdminPass = passwd + if len(userData) > 0 { + pwd := "" + k := "" + if len(keypair) > 0 { + k = keypair + } else { + pwd = passwd + } + + udata, err := updateUserData(userData, "root", pwd, k) if err != nil { return "", errors.Wrap(err, "region.CreateInstance.UpdateUserData") } - params.AdminPass = passwd - params.UserData = udata - } else { - udata, err := updateUserData(userData, "root", passwd, "") - if err != nil { - return "", errors.Wrap(err, "region.CreateInstance.UpdateUserData") - } - params.AdminPass = passwd + params.UserData = udata } From a245679f8c4195ae3ad7b00d937c71ee28cd9495 Mon Sep 17 00:00:00 2001 From: TangBin Date: Thu, 22 Aug 2019 14:22:17 +0800 Subject: [PATCH 02/13] huawei bugfix --- pkg/multicloud/huawei/instance.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/multicloud/huawei/instance.go b/pkg/multicloud/huawei/instance.go index 7aa9573462..dd56883ec0 100644 --- a/pkg/multicloud/huawei/instance.go +++ b/pkg/multicloud/huawei/instance.go @@ -787,8 +787,12 @@ func (self *SRegion) CreateInstance(name string, imageId string, instanceType st } // https://support.huaweicloud.com/api-ecs/zh-cn_topic_0020212668.html#ZH-CN_TOPIC_0020212668__table761103195216 - params.KeyName = keypair - params.AdminPass = passwd + if len(keypair) > 0 { + params.KeyName = keypair + } else { + params.AdminPass = passwd + } + if len(userData) > 0 { pwd := "" k := "" From 5bf89ae89be68d8978fe384419f83ecc9a2ffada Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Thu, 22 Aug 2019 06:38:30 +0000 Subject: [PATCH 03/13] =?UTF-8?q?networks:=20=E6=B7=BB=E5=8A=A0ifname=5Fhi?= =?UTF-8?q?nt=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 升级部署时,从network.Name初始化 - 创建时,尝试从network.Name取值,取不到则生成随机值 - 不允许更新 --- cmd/climc/shell/networks.go | 4 ++ pkg/compute/models/guestnetworks.go | 2 +- pkg/compute/models/networks.go | 71 +++++++++++++++++++++++++++-- 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/cmd/climc/shell/networks.go b/cmd/climc/shell/networks.go index aec9e62a33..b101d686fd 100644 --- a/cmd/climc/shell/networks.go +++ b/cmd/climc/shell/networks.go @@ -210,6 +210,7 @@ func init() { NETMASK int64 `help:"Length of network mask"` Gateway string `help:"Default gateway"` VlanId int64 `help:"Vlan ID" default:"1"` + IfnameHint string `help:"Hint for ifname generation"` AllocPolicy string `help:"Address allocation policy" choices:"none|stepdown|stepup|random"` ServerType string `help:"Server type" choices:"baremetal|guest|container|pxe|ipmi"` Desc string `help:"Description" metavar:"DESCRIPTION"` @@ -229,6 +230,9 @@ func init() { if len(args.ServerType) > 0 { params.Add(jsonutils.NewString(args.ServerType), "server_type") } + if len(args.IfnameHint) > 0 { + params.Add(jsonutils.NewString(args.IfnameHint), "ifname_hint") + } if len(args.AllocPolicy) > 0 { params.Add(jsonutils.NewString(args.AllocPolicy), "alloc_policy") } diff --git a/pkg/compute/models/guestnetworks.go b/pkg/compute/models/guestnetworks.go index 2ddb7c44f6..28e5777d43 100644 --- a/pkg/compute/models/guestnetworks.go +++ b/pkg/compute/models/guestnetworks.go @@ -224,7 +224,7 @@ func (self *SGuestnetwork) getVirtualRand(width int, randomized bool) string { func (self *SGuestnetwork) generateIfname(network *SNetwork, virtual bool, randomized bool) string { pattern := regexp.MustCompile(`\W+`) - nName := pattern.ReplaceAllString(network.Name, "") + nName := pattern.ReplaceAllString(network.IfnameHint, "") if len(nName) > MAX_IFNAME_SIZE-4 { nName = nName[:(MAX_IFNAME_SIZE - 4)] } diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index e70e67d6a6..40d7548376 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -44,6 +44,7 @@ import ( "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/auth" "yunion.io/x/onecloud/pkg/util/logclient" + "yunion.io/x/onecloud/pkg/util/rand" "yunion.io/x/onecloud/pkg/util/rbacutils" ) @@ -76,8 +77,6 @@ func init() { "networks", ), } - NetworkManager.NameLength = 9 - NetworkManager.NameRequireAscii = true NetworkManager.SetVirtualObject(NetworkManager) } @@ -85,6 +84,8 @@ type SNetwork struct { db.SSharableVirtualResourceBase db.SExternalizedResourceBase + IfnameHint string `width:"9" charset:"ascii" nullable:"false" list:"user" create:"optional"` + GuestIpStart string `width:"16" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"` // Column(VARCHAR(16, charset='ascii'), nullable=False) GuestIpEnd string `width:"16" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"` // Column(VARCHAR(16, charset='ascii'), nullable=False) GuestIpMask int8 `nullable:"false" list:"user" update:"user" create:"required"` // Column(TINYINT, nullable=False) @@ -1104,6 +1105,39 @@ func isValidMaskLen(maskLen int64) bool { } } +func (manager *SNetworkManager) newIfnameHint(hint string) (string, error) { + r := "" + // sanitize hint + for _, c := range hint { + if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' { + r += string(c) + } + } + newHint := func() (string, error) { + for i := 0; i < 3; i++ { + r := rand.String(7) + cnt, err := manager.Query().Equals("ifname_hint", r).CountWithError() + if err == nil && cnt == 0 { + return r, nil + } + } + return "", fmt.Errorf("failed finding ifname hint after 3 tries") + } + if len(r) < 3 { + return newHint() + } + if len(r) > 9 { + r = r[:9] + } + if cnt, err := manager.Query().Equals("ifname_hint", r).CountWithError(); err != nil { + return "", err + } else if cnt > 0 { + r, err := newHint() + return r, err + } + return r, nil +} + func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { prefixStr, _ := data.GetString("guest_ip_prefix") var maskLen64 int64 @@ -1143,6 +1177,18 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred data.Add(jsonutils.NewString(startIp.String()), "guest_ip_start") data.Add(jsonutils.NewString(endIp.String()), "guest_ip_end") + { + hint, _ := data.GetString("ifname_hint") + if hint == "" { + hint, _ = data.GetString("name") + } + hint, err = manager.newIfnameHint(hint) + if err != nil { + return nil, httperrors.NewBadRequestError("cannot derive valid ifname hint: %v", err) + } + data.Set("ifname_hint", jsonutils.NewString(hint)) + } + for _, key := range []string{"guest_gateway", "guest_dns", "guest_dhcp"} { ipStr, _ := data.GetString(key) if len(ipStr) > 0 && !regutils.MatchIPAddr(ipStr) { @@ -1700,9 +1746,26 @@ func (manager *SNetworkManager) InitializeData() error { return err } for _, n := range networks { - if len(n.ExternalId) == 0 && len(n.WireId) > 0 && n.Status == api.NETWORK_STATUS_INIT { + if n.ExternalId != "" { + var statusNew string + if n.WireId != "" && n.Status == api.NETWORK_STATUS_INIT { + statusNew = api.NETWORK_STATUS_AVAILABLE + } db.Update(&n, func() error { - n.Status = api.NETWORK_STATUS_AVAILABLE + if statusNew != "" { + n.Status = statusNew + } + return nil + }) + } else { + var ifnameHintNew string + if n.IfnameHint == "" { + ifnameHintNew = n.Name + } + db.Update(&n, func() error { + if ifnameHintNew != "" { + n.IfnameHint = ifnameHintNew + } return nil }) } From d8dc6aeb0eb1dfc98d37d23176d8757a5246388c Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Thu, 22 Aug 2019 08:26:04 +0000 Subject: [PATCH 04/13] =?UTF-8?q?appsrv:=20=E5=8F=8D=E8=BD=AC=E4=B9=89path?= =?UTF-8?q?=20segs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持"climc network-delete 中文" --- pkg/appsrv/appsrv.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/appsrv/appsrv.go b/pkg/appsrv/appsrv.go index 78fa92263a..5848cd9d6f 100644 --- a/pkg/appsrv/appsrv.go +++ b/pkg/appsrv/appsrv.go @@ -21,6 +21,7 @@ import ( "math/rand" "net" "net/http" + "net/url" "os" "strings" "sync" @@ -234,6 +235,11 @@ func (app *Application) handleCORS(w http.ResponseWriter, r *http.Request) bool func (app *Application) defaultHandle(w http.ResponseWriter, r *http.Request, rid string) (*SHandlerInfo, *SAppParams) { segs := SplitPath(r.URL.EscapedPath()) + for i := range segs { + if p, err := url.PathUnescape(segs[i]); err == nil { + segs[i] = p + } + } params := make(map[string]string) w.Header().Set("Server", "Yunion AppServer/Go/2018.4") w.Header().Set("X-Frame-Options", "SAMEORIGIN") From 6583d52543162c5cb341dfdb57351dfbcc4ddf1d Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Thu, 22 Aug 2019 08:38:09 +0000 Subject: [PATCH 05/13] =?UTF-8?q?guests:=20detachnetwork=E6=97=B6=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E4=BB=A5=E5=90=8D=E5=AD=97=E6=8C=87=E5=AE=9Anetwork?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/compute/models/guest_actions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index d58260453e..f331c9c865 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -1726,7 +1726,7 @@ func (self *SGuest) PerformDetachnetwork(ctx context.Context, userCred mcclient. netStr, _ := data.GetString("net_id") if len(netStr) > 0 { - netObj, err := NetworkManager.FetchById(netStr) + netObj, err := NetworkManager.FetchByIdOrName(userCred, netStr) if err != nil { if err == sql.ErrNoRows { return nil, httperrors.NewResourceNotFoundError2(NetworkManager.Keyword(), netStr) From 7d25a44b4e978d801b0820c73417b111ba5bad43 Mon Sep 17 00:00:00 2001 From: ioito Date: Thu, 22 Aug 2019 19:40:57 +0800 Subject: [PATCH 06/13] fix: qcloud sync unavailable storages --- pkg/multicloud/qcloud/localstorage.go | 6 +++++- pkg/multicloud/qcloud/storage.go | 6 +++++- pkg/multicloud/qcloud/zone.go | 16 +++++++++++----- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/pkg/multicloud/qcloud/localstorage.go b/pkg/multicloud/qcloud/localstorage.go index 4dc6f9c0cf..1261719d2f 100644 --- a/pkg/multicloud/qcloud/localstorage.go +++ b/pkg/multicloud/qcloud/localstorage.go @@ -27,6 +27,7 @@ import ( type SLocalStorage struct { zone *SZone storageType string + available bool } func (self *SLocalStorage) GetMetadata() *jsonutils.JSONDict { @@ -84,6 +85,9 @@ func (self *SLocalStorage) GetStorageConf() jsonutils.JSONObject { } func (self *SLocalStorage) GetStatus() string { + if !self.available { + return api.STORAGE_OFFLINE + } return api.STORAGE_ONLINE } @@ -93,7 +97,7 @@ func (self *SLocalStorage) Refresh() error { } func (self *SLocalStorage) GetEnabled() bool { - return true + return self.available == true } func (self *SLocalStorage) GetIStoragecache() cloudprovider.ICloudStoragecache { diff --git a/pkg/multicloud/qcloud/storage.go b/pkg/multicloud/qcloud/storage.go index 71dd329533..d997e8e992 100644 --- a/pkg/multicloud/qcloud/storage.go +++ b/pkg/multicloud/qcloud/storage.go @@ -29,6 +29,7 @@ import ( type SStorage struct { zone *SZone storageType string + available bool } func (self *SStorage) GetMetadata() *jsonutils.JSONDict { @@ -97,6 +98,9 @@ func (self *SStorage) GetStorageConf() jsonutils.JSONObject { } func (self *SStorage) GetStatus() string { + if !self.available { + return api.STORAGE_OFFLINE + } return api.STORAGE_ONLINE } @@ -106,7 +110,7 @@ func (self *SStorage) Refresh() error { } func (self *SStorage) GetEnabled() bool { - return true + return self.available == true } func (self *SStorage) GetIStoragecache() cloudprovider.ICloudStoragecache { diff --git a/pkg/multicloud/qcloud/zone.go b/pkg/multicloud/qcloud/zone.go index be473e0daa..428c9a2237 100644 --- a/pkg/multicloud/qcloud/zone.go +++ b/pkg/multicloud/qcloud/zone.go @@ -141,14 +141,20 @@ func (self *SZone) fetchStorages() error { } storageTypes := []string{} for _, diskConfig := range diskConfigSet { - if diskConfig.Available && !utils.IsInStringArray(diskConfig.DiskType, storageTypes) { - storageTypes = append(storageTypes, diskConfig.DiskType) - storage := SStorage{zone: self, storageType: diskConfig.DiskType} + if !utils.IsInStringArray(strings.ToUpper(diskConfig.DiskType), storageTypes) { + storageTypes = append(storageTypes, strings.ToUpper(diskConfig.DiskType)) + storage := SStorage{zone: self, storageType: diskConfig.DiskType, available: diskConfig.Available} + self.istorages = append(self.istorages, &storage) + } + } + for _, storageType := range []string{"CLOUD_PREMIUM", "CLOUD_SSD", "CLOUD_BASIC"} { + if !utils.IsInStringArray(storageType, storageTypes) { + storage := SStorage{zone: self, storageType: storageType, available: false} self.istorages = append(self.istorages, &storage) } } for _, localstorageType := range []string{"LOCAL_BASIC", "LOCAL_SSD"} { - storage := SLocalStorage{zone: self, storageType: localstorageType} + storage := SLocalStorage{zone: self, storageType: localstorageType, available: self.region.GetId() == "ap-hongkong"} //仅有香港本地存储可用 self.istorages = append(self.istorages, &storage) } return nil @@ -186,7 +192,7 @@ func (self *SZone) getStorageByCategory(category string) (*SStorage, error) { //return &SStorage{zone: self, storageType: strings.ToUpper(storages[i].GetStorageType())}, nil } storage := storages[i].(*SStorage) - if storage.storageType == category { + if strings.ToLower(storage.storageType) == strings.ToLower(category) { return storage, nil } } From 8376d14d42f00037900dab7bae0fad5b71753972 Mon Sep 17 00:00:00 2001 From: wanyaoqi Date: Wed, 21 Aug 2019 16:35:32 +0800 Subject: [PATCH 07/13] io throttle --- cmd/climc/shell/servers.go | 15 ++++++ pkg/compute/models/guest_actions.go | 44 ++++++++++++++++ .../tasks/guest_block_io_throttle_task.go | 40 +++++++++++++++ .../guestman/guesthandlers/guesthandler.go | 37 ++++++++++---- pkg/hostman/guestman/guesthelper.go | 6 +++ pkg/hostman/guestman/guestman.go | 12 +++++ pkg/hostman/guestman/guesttasks.go | 51 +++++++++++++++++++ pkg/hostman/guestman/qemu-kvm.go | 5 ++ pkg/hostman/monitor/hmp.go | 5 ++ pkg/hostman/monitor/monitor.go | 1 + pkg/hostman/monitor/qmp.go | 5 ++ 11 files changed, 212 insertions(+), 9 deletions(-) create mode 100644 pkg/compute/tasks/guest_block_io_throttle_task.go diff --git a/cmd/climc/shell/servers.go b/cmd/climc/shell/servers.go index 90d08cd1db..e3ba90e895 100644 --- a/cmd/climc/shell/servers.go +++ b/cmd/climc/shell/servers.go @@ -1016,4 +1016,19 @@ func init() { printObject(result) return nil }) + + type ServerIoThrottle struct { + ID string `help:"ID or name of VM" json:"-"` + BPS int `help:"bps(MB) of throttle" json:"bps"` + IOPS int `help:"iops of throttle" json:"iops"` + } + R(&ServerIoThrottle{}, "server-io-throttle", "Guest io set throttle", func(s *mcclient.ClientSession, opts *ServerIoThrottle) error { + params := jsonutils.Marshal(opts) + result, err := modules.Servers.PerformAction(s, opts.ID, "io-throttle", params) + if err != nil { + return err + } + printObject(result) + return nil + }) } diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index d58260453e..fceeab20d9 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -3474,3 +3474,47 @@ func (guest *SGuest) StartGuestDiskResizeTask(ctx context.Context, userCred mccl task.ScheduleRun(nil) return nil } + +func (self *SGuest) AllowPerformIoThrottle(ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + data jsonutils.JSONObject) bool { + return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "io-throttle") +} + +func (self *SGuest) PerformIoThrottle(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if self.Hypervisor != api.HYPERVISOR_KVM { + return nil, httperrors.NewBadRequestError("Hypervisor %s can't do io throttle", self.Hypervisor) + } + if self.Status != api.VM_RUNNING { + return nil, httperrors.NewServerStatusError("Cannot do io throttle in status %s", self.Status) + } + bpsMb, err := data.Int("bps") + if err != nil { + return nil, httperrors.NewMissingParameterError("bps") + } + if bpsMb < 0 { + return nil, httperrors.NewInputParameterError("bps must > 0") + } + iops, err := data.Int("iops") + if err != nil { + return nil, httperrors.NewMissingParameterError("iops") + } + if iops < 0 { + return nil, httperrors.NewInputParameterError("iops must > 0") + } + return nil, self.StartBlockIoThrottleTask(ctx, userCred, bpsMb, iops) +} + +func (self *SGuest) StartBlockIoThrottleTask(ctx context.Context, userCred mcclient.TokenCredential, bpsMb, iops int64) error { + params := jsonutils.NewDict() + params.Set("bps", jsonutils.NewInt(bpsMb)) + params.Set("iops", jsonutils.NewInt(iops)) + task, err := taskman.TaskManager.NewTask(ctx, "GuestBlockIoThrottleTask", self, userCred, params, "", "", nil) + if err != nil { + log.Errorf("%s", err) + return err + } + task.ScheduleRun(nil) + return nil +} diff --git a/pkg/compute/tasks/guest_block_io_throttle_task.go b/pkg/compute/tasks/guest_block_io_throttle_task.go new file mode 100644 index 0000000000..b2328f386f --- /dev/null +++ b/pkg/compute/tasks/guest_block_io_throttle_task.go @@ -0,0 +1,40 @@ +package tasks + +import ( + "context" + "fmt" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/models" +) + +type GuestBlockIoThrottleTask struct { + SGuestBaseTask +} + +func init() { + taskman.RegisterTask(GuestBlockIoThrottleTask{}) +} + +func (self *GuestBlockIoThrottleTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + guest := obj.(*models.SGuest) + url := fmt.Sprintf("/servers/%s/io-throttle", guest.Id) + headers := self.GetTaskRequestHeader() + host := guest.GetHost() + self.SetStage("OnIoThrottle", nil) + _, err := host.Request(ctx, self.UserCred, "POST", url, headers, self.Params) + if err != nil { + self.SetStageFailed(ctx, err.Error()) + } +} + +func (self *GuestBlockIoThrottleTask) OnIoThrottle(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { + guest.SetMetadata(ctx, "io-throttle", self.Params.String(), self.UserCred) + self.SetStageComplete(ctx, nil) +} + +func (self *GuestBlockIoThrottleTask) OnIoThrottleFailed(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) { + self.SetStageFailed(ctx, data.String()) +} diff --git a/pkg/hostman/guestman/guesthandlers/guesthandler.go b/pkg/hostman/guestman/guesthandlers/guesthandler.go index d91ebb84a9..35a2f0015b 100644 --- a/pkg/hostman/guestman/guesthandlers/guesthandler.go +++ b/pkg/hostman/guestman/guesthandlers/guesthandler.go @@ -40,20 +40,19 @@ type actionFunc func(context.Context, string, jsonutils.JSONObject) (interface{} var ( keyWords = []string{"servers"} actionFuncs = map[string]actionFunc{ - "create": guestCreate, - "deploy": guestDeploy, - "start": guestStart, - "stop": guestStop, - "monitor": guestMonitor, - "sync": guestSync, - "suspend": guestSuspend, + "create": guestCreate, + "deploy": guestDeploy, + "start": guestStart, + "stop": guestStop, + "monitor": guestMonitor, + "sync": guestSync, + "suspend": guestSuspend, + "io-throttle": guestIoThrottle, "snapshot": guestSnapshot, "delete-snapshot": guestDeleteSnapshot, "reload-disk-snapshot": guestReloadDiskSnapshot, // "remove-statefile": guestRemoveStatefile, - // "io-throttle": guestIoThrottle, - "src-prepare-migrate": guestSrcPrepareMigrate, "dest-prepare-migrate": guestDestPrepareMigrate, "live-migrate": guestLiveMigrate, @@ -244,6 +243,26 @@ func guestSuspend(ctx context.Context, sid string, body jsonutils.JSONObject) (i return nil, nil } +func guestIoThrottle(ctx context.Context, sid string, body jsonutils.JSONObject) (interface{}, error) { + guest, ok := guestman.GetGuestManager().Servers[sid] + if !ok { + return nil, httperrors.NewNotFoundError("Guest %s not found", sid) + } + if !guest.IsRunning() { + return nil, httperrors.NewInvalidStatusError("Not running") + } + bps, err := body.Int("bps") + if err != nil { + return nil, httperrors.NewMissingParameterError("bps") + } + iops, err := body.Int("iops") + if err != nil { + return nil, httperrors.NewMissingParameterError("iops") + } + hostutils.DelayTaskWithoutReqctx(ctx, guestman.GetGuestManager().GuestIoThrottle, &guestman.SGuestIoThrottle{sid, bps, iops}) + return nil, nil +} + func guestSrcPrepareMigrate(ctx context.Context, sid string, body jsonutils.JSONObject) (interface{}, error) { if !guestman.GetGuestManager().IsGuestExist(sid) { return nil, httperrors.NewNotFoundError("Guest %s not found", sid) diff --git a/pkg/hostman/guestman/guesthelper.go b/pkg/hostman/guestman/guesthelper.go index 327a0fc020..2acbd6812c 100644 --- a/pkg/hostman/guestman/guesthelper.go +++ b/pkg/hostman/guestman/guesthelper.go @@ -105,3 +105,9 @@ type SGuestCreateFromLibvirt struct { GuestDesc *jsonutils.JSONDict DisksPath *jsonutils.JSONDict } + +type SGuestIoThrottle struct { + Sid string + BPS int64 + IOPS int64 +} diff --git a/pkg/hostman/guestman/guestman.go b/pkg/hostman/guestman/guestman.go index 7a70064705..05f06bac46 100644 --- a/pkg/hostman/guestman/guestman.go +++ b/pkg/hostman/guestman/guestman.go @@ -454,6 +454,18 @@ func (m *SGuestManager) GuestSuspend(ctx context.Context, params interface{}) (j return nil, nil } +func (m *SGuestManager) GuestIoThrottle(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { + guestIoThrottle, ok := params.(*SGuestIoThrottle) + if !ok { + return nil, hostutils.ParamsError + } + guest := m.Servers[guestIoThrottle.Sid] + if guest.IsRunning() { + return nil, guest.BlockIoThrottle(ctx, guestIoThrottle.BPS, guestIoThrottle.IOPS) + } + return nil, httperrors.NewInvalidStatusError("Guest not running") +} + func (m *SGuestManager) SrcPrepareMigrate(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { migParams, ok := params.(*SSrcPrepareMigrate) if !ok { diff --git a/pkg/hostman/guestman/guesttasks.go b/pkg/hostman/guestman/guesttasks.go index a4c74f35f1..1c785beaf5 100644 --- a/pkg/hostman/guestman/guesttasks.go +++ b/pkg/hostman/guestman/guesttasks.go @@ -1155,3 +1155,54 @@ func (task *SGuestHotplugCpuMemTask) onFail(reason string) { func (task *SGuestHotplugCpuMemTask) onSucc() { hostutils.TaskComplete(task.ctx, nil) } + +type SGuestBlockIoThrottleTask struct { + *SKVMGuestInstance + + ctx context.Context + bps int64 + iops int64 +} + +func (task *SGuestBlockIoThrottleTask) Start() error { + task.findBlockDevices() + return nil +} + +func (task *SGuestBlockIoThrottleTask) findBlockDevices() { + task.Monitor.GetBlocks(task.onBlockDriversSucc) +} + +func (task *SGuestBlockIoThrottleTask) onBlockDriversSucc(res *jsonutils.JSONArray) { + drivers := make([]string, 0) + for i := 0; i < res.Length(); i++ { + device, err := res.GetAt(i) + if err == nil { + driver, err := device.GetString("device") + if err == nil { + if strings.HasPrefix(driver, "drive_") { + drivers = append(drivers, driver) + } + } + } + } + log.Infof("Drivers %s do io throttle bps %d iops %d", drivers, task.bps, task.iops) + task.doIoThrottle(drivers) +} + +func (task *SGuestBlockIoThrottleTask) doIoThrottle(drivers []string) { + if len(drivers) == 0 { + hostutils.TaskComplete(task.ctx, nil) + } else { + driver := drivers[0] + drivers = drivers[1:] + _cb := func(res string) { + if len(res) > 0 { + hostutils.TaskFailed(task.ctx, res) + } else { + task.doIoThrottle(drivers) + } + } + task.Monitor.BlockIoThrottle(driver, task.bps, task.iops, _cb) + } +} diff --git a/pkg/hostman/guestman/qemu-kvm.go b/pkg/hostman/guestman/qemu-kvm.go index 0fd5bd8c4c..6c1d74e921 100644 --- a/pkg/hostman/guestman/qemu-kvm.go +++ b/pkg/hostman/guestman/qemu-kvm.go @@ -1422,3 +1422,8 @@ func (s *SKVMGuestInstance) onlineResizeDisk(ctx context.Context, diskId string, task := NewGuestOnlineResizeDiskTask(ctx, s, diskId, sizeMB) task.Start() } + +func (s *SKVMGuestInstance) BlockIoThrottle(ctx context.Context, bps, iops int64) error { + task := SGuestBlockIoThrottleTask{s, ctx, bps, iops} + return task.Start() +} diff --git a/pkg/hostman/monitor/hmp.go b/pkg/hostman/monitor/hmp.go index dcecb5c324..747858f23d 100644 --- a/pkg/hostman/monitor/hmp.go +++ b/pkg/hostman/monitor/hmp.go @@ -453,3 +453,8 @@ func (m *HmpMonitor) ObjectAdd(objectType string, params map[string]string, call cmd := fmt.Sprintf("object_add %s,%s", objectType, strings.Join(paramsKvs, ",")) m.Query(cmd, callback) } + +func (m *HmpMonitor) BlockIoThrottle(driveName string, bps, iops int64, callback StringCallback) { + cmd := fmt.Sprintf("block_set_io_throttle %s %d 0 0 %d 0 0", driveName, bps, iops) + m.Query(cmd, callback) +} diff --git a/pkg/hostman/monitor/monitor.go b/pkg/hostman/monitor/monitor.go index 1c05cc3f49..61572ed7bb 100644 --- a/pkg/hostman/monitor/monitor.go +++ b/pkg/hostman/monitor/monitor.go @@ -71,6 +71,7 @@ type Monitor interface { StartNbdServer(port int, exportAllDevice, writable bool, callback StringCallback) ResizeDisk(driveName string, sizeMB int64, callback StringCallback) + BlockIoThrottle(driveName string, bps, iops int64, callback StringCallback) } type MonitorErrorFunc func(error) diff --git a/pkg/hostman/monitor/qmp.go b/pkg/hostman/monitor/qmp.go index 5f71c02c51..790bd44279 100644 --- a/pkg/hostman/monitor/qmp.go +++ b/pkg/hostman/monitor/qmp.go @@ -807,3 +807,8 @@ func (m *QmpMonitor) GeMemtSlotIndex(callback func(index int)) { } m.HumanMonitorCommand("info memory-devices", cb) } + +func (m *QmpMonitor) BlockIoThrottle(driveName string, bps, iops int64, callback StringCallback) { + cmd := fmt.Sprintf("block_set_io_throttle %s %d 0 0 %d 0 0", driveName, bps, iops) + m.HumanMonitorCommand(cmd, callback) +} From e7f93e3fe2e7a2ee6a09c75568c8057f6631c737 Mon Sep 17 00:00:00 2001 From: wanyaoqi Date: Thu, 22 Aug 2019 20:56:24 +0800 Subject: [PATCH 08/13] add io-throttle doc --- docs/index.yaml | 2 ++ docs/instance/io-throttle.yaml | 16 ++++++++++++++++ docs/schemas/instance.yaml | 11 +++++++++++ 3 files changed, 29 insertions(+) create mode 100644 docs/instance/io-throttle.yaml diff --git a/docs/index.yaml b/docs/index.yaml index 2dbd5e1906..26e6421421 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -231,6 +231,8 @@ paths: $ref: "./instance/user-data.yaml" /servers/{instanceId}/vnc: $ref: "./instance/vnc.yaml" + /servers/{instanceId}/io-throttle: + $ref: "./instance/io-throttle.yaml" /images: $ref: "./image/images.yaml" diff --git a/docs/instance/io-throttle.yaml b/docs/instance/io-throttle.yaml new file mode 100644 index 0000000000..eb554c4b22 --- /dev/null +++ b/docs/instance/io-throttle.yaml @@ -0,0 +1,16 @@ +post: + summary: 虚拟机磁盘IO限速 + parameters: + - $ref: '../parameters/instance.yaml#/instanceId' + - in: body + name: server + required: true + schema: + $ref: '../schemas/instance.yaml#/InstanceIoThrottle' + responses: + 200: + description: 实例信息 + schema: + $ref: '../schemas/instance.yaml#/InstanceResponse' + tags: + - instance \ No newline at end of file diff --git a/docs/schemas/instance.yaml b/docs/schemas/instance.yaml index e158d018d1..77c4f48f0e 100644 --- a/docs/schemas/instance.yaml +++ b/docs/schemas/instance.yaml @@ -114,6 +114,17 @@ InstanceAddSecgroup: example: 38dc8e80-14e5-4629-81c1-3847320ae821 description: 待绑定的安全组名称或ID +InstanceIoThrottle: + type: object + properties: + iops: + type: integer + example: 1000 + description: 最高iops + bps: + type: integer + example: 1000 + description: 最高bps,单位MB InstanceRevokeSecgroup: type: object From 83613793e6b61a2b9fc2bdc308637318db786316 Mon Sep 17 00:00:00 2001 From: Zexi Date: Thu, 22 Aug 2019 21:47:16 +0800 Subject: [PATCH 09/13] scheduler: fix disk or net specify resource id execute schedtag filter --- pkg/scheduler/algorithm/predicates/disk_schedtag_predicate.go | 4 ++++ .../algorithm/predicates/network_schedtag_predicate.go | 4 ++++ pkg/scheduler/algorithm/predicates/predicates.go | 3 ++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/scheduler/algorithm/predicates/disk_schedtag_predicate.go b/pkg/scheduler/algorithm/predicates/disk_schedtag_predicate.go index 37cadf94ad..40e7c2d8ad 100644 --- a/pkg/scheduler/algorithm/predicates/disk_schedtag_predicate.go +++ b/pkg/scheduler/algorithm/predicates/disk_schedtag_predicate.go @@ -56,6 +56,10 @@ func (d diskW) ResourceKeyword() string { return "storage" } +func (d diskW) IsSpecifyResource() bool { + return d.Storage != "" +} + func (d diskW) GetSchedtags() []*computeapi.SchedtagConfig { return d.DiskConfig.Schedtags } diff --git a/pkg/scheduler/algorithm/predicates/network_schedtag_predicate.go b/pkg/scheduler/algorithm/predicates/network_schedtag_predicate.go index 9d47d14780..f3c9bba6da 100644 --- a/pkg/scheduler/algorithm/predicates/network_schedtag_predicate.go +++ b/pkg/scheduler/algorithm/predicates/network_schedtag_predicate.go @@ -59,6 +59,10 @@ func (n netW) ResourceKeyword() string { return "network" } +func (n netW) IsSpecifyResource() bool { + return n.Network != "" +} + func (n netW) GetSchedtags() []*computeapi.SchedtagConfig { return n.NetworkConfig.Schedtags } diff --git a/pkg/scheduler/algorithm/predicates/predicates.go b/pkg/scheduler/algorithm/predicates/predicates.go index 7ce8f008a3..6a070e408b 100644 --- a/pkg/scheduler/algorithm/predicates/predicates.go +++ b/pkg/scheduler/algorithm/predicates/predicates.go @@ -282,6 +282,7 @@ func (p *PredicatedSchedtagResource) hasAvoidTags() bool { type ISchedtagCustomer interface { JSON(interface{}) *jsonutils.JSONDict Keyword() string + IsSpecifyResource() bool GetSchedtags() []*computeapi.SchedtagConfig ResourceKeyword() string } @@ -330,7 +331,7 @@ func (p *BaseSchedtagPredicate) check(input ISchedtagCustomer, candidate ISchedt res := &PredicatedSchedtagResource{ ISchedtagCandidateResource: candidate, } - if shouldExec { + if shouldExec && !input.IsSpecifyResource() { if err := tagPredicate.Check( SchedtagResourceW{ candidater: candidate, From b1a3f8bd03feddfe4274c37e27fd45ef59eb56ff Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 23 Aug 2019 03:10:22 +0000 Subject: [PATCH 10/13] =?UTF-8?q?snapshotpolicy:=20=E6=94=B9=E6=AD=A3bool?= =?UTF-8?q?=20default:"true"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/compute/models/snapshotpolicy.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/compute/models/snapshotpolicy.go b/pkg/compute/models/snapshotpolicy.go index 1ac6d48948..4626d88b97 100644 --- a/pkg/compute/models/snapshotpolicy.go +++ b/pkg/compute/models/snapshotpolicy.go @@ -20,6 +20,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/tristate" "yunion.io/x/pkg/util/compare" "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" @@ -51,8 +52,8 @@ type SSnapshotPolicy struct { // 0~6, 0 is Monday RepeatWeekdays uint8 `charset:"utf8" create:"required"` // 0~23 - TimePoints uint32 `charset:"utf8" create:"required"` - IsActivated bool `list:"user" get:"user" create:"optional" default:"true"` + TimePoints uint32 `charset:"utf8" create:"required"` + IsActivated tristate.TriState `list:"user" get:"user" create:"optional" default:"true"` } var SnapshotPolicyManager *SSnapshotPolicyManager From dfc92a66ebc9d59098396213bd3c42124639e187 Mon Sep 17 00:00:00 2001 From: Zexi Date: Wed, 21 Aug 2019 17:49:51 +0800 Subject: [PATCH 11/13] schedtags: support scope --- cmd/climc/shell/schedtags.go | 20 +++ pkg/cloudcommon/db/scoperesource.go | 188 ++++++++++++++++++++++++++ pkg/cloudcommon/policy/defaults.go | 12 -- pkg/cloudcommon/policy/resources.go | 1 - pkg/compute/models/schedtags.go | 11 ++ pkg/mcclient/modules/mod_schedtags.go | 2 +- 6 files changed, 220 insertions(+), 14 deletions(-) create mode 100644 pkg/cloudcommon/db/scoperesource.go diff --git a/cmd/climc/shell/schedtags.go b/cmd/climc/shell/schedtags.go index 939d6e086c..dc26ccde0b 100644 --- a/cmd/climc/shell/schedtags.go +++ b/cmd/climc/shell/schedtags.go @@ -129,4 +129,24 @@ func init() { return nil }) + type SetScopeOptions struct { + ID []string `help:"ID or Name of schetag"` + Project string `help:"ID or Name of project"` + Domain string `help:"ID or Name of domain"` + System bool `help:"Set to system scope"` + } + R(&SetScopeOptions{}, "schedtag-set-scope", "Set schedtag scope", func(s *mcclient.ClientSession, args *SetScopeOptions) error { + params := jsonutils.NewDict() + domainId := args.Domain + projectId := args.Project + if args.System { + domainId = "" + projectId = "" + } + params.Add(jsonutils.NewString(domainId), "domain") + params.Add(jsonutils.NewString(projectId), "project") + ret := modules.Schedtags.BatchPerformAction(s, args.ID, "set-scope", params) + printBatchResults(ret, modules.Schedtags.GetColumns(s)) + return nil + }) } diff --git a/pkg/cloudcommon/db/scoperesource.go b/pkg/cloudcommon/db/scoperesource.go new file mode 100644 index 0000000000..4758830c1d --- /dev/null +++ b/pkg/cloudcommon/db/scoperesource.go @@ -0,0 +1,188 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package db + +import ( + "context" + + "yunion.io/x/jsonutils" + "yunion.io/x/sqlchemy" + + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/rbacutils" +) + +type SScopedResourceBaseManager struct{} + +type SScopedResourceBase struct { + DomainId string `width:"64" charset:"ascii" nullable:"true" index:"true" list:"user"` + ProjectId string `name:"tenant_id" width:"128" charset:"ascii" nullable:"true" index:"true" list:"user"` +} + +func (m *SScopedResourceBaseManager) ResourceScope() rbacutils.TRbacScope { + return rbacutils.ScopeProject +} + +func (m *SScopedResourceBaseManager) FilterByOwner(q *sqlchemy.SQuery, userCred mcclient.IIdentityProvider, scope rbacutils.TRbacScope) *sqlchemy.SQuery { + if userCred == nil { + return q + } + switch scope { + case rbacutils.ScopeDomain: + q = q.Filter(sqlchemy.Equals(q.Field("domain_id"), userCred.GetDomainId())) + case rbacutils.ScopeProject: + q = q.Filter(sqlchemy.Equals(q.Field("tenant_id"), userCred.GetProjectId())) + } + return q +} + +func getScopedResourceScope(domainId, projectId string) rbacutils.TRbacScope { + if domainId == "" && projectId == "" { + return rbacutils.ScopeSystem + } + if domainId != "" && projectId == "" { + return rbacutils.ScopeDomain + } + if domainId != "" && projectId != "" { + return rbacutils.ScopeProject + } + return rbacutils.ScopeNone +} + +func (s *SScopedResourceBase) GetOwnerId() mcclient.IIdentityProvider { + return &SOwnerId{ + DomainId: s.DomainId, + ProjectId: s.ProjectId, + } +} + +func (s *SScopedResourceBase) GetResourceScope() rbacutils.TRbacScope { + return getScopedResourceScope(s.DomainId, s.ProjectId) +} + +func (s *SScopedResourceBase) GetDomainId() string { + return s.DomainId +} + +func (s *SScopedResourceBase) GetProjectId() string { + return s.ProjectId +} + +func (s *SScopedResourceBase) SetResourceScope(domainId, projectId string) error { + s.DomainId = domainId + s.ProjectId = projectId + return nil +} + +func (s *SScopedResourceBase) GetMoreColumns(extra *jsonutils.JSONDict) *jsonutils.JSONDict { + if s.ProjectId != "" { + extra.Add(jsonutils.NewString(s.ProjectId), "project_id") + } + return extra +} + +type IScopedResourceModel interface { + IModel + + GetDomainId() string + GetProjectId() string + SetResourceScope(domainId, projectId string) error +} + +func (m *SScopedResourceBaseManager) PerformSetScope( + ctx context.Context, + obj IScopedResourceModel, + userCred mcclient.TokenCredential, + data jsonutils.JSONObject, +) (jsonutils.JSONObject, error) { + domainId := jsonutils.GetAnyString(data, []string{"domain_id", "domain", "project_domain_id", "project_domain"}) + projectId := jsonutils.GetAnyString(data, []string{"project_id", "project"}) + if projectId != "" { + project, err := TenantCacheManager.FetchTenantByIdOrName(ctx, projectId) + if err != nil { + return nil, err + } + projectId = project.GetId() + domainId = project.GetDomainId() + } + if domainId != "" { + domain, err := TenantCacheManager.FetchDomainByIdOrName(ctx, domainId) + if err != nil { + return nil, err + } + domainId = domain.GetId() + } + scopeToSet := getScopedResourceScope(domainId, projectId) + var err error + switch scopeToSet { + case rbacutils.ScopeSystem: + err = m.SetScopedResourceToSystem(obj, userCred) + case rbacutils.ScopeDomain: + err = m.SetScopedResourceToDomain(obj, userCred, domainId) + case rbacutils.ScopeProject: + err = m.SetScopedResourceToProject(obj, userCred, projectId) + } + return nil, err +} + +func setScopedResourceIds(model IScopedResourceModel, userCred mcclient.TokenCredential, domainId, projectId string) error { + diff, err := Update(model, func() error { + model.SetResourceScope(domainId, projectId) + return nil + }) + if err == nil { + OpsLog.LogEvent(model, ACT_UPDATE, diff, userCred) + } + return err +} + +func (m *SScopedResourceBaseManager) SetScopedResourceToSystem(model IScopedResourceModel, userCred mcclient.TokenCredential) error { + if !IsAdminAllowPerform(userCred, model, "set-scope") { + return httperrors.NewForbiddenError("Not allow set scope to system") + } + if model.GetProjectId() == "" && model.GetDomainId() == "" { + return nil + } + return setScopedResourceIds(model, userCred, "", "") +} + +func (m *SScopedResourceBaseManager) SetScopedResourceToDomain(model IScopedResourceModel, userCred mcclient.TokenCredential, domainId string) error { + if !IsDomainAllowPerform(userCred, model, "set-scope") { + return httperrors.NewForbiddenError("Not allow set scope to domain %s", domainId) + } + if model.GetDomainId() == domainId && model.GetProjectId() == "" { + return nil + } + domain, err := TenantCacheManager.FetchDomainById(context.TODO(), domainId) + if err != nil { + return err + } + return setScopedResourceIds(model, userCred, domain.GetId(), "") +} + +func (m *SScopedResourceBaseManager) SetScopedResourceToProject(model IScopedResourceModel, userCred mcclient.TokenCredential, projectId string) error { + if !IsProjectAllowPerform(userCred, model, "set-scope") { + return httperrors.NewForbiddenError("Not allow set scope to project %s", projectId) + } + if model.GetProjectId() == projectId { + return nil + } + project, err := TenantCacheManager.FetchTenantById(context.TODO(), projectId) + if err != nil { + return err + } + return setScopedResourceIds(model, userCred, project.GetDomainId(), projectId) +} diff --git a/pkg/cloudcommon/policy/defaults.go b/pkg/cloudcommon/policy/defaults.go index bf1cb2cde4..84b07d77d9 100644 --- a/pkg/cloudcommon/policy/defaults.go +++ b/pkg/cloudcommon/policy/defaults.go @@ -90,18 +90,6 @@ var ( Action: PolicyActionGet, Result: rbacutils.Allow, }, - { - Service: "compute", - Resource: "schedtags", - Action: PolicyActionList, - Result: rbacutils.Allow, - }, - { - Service: "compute", - Resource: "schedtags", - Action: PolicyActionGet, - Result: rbacutils.Allow, - }, { Service: "compute", Resource: "cloudregions", diff --git a/pkg/cloudcommon/policy/resources.go b/pkg/cloudcommon/policy/resources.go index 61c82f4143..6d5626bd4a 100644 --- a/pkg/cloudcommon/policy/resources.go +++ b/pkg/cloudcommon/policy/resources.go @@ -25,7 +25,6 @@ var ( "vpcs", "route_tables", "cloudregions", - "schedtags", "serverskus", "cachedimages", "dynamicschedtags", diff --git a/pkg/compute/models/schedtags.go b/pkg/compute/models/schedtags.go index fdbcaa805e..cfbd8f67ee 100644 --- a/pkg/compute/models/schedtags.go +++ b/pkg/compute/models/schedtags.go @@ -63,6 +63,7 @@ type IModelWithSchedtag interface { type SSchedtagManager struct { db.SStandaloneResourceBaseManager + db.SScopedResourceBaseManager jointsManager map[string]ISchedtagJointManager } @@ -121,6 +122,7 @@ func (manager *SSchedtagManager) GetResourceTypes() []string { type SSchedtag struct { db.SStandaloneResourceBase + db.SScopedResourceBase DefaultStrategy string `width:"16" charset:"ascii" nullable:"true" default:"" list:"user" update:"admin" create:"admin_optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True, default='') ResourceType string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"required"` // Column(VARCHAR(16, charset='ascii'), nullable=True, default='') @@ -301,6 +303,7 @@ func (self *SSchedtag) getDynamicSchedtagCount() (int, error) { } func (self *SSchedtag) getMoreColumns(extra *jsonutils.JSONDict) *jsonutils.JSONDict { + extra = self.SScopedResourceBase.GetMoreColumns(extra) cnt, _ := self.GetObjectCount() extra.Add(jsonutils.NewInt(int64(cnt)), fmt.Sprintf("%s_count", self.GetJointManager().GetMasterManager().Keyword())) cnt, _ = self.getDynamicSchedtagCount() @@ -458,3 +461,11 @@ func GetSchedtagsDetailsToResource(obj IModelWithSchedtag, ctx context.Context, } return extra } + +func (s *SSchedtag) AllowPerformSetScope(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return true +} + +func (s *SSchedtag) PerformSetScope(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + return SchedtagManager.PerformSetScope(ctx, s, userCred, data) +} diff --git a/pkg/mcclient/modules/mod_schedtags.go b/pkg/mcclient/modules/mod_schedtags.go index d52b91dddb..94b6405cea 100644 --- a/pkg/mcclient/modules/mod_schedtags.go +++ b/pkg/mcclient/modules/mod_schedtags.go @@ -69,7 +69,7 @@ func (this *SchedtagManager) DoBatchSchedtagHostAddRemove(s *mcclient.ClientSess func init() { Schedtags = SchedtagManager{NewComputeManager("schedtag", "schedtags", - []string{"ID", "Name", "Default_strategy", "Resource_type"}, + []string{"ID", "Name", "Default_strategy", "Resource_type", "Domain_id", "Project_id"}, []string{})} registerCompute(&Schedtags) From c846b1f96dbe7884f8b59177143480a27e175b9e Mon Sep 17 00:00:00 2001 From: ioito Date: Fri, 23 Aug 2019 16:46:23 +0800 Subject: [PATCH 12/13] fix: sync status after guest sync conf --- pkg/compute/tasks/guest_change_config_task.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/compute/tasks/guest_change_config_task.go b/pkg/compute/tasks/guest_change_config_task.go index a801e30df2..8763af3ab1 100644 --- a/pkg/compute/tasks/guest_change_config_task.go +++ b/pkg/compute/tasks/guest_change_config_task.go @@ -239,7 +239,7 @@ func (self *GuestChangeConfigTask) OnGuestChangeCpuMemSpecCompleteFailed(ctx con func (self *GuestChangeConfigTask) OnGuestChangeCpuMemSpecFinish(ctx context.Context, guest *models.SGuest) { models.HostManager.ClearSchedDescCache(guest.HostId) self.SetStage("OnSyncConfigComplete", nil) - err := guest.StartSyncTask(ctx, self.UserCred, false, self.GetTaskId()) + err := guest.StartSyncTaskWithoutSyncstatus(ctx, self.UserCred, false, self.GetTaskId()) if err != nil { self.markStageFailed(ctx, guest, fmt.Sprintf("StartSyncstatus fail %s", err)) return From 636ae62373dc6bd3a58eac8336274efd506a2e49 Mon Sep 17 00:00:00 2001 From: ioito Date: Fri, 23 Aug 2019 20:38:57 +0800 Subject: [PATCH 13/13] fix: deploy action not sync guest status in subtask --- pkg/compute/guestdrivers/base.go | 9 --------- pkg/compute/guestdrivers/container.go | 6 ------ pkg/compute/models/guestdrivers.go | 2 -- pkg/compute/tasks/guest_deploy_task.go | 14 +++++++++++++- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/pkg/compute/guestdrivers/base.go b/pkg/compute/guestdrivers/base.go index 475d76f8cb..249dcf91ca 100644 --- a/pkg/compute/guestdrivers/base.go +++ b/pkg/compute/guestdrivers/base.go @@ -313,12 +313,3 @@ func (self *SBaseGuestDriver) GetGuestSecgroupVpcid(guest *models.SGuest) (strin } return vpcId, nil } - -func (self *SBaseGuestDriver) OnGuestDeployTaskComplete(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { - if jsonutils.QueryBoolean(task.GetParams(), "restart", false) { - task.SetStage("OnDeployStartGuestComplete", nil) - return guest.StartGueststartTask(ctx, task.GetUserCred(), nil, task.GetTaskId()) - } - task.SetStage("OnDeployGuestSyncstatusComplete", nil) - return guest.StartSyncstatus(ctx, task.GetUserCred(), task.GetTaskId()) -} diff --git a/pkg/compute/guestdrivers/container.go b/pkg/compute/guestdrivers/container.go index ada1cc3c3d..2c2896450d 100644 --- a/pkg/compute/guestdrivers/container.go +++ b/pkg/compute/guestdrivers/container.go @@ -143,12 +143,6 @@ func (self *SContainerDriver) RequestUndeployGuestOnHost(ctx context.Context, gu return err } -func (self *SContainerDriver) OnGuestDeployTaskComplete(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { - guest.SetStatus(task.GetUserCred(), api.VM_RUNNING, "on deploy complete") - task.SetStageComplete(ctx, nil) - return nil -} - func (self *SContainerDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject { return guest.GetJsonDescAtHypervisor(ctx, host) } diff --git a/pkg/compute/models/guestdrivers.go b/pkg/compute/models/guestdrivers.go index 2c6001ab93..ff403679eb 100644 --- a/pkg/compute/models/guestdrivers.go +++ b/pkg/compute/models/guestdrivers.go @@ -96,8 +96,6 @@ type IGuestDriver interface { OnGuestDeployTaskDataReceived(ctx context.Context, guest *SGuest, task taskman.ITask, data jsonutils.JSONObject) error - OnGuestDeployTaskComplete(ctx context.Context, guest *SGuest, task taskman.ITask) error - StartGuestSyncstatusTask(guest *SGuest, ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error RequestSyncConfigOnHost(ctx context.Context, guest *SGuest, host *SHost, task taskman.ITask) error diff --git a/pkg/compute/tasks/guest_deploy_task.go b/pkg/compute/tasks/guest_deploy_task.go index 4e8b6dadb7..fbc1498e64 100644 --- a/pkg/compute/tasks/guest_deploy_task.go +++ b/pkg/compute/tasks/guest_deploy_task.go @@ -108,7 +108,6 @@ func (self *GuestDeployTask) OnDeployGuestComplete(ctx context.Context, obj db.I log.Infof("on_guest_deploy_task_data_received %s", data) guest := obj.(*models.SGuest) guest.GetDriver().OnGuestDeployTaskDataReceived(ctx, guest, self, data) - guest.GetDriver().OnGuestDeployTaskComplete(ctx, guest, self) action, _ := self.Params.GetString("deploy_action") keypair, _ := self.Params.GetString("keypair") reset_password := jsonutils.QueryBoolean(self.Params, "reset_password", false) @@ -133,6 +132,19 @@ func (self *GuestDeployTask) OnDeployGuestComplete(ctx context.Context, obj db.I // 如果 deploy 有其他事件,统一记在这里。 logclient.AddActionLogWithStartable(self, guest, "misc部署", "", self.UserCred, true) } + + if self.HasParentTask() { + self.SetStageComplete(ctx, nil) + return + } + + if jsonutils.QueryBoolean(self.GetParams(), "restart", false) { + self.SetStage("OnDeployStartGuestComplete", nil) + guest.StartGueststartTask(ctx, self.GetUserCred(), nil, self.GetTaskId()) + } else { + self.SetStage("OnDeployGuestSyncstatusComplete", nil) + guest.StartSyncstatus(ctx, self.GetUserCred(), self.GetTaskId()) + } } func (self *GuestDeployTask) OnDeployGuestCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {