Compare commits

...

46 Commits

Author SHA1 Message Date
wanyaoqi
5a17a9e37b feat(host-deployer): deploy vlan subinterface on baremetal hypervisor (#22629)
- host-deployer add deploy vlan subinterface on baremetal hypervisor
- fix baremetal-agent cache image parse input data
2025-05-30 10:32:01 +08:00
wanyaoqi
44ed13473a feat(region,host,host-deployer): support set uefi boot order (#22560) 2025-05-30 10:31:03 +08:00
wanyaoqi
d8b00188a0 feat(region,host): qga deploy network config on attach nics (#22548) 2025-05-30 10:29:48 +08:00
屈轩
0b61ca0432 fix(region): vendor update (#22624) 2025-05-29 11:23:02 +08:00
屈轩
b9a9151555 fix(region): secgroup rule filter (#22619) 2025-05-28 18:38:37 +08:00
wanyaoqi
17db7be134 fix(host): qemu extra params format (#22609) 2025-05-27 13:34:48 +08:00
屈轩
892431d5b9 fix(region): update qcloud storage type (#22615) 2025-05-27 12:44:12 +08:00
屈轩
3d36c293e5 fix(region): add region driver for cloudflare (#22606) 2025-05-26 16:22:25 +08:00
Jian Qiu
3ff50f0d11 fix: tar ignore error revisit (#22604)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-05-26 12:35:10 +08:00
Jian Qiu
6144325daf fix: container save image with leading prefix (#22602)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-05-26 12:34:48 +08:00
Zexi Li
bf3b83870c fix: ignore init loop device error (#22601)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-05-26 11:36:09 +08:00
wanyaoqi
62b7eea828 fix(host): check min_free_kbytes size is enough (#22592) 2025-05-24 01:11:09 +08:00
Zexi Li
4e07811e2f fix(monitor): not filter status tag key (#22596) 2025-05-23 09:59:43 +08:00
Zexi Li
f819e021b3 feat(host): loop device manager (#22590) 2025-05-22 13:24:24 +08:00
屈轩
3224eac821 fix(cloudmon): add vm_ip metric for vm (#22586) 2025-05-22 10:53:59 +08:00
屈轩
a1fce125da fix(cloudmon): vm metrics with ips (#22583) 2025-05-22 10:41:11 +08:00
屈轩
aa0a6b58d3 fix(region): add filter for secgroup rule (#22580) 2025-05-21 19:27:29 +08:00
屈轩
0c9a44e279 fix(region): add account info for sec rules (#22577) 2025-05-21 18:40:18 +08:00
Zexi Li
4dd92825a8 fix(host): ignore internal stopped event of primary container (#22574) 2025-05-21 16:23:17 +08:00
屈轩
30a319fe5e fix(region): allow create public server sku (#22571) 2025-05-21 14:43:28 +08:00
Zexi Li
faf35a6879 fix(host): status of stopping primary container (#22568) 2025-05-21 12:23:33 +08:00
屈轩
4520b54a5b fix(region): init aliyun storage medium type (#22564) 2025-05-20 21:48:33 +08:00
wanyaoqi
fa53aea075 fix error when create vm with ceph disk (#22561)
Co-authored-by: liangkaijun <liangkaijun52013@gmail.com>
2025-05-20 21:28:00 +08:00
Jian Qiu
36b5532b04 feature: monitor bucket formance and resource status (#22553)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-05-19 22:28:32 +08:00
Zexi Li
01ab43941e fix(host): clean loop device before deleting (#22558) 2025-05-19 19:47:28 +08:00
屈轩
249569be9f fix(region): user sync with ctx (#22555) 2025-05-19 18:09:26 +08:00
Jian Qiu
db0b275772 fix: host files not initialized when host reboot (#22551)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-05-19 01:57:58 +08:00
wanyaoqi
e6bb86c934 fix: vendor update 2505013 (#22521) 2025-05-19 01:50:32 +08:00
屈轩
c53f707089 fix(region): vendor update (#22544) 2025-05-16 11:19:49 +08:00
屈轩
ef36176130 fix(region): guest disk iops (#22542) 2025-05-15 15:46:12 +08:00
屈轩
edd11ea1cd fix(region): show correct disk iops (#22538) 2025-05-15 12:48:51 +08:00
屈轩
4ad7d635bc fix(region): change disk billing type (#22533) 2025-05-15 11:48:26 +08:00
Zexi Li
18604b46b1 feat(region,host): change ownership of post overlay merged dir (#22525) 2025-05-13 19:52:52 +08:00
屈轩
4609c543c9 fix(region): add dns brand (#22523) 2025-05-13 16:30:57 +08:00
屈轩
779559dbdc fix(region): add proxied for dns record (#22514) 2025-05-12 17:52:46 +08:00
Zexi Li
27c6c82b82 fix(monitor): return measurements info from db directly (#22512) 2025-05-12 17:17:36 +08:00
屈轩
fa99905bf6 fix(common): avoid empty context (#22509) 2025-05-12 16:21:18 +08:00
屈轩
608e4edb4e fix(region): add cloudflare const (#22507) 2025-05-12 14:10:58 +08:00
屈轩
43c646b537 fix(notify): support notify with robotids (#22503) 2025-05-09 21:23:07 +08:00
屈轩
ad798d7bbe fix(notify): optimized notify error msg (#22501) 2025-05-09 17:35:17 +08:00
屈轩
238458066a fix(region): vendor update (#22498) 2025-05-09 14:56:01 +08:00
屈轩
90a8b10401 fix(common): obj id cache (#22495) 2025-05-09 14:50:24 +08:00
Jian Qiu
46195f72ee fix: hostfile delete validation (#22493)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-05-08 23:50:06 +08:00
屈轩
05829a225a fix(region): vendor update (#22491) 2025-05-08 18:36:31 +08:00
屈轩
f0858ce4d2 fix(common): policy tag filter cache (#22485) 2025-05-08 15:56:57 +08:00
Jian Qiu
cc3477415d fix: host files support (#22481)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-05-07 11:28:47 +08:00
168 changed files with 5074 additions and 994 deletions

View File

@@ -1,4 +1,4 @@
FROM registry.cn-beijing.aliyuncs.com/yunionio/host-deployer-base:1.4.9
FROM registry.cn-beijing.aliyuncs.com/yunionio/host-deployer-base:1.4.10
MAINTAINER "Yaoqi Wan wanyaoqi@yunionyun.com"

View File

@@ -19,178 +19,71 @@ import (
"io"
"os"
"yunion.io/x/cloudmux/pkg/multicloud/objectstore"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/util/fileutils"
"yunion.io/x/pkg/util/printutils"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/cmd/climc/shell"
"yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/mcclient"
modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
"yunion.io/x/onecloud/pkg/mcclient/options"
computeoptions "yunion.io/x/onecloud/pkg/mcclient/options/compute"
)
func init() {
type BucketListOptions struct {
options.BaseListOptions
DistinctField string `help:"query specified distinct field"`
}
R(&BucketListOptions{}, "bucket-list", "List all buckets", func(s *mcclient.ClientSession, args *BucketListOptions) error {
params, err := options.ListStructToParams(args)
if err != nil {
return err
}
if len(args.DistinctField) > 0 {
params.Add(jsonutils.NewString(args.DistinctField), "extra_field")
result, err := modules.Buckets.Get(s, "distinct-field", params)
if err != nil {
return err
}
fmt.Println(result)
return nil
}
result, err := modules.Buckets.List(s, params)
if err != nil {
return err
}
printList(result, modules.Buckets.GetColumns(s))
return nil
})
type BucketIdOptions struct {
ID string `help:"ID or name of bucket"`
}
R(&BucketIdOptions{}, "bucket-show", "Id details of bucket", func(s *mcclient.ClientSession, args *BucketIdOptions) error {
result, err := modules.Buckets.Get(s, args.ID, nil)
if err != nil {
return err
}
printObject(result)
return nil
})
R(&BucketIdOptions{}, "bucket-syncstatus", "Sync bucket statust", func(s *mcclient.ClientSession, args *BucketIdOptions) error {
result, err := modules.Buckets.PerformAction(s, args.ID, "syncstatus", nil)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketUpdateOptions struct {
ID string `help:"ID or name of bucket" json:"-"`
Name string `help:"new name of bucket" json:"name"`
Desc string `help:"Description of bucket" json:"description" token:"desc"`
}
R(&BucketUpdateOptions{}, "bucket-update", "update bucket", func(s *mcclient.ClientSession, args *BucketUpdateOptions) error {
params := jsonutils.Marshal(args)
result, err := modules.Buckets.Update(s, args.ID, params)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketDeleteOptions struct {
ID string `help:"ID or name of bucket" json:"-"`
}
R(&BucketDeleteOptions{}, "bucket-delete", "delete bucket", func(s *mcclient.ClientSession, args *BucketDeleteOptions) error {
result, err := modules.Buckets.Delete(s, args.ID, nil)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketCreateOptions struct {
NAME string `help:"name of bucket" json:"name"`
CLOUDREGION string `help:"location of bucket" json:"cloudregion"`
MANAGER string `help:"cloud provider" json:"manager"`
StorageClass string `help:"bucket storage class"`
Acl string `help:"bucket ACL"`
}
R(&BucketCreateOptions{}, "bucket-create", "Create a bucket", func(s *mcclient.ClientSession, args *BucketCreateOptions) error {
params, err := options.StructToParams(args)
if err != nil {
return err
}
result, err := modules.Buckets.Create(s, params)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketListObjectsOptions struct {
ID string `help:"ID or name of bucket" json:"-"`
Prefix string `help:"List objects with prefix"`
Recursive bool `help:"List objects recursively"`
Limit int `help:"maximal items per request"`
PagingMarker string `help:"paging marker"`
}
R(&BucketListObjectsOptions{}, "bucket-object-list", "List objects in a bucket", func(s *mcclient.ClientSession, args *BucketListObjectsOptions) error {
params, err := options.StructToParams(args)
if err != nil {
return err
}
result, err := modules.Buckets.GetSpecific(s, args.ID, "objects", params)
if err != nil {
return err
}
cmd := shell.NewResourceCmd(&modules.Buckets)
cmd.List(&computeoptions.BucketListOptions{})
cmd.GetProperty(&computeoptions.BucketGetPropertyOptions{})
cmd.Show(&computeoptions.BucketIdOptions{})
cmd.Perform("syncstatus", &computeoptions.BucketIdOptions{})
cmd.Update(&computeoptions.BucketUpdateOptions{})
cmd.Delete(&computeoptions.BucketIdOptions{})
cmd.Create(&computeoptions.BucketCreateOptions{})
cmd.GetWithCustomOptionShow("objects", func(data jsonutils.JSONObject, args shell.IGetOpt) {
listResult := printutils.ListResult{}
err = result.Unmarshal(&listResult)
err := data.Unmarshal(&listResult)
if err != nil {
return err
return
}
printList(&listResult, []string{})
return nil
})
}, &computeoptions.BucketListObjectsOptions{})
cmd.Perform("delete", &computeoptions.BucketDeleteObjectsOptions{})
cmd.Perform("makedir", &computeoptions.BucketMakeDirOptions{})
cmd.Perform("temp-url", &computeoptions.BucketPresignObjectsOptions{})
cmd.Perform("acl", &computeoptions.BucketSetAclOptions{})
cmd.GetWithCustomOptionShow("acl", func(data jsonutils.JSONObject, args shell.IGetOpt) {
printObject(data)
}, &computeoptions.BucketAclOptions{})
cmd.Perform("sync", &computeoptions.BucketSyncOptions{})
cmd.Perform("limit", &computeoptions.BucketLimitOptions{})
cmd.GetWithCustomOptionShow("access-info", func(data jsonutils.JSONObject, args shell.IGetOpt) {
printObject(data)
}, &computeoptions.BucketAccessInfoOptions{})
cmd.Perform("metadata", &computeoptions.BucketSetMetadataOptions{})
cmd.Perform("set-website", &computeoptions.BucketSetWebsiteOption{})
cmd.GetWithCustomOptionShow("website", func(data jsonutils.JSONObject, args shell.IGetOpt) {
printObject(data)
}, &computeoptions.BucketGetWebsiteConfOption{})
cmd.Perform("delete-website", &computeoptions.BucketDeleteWebsiteConfOption{})
cmd.Perform("set-cors", &computeoptions.BucketSetCorsOption{})
cmd.GetWithCustomOptionShow("cors", func(data jsonutils.JSONObject, args shell.IGetOpt) {
printObject(data)
}, &computeoptions.BucketGetCorsOption{})
cmd.Perform("delete-cors", &computeoptions.BucketDeleteCorsOption{})
cmd.Perform("set-referer", &computeoptions.BucketSetRefererOption{})
cmd.GetWithCustomOptionShow("referer", func(data jsonutils.JSONObject, args shell.IGetOpt) {
printObject(data)
}, &computeoptions.BucketGetRefererOption{})
cmd.GetWithCustomOptionShow("cdn-domain", func(data jsonutils.JSONObject, args shell.IGetOpt) {
printObject(data)
}, &computeoptions.BucketGetCdnDomainOption{})
cmd.GetWithCustomOptionShow("policy", func(data jsonutils.JSONObject, args shell.IGetOpt) {
printObject(data)
}, &computeoptions.BucketGetPolicyOption{})
cmd.Perform("set-policy", &computeoptions.BucketSetPolicyOption{})
cmd.Perform("delete-policy", &computeoptions.BucketDeletePolicyOption{})
type BucketDeleteObjectsOptions struct {
ID string `help:"ID or name of bucket" json:"-"`
KEYS []string `help:"List of objects to delete"`
}
R(&BucketDeleteObjectsOptions{}, "bucket-object-delete", "Delete objects in a bucket", func(s *mcclient.ClientSession, args *BucketDeleteObjectsOptions) error {
params := jsonutils.Marshal(args)
result, err := modules.Buckets.PerformAction(s, args.ID, "delete", params)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketMakeDirOptions struct {
ID string `help:"ID or name of bucket" json:"-"`
KEY string `help:"DIR key to create"`
}
R(&BucketMakeDirOptions{}, "bucket-mkdir", "Mkdir in a bucket", func(s *mcclient.ClientSession, args *BucketMakeDirOptions) error {
params := jsonutils.Marshal(args)
result, err := modules.Buckets.PerformAction(s, args.ID, "makedir", params)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketUploadObjectsOptions struct {
ID string `help:"ID or name of bucket" json:"-"`
KEY string `help:"Key of object to upload"`
Path string `help:"Path to file to upload" required:"true"`
ContentLength int64 `help:"Content lenght (bytes)" default:"-1"`
StorageClass string `help:"storage CLass"`
Acl string `help:"object acl." choices:"private|public-read|public-read-write"`
objectstore.ObjectHeaderOptions
}
R(&BucketUploadObjectsOptions{}, "bucket-object-upload", "Upload an object into a bucket", func(s *mcclient.ClientSession, args *BucketUploadObjectsOptions) error {
R(&computeoptions.BucketUploadObjectsOptions{}, "bucket-object-upload", "Upload an object into a bucket", func(s *mcclient.ClientSession, args *computeoptions.BucketUploadObjectsOptions) error {
var body io.Reader
if len(args.Path) > 0 {
file, err := os.Open(args.Path)
@@ -223,332 +116,36 @@ func init() {
return nil
})
type BucketPresignObjectsOptions struct {
ID string `help:"ID or name of bucket" json:"-"`
KEY string `help:"Key of object to upload"`
Method string `help:"Request method" choices:"GET|PUT|DELETE"`
ExpireSeconds int `help:"expire in seconds" default:"60"`
}
R(&BucketPresignObjectsOptions{}, "bucket-object-tempurl", "Get temporal URL for an object in a bucket", func(s *mcclient.ClientSession, args *BucketPresignObjectsOptions) error {
params, err := options.StructToParams(args)
R(&computeoptions.BucketPerfMonOptions{}, "bucket-perf-mon", "Bucket performance monitor", func(s *mcclient.ClientSession, args *computeoptions.BucketPerfMonOptions) error {
result, err := modules.Buckets.Get(s, args.ID, nil)
if err != nil {
return err
}
result, err := modules.Buckets.PerformAction(s, args.ID, "temp-url", params)
bucketDetails := compute.BucketDetails{}
err = result.Unmarshal(&bucketDetails)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketSetAclOptions struct {
ID string `help:"ID or name of bucket" json:"-"`
ACL string `help:"ACL to set" choices:"default|private|public-read|public-read-write" json:"acl"`
Key []string `help:"Optional object key" json:"key"`
}
R(&BucketSetAclOptions{}, "bucket-set-acl", "Set ACL of bucket or object", func(s *mcclient.ClientSession, args *BucketSetAclOptions) error {
params := jsonutils.Marshal(args)
result, err := modules.Buckets.PerformAction(s, args.ID, "acl", params)
bucket, err := modules.GetIBucket(s.GetContext(), s, &bucketDetails)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketAclOptions struct {
ID string `help:"ID or name of bucket" json:"-"`
Key string `help:"Optional object key"`
}
R(&BucketAclOptions{}, "bucket-acl", "Get ACL of bucket or object", func(s *mcclient.ClientSession, args *BucketAclOptions) error {
params, err := options.StructToParams(args)
payload, err := fileutils.GetSizeBytes(args.Payload, 1024)
if err != nil {
return err
}
result, err := modules.Buckets.GetSpecific(s, args.ID, "acl", params)
stats, err := modules.ProbeBucketStats(s.GetContext(), bucket, "test", int64(payload))
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketSyncOptions struct {
ID string `help:"ID or name of bucket" json:"-"`
StatsOnly bool `help:"sync statistics only"`
}
R(&BucketSyncOptions{}, "bucket-sync", "Sync bucket", func(s *mcclient.ClientSession, args *BucketSyncOptions) error {
params, err := options.StructToParams(args)
if err != nil {
return err
}
result, err := modules.Buckets.PerformAction(s, args.ID, "sync", params)
if err != nil {
return err
}
printObject(result)
return nil
})
fmt.Printf("Upload delay %f ms throughput %f MB/s\n", stats.UploadDelayMs(), stats.UploadThroughputMbps(payload/1024/1024))
fmt.Printf("Download delay %f ms throughput %f MB/s\n", stats.DownloadDelayMs(), stats.DownloadThroughputMbps(payload/1024/1024))
fmt.Printf("Delete delay %f ms\n", stats.DeleteDelayMs())
type BucketLimitOptions struct {
ID string `help:"ID or name of bucket" json:"-"`
SizeBytes int64 `help:"size limit in bytes"`
ObjectCount int64 `help:"object count limit"`
}
R(&BucketLimitOptions{}, "bucket-limit", "Set limit of bucket", func(s *mcclient.ClientSession, args *BucketLimitOptions) error {
limit := jsonutils.Marshal(args)
params := jsonutils.NewDict()
params.Set("limit", limit)
result, err := modules.Buckets.PerformAction(s, args.ID, "limit", params)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketAccessInfoOptions struct {
ID string `help:"ID or name of bucket" json:"-"`
}
R(&BucketAccessInfoOptions{}, "bucket-access-info", "Show backend access info of a bucket", func(s *mcclient.ClientSession, args *BucketAccessInfoOptions) error {
result, err := modules.Buckets.GetSpecific(s, args.ID, "access-info", nil)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketSetMetadataOptions struct {
ID string `help:"ID or name of bucket" json:"-"`
Key []string `help:"Optional object key" json:"key"`
objectstore.ObjectHeaderOptions
}
R(&BucketSetMetadataOptions{}, "bucket-set-metadata", "Set metadata of object", func(s *mcclient.ClientSession, args *BucketSetMetadataOptions) error {
input := api.BucketMetadataInput{}
input.Key = args.Key
input.Metadata = args.ObjectHeaderOptions.Options2Header()
err := input.Validate()
if err != nil {
return err
}
result, err := modules.Buckets.PerformAction(s, args.ID, "metadata", jsonutils.Marshal(input))
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketSetWebsiteOption struct {
ID string `help:"ID or name of bucket" json:"-"`
// 主页
Index string `help:"main page"`
// 错误时返回的文档
ErrorDocument string `help:"error return"`
// http或https
Protocol string `help:"force https" choices:"http|https"`
}
R(&BucketSetWebsiteOption{}, "bucket-set-website", "Set bucket website", func(s *mcclient.ClientSession, args *BucketSetWebsiteOption) error {
conf := api.BucketWebsiteConf{
Index: args.Index,
ErrorDocument: args.ErrorDocument,
Protocol: args.Protocol,
}
result, err := modules.Buckets.PerformAction(s, args.ID, "set-website", jsonutils.Marshal(conf))
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketGetWebsiteConfOption struct {
ID string `help:"ID or name of bucket" json:"-"`
}
R(&BucketGetWebsiteConfOption{}, "bucket-get-website", "Get bucket website", func(s *mcclient.ClientSession, args *BucketGetWebsiteConfOption) error {
result, err := modules.Buckets.GetSpecific(s, args.ID, "website", nil)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketDeleteWebsiteConfOption struct {
ID string `help:"ID or name of bucket" json:"-"`
}
R(&BucketDeleteWebsiteConfOption{}, "bucket-delete-website", "Delete bucket website", func(s *mcclient.ClientSession, args *BucketDeleteWebsiteConfOption) error {
result, err := modules.Buckets.PerformAction(s, args.ID, "delete-website", nil)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketSetCorsOption struct {
ID string `help:"ID or name of bucket" json:"-"`
AllowedMethods []string `help:"allowed http method" choices:"PUT|GET|POST|DELETE|HEAD"`
// 允许的源站,可以设为*
AllowedOrigins []string
AllowedHeaders []string
MaxAgeSeconds int
ExposeHeaders []string
RuleId string
}
R(&BucketSetCorsOption{}, "bucket-set-cors", "Set bucket cors", func(s *mcclient.ClientSession, args *BucketSetCorsOption) error {
rule := api.BucketCORSRule{
AllowedOrigins: args.AllowedOrigins,
AllowedMethods: args.AllowedMethods,
AllowedHeaders: args.AllowedHeaders,
MaxAgeSeconds: args.MaxAgeSeconds,
ExposeHeaders: args.ExposeHeaders,
Id: args.RuleId,
}
rules := api.BucketCORSRules{Data: []api.BucketCORSRule{rule}}
result, err := modules.Buckets.PerformAction(s, args.ID, "set-cors", jsonutils.Marshal(rules))
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketGetCorsOption struct {
ID string `help:"ID or name of bucket" json:"-"`
}
R(&BucketGetCorsOption{}, "bucket-get-cors", "Get bucket cors", func(s *mcclient.ClientSession, args *BucketGetCorsOption) error {
result, err := modules.Buckets.GetSpecific(s, args.ID, "cors", nil)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketDeleteCorsOption struct {
ID string `help:"ID or name of bucket" json:"-"`
Id []string `"help:Id of rules to delete"`
}
R(&BucketDeleteCorsOption{}, "bucket-delete-cors", "Delete bucket cors", func(s *mcclient.ClientSession, args *BucketDeleteCorsOption) error {
input := api.BucketCORSRuleDeleteInput{}
input.Id = args.Id
result, err := modules.Buckets.PerformAction(s, args.ID, "delete-cors", jsonutils.Marshal(input))
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketSetRefererOption struct {
ID string `help:"ID or name of bucket" json:"-"`
// 域名列表
DomainList []string
// 是否允许空referer 访问
AllowEmptyRefer bool `help:"all empty refer access"`
Enabled bool
RerererType string `help:"Referer type" choices:"Black-List|White-List"`
}
R(&BucketSetRefererOption{}, "bucket-set-referer", "Set bucket referer", func(s *mcclient.ClientSession, args *BucketSetRefererOption) error {
conf := api.BucketRefererConf{
Enabled: args.Enabled,
AllowEmptyRefer: args.AllowEmptyRefer,
RefererType: args.RerererType,
DomainList: args.DomainList,
}
result, err := modules.Buckets.PerformAction(s, args.ID, "set-referer", jsonutils.Marshal(conf))
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketGetRefererOption struct {
ID string `help:"ID or name of bucket" json:"-"`
}
R(&BucketGetRefererOption{}, "bucket-get-referer", "get bucket referer", func(s *mcclient.ClientSession, args *BucketGetRefererOption) error {
result, err := modules.Buckets.GetSpecific(s, args.ID, "referer", nil)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketGetCdnDomainOption struct {
ID string `help:"ID or name of bucket" json:"-"`
}
R(&BucketGetRefererOption{}, "bucket-get-cdn-domain", "get bucket cdn domain", func(s *mcclient.ClientSession, args *BucketGetRefererOption) error {
result, err := modules.Buckets.GetSpecific(s, args.ID, "cdn-domain", nil)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketGetPolicyOption struct {
ID string `help:"ID or name of bucket" json:"-"`
}
R(&BucketGetPolicyOption{}, "bucket-get-policy", "get bucket policy", func(s *mcclient.ClientSession, args *BucketGetPolicyOption) error {
result, err := modules.Buckets.GetSpecific(s, args.ID, "policy", nil)
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketSetPolicyOption struct {
ID string `help:"ID or name of bucket" json:"-"`
// 格式主账号id:子账号id
PrincipalId []string `help:"ext account id, accountId:subaccountId"`
// Read|ReadWrite|FullControl
CannedAction string `help:"authority action" choice:"Read|FullControl"`
// Allow|Deny
Effect string `help:"allow or deny" choice:"Allow|Deny"`
// 被授权的资源地址
ResourcePath []string
// ip 条件
IpEquals []string
IpNotEquals []string
}
R(&BucketSetPolicyOption{}, "bucket-set-policy", "set bucket policy", func(s *mcclient.ClientSession, args *BucketSetPolicyOption) error {
opts := api.BucketPolicyStatementInput{}
opts.CannedAction = args.CannedAction
opts.Effect = args.Effect
opts.IpEquals = args.IpEquals
opts.IpNotEquals = args.IpNotEquals
opts.ResourcePath = args.ResourcePath
opts.PrincipalId = args.PrincipalId
result, err := modules.Buckets.PerformAction(s, args.ID, "set-policy", jsonutils.Marshal(opts))
if err != nil {
return err
}
printObject(result)
return nil
})
type BucketDeletePolicyOption struct {
ID string `help:"ID or name of bucket" json:"-"`
Id []string
}
R(&BucketDeletePolicyOption{}, "bucket-delete-policy", "delete bucket policy", func(s *mcclient.ClientSession, args *BucketDeletePolicyOption) error {
input := api.BucketPolicyDeleteInput{}
input.Id = args.Id
result, err := modules.Buckets.PerformAction(s, args.ID, "delete-policy", jsonutils.Marshal(input))
if err != nil {
return err
}
printObject(result)
return nil
})
}

View File

@@ -46,6 +46,7 @@ func init() {
cmd.Perform("rebuild", &compute_options.DiskRebuildOptions{})
cmd.Perform("migrate", &compute_options.DiskMigrateOptions{})
cmd.Perform("reset-template", &compute_options.DiskResetTemplateOptions{})
cmd.Perform("change-billing-type", new(compute_options.DiskChangeBillingTypeOptions))
type DiskDetailOptions struct {
ID string `help:"ID or Name of disk"`

View 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 compute
import (
"yunion.io/x/onecloud/cmd/climc/shell"
computemodules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
"yunion.io/x/onecloud/pkg/mcclient/options/compute"
)
func init() {
cmd := shell.NewResourceCmd(&computemodules.HostFiles)
cmd.List(&compute.HostFileListOptions{})
cmd.Create(&compute.HostFileCreateOptions{})
cmd.Show(&compute.HostFileShowOptions{})
cmd.Update(&compute.HostFileUpdateOptions{})
cmd.Edit(&compute.HostFileEditOptions{})
cmd.Delete(&compute.HostFileDeleteOptions{})
}

View File

@@ -55,8 +55,8 @@ func init() {
cmd.Perform("class-metadata", &options.ResourceMetadataOptions{})
cmd.Perform("set-class-metadata", &options.ResourceMetadataOptions{})
cmd.PerformClass("validate-ipmi", &compute.HostValidateIPMI{})
cmd.Perform("set-commit-bound", &compute.HostSetCommitBoundOptions{})
cmd.BatchPerform("set-commit-bound", &compute.HostSetCommitBoundOptions{})
cmd.BatchPerform("enable", &options.BaseIdsOptions{})
cmd.BatchPerform("disable", &options.BaseIdsOptions{})
cmd.BatchPerform("syncstatus", &options.BaseIdsOptions{})
@@ -67,6 +67,7 @@ func init() {
cmd.BatchPerform("unreserve-cpus", &options.BaseIdsOptions{})
cmd.BatchPerform("auto-migrate-on-host-down", &compute.HostAutoMigrateOnHostDownOptions{})
cmd.BatchPerform("restart-host-agent", &options.BaseIdsOptions{})
cmd.BatchPerform("set-host-files", &compute.HostSetHostFilesOptions{})
cmd.Get("ipmi", &options.BaseIdOptions{})
cmd.Get("vnc", &options.BaseIdOptions{})
@@ -93,6 +94,9 @@ func init() {
fmt.Println("error", err)
}
}, &options.BaseIdOptions{})
cmd.GetWithCustomShow("host-files", func(data jsonutils.JSONObject) {
printObject(data)
}, &options.BaseIdOptions{})
R(&compute.HostShowOptions{}, "host-show", "Show details of a host", func(s *mcclient.ClientSession, args *compute.HostShowOptions) error {
params, err := args.Params()

View File

@@ -165,8 +165,9 @@ func init() {
})
type ServerAttachNetworkOptions struct {
SERVER string `help:"ID or Name of server"`
NETDESC []string `help:"Network description"`
SERVER string `help:"ID or Name of server"`
DisableSyncConfig bool `help:"Disable sync config"`
NETDESC []string `help:"Network description"`
}
R(&ServerAttachNetworkOptions{}, "server-attach-network", "Attach a server to a virtual network", func(s *mcclient.ClientSession, args *ServerAttachNetworkOptions) error {
input := compute.AttachNetworkInput{}
@@ -177,6 +178,7 @@ func init() {
}
input.Nets = append(input.Nets, conf)
}
input.DisableSyncConfig = &args.DisableSyncConfig
params := jsonutils.Marshal(input)
srv, err := modules.Servers.PerformAction(s, args.SERVER, "attachnetwork", params)
if err != nil {

View File

@@ -21,6 +21,7 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/util/printutils"
"yunion.io/x/pkg/util/shellutils"
"yunion.io/x/pkg/utils"
"yunion.io/x/onecloud/pkg/mcclient"
@@ -544,6 +545,81 @@ func (cmd ResourceCmd) Update(args IUpdateOpt) {
cmd.UpdateWithKeyword("update", args)
}
type EditType string
const (
EditTypeText EditType = "text"
EditTypeYaml EditType = "yaml"
EditTypeJson EditType = "json"
)
type IEditOpt interface {
IGetOpt
EditType() EditType
EditFields() []string
}
func (cmd ResourceCmd) Edit(args IEditOpt) {
man := cmd.manager
callback := func(s *mcclient.ClientSession, args IEditOpt) error {
params, err := args.Params()
if err != nil {
return err
}
obj, err := man.(modulebase.Manager).Get(s, args.GetId(), params)
if err != nil {
return err
}
format := args.EditType()
fields := args.EditFields()
var editText string
switch format {
case EditTypeText:
editText, _ = obj.GetString(fields...)
default:
editJson, _ := obj.Get(fields...)
if editJson == nil {
editJson = jsonutils.NewDict()
}
if format == EditTypeYaml {
editText = editJson.YAMLString()
} else if format == EditTypeJson {
editText = editJson.PrettyString()
}
}
editText, err = shellutils.Edit(editText)
if err != nil {
return err
}
updateParams := jsonutils.NewDict()
switch format {
case EditTypeText:
updateParams.Add(jsonutils.NewString(editText), fields...)
case EditTypeYaml:
yamlObj, err := jsonutils.ParseYAML(editText)
if err != nil {
return err
}
updateParams.Add(yamlObj, fields...)
case EditTypeJson:
jsonObj, err := jsonutils.ParseString(editText)
if err != nil {
return err
}
updateParams.Add(jsonObj, fields...)
}
updateResult, err := man.(modulebase.Manager).Update(s, args.GetId(), updateParams)
if err != nil {
return err
}
PrintObject(updateResult)
return nil
}
cmd.Run("edit", args, callback)
}
type IMetadataOpt interface {
IIdOpt
IOpt

View File

@@ -1,3 +1,17 @@
// 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 main
import (

10
go.mod
View File

@@ -93,14 +93,14 @@ require (
k8s.io/cri-api v0.22.17
k8s.io/klog/v2 v2.20.0
moul.io/http2curl/v2 v2.3.0
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250427071042-3ec467b38f4d
yunion.io/x/executor v0.0.0-20241205080005-48f5b1212256
yunion.io/x/jsonutils v1.0.1-0.20250406102002-98c9140a9edd
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250528153742-2296b9b7287c
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
yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900
yunion.io/x/pkg v1.10.4-0.20250403114914-586e94d39281
yunion.io/x/pkg v1.10.4-0.20250519013345-54017bf6c1f0
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1
yunion.io/x/sqlchemy v1.1.3-0.20250420094945-40f40c75a31f
yunion.io/x/sqlchemy v1.1.3-0.20250513031856-ce9f71063b3a
yunion.io/x/structarg v0.0.0-20231017124457-df4d5009457c
)

20
go.sum
View File

@@ -1376,13 +1376,13 @@ 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.20250427071042-3ec467b38f4d h1:RnK6jhPwXRkQUDRL96YV7qeKbGq5zSZ2SQ/T6Jur574=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250427071042-3ec467b38f4d/go.mod h1:FXxAEbdNfWXX9gjME3K2nJhkydHY5EKEUZb+RLEzVwQ=
yunion.io/x/executor v0.0.0-20241205080005-48f5b1212256 h1:kLKQ6zbgPDQflRwoHFAjxNChcbhXIFgsUVLkJwiXu/8=
yunion.io/x/executor v0.0.0-20241205080005-48f5b1212256/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250528153742-2296b9b7287c h1:DGnSo1KJS+0dZQTz76vArY+D1dEB8LoINmQP257Miso=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250528153742-2296b9b7287c/go.mod h1:FXxAEbdNfWXX9gjME3K2nJhkydHY5EKEUZb+RLEzVwQ=
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=
yunion.io/x/jsonutils v1.0.1-0.20250406102002-98c9140a9edd h1:FWwB4CXiyIrh2YvxPliC9ZmrzXcFv+nuG3lhpipR4Sk=
yunion.io/x/jsonutils v1.0.1-0.20250406102002-98c9140a9edd/go.mod h1:VK4Z93dgiKgAijcSqbMKmGaBMJuHulR16Hz4K015ZPo=
yunion.io/x/jsonutils v1.0.1-0.20250507052344-1abcf4f443b1 h1:/+THlvf/MvgCW+7KeCDCr33e81KSRa5JmdZ1IIyLOXQ=
yunion.io/x/jsonutils v1.0.1-0.20250507052344-1abcf4f443b1/go.mod h1:VK4Z93dgiKgAijcSqbMKmGaBMJuHulR16Hz4K015ZPo=
yunion.io/x/log v0.0.0-20190514041436-04ce53b17c6b/go.mod h1:+gauLs73omeJAPlsXcevLsJLKixV+sR/E7WSYTSx1fE=
yunion.io/x/log v0.0.0-20190629062853-9f6483a7103d/go.mod h1:LC6f/4FozL0iaAbnFt2eDX9jlsyo3WiOUPm03d7+U4U=
yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91 h1:inY5o3LDa/zgsIZuPN0HmpzKIsu/lLgsBmMttuDPGj4=
@@ -1391,11 +1391,11 @@ yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900 h1:Hu/4ERvoWaN6aiFs4h4/yvVB
yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900/go.mod h1:0vLkNEhlmA64HViPBAnSTUMrx5QP1CLsxXmxDKQ80tc=
yunion.io/x/pkg v0.0.0-20190620104149-945c25821dbf/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/pkg v0.0.0-20190628082551-f4033ba2ea30/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/pkg v1.10.4-0.20250403114914-586e94d39281 h1:P22/MaBc9bqE9DyHvS3oQjc4XsaIEbIVGMLdTSroDew=
yunion.io/x/pkg v1.10.4-0.20250403114914-586e94d39281/go.mod h1:0Bwxqd9MA3ACi119/l02FprY/o9gHahmYC2bsSbnVpM=
yunion.io/x/pkg v1.10.4-0.20250519013345-54017bf6c1f0 h1:iKWkBMKazSijYNhOaSh4qBuIu+PmXYhEAMNwrxaXL4Q=
yunion.io/x/pkg v1.10.4-0.20250519013345-54017bf6c1f0/go.mod h1:0Bwxqd9MA3ACi119/l02FprY/o9gHahmYC2bsSbnVpM=
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1 h1:1KJ3YYinydPHpDEQRXdr/T8SYcKZ5Er+m489H+PnaQ4=
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1/go.mod h1:0iFKpOs1y4lbCxeOmq3Xx/0AcQoewVPwj62eRluioEo=
yunion.io/x/sqlchemy v1.1.3-0.20250420094945-40f40c75a31f h1:xuy9a8Hg0d8dfD8j13f687eKPCxEvttgpLQWb4NvKpI=
yunion.io/x/sqlchemy v1.1.3-0.20250420094945-40f40c75a31f/go.mod h1:vCIZpqhZ5Jzaq3tFyrti/vv8BijQKtkzSgNT/uH4H5A=
yunion.io/x/sqlchemy v1.1.3-0.20250513031856-ce9f71063b3a h1:mDW1VyYJxZ4ORITZGDWacUiBNyqX6rMObnQk77NvUOg=
yunion.io/x/sqlchemy v1.1.3-0.20250513031856-ce9f71063b3a/go.mod h1:vCIZpqhZ5Jzaq3tFyrti/vv8BijQKtkzSgNT/uH4H5A=
yunion.io/x/structarg v0.0.0-20231017124457-df4d5009457c h1:QuLab2kSRECZRxo4Lo2KcYn6XjQFDGaZ1+x0pYDVVwQ=
yunion.io/x/structarg v0.0.0-20231017124457-df4d5009457c/go.mod h1:EP6NSv2C0zzqBDTKumv8hPWLb3XvgMZDHQRfyuOrQng=

View File

@@ -15,28 +15,5 @@
package handler
import (
_ "yunion.io/x/onecloud/pkg/mcclient/modules/ansible"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/cloudevent"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/cloudid"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/cloudnet"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/cloudproxy"
modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/devtool"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/etcd"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/identity"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/image"
"yunion.io/x/onecloud/pkg/mcclient/modules/k8s"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/logger"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/monitor"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/notify"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/quota"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/scheduledtask"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/scheduler"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/webconsole"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/yunionconf"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/loader"
)
func init() {
modules.InitUsages()
modules.Usages.RegisterManager(modules.UsageManagerK8s, k8s.Usages)
}

View File

@@ -17,6 +17,7 @@ package compute
import (
"net/http"
"reflect"
"time"
"yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
@@ -53,6 +54,8 @@ type BucketCreateInput struct {
CloudproviderResourceInput
StorageClass string `json:"storage_class"`
EnablePerfMon *bool `json:"enable_perf_mon"`
}
type BucketDetails struct {
@@ -66,28 +69,28 @@ type BucketDetails struct {
AccessUrls []cloudprovider.SBucketAccessUrl `json:"access_urls"`
}
func (self BucketDetails) GetMetricTags() map[string]string {
func (bucket BucketDetails) GetMetricTags() map[string]string {
ret := map[string]string{
"id": self.Id,
"brand": self.Brand,
"cloudregion": self.Cloudregion,
"cloudregion_id": self.CloudregionId,
"domain_id": self.DomainId,
"oss_id": self.Id,
"oss_name": self.Name,
"project_domain": self.ProjectDomain,
"region_ext_id": self.RegionExtId,
"status": self.Status,
"tenant": self.Project,
"tenant_id": self.ProjectId,
"account": self.Account,
"account_id": self.AccountId,
"external_id": self.ExternalId,
"id": bucket.Id,
"brand": bucket.Brand,
"cloudregion": bucket.Cloudregion,
"cloudregion_id": bucket.CloudregionId,
"domain_id": bucket.DomainId,
"oss_id": bucket.Id,
"oss_name": bucket.Name,
"project_domain": bucket.ProjectDomain,
"region_ext_id": bucket.RegionExtId,
"status": bucket.Status,
"tenant": bucket.Project,
"tenant_id": bucket.ProjectId,
"account": bucket.Account,
"account_id": bucket.AccountId,
"external_id": bucket.ExternalId,
}
return AppendMetricTags(ret, self.MetadataResourceInfo, self.ProjectizedResourceInfo)
return AppendMetricTags(ret, bucket.MetadataResourceInfo, bucket.ProjectizedResourceInfo)
}
func (self BucketDetails) GetMetricPairs() map[string]string {
func (bucket BucketDetails) GetMetricPairs() map[string]string {
ret := map[string]string{}
return ret
}
@@ -150,6 +153,8 @@ type BucketSyncstatusInput struct {
type BucketUpdateInput struct {
apis.SharableVirtualResourceBaseUpdateInput
EnablePerfMon *bool `json:"enable_perf_mon"`
}
type BucketPerformTempUrlInput struct {
@@ -370,3 +375,29 @@ func init() {
return &SBackupStorageAccessInfo{}
})
}
type BucketProbeResult struct {
UploadTime time.Duration
DownloadTime time.Duration
DeleteTime time.Duration
}
func (result BucketProbeResult) UploadDelayMs() float64 {
return float64(result.UploadTime) / float64(time.Millisecond)
}
func (result BucketProbeResult) DownloadDelayMs() float64 {
return float64(result.DownloadTime) / float64(time.Millisecond)
}
func (result BucketProbeResult) DeleteDelayMs() float64 {
return float64(result.DeleteTime) / float64(time.Millisecond)
}
func (result BucketProbeResult) UploadThroughputMbps(sizeMBytes int) float64 {
return float64(sizeMBytes) * 8 / float64(result.UploadTime.Seconds())
}
func (result BucketProbeResult) DownloadThroughputMbps(sizeMBytes int) float64 {
return float64(sizeMBytes) * 8 / float64(result.DownloadTime.Seconds())
}

View File

@@ -75,6 +75,7 @@ const (
CLOUD_PROVIDER_ZETTAKIT = compute.CLOUD_PROVIDER_ZETTAKIT
CLOUD_PROVIDER_UIS = compute.CLOUD_PROVIDER_UIS
CLOUD_PROVIDER_CAS = compute.CLOUD_PROVIDER_CAS
CLOUD_PROVIDER_CLOUDFLARE = compute.CLOUD_PROVIDER_CLOUDFLARE
CLOUD_PROVIDER_GENERICS3 = compute.CLOUD_PROVIDER_GENERICS3
CLOUD_PROVIDER_CEPH = compute.CLOUD_PROVIDER_CEPH
@@ -143,6 +144,7 @@ var (
CLOUD_PROVIDER_JDCLOUD,
CLOUD_PROVIDER_VOLCENGINE,
CLOUD_PROVIDER_ORACLE,
CLOUD_PROVIDER_CLOUDFLARE,
}
CLOUD_PROVIDERS = []string{
@@ -181,6 +183,7 @@ var (
CLOUD_PROVIDER_ZETTAKIT,
CLOUD_PROVIDER_UIS,
CLOUD_PROVIDER_CAS,
CLOUD_PROVIDER_CLOUDFLARE,
}
CLOUD_PROVIDER_HOST_TYPE_MAP = map[string][]string{

View File

@@ -207,6 +207,8 @@ type ContainerSaveVolumeMountToImageInput struct {
Index int `json:"index"`
Dirs []string `json:"dirs"`
UsedByPostOverlay bool `json:"used_by_post_overlay"`
DirPrefix string `json:"dir_prefix"`
}
type ContainerExecInfoOutput struct {

View File

@@ -376,3 +376,11 @@ func (d *DiskFsFeatures) IsZero() bool {
}
return false
}
type DiskChangeBillingTypeInput struct {
// 仅在磁盘挂载在虚拟机上时调用
// 目前支持阿里云
// enmu: [postpaid, prepaid]
// required: true
BillingType string `json:"billing_type"`
}

View File

@@ -36,6 +36,7 @@ type DnsRecordCreateInput struct {
DnsValue string `json:"dns_value"`
TTL int64 `json:"ttl"`
MxPriority int64 `json:"mx_priority"`
Proxied *bool `json:"proxied"`
PolicyType string `json:"policy_type"`
PolicyValue string `json:"policy_value"`
@@ -48,6 +49,7 @@ type DnsRecordUpdateInput struct {
DnsValue string `json:"dns_value"`
TTL *int64 `json:"ttl"`
MxPriority *int64 `json:"mx_priority"`
Proxied *bool `json:"proxied"`
}
type DnsRecordDetails struct {

View File

@@ -180,10 +180,6 @@ const (
VM_QGA_SET_NETWORK = "qga_set_network"
VM_QGA_SET_NETWORK_FAILED = "qga_set_network_failed"
// 更改计费模式
VM_CHANGE_BILLING_TYPE = "change_billing_type"
VM_CHANGE_BILLING_TYPE_FAILED = "change_billing_type_failed"
SHUTDOWN_STOP = "stop"
SHUTDOWN_TERMINATE = "terminate"
SHUTDOWN_STOP_RELEASE_GPU = "stop_release_gpu"
@@ -265,6 +261,9 @@ const (
VM_VIDEO_STANDARD = "std"
VM_VIDEO_QXL = "qxl"
VM_VIDEO_VIRTIO = "virtio"
VM_BOOT_MODE_BIOS = "BIOS"
VM_BOOT_MODE_UEFI = "UEFI"
)
var VM_RUNNING_STATUS = []string{VM_START_START, VM_STARTING, VM_RUNNING, VM_BLOCK_STREAM, VM_BLOCK_STREAM_FAIL}

View File

@@ -324,6 +324,8 @@ func (self ServerDetails) GetMetricTags() map[string]string {
"paltform": self.Hypervisor,
"host": self.Host,
"host_id": self.HostId,
"ips": self.IPs,
"vm_ip": self.IPs,
"vm_id": self.Id,
"vm_name": self.Name,
"zone": self.Zone,
@@ -377,6 +379,7 @@ type GuestDiskInfo struct {
MediumType string `json:"medium_type"`
StorageType string `json:"storage_type"`
Iops int `json:"iops"`
Throughput int `json:"throughput"`
Bps int `json:"bps"`
ImageId string `json:"image_id,omitempty"`
Image string `json:"image,omitemtpy"`
@@ -1201,7 +1204,7 @@ type ServerQgaGetNetworkInput struct {
}
type ServerQgaTimeoutInput struct {
// qga execute timeout millisecond
// qga execute timeout second
Timeout int
}

View File

@@ -250,6 +250,8 @@ type HostDetails struct {
SysWarn string `json:"sys_warn"`
// host init error info
SysError string `json:"sys_error"`
HostFiles []string `json:"host_files"`
}
func (self HostDetails) GetMetricTags() map[string]string {

View File

@@ -0,0 +1,62 @@
// 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 compute
import (
"yunion.io/x/onecloud/pkg/apis"
)
type HostFileType string
const (
PlainFile HostFileType = "plain"
ScriptFile HostFileType = "script"
TelegrafConf HostFileType = "telegraf"
ApparmorProfile HostFileType = "apparmor"
)
type HostFileCreateInput struct {
apis.InfrasResourceBaseCreateInput
Path string `json:"path" help:"Path of the host file"`
Content string `json:"content" help:"Content of the host file"`
Type HostFileType `json:"type" help:"Type of the host file"`
}
type HostFileUpdateInput struct {
apis.InfrasResourceBaseUpdateInput
Path string `json:"path"`
Content string `json:"content"`
}
type HostFileListInput struct {
apis.InfrasResourceBaseListInput
Type []string `json:"type"`
Path string `json:"path"`
}
type HostSetHostFilesInput struct {
HostFiles []string `json:"host_files"`
}
type HostFileDetails struct {
apis.InfrasResourceBaseDetails
Hosts []string `json:"hosts"`
SHostFile
}

View File

@@ -255,6 +255,8 @@ type SecgroupResourceInput struct {
type SecgroupFilterListInput struct {
SecgroupResourceInput
RegionalFilterListInput
ManagedResourceListInput
// 以安全组排序
OrderBySecgroup string `json:"order_by_secgroup"`
@@ -291,6 +293,16 @@ type SecgroupDetails struct {
type SecurityGroupResourceInfo struct {
// 安全组名称
Secgroup string `json:"secgroup"`
// VPC归属区域ID
CloudregionId string `json:"cloudregion_id"`
CloudregionResourceInfo
// VPC归属云订阅ID
ManagerId string `json:"manager_id"`
ManagedResourceInfo
}
type GuestsecgroupListInput struct {

View File

@@ -74,6 +74,8 @@ const (
STORAGE_CLOUD_BASIC = compute.STORAGE_CLOUD_BASIC
STORAGE_CLOUD_PREMIUM = compute.STORAGE_CLOUD_PREMIUM //高性能云硬盘
STORAGE_CLOUD_HSSD = compute.STORAGE_CLOUD_HSSD //增强型SSD云硬盘
STORAGE_CLOUD_BSSD = compute.STORAGE_CLOUD_BSSD //增强型SSD云硬盘
STORAGE_CLOUD_TSSD = compute.STORAGE_CLOUD_TSSD //极速型SSD云硬盘
// huawei storage type
STORAGE_HUAWEI_SSD = compute.STORAGE_HUAWEI_SSD // 超高IO云硬盘

View File

@@ -247,7 +247,6 @@ type SCloudaccount struct {
// 云系统信息
Sysinfo jsonutils.JSONObject `json:"sysinfo"`
// 品牌信息, 一般和provider相同
// example: DStack
Brand string `json:"brand"`
// 额外信息
Options *jsonutils.JSONDict `json:"options"`
@@ -568,6 +567,8 @@ type SDisk struct {
Preallocation string `json:"preallocation"`
// # is persistent
Nonpersistent bool `json:"nonpersistent"`
// auto reset disk after guest shutdown
AutoReset bool `json:"auto_reset"`
// 是否标记为SSD磁盘
IsSsd bool `json:"is_ssd"`
// 最大连接数
@@ -1014,6 +1015,8 @@ type SGuest struct {
VmemSize int `json:"vmem_size"`
// CPU 内存绑定信息
CpuNumaPin jsonutils.JSONObject `json:"cpu_numa_pin"`
// 额外分配的 CPU 数量
ExtraCpuCount int `json:"extra_cpu_count"`
// 启动顺序
BootOrder string `json:"boot_order"`
// 关机操作类型
@@ -1266,6 +1269,21 @@ type SHostBackupstorage struct {
BackupstorageId string `json:"backupstorage_id"`
}
// SHostFile is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SHostFile.
type SHostFile struct {
apis.SInfrasResourceBase
Type string `json:"type"`
Path string `json:"path"`
Content string `json:"content"`
}
// SHostFileJoint is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SHostFileJoint.
type SHostFileJoint struct {
HostId string `json:"host_id"`
HostFileId string `json:"host_file_id"`
Deleted bool `json:"deleted"`
}
// SHostJointsBase is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SHostJointsBase.
type SHostJointsBase struct {
apis.SJointResourceBase
@@ -1449,6 +1467,8 @@ type SInterVpcNetworkVpc struct {
// SIsolatedDevice is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SIsolatedDevice.
type SIsolatedDevice struct {
apis.SStandaloneResourceBase
apis.SExternalizedResourceBase
apis.SSharableBaseResource
SHostResourceBase
// # PCI / GPU-HPC / GPU-VGA / USB / NIC
// 设备类型

View File

@@ -58,6 +58,10 @@ const (
STATUS_AVAILABLE = "available"
STATUS_CREATE_FAILED = "create_failed"
// 更改计费模式
STATUS_CHANGE_BILLING_TYPE = "change_billing_type"
STATUS_CHANGE_BILLING_TYPE_FAILED = "change_billing_type_failed"
CLOUD_TAG_PREFIX = "ext:"
USER_TAG_PREFIX = "user:"
SYS_CLOUD_TAG_PREFIX = "sys:"

View File

@@ -275,6 +275,8 @@ type ContainerVolumeMountDiskPostOverlay struct {
// 合并后要挂载到容器的目录
ContainerTargetDir string `json:"container_target_dir"`
Image *ContainerVolumeMountDiskPostImageOverlay `json:"image"`
FsUser *int64 `json:"fs_user,omitempty"`
FsGroup *int64 `json:"fs_group,omitempty"`
}
func (o ContainerVolumeMountDiskPostOverlay) IsEqual(input ContainerVolumeMountDiskPostOverlay) bool {

View File

@@ -130,6 +130,8 @@ type ContainerSaveVolumeMountToImageInput struct {
VolumeMountIndex int `json:"volume_mount_index"`
VolumeMount *ContainerVolumeMount `json:"volume_mount"`
VolumeMountDirs []string `json:"volume_mount_dirs"`
VolumeMountPrefix string `json:"volume_mount_prefix"`
}
type ContainerCommitInput struct {

View File

@@ -64,14 +64,15 @@ var (
MetricUnit = []string{METRIC_UNIT_PERCENT, METRIC_UNIT_BPS, METRIC_UNIT_MBPS, METRIC_UNIT_BYTEPS, "count/s",
METRIC_UNIT_COUNT, METRIC_UNIT_MS, METRIC_UNIT_BYTE, METRIC_UNIT_NULL}
ResTypeScoreMap = map[string]float64{
METRIC_RES_TYPE_GUEST: 1,
METRIC_RES_TYPE_AGENT: 1.1,
METRIC_RES_TYPE_HOST: 2,
METRIC_RES_TYPE_OSS: 3,
METRIC_RES_TYPE_RDS: 4,
METRIC_RES_TYPE_REDIS: 5,
METRIC_RES_TYPE_CLOUDACCOUNT: 6,
METRIC_RES_TYPE_STORAGE: 7,
METRIC_RES_TYPE_HOST: -100,
METRIC_RES_TYPE_GUEST: -99,
METRIC_RES_TYPE_AGENT: -98,
METRIC_RES_TYPE_CONTAINER: -97,
METRIC_RES_TYPE_SYSTEM: -96,
METRIC_RES_TYPE_K8S: -95,
METRIC_RES_TYPE_ELB: -94,
METRIC_RES_TYPE_CLOUDACCOUNT: -93,
METRIC_RES_TYPE_STORAGE: -93,
}
)

View File

@@ -80,7 +80,7 @@ type SAlertPanel struct {
type SAlertRecord struct {
// db.SVirtualResourceBase
apis.SEnabledResourceBase
apis.SStatusStandaloneResourceBase
apis.SStandaloneAnonResourceBase
SMonitorScopedResource
AlertId string `json:"alert_id"`
Level string `json:"level"`
@@ -89,6 +89,7 @@ type SAlertRecord struct {
EvalData jsonutils.JSONObject `json:"eval_data"`
AlertRule jsonutils.JSONObject `json:"alert_rule"`
ResType string `json:"res_type"`
ResIds string `json:"res_ids"`
}
// SAlertRecordShield is an autogenerated struct via yunion.io/x/onecloud/pkg/monitor/models.SAlertRecordShield.

View File

@@ -102,6 +102,8 @@ type NotificationManagerEventNotifyInput struct {
// description: direct contact, admin privileges required
// required: false
Contacts []SContact `json:"contacts"`
// 消息机器人列表
RobotIds []string `json:"robot_ids"`
// description: contact types
// required: false
// example: email

View File

@@ -123,16 +123,6 @@ type SNotificationLog struct {
SendTimes int `json:"send_times"`
}
// SNotifyAction is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SNotifyAction.
type SNotifyAction struct {
apis.SEnabledStatusStandaloneResourceBase
}
// SNotifyResource is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SNotifyResource.
type SNotifyResource struct {
apis.SEnabledStatusStandaloneResourceBase
}
// SReceiver is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SReceiver.
type SReceiver struct {
apis.SVirtualResourceBase
@@ -216,14 +206,14 @@ type STopic struct {
// STopicAction is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.STopicAction.
type STopicAction struct {
apis.SJointResourceBase
apis.SResourceBase
ActionId string `json:"action_id"`
TopicId string `json:"topic_id"`
}
// STopicResource is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.STopicResource.
type STopicResource struct {
apis.SJointResourceBase
apis.SResourceBase
ResourceId string `json:"resource_id"`
TopicId string `json:"topic_id"`
}

View File

@@ -19,6 +19,7 @@ import (
"fmt"
"net/http"
"yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/cloudcommon/workmanager"
"yunion.io/x/onecloud/pkg/hostman/hostutils"
@@ -48,12 +49,13 @@ func performImageCache(
) {
_, _, body := appsrv.FetchEnv(ctx, w, r)
disk, err := body.Get("disk")
input := compute.CacheImageInput{}
err := body.Unmarshal(&input, "disk")
if err != nil {
httperrors.MissingParameterError(ctx, w, "disk")
httperrors.BadRequestError(ctx, w, "unmarshal disk %s", err)
return
}
hostutils.DelayImageCacheTask(ctx, performTask, disk)
hostutils.DelayImageCacheTask(ctx, performTask, input)
hostutils.ResponseOk(ctx, w)
}

View File

@@ -26,6 +26,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/mcclient/auth"
modules "yunion.io/x/onecloud/pkg/mcclient/modules/identity"
"yunion.io/x/onecloud/pkg/util/ctx"
)
type tenantCacheSyncWorker struct {
@@ -34,7 +35,7 @@ type tenantCacheSyncWorker struct {
func (w *tenantCacheSyncWorker) Run() {
log.Debugf("[tenantCacheSyncWorker] Run project cache sync worker ...")
err := syncProjects(context.Background(), w.ids)
err := syncProjects(ctx.CtxWithTime(), w.ids)
if err != nil {
log.Errorf("fail to syncProjects %s", err)
}

View File

@@ -26,6 +26,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/mcclient/auth"
modules "yunion.io/x/onecloud/pkg/mcclient/modules/identity"
"yunion.io/x/onecloud/pkg/util/ctx"
)
type userCacheSyncWorker struct {
@@ -34,7 +35,7 @@ type userCacheSyncWorker struct {
func (w *userCacheSyncWorker) Run() {
log.Debugf("[userCacheSyncWorker] Run project cache sync worker ...")
err := syncUsers(context.Background(), w.ids)
err := syncUsers(ctx.CtxWithTime(), w.ids)
if err != nil {
log.Errorf("fail to syncUsers %s", err)
}

View File

@@ -66,13 +66,12 @@ func ObjectIdQueryWithTagFiltersOptimized(ctx context.Context, q *sqlchemy.SQuer
if len(filters.Filters) > 0 || len(filters.NoFilters) > 0 {
idSubQ := q.Copy().SubQuery().Query()
idSubQ.AppendField(sqlchemy.DISTINCT(idField, idSubQ.Field(idField)))
subQ := idSubQ.SubQuery()
if len(filters.Filters) > 0 {
if GetMetadaManagerInContext(ctx) == Metadata {
sq := tenantIdQueryWithTags(ctx, modelName, filters.Filters)
q = q.In(idField, sq.SubQuery())
} else { // clickhouse
ids := objIdQueryWithTagsOptimized(ctx, subQ, idField, modelName, filters.Filters)
ids := tenantIdQueryWithTagsWithCache(ctx, modelName, filters.Filters)
if len(ids) > 0 {
q = q.In(idField, ids)
}
@@ -83,7 +82,7 @@ func ObjectIdQueryWithTagFiltersOptimized(ctx context.Context, q *sqlchemy.SQuer
sq := tenantIdQueryWithTags(ctx, modelName, filters.Filters)
q = q.NotIn(idField, sq.SubQuery())
} else { // clickhouse
ids := objIdQueryWithTagsOptimized(ctx, subQ, idField, modelName, filters.NoFilters)
ids := tenantIdQueryWithTagsWithCache(ctx, modelName, filters.NoFilters)
if len(ids) > 0 {
q = q.NotIn(idField, ids)
}
@@ -153,6 +152,50 @@ func tenantIdQueryWithTags(ctx context.Context, modelName string, tagsList []map
return sq.Filter(sqlchemy.OR(conditions...)).Distinct()
}
var (
tagsCache = hashcache.NewCache(1024, time.Minute*15)
)
func tenantIdQueryWithTagsWithCache(ctx context.Context, modelName string, tagsList []map[string][]string) []string {
manager := Metadata
ret := []string{}
sq := manager.Query("obj_id")
for _, tags := range tagsList {
if len(tags) == 0 {
continue
}
hashKeys := []string{modelName, jsonutils.Marshal(tags).String()}
hash := fmt.Sprintf("%x", md5.Sum([]byte(jsonutils.Marshal(hashKeys).String())))
cache := tagsCache.Get(hash)
if cache != nil {
ids := cache.([]string)
ret = append(ret, ids...)
log.Debugf("cache hit %s %s %s", hash, hashKeys, ids)
continue
}
conditions := []sqlchemy.ICondition{}
for key, val := range tags {
if len(val) > 0 {
sqq := sq.Copy().Equals("obj_type", modelName).Equals("key", key).In("value", val)
conditions = append(conditions, sqlchemy.In(sq.Field("obj_id"), sqq.SubQuery()))
} else {
sqq := sq.Copy().Equals("obj_type", modelName).Equals("key", key)
conditions = append(conditions, sqlchemy.In(sq.Field("obj_id"), sqq.SubQuery()))
}
}
ids, err := FetchIds(sq.Copy().Filter(sqlchemy.AND(conditions...)).Distinct())
if err != nil {
log.Errorf("FetchIds %s %v", sq.String(), err)
continue
}
ret = append(ret, ids...)
log.Debugf("cache miss %s %s %s", hash, hashKeys, ids)
tagsCache.AtomicSet(hash, ids)
}
return ret
}
func objIdQueryWithTags(ctx context.Context, objIdSubQ *sqlchemy.SSubQuery, idField string, modelName string, tagsList []map[string][]string) *sqlchemy.SQuery {
manager := GetMetadaManagerInContext(ctx)
@@ -192,53 +235,6 @@ func objIdQueryWithTags(ctx context.Context, objIdSubQ *sqlchemy.SSubQuery, idFi
return query
}
var (
tagsCache = hashcache.NewCache(1024, time.Minute*15)
)
func objIdQueryWithTagsOptimized(ctx context.Context, objIdSubQ *sqlchemy.SSubQuery, idField string, modelName string, tagsList []map[string][]string) []string {
manager := GetMetadaManagerInContext(ctx)
ret := []string{}
for _, tags := range tagsList {
if len(tags) == 0 {
continue
}
objIdQ := objIdSubQ.Query()
objIdQ = objIdQ.AppendField(objIdQ.Field(idField))
for key, val := range tags {
hashKeys := []string{idField, key, modelName}
hashKeys = append(hashKeys, val...)
hash := fmt.Sprintf("%x", md5.Sum([]byte(jsonutils.Marshal(hashKeys).String())))
cache := tagsCache.Get(hash)
if cache != nil {
ids := cache.([]string)
ret = append(ret, ids...)
continue
}
sq := manager.Query("obj_id").Equals("obj_type", modelName).Equals("key", key)
if len(val) > 0 {
ssq := sq.In("value", val).SubQuery()
if utils.IsInArray(tagutils.NoValue, val) {
objIdQ = objIdQ.LeftJoin(ssq, sqlchemy.Equals(objIdQ.Field(idField), ssq.Field("obj_id")))
} else {
objIdQ = objIdQ.Join(ssq, sqlchemy.Equals(objIdQ.Field(idField), ssq.Field("obj_id")))
}
} else {
ssq := sq.SubQuery()
objIdQ = objIdQ.Join(ssq, sqlchemy.Equals(objIdQ.Field(idField), ssq.Field("obj_id")))
}
ids, err := FetchIds(objIdQ)
if err != nil {
continue
}
ret = append(ret, ids...)
tagsCache.AtomicSet(hash, ids)
}
}
return ret
}
func (meta *SMetadataResourceBaseModelManager) ListItemFilter(
ctx context.Context,
manager IModelManager,

View File

@@ -65,28 +65,29 @@ func (n SNic) GetMac() net.HardwareAddr {
}
type SServerNic struct {
Name string `json:"name"`
Index int `json:"index"`
Bridge string `json:"bridge"`
Domain string `json:"domain"`
Ip string `json:"ip"`
Vlan int `json:"vlan"`
Driver string `json:"driver"`
Masklen int `json:"masklen"`
Virtual bool `json:"virtual"`
Manual bool `json:"manual"`
WireId string `json:"wire_id"`
NetId string `json:"net_id"`
Mac string `json:"mac"`
BandWidth int `json:"bw"`
Mtu int16 `json:"mtu,omitempty"`
Dns string `json:"dns"`
Ntp string `json:"ntp"`
Net string `json:"net"`
Interface string `json:"interface"`
Gateway string `json:"gateway"`
Ifname string `json:"ifname"`
Routes []SRoute `json:"routes,omitempty"`
Name string `json:"name"`
Index int `json:"index"`
Bridge string `json:"bridge"`
Domain string `json:"domain"`
Ip string `json:"ip"`
Vlan int `json:"vlan"`
VlanInterface bool `json:"vlan_interface"`
Driver string `json:"driver"`
Masklen int `json:"masklen"`
Virtual bool `json:"virtual"`
Manual bool `json:"manual"`
WireId string `json:"wire_id"`
NetId string `json:"net_id"`
Mac string `json:"mac"`
BandWidth int `json:"bw"`
Mtu int16 `json:"mtu,omitempty"`
Dns string `json:"dns"`
Ntp string `json:"ntp"`
Net string `json:"net"`
Interface string `json:"interface"`
Gateway string `json:"gateway"`
Ifname string `json:"ifname"`
Routes []SRoute `json:"routes,omitempty"`
Ip6 string `json:"ip6"`
Masklen6 int `json:"masklen6"`

View File

@@ -0,0 +1,177 @@
// 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 misc
import (
"context"
"fmt"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudmon/options"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
computemodules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
baseoptions "yunion.io/x/onecloud/pkg/mcclient/options"
"yunion.io/x/onecloud/pkg/util/influxdb"
)
func BucketProbe(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
if options.Options.EnableBucketProbeDebug {
log.Debugf("BucketProbe start")
}
if !options.Options.EnableBucketProbe {
if options.Options.EnableBucketProbeDebug {
log.Debugf("BucketProbe is disabled")
}
return
}
sess := auth.GetSession(ctx, userCred, options.Options.Region)
metrics, err := gatherBucketMetrics(ctx, sess)
if err != nil {
log.Errorf("BucketProbe gatherBucketMetrics failed: %s", err)
return
}
err = sendMetrics(sess, metrics, "telegraf")
if err != nil {
log.Errorf("StatusProbe SendMetrics error: %s", err)
}
}
func gatherBucketMetrics(ctx context.Context, sess *mcclient.ClientSession) ([]influxdb.SMetricData, error) {
allMetrics := []influxdb.SMetricData{}
params := baseoptions.BaseListOptions{}
params.Scope = "max"
limit := 1000
params.Limit = &limit
params.Filter = []string{
"enable_perf_mon.equals(1)",
}
boolTrue := true
params.Details = &boolTrue
total := -1
offset := 0
for total < 0 || offset < total {
params.Offset = &offset
results, err := computemodules.Buckets.List(sess, jsonutils.Marshal(params))
if err != nil {
return nil, errors.Wrap(err, "computemodules.Buckets.List")
}
total = results.Total
offset = results.Offset + len(results.Data)
for _, bucket := range results.Data {
bucketDetails := computeapi.BucketDetails{}
err = bucket.Unmarshal(&bucketDetails)
if err != nil {
log.Errorf("BucketProbe failed: %s", err)
continue
}
metrics, err := probeBucketStats(ctx, sess, &bucketDetails)
if err != nil {
log.Errorf("BucketProbe failed: %s", err)
continue
}
allMetrics = append(allMetrics, metrics...)
}
}
return allMetrics, nil
}
func probeBucketStats(ctx context.Context, sess *mcclient.ClientSession, bucketDetails *computeapi.BucketDetails) ([]influxdb.SMetricData, error) {
bucket, err := computemodules.GetIBucket(ctx, sess, bucketDetails)
if err != nil {
return nil, errors.Wrap(err, "getIBucket")
}
resultDelay, err := computemodules.ProbeBucketStats(ctx, bucket, options.Options.BucketProbeTestKey, 0)
if err != nil {
return nil, errors.Wrap(err, "doProbeBucketStats zero")
}
resultRate, err := computemodules.ProbeBucketStats(ctx, bucket, options.Options.BucketProbeTestKey, int64(options.Options.BucketProbeTestSizeMb)*1024*1024)
if err != nil {
return nil, errors.Wrap(err, "doProbeBucketStats with payload")
}
metricTags := []influxdb.SKeyValue{}
for k, v := range bucketDetails.GetMetricTags() {
if len(v) == 0 {
continue
}
metricTags = append(metricTags, influxdb.SKeyValue{
Key: k,
Value: v,
})
}
metrics := []influxdb.SKeyValue{}
for k, v := range bucketDetails.GetMetricTags() {
if len(v) == 0 {
continue
}
metrics = append(metrics, influxdb.SKeyValue{
Key: k,
Value: v,
})
}
metrics = append(metrics,
influxdb.SKeyValue{
Key: "upload_delay_ms",
Value: fmt.Sprintf("%f", resultDelay.UploadDelayMs()),
},
influxdb.SKeyValue{
Key: "download_delay_ms",
Value: fmt.Sprintf("%f", resultDelay.DownloadDelayMs()),
},
influxdb.SKeyValue{
Key: "delete_delay_ms",
Value: fmt.Sprintf("%f", resultDelay.DeleteDelayMs()),
},
influxdb.SKeyValue{
Key: "upload_rate_mbps",
Value: fmt.Sprintf("%f", resultRate.UploadThroughputMbps(options.Options.BucketProbeTestSizeMb)),
},
influxdb.SKeyValue{
Key: "download_rate_mbps",
Value: fmt.Sprintf("%f", resultRate.DownloadThroughputMbps(options.Options.BucketProbeTestSizeMb)),
},
)
if options.Options.EnableBucketProbeDebug {
log.Debugf("BucketProbe for bucket %s metrics: %s", bucketDetails.Name, jsonutils.Marshal(metrics))
}
return []influxdb.SMetricData{
{
Name: "bucket_perf",
Tags: metricTags,
Metrics: metrics,
Timestamp: time.Now(),
},
}, nil
}

View File

@@ -110,9 +110,11 @@ func getNetworkAddrMap(s *mcclient.ClientSession, netId string) (map[string]api.
return nil, errors.Wrap(err, "GetSpecific addresses")
}
addrList := make([]api.SNetworkUsedAddress, 0)
err = addrListJson.Unmarshal(&addrList, "addresses")
if err != nil {
return nil, errors.Wrap(err, "Unmarshal addreses")
if addrListJson.Contains("addresses") {
err = addrListJson.Unmarshal(&addrList, "addresses")
if err != nil {
return nil, errors.Wrap(err, "Unmarshal addreses")
}
}
addrMap := make(map[string]api.SNetworkUsedAddress)
for i := range addrList {

40
pkg/cloudmon/misc/send.go Normal file
View File

@@ -0,0 +1,40 @@
// 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 misc
import (
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/cloudcommon/tsdb"
"yunion.io/x/onecloud/pkg/cloudmon/options"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/influxdb"
)
func sendMetrics(sess *mcclient.ClientSession, metrics []influxdb.SMetricData, database string) error {
if len(metrics) == 0 {
return nil
}
urls, err := tsdb.GetDefaultServiceSourceURLs(sess, options.Options.SessionEndpointType)
if err != nil {
return errors.Wrap(err, "GetServiceURLs")
}
err = influxdb.SendMetrics(urls, database, metrics, false)
if err != nil {
return errors.Wrap(err, "SendMetrics")
}
return nil
}

View File

@@ -0,0 +1,137 @@
// 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 misc
import (
"context"
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/apis"
"yunion.io/x/onecloud/pkg/cloudmon/options"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
baseOptions "yunion.io/x/onecloud/pkg/mcclient/options"
"yunion.io/x/onecloud/pkg/util/influxdb"
)
func StatusProbe(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
if options.Options.EnableStatusProbeDebug {
log.Debugf("Start resource status probe")
}
if !options.Options.EnableStatusProbe {
if options.Options.EnableStatusProbeDebug {
log.Debugf("Resource status probe is disabled")
}
return
}
sess := auth.GetSession(ctx, userCred, options.Options.Region)
metrics := make([]influxdb.SMetricData, 0)
for _, model := range options.Options.StatusProbeModels {
mts, err := doModelStatusProbe(sess, model)
if err != nil {
log.Errorf("doModelStatusProbe failed: %s", err)
}
metrics = append(metrics, mts...)
}
err := sendMetrics(sess, metrics, "system")
if err != nil {
log.Errorf("StatusProbe SendMetrics error: %s", err)
}
}
func doModelStatusProbe(sess *mcclient.ClientSession, modelName string) ([]influxdb.SMetricData, error) {
model, err := modulebase.GetModule(sess, modelName)
if err != nil {
return nil, errors.Wrap(err, "GetModule")
}
listOpts := baseOptions.BaseListOptions{}
listOpts.Scope = "max"
listOpts.SummaryStats = true
limit := 0
listOpts.Limit = &limit
results, err := model.List(sess, jsonutils.Marshal(listOpts))
if err != nil {
return nil, errors.Wrap(err, "List")
}
statusInfoTotal := struct {
apis.TotalCountBase
StatusInfo []apis.StatusStatisticStatusInfo
}{}
err = results.Totals.Unmarshal(&statusInfoTotal)
if err != nil {
return nil, errors.Wrap(err, "Unmarshal statusInfoTotal")
}
log.Infof("statusInfoTotal: %s", jsonutils.Marshal(statusInfoTotal))
metrics := make([]influxdb.SMetricData, 0)
totalCount := int64(0)
pendingDeletedCount := int64(0)
for _, statusInfo := range statusInfoTotal.StatusInfo {
metrics = append(metrics, genStatusMetricData(model, statusInfo.Status, statusInfo.TotalCount, statusInfo.PendingDeletedCount))
totalCount += statusInfo.TotalCount
pendingDeletedCount += statusInfo.PendingDeletedCount
}
metrics = append(metrics, genStatusMetricData(model, "total", totalCount, pendingDeletedCount))
if options.Options.EnableStatusProbeDebug {
log.Debugf("StatusProbe for model %s metrics: %s", modelName, jsonutils.Marshal(metrics))
}
return metrics, nil
}
func genStatusMetricData(model modulebase.Manager, status string, count int64, pendingDeletedCount int64) influxdb.SMetricData {
return influxdb.SMetricData{
Name: "status_probe",
Tags: influxdb.TKeyValuePairs{
influxdb.SKeyValue{
Key: "service",
Value: model.ServiceType(),
},
influxdb.SKeyValue{
Key: "model",
Value: model.GetKeyword(),
},
influxdb.SKeyValue{
Key: "status",
Value: status,
},
},
Metrics: influxdb.TKeyValuePairs{
influxdb.SKeyValue{
Key: "count",
Value: fmt.Sprintf("%d", count),
},
influxdb.SKeyValue{
Key: "pending_deleted",
Value: fmt.Sprintf("%d", pendingDeletedCount),
},
},
}
}

View File

@@ -37,6 +37,17 @@ type CloudMonOptions struct {
CloudAccountCollectMetricsBatchCount int `help:"Cloud Account Collect Metrics Batch Count" default:"10"`
CloudResourceCollectMetricsBatchCount int `help:"Cloud Resource Collect Metrics BatchC ount" default:"40"`
OracleCloudResourceCollectMetricsBatchCount int `help:"OracleCloud Resource Collect Metrics BatchC ount" default:"1"`
StatusProbeIntervalMinutes int `help:"Status Probe Interval unit:minute" default:"15"`
StatusProbeModels []string `help:"Status Probe Models" default:"compute-servers,compute-hosts"`
EnableStatusProbe bool `help:"Enable Status Probe" default:"false"`
EnableStatusProbeDebug bool `help:"Enable Status Probe Debug" default:"false"`
BucketProbeIntervalMinutes int `help:"Bucket Probe Interval unit:minute" default:"15"`
EnableBucketProbe bool `help:"Enable Bucket Probe" default:"false"`
EnableBucketProbeDebug bool `help:"Enable Bucket Probe Debug" default:"false"`
BucketProbeTestKey string `help:"Bucket Probe Test Key" default:"bucket_performance_test_object"`
BucketProbeTestSizeMb int `help:"Bucket Probe Test Size" default:"4"`
}
type PingProbeOptions struct {

View File

@@ -33,6 +33,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudmon/options"
"yunion.io/x/onecloud/pkg/cloudmon/resources"
"yunion.io/x/onecloud/pkg/mcclient/auth"
_ "yunion.io/x/onecloud/pkg/mcclient/modules/loader"
)
func StartService() {
@@ -57,6 +58,10 @@ func StartService() {
cron.AddJobAtIntervalsWithStartRun("PingProb", time.Duration(opts.PingProbIntervalHours)*time.Hour, misc.PingProbe, true)
cron.AddJobAtIntervalsWithStartRun("StatusProbe", time.Duration(opts.StatusProbeIntervalMinutes)*time.Minute, misc.StatusProbe, true)
cron.AddJobAtIntervalsWithStartRun("BucketProbe", time.Duration(opts.BucketProbeIntervalMinutes)*time.Minute, misc.BucketProbe, true)
cron.AddJobEveryFewDays("UsageMetricCollect", 1, 23, 10, 10, misc.UsegReport, false)
cron.AddJobEveryFewDays("AlertHistoryMetricCollect", 1, 23, 59, 59, misc.AlertHistoryReport, false)

View File

@@ -666,6 +666,9 @@ func (self *SKVMGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest
if fw_only, _ := task.GetParams().Bool("fw_only"); fw_only {
body.Add(jsonutils.JSONTrue, "fw_only")
}
if setUefiBootOrder, _ := task.GetParams().Bool("set_uefi_boot_order"); setUefiBootOrder {
body.Add(jsonutils.JSONTrue, "set_uefi_boot_order")
}
url := fmt.Sprintf("%s/servers/%s/sync", host.ManagerUri, guest.Id)
header := self.getTaskRequestHeader(task)
_, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)

View File

@@ -484,7 +484,7 @@ func (drv *SManagedVirtualizedGuestDriver) RequestStartOnHost(ctx context.Contex
if guest.BillingType == billing_api.BILLING_TYPE_POSTPAID && jsonutils.QueryBoolean(task.GetParams(), "auto_prepaid", false) {
err = ivm.ChangeBillingType(billing_api.BILLING_TYPE_PREPAID)
if err != nil && errors.Cause(err) != cloudprovider.ErrNotImplemented {
logclient.AddSimpleActionLog(guest, logclient.ACT_VM_CHANGE_BILLING_TYPE, errors.Wrapf(err, billing_api.BILLING_TYPE_PREPAID), userCred, false)
logclient.AddSimpleActionLog(guest, logclient.ACT_CHANGE_BILLING_TYPE, errors.Wrapf(err, billing_api.BILLING_TYPE_PREPAID), userCred, false)
}
}
@@ -985,7 +985,7 @@ func (drv *SManagedVirtualizedGuestDriver) RequestStopOnHost(ctx context.Context
if opts.StopCharging && guest.BillingType == billing_api.BILLING_TYPE_PREPAID {
err = ivm.ChangeBillingType(billing_api.BILLING_TYPE_POSTPAID)
if err != nil && errors.Cause(err) != cloudprovider.ErrNotImplemented {
logclient.AddSimpleActionLog(guest, logclient.ACT_VM_CHANGE_BILLING_TYPE, errors.Wrapf(err, billing_api.BILLING_TYPE_POSTPAID), task.GetUserCred(), false)
logclient.AddSimpleActionLog(guest, logclient.ACT_CHANGE_BILLING_TYPE, errors.Wrapf(err, billing_api.BILLING_TYPE_POSTPAID), task.GetUserCred(), false)
}
}

View File

@@ -76,6 +76,8 @@ func (self *SQcloudGuestDriver) GetStorageTypes() []string {
api.STORAGE_LOCAL_BASIC,
api.STORAGE_LOCAL_SSD,
api.STORAGE_CLOUD_HSSD,
api.STORAGE_CLOUD_BSSD,
api.STORAGE_CLOUD_TSSD,
}
}
@@ -139,7 +141,7 @@ func (self *SQcloudGuestDriver) ValidateCreateData(ctx context.Context, userCred
if sysDisk.SizeMb > 1024*1024 {
return nil, fmt.Errorf("The %s system disk size must be less than 1024GB", sysDisk.Backend)
}
case api.STORAGE_LOCAL_PRO, api.STORAGE_CLOUD_HSSD: //https://cloud.tencent.com/document/product/362/2353
case api.STORAGE_LOCAL_PRO: //https://cloud.tencent.com/document/product/362/2353
return nil, fmt.Errorf("storage %s can not be system disk", sysDisk.Backend)
}
@@ -154,7 +156,7 @@ func (self *SQcloudGuestDriver) ValidateCreateData(ctx context.Context, userCred
if disk.SizeMb < 10*1024 || disk.SizeMb > 32000*1024 {
return nil, httperrors.NewInputParameterError("The %s disk size must be in the range of 10GB ~ 32000GB", disk.Backend)
}
case api.STORAGE_CLOUD_SSD, api.STORAGE_CLOUD_HSSD:
case api.STORAGE_CLOUD_SSD, api.STORAGE_CLOUD_HSSD, api.STORAGE_CLOUD_BSSD, api.STORAGE_CLOUD_TSSD:
if disk.SizeMb < 20*1024 || disk.SizeMb > 32000*1024 {
return nil, httperrors.NewInputParameterError("The %s disk size must be in the range of 20GB ~ 32000GB", disk.Backend)
}
@@ -199,7 +201,7 @@ func (qcloud *SQcloudGuestDriver) ValidateGuestChangeConfigInput(ctx context.Con
if newDisk.SizeMb < 10*1024 || newDisk.SizeMb > 32000*1024 {
return nil, httperrors.NewInputParameterError("The %s disk size must be in the range of 10GB ~ 32000GB", newDisk.Backend)
}
case api.STORAGE_CLOUD_SSD, api.STORAGE_CLOUD_HSSD:
case api.STORAGE_CLOUD_SSD, api.STORAGE_CLOUD_HSSD, api.STORAGE_CLOUD_BSSD, api.STORAGE_CLOUD_TSSD:
if newDisk.SizeMb < 20*1024 || newDisk.SizeMb > 32000*1024 {
return nil, httperrors.NewInputParameterError("The %s disk size must be in the range of 20GB ~ 32000GB", newDisk.Backend)
}
@@ -273,12 +275,16 @@ func (self *SQcloudGuestDriver) GetInstanceCapability() cloudprovider.SInstanceC
cloudprovider.StorageInfo{StorageType: api.STORAGE_CLOUD_BASIC, MaxSizeGb: 16000, MinSizeGb: 10, StepSizeGb: 10, Resizable: true},
cloudprovider.StorageInfo{StorageType: api.STORAGE_CLOUD_PREMIUM, MaxSizeGb: 16000, MinSizeGb: 50, StepSizeGb: 10, Resizable: true},
cloudprovider.StorageInfo{StorageType: api.STORAGE_CLOUD_SSD, MaxSizeGb: 16000, MinSizeGb: 100, StepSizeGb: 10, Resizable: true},
cloudprovider.StorageInfo{StorageType: api.STORAGE_CLOUD_HSSD, MaxSizeGb: 32000, MinSizeGb: 20, StepSizeGb: 10, Resizable: true},
cloudprovider.StorageInfo{StorageType: api.STORAGE_CLOUD_HSSD, MaxSizeGb: 32000, MinSizeGb: 20, StepSizeGb: 1, Resizable: true},
cloudprovider.StorageInfo{StorageType: api.STORAGE_CLOUD_BSSD, MaxSizeGb: 32000, MinSizeGb: 20, StepSizeGb: 1, Resizable: true},
cloudprovider.StorageInfo{StorageType: api.STORAGE_CLOUD_TSSD, MaxSizeGb: 32000, MinSizeGb: 20, StepSizeGb: 1, Resizable: true},
},
SysDisk: []cloudprovider.StorageInfo{
cloudprovider.StorageInfo{StorageType: api.STORAGE_CLOUD_BASIC, MaxSizeGb: 500, MinSizeGb: 50, StepSizeGb: 10, Resizable: false},
cloudprovider.StorageInfo{StorageType: api.STORAGE_CLOUD_PREMIUM, MaxSizeGb: 1024, MinSizeGb: 50, StepSizeGb: 10, Resizable: false},
cloudprovider.StorageInfo{StorageType: api.STORAGE_CLOUD_SSD, MaxSizeGb: 500, MinSizeGb: 50, StepSizeGb: 10, Resizable: false},
cloudprovider.StorageInfo{StorageType: api.STORAGE_CLOUD_SSD, MaxSizeGb: 500, MinSizeGb: 50, StepSizeGb: 1, Resizable: false},
cloudprovider.StorageInfo{StorageType: api.STORAGE_CLOUD_BSSD, MaxSizeGb: 32000, MinSizeGb: 20, StepSizeGb: 1, Resizable: true},
cloudprovider.StorageInfo{StorageType: api.STORAGE_CLOUD_TSSD, MaxSizeGb: 32000, MinSizeGb: 20, StepSizeGb: 1, Resizable: true},
},
},
}

View File

@@ -230,11 +230,11 @@ func (self *SKVMHostDriver) CheckAndSetCacheImage(ctx context.Context, userCred
return err
}
srcHost, err := srcHostCacheImage.GetHost()
/*srcHost, err := srcHostCacheImage.GetHost()
if err != nil {
return errors.Wrapf(err, "Get storage cached image %s host", srcHostCacheImage.GetId())
}
input.SrcUrl = fmt.Sprintf("%s/download/images/%s", srcHost.ManagerUri, input.ImageId)
input.SrcUrl = fmt.Sprintf("%s/download/images/%s", srcHost.ManagerUri, input.ImageId)*/
}
url := fmt.Sprintf("%s/disks/image_cache", host.ManagerUri)

View File

@@ -60,7 +60,7 @@ func (self *SQcloudHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb
min, max = 50, 16000
case api.STORAGE_CLOUD_SSD:
min, max = 100, 16000
case api.STORAGE_CLOUD_HSSD:
case api.STORAGE_CLOUD_HSSD, api.STORAGE_CLOUD_BSSD, api.STORAGE_CLOUD_TSSD:
min, max = 20, 320000
default:
return fmt.Errorf("Not support create or resize %s disk", storage.StorageType)

View File

@@ -92,6 +92,8 @@ type SBucket struct {
ObjectCntLimit int `nullable:"false" default:"0" list:"user"`
AccessUrls jsonutils.JSONObject `nullable:"true" list:"user"`
EnablePerfMon bool `default:"false" list:"user" update:"user" create:"optional"`
}
func (manager *SBucketManager) SetHandlerProcessTimeout(info *appsrv.SHandlerInfo, r *http.Request) time.Duration {

View File

@@ -75,6 +75,9 @@ type SCapabilities struct {
DisabledModelartsPoolsBrands []string `json:",allowempty"`
ModelartsPoolsBrands []string `json:",allowempty"`
DisabledDnsBrands []string `json:",allowempty"`
DnsBrands []string `json:",allowempty"`
ContainerBrands []string `json:",allowempty"`
DisabledContainerBrands []string `json:",allowempty"`
@@ -119,6 +122,9 @@ type SCapabilities struct {
ReadOnlyModelartsPoolsBrands []string `json:",allowempty"`
ReadOnlyDisabledModelartsPoolsBrands []string `json:",allowempty"`
ReadOnlyDnsBrands []string `json:",allowempty"`
ReadOnlyDisabledDnsBrands []string `json:",allowempty"`
ReadOnlyContainerBrands []string `json:",allowempty"`
ReadOnlyDisabledContainerBrands []string `json:",allowempty"`
@@ -422,6 +428,7 @@ func getBrands(region *SCloudregion, domainId string, capa *SCapabilities) {
capa.SecurityGroupBrands = append(capa.SecurityGroupBrands, api.ONECLOUD_BRAND_ONECLOUD)
capa.ComputeEngineBrands = append(capa.ComputeEngineBrands, api.ONECLOUD_BRAND_ONECLOUD)
capa.SnapshotPolicyBrands = append(capa.SnapshotPolicyBrands, api.ONECLOUD_BRAND_ONECLOUD)
capa.DnsBrands = append(capa.DnsBrands, api.ONECLOUD_BRAND_ONECLOUD)
} else if utils.IsInStringArray(api.HYPERVISOR_POD, capa.Hypervisors) {
capa.Brands = append(capa.Brands, api.ONECLOUD_BRAND_ONECLOUD)
capa.ComputeEngineBrands = append(capa.ComputeEngineBrands, api.ONECLOUD_BRAND_ONECLOUD)
@@ -519,6 +526,8 @@ func getBrands(region *SCloudregion, domainId string, capa *SCapabilities) {
appendBrand(&capa.SnapshotPolicyBrands, &capa.DisabledSnapshotPolicyBrands, &capa.ReadOnlySnapshotPolicyBrands, &capa.ReadOnlyDisabledSnapshotPolicyBrands, brand, capability, enabled, readOnly)
case cloudprovider.CLOUD_CAPABILITY_MODELARTES:
appendBrand(&capa.ModelartsPoolsBrands, &capa.DisabledModelartsPoolsBrands, &capa.ReadOnlyModelartsPoolsBrands, &capa.ReadOnlyDisabledModelartsPoolsBrands, brand, capability, enabled, readOnly)
case cloudprovider.CLOUD_CAPABILITY_DNSZONE:
appendBrand(&capa.DnsBrands, &capa.DisabledDnsBrands, &capa.ReadOnlyDnsBrands, &capa.ReadOnlyDisabledDnsBrands, brand, capability, enabled, readOnly)
default:
}
}

View File

@@ -817,11 +817,32 @@ func (c *SContainer) PerformSaveVolumeMountImage(ctx context.Context, userCred m
if err != nil {
return nil, errors.Wrap(err, "ToHostMount")
}
cleanupDirPath := func(dirPath string) string {
nPath := ""
pathSegs := strings.Split(dirPath, "/")
for _, seg := range pathSegs {
if len(seg) > 0 {
nPath = filepath.Join(nPath, seg)
}
}
return nPath
}
cleanupDirPaths := func(dirPaths []string) []string {
for i := range dirPaths {
dirPaths[i] = cleanupDirPath(dirPaths[i])
}
return dirPaths
}
hostInput := &hostapi.ContainerSaveVolumeMountToImageInput{
ImageId: imageId,
VolumeMountIndex: input.Index,
VolumeMount: hvm,
VolumeMountDirs: input.Dirs,
VolumeMountDirs: cleanupDirPaths(input.Dirs),
VolumeMountPrefix: cleanupDirPath(input.DirPrefix),
}
return hostInput, c.StartSaveVolumeMountImage(ctx, userCred, hostInput, "")

View File

@@ -2616,9 +2616,8 @@ func (manager *SDiskManager) FetchCustomizeColumns(
rows[i].Brand = "Unknown"
rows[i].Provider = "Unknown"
}
if len(rows[i].ExternalId) == 0 {
//rows[i].Iops = iops
//rows[i].Throughput = bps
// 仅kvm使用
if len(rows[i].ManagerId) == 0 {
disk.Iops = iops
disk.Throughput = bps
}
@@ -3339,3 +3338,29 @@ func (disk *SDisk) resetDiskinfo(
db.OpsLog.LogEvent(disk, db.ACT_UPDATE, notes, userCred)
return nil
}
func (disk *SDisk) PerformChangeBillingType(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.DiskChangeBillingTypeInput) (jsonutils.JSONObject, error) {
if !utils.IsInStringArray(disk.Status, []string{api.DISK_READY}) {
return nil, httperrors.NewServerStatusError("Cannot change disk billing type in status %s", disk.Status)
}
if len(input.BillingType) == 0 {
return nil, httperrors.NewMissingParameterError("billing_type")
}
if !utils.IsInStringArray(input.BillingType, []string{billing_api.BILLING_TYPE_POSTPAID, billing_api.BILLING_TYPE_PREPAID}) {
return nil, httperrors.NewInputParameterError("invalid billing_type %s", input.BillingType)
}
if disk.BillingType == input.BillingType {
return nil, nil
}
return nil, disk.StartChangeBillingTypeTask(ctx, userCred, "")
}
func (disk *SDisk) StartChangeBillingTypeTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
disk.SetStatus(ctx, userCred, apis.STATUS_CHANGE_BILLING_TYPE, "")
kwargs := jsonutils.NewDict()
task, err := taskman.TaskManager.NewTask(ctx, "DiskChangeBillingTypeTask", disk, userCred, kwargs, parentTaskId, "", nil)
if err != nil {
return err
}
return task.ScheduleRun(nil)
}

View File

@@ -76,6 +76,8 @@ type SDnsRecord struct {
DnsValue string `width:"256" charset:"ascii" nullable:"false" list:"user" update:"user" create:"required"`
TTL int64 `nullable:"false" list:"user" update:"user" create:"required" json:"ttl"`
MxPriority int64 `nullable:"false" list:"user" update:"user" create:"optional"`
// cloudflare 特有
Proxied tristate.TriState `default:"false" list:"user" create:"optional"`
// 解析线路类型
PolicyType string `width:"36" charset:"ascii" nullable:"false" list:"user" update:"user" create:"optional"`
@@ -491,6 +493,7 @@ func (self *SDnsRecord) syncWithDnsRecord(ctx context.Context, userCred mcclient
diff, err := db.Update(self, func() error {
self.Name = ext.GetDnsName()
self.Enabled = tristate.NewFromBool(ext.GetEnabled())
self.Proxied = tristate.NewFromBool(ext.IsProxied())
self.Status = ext.GetStatus()
self.TTL = ext.GetTTL()
self.MxPriority = ext.GetMxPriority()
@@ -525,6 +528,7 @@ func (self *SDnsZone) newFromCloudDnsRecord(ctx context.Context, userCred mcclie
record.Name = ext.GetDnsName()
record.Status = ext.GetStatus()
record.Enabled = tristate.NewFromBool(ext.GetEnabled())
record.Proxied = tristate.NewFromBool(ext.IsProxied())
record.TTL = ext.GetTTL()
record.MxPriority = ext.GetMxPriority()
record.DnsType = string(ext.GetDnsType())

View File

@@ -6394,6 +6394,14 @@ func (self *SGuest) PerformSetBootIndex(ctx context.Context, userCred mcclient.T
}
}
if self.Bios == api.VM_BOOT_MODE_UEFI {
data := jsonutils.NewDict()
data.Set("set_uefi_boot_order", jsonutils.JSONTrue)
if err := self.startSyncTask(ctx, userCred, false, "", data); err != nil {
return nil, err
}
}
return nil, nil
}
@@ -6757,7 +6765,7 @@ func (g *SGuest) PerformChangeBillingType(ctx context.Context, userCred mcclient
}
func (self *SGuest) StartChangeBillingTypeTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
self.SetStatus(ctx, userCred, api.VM_CHANGE_BILLING_TYPE, "")
self.SetStatus(ctx, userCred, apis.STATUS_CHANGE_BILLING_TYPE, "")
kwargs := jsonutils.NewDict()
task, err := taskman.TaskManager.NewTask(ctx, "GuestChangeBillingTypeTask", self, userCred, kwargs, parentTaskId, "", nil)
if err != nil {

View File

@@ -71,7 +71,15 @@ func (manager *SGuestManager) FetchCustomizeColumns(
gds := fetchGuestDisksInfo(guestIds)
if gds != nil {
for i := range rows {
rows[i].DisksInfo, _ = gds[guestIds[i]]
rows[i].DisksInfo = []api.GuestDiskInfo{}
if disks, ok := gds[guestIds[i]]; ok {
for j := range disks {
if len(rows[i].ManagerId) > 0 {
disks[j].Iops = disks[j].DiskIops
}
rows[i].DisksInfo = append(rows[i].DisksInfo, disks[j].GuestDiskInfo)
}
}
rows[i].DiskCount = len(rows[i].DisksInfo)
shortDescs := []string{}
for _, info := range rows[i].DisksInfo {
@@ -368,12 +376,17 @@ type sGustDiskSize struct {
DiskCount int
}
type sGuestDiskInfo struct {
type GuestDiskInfo struct {
DiskIops int
api.GuestDiskInfo
}
type sGuestDiskInfo struct {
GuestDiskInfo
GuestId string
}
func fetchGuestDisksInfo(guestIds []string) map[string][]api.GuestDiskInfo {
func fetchGuestDisksInfo(guestIds []string) map[string][]GuestDiskInfo {
disks := DiskManager.Query().SubQuery()
guestdisks := GuestdiskManager.Query().SubQuery()
storages := StorageManager.Query().SubQuery()
@@ -392,6 +405,8 @@ func fetchGuestDisksInfo(guestIds []string) map[string][]api.GuestDiskInfo {
storages.Field("medium_type"),
storages.Field("storage_type"),
guestdisks.Field("iops"),
disks.Field("iops").Label("disk_iops"),
disks.Field("throughput"),
guestdisks.Field("bps"),
disks.Field("template_id").Label("image_id"),
guestdisks.Field("guest_id"),
@@ -405,17 +420,18 @@ func fetchGuestDisksInfo(guestIds []string) map[string][]api.GuestDiskInfo {
gds := []sGuestDiskInfo{}
err := q.All(&gds)
if err != nil {
log.Errorf("fetchGuestDisksInfo: %v", err)
return nil
}
imageIds := []string{}
ret := map[string][]api.GuestDiskInfo{}
ret := map[string][]GuestDiskInfo{}
for i := range gds {
if len(gds[i].ImageId) > 0 {
imageIds = append(imageIds, gds[i].ImageId)
}
_, ok := ret[gds[i].GuestId]
if !ok {
ret[gds[i].GuestId] = []api.GuestDiskInfo{}
ret[gds[i].GuestId] = []GuestDiskInfo{}
}
ret[gds[i].GuestId] = append(ret[gds[i].GuestId], gds[i].GuestDiskInfo)
}

View File

@@ -0,0 +1,227 @@
// 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"
"database/sql"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
)
type SHostFileJontsManager struct {
db.SModelBaseManager
}
var HostFileJointsManager *SHostFileJontsManager
func init() {
HostFileJointsManager = &SHostFileJontsManager{
SModelBaseManager: db.NewModelBaseManager(
SHostFileJoint{},
"hostfilejoints_tbl",
"hostfilejoint",
"hostfilejoints",
),
}
HostFileJointsManager.SetVirtualObject(HostFileJointsManager)
}
// +onecloud:model-api-gen
type SHostFileJoint struct {
db.SModelBase
HostId string `width:"128" charset:"ascii" nullable:"false" primary:"true"`
HostFileId string `width:"128" charset:"ascii" nullable:"false" primary:"true"`
Deleted bool `nullable:"false"`
}
func (host *SHost) PerformSetHostFiles(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
input computeapi.HostSetHostFilesInput,
) (jsonutils.JSONObject, error) {
hostFileIds := make([]string, 0)
for _, hostFileName := range input.HostFiles {
hostFileObj, err := HostFileManager.FetchByIdOrName(ctx, userCred, hostFileName)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2(HostFileManager.Keyword(), hostFileName)
}
return nil, errors.Wrap(err, "HostFileManager.FetchByIdOrName")
}
hostFile := hostFileObj.(*SHostFile)
if hostFile.DomainId != host.DomainId {
domains := hostFile.GetSharedDomains()
if !utils.IsInArray(host.DomainId, domains) {
return nil, errors.Wrapf(httperrors.ErrNoPermission, "host file %s not accessible to host %s", hostFile.Name, host.Name)
}
}
hostFileIds = append(hostFileIds, hostFileObj.GetId())
}
err := HostFileJointsManager.setHostFiles(ctx, userCred, host.Id, hostFileIds)
if err != nil {
return nil, errors.Wrap(err, "HostFileJontsManager.setHostFiles")
}
return nil, nil
}
func (manager *SHostFileJontsManager) getHostFiles(hostId string) ([]SHostFileJoint, error) {
q := manager.Query().Equals("host_id", hostId)
hostFiles := make([]SHostFileJoint, 0)
err := db.FetchModelObjects(manager, q, &hostFiles)
if err != nil {
return nil, errors.Wrap(err, "db.FetchModelObjects")
}
return hostFiles, nil
}
func (manager *SHostFileJontsManager) setHostFiles(
ctx context.Context,
userCred mcclient.TokenCredential,
hostId string,
hostFileIds []string,
) error {
hostFiles, err := manager.getHostFiles(hostId)
if err != nil {
return errors.Wrap(err, "manager.getHostFiles")
}
existingIds := make(map[string]bool)
for i := range hostFiles {
existingIds[hostFiles[i].HostFileId] = true
if utils.IsInStringArray(hostFiles[i].HostFileId, hostFileIds) {
hostFiles[i].Deleted = false
} else {
hostFiles[i].Deleted = true
}
}
for i := range hostFileIds {
if _, ok := existingIds[hostFileIds[i]]; !ok {
hostFileJoint := SHostFileJoint{
HostId: hostId,
HostFileId: hostFileIds[i],
Deleted: false,
}
hostFileJoint.SetModelManager(manager, &hostFileJoint)
hostFiles = append(hostFiles, hostFileJoint)
}
}
errs := make([]error, 0)
for i := range hostFiles {
err := manager.TableSpec().InsertOrUpdate(ctx, &hostFiles[i])
if err != nil {
errs = append(errs, err)
}
}
if len(errs) > 0 {
return errors.NewAggregate(errs)
}
return nil
}
func (h *SHost) getHostFiles() ([]computeapi.SHostFile, error) {
q := HostFileManager.Query()
jointQ := HostFileJointsManager.Query().Equals("host_id", h.Id).SubQuery()
q = q.Join(jointQ, sqlchemy.Equals(q.Field("id"), jointQ.Field("host_file_id")))
hostFiles := make([]computeapi.SHostFile, 0)
err := q.All(&hostFiles)
if err != nil {
return nil, errors.Wrap(err, "q.All")
}
return hostFiles, nil
}
func fetchHostHostFiles(hostIds []string) (map[string][]string, error) {
q := HostFileJointsManager.Query().In("host_id", hostIds)
hostFilesQ := HostFileManager.Query().SubQuery()
q = q.Join(hostFilesQ, sqlchemy.Equals(q.Field("host_file_id"), hostFilesQ.Field("id")))
q = q.AppendField(q.Field("host_id"))
q = q.AppendField(hostFilesQ.Field("name"))
results := make([]struct {
HostId string
Name string
}, 0)
err := q.All(&results)
if err != nil {
return nil, errors.Wrap(err, "q.All")
}
hostFiles := make(map[string][]string)
for _, result := range results {
hostFiles[result.HostId] = append(hostFiles[result.HostId], result.Name)
}
return hostFiles, nil
}
func fetchHostFilesHosts(hostFileIds []string) (map[string][]string, error) {
q := HostFileJointsManager.Query().In("host_file_id", hostFileIds)
hostQ := HostManager.Query().SubQuery()
q = q.Join(hostQ, sqlchemy.Equals(q.Field("host_id"), hostQ.Field("id")))
q = q.AppendField(q.Field("host_file_id"))
q = q.AppendField(hostQ.Field("name"))
results := make([]struct {
HostFileId string
Name string
}, 0)
err := q.All(&results)
if err != nil {
return nil, errors.Wrap(err, "q.All")
}
hostFiles := make(map[string][]string)
for _, result := range results {
hostFiles[result.HostFileId] = append(hostFiles[result.HostFileId], result.Name)
}
return hostFiles, nil
}
func (host *SHost) GetDetailsHostFiles(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
) (jsonutils.JSONObject, error) {
hostFiles, err := host.getHostFiles()
if err != nil {
return nil, errors.Wrap(err, "GetHostFiles")
}
hostFilesObj := jsonutils.NewDict()
hostFilesObj.Add(jsonutils.Marshal(hostFiles), "host_files")
return hostFilesObj, nil
}

View File

@@ -0,0 +1,151 @@
// 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/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/sqlchemy"
api "yunion.io/x/onecloud/pkg/apis/compute"
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
"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"
)
type SHostFileManager struct {
db.SInfrasResourceBaseManager
}
var HostFileManager *SHostFileManager
func init() {
HostFileManager = &SHostFileManager{
SInfrasResourceBaseManager: db.NewInfrasResourceBaseManager(
SHostFile{},
"hostfiles_tbl",
"hostfile",
"hostfiles",
),
}
HostFileManager.SetVirtualObject(HostFileManager)
}
// +onecloud:model-api-gen
type SHostFile struct {
db.SInfrasResourceBase
Type computeapi.HostFileType `width:"64" charset:"ascii" nullable:"false" list:"domain" create:"domain_required"`
Path string `width:"256" charset:"utf8" nullable:"true" list:"domain" update:"domain" create:"domain_optional"`
Content string `charset:"utf8" nullable:"true" get:"domain" update:"domain" create:"domain_optional"`
}
func (manager *SHostFileManager) ValidateCreateData(
ctx context.Context,
userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject,
input computeapi.HostFileCreateInput,
) (computeapi.HostFileCreateInput, 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")
}
if len(input.Type) == 0 {
return input, httperrors.NewInputParameterError("Type is required")
}
return input, nil
}
func (manager *SHostFileManager) ValidateUpdateData(
ctx context.Context,
userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject,
input computeapi.HostFileUpdateInput,
) (computeapi.HostFileUpdateInput, error) {
return input, nil
}
func (manager *SHostFileManager) ListItemFilter(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query computeapi.HostFileListInput,
) (*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")
}
if len(query.Type) > 0 {
q = q.In("type", query.Type)
}
if len(query.Path) > 0 {
q = q.Equals("path", query.Path)
}
return q, nil
}
func (manager *SHostFileManager) FetchCustomizeColumns(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
objs []interface{},
fields stringutils2.SSortedStrings,
isList bool,
) []api.HostFileDetails {
rows := make([]api.HostFileDetails, len(objs))
infrasRows := manager.SInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
hostFileIds := make([]string, 0, len(objs))
for i := range rows {
rows[i] = api.HostFileDetails{
InfrasResourceBaseDetails: infrasRows[i],
}
hostFileIds = append(hostFileIds, objs[i].(*SHostFile).Id)
}
hostFiles, err := fetchHostFilesHosts(hostFileIds)
if err != nil {
log.Errorf("fetchHostFilesHosts error: %v", err)
}
for i := range rows {
rows[i].Hosts = hostFiles[hostFileIds[i]]
}
return rows
}
func (hostfile *SHostFile) ValidateDeleteCondition(ctx context.Context, info api.HostFileDetails) error {
err := hostfile.SInfrasResourceBase.ValidateDeleteCondition(ctx, jsonutils.Marshal(info))
if err != nil {
return errors.Wrap(err, "SInfrasResourceBase.ValidateDeleteCondition")
}
if len(info.Hosts) > 0 {
return httperrors.NewNotEmptyError("hostfile is used by hosts")
}
return nil
}

View File

@@ -3801,22 +3801,28 @@ func (manager *SHostManager) FetchCustomizeColumns(
schedtags, err := fetchHostSchedtags(hostIds)
if err != nil {
log.Errorf("fetchHostSchedtags error: %v", err)
return rows
// return rows
}
storages, err := fetchHostStorages(hostIds)
if err != nil {
log.Errorf("host storages error: %v", err)
return rows
// return rows
}
nics, err := fetchHostNics(hostIds)
if err != nil {
log.Errorf("fetchHostNics error: %v", err)
return rows
// return rows
}
guestCnts := manager.FetchGuestCnt(hostIds)
hostFiles, err := fetchHostHostFiles(hostIds)
if err != nil {
log.Errorf("fetchHostHostFiles error: %v", err)
}
for i := range rows {
cnt, ok := guestCnts[hostIds[i]]
if ok {
@@ -3919,6 +3925,7 @@ func (manager *SHostManager) FetchCustomizeColumns(
rows[i].Schedtags, _ = schedtags[hostIds[i]]
rows[i].NicInfo, _ = nics[hostIds[i]]
rows[i].NicCount = len(rows[i].NicInfo)
rows[i].HostFiles = hostFiles[hostIds[i]]
if hideCpuTypoInfo {
sysInfo, ok := hosts[i].SysInfo.(*jsonutils.JSONDict)
@@ -4973,14 +4980,19 @@ func (hh *SHost) PerformPing(ctx context.Context, userCred mcclient.TokenCredent
dependSvcs := []string{"ntpd", "kafka", apis.SERVICE_TYPE_INFLUXDB, apis.SERVICE_TYPE_VICTORIA_METRICS, "elasticsearch", "opentsdb"}
catalog := auth.GetCatalogData(dependSvcs, options.Options.Region)
if catalog == nil {
return nil, fmt.Errorf("Get catalog error")
return nil, errors.Wrap(errors.ErrServer, "Get catalog error")
}
result.Set("catalog", catalog)
if storages, err := hh.GetStoragesByMasterHost(); err != nil {
return nil, err
return nil, errors.Wrap(err, "get storages by master host")
} else {
result.Set("master_host_storages", jsonutils.NewStringArray(storages))
}
hostFiles, err := hh.getHostFiles()
if err != nil {
return nil, errors.Wrap(err, "get host files")
}
result.Set("host_files", jsonutils.Marshal(hostFiles))
appParams := appsrv.AppContextGetParams(ctx)
if appParams != nil {

View File

@@ -36,7 +36,10 @@ type SSecurityGroupResourceBase struct {
SecgroupId string `width:"36" charset:"ascii" nullable:"false" create:"required" index:"true" list:"user" json:"secgroup_id"`
}
type SSecurityGroupResourceBaseManager struct{}
type SSecurityGroupResourceBaseManager struct {
SCloudregionResourceBaseManager
SManagedResourceBaseManager
}
func ValidateSecurityGroupResourceInput(ctx context.Context, userCred mcclient.TokenCredential, query api.SecgroupResourceInput) (*SSecurityGroup, api.SecgroupResourceInput, error) {
secgrpObj, err := SecurityGroupManager.FetchByIdOrName(ctx, userCred, query.SecgroupId)
@@ -78,16 +81,34 @@ func (manager *SSecurityGroupResourceBaseManager) FetchCustomizeColumns(
}
secgrpIds[i] = base.SecgroupId
}
secgrpNames, err := db.FetchIdNameMap2(SecurityGroupManager, secgrpIds)
groups := make(map[string]SSecurityGroup)
err := db.FetchStandaloneObjectsByIds(SecurityGroupManager, secgrpIds, groups)
if err != nil {
log.Errorf("FetchIdNameMap2 fail %s", err)
return rows
log.Errorf("FetchStandaloneObjectsByIds fail %s", err)
return nil
}
regionList := make([]interface{}, len(rows))
managerList := make([]interface{}, len(rows))
for i := range rows {
if name, ok := secgrpNames[secgrpIds[i]]; ok {
rows[i].Secgroup = name
rows[i] = api.SecurityGroupResourceInfo{}
if group, ok := groups[secgrpIds[i]]; ok {
rows[i].Secgroup = group.Name
rows[i].CloudregionId = group.CloudregionId
rows[i].ManagerId = group.ManagerId
}
regionList[i] = &SCloudregionResourceBase{rows[i].CloudregionId}
managerList[i] = &SManagedResourceBase{rows[i].ManagerId}
}
regionRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, regionList, fields, isList)
managerRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, managerList, fields, isList)
for i := range rows {
rows[i].CloudregionResourceInfo = regionRows[i]
rows[i].ManagedResourceInfo = managerRows[i]
}
return rows
}
@@ -104,10 +125,27 @@ func (manager *SSecurityGroupResourceBaseManager) ListItemFilter(
}
q = q.Equals("secgroup_id", secgrpObj.GetId())
}
if len(query.SecgroupName) > 0 {
sq := SecurityGroupManager.Query("id").Like("name", "%"+query.SecgroupName+"%")
q = q.In("secgroup_id", sq.SubQuery())
}
var err error
subq := SecurityGroupManager.Query("id").Snapshot()
subq, err = manager.SManagedResourceBaseManager.ListItemFilter(ctx, subq, userCred, query.ManagedResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter")
}
subq, err = manager.SCloudregionResourceBaseManager.ListItemFilter(ctx, subq, userCred, query.RegionalFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemFilter")
}
if subq.IsAltered() {
q = q.Filter(sqlchemy.In(q.Field("secgroup_id"), subq.SubQuery()))
}
return q, nil
}

View File

@@ -122,7 +122,8 @@ type SServerSku struct {
func (manager *SServerSkuManager) FetchUniqValues(ctx context.Context, data jsonutils.JSONObject) jsonutils.JSONObject {
regionId, _ := data.GetString("cloudregion_id")
return jsonutils.Marshal(map[string]string{"cloudregion_id": regionId})
zoneId, _ := data.GetString("zone_id")
return jsonutils.Marshal(map[string]string{"cloudregion_id": regionId, "zone_id": zoneId})
}
func (manager *SServerSkuManager) FilterByUniqValues(q *sqlchemy.SQuery, values jsonutils.JSONObject) *sqlchemy.SQuery {
@@ -130,6 +131,10 @@ func (manager *SServerSkuManager) FilterByUniqValues(q *sqlchemy.SQuery, values
if len(regionId) > 0 {
q = q.Equals("cloudregion_id", regionId)
}
zoneId, _ := values.GetString("zone_id")
if len(zoneId) > 0 {
q = q.Equals("zone_id", zoneId)
}
return q
}
@@ -351,8 +356,6 @@ func (self *SServerSkuManager) ValidateCreateData(ctx context.Context, userCred
if input.Provider == api.CLOUD_PROVIDER_ONECLOUD {
} else if utils.IsInStringArray(input.Provider, api.PRIVATE_CLOUD_PROVIDERS) {
input.Status = api.SkuStatusCreating
} else {
return input, httperrors.NewUnsupportOperationError("Not support create public cloud sku")
}
input.LocalCategory = input.InstanceTypeCategory

View File

@@ -1694,6 +1694,19 @@ func (manager *SStorageManager) InitializeData() error {
}
}
}
sq := CloudproviderManager.Query("id").Equals("provider", api.CLOUD_PROVIDER_ALIYUN).SubQuery()
q = manager.Query().NotEquals("medium_type", api.DISK_TYPE_SSD).In("manager_id", sq)
storages = make([]SStorage, 0)
err = db.FetchModelObjects(manager, q, &storages)
if err != nil {
return err
}
for i := range storages {
db.Update(&storages[i], func() error {
storages[i].MediumType = api.DISK_TYPE_SSD
return nil
})
}
return nil
}

View File

@@ -0,0 +1,33 @@
// 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 regiondrivers
import (
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/compute/models"
)
type SCloudflareRegionDriver struct {
SManagedVirtualizationRegionDriver
}
func init() {
driver := SCloudflareRegionDriver{}
models.RegisterRegionDriver(&driver)
}
func (self *SCloudflareRegionDriver) GetProvider() string {
return api.CLOUD_PROVIDER_CLOUDFLARE
}

View File

@@ -106,6 +106,8 @@ func InitHandlers(app *appsrv.Application) {
models.SnapshotPolicyDiskManager,
models.LoadbalancerSecurityGroupManager,
models.HostFileJointsManager,
} {
db.RegisterModelManager(manager)
}
@@ -249,6 +251,8 @@ func InitHandlers(app *appsrv.Application) {
models.SSLCertificateManager,
baremetalmodels.BaremetalProfileManager,
models.HostFileManager,
} {
db.RegisterModelManager(manager)
handler := db.NewModelHandler(manager)

View File

@@ -0,0 +1,95 @@
// 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 disk
import (
"context"
"time"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/apis"
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/util/logclient"
)
type DiskChangeBillingTypeTask struct {
SDiskBaseTask
}
func init() {
taskman.RegisterTask(DiskChangeBillingTypeTask{})
}
func (self *DiskChangeBillingTypeTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
disk := obj.(*models.SDisk)
idisk, err := disk.GetIDisk(ctx)
if err != nil {
self.taskFail(ctx, disk, errors.Wrapf(err, "GetIDisk"))
return
}
billType := ""
switch disk.BillingType {
case billing_api.BILLING_TYPE_POSTPAID:
billType = billing_api.BILLING_TYPE_PREPAID
case billing_api.BILLING_TYPE_PREPAID:
billType = billing_api.BILLING_TYPE_POSTPAID
}
err = idisk.ChangeBillingType(billType)
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotImplemented {
disk.SetStatus(ctx, self.GetUserCred(), api.DISK_READY, "")
self.SetStageComplete(ctx, nil)
return
}
self.taskFail(ctx, disk, errors.Wrapf(err, "ChangeBillingType"))
return
}
idisk.Refresh()
db.Update(disk, func() error {
disk.BillingType = billType
disk.ExpiredAt = time.Time{}
if disk.BillingType == billing_api.BILLING_TYPE_PREPAID {
disk.AutoRenew = idisk.IsAutoRenew()
disk.ExpiredAt = idisk.GetExpiredAt()
}
return nil
})
self.taskComplete(ctx, disk)
}
func (self *DiskChangeBillingTypeTask) taskComplete(ctx context.Context, disk *models.SDisk) {
disk.SetStatus(ctx, self.GetUserCred(), api.DISK_READY, "")
logclient.AddActionLogWithStartable(self, disk, logclient.ACT_CHANGE_BILLING_TYPE, disk.BillingType, self.UserCred, true)
self.SetStageComplete(ctx, nil)
}
func (self *DiskChangeBillingTypeTask) taskFail(ctx context.Context, disk *models.SDisk, err error) {
disk.SetStatus(ctx, self.GetUserCred(), apis.STATUS_CHANGE_BILLING_TYPE_FAILED, "")
logclient.AddActionLogWithStartable(self, disk, logclient.ACT_CHANGE_BILLING_TYPE, err, self.UserCred, false)
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
}

View File

@@ -69,6 +69,7 @@ func (self *DnsRecordCreateTask) OnInit(ctx context.Context, obj db.IStandaloneM
Enabled: record.Enabled.Bool(),
DnsType: cloudprovider.TDnsType(record.DnsType),
DnsValue: record.DnsValue,
Proxied: record.Proxied.Bool(),
Ttl: record.TTL,
MxPriority: record.MxPriority,
PolicyType: cloudprovider.TDnsPolicyType(record.PolicyType),

View File

@@ -76,6 +76,7 @@ func (self *DnsRecordUpdateTask) OnInit(ctx context.Context, obj db.IStandaloneM
DnsValue: record.DnsValue,
DnsType: cloudprovider.TDnsType(record.DnsType),
Enabled: record.Enabled.Bool(),
Proxied: record.Proxied.Bool(),
Ttl: record.TTL,
MxPriority: record.MxPriority,
PolicyType: cloudprovider.TDnsPolicyType(record.PolicyType),

View File

@@ -19,7 +19,7 @@ import (
"yunion.io/x/jsonutils"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/apis"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/compute/models"
@@ -49,12 +49,12 @@ func (self *GuestChangeBillingTypeTask) OnInit(ctx context.Context, obj db.IStan
}
func (self *GuestChangeBillingTypeTask) OnGuestChangeBillingTypeTaskComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_VM_CHANGE_BILLING_TYPE, guest.BillingType, self.UserCred, false)
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_CHANGE_BILLING_TYPE, guest.BillingType, self.UserCred, false)
self.SetStageComplete(ctx, nil)
}
func (self *GuestChangeBillingTypeTask) OnGuestChangeBillingTypeTaskCompleteFailed(ctx context.Context, guest *models.SGuest, reason jsonutils.JSONObject) {
guest.SetStatus(ctx, self.GetUserCred(), api.VM_CHANGE_BILLING_TYPE_FAILED, "")
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_VM_CHANGE_BILLING_TYPE, reason.String(), self.UserCred, false)
guest.SetStatus(ctx, self.GetUserCred(), apis.STATUS_CHANGE_BILLING_TYPE_FAILED, "")
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_CHANGE_BILLING_TYPE, reason.String(), self.UserCred, false)
self.SetStageFailed(ctx, reason)
}

View File

@@ -24,10 +24,12 @@ import (
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/util/logclient"
)
type GuestCreateDiskTask struct {
@@ -211,12 +213,12 @@ func (self *ManagedGuestCreateDiskTask) OnManagedDiskPrepared(ctx context.Contex
for _, d := range disks {
diskId := d.DiskId
iDisk, err := models.DiskManager.FetchById(diskId)
_disk, err := models.DiskManager.FetchById(diskId)
if err != nil {
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
return
}
disk := iDisk.(*models.SDisk)
disk := _disk.(*models.SDisk)
if disk.Status != api.DISK_READY {
self.SetStageFailed(ctx, jsonutils.NewString(fmt.Sprintf("disk %s is not ready(status=%s)", disk.Id, disk.Status)))
return
@@ -240,7 +242,28 @@ func (self *ManagedGuestCreateDiskTask) OnManagedDiskPrepared(ctx context.Contex
self.SetStageFailed(ctx, jsonutils.NewString(fmt.Sprintf("Attach Disk to guest fail error: %v", err)))
return
}
time.Sleep(time.Second * 5)
if guest.BillingType == billing_api.BILLING_TYPE_PREPAID {
idisk, err := disk.GetIDisk(ctx)
if err != nil {
if errors.Cause(err) != cloudprovider.ErrNotFound {
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_CHANGE_BILLING_TYPE, errors.Wrapf(err, "GetIDisk %s", disk.ExternalId), self.UserCred, false)
continue
}
}
err = idisk.ChangeBillingType(guest.BillingType)
if err != nil {
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_CHANGE_BILLING_TYPE, errors.Wrapf(err, "ChangeBillingType %s", disk.ExternalId), self.UserCred, false)
continue
}
db.Update(disk, func() error {
disk.BillingType = guest.BillingType
return nil
})
}
}
self.SetStageComplete(ctx, nil)

View File

@@ -108,7 +108,7 @@ func (self *GuestSyncConfTask) StartRestartNetworkTask(ctx context.Context, gues
if err != nil {
return err
}
err = drv.QgaRequestGuestPing(ctx, self.GetTaskRequestHeader(), host, guest, false, &api.ServerQgaTimeoutInput{Timeout: 1000})
err = drv.QgaRequestGuestPing(ctx, self.GetTaskRequestHeader(), host, guest, false, &api.ServerQgaTimeoutInput{Timeout: 1})
if err != nil {
return errors.Wrap(err, "qga guest-ping")
}

View File

@@ -54,9 +54,15 @@ func (p postOverlayHostPath) Mount(d diskPostOverlay, pod volume_mount.IPodInfo,
return errors.Wrapf(err, "get post overlay mountpoint for container %s", ctrId)
}
return mountutils.MountOverlayWithFeatures(ov.HostLowerDir, upperDir, workDir, mergedDir, &mountutils.MountOverlayFeatures{
if err := mountutils.MountOverlayWithFeatures(ov.HostLowerDir, upperDir, workDir, mergedDir, &mountutils.MountOverlayFeatures{
MetaCopy: true,
})
}); err != nil {
return errors.Wrapf(err, "mount overlay dir for container %s", ctrId)
}
if err := volume_mount.ChangeDirOwnerDirectly(mergedDir, ov.FsUser, ov.FsGroup); err != nil {
return errors.Wrapf(err, "change dir owner")
}
return nil
}
func (p postOverlayHostPath) Unmount(d diskPostOverlay, pod volume_mount.IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, ov *apis.ContainerVolumeMountDiskPostOverlay, useLazy bool, cleanLayers bool) error {

View File

@@ -61,6 +61,8 @@ func (i postOverlayImage) convertToDiskOV(ov *apis.ContainerVolumeMountDiskPostO
return &apis.ContainerVolumeMountDiskPostOverlay{
HostLowerDir: []string{hostPath},
ContainerTargetDir: ctrPath,
FsUser: ov.FsUser,
FsGroup: ov.FsGroup,
}
}

View File

@@ -47,12 +47,19 @@ func ChangeDirOwner(pod IPodInfo, drv IVolumeMount, ctrId string, vol *hostapi.C
if err != nil {
return errors.Wrap(err, "GetRuntimeMountHostPath")
}
return ChangeDirOwnerDirectly(hostPath, vol.FsUser, vol.FsGroup)
}
func ChangeDirOwnerDirectly(hostPath string, fsUser, fsGroup *int64) error {
args := ""
if vol.FsUser != nil {
args = fmt.Sprintf("%d", *vol.FsUser)
if fsUser != nil {
args = fmt.Sprintf("%d", *fsUser)
}
if vol.FsGroup != nil {
args = fmt.Sprintf("%s:%d", args, *vol.FsGroup)
if fsGroup != nil {
args = fmt.Sprintf("%s:%d", args, *fsGroup)
}
if args == "" {
return nil
}
out, err := procutils.NewRemoteCommandAsFarAsPossible("chown", args, hostPath).Output()
if err != nil {

View File

@@ -65,8 +65,8 @@ func DoDeployGuestFs(rootfs fsdriver.IRootFsDriver, guestDesc *deployapi.GuestDe
hn = guestDesc.Name
domain = guestDesc.Domain
gid = guestDesc.Uuid
nics = fsdriver.ToServerNics(guestDesc.Nics)
nicsStandby = fsdriver.ToServerNics(guestDesc.NicsStandby)
nics = fsdriver.ToServerNics(guestDesc, guestDesc.Nics)
nicsStandby = fsdriver.ToServerNics(guestDesc, guestDesc.NicsStandby)
partition = rootfs.GetPartition()
releaseInfo = rootfs.GetReleaseInfo(partition)
)

View File

@@ -959,8 +959,16 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics
cmds.WriteString(" netmask 255.255.255.255\n")
cmds.WriteString("\n")
} else if nicDesc.Manual {
netmask := netutils2.Netlen2Mask(int(nicDesc.Masklen))
ifname := nicDesc.Name
cmds.WriteString(fmt.Sprintf("iface %s inet static\n", nicDesc.Name))
if nicDesc.VlanInterface {
cmds.WriteString("\n")
ifname = fmt.Sprintf("%s.%d", nicDesc.Name, nicDesc.Vlan)
cmds.WriteString(fmt.Sprintf("auto %s\n", ifname))
cmds.WriteString(fmt.Sprintf("iface %s inet static\n", ifname))
}
netmask := netutils2.Netlen2Mask(int(nicDesc.Masklen))
cmds.WriteString(fmt.Sprintf(" address %s\n", nicDesc.Ip))
cmds.WriteString(fmt.Sprintf(" netmask %s\n", netmask))
cmds.WriteString(fmt.Sprintf(" hwaddress ether %s\n", nicDesc.Mac))
@@ -990,7 +998,7 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics
}
cmds.WriteString("\n")
if len(nicDesc.Ip6) > 0 {
cmds.WriteString(fmt.Sprintf("iface %s inet6 static\n", nicDesc.Name))
cmds.WriteString(fmt.Sprintf("iface %s inet6 static\n", ifname))
cmds.WriteString(fmt.Sprintf(" address %s\n", nicDesc.Ip6))
cmds.WriteString(fmt.Sprintf(" netmask %d\n", nicDesc.Masklen6))
if len(nicDesc.Gateway6) > 0 && nicDesc.Ip == mainIp {
@@ -1426,54 +1434,60 @@ func (r *sRedhatLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics
cmds.WriteString(netutils2.PSEUDO_VIP)
cmds.WriteString("\n")
} else if nicDesc.Manual {
netmask := netutils2.Netlen2Mask(int(nicDesc.Masklen))
cmds.WriteString("BOOTPROTO=none\n")
cmds.WriteString("NETMASK=")
cmds.WriteString(netmask)
cmds.WriteString("\n")
cmds.WriteString("IPADDR=")
cmds.WriteString(nicDesc.Ip)
cmds.WriteString("\n")
if len(nicDesc.Gateway) > 0 && nicDesc.Ip == mainIp {
cmds.WriteString("GATEWAY=")
cmds.WriteString(nicDesc.Gateway)
if nicDesc.VlanInterface {
if err := r.deployVlanNetworkingScripts(rootFs, scriptPath, mainIp, nicCnt, nicDesc); err != nil {
return errors.Wrap(err, "deployVlanNetworkingScripts")
}
} else {
netmask := netutils2.Netlen2Mask(int(nicDesc.Masklen))
cmds.WriteString("NETMASK=")
cmds.WriteString(netmask)
cmds.WriteString("\n")
}
var routes = make([][]string, 0)
routes = netutils2.AddNicRoutes(routes, nicDesc, mainIp, nicCnt)
var rtbl strings.Builder
for _, r := range routes {
rtbl.WriteString(r[0])
rtbl.WriteString(" via ")
rtbl.WriteString(r[1])
rtbl.WriteString(" dev ")
rtbl.WriteString(nicDesc.Name)
rtbl.WriteString("\n")
}
rtblStr := rtbl.String()
if len(rtblStr) > 0 {
var fn = fmt.Sprintf("%s/route-%s", scriptPath, nicDesc.Name)
if err := rootFs.FilePutContents(fn, rtblStr, false, false); err != nil {
return err
cmds.WriteString("IPADDR=")
cmds.WriteString(nicDesc.Ip)
cmds.WriteString("\n")
if len(nicDesc.Gateway) > 0 && nicDesc.Ip == mainIp {
cmds.WriteString("GATEWAY=")
cmds.WriteString(nicDesc.Gateway)
cmds.WriteString("\n")
}
}
dnslist := netutils2.GetNicDns(nicDesc)
if len(dnslist) > 0 {
cmds.WriteString("PEERDNS=yes\n")
for i := 0; i < len(dnslist); i++ {
cmds.WriteString(fmt.Sprintf("DNS%d=%s\n", i+1, dnslist[i]))
var routes = make([][]string, 0)
routes = netutils2.AddNicRoutes(routes, nicDesc, mainIp, nicCnt)
var rtbl strings.Builder
for _, r := range routes {
rtbl.WriteString(r[0])
rtbl.WriteString(" via ")
rtbl.WriteString(r[1])
rtbl.WriteString(" dev ")
rtbl.WriteString(nicDesc.Name)
rtbl.WriteString("\n")
}
if len(nicDesc.Domain) > 0 {
cmds.WriteString(fmt.Sprintf("DOMAIN=%s\n", nicDesc.Domain))
rtblStr := rtbl.String()
if len(rtblStr) > 0 {
var fn = fmt.Sprintf("%s/route-%s", scriptPath, nicDesc.Name)
if err := rootFs.FilePutContents(fn, rtblStr, false, false); err != nil {
return err
}
}
}
if len(nicDesc.Ip6) > 0 {
cmds.WriteString("IPV6INIT=yes\n")
cmds.WriteString("DHCPV6C=no\n")
cmds.WriteString("IPV6_AUTOCONF=no\n")
cmds.WriteString(fmt.Sprintf("IPV6ADDR=%s/%d\n", nicDesc.Ip6, nicDesc.Masklen6))
if len(nicDesc.Gateway6) > 0 {
cmds.WriteString(fmt.Sprintf("IPV6_DEFAULTGW=%s\n", nicDesc.Gateway6))
dnslist := netutils2.GetNicDns(nicDesc)
if len(dnslist) > 0 {
cmds.WriteString("PEERDNS=yes\n")
for i := 0; i < len(dnslist); i++ {
cmds.WriteString(fmt.Sprintf("DNS%d=%s\n", i+1, dnslist[i]))
}
if len(nicDesc.Domain) > 0 {
cmds.WriteString(fmt.Sprintf("DOMAIN=%s\n", nicDesc.Domain))
}
}
if len(nicDesc.Ip6) > 0 {
cmds.WriteString("IPV6INIT=yes\n")
cmds.WriteString("DHCPV6C=no\n")
cmds.WriteString("IPV6_AUTOCONF=no\n")
cmds.WriteString(fmt.Sprintf("IPV6ADDR=%s/%d\n", nicDesc.Ip6, nicDesc.Masklen6))
if len(nicDesc.Gateway6) > 0 {
cmds.WriteString(fmt.Sprintf("IPV6_DEFAULTGW=%s\n", nicDesc.Gateway6))
}
}
}
} else {
@@ -1499,6 +1513,75 @@ func (r *sRedhatLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics
return nil
}
func (r *sRedhatLikeRootFs) deployVlanNetworkingScripts(rootFs IDiskPartition, scriptPath, mainIp string, nicCnt int, nicDesc *types.SServerNic) error {
if nicDesc.Vlan <= 1 {
return nil
}
var cmds strings.Builder
cmds.WriteString("BOOTPROTO=none\n")
cmds.WriteString(fmt.Sprintf("DEVICE=%s.%d\n", nicDesc.Name, nicDesc.Vlan))
cmds.WriteString(fmt.Sprintf("NAME=%s.%d\n", nicDesc.Name, nicDesc.Vlan))
cmds.WriteString("ONBOOT=yes\n")
if r.isNetworkManagerEnabled(rootFs) {
cmds.WriteString("NM_CONTROLLED=yes\n")
} else {
cmds.WriteString("NM_CONTROLLED=no\n")
}
cmds.WriteString("USERCTL=no\n")
if nicDesc.Mtu > 0 {
cmds.WriteString(fmt.Sprintf("MTU=%d\n", nicDesc.Mtu))
}
netmask := netutils2.Netlen2Mask(int(nicDesc.Masklen))
cmds.WriteString("NETMASK=")
cmds.WriteString(netmask)
cmds.WriteString("\n")
cmds.WriteString("IPADDR=")
cmds.WriteString(nicDesc.Ip)
cmds.WriteString("\n")
if len(nicDesc.Gateway) > 0 && nicDesc.Ip == mainIp {
cmds.WriteString("GATEWAY=")
cmds.WriteString(nicDesc.Gateway)
cmds.WriteString("\n")
}
var routes = make([][]string, 0)
routes = netutils2.AddNicRoutes(routes, nicDesc, mainIp, nicCnt)
var rtbl strings.Builder
for _, r := range routes {
rtbl.WriteString(r[0])
rtbl.WriteString(" via ")
rtbl.WriteString(r[1])
rtbl.WriteString(" dev ")
rtbl.WriteString(fmt.Sprintf("%s.%d", nicDesc.Name, nicDesc.Vlan))
rtbl.WriteString("\n")
}
rtblStr := rtbl.String()
if len(rtblStr) > 0 {
var fn = fmt.Sprintf("%s/route-%s", scriptPath, nicDesc.Name)
if err := rootFs.FilePutContents(fn, rtblStr, false, false); err != nil {
return err
}
}
dnslist := netutils2.GetNicDns(nicDesc)
if len(dnslist) > 0 {
cmds.WriteString("PEERDNS=yes\n")
for i := 0; i < len(dnslist); i++ {
cmds.WriteString(fmt.Sprintf("DNS%d=%s\n", i+1, dnslist[i]))
}
if len(nicDesc.Domain) > 0 {
cmds.WriteString(fmt.Sprintf("DOMAIN=%s\n", nicDesc.Domain))
}
}
cmds.WriteString("VLAN=yes\n")
var fn = fmt.Sprintf("%s/ifcfg-%s.%d", scriptPath, nicDesc.Name, nicDesc.Vlan)
log.Debugf("%s: %s", fn, cmds.String())
if err := rootFs.FilePutContents(fn, cmds.String(), false, false); err != nil {
return err
}
return nil
}
func (r *sRedhatLikeRootFs) DeployStandbyNetworkingScripts(rootFs IDiskPartition, nics, nicsStandby []*types.SServerNic) error {
if err := r.sLinuxRootFs.DeployStandbyNetworkingScripts(rootFs, nics, nicsStandby); err != nil {
return err

View File

@@ -35,12 +35,29 @@ func newNetplanNetwork(allNics []*types.SServerNic, bondNics []*types.SServerNic
nicCnt := len(allNics) - len(bondNics)
for _, nic := range allNics {
nicConf := getNetplanEthernetConfig(nic, false, mainIp, nicCnt)
if nicConf == nil {
continue
}
network.AddEthernet(nic.Name, nicConf)
if nic.VlanInterface {
ifname := fmt.Sprintf("%s.%d", nic.Name, nic.Vlan)
vlanConfig := &netplan.VlanConfig{
EthernetConfig: *nicConf,
Link: nic.Name,
Id: nic.Vlan,
}
network.AddVlan(ifname, vlanConfig)
ethConfig := &netplan.EthernetConfig{
DHCP4: false,
DHCP6: false,
MacAddress: nic.Mac,
Match: netplan.NewEthernetConfigMatchMac(nic.Mac),
}
network.AddEthernet(nic.Name, ethConfig)
} else {
network.AddEthernet(nic.Name, nicConf)
}
}
for _, bondNic := range bondNics {

View File

@@ -46,7 +46,7 @@ func findTeamingNic(nics []*types.SServerNic, mac string) *types.SServerNic {
return nil
}
func ToServerNics(nics []*deployapi.Nic) []*types.SServerNic {
func ToServerNics(guestDesc *deployapi.GuestDesc, nics []*deployapi.Nic) []*types.SServerNic {
ret := make([]*types.SServerNic, len(nics))
for i := 0; i < len(nics); i++ {
domain := nics[i].Domain
@@ -84,6 +84,9 @@ func ToServerNics(nics []*deployapi.Nic) []*types.SServerNic {
Masklen6: int(nics[i].Masklen6),
Gateway6: nics[i].Gateway6,
}
if guestDesc.Hypervisor == computeapi.HYPERVISOR_BAREMETAL && ret[i].Vlan > 1 {
ret[i].VlanInterface = true
}
}
return ret
}

View File

@@ -85,6 +85,73 @@ func (r *sSuseLikeRootFs) enableBondingModule(rootFs IDiskPartition, bondNics []
return rootFs.FilePutContents("/etc/modprobe.d/bonding.conf", content.String(), false, false)
}
func (r *sSuseLikeRootFs) deployVlanNetworkingScripts(rootFs IDiskPartition, scriptPath, mainIp string, nicCnt int, nicDesc *types.SServerNic) error {
var cmds strings.Builder
var ifname = fmt.Sprintf("%s.%d", nicDesc.Name, nicDesc.Vlan)
cmds.WriteString("STARTMODE=auto\n")
if nicDesc.Mtu > 0 {
cmds.WriteString(fmt.Sprintf("MTU=%d\n", nicDesc.Mtu))
}
cmds.WriteString("BOOTPROTO=static\n")
cmds.WriteString(fmt.Sprintf("IPADDR=%s/%d\n", nicDesc.Ip, nicDesc.Masklen))
if len(nicDesc.Ip6) > 0 {
cmds.WriteString("IPV6INIT=yes\n")
cmds.WriteString("IPV6_AUTOCONF=no\n")
cmds.WriteString(fmt.Sprintf("IPADDR_V6=%s/%d\n", nicDesc.Ip6, nicDesc.Masklen6))
}
cmds.WriteString(fmt.Sprintf("VLAN_ID=%d\n", nicDesc.Vlan))
cmds.WriteString(fmt.Sprintf("ETHERDEVICE=%s\n", nicDesc.Name))
var routes = make([][]string, 0)
var dnsSrv []string
routes = netutils2.AddNicRoutes(routes, nicDesc, mainIp, nicCnt)
if len(nicDesc.Gateway) > 0 && nicDesc.Ip == mainIp {
routes = append(routes, []string{
"default",
nicDesc.Gateway,
})
}
if len(nicDesc.Gateway6) > 0 && nicDesc.Ip == mainIp {
routes = append(routes, []string{
"default",
nicDesc.Gateway6,
})
}
var rtbl strings.Builder
for _, r := range routes {
rtbl.WriteString(r[0])
rtbl.WriteString(" ")
rtbl.WriteString(r[1])
rtbl.WriteString(" - ")
rtbl.WriteString(nicDesc.Name)
rtbl.WriteString("\n")
}
rtblStr := rtbl.String()
if len(rtblStr) > 0 {
var fn = fmt.Sprintf("/etc/sysconfig/network/ifroute-%s", ifname)
if err := rootFs.FilePutContents(fn, rtblStr, false, false); err != nil {
return err
}
}
dnslist := netutils2.GetNicDns(nicDesc)
for i := 0; i < len(dnslist); i++ {
if !utils.IsInArray(dnslist[i], dnsSrv) {
dnsSrv = append(dnsSrv, dnslist[i])
}
}
var fn = fmt.Sprintf("/etc/sysconfig/network/ifcfg-%s", ifname)
log.Debugf("%s: %s", fn, cmds.String())
if err := rootFs.FilePutContents(fn, cmds.String(), false, false); err != nil {
return err
}
return nil
}
func (r *sSuseLikeRootFs) deployNetworkingScripts(rootFs IDiskPartition, nics []*types.SServerNic) error {
if err := r.sLinuxRootFs.DeployNetworkingScripts(rootFs, nics); err != nil {
return err

View File

@@ -20,6 +20,7 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/hostman/guestman/desc"
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
"yunion.io/x/onecloud/pkg/hostman/monitor"
"yunion.io/x/onecloud/pkg/httperrors"
@@ -107,7 +108,6 @@ func (m *SGuestManager) QgaSetNetwork(ctx context.Context, params interface{}) (
Gateway: input.Gateway,
}
//func (m *SGuestManager) QgaSetNetwork(netmod *monitor.NetworkModify, sid string, execTimeout int) (string, error) {
guest, err := m.checkAndInitGuestQga(input.Sid)
if err != nil {
return nil, err
@@ -144,3 +144,15 @@ func (m *SGuestManager) QgaGetOsInfo(sid string) (jsonutils.JSONObject, error) {
}
return jsonutils.Marshal(res), nil
}
func (guest *SKVMGuestInstance) QgaAddNicsConfigure(addNics []*desc.SGuestNetwork) error {
if guest.guestAgent == nil {
if err := guest.InitQga(); err != nil {
return errors.Wrap(err, "init qga")
}
}
if err := guest.guestAgent.GuestPing(1); err != nil {
return errors.Wrap(err, "Qga ping")
}
return guest.guestAgent.QgaDeployNics(deployapi.GuestNicsToServerNics(addNics))
}

View File

@@ -1072,7 +1072,8 @@ func (m *SGuestManager) GuestSync(ctx context.Context, params interface{}) (json
}
fwOnly := jsonutils.QueryBoolean(syncParams.Body, "fw_only", false)
return guest.SyncConfig(ctx, guestDesc, fwOnly)
setUefiBootOrder := jsonutils.QueryBoolean(syncParams.Body, "set_uefi_boot_order", false)
return guest.SyncConfig(ctx, guestDesc, fwOnly, setUefiBootOrder)
}
return nil, nil
}

View File

@@ -615,11 +615,34 @@ type SGuestNetworkSyncTask struct {
addNics []*desc.SGuestNetwork
errors []error
addNicMacs []string
addNicConfs []*monitor.NetworkModify
callback func(...error)
}
func (n *SGuestNetworkSyncTask) Start(callback func(...error)) {
n.callback = callback
if len(n.addNics) > 0 {
nics := make([]*desc.SGuestNetwork, 0)
nics = append(nics, n.guest.Desc.Nics...)
nics = append(nics, n.addNics...)
if err := n.guest.QgaAddNicsConfigure(nics); err != nil {
log.Errorf("QgaAddNicsConfigure failed %s", err)
} else {
addNicMacs := make([]string, 0)
addNicConfs := make([]*monitor.NetworkModify, 0)
for i := range n.addNics {
addNicMacs = append(addNicMacs, n.addNics[i].Mac)
addNicConfs = append(addNicConfs, &monitor.NetworkModify{
Ipmask: fmt.Sprintf("%s/%d", n.addNics[i].Ip, n.addNics[i].Masklen),
Gateway: n.addNics[i].Gateway,
})
}
n.addNicMacs = addNicMacs
n.addNicConfs = addNicConfs
}
}
n.syncNetworkConf()
}
@@ -633,10 +656,59 @@ func (n *SGuestNetworkSyncTask) syncNetworkConf() {
n.addNics = n.addNics[:len(n.addNics)-1]
n.addNic(nic)
} else {
if len(n.addNicMacs) > 0 {
// try restart added nics, wait for added nic ready
time.Sleep(3 * time.Second)
if err := n.qgaRestartAddedNics(); err != nil {
log.Errorf("failed qgaRestartAddedNics")
}
}
n.callback(n.errors...)
}
}
func (n *SGuestNetworkSyncTask) qgaRestartAddedNics() error {
err := n.qgaGetAddedNicDevs()
if err != nil {
return err
}
for i := range n.addNicConfs {
if n.addNicConfs[i].Device != "" {
err = n.guest.guestAgent.QgaRestartNetwork(n.addNicConfs[i])
if err != nil {
log.Errorf("Failed QgaRestartNetwork %s %s", n.addNicConfs[i].Device, err)
}
}
}
return nil
}
func (n *SGuestNetworkSyncTask) qgaGetAddedNicDevs() error {
data, err := n.guest.guestAgent.QgaGetNetwork()
if err != nil {
return errors.Wrap(err, "QgaGetNetwork")
}
var parsedData []api.IfnameDetail
ifnames, err := jsonutils.Parse(data)
if err != nil {
return errors.Wrapf(err, "parse qga network output %s", data)
}
err = ifnames.Unmarshal(&parsedData)
if err != nil {
return errors.Wrap(err, "unmarshal ifnames")
}
for i := range n.addNicMacs {
for j := range parsedData {
if n.addNicMacs[i] == parsedData[j].HardwareAddress {
n.addNicConfs[i].Device = parsedData[j].Name
break
}
}
}
return nil
}
func (n *SGuestNetworkSyncTask) removeNic(nic *desc.SGuestNetwork) {
callback := func(res string) {
if len(res) > 0 && !strings.Contains(res, "not found") {
@@ -794,7 +866,7 @@ func (n *SGuestNetworkSyncTask) onDeviceAdd(nic *desc.SGuestNetwork) {
func NewGuestNetworkSyncTask(
guest *SKVMGuestInstance, delNics, addNics []*desc.SGuestNetwork,
) *SGuestNetworkSyncTask {
return &SGuestNetworkSyncTask{guest, delNics, addNics, make([]error, 0), nil}
return &SGuestNetworkSyncTask{guest, delNics, addNics, make([]error, 0), nil, nil, nil}
}
/**

View File

@@ -189,7 +189,7 @@ func (h startStatHelper) removeStatFile(fp string) error {
if !fileutils2.Exists(fp) {
return nil
}
if err := os.Remove(fp); err != nil {
if err := os.Remove(fp); err != nil && !strings.Contains(err.Error(), "no such file or directory") {
return errors.Wrapf(err, "remove file %s", fp)
}
return nil
@@ -345,7 +345,7 @@ func (s *sPodGuestInstance) getStatus(ctx context.Context, defaultStatus string)
continue
}
if cs != nil {
status = GetPodStatusByContainerStatus(status, cStatus)
status = GetPodStatusByContainerStatus(status, cStatus, s.IsPrimaryContainer(c.Id))
}
}
return status
@@ -408,7 +408,7 @@ func (s *sPodGuestInstance) GetUploadStatus(ctx context.Context, reason string)
}
}
cStatuss[c.Id] = ctrStatusInput
status = GetPodStatusByContainerStatus(status, cStatus)
status = GetPodStatusByContainerStatus(status, cStatus, s.IsPrimaryContainer(c.Id))
}
if len(errs) > 0 {
log.Errorf("get upload status error: %v", errors.NewAggregate(errs))
@@ -1069,7 +1069,7 @@ func (s *sPodGuestInstance) PostLoad(m *SGuestManager) error {
return LoadGuestCpuset(m, s)
}
func (s *sPodGuestInstance) SyncConfig(ctx context.Context, guestDesc *desc.SGuestDesc, fwOnly bool) (jsonutils.JSONObject, error) {
func (s *sPodGuestInstance) SyncConfig(ctx context.Context, guestDesc *desc.SGuestDesc, fwOnly, setUefiBootOrder bool) (jsonutils.JSONObject, error) {
if err := SaveDesc(s, guestDesc); err != nil {
return nil, errors.Wrap(err, "SaveDesc")
}
@@ -2203,6 +2203,9 @@ func (s *sPodGuestInstance) tarGzDir(input *hostapi.ContainerSaveVolumeMountToIm
dirPath = strings.Join(input.VolumeMountDirs, " ")
}
cmd := fmt.Sprintf("tar -czf %s -C %s %s", outputFp, hostPath, dirPath)
if input.VolumeMountPrefix != "" {
cmd += fmt.Sprintf(" --transform 's,^,%s/,'", input.VolumeMountPrefix)
}
if out, err := procutils.NewRemoteCommandAsFarAsPossible("sh", "-c", cmd).Output(); err != nil {
return "", errors.Wrapf(err, "%s: %s", cmd, out)
}
@@ -2460,7 +2463,7 @@ func (s *sPodGuestInstance) tarHostDir(srcDir, targetPath string,
if len(includeFiles) > 0 {
includeStr = strings.Join(includeFiles, " ")
}
cmd := fmt.Sprintf("%s --warning=no-file-changed --ignore-failed-read -cf %s -C %s %s", baseCmd, targetPath, srcDir, includeStr)
cmd := fmt.Sprintf("%s --ignore-failed-read -cf %s -C %s %s", baseCmd, targetPath, srcDir, includeStr)
log.Infof("[%s] tar cmd: %s", s.GetName(), cmd)
if out, err := procutils.NewRemoteCommandAsFarAsPossible("sh", "-c", cmd).Output(); err != nil {
outErr := errors.Wrapf(err, "%s: %s", cmd, out)

View File

@@ -232,12 +232,15 @@ func (t *localPodRestartTask) Dump() string {
return fmt.Sprintf("pod restart task %s/%s", t.pod.GetId(), t.pod.GetName())
}
func GetPodStatusByContainerStatus(status string, cStatus string) string {
func GetPodStatusByContainerStatus(status string, cStatus string, isPrimary bool) string {
if cStatus == computeapi.CONTAINER_STATUS_CRASH_LOOP_BACK_OFF {
status = computeapi.POD_STATUS_CRASH_LOOP_BACK_OFF
}
if cStatus == computeapi.CONTAINER_STATUS_EXITED && status != computeapi.VM_READY {
status = computeapi.POD_STATUS_CONTAINER_EXITED
if isPrimary {
status = computeapi.VM_READY
}
}
return status
}

View File

@@ -130,6 +130,10 @@ func (m *SGuestManager) startContainer(obj *sPodGuestInstance, ctr *hostapi.Cont
return nil
}
func (m *SGuestManager) GetPleg() pleg.PodLifecycleEventGenerator {
return m.pleg
}
func (m *SGuestManager) syncContainerLoop(plegCh chan *pleg.PodLifecycleEvent) {
log.Infof("start sync container loop")
for {
@@ -181,7 +185,7 @@ func (m *SGuestManager) syncContainerLoopIteration(plegCh chan *pleg.PodLifecycl
if ctrObj != nil {
ccStatus, _, _ = podMan.GetContainerStatus(ctx, ctrObj.Id)
}
if !isInternalStopped || ccStatus == computeapi.CONTAINER_STATUS_EXITED {
if !isInternalStopped && ccStatus == computeapi.CONTAINER_STATUS_EXITED {
podStatus, err := m.podCache.Get(e.Id)
if err != nil {
log.Errorf("get pod %s status error: %v", e.Id, err)
@@ -208,7 +212,7 @@ func (m *SGuestManager) syncContainerLoopIteration(plegCh chan *pleg.PodLifecycl
}
log.Infof("sync pod %s container %s status: %s", e.Id, ctrCriId, reason)
// 如果是 primary container 退出,就退出其他容器
if ctrObj != nil && podMan.IsPrimaryContainer(ctrObj.Id) && ccStatus == computeapi.CONTAINER_STATUS_EXITED {
if ctrObj != nil && !isInternalStopped && podMan.IsPrimaryContainer(ctrObj.Id) && ccStatus == computeapi.CONTAINER_STATUS_EXITED {
reason = fmt.Sprintf("stop all containers when primary container %s exited", ctrObj.Name)
if err := podMan.StopAll(context.Background()); err != nil {
log.Errorf("stop all pod containers error: %s", err.Error())

View File

@@ -1942,8 +1942,10 @@ func (s *SKVMGuestInstance) DeployFs(ctx context.Context, userCred mcclient.Toke
}
var sysDisk storageman.IDisk
disks := s.Desc.Disks
var diskPaths = make([]string, len(disks))
for i := range disks {
diskPath := disks[i].Path
diskPaths[i] = diskPath
// GetDiskByPath will probe disks
disk, err := storageman.GetManager().GetDiskByPath(diskPath)
if err != nil {
@@ -1954,11 +1956,13 @@ func (s *SKVMGuestInstance) DeployFs(ctx context.Context, userCred mcclient.Toke
diskInfo.Path = disk.GetPath()
sysDisk = disk
}
disks[i].Path = disk.GetPath()
}
ret, err := sysDisk.DeployGuestFs(&diskInfo, s.Desc, deployInfo)
for i := range disks {
diskPath := disks[i].Path
for i := range diskPaths {
diskPath := diskPaths[i]
disks[i].Path = diskPath
disk, e := storageman.GetManager().GetDiskByPath(diskPath)
if e != nil {
log.Errorf("failed get disk bypath %s %s", diskPath, e)
@@ -2516,9 +2520,7 @@ func (s *SKVMGuestInstance) onNicChange(oldNic, newNic *desc.SGuestNetwork) erro
return nil
}
func (s *SKVMGuestInstance) SyncConfig(
ctx context.Context, guestDesc *desc.SGuestDesc, fwOnly bool,
) (jsonutils.JSONObject, error) {
func (s *SKVMGuestInstance) SyncConfig(ctx context.Context, guestDesc *desc.SGuestDesc, fwOnly, setUefiBootOrder bool) (jsonutils.JSONObject, error) {
var delDisks, addDisks []*desc.SGuestDisk
var delNetworks, addNetworks []*desc.SGuestNetwork
var changedNetworks [][2]*desc.SGuestNetwork
@@ -2549,6 +2551,12 @@ func (s *SKVMGuestInstance) SyncConfig(
}
if !s.IsRunning() {
if setUefiBootOrder && s.getBios() == api.VM_BOOT_MODE_UEFI {
if err := s.setUefiBootOrder(ctx); err != nil {
log.Errorf("failed set uefi boot order %s", err)
return nil, errors.Wrap(err, "setUefiBootOrder")
}
}
return nil, nil
}
@@ -2598,6 +2606,13 @@ func (s *SKVMGuestInstance) SyncConfig(
}
}
if setUefiBootOrder && s.getBios() == api.VM_BOOT_MODE_UEFI {
if err := s.setUefiBootOrder(ctx); err != nil {
log.Errorf("failed set uefi boot order %s", err)
errs = append(errs, err)
}
}
if len(errs) == 0 {
hostutils.TaskComplete(ctx, nil)
} else {

View File

@@ -15,6 +15,7 @@
package guestman
import (
"context"
"fmt"
"net"
"path"
@@ -36,6 +37,9 @@ import (
"yunion.io/x/onecloud/pkg/hostman/guestman/desc"
"yunion.io/x/onecloud/pkg/hostman/guestman/qemu"
qemucerts "yunion.io/x/onecloud/pkg/hostman/guestman/qemu/certs"
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
"yunion.io/x/onecloud/pkg/hostman/hostdeployer/deployclient"
"yunion.io/x/onecloud/pkg/hostman/hostdeployer/uefi"
"yunion.io/x/onecloud/pkg/hostman/monitor"
"yunion.io/x/onecloud/pkg/hostman/options"
"yunion.io/x/onecloud/pkg/hostman/storageman"
@@ -244,7 +248,7 @@ func (s *SKVMGuestInstance) getMachine() string {
func (s *SKVMGuestInstance) getBios() string {
bios := s.Desc.Bios
if bios == "" {
bios = "bios"
bios = api.VM_BOOT_MODE_BIOS
}
return bios
}
@@ -331,10 +335,12 @@ func (s *SKVMGuestInstance) extraOptions() string {
case *jsonutils.JSONArray:
for i := 0; i < jsonV.Size(); i++ {
vAtI, _ := jsonV.GetAt(i)
cmd += fmt.Sprintf(" -%s %s", k, vAtI.String())
vStr, _ := vAtI.GetString()
cmd += fmt.Sprintf(" -%s %s", k, vStr)
}
default:
cmd += fmt.Sprintf(" -%s %s", k, v.String())
vstr, _ := v.GetString()
cmd += fmt.Sprintf(" -%s %s", k, vstr)
}
}
return cmd
@@ -499,6 +505,7 @@ function nic_mtu() {
if s.Desc.Bios == qemu.BIOS_UEFI {
if len(input.OVMFPath) == 0 {
input.OVMFPath = options.HostOptions.OvmfPath
input.OVMFVarsPath = options.HostOptions.OvmfVarsPath
}
}
@@ -1114,3 +1121,69 @@ func (s *SKVMGuestInstance) vfioDevCount() int {
}
return res
}
func (s *SKVMGuestInstance) getOvmfVarsPath() string {
return path.Join(s.HomeDir(), "OVMF_VARS.fd")
}
func (s *SKVMGuestInstance) getDiskBootOrderType(driver string) uefi.OvmfDevicePathType {
switch driver {
case qemu.DISK_DRIVER_VIRTIO:
return uefi.DEVICE_TYPE_PCI
case qemu.DISK_DRIVER_SCSI, qemu.DISK_DRIVER_PVSCSI:
return uefi.DEVICE_TYPE_SCSI
case qemu.DISK_DRIVER_IDE:
if s.manager.host.IsAarch64() {
return uefi.DEVICE_TYPE_SCSI
}
return uefi.DEVICE_TYPE_IDE
case qemu.DISK_DRIVER_SATA:
if s.manager.host.IsAarch64() {
return uefi.DEVICE_TYPE_SCSI
}
return uefi.DEVICE_TYPE_SATA
}
return uefi.DEVICE_TYPE_UNKNOWN
}
func (s *SKVMGuestInstance) getCdromBootOrder() uefi.OvmfDevicePathType {
if s.manager.host.IsAarch64() {
return uefi.DEVICE_TYPE_SCSI_CDROM
}
return uefi.DEVICE_TYPE_CDROM
}
func (s *SKVMGuestInstance) setUefiBootOrder(ctx context.Context) error {
params := &deployapi.OvmfBootOrderParams{
OvmfVarsPath: s.getOvmfVarsPath(),
}
devs := make([]*deployapi.BootDevices, 0)
for i := range s.Desc.Cdroms {
if s.Desc.Cdroms[i].BootIndex == nil || *s.Desc.Disks[i].BootIndex < 0 {
continue
}
dev := &deployapi.BootDevices{
BootOrder: int32(*s.Desc.Cdroms[i].BootIndex),
AttachOrder: int32(s.Desc.Cdroms[i].Ordinal),
DevType: int32(s.getCdromBootOrder()),
}
devs = append(devs, dev)
}
for i := range s.Desc.Disks {
if s.Desc.Disks[i].BootIndex == nil || *s.Desc.Disks[i].BootIndex < 0 {
continue
}
dev := &deployapi.BootDevices{
BootOrder: int32(*s.Desc.Disks[i].BootIndex),
AttachOrder: int32(s.Desc.Disks[i].Index),
DevType: int32(s.getDiskBootOrderType(s.Desc.Disks[i].Driver)),
}
devs = append(devs, dev)
}
params.Devs = devs
_, err := deployclient.GetDeployClient().SetOvmfBootOrder(ctx, params)
if err != nil {
return errors.Wrap(err, "SetOvmfBootOrder")
}
return nil
}

View File

@@ -665,6 +665,7 @@ type GenerateStartOptionsInput struct {
OVNIntegrationBridge string
Devices []string
OVMFPath string
OVMFVarsPath string
VNCPort uint
VNCPassword bool
EnableLog bool
@@ -763,7 +764,7 @@ func GenerateStartOptions(
if input.OVMFPath == "" {
return "", errors.Errorf("input OVMF path is empty")
}
fmOpt, err := drvOpt.BIOS(input.OVMFPath, input.HomeDir)
fmOpt, err := drvOpt.BIOS(input.OVMFPath, input.OVMFVarsPath, input.HomeDir)
if err != nil {
return "", errors.Wrap(err, "bios option")
}
@@ -862,11 +863,6 @@ func GenerateStartOptions(
// pidfile
opts = append(opts, drvOpt.Pidfile(input.PidFilePath))
// extra options
if len(input.ExtraOptions) != 0 {
opts = append(opts, input.ExtraOptions...)
}
// qga
// opts = append(opts, drvOpt.QGA(input.HomeDir)...)
if input.GuestDesc.Qga != nil {
@@ -891,5 +887,10 @@ func GenerateStartOptions(
opts = append(opts, generatePvpanicDeviceOption(input.GuestDesc.Pvpanic))
}
// move extra options to end of cmdline
if len(input.ExtraOptions) != 0 {
opts = append(opts, input.ExtraOptions...)
}
return strings.Join(opts, " "), nil
}

View File

@@ -97,7 +97,7 @@ type QemuOptions interface {
MemDev(sizeMB uint64) string
MemFd(sizeMB uint64) string
Boot(order *string, enableMenu bool) string
BIOS(ovmfPath, homedir string) (string, error)
BIOS(ovmfPath, ovmfVarsPath, homedir string) (string, error)
Device(devStr string) string
Drive(driveStr string) string
Chardev(backend string, id string, name string) string
@@ -272,17 +272,21 @@ func (o baseOptions) Boot(order *string, enableMenu bool) string {
return fmt.Sprintf("-boot %s", strings.Join(opts, ","))
}
func (o baseOptions) BIOS(ovmfPath, homedir string) (string, error) {
ovmfVarsPath := path.Join(homedir, "OVMF_VARS.fd")
if !fileutils2.Exists(ovmfVarsPath) {
err := procutils.NewRemoteCommandAsFarAsPossible("cp", "-f", ovmfPath, ovmfVarsPath).Run()
func (o baseOptions) BIOS(ovmfPath, ovmfVarsPath, homedir string) (string, error) {
if ovmfVarsPath == "" || !fileutils2.Exists(ovmfVarsPath) {
ovmfVarsPath = ovmfPath
}
destOvmfVarsPath := path.Join(homedir, "OVMF_VARS.fd")
if !fileutils2.Exists(destOvmfVarsPath) {
err := procutils.NewRemoteCommandAsFarAsPossible("cp", "-f", ovmfVarsPath, destOvmfVarsPath).Run()
if err != nil {
return "", errors.Wrap(err, "failed copy ovmf vars")
}
}
return fmt.Sprintf(
"-drive if=pflash,format=raw,unit=0,file=%s,readonly=on -drive if=pflash,format=raw,unit=1,file=%s",
ovmfPath, ovmfVarsPath,
ovmfPath, destOvmfVarsPath,
), nil
}

View File

@@ -70,7 +70,7 @@ type GuestRuntimeInstance interface {
LoadDesc() error
PostLoad(m *SGuestManager) error
SyncConfig(ctx context.Context, guestDesc *desc.SGuestDesc, fwOnly bool) (jsonutils.JSONObject, error)
SyncConfig(ctx context.Context, guestDesc *desc.SGuestDesc, fwOnly, setUefiBootOrder bool) (jsonutils.JSONObject, error)
DoSnapshot(ctx context.Context, params *SDiskSnapshot) (jsonutils.JSONObject, error)
DeleteSnapshot(ctx context.Context, params *SDeleteDiskSnapshot) (jsonutils.JSONObject, error)
OnlineResizeDisk(ctx context.Context, disk storageman.IDisk, sizeMB int64)

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc-gen-go v1.20.0
// protoc v3.21.5
// source: deploy.proto
@@ -14,6 +14,7 @@ import (
reflect "reflect"
sync "sync"
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
@@ -25,6 +26,10 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type GuestDesc struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -1960,6 +1965,125 @@ func (x *EsxiDisksConnectionInfo) GetDisks() []*EsxiDiskInfo {
return nil
}
// cdrom, scsi cdrom, hard drive, scsi, pci
type BootDevices struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
BootOrder int32 `protobuf:"varint,1,opt,name=BootOrder,proto3" json:"BootOrder,omitempty"`
DevType int32 `protobuf:"varint,2,opt,name=DevType,proto3" json:"DevType,omitempty"`
AttachOrder int32 `protobuf:"varint,3,opt,name=AttachOrder,proto3" json:"AttachOrder,omitempty"`
}
func (x *BootDevices) Reset() {
*x = BootDevices{}
if protoimpl.UnsafeEnabled {
mi := &file_deploy_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BootDevices) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BootDevices) ProtoMessage() {}
func (x *BootDevices) ProtoReflect() protoreflect.Message {
mi := &file_deploy_proto_msgTypes[24]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BootDevices.ProtoReflect.Descriptor instead.
func (*BootDevices) Descriptor() ([]byte, []int) {
return file_deploy_proto_rawDescGZIP(), []int{24}
}
func (x *BootDevices) GetBootOrder() int32 {
if x != nil {
return x.BootOrder
}
return 0
}
func (x *BootDevices) GetDevType() int32 {
if x != nil {
return x.DevType
}
return 0
}
func (x *BootDevices) GetAttachOrder() int32 {
if x != nil {
return x.AttachOrder
}
return 0
}
type OvmfBootOrderParams struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
OvmfVarsPath string `protobuf:"bytes,1,opt,name=OvmfVarsPath,proto3" json:"OvmfVarsPath,omitempty"`
Devs []*BootDevices `protobuf:"bytes,2,rep,name=devs,proto3" json:"devs,omitempty"`
}
func (x *OvmfBootOrderParams) Reset() {
*x = OvmfBootOrderParams{}
if protoimpl.UnsafeEnabled {
mi := &file_deploy_proto_msgTypes[25]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *OvmfBootOrderParams) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*OvmfBootOrderParams) ProtoMessage() {}
func (x *OvmfBootOrderParams) ProtoReflect() protoreflect.Message {
mi := &file_deploy_proto_msgTypes[25]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use OvmfBootOrderParams.ProtoReflect.Descriptor instead.
func (*OvmfBootOrderParams) Descriptor() ([]byte, []int) {
return file_deploy_proto_rawDescGZIP(), []int{25}
}
func (x *OvmfBootOrderParams) GetOvmfVarsPath() string {
if x != nil {
return x.OvmfVarsPath
}
return ""
}
func (x *OvmfBootOrderParams) GetDevs() []*BootDevices {
if x != nil {
return x.Devs
}
return nil
}
var File_deploy_proto protoreflect.FileDescriptor
var file_deploy_proto_rawDesc = []byte{
@@ -2235,40 +2359,56 @@ var file_deploy_proto_rawDesc = []byte{
0x73, 0x6b, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66,
0x6f, 0x12, 0x28, 0x0a, 0x05, 0x64, 0x69, 0x73, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b,
0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x64, 0x69, 0x73, 0x6b, 0x73, 0x32, 0xc6, 0x03, 0x0a, 0x0b,
0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0d, 0x44,
0x65, 0x70, 0x6c, 0x6f, 0x79, 0x47, 0x75, 0x65, 0x73, 0x74, 0x46, 0x73, 0x12, 0x12, 0x2e, 0x61,
0x70, 0x69, 0x73, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73,
0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x47, 0x75,
0x65, 0x73, 0x74, 0x46, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a,
0x08, 0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x46, 0x73, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x73,
0x2e, 0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x46, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a,
0x0b, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x2d, 0x0a, 0x08,
0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x46, 0x73, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e,
0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x46, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x0b,
0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x44, 0x0a, 0x0c, 0x53,
0x61, 0x76, 0x65, 0x54, 0x6f, 0x47, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x2e, 0x61, 0x70,
0x69, 0x73, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x54, 0x6f, 0x47, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x50,
0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x53, 0x61, 0x76,
0x65, 0x54, 0x6f, 0x47, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x3d, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49,
0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65,
0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x50, 0x72, 0x61, 0x6d, 0x61, 0x73, 0x1a,
0x0f, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f,
0x12, 0x4f, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x73, 0x78, 0x69, 0x44,
0x69, 0x73, 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x6e,
0x65, 0x63, 0x74, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x50, 0x61, 0x72, 0x61,
0x6d, 0x73, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69,
0x73, 0x6b, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66,
0x6f, 0x12, 0x41, 0x0a, 0x13, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45,
0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x12, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e,
0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45,
0x6d, 0x70, 0x74, 0x79, 0x42, 0x34, 0x5a, 0x32, 0x79, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x2e, 0x69,
0x6f, 0x2f, 0x78, 0x2f, 0x6f, 0x6e, 0x65, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x70, 0x6b, 0x67,
0x2f, 0x68, 0x6f, 0x73, 0x74, 0x6d, 0x61, 0x6e, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x64, 0x65, 0x70,
0x6c, 0x6f, 0x79, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x64, 0x69, 0x73, 0x6b, 0x73, 0x22, 0x67, 0x0a, 0x0b, 0x42,
0x6f, 0x6f, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x42, 0x6f,
0x6f, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x42,
0x6f, 0x6f, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x65, 0x76, 0x54,
0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x44, 0x65, 0x76, 0x54, 0x79,
0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65,
0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x4f,
0x72, 0x64, 0x65, 0x72, 0x22, 0x60, 0x0a, 0x13, 0x4f, 0x76, 0x6d, 0x66, 0x42, 0x6f, 0x6f, 0x74,
0x4f, 0x72, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x4f,
0x76, 0x6d, 0x66, 0x56, 0x61, 0x72, 0x73, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0c, 0x4f, 0x76, 0x6d, 0x66, 0x56, 0x61, 0x72, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12,
0x25, 0x0a, 0x04, 0x64, 0x65, 0x76, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e,
0x61, 0x70, 0x69, 0x73, 0x2e, 0x42, 0x6f, 0x6f, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73,
0x52, 0x04, 0x64, 0x65, 0x76, 0x73, 0x32, 0x82, 0x04, 0x0a, 0x0b, 0x44, 0x65, 0x70, 0x6c, 0x6f,
0x79, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0d, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79,
0x47, 0x75, 0x65, 0x73, 0x74, 0x46, 0x73, 0x12, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x44,
0x65, 0x70, 0x6c, 0x6f, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1b, 0x2e, 0x61, 0x70,
0x69, 0x73, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x47, 0x75, 0x65, 0x73, 0x74, 0x46, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x69,
0x7a, 0x65, 0x46, 0x73, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x52, 0x65, 0x73, 0x69,
0x7a, 0x65, 0x46, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69,
0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x2d, 0x0a, 0x08, 0x46, 0x6f, 0x72, 0x6d, 0x61,
0x74, 0x46, 0x73, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x61,
0x74, 0x46, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x73,
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x44, 0x0a, 0x0c, 0x53, 0x61, 0x76, 0x65, 0x54, 0x6f,
0x47, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x53, 0x61,
0x76, 0x65, 0x54, 0x6f, 0x47, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73,
0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x54, 0x6f, 0x47, 0x6c,
0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0e,
0x50, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a,
0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65,
0x49, 0x6e, 0x66, 0x6f, 0x50, 0x72, 0x61, 0x6d, 0x61, 0x73, 0x1a, 0x0f, 0x2e, 0x61, 0x70, 0x69,
0x73, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4f, 0x0a, 0x10, 0x43,
0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x12,
0x1c, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x73,
0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1d, 0x2e,
0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x43, 0x6f,
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x41, 0x0a, 0x13,
0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69,
0x73, 0x6b, 0x73, 0x12, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x73, 0x78, 0x69, 0x44,
0x69, 0x73, 0x6b, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e,
0x66, 0x6f, 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12,
0x3a, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4f, 0x76, 0x6d, 0x66, 0x42, 0x6f, 0x6f, 0x74, 0x4f, 0x72,
0x64, 0x65, 0x72, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x4f, 0x76, 0x6d, 0x66, 0x42,
0x6f, 0x6f, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x0b,
0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x34, 0x5a, 0x32, 0x79,
0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x2e, 0x69, 0x6f, 0x2f, 0x78, 0x2f, 0x6f, 0x6e, 0x65, 0x63, 0x6c,
0x6f, 0x75, 0x64, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x6d, 0x61, 0x6e, 0x2f,
0x68, 0x6f, 0x73, 0x74, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69,
0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -2283,7 +2423,7 @@ func file_deploy_proto_rawDescGZIP() []byte {
return file_deploy_proto_rawDescData
}
var file_deploy_proto_msgTypes = make([]protoimpl.MessageInfo, 24)
var file_deploy_proto_msgTypes = make([]protoimpl.MessageInfo, 26)
var file_deploy_proto_goTypes = []interface{}{
(*GuestDesc)(nil), // 0: apis.GuestDesc
(*Disk)(nil), // 1: apis.Disk
@@ -2309,6 +2449,8 @@ var file_deploy_proto_goTypes = []interface{}{
(*EsxiDiskInfo)(nil), // 21: apis.EsxiDiskInfo
(*ConnectEsxiDisksParams)(nil), // 22: apis.ConnectEsxiDisksParams
(*EsxiDisksConnectionInfo)(nil), // 23: apis.EsxiDisksConnectionInfo
(*BootDevices)(nil), // 24: apis.BootDevices
(*OvmfBootOrderParams)(nil), // 25: apis.OvmfBootOrderParams
}
var file_deploy_proto_depIdxs = []int32{
2, // 0: apis.GuestDesc.nics:type_name -> apis.Nic
@@ -2333,25 +2475,28 @@ var file_deploy_proto_depIdxs = []int32{
3, // 19: apis.ConnectEsxiDisksParams.vddk_info:type_name -> apis.VDDKConInfo
21, // 20: apis.ConnectEsxiDisksParams.access_info:type_name -> apis.EsxiDiskInfo
21, // 21: apis.EsxiDisksConnectionInfo.disks:type_name -> apis.EsxiDiskInfo
11, // 22: apis.DeployAgent.DeployGuestFs:input_type -> apis.DeployParams
12, // 23: apis.DeployAgent.ResizeFs:input_type -> apis.ResizeFsParams
15, // 24: apis.DeployAgent.FormatFs:input_type -> apis.FormatFsParams
17, // 25: apis.DeployAgent.SaveToGlance:input_type -> apis.SaveToGlanceParams
19, // 26: apis.DeployAgent.ProbeImageInfo:input_type -> apis.ProbeImageInfoPramas
22, // 27: apis.DeployAgent.ConnectEsxiDisks:input_type -> apis.ConnectEsxiDisksParams
23, // 28: apis.DeployAgent.DisconnectEsxiDisks:input_type -> apis.EsxiDisksConnectionInfo
9, // 29: apis.DeployAgent.DeployGuestFs:output_type -> apis.DeployGuestFsResponse
8, // 30: apis.DeployAgent.ResizeFs:output_type -> apis.Empty
8, // 31: apis.DeployAgent.FormatFs:output_type -> apis.Empty
18, // 32: apis.DeployAgent.SaveToGlance:output_type -> apis.SaveToGlanceResponse
20, // 33: apis.DeployAgent.ProbeImageInfo:output_type -> apis.ImageInfo
23, // 34: apis.DeployAgent.ConnectEsxiDisks:output_type -> apis.EsxiDisksConnectionInfo
8, // 35: apis.DeployAgent.DisconnectEsxiDisks:output_type -> apis.Empty
29, // [29:36] is the sub-list for method output_type
22, // [22:29] is the sub-list for method input_type
22, // [22:22] is the sub-list for extension type_name
22, // [22:22] is the sub-list for extension extendee
0, // [0:22] is the sub-list for field type_name
24, // 22: apis.OvmfBootOrderParams.devs:type_name -> apis.BootDevices
11, // 23: apis.DeployAgent.DeployGuestFs:input_type -> apis.DeployParams
12, // 24: apis.DeployAgent.ResizeFs:input_type -> apis.ResizeFsParams
15, // 25: apis.DeployAgent.FormatFs:input_type -> apis.FormatFsParams
17, // 26: apis.DeployAgent.SaveToGlance:input_type -> apis.SaveToGlanceParams
19, // 27: apis.DeployAgent.ProbeImageInfo:input_type -> apis.ProbeImageInfoPramas
22, // 28: apis.DeployAgent.ConnectEsxiDisks:input_type -> apis.ConnectEsxiDisksParams
23, // 29: apis.DeployAgent.DisconnectEsxiDisks:input_type -> apis.EsxiDisksConnectionInfo
25, // 30: apis.DeployAgent.SetOvmfBootOrder:input_type -> apis.OvmfBootOrderParams
9, // 31: apis.DeployAgent.DeployGuestFs:output_type -> apis.DeployGuestFsResponse
8, // 32: apis.DeployAgent.ResizeFs:output_type -> apis.Empty
8, // 33: apis.DeployAgent.FormatFs:output_type -> apis.Empty
18, // 34: apis.DeployAgent.SaveToGlance:output_type -> apis.SaveToGlanceResponse
20, // 35: apis.DeployAgent.ProbeImageInfo:output_type -> apis.ImageInfo
23, // 36: apis.DeployAgent.ConnectEsxiDisks:output_type -> apis.EsxiDisksConnectionInfo
8, // 37: apis.DeployAgent.DisconnectEsxiDisks:output_type -> apis.Empty
8, // 38: apis.DeployAgent.SetOvmfBootOrder:output_type -> apis.Empty
31, // [31:39] is the sub-list for method output_type
23, // [23:31] is the sub-list for method input_type
23, // [23:23] is the sub-list for extension type_name
23, // [23:23] is the sub-list for extension extendee
0, // [0:23] is the sub-list for field type_name
}
func init() { file_deploy_proto_init() }
@@ -2648,6 +2793,30 @@ func file_deploy_proto_init() {
return nil
}
}
file_deploy_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BootDevices); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_deploy_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*OvmfBootOrderParams); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
@@ -2655,7 +2824,7 @@ func file_deploy_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_deploy_proto_rawDesc,
NumEnums: 0,
NumMessages: 24,
NumMessages: 26,
NumExtensions: 0,
NumServices: 1,
},

View File

@@ -206,6 +206,19 @@ message EsxiDisksConnectionInfo {
repeated EsxiDiskInfo disks = 1;
}
// cdrom, scsi cdrom, hard drive, scsi, pci
message BootDevices {
int32 BootOrder = 1;
int32 DevType = 2;
int32 AttachOrder = 3;
}
message OvmfBootOrderParams {
string OvmfVarsPath = 1;
repeated BootDevices devs = 2;
}
service DeployAgent {
rpc DeployGuestFs (DeployParams) returns (DeployGuestFsResponse);
rpc ResizeFs (ResizeFsParams) returns (Empty);
@@ -214,4 +227,5 @@ service DeployAgent {
rpc ProbeImageInfo(ProbeImageInfoPramas) returns (ImageInfo);
rpc ConnectEsxiDisks(ConnectEsxiDisksParams) returns (EsxiDisksConnectionInfo);
rpc DisconnectEsxiDisks(EsxiDisksConnectionInfo) returns (Empty);
rpc SetOvmfBootOrder(OvmfBootOrderParams) returns (Empty);
}

View File

@@ -1,8 +1,4 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.21.5
// source: deploy.proto
package apis
@@ -16,7 +12,6 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// DeployAgentClient is the client API for DeployAgent service.
@@ -30,6 +25,7 @@ type DeployAgentClient interface {
ProbeImageInfo(ctx context.Context, in *ProbeImageInfoPramas, opts ...grpc.CallOption) (*ImageInfo, error)
ConnectEsxiDisks(ctx context.Context, in *ConnectEsxiDisksParams, opts ...grpc.CallOption) (*EsxiDisksConnectionInfo, error)
DisconnectEsxiDisks(ctx context.Context, in *EsxiDisksConnectionInfo, opts ...grpc.CallOption) (*Empty, error)
SetOvmfBootOrder(ctx context.Context, in *OvmfBootOrderParams, opts ...grpc.CallOption) (*Empty, error)
}
type deployAgentClient struct {
@@ -103,6 +99,15 @@ func (c *deployAgentClient) DisconnectEsxiDisks(ctx context.Context, in *EsxiDis
return out, nil
}
func (c *deployAgentClient) SetOvmfBootOrder(ctx context.Context, in *OvmfBootOrderParams, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
err := c.cc.Invoke(ctx, "/apis.DeployAgent/SetOvmfBootOrder", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// DeployAgentServer is the server API for DeployAgent service.
// All implementations must embed UnimplementedDeployAgentServer
// for forward compatibility
@@ -114,6 +119,7 @@ type DeployAgentServer interface {
ProbeImageInfo(context.Context, *ProbeImageInfoPramas) (*ImageInfo, error)
ConnectEsxiDisks(context.Context, *ConnectEsxiDisksParams) (*EsxiDisksConnectionInfo, error)
DisconnectEsxiDisks(context.Context, *EsxiDisksConnectionInfo) (*Empty, error)
SetOvmfBootOrder(context.Context, *OvmfBootOrderParams) (*Empty, error)
mustEmbedUnimplementedDeployAgentServer()
}
@@ -142,6 +148,9 @@ func (UnimplementedDeployAgentServer) ConnectEsxiDisks(context.Context, *Connect
func (UnimplementedDeployAgentServer) DisconnectEsxiDisks(context.Context, *EsxiDisksConnectionInfo) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DisconnectEsxiDisks not implemented")
}
func (UnimplementedDeployAgentServer) SetOvmfBootOrder(context.Context, *OvmfBootOrderParams) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetOvmfBootOrder not implemented")
}
func (UnimplementedDeployAgentServer) mustEmbedUnimplementedDeployAgentServer() {}
// UnsafeDeployAgentServer may be embedded to opt out of forward compatibility for this service.
@@ -152,7 +161,7 @@ type UnsafeDeployAgentServer interface {
}
func RegisterDeployAgentServer(s grpc.ServiceRegistrar, srv DeployAgentServer) {
s.RegisterService(&DeployAgent_ServiceDesc, srv)
s.RegisterService(&_DeployAgent_serviceDesc, srv)
}
func _DeployAgent_DeployGuestFs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
@@ -281,10 +290,25 @@ func _DeployAgent_DisconnectEsxiDisks_Handler(srv interface{}, ctx context.Conte
return interceptor(ctx, in, info, handler)
}
// DeployAgent_ServiceDesc is the grpc.ServiceDesc for DeployAgent service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var DeployAgent_ServiceDesc = grpc.ServiceDesc{
func _DeployAgent_SetOvmfBootOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(OvmfBootOrderParams)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(DeployAgentServer).SetOvmfBootOrder(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/apis.DeployAgent/SetOvmfBootOrder",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DeployAgentServer).SetOvmfBootOrder(ctx, req.(*OvmfBootOrderParams))
}
return interceptor(ctx, in, info, handler)
}
var _DeployAgent_serviceDesc = grpc.ServiceDesc{
ServiceName: "apis.DeployAgent",
HandlerType: (*DeployAgentServer)(nil),
Methods: []grpc.MethodDesc{
@@ -316,6 +340,10 @@ var DeployAgent_ServiceDesc = grpc.ServiceDesc{
MethodName: "DisconnectEsxiDisks",
Handler: _DeployAgent_DisconnectEsxiDisks_Handler,
},
{
MethodName: "SetOvmfBootOrder",
Handler: _DeployAgent_SetOvmfBootOrder_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "deploy.proto",

View File

@@ -127,3 +127,13 @@ func (c *DeployClient) DisconnectEsxiDisks(
client := deployapi.NewDeployAgentClient(conn)
return client.DisconnectEsxiDisks(ctx, in, opts...)
}
func (c *DeployClient) SetOvmfBootOrder(ctx context.Context, in *deployapi.OvmfBootOrderParams, opts ...grpc.CallOption) (*deployapi.Empty, error) {
conn, err := grcpDialWithUnixSocket(ctx, c.socketPath)
if err != nil {
return nil, err
}
defer conn.Close()
client := deployapi.NewDeployAgentClient(conn)
return client.SetOvmfBootOrder(ctx, in, opts...)
}

View File

@@ -22,6 +22,7 @@ import (
"net"
"os"
"runtime/debug"
"sort"
"strings"
"time"
@@ -32,6 +33,7 @@ import (
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/utils"
app_common "yunion.io/x/onecloud/pkg/cloudcommon/app"
commonconsts "yunion.io/x/onecloud/pkg/cloudcommon/consts"
@@ -45,6 +47,7 @@ import (
"yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver"
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
"yunion.io/x/onecloud/pkg/hostman/hostdeployer/consts"
"yunion.io/x/onecloud/pkg/hostman/hostdeployer/uefi"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/procutils"
"yunion.io/x/onecloud/pkg/util/qemuimg"
@@ -264,6 +267,75 @@ func (*DeployerServer) DisconnectEsxiDisks(
return new(deployapi.Empty), nil
}
func (*DeployerServer) SetOvmfBootOrder(ctx context.Context, req *deployapi.OvmfBootOrderParams) (*deployapi.Empty, error) {
log.Infof("Request SetOvmfBootOrder of %s", req.OvmfVarsPath)
if !fileutils2.Exists(req.OvmfVarsPath) {
return new(deployapi.Empty), errors.Errorf("ovmf %s not found", req.OvmfVarsPath)
}
// parse boot entry from ovmf vars
bootEntries, bootOrder, ovmfJsonTmpPath, err := uefi.ParseUefiVars(req.OvmfVarsPath)
if err != nil {
return new(deployapi.Empty), errors.Wrapf(err, "failed parse uefi vars %s", req.OvmfVarsPath)
}
sort.Slice(req.Devs, func(i, j int) bool {
return req.Devs[i].AttachOrder < req.Devs[j].AttachOrder
})
bootEntryIdx := map[string]*deployapi.BootDevices{}
bootentryOrder := map[int32]string{}
findBootentry := func(dev *deployapi.BootDevices) {
for x, bootEntry := range bootEntries {
if _, ok := bootEntryIdx[bootEntry.ID]; ok {
continue
}
log.Errorf("bootentry %v type %v", x, bootEntry.GetType())
if bootEntry.GetType() == uefi.OvmfDevicePathType(dev.DevType) {
bootEntryIdx[bootEntry.ID] = dev
bootentryOrder[dev.BootOrder] = bootEntry.ID
break
}
}
}
for i := range req.Devs {
findBootentry(req.Devs[i])
}
sort.Slice(req.Devs, func(i, j int) bool {
return req.Devs[i].BootOrder < req.Devs[j].BootOrder
})
newBootOrder := []uint16{}
for i := range req.Devs {
bentry, ok := bootentryOrder[req.Devs[i].BootOrder]
if !ok {
continue
}
order, err := uefi.ParseBootentryToBootorder(bentry)
if err != nil {
log.Errorf("failed ParseBootentryToBootorder %s %s", bentry, err)
continue
}
newBootOrder = append(newBootOrder, order)
}
for i := range bootOrder {
if utils.IsInArray(bootOrder[i], newBootOrder) {
continue
}
newBootOrder = append(newBootOrder, bootOrder[i])
}
if err := uefi.UpdateBootOrderInJson(ovmfJsonTmpPath, newBootOrder); err != nil {
log.Errorf("failed UpdateBootOrderInJson %s", err)
return new(deployapi.Empty), errors.Wrapf(err, "failed UpdateBootOrderInJson %v", newBootOrder)
}
out, err := uefi.ApplyJsonToVars(ovmfJsonTmpPath, req.OvmfVarsPath, req.OvmfVarsPath)
if err != nil {
log.Errorf("failed ApplyJsonToVars %s %s", out, err)
return new(deployapi.Empty), errors.Wrapf(err, "failed ApplyJsonToVars %v", out)
}
return new(deployapi.Empty), nil
}
type SDeployService struct {
*service.SServiceBase

View File

@@ -0,0 +1,175 @@
// 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 uefi
import (
"encoding/binary"
"encoding/hex"
"fmt"
"strings"
)
type OvmfDevicePathType int
const (
DEVICE_TYPE_UNKNOWN OvmfDevicePathType = 0
DEVICE_TYPE_CDROM OvmfDevicePathType = 1
DEVICE_TYPE_IDE OvmfDevicePathType = 2
DEVICE_TYPE_SCSI OvmfDevicePathType = 3
DEVICE_TYPE_SCSI_CDROM OvmfDevicePathType = 4
DEVICE_TYPE_PCI OvmfDevicePathType = 5
DEVICE_TYPE_SATA OvmfDevicePathType = 6
)
type BootEntry struct {
ID string // Boot0000, Boot0001, etc.
Name string // Entry title
DevPaths []*DevicePathElement // Device path elements
RawData string // Raw hex data
}
func (b *BootEntry) GetType() OvmfDevicePathType {
lenElements := len(b.DevPaths)
if lenElements == 0 {
return DEVICE_TYPE_UNKNOWN
}
devElement := b.DevPaths[lenElements-1]
// fetch last device path element type
switch devElement.devType {
case DevicePathTypeHardware:
if devElement.subType == 0x01 {
return DEVICE_TYPE_PCI
}
case DevicePathTypeMessaging:
switch devElement.subType {
case 0x01:
if strings.HasPrefix(b.Name, "UEFI QEMU DVD-ROM") {
return DEVICE_TYPE_CDROM
} else if strings.HasPrefix(b.Name, "UEFI QEMU HARDDISK") {
return DEVICE_TYPE_IDE
}
case 0x02:
if strings.HasPrefix(b.Name, "UEFI QEMU QEMU CD-ROM") {
return DEVICE_TYPE_SCSI_CDROM
} else if strings.HasPrefix(b.Name, "UEFI QEMU QEMU HARDDISK") {
return DEVICE_TYPE_SCSI
}
case 0x12:
return DEVICE_TYPE_SATA
}
}
return DEVICE_TYPE_UNKNOWN
}
// ParseBootEntryData parses a boot entry from hex data
func ParseBootEntryData(hexData string) (string, []*DevicePathElement, error) {
// Decode hex data
data, err := hex.DecodeString(hexData)
if err != nil {
return "", nil, fmt.Errorf("failed to decode hex data: %v", err)
}
// Check minimum length
if len(data) < 8 {
return "", nil, fmt.Errorf("data too short")
}
// Parse attributes and path list length
// attributes := binary.LittleEndian.Uint32(data[0:4])
pathListLen := binary.LittleEndian.Uint16(data[4:6])
// Extract description string
descData := data[6:]
descBytes, strLen := ExtractUCS16String(descData)
name := DecodeUTF16LE(descBytes)
// Calculate path list start
pathListStart := 6 + uint32(strLen)
// Check if we have enough data for the path list
if pathListLen == 0 {
return name, []*DevicePathElement{}, nil
}
if uint32(len(data)) < pathListStart+uint32(pathListLen) {
return name, nil, fmt.Errorf("invalid path list length")
}
// Extract path list
pathListData := data[pathListStart : pathListStart+uint32(pathListLen)]
// Parse device path elements
devPaths, err := ParseDevicePathElements(pathListData)
if err != nil {
return name, nil, fmt.Errorf("failed to parse device path: %v", err)
}
return name, devPaths, nil
}
// ParseBootOrder parses a boot order from hex data
func ParseBootOrder(hexData string) ([]uint16, error) {
// Decode hex data
data, err := hex.DecodeString(hexData)
if err != nil {
return nil, fmt.Errorf("failed to decode hex data: %v", err)
}
// Check data length
if len(data) == 0 {
return []uint16{}, nil
}
// Check if data length is valid (must be even)
if len(data)%2 != 0 {
return nil, fmt.Errorf("invalid boot order data length (must be even)")
}
// Parse boot order (2 bytes per entry)
var bootOrder []uint16
for i := 0; i < len(data); i += 2 {
entryNum := binary.LittleEndian.Uint16(data[i : i+2])
bootOrder = append(bootOrder, entryNum)
}
return bootOrder, nil
}
func ParseBootentryToBootorder(entry string) (uint16, error) {
if !strings.HasPrefix(entry, "Boot") {
return 0, fmt.Errorf("unknonw boot entry %s", entry)
}
hexData := entry[4:]
// Decode hex data
data, err := hex.DecodeString(hexData)
if err != nil {
return 0, fmt.Errorf("failed to decode hex data %s: %v", hexData, err)
}
return binary.BigEndian.Uint16(data), nil
}
// BuildBootOrderHex builds a hex string from boot order list
func BuildBootOrderHex(bootOrder []uint16) string {
// Allocate space for boot order (2 bytes per entry)
data := make([]byte, len(bootOrder)*2)
for i, entry := range bootOrder {
// Write little-endian uint16
binary.LittleEndian.PutUint16(data[i*2:], entry)
}
// Return hex string
return hex.EncodeToString(data)
}

View File

@@ -0,0 +1,98 @@
// 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 uefi
import (
"encoding/binary"
"fmt"
)
// DevicePathType constants
const (
DevicePathTypeHardware = 0x01
DevicePathTypeACPI = 0x02
DevicePathTypeMessaging = 0x03
DevicePathTypeMedia = 0x04
DevicePathTypeEnd = 0x7F
)
// DevicePathElement represents a UEFI device path element
type DevicePathElement struct {
devType byte
subType byte
data []byte
}
func (e *DevicePathElement) String() string {
return fmt.Sprintf("devType %v, subType %v", e.devType, e.subType)
}
// Type returns the device path type
func (e *DevicePathElement) Type() byte {
return e.devType
}
// SubType returns the device path subtype
func (e *DevicePathElement) SubType() byte {
return e.subType
}
// ParseDevicePathElements parses a device path from binary data
func ParseDevicePathElements(data []byte) ([]*DevicePathElement, error) {
if len(data) == 0 {
return nil, fmt.Errorf("empty device path data")
}
var elements []*DevicePathElement
pos := 0
for pos < len(data) {
// Check if we have enough data for the header
if pos+4 > len(data) {
return nil, fmt.Errorf("truncated device path data")
}
// Parse header
devType := data[pos]
subType := data[pos+1]
length := binary.LittleEndian.Uint16(data[pos+2 : pos+4])
// Validate length
if length < 4 {
return nil, fmt.Errorf("invalid device path element length")
}
// Check if we have enough data for the element
if pos+int(length) > len(data) {
return nil, fmt.Errorf("truncated device path element")
}
// Check if this is the end of the device path
if devType == DevicePathTypeEnd {
break
}
element := &DevicePathElement{
devType: devType,
subType: subType,
data: data[pos+4 : pos+int(length)],
}
elements = append(elements, element)
pos += int(length)
}
return elements, nil
}

View File

@@ -0,0 +1,15 @@
// 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 uefi // import "yunion.io/x/onecloud/pkg/hostman/hostdeployer/uefi"

View File

@@ -0,0 +1,76 @@
// 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 uefi
import (
"unicode/utf16"
)
// ExtractUCS16String extracts a UCS-16 string from a byte array
// Returns the string data and the total length (including null terminator)
func ExtractUCS16String(data []byte) ([]byte, uint32) {
// Find the null terminator (two consecutive zero bytes)
var i int
for i = 0; i < len(data)-1; i += 2 {
if data[i] == 0 && data[i+1] == 0 {
break
}
}
// Include the null terminator in the length
strLen := i + 2
// If we reached the end without finding a null terminator,
// use the entire data length
if i >= len(data)-1 {
strLen = len(data)
i = len(data)
if i%2 != 0 {
i--
}
}
// Return the string data and length
return data[:i], uint32(strLen)
}
// DecodeUTF16LE decodes a UTF-16LE byte array to a string
func DecodeUTF16LE(b []byte) string {
// Check if the byte array is empty
if len(b) == 0 {
return ""
}
// Convert bytes to uint16 array
u16s := make([]uint16, len(b)/2)
for i := range u16s {
// Little-endian: low byte first, then high byte
u16s[i] = uint16(b[i*2]) | (uint16(b[i*2+1]) << 8)
}
// Decode UTF-16 to UTF-8
return string(utf16.Decode(u16s))
}
// EncodeUTF16LE encodes a string to UTF-16LE bytes
func EncodeUTF16LE(s string) []byte {
u16s := utf16.Encode([]rune(s))
bytes := make([]byte, len(u16s)*2)
for i, u16 := range u16s {
bytes[i*2] = byte(u16)
bytes[i*2+1] = byte(u16 >> 8)
}
return bytes
}

View File

@@ -0,0 +1,59 @@
// 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 uefi
import (
"fmt"
"io/ioutil"
"os"
"sort"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/util/procutils"
)
func DumpOvmfVarsToJson(ovmfVarsPath string) (string, error) {
// Create temporary file for JSON output
jsonFile, err := ioutil.TempFile("", "ovmf-vars-*.json")
if err != nil {
return "", fmt.Errorf("failed to create temporary file: %v", err)
}
jsonPath := jsonFile.Name()
jsonFile.Close()
output, err := procutils.NewCommand("virt-fw-vars", "-i", ovmfVarsPath, "--output-json", jsonPath).Output()
if err != nil {
os.Remove(jsonPath)
return "", errors.Wrapf(err, "virt-fw-vars failed dump to json %s", output)
}
return jsonPath, nil
}
func ParseUefiVars(ovmfVarsPath string) ([]*BootEntry, []uint16, string, error) {
jsonPath, err := DumpOvmfVarsToJson(ovmfVarsPath)
if err != nil {
return nil, nil, "", errors.Wrap(err, "DumpOvmfVarsToJson")
}
bootEntry, bootOrder, err := ParseVarsJson(jsonPath)
if err != nil {
return nil, nil, "", errors.Wrap(err, "ParseVarsJson")
}
sort.Slice(bootEntry, func(i, j int) bool {
return bootEntry[i].ID < bootEntry[j].ID
})
return bootEntry, bootOrder, jsonPath, nil
}

View File

@@ -0,0 +1,157 @@
// 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 uefi
import (
"encoding/json"
"fmt"
"io/ioutil"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/util/procutils"
)
// VarsData represents the UEFI variables data
type VarsData struct {
Version int `json:"version"`
Variables []Variable `json:"variables"`
}
// Variable represents a UEFI variable
type Variable struct {
Name string `json:"name"`
GUID string `json:"guid"`
Attr int `json:"attr"`
Data string `json:"data"`
}
// EFI_GLOBAL_VARIABLE_GUID is the GUID for EFI global variables
const EFI_GLOBAL_VARIABLE_GUID = "8be4df61-93ca-11d2-aa0d-00e098032b8c"
// ParseVarsJson parses UEFI variables from a JSON file
func ParseVarsJson(jsonPath string) ([]*BootEntry, []uint16, error) {
// Read JSON file
data, err := ioutil.ReadFile(jsonPath)
if err != nil {
return nil, nil, fmt.Errorf("failed to read JSON file: %v", err)
}
// Parse JSON
var varsData VarsData
err = json.Unmarshal(data, &varsData)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse JSON: %v", err)
}
// Parse boot entries and boot order
var bootEntries []*BootEntry
var bootOrder []uint16
for _, v := range varsData.Variables {
// Check if this is a boot entry
if len(v.Name) >= 8 && v.Name[:4] == "Boot" && v.GUID == EFI_GLOBAL_VARIABLE_GUID {
// Check if this is the boot order
if v.Name == "BootOrder" {
var err error
bootOrder, err = ParseBootOrder(v.Data)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse boot order: %v", err)
}
continue
}
// Parse boot entry
name, devPaths, err := ParseBootEntryData(v.Data)
if err != nil {
log.Errorf("failed to parse boot entry %s: %s", v.Name, err)
continue
}
// Create boot entry
entry := &BootEntry{
ID: v.Name,
Name: name,
DevPaths: devPaths,
RawData: v.Data,
}
// Add entry to list
bootEntries = append(bootEntries, entry)
}
}
return bootEntries, bootOrder, nil
}
// UpdateBootOrderInJson updates the boot order in a UEFI variables JSON file
func UpdateBootOrderInJson(jsonPath string, bootOrder []uint16) error {
// Read JSON file
data, err := ioutil.ReadFile(jsonPath)
if err != nil {
return fmt.Errorf("failed to read JSON file: %v", err)
}
// Parse JSON
var varsData VarsData
err = json.Unmarshal(data, &varsData)
if err != nil {
return fmt.Errorf("failed to parse JSON: %v", err)
}
bootOrderHex := BuildBootOrderHex(bootOrder)
bootOrderFound := false
for i, v := range varsData.Variables {
if v.Name == "BootOrder" && v.GUID == EFI_GLOBAL_VARIABLE_GUID {
varsData.Variables[i].Data = bootOrderHex
bootOrderFound = true
break
}
}
// Add boot order if not found
if !bootOrderFound {
varsData.Variables = append(varsData.Variables, Variable{
Name: "BootOrder",
GUID: EFI_GLOBAL_VARIABLE_GUID,
Attr: 7, // NV+BS+RT
Data: bootOrderHex,
})
}
// Write updated JSON
updatedData, err := json.MarshalIndent(varsData, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal JSON: %v", err)
}
err = ioutil.WriteFile(jsonPath, updatedData, 0644)
if err != nil {
return fmt.Errorf("failed to write JSON file: %v", err)
}
return nil
}
// ApplyJsonToVars applies a JSON file to OVMF_VARS.fd
func ApplyJsonToVars(jsonPath, inputVarsPath, outputVarsPath string) ([]byte, error) {
// Execute virt-fw-vars to apply JSON
output, err := procutils.NewCommand("virt-fw-vars", "-i", inputVarsPath, "-o", outputVarsPath, "--set-json", jsonPath).Output()
if err != nil {
return output, fmt.Errorf("failed to execute virt-fw-vars --set-json command: %v", err)
}
return output, nil
}

View File

@@ -0,0 +1,236 @@
// 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 hostinfo
import (
"fmt"
"os"
"path/filepath"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/hostman/options"
"yunion.io/x/onecloud/pkg/hostman/system_service"
computemodules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
"yunion.io/x/onecloud/pkg/util/apparmorutils"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/procutils"
)
func (h *SHostInfo) loadExistingHostFiles() ([]api.SHostFile, error) {
if !fileutils2.Exists(options.HostOptions.HostFilesPath) {
return nil, nil
}
hostFilesContent, err := os.ReadFile(options.HostOptions.HostFilesPath)
if err != nil {
return nil, errors.Wrap(err, "os.ReadFile")
}
hostFilesJson, err := jsonutils.Parse(hostFilesContent)
if err != nil {
return nil, errors.Wrap(err, "jsonutils.Parse")
}
hostFiles := make([]api.SHostFile, 0)
err = hostFilesJson.Unmarshal(&hostFiles)
if err != nil {
return nil, errors.Wrap(err, "json.Unmarshal")
}
return hostFiles, nil
}
func (h *SHostInfo) saveHostFiles(hostfiles []api.SHostFile) error {
hostFilesJson := jsonutils.Marshal(hostfiles)
err := os.WriteFile(options.HostOptions.HostFilesPath, []byte(hostFilesJson.String()), 0644)
if err != nil {
return errors.Wrap(err, "os.WriteFile")
}
return nil
}
func (h *SHostInfo) clearHostFiles() error {
return os.Remove(options.HostOptions.HostFilesPath)
}
type hostFilePaire struct {
old *api.SHostFile
new *api.SHostFile
}
func (h *SHostInfo) OnHostFilesChanged(hostfiles []api.SHostFile) error {
existing, err := h.loadExistingHostFiles()
if err != nil {
return errors.Wrap(err, "loadExistingHostFiles")
}
pairMap := make(map[string]*hostFilePaire)
for i := range existing {
hf := fixTelegrafConfPath(&existing[i])
pairMap[hf.Id] = &hostFilePaire{
old: hf,
}
}
for i := range hostfiles {
hf := fixTelegrafConfPath(&hostfiles[i])
if _, ok := pairMap[hf.Id]; !ok {
pairMap[hf.Id] = &hostFilePaire{
new: hf,
}
} else {
pairMap[hf.Id].new = hf
}
}
for _, pair := range pairMap {
if pair.new == nil {
// delete file
err := handleHostFileRemove(pair.old)
if err != nil {
return errors.Wrap(err, "handleHostFileRemove")
}
} else {
// update file
err := handleHostFileChanged(pair)
if err != nil {
return errors.Wrap(err, "handleHostFileChanged")
}
}
}
err = h.saveHostFiles(hostfiles)
if err != nil {
return errors.Wrap(err, "saveHostFiles")
}
return nil
}
func (h *SHostInfo) initHostFiles() error {
hostFilesObj, err := computemodules.Hosts.GetSpecific(h.GetSession(), h.GetId(), "host-files", nil)
if err != nil {
return errors.Wrap(err, "computemodules.Hosts.GetSpecific")
}
hostFiles := []api.SHostFile{}
err = hostFilesObj.Unmarshal(&hostFiles, "host_files")
if err != nil {
return errors.Wrap(err, "Unmarshal")
}
if err := h.clearHostFiles(); err != nil {
return errors.Wrap(err, "clearHostFiles")
}
return h.OnHostFilesChanged(hostFiles)
}
func handleHostFileChanged(pair *hostFilePaire) error {
switch pair.new.Type {
case string(api.ApparmorProfile):
if pair.old == nil || pair.old.Content != pair.new.Content {
if !apparmorutils.IsEnabled() {
log.Warningf("apparmor is not enabled, skip loading profile %s", pair.new.Name)
} else {
log.Infof("load apparmor profile %s", pair.new.Name)
err := apparmorutils.Parser(pair.new.Content)
if err != nil {
return errors.Wrap(err, "apparmorutils.Parser")
}
}
}
default:
changed := false
if pair.old == nil || pair.old.Path != pair.new.Path || pair.old.Content != pair.new.Content {
// new or changed file
log.Infof("update host file %s", pair.new.Name)
err := procutils.FilePutContents(pair.new.Path, pair.new.Content)
if err != nil {
return errors.Wrap(err, "procutils.FilePutContents")
}
changed = true
}
if pair.old != nil && pair.old.Path != pair.new.Path {
// remove old file
log.Infof("remove obsoleted host file %s", pair.old.Name)
err := procutils.NewRemoteCommandAsFarAsPossible("rm", "-f", pair.old.Path).Run()
if err != nil {
return errors.Wrapf(err, "remove file %s", pair.old.Path)
}
changed = true
}
if changed {
err := finalizeFileChange(pair.new)
if err != nil {
return errors.Wrap(err, "finalizeFileChange")
}
}
}
return nil
}
func handleHostFileRemove(hostFile *api.SHostFile) error {
switch hostFile.Type {
case string(api.ApparmorProfile):
// do nothing
default:
log.Infof("remove file %s", hostFile.Path)
err := procutils.NewRemoteCommandAsFarAsPossible("rm", "-f", hostFile.Path).Run()
if err != nil {
return errors.Wrapf(err, "remove file %s", hostFile.Path)
}
{
err := finalizeFileChange(hostFile)
if err != nil {
return errors.Wrap(err, "finalizeFileChange")
}
}
}
return nil
}
func finalizeFileChange(hostFile *api.SHostFile) error {
switch hostFile.Type {
case string(api.TelegrafConf):
telegrafService := system_service.NewTelegrafService()
err := telegrafService.ReloadTelegraf()
if err != nil {
log.Warningf("failed to reload telegraf: %s", err)
}
case string(api.ScriptFile):
err := procutils.NewRemoteCommandAsFarAsPossible("chmod", "+x", hostFile.Path).Run()
if err != nil {
log.Warningf("failed to chmod script file %s: %s", hostFile.Path, err)
}
}
return nil
}
func fixTelegrafConfPath(hostFile *api.SHostFile) *api.SHostFile {
if hostFile.Type != string(api.TelegrafConf) {
return hostFile
}
var baseFile string
if len(hostFile.Path) > 0 {
baseFile = filepath.Base(hostFile.Path)
}
if len(baseFile) == 0 {
baseFile = fmt.Sprintf("%s.conf", hostFile.Name)
}
telegrafDDir := system_service.GetTelegrafConfDDir()
procutils.NewRemoteCommandAsFarAsPossible("mkdir", "-p", telegrafDDir).Run()
hostFile.Path = filepath.Join(telegrafDDir, baseFile)
return hostFile
}

View File

@@ -0,0 +1,63 @@
// 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 hostinfo
import (
"testing"
"yunion.io/x/onecloud/pkg/apis"
api "yunion.io/x/onecloud/pkg/apis/compute"
)
func TestFixTelegrafConfPath(t *testing.T) {
cases := []struct {
hostFile api.SHostFile
path string
}{
{
hostFile: api.SHostFile{
SInfrasResourceBase: apis.SInfrasResourceBase{
SDomainLevelResourceBase: apis.SDomainLevelResourceBase{
SStandaloneResourceBase: apis.SStandaloneResourceBase{
Name: "mysql1",
},
},
},
Type: string(api.TelegrafConf),
Path: "/etc/telegraf/mysqlmon.conf",
},
path: "/etc/telegraf/telegraf.d/mysqlmon.conf",
},
{
hostFile: api.SHostFile{
SInfrasResourceBase: apis.SInfrasResourceBase{
SDomainLevelResourceBase: apis.SDomainLevelResourceBase{
SStandaloneResourceBase: apis.SStandaloneResourceBase{
Name: "mysql2",
},
},
},
Type: string(api.TelegrafConf),
},
path: "/etc/telegraf/telegraf.d/mysql2.conf",
},
}
for _, c := range cases {
fixed := fixTelegrafConfPath(&c.hostFile)
if fixed.Path != c.path {
t.Errorf("fixTelegrafConfPath(%s) = %s, expected %s", c.hostFile.Name, fixed.Path, c.path)
}
}
}

View File

@@ -773,11 +773,10 @@ func (h *SHostInfo) tuneSystem() {
if minMemMb < 100 {
minMemMb = 100
}
minMemKB := fmt.Sprintf("%d", 2*minMemMb*1024)
minMemKB := 2 * minMemMb * 1024
kv := map[string]string{
"/proc/sys/vm/swappiness": "0",
"/proc/sys/vm/vfs_cache_pressure": "350",
"/proc/sys/vm/min_free_kbytes": minMemKB,
"/proc/sys/net/ipv4/tcp_mtu_probing": "2",
"/proc/sys/net/ipv4/neigh/default/gc_thresh1": "1024",
"/proc/sys/net/ipv4/neigh/default/gc_thresh2": "4096",
@@ -787,6 +786,13 @@ func (h *SHostInfo) tuneSystem() {
"/proc/sys/net/netfilter/nf_conntrack_tcp_be_liberal": "1",
}
ret, err := fileutils2.FileGetIntContent("/proc/sys/vm/min_free_kbytes")
if err != nil {
log.Errorf("failed get /proc/sys/vm/min_free_kbytes: %s", err)
} else if ret < minMemKB {
kv["/proc/sys/vm/min_free_kbytes"] = fmt.Sprintf("%d", minMemKB)
}
for k, v := range kv {
sysutils.SetSysConfig(k, v)
}
@@ -1216,6 +1222,11 @@ func (h *SHostInfo) register() {
h.onFail(errors.Wrap(err, "finalizeNetworkSetup"))
return
}
if err := h.initHostFiles(); err != nil {
log.Errorf("initHostFiles failed: %s", err)
} else {
log.Infof("initHostFiles success")
}
h.deployAdminAuthorizedKeys()
h.onSucc()
}

Some files were not shown because too many files have changed in this diff Show More