mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-20 08:03:53 +08:00
Compare commits
20 Commits
v3.11.13-r
...
release/3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6d5c9ea0a | ||
|
|
ecb65c2563 | ||
|
|
7dc2246c10 | ||
|
|
0b02c49c7c | ||
|
|
5e565cf2be | ||
|
|
aef2e3e956 | ||
|
|
335cdf37b4 | ||
|
|
43bb829edc | ||
|
|
217496161a | ||
|
|
f2d2495be3 | ||
|
|
b333d023f9 | ||
|
|
29d1a9beb3 | ||
|
|
8a70ad1fe1 | ||
|
|
55c139f4ab | ||
|
|
24fdc3782f | ||
|
|
69fce36cab | ||
|
|
aef3f68fd5 | ||
|
|
67566abfbb | ||
|
|
382ec17c04 | ||
|
|
28f83d8dac |
@@ -42,32 +42,18 @@ import (
|
||||
func init() {
|
||||
cmd := shell.NewResourceCmd(&modules.Disks)
|
||||
cmd.List(&compute_options.DiskListOptions{})
|
||||
cmd.Show(&compute_options.DiskIdOptions{})
|
||||
cmd.Perform("public", &compute_options.DiskIdOptions{})
|
||||
cmd.Perform("private", &compute_options.DiskIdOptions{})
|
||||
cmd.Perform("syncstatus", &compute_options.DiskIdOptions{})
|
||||
cmd.Perform("change-owner-candidate-domains", &compute_options.DiskIdOptions{})
|
||||
cmd.Perform("disk-cancel-delete", &compute_options.DiskIdOptions{})
|
||||
cmd.Perform("set-class-metadata", &options.ResourceMetadataOptions{})
|
||||
cmd.Perform("rebuild", &options.ResourceIdOptions{})
|
||||
cmd.Perform("migrate", &compute_options.DiskMigrateOptions{})
|
||||
cmd.Perform("change-billing-type", new(compute_options.DiskChangeBillingTypeOptions))
|
||||
cmd.Perform("change-storage-type", &compute_options.DiskChangeStorageTypeOptions{})
|
||||
|
||||
type DiskDetailOptions struct {
|
||||
ID string `help:"ID or Name of disk"`
|
||||
}
|
||||
R(&DiskDetailOptions{}, "disk-show", "Show details of disk", func(s *mcclient.ClientSession, args *DiskDetailOptions) error {
|
||||
disk, e := modules.Disks.Get(s, args.ID, nil)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printObject(disk)
|
||||
return nil
|
||||
})
|
||||
R(&DiskDetailOptions{}, "disk-cancel-delete", "Cancel pending delete disks", func(s *mcclient.ClientSession, args *DiskDetailOptions) error {
|
||||
disk, e := modules.Disks.PerformAction(s, args.ID, "cancel-delete", nil)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printObject(disk)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskDeleteOptions struct {
|
||||
ID []string `help:"ID of disks to delete" metavar:"DISK"`
|
||||
OverridePendingDelete bool `help:"Delete disk directly instead of pending delete" short-token:"f"`
|
||||
@@ -96,25 +82,7 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&DiskDetailOptions{}, "disk-public", "Make a disk public", func(s *mcclient.ClientSession, args *DiskDetailOptions) error {
|
||||
disk, e := modules.Disks.PerformAction(s, args.ID, "public", nil)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printObject(disk)
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&DiskDetailOptions{}, "disk-private", "Make a disk private", func(s *mcclient.ClientSession, args *DiskDetailOptions) error {
|
||||
disk, e := modules.Disks.PerformAction(s, args.ID, "private", nil)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printObject(disk)
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&DiskDetailOptions{}, "disk-metadata", "Get metadata of a disk", func(s *mcclient.ClientSession, args *DiskDetailOptions) error {
|
||||
R(&compute_options.DiskIdOptions{}, "disk-metadata", "Get metadata of a disk", func(s *mcclient.ClientSession, args *compute_options.DiskIdOptions) error {
|
||||
meta, e := modules.Disks.GetMetadata(s, args.ID, nil)
|
||||
if e != nil {
|
||||
return e
|
||||
@@ -123,15 +91,6 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&DiskDetailOptions{}, "disk-syncstatus", "Sync status for disk", func(s *mcclient.ClientSession, args *DiskDetailOptions) error {
|
||||
ret, e := modules.Disks.PerformAction(s, args.ID, "syncstatus", nil)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
printObject(ret)
|
||||
return nil
|
||||
})
|
||||
|
||||
type DiskUpdateOptions struct {
|
||||
ID string `help:"ID or name of disk"`
|
||||
Name string `help:"New name of disk"`
|
||||
@@ -300,7 +259,7 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&DiskDetailOptions{}, "disk-change-owner-candidate-domains", "Get change owner candidate domain list", func(s *mcclient.ClientSession, args *DiskDetailOptions) error {
|
||||
R(&compute_options.DiskIdOptions{}, "disk-change-owner-candidate-domains", "Get change owner candidate domain list", func(s *mcclient.ClientSession, args *compute_options.DiskIdOptions) error {
|
||||
result, err := modules.Disks.GetSpecific(s, args.ID, "change-owner-candidate-domains", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -333,6 +333,7 @@ func init() {
|
||||
IpAddr string `help:"IP address"`
|
||||
Bridge string `help:"Bridge of hostwire"`
|
||||
Interface string `help:"Interface name, eg:eth0, en0"`
|
||||
VlanId int `help:"Vlan ID"`
|
||||
}
|
||||
R(&HostAddNetIfOptions{}, "host-add-netif", "Host add a NIC", func(s *mcclient.ClientSession, args *HostAddNetIfOptions) error {
|
||||
params := jsonutils.NewDict()
|
||||
@@ -352,6 +353,7 @@ func init() {
|
||||
if len(args.Interface) > 0 {
|
||||
params.Add(jsonutils.NewString(args.Interface), "interface")
|
||||
}
|
||||
addVlanIdToParams(params, args.VlanId)
|
||||
result, err := modules.Hosts.PerformAction(s, args.ID, "add-netif", params)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -361,12 +363,14 @@ func init() {
|
||||
})
|
||||
|
||||
type HostRemoveNetIfOptions struct {
|
||||
ID string `help:"ID or Name of host"`
|
||||
MAC string `help:"MAC of NIC to remove"`
|
||||
ID string `help:"ID or Name of host"`
|
||||
MAC string `help:"MAC of NIC to remove"`
|
||||
VlanId int `help:"Vlan Id"`
|
||||
}
|
||||
R(&HostRemoveNetIfOptions{}, "host-remove-netif", "Remove NIC from host", func(s *mcclient.ClientSession, args *HostRemoveNetIfOptions) error {
|
||||
params := jsonutils.NewDict()
|
||||
params.Add(jsonutils.NewString(args.MAC), "mac")
|
||||
addVlanIdToParams(params, args.VlanId)
|
||||
result, err := modules.Hosts.PerformAction(s, args.ID, "remove-netif", params)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -381,6 +385,7 @@ func init() {
|
||||
Ip string `help:"IP address"`
|
||||
Network string `help:"network to connect"`
|
||||
Reserved bool `help:"fetch IP from reserved pool"`
|
||||
VlanId int `help:"Vlan ID"`
|
||||
}
|
||||
R(&HostEnableNetIfOptions{}, "host-enable-netif", "Enable a network interface for a host", func(s *mcclient.ClientSession, args *HostEnableNetIfOptions) error {
|
||||
params := jsonutils.NewDict()
|
||||
@@ -394,6 +399,7 @@ func init() {
|
||||
if len(args.Network) > 0 {
|
||||
params.Add(jsonutils.NewString(args.Network), "network")
|
||||
}
|
||||
addVlanIdToParams(params, args.VlanId)
|
||||
result, err := modules.Hosts.PerformAction(s, args.ID, "enable-netif", params)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -406,6 +412,7 @@ func init() {
|
||||
ID string `help:"ID or Name of host"`
|
||||
MAC string `help:"MAC of NIC to disable"`
|
||||
Reserve bool `help:"Reserve the IP address"`
|
||||
VlanId int `help:"Vlan Id"`
|
||||
}
|
||||
R(&HostDisableNetIfOptions{}, "host-disable-netif", "Disable a network interface", func(s *mcclient.ClientSession, args *HostDisableNetIfOptions) error {
|
||||
params := jsonutils.NewDict()
|
||||
@@ -413,6 +420,7 @@ func init() {
|
||||
if args.Reserve {
|
||||
params.Add(jsonutils.JSONTrue, "reserve")
|
||||
}
|
||||
addVlanIdToParams(params, args.VlanId)
|
||||
result, err := modules.Hosts.PerformAction(s, args.ID, "disable-netif", params)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -754,3 +762,9 @@ func init() {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func addVlanIdToParams(params *jsonutils.JSONDict, vlanId int) {
|
||||
if vlanId > 1 {
|
||||
params.Add(jsonutils.NewInt(int64(vlanId)), "vlan_id")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&options.TagListOptions{}, "tag-list", "List tags", func(s *mcclient.ClientSession, opts *options.TagListOptions) error {
|
||||
R(&options.TagListOptions{}, "service-metadata-list", "List service metadata", func(s *mcclient.ClientSession, opts *options.TagListOptions) error {
|
||||
var mod modulebase.IResourceManager
|
||||
switch opts.Service {
|
||||
case "compute":
|
||||
|
||||
@@ -145,6 +145,7 @@ func init() {
|
||||
cmd.Get("hardware-info", new(options.ServerIdOptions))
|
||||
cmd.Get("screen-dump-show", new(options.ServerScreenDumpOptions))
|
||||
cmd.BatchPerform("screen-dump", new(options.ServerIdsOptions))
|
||||
cmd.Perform("set-network-num-queues", new(options.ServerSetNetworkNumQueues))
|
||||
|
||||
cmd.GetProperty(&options.ServerStatusStatisticsOptions{})
|
||||
cmd.GetProperty(&options.ServerProjectStatisticsOptions{})
|
||||
|
||||
2
go.mod
2
go.mod
@@ -93,7 +93,7 @@ require (
|
||||
k8s.io/client-go v0.19.3
|
||||
k8s.io/cluster-bootstrap v0.19.3
|
||||
moul.io/http2curl/v2 v2.3.0
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260123023413-f5d35910430c
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260207043119-2907d68518c5
|
||||
yunion.io/x/executor v0.0.0-20250518005516-5402e9e0bed0
|
||||
yunion.io/x/jsonutils v1.0.1-0.20250507052344-1abcf4f443b1
|
||||
yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91
|
||||
|
||||
4
go.sum
4
go.sum
@@ -1272,8 +1272,8 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260123023413-f5d35910430c h1:x+vf8gAXmf1CYfMTKO7BAdXa1a8iMpffNUW0B3P3wNg=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260123023413-f5d35910430c/go.mod h1:GdAwZ78fiqj13i2r1zNuE/D6YOlhh2q4jZjuyHiZTq8=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260207043119-2907d68518c5 h1:CqO2GuJk/pT4yjI3OTeLm+/mY1H+aUK+B6hxhzIdUQ4=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260207043119-2907d68518c5/go.mod h1:GdAwZ78fiqj13i2r1zNuE/D6YOlhh2q4jZjuyHiZTq8=
|
||||
yunion.io/x/executor v0.0.0-20250518005516-5402e9e0bed0 h1:msG4SiDSVU7CrXH06WuHlNEZXIooTcmNbfrIGHuIHBU=
|
||||
yunion.io/x/executor v0.0.0-20250518005516-5402e9e0bed0/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws=
|
||||
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634=
|
||||
|
||||
@@ -87,6 +87,6 @@ type CachedimageListInput struct {
|
||||
// valid cachedimage
|
||||
Valid bool `json:"valid"`
|
||||
|
||||
// enum: [public, private]
|
||||
// enum: ["public", "private"]
|
||||
CloudEnv string `json:"cloud_env"`
|
||||
}
|
||||
|
||||
@@ -169,6 +169,8 @@ type DiskListInput struct {
|
||||
|
||||
// 根据是否绑定快照策略过滤
|
||||
BindingSnapshotpolicy *bool `json:"binding_snapshotpolicy"`
|
||||
// 根据是否磁盘所在虚拟机是否绑定主机快照策略
|
||||
BindingServerSnapshotpolicy *bool `json:"binding_server_snapshotpolicy"`
|
||||
}
|
||||
|
||||
type DiskResourceInput struct {
|
||||
@@ -213,13 +215,22 @@ type SimpleGuest struct {
|
||||
Bps int `json:"bps"`
|
||||
// 计费类型
|
||||
BillingType string `json:"billing_type"`
|
||||
|
||||
// 磁盘绑定的快照策略列表
|
||||
Snapshotpolicy []SimpleSnapshotPolicy `json:"snapshotpolicy"`
|
||||
}
|
||||
|
||||
type SimpleSnapshotPolicy struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
RepeatWeekdays []int `json:"repeat_weekdays"`
|
||||
TimePoints []int `json:"time_points"`
|
||||
// 快照策略ID
|
||||
Id string `json:"id"`
|
||||
// 快照策略名称
|
||||
Name string `json:"name"`
|
||||
// 快照策略类型
|
||||
ResourceType string `json:"resource_type"`
|
||||
// 快照策略重复周期
|
||||
RepeatWeekdays []int `json:"repeat_weekdays"`
|
||||
// 快照策略时间点
|
||||
TimePoints []int `json:"time_points"`
|
||||
}
|
||||
|
||||
type DiskDetails struct {
|
||||
@@ -239,11 +250,11 @@ type DiskDetails struct {
|
||||
GuestStatus string `json:"guest_status"`
|
||||
// 所挂载虚拟机计费类型
|
||||
GuestBillingType string `json:"guest_billing_type"`
|
||||
// 磁盘所在虚拟机绑定的主机快照策略数量
|
||||
GuestSnapshotpolicyCount int `json:"guest_snapshotpolicy_count"`
|
||||
|
||||
// 自动清理时间
|
||||
AutoDeleteAt time.Time `json:"auto_delete_at"`
|
||||
// 自动快照策略状态
|
||||
SnapshotpolicyStatus string `json:"snapshotpolicy_status,allowempty"`
|
||||
|
||||
// 自动快照策略
|
||||
Snapshotpolicies []SimpleSnapshotPolicy `json:"snapshotpolicies"`
|
||||
@@ -351,6 +362,7 @@ type DiskChagneStorageTypeInput struct {
|
||||
}
|
||||
|
||||
type DiskSnapshotpolicyInput struct {
|
||||
// 快照策略ID
|
||||
SnapshotpolicyId string `json:"snapshotpolicy_id"`
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ type SImagesInGuest struct {
|
||||
DataImages []SSubImage
|
||||
}
|
||||
|
||||
type SGuestScreenDump struct {
|
||||
type SGuestScreenDumpInfo struct {
|
||||
S3AccessKey string
|
||||
S3SecretKey string
|
||||
S3Endpoint string
|
||||
|
||||
@@ -134,6 +134,14 @@ type ServerListInput struct {
|
||||
|
||||
// 根据是否绑定快照策略过滤
|
||||
BindingSnapshotpolicy *bool `json:"binding_snapshotpolicy"`
|
||||
// 根据虚机关联的磁盘是否绑定快照策略过滤
|
||||
BindingDisksSnapshotpolicy *bool `json:"binding_disks_snapshotpolicy"`
|
||||
}
|
||||
|
||||
// 主机快照策略绑定/设置接口入参
|
||||
type ServerSnapshotpolicyInput struct {
|
||||
// 快照策略ID
|
||||
SnapshotpolicyId string `json:"snapshotpolicy_id"`
|
||||
}
|
||||
|
||||
func (input *ServerListInput) AfterUnmarshal() {
|
||||
@@ -183,6 +191,11 @@ type ServerDetails struct {
|
||||
// 磁盘概要
|
||||
Disks string `json:"disks"`
|
||||
|
||||
// 主机快照策略数量
|
||||
SnapshotpolicyCount int `json:"snapshotpolicy_count"`
|
||||
// 磁盘快照策略数量
|
||||
DisksSnapshotpolicyCount int `json:"disks_snapshotpolicy_count"`
|
||||
|
||||
// 磁盘详情
|
||||
DisksInfo []GuestDiskInfo `json:"disks_info"`
|
||||
// 虚拟机Ip列表
|
||||
@@ -712,6 +725,13 @@ func (input ServerDetachnetworkInput) IsForce() bool {
|
||||
return input.Force != nil && *input.Force
|
||||
}
|
||||
|
||||
type ServerSetNetworkNumQueuesInput struct {
|
||||
// 虚机网卡 mac addr
|
||||
MacAddr string `json:"mac_addr"`
|
||||
// 网卡队列数
|
||||
NumQueues int `json:"num_queues"`
|
||||
}
|
||||
|
||||
type ServerMigrateForecastInput struct {
|
||||
PreferHostId string `json:"prefer_host_id"`
|
||||
// Deprecated
|
||||
|
||||
@@ -162,6 +162,7 @@ type SendParams struct {
|
||||
Header jsonutils.JSONObject
|
||||
Body jsonutils.JSONObject
|
||||
MsgKey string
|
||||
SecretKey string `json:"secret_key"`
|
||||
DomainId string
|
||||
RemoteTemplateParam SRemoteTemplateParam
|
||||
GroupKey string
|
||||
|
||||
@@ -21,6 +21,10 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
)
|
||||
|
||||
const (
|
||||
WEBHOOK_SIGNATURE_HEADER = "X-Auth-Token"
|
||||
)
|
||||
|
||||
type RobotCreateInput struct {
|
||||
apis.SharableVirtualResourceCreateInput
|
||||
apis.EnabledBaseResourceCreateInput
|
||||
@@ -37,6 +41,7 @@ type RobotCreateInput struct {
|
||||
Header jsonutils.JSONObject `json:"header"`
|
||||
Body jsonutils.JSONObject `json:"body"`
|
||||
MsgKey string `json:"msg_key"`
|
||||
SecretKey string `json:"secret_key"`
|
||||
UseTemplate tristate.TriState `json:"use_template"`
|
||||
}
|
||||
|
||||
@@ -63,8 +68,9 @@ type RobotUpdateInput struct {
|
||||
Address string `json:"address"`
|
||||
// description: Language preference
|
||||
// example: en
|
||||
Lang string `json:"lang"`
|
||||
Header jsonutils.JSONObject `json:"header"`
|
||||
Body jsonutils.JSONObject `json:"body"`
|
||||
MsgKey string `json:"msg_key"`
|
||||
Lang string `json:"lang"`
|
||||
Header jsonutils.JSONObject `json:"header"`
|
||||
Body jsonutils.JSONObject `json:"body"`
|
||||
MsgKey string `json:"msg_key"`
|
||||
SecretKey string `json:"secret_key"`
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ package yunionconf
|
||||
import "yunion.io/x/onecloud/pkg/apis"
|
||||
|
||||
const (
|
||||
SERVICE_TYPE = apis.SERVICE_TYPE_YUNIONCONF
|
||||
SERVICE_TYPE = apis.SERVICE_TYPE_YUNIONCONF
|
||||
SERVICE_VERSION = ""
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
31
pkg/apis/yunionconf/tags.go
Normal file
31
pkg/apis/yunionconf/tags.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// 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 yunionconf
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/apis"
|
||||
|
||||
type TagListInput struct {
|
||||
apis.InfrasResourceBaseListInput
|
||||
}
|
||||
|
||||
type TagDetails struct {
|
||||
apis.InfrasResourceBaseDetails
|
||||
}
|
||||
|
||||
type TagCreateInput struct {
|
||||
apis.InfrasResourceBaseCreateInput
|
||||
|
||||
Values []string `json:"values"`
|
||||
}
|
||||
@@ -169,7 +169,7 @@ type S3CommonOptions struct {
|
||||
S3SecretKey string `help:"s3 secret key"`
|
||||
S3Endpoint string `help:"s3 endpoint"`
|
||||
S3UseSSL bool `help:"s3 access use ssl"`
|
||||
S3BucketName string `help:"s3 bucket name" default:"onecloud-screendump"`
|
||||
S3BucketName string `help:"s3 bucket name"`
|
||||
S3BucketLifecycleKeepDay int `help:"s3 bucket lifecycle keep day" default:"180"`
|
||||
}
|
||||
|
||||
|
||||
@@ -128,6 +128,10 @@ func (drv *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Context
|
||||
config.SysDisk.SizeGB = int(math.Ceil(float64(disk.DiskSize) / 1024))
|
||||
config.SysDisk.Iops = disk.Iops
|
||||
config.SysDisk.Throughput = disk.Throughput
|
||||
if gds, err := disk.GetGuestDisk(); err == nil {
|
||||
config.SysDisk.Driver = gds.Driver
|
||||
config.SysDisk.CacheMode = gds.CacheMode
|
||||
}
|
||||
cache := storage.GetStoragecache()
|
||||
imageId := disk.GetTemplateId()
|
||||
//避免因同步过来的instance没有对应的imagecache信息,重置密码时引发空指针访问
|
||||
@@ -153,6 +157,10 @@ func (drv *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Context
|
||||
Throughput: disk.Throughput,
|
||||
Name: disk.Name,
|
||||
}
|
||||
if gds, err := disk.GetGuestDisk(); err == nil {
|
||||
dataDisk.Driver = gds.Driver
|
||||
dataDisk.CacheMode = gds.CacheMode
|
||||
}
|
||||
config.DataDisks = append(config.DataDisks, dataDisk)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -765,7 +765,7 @@ func (acnt *SCloudaccount) PerformUpdateCredential(
|
||||
}
|
||||
|
||||
changed := false
|
||||
if len(account.Secret) > 0 || len(account.Account) > 0 {
|
||||
if acnt.Account != account.Account && (len(account.Secret) > 0 || len(account.Account) > 0) {
|
||||
// check duplication
|
||||
q := acnt.GetModelManager().Query()
|
||||
q = q.Equals("account", account.Account)
|
||||
|
||||
@@ -218,6 +218,16 @@ func (manager *SDiskManager) ListItemFilter(
|
||||
q = q.NotIn("id", spjsq)
|
||||
}
|
||||
}
|
||||
if query.BindingServerSnapshotpolicy != nil {
|
||||
guestDisks := GuestdiskManager.Query("disk_id")
|
||||
sq := SnapshotPolicyResourceManager.Query("resource_id").Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_SERVER).SubQuery()
|
||||
gdsq := guestDisks.Join(sq, sqlchemy.Equals(guestDisks.Field("guest_id"), sq.Field("resource_id"))).SubQuery()
|
||||
if *query.BindingServerSnapshotpolicy {
|
||||
q = q.In("id", gdsq)
|
||||
} else {
|
||||
q = q.NotIn("id", gdsq)
|
||||
}
|
||||
}
|
||||
|
||||
guestId := query.ServerId
|
||||
if len(guestId) > 0 {
|
||||
@@ -383,11 +393,24 @@ func (manager *SDiskManager) QueryDistinctExtraFields(q *sqlchemy.SQuery, resour
|
||||
return q, httperrors.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SDisk) GetGuestDiskCount() (int, error) {
|
||||
func (disk *SDisk) GetGuestDiskQuery() *sqlchemy.SQuery {
|
||||
guestdisks := GuestdiskManager.Query()
|
||||
guests := GuestManager.Query().SubQuery()
|
||||
guestdisks = guestdisks.Join(guests, sqlchemy.Equals(guestdisks.Field("guest_id"), guests.Field("id")))
|
||||
return guestdisks.Equals("disk_id", self.Id).CountWithError()
|
||||
return guestdisks.Equals("disk_id", disk.Id)
|
||||
}
|
||||
|
||||
func (self *SDisk) GetGuestDiskCount() (int, error) {
|
||||
return self.GetGuestDiskQuery().CountWithError()
|
||||
}
|
||||
|
||||
func (disk *SDisk) GetGuestDisk() (*SGuestdisk, error) {
|
||||
guestdisk := &SGuestdisk{}
|
||||
err := disk.GetGuestDiskQuery().First(guestdisk)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "First")
|
||||
}
|
||||
return guestdisk, nil
|
||||
}
|
||||
|
||||
func (self *SDisk) isAttached() (bool, error) {
|
||||
@@ -2537,11 +2560,12 @@ func (manager *SDiskManager) FetchCustomizeColumns(
|
||||
return rows
|
||||
}
|
||||
|
||||
guests := map[string][]api.SimpleGuest{}
|
||||
guests, guestIds := map[string][]api.SimpleGuest{}, []string{}
|
||||
for _, guest := range guestInfo {
|
||||
_, ok := guests[guest.DiskId]
|
||||
if !ok {
|
||||
guests[guest.DiskId] = []api.SimpleGuest{}
|
||||
guestIds = append(guestIds, guest.Id)
|
||||
}
|
||||
guests[guest.DiskId] = append(guests[guest.DiskId], api.SimpleGuest{
|
||||
Id: guest.Id,
|
||||
@@ -2558,7 +2582,7 @@ func (manager *SDiskManager) FetchCustomizeColumns(
|
||||
}
|
||||
|
||||
policySQ := SnapshotPolicyManager.Query().SubQuery()
|
||||
dps := SnapshotPolicyResourceManager.Query().Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_DISK).SubQuery()
|
||||
dps := SnapshotPolicyResourceManager.Query().SubQuery()
|
||||
|
||||
q = policySQ.Query(
|
||||
policySQ.Field("id"),
|
||||
@@ -2566,8 +2590,9 @@ func (manager *SDiskManager) FetchCustomizeColumns(
|
||||
policySQ.Field("time_points"),
|
||||
policySQ.Field("repeat_weekdays"),
|
||||
dps.Field("resource_id"),
|
||||
dps.Field("resource_type"),
|
||||
).Join(dps, sqlchemy.Equals(dps.Field("snapshotpolicy_id"), policySQ.Field("id"))).
|
||||
Filter(sqlchemy.In(dps.Field("resource_id"), diskIds))
|
||||
Filter(sqlchemy.OR(sqlchemy.In(dps.Field("resource_id"), diskIds), sqlchemy.In(dps.Field("resource_id"), guestIds)))
|
||||
|
||||
policyInfo := []struct {
|
||||
Id string
|
||||
@@ -2575,7 +2600,8 @@ func (manager *SDiskManager) FetchCustomizeColumns(
|
||||
Status string
|
||||
TimePoints []int
|
||||
RepeatWeekdays []int
|
||||
DiskId string
|
||||
ResourceId string
|
||||
ResourceType string
|
||||
}{}
|
||||
err = q.All(&policyInfo)
|
||||
if err != nil {
|
||||
@@ -2585,15 +2611,16 @@ func (manager *SDiskManager) FetchCustomizeColumns(
|
||||
|
||||
policies := map[string][]api.SimpleSnapshotPolicy{}
|
||||
for _, policy := range policyInfo {
|
||||
_, ok := policies[policy.DiskId]
|
||||
_, ok := policies[policy.ResourceId]
|
||||
if !ok {
|
||||
policies[policy.DiskId] = []api.SimpleSnapshotPolicy{}
|
||||
policies[policy.ResourceId] = []api.SimpleSnapshotPolicy{}
|
||||
}
|
||||
policies[policy.DiskId] = append(policies[policy.DiskId], api.SimpleSnapshotPolicy{
|
||||
policies[policy.ResourceId] = append(policies[policy.ResourceId], api.SimpleSnapshotPolicy{
|
||||
Id: policy.Id,
|
||||
Name: policy.Name,
|
||||
RepeatWeekdays: policy.RepeatWeekdays,
|
||||
TimePoints: policy.TimePoints,
|
||||
ResourceType: policy.ResourceType,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2601,12 +2628,15 @@ func (manager *SDiskManager) FetchCustomizeColumns(
|
||||
rows[i].Guests, _ = guests[diskIds[i]]
|
||||
names, status, billingTypes := []string{}, []string{}, []string{}
|
||||
var iops, bps int
|
||||
for _, guest := range rows[i].Guests {
|
||||
for j := range rows[i].Guests {
|
||||
guest := rows[i].Guests[j]
|
||||
names = append(names, guest.Name)
|
||||
status = append(status, guest.Status)
|
||||
iops = guest.Iops
|
||||
bps = guest.Bps
|
||||
billingTypes = append(billingTypes, guest.BillingType)
|
||||
rows[i].Guests[j].Snapshotpolicy, _ = policies[guest.Id]
|
||||
rows[i].GuestSnapshotpolicyCount += len(rows[i].Guests[j].Snapshotpolicy)
|
||||
}
|
||||
rows[i].GuestCount = len(rows[i].Guests)
|
||||
rows[i].Guest = strings.Join(names, ",")
|
||||
@@ -3147,12 +3177,33 @@ func (disk *SDisk) GetUsages() []db.IUsage {
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定磁盘快照策略
|
||||
// 磁盘只能绑定一个快照策略,已绑定时报错
|
||||
// 若磁盘所属主机已绑定主机快照策略,则磁盘不能再绑定快照策略
|
||||
func (disk *SDisk) PerformBindSnapshotpolicy(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
input *api.DiskSnapshotpolicyInput,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
// 磁盘只能绑定一个快照策略,已绑定时报错
|
||||
cnt, err := SnapshotPolicyResourceManager.GetBindingCount(disk.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBindingCount")
|
||||
}
|
||||
if cnt > 0 {
|
||||
return nil, httperrors.NewConflictError("disk already bound to a snapshot policy")
|
||||
}
|
||||
// 若磁盘所属主机已绑定主机快照策略,则磁盘不能再绑定快照策略
|
||||
if guest := disk.GetGuest(); guest != nil {
|
||||
guestCnt, err := SnapshotPolicyResourceManager.GetBindingCount(guest.Id, api.SNAPSHOT_POLICY_TYPE_SERVER)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBindingCount for guest")
|
||||
}
|
||||
if guestCnt > 0 {
|
||||
return nil, httperrors.NewConflictError("guest already has server snapshot policy, disk cannot bind snapshot policy")
|
||||
}
|
||||
}
|
||||
spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -3177,6 +3228,52 @@ func (disk *SDisk) PerformBindSnapshotpolicy(
|
||||
return nil, sp.StartBindDisksTask(ctx, userCred, []string{disk.Id})
|
||||
}
|
||||
|
||||
// 设置磁盘快照策略
|
||||
// 可覆盖当前磁盘绑定的快照策略,若磁盘所属主机已绑定主机快照策略,则自动解除主机快照策略
|
||||
func (disk *SDisk) PerformSetSnapshotpolicy(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
input *api.DiskSnapshotpolicyInput,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sp := spObj.(*SSnapshotPolicy)
|
||||
if sp.Type != api.SNAPSHOT_POLICY_TYPE_DISK {
|
||||
return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a disk snapshot policy", sp.Name)
|
||||
}
|
||||
if len(sp.ManagerId) > 0 {
|
||||
storage, err := disk.GetStorage()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetStorage")
|
||||
}
|
||||
if storage.ManagerId != sp.ManagerId {
|
||||
return nil, httperrors.NewConflictError("The snapshot policy %s and disk account are different", sp.Name)
|
||||
}
|
||||
zone, err := storage.GetZone()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetZone")
|
||||
}
|
||||
if sp.CloudregionId != zone.CloudregionId {
|
||||
return nil, httperrors.NewConflictError("The snapshot policy %s and the disk are in different region", sp.Name)
|
||||
}
|
||||
}
|
||||
// 先解除当前绑定再绑定新策略
|
||||
if err := SnapshotPolicyResourceManager.RemoveByResource(disk.Id, api.SNAPSHOT_POLICY_TYPE_DISK); err != nil {
|
||||
return nil, errors.Wrap(err, "RemoveByResource")
|
||||
}
|
||||
// 若磁盘所属主机已绑定主机快照策略,则磁盘不能再绑定快照策略
|
||||
if guest := disk.GetGuest(); guest != nil {
|
||||
if err := SnapshotPolicyResourceManager.RemoveByResource(guest.Id, api.SNAPSHOT_POLICY_TYPE_SERVER); err != nil {
|
||||
return nil, errors.Wrap(err, "RemoveByResource")
|
||||
}
|
||||
}
|
||||
return nil, sp.StartBindDisksTask(ctx, userCred, []string{disk.Id})
|
||||
}
|
||||
|
||||
// 解绑自动快照策略
|
||||
func (disk *SDisk) PerformUnbindSnapshotpolicy(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
|
||||
@@ -143,7 +143,7 @@ func (self *SGuest) PerformEvent(ctx context.Context, userCred mcclient.TokenCre
|
||||
kwargs := jsonutils.NewDict()
|
||||
kwargs.Set("reason", jsonutils.NewString(event))
|
||||
if data.Contains("screen_dump_info") {
|
||||
screenDumpInfo := api.SGuestScreenDump{}
|
||||
screenDumpInfo := api.SGuestScreenDumpInfo{}
|
||||
if err := data.Unmarshal(&screenDumpInfo, "screen_dump_info"); err != nil {
|
||||
log.Errorf("failed unmarshal screen_dump_info %s", err)
|
||||
} else {
|
||||
@@ -4255,6 +4255,29 @@ func (self *SGuest) SaveRenewInfo(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SGuest) PerformSetNetworkNumQueues(
|
||||
ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSetNetworkNumQueuesInput,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
if self.Status != api.VM_READY {
|
||||
return nil, httperrors.NewInvalidStatusError("can't set network num_queues on vm %s", self.Status)
|
||||
}
|
||||
if input.NumQueues < 1 {
|
||||
return nil, httperrors.NewInputParameterError("invalid num_queues %d", input.NumQueues)
|
||||
}
|
||||
gn, err := self.GetGuestnetworkByMac(input.MacAddr)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return nil, httperrors.NewNotFoundError("guest network mac %s not found", input.MacAddr)
|
||||
}
|
||||
}
|
||||
_, err = db.Update(gn, func() error {
|
||||
gn.NumQueues = input.NumQueues
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (self *SGuest) PerformStreamDisksComplete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
disks, err := self.GetDisks()
|
||||
if err != nil {
|
||||
@@ -5506,6 +5529,96 @@ func (self *SGuest) PerformUnbindGroups(ctx context.Context, userCred mcclient.T
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 绑定主机快照策略
|
||||
// 主机只能绑定一个快照策略,已绑定时报错
|
||||
// 若主机下任意磁盘已绑定快照策略则报错
|
||||
func (self *SGuest) PerformBindSnapshotpolicy(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject, input *api.ServerSnapshotpolicyInput) (jsonutils.JSONObject, error) {
|
||||
if len(input.SnapshotpolicyId) == 0 {
|
||||
return nil, httperrors.NewMissingParameterError("snapshotpolicy_id")
|
||||
}
|
||||
spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sp := spObj.(*SSnapshotPolicy)
|
||||
if sp.Type != api.SNAPSHOT_POLICY_TYPE_SERVER {
|
||||
return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a server snapshot policy", sp.Name)
|
||||
}
|
||||
// 主机只能绑定一个快照策略
|
||||
cnt, err := SnapshotPolicyResourceManager.GetBindingCount(self.Id, api.SNAPSHOT_POLICY_TYPE_SERVER)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBindingCount")
|
||||
}
|
||||
if cnt > 0 {
|
||||
return nil, httperrors.NewConflictError("guest already bound to a snapshot policy")
|
||||
}
|
||||
// 若主机下任意磁盘已绑定快照策略,则主机不能再绑定主机快照策略
|
||||
disks, err := self.GetDisks()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetDisks")
|
||||
}
|
||||
for _, d := range disks {
|
||||
diskCnt, err := SnapshotPolicyResourceManager.GetBindingCount(d.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBindingCount for disk")
|
||||
}
|
||||
if diskCnt > 0 {
|
||||
return nil, httperrors.NewConflictError("guest has disk %s bound to snapshot policy, guest cannot bind server snapshot policy", d.Name)
|
||||
}
|
||||
}
|
||||
sr := &SSnapshotPolicyResource{}
|
||||
sr.SetModelManager(SnapshotPolicyResourceManager, sr)
|
||||
sr.SnapshotpolicyId = sp.Id
|
||||
sr.ResourceId = self.Id
|
||||
sr.ResourceType = api.SNAPSHOT_POLICY_TYPE_SERVER
|
||||
if err := SnapshotPolicyResourceManager.TableSpec().Insert(ctx, sr); err != nil {
|
||||
return nil, errors.Wrap(err, "Insert")
|
||||
}
|
||||
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_BIND, input, userCred, true)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 设置主机快照策略
|
||||
// 可覆盖当前主机绑定的快照策略,若主机下任意磁盘已绑定快照策略,则自动解除磁盘快照策略
|
||||
func (self *SGuest) PerformSetSnapshotpolicy(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject, input *api.ServerSnapshotpolicyInput) (jsonutils.JSONObject, error) {
|
||||
if len(input.SnapshotpolicyId) == 0 {
|
||||
return nil, httperrors.NewMissingParameterError("snapshotpolicy_id")
|
||||
}
|
||||
spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sp := spObj.(*SSnapshotPolicy)
|
||||
if sp.Type != api.SNAPSHOT_POLICY_TYPE_SERVER {
|
||||
return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a server snapshot policy", sp.Name)
|
||||
}
|
||||
if err := SnapshotPolicyResourceManager.RemoveByResource(self.Id, api.SNAPSHOT_POLICY_TYPE_SERVER); err != nil {
|
||||
return nil, errors.Wrap(err, "RemoveByResource")
|
||||
}
|
||||
// 若主机下任意磁盘已绑定快照策略,则主机不能再绑定主机快照策略
|
||||
disks, err := self.GetDisks()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetDisks")
|
||||
}
|
||||
for _, d := range disks {
|
||||
if err := SnapshotPolicyResourceManager.RemoveByResource(d.Id, api.SNAPSHOT_POLICY_TYPE_DISK); err != nil {
|
||||
return nil, errors.Wrap(err, "RemoveByResource")
|
||||
}
|
||||
}
|
||||
sr := &SSnapshotPolicyResource{}
|
||||
sr.SetModelManager(SnapshotPolicyResourceManager, sr)
|
||||
sr.SnapshotpolicyId = sp.Id
|
||||
sr.ResourceId = self.Id
|
||||
sr.ResourceType = api.SNAPSHOT_POLICY_TYPE_SERVER
|
||||
if err := SnapshotPolicyResourceManager.TableSpec().Insert(ctx, sr); err != nil {
|
||||
return nil, errors.Wrap(err, "Insert")
|
||||
}
|
||||
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_UPDATE, input, userCred, true)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *SGuest) checkGroups(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
|
||||
data jsonutils.JSONObject) (sets.String, error) {
|
||||
|
||||
|
||||
@@ -90,6 +90,15 @@ func (manager *SGuestManager) FetchCustomizeColumns(
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(fields) == 0 || fields.Contains("snapshotpolicy") {
|
||||
counts := fetchGuestSnapshotpolicyInfo(guestIds)
|
||||
for i := range rows {
|
||||
rows[i].SnapshotpolicyCount = counts[guestIds[i]]
|
||||
for j := range rows[i].DisksInfo {
|
||||
rows[i].DisksSnapshotpolicyCount += counts[rows[i].DisksInfo[j].Id]
|
||||
}
|
||||
}
|
||||
}
|
||||
/*if len(fields) == 0 || fields.Contains("ips") {
|
||||
gips := fetchGuestIPs(guestIds, tristate.False)
|
||||
if gips != nil {
|
||||
@@ -440,6 +449,34 @@ func fetchGuestDisksInfo(guestIds []string) map[string][]GuestDiskInfo {
|
||||
return ret
|
||||
}
|
||||
|
||||
func fetchGuestSnapshotpolicyInfo(guestIds []string) map[string]int {
|
||||
ret := map[string]int{}
|
||||
disks := GuestdiskManager.Query("disk_id").In("guest_id", guestIds).SubQuery()
|
||||
spq := SnapshotPolicyResourceManager.Query()
|
||||
spq = spq.Filter(sqlchemy.OR(
|
||||
sqlchemy.In(spq.Field("resource_id"), guestIds),
|
||||
sqlchemy.In(spq.Field("resource_id"), disks),
|
||||
))
|
||||
sq := spq.SubQuery()
|
||||
q := sq.Query(
|
||||
sq.Field("resource_id"),
|
||||
sqlchemy.COUNT("count", sq.Field("snapshotpolicy_id")),
|
||||
).GroupBy(sq.Field("resource_id"))
|
||||
counts := []struct {
|
||||
ResourceId string
|
||||
Count int
|
||||
}{}
|
||||
err := q.All(&counts)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, count := range counts {
|
||||
ret[count.ResourceId] = count.Count
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (guest *SGuest) GetDisksSize() int {
|
||||
return guest.getDiskSize()
|
||||
}
|
||||
|
||||
@@ -738,6 +738,16 @@ func (manager *SGuestManager) ListItemFilter(
|
||||
q = q.NotIn("id", spjsq)
|
||||
}
|
||||
}
|
||||
if query.BindingDisksSnapshotpolicy != nil {
|
||||
guestDisks := GuestdiskManager.Query("guest_id")
|
||||
sq := SnapshotPolicyResourceManager.Query("resource_id").Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_DISK).SubQuery()
|
||||
gdsq := guestDisks.Join(sq, sqlchemy.Equals(guestDisks.Field("disk_id"), sq.Field("resource_id"))).SubQuery()
|
||||
if *query.BindingDisksSnapshotpolicy {
|
||||
q = q.In("id", gdsq)
|
||||
} else {
|
||||
q = q.NotIn("id", gdsq)
|
||||
}
|
||||
}
|
||||
|
||||
return q, nil
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ func (self *SGuestScreenDump) Delete(ctx context.Context, userCred mcclient.Toke
|
||||
return db.DeleteModel(ctx, userCred, self)
|
||||
}
|
||||
|
||||
func (self *SGuest) SaveGuestScreenDump(ctx context.Context, userCred mcclient.TokenCredential, screenDumpInfo *api.SGuestScreenDump) (*SGuestScreenDump, error) {
|
||||
func (self *SGuest) SaveGuestScreenDump(ctx context.Context, userCred mcclient.TokenCredential, screenDumpInfo *api.SGuestScreenDumpInfo) (*SGuestScreenDump, error) {
|
||||
sd := new(SGuestScreenDump)
|
||||
sd.SetModelManager(GuestScreenDumpManager, sd)
|
||||
sd.GuestId = self.GetId()
|
||||
|
||||
@@ -5876,6 +5876,7 @@ func (hh *SHost) Attach2Network(
|
||||
bn.NetworkId = net.Id
|
||||
bn.IpAddr = freeIp
|
||||
bn.MacAddr = netif.Mac
|
||||
bn.VlanId = netif.VlanId
|
||||
err = HostnetworkManager.TableSpec().Insert(ctx, bn)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "HostnetworkManager.TableSpec().Insert")
|
||||
|
||||
@@ -320,22 +320,6 @@ func (self *SServerSkuManager) ValidateCreateData(ctx context.Context, userCred
|
||||
region, _ = zone.GetRegion()
|
||||
}
|
||||
|
||||
if input.CpuCoreCount < 1 || input.CpuCoreCount > options.Options.SkuMaxCpuCount {
|
||||
return input, httperrors.NewOutOfRangeError("cpu_core_count should be range of 1~%d", options.Options.SkuMaxCpuCount)
|
||||
}
|
||||
|
||||
if input.MemorySizeMB < 512 || input.MemorySizeMB > 1024*options.Options.SkuMaxMemSize {
|
||||
return input, httperrors.NewOutOfRangeError("memory_size_mb, shoud be range of 512~%d", 1024*options.Options.SkuMaxMemSize)
|
||||
}
|
||||
|
||||
if len(input.InstanceTypeCategory) == 0 {
|
||||
input.InstanceTypeCategory = api.SkuCategoryGeneralPurpose
|
||||
}
|
||||
|
||||
if !utils.IsInStringArray(input.InstanceTypeCategory, api.SKU_FAMILIES) {
|
||||
return input, httperrors.NewInputParameterError("instance_type_category shoud be one of %s", api.SKU_FAMILIES)
|
||||
}
|
||||
|
||||
if input.Enabled == nil {
|
||||
enabled := true
|
||||
input.Enabled = &enabled
|
||||
@@ -346,14 +330,41 @@ func (self *SServerSkuManager) ValidateCreateData(ctx context.Context, userCred
|
||||
if region != nil {
|
||||
input.Provider = region.Provider
|
||||
}
|
||||
|
||||
if input.Provider == api.CLOUD_PROVIDER_ONECLOUD {
|
||||
input.CloudregionId = api.DEFAULT_REGION_ID
|
||||
} else if utils.IsInStringArray(input.Provider, api.PRIVATE_CLOUD_PROVIDERS) {
|
||||
input.Status = api.SkuStatusCreating
|
||||
}
|
||||
|
||||
input.LocalCategory = input.InstanceTypeCategory
|
||||
input.InstanceTypeFamily = api.InstanceFamilies[input.InstanceTypeCategory]
|
||||
if !utils.IsInStringArray(input.Provider, api.PUBLIC_CLOUD_PROVIDERS) {
|
||||
if input.CpuCoreCount < 1 || input.CpuCoreCount > options.Options.SkuMaxCpuCount {
|
||||
return input, httperrors.NewOutOfRangeError("cpu_core_count should be range of 1~%d", options.Options.SkuMaxCpuCount)
|
||||
}
|
||||
|
||||
if input.MemorySizeMB < 512 || input.MemorySizeMB > 1024*options.Options.SkuMaxMemSize {
|
||||
return input, httperrors.NewOutOfRangeError("memory_size_mb, shoud be range of 512~%d", 1024*options.Options.SkuMaxMemSize)
|
||||
}
|
||||
|
||||
if len(input.InstanceTypeCategory) == 0 {
|
||||
input.InstanceTypeCategory = api.SkuCategoryGeneralPurpose
|
||||
}
|
||||
|
||||
if !utils.IsInStringArray(input.InstanceTypeCategory, api.SKU_FAMILIES) {
|
||||
return input, httperrors.NewInputParameterError("instance_type_category shoud be one of %s", api.SKU_FAMILIES)
|
||||
}
|
||||
|
||||
input.LocalCategory = input.InstanceTypeCategory
|
||||
input.InstanceTypeFamily = api.InstanceFamilies[input.InstanceTypeCategory]
|
||||
}
|
||||
|
||||
if len(input.LocalCategory) == 0 {
|
||||
input.LocalCategory = input.InstanceTypeCategory
|
||||
}
|
||||
|
||||
if len(input.InstanceTypeFamily) == 0 {
|
||||
input.InstanceTypeFamily = api.InstanceFamilies[input.InstanceTypeCategory]
|
||||
}
|
||||
|
||||
var err error
|
||||
if len(input.Name) == 0 {
|
||||
|
||||
@@ -91,3 +91,8 @@ func (man *SSnapshotPolicyResourceManager) RemoveBySnapshotpolicy(id string) err
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetBindingCount returns the number of snapshot policies bound to the given resource.
|
||||
func (man *SSnapshotPolicyResourceManager) GetBindingCount(resourceId, resourceType string) (int, error) {
|
||||
return man.Query().Equals("resource_id", resourceId).Equals("resource_type", resourceType).CountWithError()
|
||||
}
|
||||
|
||||
@@ -510,6 +510,12 @@ func (sp *SSnapshotPolicy) PerformBindDisks(
|
||||
return nil, sp.StartBindDisksTask(ctx, userCred, diskIds)
|
||||
}
|
||||
|
||||
// 绑定资源
|
||||
// 目前仅支持绑定主机和磁盘
|
||||
// 磁盘只能绑定一个快照策略,已绑定时报错
|
||||
// 若磁盘所属主机已绑定主机快照策略,则磁盘不能再绑定快照策略
|
||||
// 主机只能绑定一个快照策略,已绑定时报错
|
||||
// 若主机下任意磁盘已绑定快照策略,则主机不能再绑定主机快照策略
|
||||
func (sp *SSnapshotPolicy) PerformBindResources(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
@@ -522,15 +528,63 @@ func (sp *SSnapshotPolicy) PerformBindResources(
|
||||
for i := range input.Resources {
|
||||
switch input.Resources[i].Type {
|
||||
case api.SNAPSHOT_POLICY_TYPE_DISK:
|
||||
_, err := validators.ValidateModel(ctx, userCred, DiskManager, &input.Resources[i].Id)
|
||||
if sp.Type != api.SNAPSHOT_POLICY_TYPE_DISK {
|
||||
return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a disk snapshot policy", sp.Name)
|
||||
}
|
||||
diskObj, err := validators.ValidateModel(ctx, userCred, DiskManager, &input.Resources[i].Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disk := diskObj.(*SDisk)
|
||||
// 磁盘只能绑定一个快照策略
|
||||
cnt, err := SnapshotPolicyResourceManager.GetBindingCount(disk.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBindingCount")
|
||||
}
|
||||
if cnt > 0 {
|
||||
return nil, httperrors.NewConflictError("disk %s already bound to a snapshot policy", disk.Name)
|
||||
}
|
||||
// 若磁盘所属主机已绑定主机快照策略,则磁盘不能再绑定
|
||||
if guest := disk.GetGuest(); guest != nil {
|
||||
guestCnt, err := SnapshotPolicyResourceManager.GetBindingCount(guest.Id, api.SNAPSHOT_POLICY_TYPE_SERVER)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBindingCount for guest")
|
||||
}
|
||||
if guestCnt > 0 {
|
||||
return nil, httperrors.NewConflictError("guest %s already has server snapshot policy, disk cannot bind snapshot policy", guest.Name)
|
||||
}
|
||||
}
|
||||
case api.SNAPSHOT_POLICY_TYPE_SERVER:
|
||||
_, err := validators.ValidateModel(ctx, userCred, GuestManager, &input.Resources[i].Id)
|
||||
if sp.Type != api.SNAPSHOT_POLICY_TYPE_SERVER {
|
||||
return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a server snapshot policy", sp.Name)
|
||||
}
|
||||
guestObj, err := validators.ValidateModel(ctx, userCred, GuestManager, &input.Resources[i].Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
guest := guestObj.(*SGuest)
|
||||
// 主机只能绑定一个快照策略
|
||||
cnt, err := SnapshotPolicyResourceManager.GetBindingCount(guest.Id, api.SNAPSHOT_POLICY_TYPE_SERVER)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBindingCount")
|
||||
}
|
||||
if cnt > 0 {
|
||||
return nil, httperrors.NewConflictError("guest %s already bound to a snapshot policy", guest.Name)
|
||||
}
|
||||
// 若主机下任意磁盘已绑定快照策略,则主机不能再绑定主机快照策略
|
||||
disks, err := guest.GetDisks()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "guest.GetDisks")
|
||||
}
|
||||
for _, d := range disks {
|
||||
diskCnt, err := SnapshotPolicyResourceManager.GetBindingCount(d.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetBindingCount for disk")
|
||||
}
|
||||
if diskCnt > 0 {
|
||||
return nil, httperrors.NewConflictError("guest %s has disk %s bound to snapshot policy, guest cannot bind server snapshot policy", guest.Name, d.Name)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil, httperrors.NewBadRequestError("Invalid resource type: %s", input.Resources[i].Type)
|
||||
}
|
||||
|
||||
@@ -101,6 +101,8 @@ func InitHandlers(app *appsrv.Application) {
|
||||
models.WafRuleStatementManager,
|
||||
|
||||
models.LoadbalancerSecurityGroupManager,
|
||||
|
||||
models.SnapshotPolicyDiskManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
}
|
||||
|
||||
@@ -653,6 +653,12 @@ func (n *SGuestNetworkSyncTask) Start(callback func(...error)) {
|
||||
}
|
||||
n.addNicMacs = addNicMacs
|
||||
n.addNicConfs = addNicConfs
|
||||
|
||||
// deploy nics configure before do add nics
|
||||
allNics := append(n.guest.Desc.Nics, n.addNics...)
|
||||
if err := n.guest.QgaDeployNicsConfigure(allNics); err != nil {
|
||||
log.Errorf("failed do QgaDeployNicsConfigure %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
n.delNicCnt = len(n.delNics)
|
||||
@@ -671,6 +677,7 @@ func (n *SGuestNetworkSyncTask) syncNetworkConf() {
|
||||
} else {
|
||||
func() {
|
||||
if len(n.addNicMacs) > 0 || n.delNicCnt > 0 {
|
||||
// redeploy nics config after add/del nics
|
||||
if err := n.guest.QgaDeployNicsConfigure(n.guest.Desc.Nics); err != nil {
|
||||
log.Errorf("failed do QgaDeployNicsConfigure %s", err)
|
||||
return
|
||||
@@ -678,7 +685,7 @@ func (n *SGuestNetworkSyncTask) syncNetworkConf() {
|
||||
}
|
||||
if len(n.addNicMacs) > 0 {
|
||||
// try restart added nics, wait for added nic ready
|
||||
time.Sleep(3 * time.Second)
|
||||
time.Sleep(6 * time.Second)
|
||||
if err := n.qgaRestartAddedNics(); err != nil {
|
||||
log.Errorf("failed qgaRestartAddedNics %s", err)
|
||||
return
|
||||
@@ -732,6 +739,11 @@ func (n *SGuestNetworkSyncTask) qgaGetAddedNicDevs() error {
|
||||
}
|
||||
|
||||
func (n *SGuestNetworkSyncTask) removeNic(nic *desc.SGuestNetwork) {
|
||||
if nic.Driver == "vfio-pci" {
|
||||
n.onDeviceDel(nic)
|
||||
return
|
||||
}
|
||||
|
||||
callback := func(res string) {
|
||||
if len(res) > 0 && !strings.Contains(res, "not found") {
|
||||
log.Errorf("netdev del failed %s", res)
|
||||
@@ -754,30 +766,34 @@ func (n *SGuestNetworkSyncTask) onNetdevDel(nic *desc.SGuestNetwork) {
|
||||
n.delNicDevice(nic)
|
||||
}
|
||||
|
||||
func (n *SGuestNetworkSyncTask) onDeviceDel(nic *desc.SGuestNetwork) {
|
||||
var i = 0
|
||||
for ; i < len(n.guest.Desc.Nics); i++ {
|
||||
if n.guest.Desc.Nics[i].Index == nic.Index {
|
||||
if nic.Pci != nil {
|
||||
err := n.guest.pciAddrs.ReleasePCIAddress(nic.Pci.PCIAddr)
|
||||
if err != nil {
|
||||
log.Errorf("failed release nic pci addr %s", nic.Pci.PCIAddr)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if i < len(n.guest.Desc.Nics) {
|
||||
n.guest.Desc.Nics = append(n.guest.Desc.Nics[:i], n.guest.Desc.Nics[i+1:]...)
|
||||
}
|
||||
|
||||
n.syncNetworkConf()
|
||||
}
|
||||
|
||||
func (n *SGuestNetworkSyncTask) delNicDevice(nic *desc.SGuestNetwork) {
|
||||
callback := func(res string) {
|
||||
if len(res) > 0 {
|
||||
log.Errorf("network device del failed %s", res)
|
||||
n.errors = append(n.errors, fmt.Errorf("network device del failed %s", res))
|
||||
} else {
|
||||
var i = 0
|
||||
for ; i < len(n.guest.Desc.Nics); i++ {
|
||||
if n.guest.Desc.Nics[i].Index == nic.Index {
|
||||
if nic.Pci != nil {
|
||||
err := n.guest.pciAddrs.ReleasePCIAddress(nic.Pci.PCIAddr)
|
||||
if err != nil {
|
||||
log.Errorf("failed release nic pci addr %s", nic.Pci.PCIAddr)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if i < len(n.guest.Desc.Nics) {
|
||||
n.guest.Desc.Nics = append(n.guest.Desc.Nics[:i], n.guest.Desc.Nics[i+1:]...)
|
||||
}
|
||||
|
||||
n.syncNetworkConf()
|
||||
n.onDeviceDel(nic)
|
||||
}
|
||||
}
|
||||
n.guest.Monitor.DeviceDel(fmt.Sprintf("netdev-%s", nic.Ifname), callback)
|
||||
@@ -1169,7 +1185,22 @@ func (s *SGuestLiveMigrateTask) onSetAutoConverge(res string) {
|
||||
return
|
||||
}
|
||||
|
||||
// https://wiki.qemu.org/Features/AutoconvergeLiveMigration
|
||||
s.Monitor.MigrateSetParameter("cpu-throttle-initial", options.HostOptions.LiveMigrateCpuThrottleInitial, s.onSetCpuThrottleInitial)
|
||||
}
|
||||
|
||||
func (s *SGuestLiveMigrateTask) onSetCpuThrottleInitial(res string) {
|
||||
if strings.Contains(strings.ToLower(res), "error") {
|
||||
s.migrateFailed(fmt.Sprintf("Migrate set params cpu-throttle-initial error: %s", res))
|
||||
return
|
||||
}
|
||||
s.Monitor.MigrateSetParameter("cpu-throttle-increment", options.HostOptions.LiveMigrateCpuThrottleIncrement, s.onSetCpuThrottleIncrement)
|
||||
}
|
||||
|
||||
func (s *SGuestLiveMigrateTask) onSetCpuThrottleIncrement(res string) {
|
||||
if strings.Contains(strings.ToLower(res), "error") {
|
||||
s.migrateFailed(fmt.Sprintf("Migrate set params cpu-throttle-increment error: %s", res))
|
||||
return
|
||||
}
|
||||
s.Monitor.MigrateSetCapability("events", "on", s.onMigrateEnableEvents)
|
||||
}
|
||||
|
||||
@@ -1355,6 +1386,7 @@ func (s *SGuestLiveMigrateTask) startMigrateStatusCheck(res string) {
|
||||
s.migrateFailed(fmt.Sprintf("Migrate error: %s", res))
|
||||
return
|
||||
}
|
||||
s.startRamMigrateTimeout()
|
||||
|
||||
s.c = make(chan struct{})
|
||||
for s.c != nil {
|
||||
@@ -1401,6 +1433,14 @@ func (s *SGuestLiveMigrateTask) onGetMigrateStatus(stats *monitor.MigrationInfo)
|
||||
} else if status == "cancelled" {
|
||||
s.migrateFailed(status)
|
||||
} else if status == "active" {
|
||||
if !s.doTimeoutMigrate && s.timeoutAt.Before(time.Now()) {
|
||||
s.Monitor.SimpleCommand("stop", s.onMigrateStartPostcopy)
|
||||
s.doTimeoutMigrate = true
|
||||
}
|
||||
if s.doTimeoutMigrate {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
ramRemain int64
|
||||
mbps float64
|
||||
|
||||
@@ -1242,7 +1242,7 @@ func (s *SKVMGuestInstance) eventGuestPaniced(event *monitor.Event) {
|
||||
if err != nil {
|
||||
log.Errorf("faild put screenDumpPath %s to s3 %s", screenDumpPath, err)
|
||||
} else {
|
||||
screenDumpInfo := api.SGuestScreenDump{
|
||||
screenDumpInfo := api.SGuestScreenDumpInfo{
|
||||
S3AccessKey: options.HostOptions.S3AccessKey,
|
||||
S3SecretKey: options.HostOptions.S3SecretKey,
|
||||
S3Endpoint: options.HostOptions.S3Endpoint,
|
||||
|
||||
@@ -166,6 +166,8 @@ func (host *SHostService) initHandlers(app *appsrv.Application) {
|
||||
app_common.ExportOptionsHandler(app, &options.HostOptions)
|
||||
}
|
||||
|
||||
const DEFAULT_SCREENDUMP_S3_BUCKET = "onecloud-screendump-new"
|
||||
|
||||
func initS3() {
|
||||
url := options.HostOptions.S3Endpoint
|
||||
if len(url) == 0 {
|
||||
@@ -179,6 +181,10 @@ func initS3() {
|
||||
}
|
||||
url = prefix + url
|
||||
}
|
||||
if options.HostOptions.S3BucketName == "" {
|
||||
options.HostOptions.S3BucketName = DEFAULT_SCREENDUMP_S3_BUCKET
|
||||
}
|
||||
|
||||
err := s3.Init(
|
||||
url,
|
||||
options.HostOptions.S3AccessKey,
|
||||
|
||||
@@ -104,7 +104,7 @@ func (c *SHostDmesgCollector) Start() {
|
||||
|
||||
entry, err := c.parseKmsgLine(line, bootTime)
|
||||
if err != nil {
|
||||
log.Errorf("failed parse kmsg line %s: %s", line, err)
|
||||
log.Debugf("failed parse kmsg line %s: %s", line, err)
|
||||
continue
|
||||
}
|
||||
if entry.Seq <= lastSeq {
|
||||
|
||||
@@ -379,6 +379,9 @@ type GuestMetrics struct {
|
||||
func (d *GuestMetrics) toTelegrafData(tags map[string]string) []string {
|
||||
var tagArr = []string{}
|
||||
for k, v := range tags {
|
||||
if len(k) == 0 || len(v) == 0 {
|
||||
continue
|
||||
}
|
||||
tagArr = append(tagArr, fmt.Sprintf("%s=%s", k, strings.ReplaceAll(v, " ", "+")))
|
||||
}
|
||||
tagStr := strings.Join(tagArr, ",")
|
||||
@@ -386,6 +389,9 @@ func (d *GuestMetrics) toTelegrafData(tags map[string]string) []string {
|
||||
mapToStatStr := func(m map[string]interface{}) string {
|
||||
var statArr = []string{}
|
||||
for k, v := range m {
|
||||
if vs, ok := v.(string); ok && len(vs) == 0 {
|
||||
continue
|
||||
}
|
||||
statArr = append(statArr, fmt.Sprintf("%s=%v", k, v))
|
||||
}
|
||||
return strings.Join(statArr, ",")
|
||||
@@ -398,6 +404,9 @@ func (d *GuestMetrics) toTelegrafData(tags map[string]string) []string {
|
||||
for i := range d.VmNetio {
|
||||
netTagMap := d.VmNetio[i].ToTag()
|
||||
for k, v := range netTagMap {
|
||||
if len(k) == 0 || len(v) == 0 {
|
||||
continue
|
||||
}
|
||||
tagStr = fmt.Sprintf("%s,%s=%s", tagStr, k, v)
|
||||
}
|
||||
res = append(res, fmt.Sprintf("%s,%s %s", "vm_netio", tagStr, mapToStatStr(d.VmNetio[i].ToMap())))
|
||||
|
||||
@@ -32,8 +32,10 @@ type SHostBaseOptions struct {
|
||||
|
||||
DisableSecurityGroup bool `help:"disable security group" default:"false"`
|
||||
|
||||
HostCpuPassthrough bool `default:"true" help:"if it is true, set qemu cpu type as -cpu host, otherwise, qemu64. default is true"`
|
||||
LiveMigrateCpuThrottleMax int64 `default:"99" help:"live migrate auto converge cpu throttle max"`
|
||||
HostCpuPassthrough bool `default:"true" help:"if it is true, set qemu cpu type as -cpu host, otherwise, qemu64. default is true"`
|
||||
LiveMigrateCpuThrottleMax int64 `default:"99" help:"live migrate auto converge cpu throttle max"`
|
||||
LiveMigrateCpuThrottleInitial int64 `default:"60" help:"live migrate auto convert cpu throttle initial"`
|
||||
LiveMigrateCpuThrottleIncrement int64 `default:"20" help:"live migrate auto convert cpu throttle increment"`
|
||||
|
||||
DefaultQemuVersion string `help:"Default qemu version" default:"4.2.0"`
|
||||
NoHpet bool `help:"Disable qemu hpet timer" default:"true"`
|
||||
|
||||
@@ -15,12 +15,16 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/log"
|
||||
|
||||
common_options "yunion.io/x/onecloud/pkg/cloudcommon/options"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/pending_delete"
|
||||
)
|
||||
|
||||
type SImageOptions struct {
|
||||
common_options.HostCommonOptions
|
||||
common_options.HostCommonOptions `"s3_bucket_name->default":"onecloud-images" "s3_bucket_lifecycle_keep_day->default":"0"`
|
||||
|
||||
common_options.DBOptions
|
||||
|
||||
@@ -81,5 +85,22 @@ func OnOptionsChange(oldO, newO interface{}) bool {
|
||||
}
|
||||
|
||||
func (opt SImageOptions) HasValidS3Options() bool {
|
||||
return len(opt.S3Endpoint) > 0 && len(opt.S3AccessKey) > 0 && len(opt.S3SecretKey) > 0 && len(opt.S3BucketName) > 0
|
||||
msg := []string{}
|
||||
if len(opt.S3Endpoint) <= 0 {
|
||||
msg = append(msg, "s3_endpoint is required")
|
||||
}
|
||||
if len(opt.S3AccessKey) <= 0 {
|
||||
msg = append(msg, "s3_access_key is required")
|
||||
}
|
||||
if len(opt.S3SecretKey) <= 0 {
|
||||
msg = append(msg, "s3_secret_key is required")
|
||||
}
|
||||
if len(opt.S3BucketName) <= 0 {
|
||||
msg = append(msg, "s3_bucket_name is required")
|
||||
}
|
||||
if len(msg) > 0 {
|
||||
log.Errorf("invalid s3 options: %s", strings.Join(msg, ", "))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -140,8 +140,18 @@ func StartService() {
|
||||
}
|
||||
|
||||
go func() {
|
||||
if options.Options.S3BucketName == "" {
|
||||
options.Options.S3BucketName = DEFAULT_IMAGE_S3_BUCKET
|
||||
log.Infof("Set s3 bucket name to %s", options.Options.S3BucketName)
|
||||
}
|
||||
|
||||
if options.Options.HasValidS3Options() {
|
||||
initS3()
|
||||
log.Infof("init s3 client success")
|
||||
} else if options.Options.StorageDriver == api.IMAGE_STORAGE_DRIVER_S3 {
|
||||
log.Fatalf("storage driver is s3, but s3 options are not valid")
|
||||
} else {
|
||||
log.Infof("storage driver is not s3 and no valid s3 options, skip init s3 client")
|
||||
}
|
||||
// check image after s3 mounted
|
||||
models.CheckImages(app.GetContext())
|
||||
@@ -188,6 +198,8 @@ func hasVmwareAccount() (bool, error) {
|
||||
return res.Total > 0, nil
|
||||
}
|
||||
|
||||
const DEFAULT_IMAGE_S3_BUCKET = "onecloud-images"
|
||||
|
||||
func initS3() {
|
||||
url := options.Options.S3Endpoint
|
||||
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
|
||||
@@ -197,6 +209,7 @@ func initS3() {
|
||||
}
|
||||
url = prefix + url
|
||||
}
|
||||
|
||||
err := s3.Init(
|
||||
url,
|
||||
options.Options.S3AccessKey,
|
||||
@@ -207,6 +220,11 @@ func initS3() {
|
||||
if err != nil {
|
||||
log.Fatalf("failed init s3 client %s", err)
|
||||
}
|
||||
// clear glance bucket lifecycle definiton
|
||||
if err = s3.SetBucketLifecycle(""); err != nil {
|
||||
log.Warningf("remove onecloud-screendump lifecycle %s", err)
|
||||
}
|
||||
|
||||
func() {
|
||||
fd, err := os.OpenFile("/tmp/s3-pass", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
|
||||
@@ -517,22 +517,45 @@ func (self *SRegion) CreateInstance(hostId, hypervisor string, opts *cloudprovid
|
||||
if opts.BillingCycle != nil {
|
||||
input.Duration = opts.BillingCycle.String()
|
||||
}
|
||||
input.Disks = append(input.Disks, &api.DiskConfig{
|
||||
image, err := self.GetImage(opts.ExternalImageId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetImage")
|
||||
}
|
||||
imageId := opts.ExternalImageId
|
||||
if image.DiskFormat == "iso" {
|
||||
input.Cdrom = opts.ExternalImageId
|
||||
imageId = ""
|
||||
}
|
||||
sysDisk := &api.DiskConfig{
|
||||
Index: 0,
|
||||
ImageId: opts.ExternalImageId,
|
||||
ImageId: imageId,
|
||||
DiskType: api.DISK_TYPE_SYS,
|
||||
SizeMb: opts.SysDisk.SizeGB * 1024,
|
||||
Backend: opts.SysDisk.StorageType,
|
||||
Storage: opts.SysDisk.StorageExternalId,
|
||||
})
|
||||
}
|
||||
if len(opts.SysDisk.Driver) > 0 {
|
||||
sysDisk.Driver = opts.SysDisk.Driver
|
||||
}
|
||||
if len(opts.SysDisk.CacheMode) > 0 {
|
||||
sysDisk.Cache = opts.SysDisk.CacheMode
|
||||
}
|
||||
input.Disks = append(input.Disks, sysDisk)
|
||||
for idx, disk := range opts.DataDisks {
|
||||
input.Disks = append(input.Disks, &api.DiskConfig{
|
||||
dataDisk := &api.DiskConfig{
|
||||
Index: idx + 1,
|
||||
DiskType: api.DISK_TYPE_DATA,
|
||||
SizeMb: disk.SizeGB * 1024,
|
||||
Backend: disk.StorageType,
|
||||
Storage: disk.StorageExternalId,
|
||||
})
|
||||
}
|
||||
if len(disk.Driver) > 0 {
|
||||
dataDisk.Driver = disk.Driver
|
||||
}
|
||||
if len(disk.CacheMode) > 0 {
|
||||
dataDisk.Cache = disk.CacheMode
|
||||
}
|
||||
input.Disks = append(input.Disks, dataDisk)
|
||||
}
|
||||
input.Networks = append(input.Networks, &api.NetworkConfig{
|
||||
Index: 0,
|
||||
|
||||
@@ -29,7 +29,7 @@ func init() {
|
||||
"baremetalnetworks",
|
||||
[]string{"Baremetal_ID", "Host",
|
||||
"Network_ID", "Network", "IP_addr", "Mac_addr",
|
||||
"Nic_Type"},
|
||||
"Nic_Type", "Vlan_ID"},
|
||||
[]string{},
|
||||
&Hosts,
|
||||
&Networks)
|
||||
|
||||
@@ -78,30 +78,34 @@ func (o DiskCreateOptions) Params() (*api.DiskCreateInput, error) {
|
||||
return params, nil
|
||||
}
|
||||
|
||||
type DiskMigrateOptions struct {
|
||||
ID string `help:"ID of the server" json:"-"`
|
||||
|
||||
TargetStorageId string `help:"Disk migrate target storage id or name" json:"target_storage_id"`
|
||||
type DiskIdOptions struct {
|
||||
ID string `help:"ID or Name of disk"`
|
||||
}
|
||||
|
||||
func (o *DiskMigrateOptions) GetId() string {
|
||||
func (o *DiskIdOptions) GetId() string {
|
||||
return o.ID
|
||||
}
|
||||
|
||||
func (o *DiskIdOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type DiskMigrateOptions struct {
|
||||
DiskIdOptions
|
||||
|
||||
TargetStorageId string `help:"Disk migrate target storage id or name" json:"target_storage_id"`
|
||||
}
|
||||
|
||||
func (o *DiskMigrateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.StructToParams(o)
|
||||
}
|
||||
|
||||
type DiskChangeStorageTypeOptions struct {
|
||||
ID string `help:"ID of the server" json:"-"`
|
||||
DiskIdOptions
|
||||
|
||||
StorageType string `help:"Disk migrate target storage type" json:"storage_type"`
|
||||
}
|
||||
|
||||
func (o *DiskChangeStorageTypeOptions) GetId() string {
|
||||
return o.ID
|
||||
}
|
||||
|
||||
func (o *DiskChangeStorageTypeOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.StructToParams(o)
|
||||
}
|
||||
@@ -124,7 +128,9 @@ type DiskListOptions struct {
|
||||
|
||||
SnapshotpolicyId string `help:"snapshotpolicy id"`
|
||||
|
||||
StorageHostId string `help:"filter disk by host"`
|
||||
StorageHostId string `help:"filter disk by host"`
|
||||
BindingServerSnapshotpolicy *bool `help:"filter disk by binding server snapshotpolicy" negative:"no-binding-server-snapshotpolicy"`
|
||||
BindingSnapshotpolicy *bool `help:"filter disk by binding snapshotpolicy" negative:"no-binding-snapshotpolicy"`
|
||||
}
|
||||
|
||||
func (opts *DiskListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
@@ -143,14 +149,10 @@ func (opts *DiskListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
}
|
||||
|
||||
type DiskChangeBillingTypeOptions struct {
|
||||
ID string
|
||||
DiskIdOptions
|
||||
BillingType string `choices:"prepaid|postpaid"`
|
||||
}
|
||||
|
||||
func (o *DiskChangeBillingTypeOptions) GetId() string {
|
||||
return o.ID
|
||||
}
|
||||
|
||||
func (o *DiskChangeBillingTypeOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(map[string]string{"billing_type": o.BillingType}), nil
|
||||
}
|
||||
|
||||
@@ -79,8 +79,10 @@ type ServerListOptions struct {
|
||||
|
||||
WithUserMeta *bool `help:"filter by user metadata" negative:"without_user_meta"`
|
||||
|
||||
WithHost *bool `help:"filter guest with host or not" negative:"without_host"`
|
||||
SnapshotpolicyId string `help:"filter guest with snapshotpolicy or not" json:"snapshotpolicy_id"`
|
||||
WithHost *bool `help:"filter guest with host or not" negative:"without_host"`
|
||||
SnapshotpolicyId string `help:"filter guest with snapshotpolicy or not" json:"snapshotpolicy_id"`
|
||||
BindingDisksSnapshotpolicy *bool `help:"filter guest with disks binding snapshotpolicy or not" negative:"no-binding-disks-snapshotpolicy" json:"binding_disks_snapshotpolicy"`
|
||||
BindingSnapshotpolicy *bool `help:"filter guest with binding snapshotpolicy or not" negative:"no-binding-snapshotpolicy" json:"binding_snapshotpolicy"`
|
||||
}
|
||||
|
||||
func (o *ServerListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
@@ -1498,6 +1500,16 @@ func (o *ServerScreenDumpOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(o), nil
|
||||
}
|
||||
|
||||
type ServerSetNetworkNumQueues struct {
|
||||
ServerIdOptions
|
||||
MacAddr string `help:"server network mac addr"`
|
||||
NumQueues int `help:"network num queues"`
|
||||
}
|
||||
|
||||
func (o *ServerSetNetworkNumQueues) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(o), nil
|
||||
}
|
||||
|
||||
type ServerIsoOptions struct {
|
||||
ServerIdOptions
|
||||
Ordinal int `help:"server iso ordinal, default 0"`
|
||||
|
||||
@@ -61,6 +61,7 @@ type ServerSkusCreateOptions struct {
|
||||
Name string `help:"ServerSku name"`
|
||||
CpuCoreCount int `help:"Cpu Count" required:"true" positional:"true"`
|
||||
MemorySizeMB int `help:"Memory MB" required:"true" positional:"true"`
|
||||
CpuArch string `help:"CPU architecture" choices:"x86|aarch64"`
|
||||
|
||||
OsName *string `help:"OS name/type" choices:"Linux|Windows|Any" default:"Any"`
|
||||
InstanceTypeCategory *string `help:"instance type category" choices:"general_purpose|compute_optimized|memory_optimized|storage_optimized|hardware_accelerated|high_memory|high_storage"`
|
||||
|
||||
@@ -41,6 +41,7 @@ type RobotCreateOptions struct {
|
||||
Header string
|
||||
Body string
|
||||
MsgKey string
|
||||
SecretKey string
|
||||
UseTemplate bool `help:"just for webhook"`
|
||||
}
|
||||
|
||||
@@ -90,6 +91,7 @@ type SrobotUpdateOptions struct {
|
||||
Header *string
|
||||
Body *string
|
||||
MsgKey string
|
||||
SecretKey string
|
||||
UseTemplate tristate.TriState
|
||||
}
|
||||
|
||||
|
||||
@@ -262,13 +262,10 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred
|
||||
|
||||
message := jsonutils.Marshal(input.ResourceDetails).String()
|
||||
|
||||
// append default receiver
|
||||
if len(input.Event) == 0 {
|
||||
for _, receiver := range input.ReceiverIds {
|
||||
// receiverIds = append(receiverIds, api.SReceiverWithGroupTimes{ReceiverId: receiver})
|
||||
if _, ok := receiverIds[receiver]; !ok {
|
||||
receiverIds[receiver] = 0
|
||||
}
|
||||
for _, receiver := range input.ReceiverIds {
|
||||
// receiverIds = append(receiverIds, api.SReceiverWithGroupTimes{ReceiverId: receiver})
|
||||
if _, ok := receiverIds[receiver]; !ok {
|
||||
receiverIds[receiver] = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,6 +313,7 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred
|
||||
}
|
||||
err := nm.create(ctx, userCred, ct, realReceiverIds, nil, input.Priority, event.GetId(), topic.GetId(), topic.Type)
|
||||
if err != nil {
|
||||
log.Errorf("unable to create notification for %s: %v", ct, err)
|
||||
output.FailedList = append(output.FailedList, api.FailedElem{
|
||||
ContactType: ct,
|
||||
Reason: err.Error(),
|
||||
@@ -324,6 +322,7 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred
|
||||
}
|
||||
err = nm.createWithWebhookRobots(ctx, userCred, webhookRobots, input.Priority, event.GetId(), topic.Type)
|
||||
if err != nil {
|
||||
log.Errorf("unable to create notification for webhook robots: %v", err)
|
||||
output.FailedList = append(output.FailedList, api.FailedElem{
|
||||
ContactType: api.WEBHOOK,
|
||||
Reason: err.Error(),
|
||||
@@ -332,6 +331,7 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred
|
||||
// robot
|
||||
err = nm.createWithRobots(ctx, userCred, realRobot, input.Priority, event.GetId(), topic.Type)
|
||||
if err != nil {
|
||||
log.Errorf("unable to create notification for robots: %v", err)
|
||||
output.FailedList = append(output.FailedList, api.FailedElem{
|
||||
ContactType: api.ROBOT,
|
||||
Reason: err.Error(),
|
||||
@@ -365,6 +365,7 @@ func (nm *SNotificationManager) PerformContactNotify(ctx context.Context, userCr
|
||||
params.Header = robot.Header
|
||||
params.Body = robot.Body
|
||||
params.MsgKey = robot.MsgKey
|
||||
params.SecretKey = robot.SecretKey
|
||||
params.Receivers = api.SNotifyReceiver{
|
||||
Contact: robot.Address,
|
||||
}
|
||||
@@ -487,11 +488,9 @@ func (nm *SNotificationManager) create(ctx context.Context, userCred mcclient.To
|
||||
n.SetModelManager(nm, n)
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "NotificationSendTask", n, userCred, nil, "", "")
|
||||
if err != nil {
|
||||
log.Errorf("NotificationSendTask newTask error %v", err)
|
||||
} else {
|
||||
task.ScheduleRun(nil)
|
||||
return errors.Wrapf(err, "NewTask")
|
||||
}
|
||||
return nil
|
||||
return task.ScheduleRun(nil)
|
||||
}
|
||||
|
||||
func (nm *SNotificationManager) createWithWebhookRobots(ctx context.Context, userCred mcclient.TokenCredential, webhookRobotIds []string, priority, eventId string, topicType string) error {
|
||||
|
||||
@@ -60,13 +60,15 @@ type SRobot struct {
|
||||
db.SSharableVirtualResourceBase
|
||||
db.SEnabledResourceBase
|
||||
|
||||
Type string `width:"16" nullable:"false" create:"required" get:"user" list:"user" index:"true"`
|
||||
Address string `nullable:"false" create:"required" update:"user" get:"user" list:"user"`
|
||||
Lang string `width:"16" nullable:"false" create:"required" update:"user" get:"user" list:"user"`
|
||||
Header jsonutils.JSONObject `length:"long" charset:"utf8" nullable:"true" list:"user" create:"optional" update:"user"`
|
||||
Body jsonutils.JSONObject `length:"long" charset:"utf8" nullable:"true" list:"user" create:"optional" update:"user"`
|
||||
MsgKey string `width:"16" nullable:"true" update:"user" get:"user" list:"user"`
|
||||
UseTemplate tristate.TriState `default:"false" list:"domain" update:"user" create:"admin_optional"`
|
||||
Type string `width:"16" nullable:"false" create:"required" get:"user" list:"user" index:"true"`
|
||||
Address string `nullable:"false" create:"required" update:"user" get:"user" list:"user"`
|
||||
Lang string `width:"16" nullable:"false" create:"required" update:"user" get:"user" list:"user"`
|
||||
Header jsonutils.JSONObject `length:"long" charset:"utf8" nullable:"true" list:"user" create:"optional" update:"user"`
|
||||
Body jsonutils.JSONObject `length:"long" charset:"utf8" nullable:"true" list:"user" create:"optional" update:"user"`
|
||||
MsgKey string `width:"16" nullable:"true" update:"user" get:"user" list:"user"`
|
||||
// webhook 签名加密
|
||||
SecretKey string `width:"128" nullable:"true" update:"user"`
|
||||
UseTemplate tristate.TriState `default:"false" list:"domain" update:"user" create:"admin_optional"`
|
||||
}
|
||||
|
||||
var RobotList = []string{api.FEISHU_ROBOT, api.DINGTALK_ROBOT, api.WORKWX_ROBOT, api.WEBHOOK, api.WEBHOOK_ROBOT}
|
||||
@@ -98,11 +100,12 @@ func (rm *SRobotManager) ValidateCreateData(ctx context.Context, userCred mcclie
|
||||
Contact: input.Address,
|
||||
DomainId: input.ProjectDomainId,
|
||||
},
|
||||
Header: input.Header,
|
||||
Body: input.Body,
|
||||
MsgKey: input.MsgKey,
|
||||
Title: "Validate",
|
||||
Message: "This is a verification message, please ignore.",
|
||||
Header: input.Header,
|
||||
Body: input.Body,
|
||||
MsgKey: input.MsgKey,
|
||||
SecretKey: input.SecretKey,
|
||||
Title: "Validate",
|
||||
Message: "This is a verification message, please ignore.",
|
||||
})
|
||||
if err != nil {
|
||||
if errors.ErrConnectRefused == errors.Cause(err) {
|
||||
@@ -158,11 +161,12 @@ func (r *SRobot) ValidateUpdateData(ctx context.Context, userCred mcclient.Token
|
||||
// check Address
|
||||
dirver := GetDriver(fmt.Sprintf("%s-robot", r.Type))
|
||||
err := dirver.Send(ctx, api.SendParams{
|
||||
Header: input.Header,
|
||||
Body: input.Body,
|
||||
MsgKey: input.MsgKey,
|
||||
Title: "Validate",
|
||||
Message: "This is a verification message, please ignore.",
|
||||
Header: input.Header,
|
||||
Body: input.Body,
|
||||
MsgKey: input.MsgKey,
|
||||
SecretKey: input.SecretKey,
|
||||
Title: "Validate",
|
||||
Message: "This is a verification message, please ignore.",
|
||||
Receivers: api.SNotifyReceiver{
|
||||
Contact: input.Address,
|
||||
},
|
||||
|
||||
@@ -129,6 +129,7 @@ func (emailSender *SEmailSender) Send(ctx context.Context, args api.SendParams)
|
||||
errs := make([]error, 0)
|
||||
for tryTime := 3; tryTime > 0; tryTime-- {
|
||||
err = gomail.Send(sender, gmsg)
|
||||
log.Debugf("send %s to %s email err: %v", args.EmailMsg.Subject, to, err)
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrapf(err, "Send"))
|
||||
time.Sleep(time.Second * 10)
|
||||
|
||||
@@ -15,6 +15,9 @@ package sender
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -35,6 +38,15 @@ func (self *SWebhookSender) GetSenderType() string {
|
||||
return api.WEBHOOK_ROBOT
|
||||
}
|
||||
|
||||
func GenerateHMACSignature(payload []byte, secret string) string {
|
||||
// 创建HMAC哈希器
|
||||
h := hmac.New(sha256.New, []byte(secret))
|
||||
// 写入要签名的数据
|
||||
h.Write(payload)
|
||||
// 计算哈希值并转为16进制字符串
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func (self *SWebhookSender) Send(ctx context.Context, args api.SendParams) error {
|
||||
dict := jsonutils.NewDict()
|
||||
header := http.Header{}
|
||||
@@ -75,6 +87,11 @@ func (self *SWebhookSender) Send(ctx context.Context, args api.SendParams) error
|
||||
}
|
||||
}
|
||||
|
||||
if len(args.SecretKey) > 0 {
|
||||
signature := GenerateHMACSignature([]byte(jsonutils.Marshal(dict).String()), args.SecretKey)
|
||||
header.Set(api.WEBHOOK_SIGNATURE_HEADER, signature)
|
||||
}
|
||||
|
||||
_, _, err := httputils.JSONRequest(cli, ctx, httputils.POST, args.Receivers.Contact, header, dict, false)
|
||||
return errors.Wrap(err, "webhook send")
|
||||
}
|
||||
|
||||
@@ -140,7 +140,8 @@ func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
|
||||
continue
|
||||
}
|
||||
if !verified {
|
||||
sendFail(&rns[i], fmt.Sprintf("unverified contactType %q", notification.ContactType))
|
||||
contact, _ := receiver.GetContact(notification.ContactType)
|
||||
sendFail(&rns[i], fmt.Sprintf("unverified contactType %q for contact %s", notification.ContactType, contact))
|
||||
continue
|
||||
}
|
||||
lang, err := receiver.GetTemplateLang(ctx)
|
||||
@@ -179,7 +180,7 @@ func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
|
||||
apis.TEMPLATE_LANG_EN: receiversEn,
|
||||
} {
|
||||
if len(receivers) == 0 {
|
||||
log.Warningf("no receiver to send, skip ...")
|
||||
log.Warningf("no receiver to send for %s %s, skip ...", notification.ContactType, lang)
|
||||
continue
|
||||
}
|
||||
// send
|
||||
@@ -265,6 +266,7 @@ func (notificationSendTask *NotificationSendTask) batchSend(ctx context.Context,
|
||||
params.Header = robot.Header
|
||||
params.Body = robot.Body
|
||||
params.MsgKey = robot.MsgKey
|
||||
params.SecretKey = robot.SecretKey
|
||||
params.GroupTimes = uint(receivers[i].rNotificaion.GroupTimes)
|
||||
err = driver.Send(ctx, params)
|
||||
if err != nil {
|
||||
|
||||
132
pkg/yunionconf/models/tags.go
Normal file
132
pkg/yunionconf/models/tags.go
Normal file
@@ -0,0 +1,132 @@
|
||||
// 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 models
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/yunionconf"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
// +onecloud:swagger-gen-model-singular=tag
|
||||
// +onecloud:swagger-gen-model-plural=tags
|
||||
type STagManager struct {
|
||||
db.SInfrasResourceBaseManager
|
||||
}
|
||||
|
||||
var (
|
||||
TagManager *STagManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
TagManager = &STagManager{
|
||||
SInfrasResourceBaseManager: db.NewInfrasResourceBaseManager(
|
||||
STag{},
|
||||
"tags_tbl",
|
||||
"tag",
|
||||
"tags",
|
||||
),
|
||||
}
|
||||
TagManager.SetVirtualObject(TagManager)
|
||||
}
|
||||
|
||||
type STag struct {
|
||||
db.SInfrasResourceBase
|
||||
|
||||
Values []string `charset:"utf8" get:"user" list:"user" update:"admin" create:"required"`
|
||||
}
|
||||
|
||||
// 预置标签列表
|
||||
func (manager *STagManager) ListItemFilter(
|
||||
ctx context.Context,
|
||||
q *sqlchemy.SQuery,
|
||||
userCred mcclient.TokenCredential,
|
||||
query api.TagListInput,
|
||||
) (*sqlchemy.SQuery, error) {
|
||||
var err error
|
||||
q, err = manager.SInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.InfrasResourceBaseListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SInfrasResourceBaseManager.ListItemFilter")
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (manager *STagManager) OrderByExtraFields(
|
||||
ctx context.Context,
|
||||
q *sqlchemy.SQuery,
|
||||
userCred mcclient.TokenCredential,
|
||||
query api.TagListInput,
|
||||
) (*sqlchemy.SQuery, error) {
|
||||
var err error
|
||||
q, err = manager.SInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.InfrasResourceBaseListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SInfrasResourceBaseManager.OrderByExtraFields")
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (manager *STagManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
|
||||
var err error
|
||||
q, err = manager.SInfrasResourceBaseManager.QueryDistinctExtraField(q, field)
|
||||
if err == nil {
|
||||
return q, nil
|
||||
}
|
||||
return q, httperrors.ErrNotFound
|
||||
}
|
||||
|
||||
func (manager *STagManager) FetchCustomizeColumns(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
objs []interface{},
|
||||
fields stringutils2.SSortedStrings,
|
||||
isList bool,
|
||||
) []api.TagDetails {
|
||||
rows := make([]api.TagDetails, len(objs))
|
||||
|
||||
stdRows := manager.SInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
for i := range rows {
|
||||
rows[i] = api.TagDetails{
|
||||
InfrasResourceBaseDetails: stdRows[i],
|
||||
}
|
||||
}
|
||||
|
||||
return rows
|
||||
}
|
||||
|
||||
func (manager *STagManager) ValidateCreateData(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
ownerId mcclient.IIdentityProvider,
|
||||
query jsonutils.JSONObject,
|
||||
input *api.TagCreateInput,
|
||||
) (*api.TagCreateInput, error) {
|
||||
var err error
|
||||
|
||||
input.InfrasResourceBaseCreateInput, err = manager.SInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.InfrasResourceBaseCreateInput)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "SInfrasResourceBaseManager.ValidateCreateData")
|
||||
}
|
||||
|
||||
return input, nil
|
||||
}
|
||||
@@ -24,3 +24,14 @@ type YunionConfOptions struct {
|
||||
var (
|
||||
Options YunionConfOptions
|
||||
)
|
||||
|
||||
func OnOptionsChange(oldO, newO interface{}) bool {
|
||||
oldOpts := oldO.(*YunionConfOptions)
|
||||
newOpts := newO.(*YunionConfOptions)
|
||||
|
||||
changed := false
|
||||
if common_options.OnCommonOptionsChange(&oldOpts.CommonOptions, &newOpts.CommonOptions) {
|
||||
changed = true
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
@@ -93,6 +93,24 @@ var (
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Auth: true,
|
||||
Scope: rbacscope.ScopeProject,
|
||||
Rules: []rbacutils.SRbacRule{
|
||||
{
|
||||
Service: api.SERVICE_TYPE,
|
||||
Resource: "tags",
|
||||
Action: PolicyActionList,
|
||||
Result: rbacutils.Allow,
|
||||
},
|
||||
{
|
||||
Service: api.SERVICE_TYPE,
|
||||
Resource: "tags",
|
||||
Action: PolicyActionGet,
|
||||
Result: rbacutils.Allow,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ func InitHandlers(app *appsrv.Application) {
|
||||
models.ParameterManager,
|
||||
models.ScopedPolicyBindingManager,
|
||||
models.ScopedPolicyManager,
|
||||
models.TagManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
handler := db.NewModelHandler(manager)
|
||||
|
||||
@@ -46,6 +46,7 @@ func StartService() {
|
||||
app_common.InitAuth(commonOpts, func() {
|
||||
log.Infof("Auth complete!!")
|
||||
})
|
||||
common_options.StartOptionManager(opts, opts.ConfigSyncPeriodSeconds, api.SERVICE_TYPE, api.SERVICE_VERSION, options.OnOptionsChange)
|
||||
|
||||
cloudcommon.InitDB(dbOpts)
|
||||
|
||||
|
||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -1589,7 +1589,7 @@ sigs.k8s.io/structured-merge-diff/v4/value
|
||||
# sigs.k8s.io/yaml v1.2.0
|
||||
## explicit; go 1.12
|
||||
sigs.k8s.io/yaml
|
||||
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260123023413-f5d35910430c
|
||||
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260207043119-2907d68518c5
|
||||
## explicit; go 1.21
|
||||
yunion.io/x/cloudmux/pkg/apis
|
||||
yunion.io/x/cloudmux/pkg/apis/billing
|
||||
|
||||
2
vendor/yunion.io/x/cloudmux/pkg/cloudprovider/instance.go
generated
vendored
2
vendor/yunion.io/x/cloudmux/pkg/cloudprovider/instance.go
generated
vendored
@@ -102,6 +102,8 @@ type SDiskInfo struct {
|
||||
StorageType string
|
||||
SizeGB int
|
||||
Iops int
|
||||
Driver string
|
||||
CacheMode string
|
||||
Name string
|
||||
// aws gp3 only
|
||||
Throughput int
|
||||
|
||||
5
vendor/yunion.io/x/cloudmux/pkg/multicloud/aws/organizations.go
generated
vendored
5
vendor/yunion.io/x/cloudmux/pkg/multicloud/aws/organizations.go
generated
vendored
@@ -192,13 +192,16 @@ func (r *SRegion) ListAccounts() ([]SAccount, error) {
|
||||
for _, actPtr := range parts.Accounts {
|
||||
account := SAccount{
|
||||
ID: *actPtr.Id,
|
||||
Name: *actPtr.Name,
|
||||
Name: *actPtr.Id,
|
||||
Arn: *actPtr.Arn,
|
||||
Email: *actPtr.Email,
|
||||
Status: *actPtr.Status,
|
||||
JoinedMethod: *actPtr.JoinedMethod,
|
||||
JoinedTimestamp: *actPtr.JoinedTimestamp,
|
||||
}
|
||||
if actPtr.Name != nil && len(*actPtr.Name) > 0 {
|
||||
account.Name = *actPtr.Name
|
||||
}
|
||||
if *orgOutput.Organization.MasterAccountId == *actPtr.Id {
|
||||
account.IsMaster = true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user