mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-21 00:24:07 +08:00
Compare commits
90 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b9f2dff61 | ||
|
|
55011704c0 | ||
|
|
9575725dfa | ||
|
|
4dc9ef631f | ||
|
|
532a45d109 | ||
|
|
c317f05b78 | ||
|
|
5741782cca | ||
|
|
da452e23c8 | ||
|
|
8304b37773 | ||
|
|
b0fcef5c6a | ||
|
|
bc55b8b276 | ||
|
|
882d885aa4 | ||
|
|
d791cba145 | ||
|
|
7cf931cc16 | ||
|
|
0f55252c5b | ||
|
|
6298415e7b | ||
|
|
38384ca1ec | ||
|
|
4c5cd45efd | ||
|
|
edb7c6050b | ||
|
|
e1e6a72b4f | ||
|
|
3c844caa4b | ||
|
|
8c51294fe7 | ||
|
|
8679dbee27 | ||
|
|
c4cd0cabdc | ||
|
|
894d12f903 | ||
|
|
f127585611 | ||
|
|
3258a1c262 | ||
|
|
86e07c0afc | ||
|
|
3a93e1754e | ||
|
|
44ea206e28 | ||
|
|
ae085eace6 | ||
|
|
629a3c55fd | ||
|
|
8459cb7ba2 | ||
|
|
92b0d323f1 | ||
|
|
322f795d88 | ||
|
|
ebcf3f5f9f | ||
|
|
4087fa4593 | ||
|
|
c1d792dc8a | ||
|
|
9e5408856f | ||
|
|
6d87de376f | ||
|
|
5054e618cb | ||
|
|
39fab4b3ae | ||
|
|
fe622c89d3 | ||
|
|
b7c3e53731 | ||
|
|
c793d5cda7 | ||
|
|
310dac6b3e | ||
|
|
8eaa4a0653 | ||
|
|
a481666baa | ||
|
|
2d1884da54 | ||
|
|
119fa29e77 | ||
|
|
c799d89764 | ||
|
|
cd76efd19e | ||
|
|
f36688d4b5 | ||
|
|
bcc8f0b581 | ||
|
|
ad5d667587 | ||
|
|
e76592de73 | ||
|
|
382962fd0e | ||
|
|
0f74c9ba0a | ||
|
|
30d4b3b7cd | ||
|
|
160a6e4dbb | ||
|
|
bf26f445a6 | ||
|
|
59fafadda6 | ||
|
|
638d01485e | ||
|
|
1de62d60ed | ||
|
|
91e833db42 | ||
|
|
7d3b5d2fcb | ||
|
|
02a17c1753 | ||
|
|
66ad8ace31 | ||
|
|
8f4ae62aa4 | ||
|
|
9c41deb42c | ||
|
|
e9f7d87688 | ||
|
|
0b5461b707 | ||
|
|
2d791dd542 | ||
|
|
910372ea0c | ||
|
|
302a411629 | ||
|
|
f25b015143 | ||
|
|
bf08a413d3 | ||
|
|
200911ef38 | ||
|
|
cb3aadd014 | ||
|
|
c84d543df2 | ||
|
|
494a9041b3 | ||
|
|
9f50a53945 | ||
|
|
d11fcc7945 | ||
|
|
ebe101d9ab | ||
|
|
87c1c0e290 | ||
|
|
cf7f5f6805 | ||
|
|
02252842e7 | ||
|
|
21c33d4844 | ||
|
|
e7dc0213f6 | ||
|
|
f13c01d20f |
@@ -1,7 +1,7 @@
|
||||
FROM registry.cn-beijing.aliyuncs.com/yunionio/ansibleserver-base:v1.0.3
|
||||
|
||||
# install playbook and telegraf install pkg
|
||||
COPY --from=registry.cn-beijing.aliyuncs.com/yunionio/file-repo:v0.1.2 /opt/yunion/playbook /opt/yunion/playbook
|
||||
COPY --from=registry.cn-beijing.aliyuncs.com/yunionio/file-repo:v0.1.2 /opt/yunion/ansible-install-pkg /opt/yunion/ansible-install-pkg
|
||||
COPY --from=registry.cn-beijing.aliyuncs.com/yunionio/file-repo:v0.1.3 /opt/yunion/playbook /opt/yunion/playbook
|
||||
COPY --from=registry.cn-beijing.aliyuncs.com/yunionio/file-repo:v0.1.3 /opt/yunion/ansible-install-pkg /opt/yunion/ansible-install-pkg
|
||||
|
||||
ADD ./_output/alpine-build/bin/ansibleserver /opt/yunion/bin/ansibleserver
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
FROM registry.cn-beijing.aliyuncs.com/yunionio/baremetal-base:v0.2
|
||||
FROM registry.cn-beijing.aliyuncs.com/yunionio/baremetal-base:v0.3
|
||||
|
||||
MAINTAINER "Zexi Li <lizexi@yunionyun.com>"
|
||||
|
||||
RUN mkdir -p /opt/yunion/bin
|
||||
|
||||
ADD ./_output/bin/baremetal-agent /opt/yunion/bin/baremetal-agent
|
||||
ADD ./_output/bin/.baremetal-agent.bin /opt/yunion/bin/.baremetal-agent.bin
|
||||
ADD ./_output/bin/bundles/baremetal-agent /opt/yunion/bin/bundles/baremetal-agent
|
||||
#ADD ./_output/bin/baremetal-agent /opt/yunion/bin/baremetal-agent
|
||||
#ADD ./_output/bin/.baremetal-agent.bin /opt/yunion/bin/.baremetal-agent.bin
|
||||
#ADD ./_output/bin/bundles/baremetal-agent /opt/yunion/bin/bundles/baremetal-agent
|
||||
RUN apk add librados librbd
|
||||
ADD ./_output/alpine-build/bin/baremetal-agent /opt/yunion/bin/baremetal-agent
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM registry.cn-beijing.aliyuncs.com/yunionio/centos-build:1.1-4 as build
|
||||
RUN yum install -y https://iso.yunion.cn/3.6/rpms/packages/host/baremetal-pxerom-1.1.0-20072810.x86_64.rpm
|
||||
FROM --platform=linux/amd64 registry.cn-beijing.aliyuncs.com/yunionio/centos-build:1.1-4 as build
|
||||
RUN yum install -y https://iso.yunion.cn/vm-images/baremetal-pxerom-1.1.0-21060506.x86_64.rpm
|
||||
|
||||
FROM frolvlad/alpine-glibc:glibc-2.28
|
||||
FROM registry.cn-beijing.aliyuncs.com/yunionio/onecloud-base:v0.2
|
||||
|
||||
MAINTAINER "Yaoqi Wan <wanyaoqi@yunionyun.com>"
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
REGISTRY ?= "registry.cn-beijing.aliyuncs.com/yunionio"
|
||||
|
||||
DOCKER_BUILD = docker build -t $(REGISTRY)
|
||||
DOCKER_BUILDX = docker buildx build --platform linux/arm64,linux/amd64 --push -t $(REGISTRY)
|
||||
|
||||
debian10-base:
|
||||
docker buildx build --platform linux/arm64,linux/amd64 --push \
|
||||
-t registry.cn-beijing.aliyuncs.com/yunionio/debian10-base:1.0 -f ./Dockerfile.debian-base .
|
||||
@@ -5,3 +10,9 @@ debian10-base:
|
||||
climc-base:
|
||||
docker buildx build --platform linux/arm64,linux/amd64 --push \
|
||||
-t registry.cn-beijing.aliyuncs.com/yunionio/climc-base:$(VERSION) -f ./Dockerfile.climc-base .
|
||||
|
||||
BAREMETAL_BASE_VERSION = v0.3
|
||||
|
||||
baremetal-base:
|
||||
$(DOCKER_BUILD)/baremetal-base:$(BAREMETAL_BASE_VERSION) -f ./Dockerfile.baremetal-base .
|
||||
docker push $(REGISTRY)/baremetal-base:$(BAREMETAL_BASE_VERSION)
|
||||
|
||||
@@ -18,251 +18,38 @@ import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/cmd/climc/shell"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options/compute"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd := shell.NewResourceCmd(&modules.Cloudproviders).WithKeyword("cloud-provider")
|
||||
cmd.List(&compute.CloudproviderListOptions{})
|
||||
cmd.Update(&compute.CloudproviderUpdateOptions{})
|
||||
cmd.Show(&options.BaseIdOptions{})
|
||||
cmd.Delete(&options.BaseIdOptions{})
|
||||
cmd.Perform("change-project", &compute.CloudproviderChangeProjectOptions{})
|
||||
cmd.Perform("enable", &options.BaseIdOptions{})
|
||||
cmd.Perform("disable", &options.BaseIdOptions{})
|
||||
cmd.Perform("sync", &compute.CloudproviderSyncOptions{})
|
||||
cmd.Perform("project-mapping", &compute.ClouproviderProjectMappingOptions{})
|
||||
|
||||
type CloudproviderListOptions struct {
|
||||
options.BaseListOptions
|
||||
|
||||
Usable bool `help:"Vpc & Network usable"`
|
||||
|
||||
HasObjectStorage bool `help:"filter cloudproviders that has object storage"`
|
||||
NoObjectStorage bool `help:"filter cloudproviders that has no object storage"`
|
||||
Capability []string `help:"capability filter" choices:"project|compute|network|loadbalancer|objectstore|rds|cache|event"`
|
||||
Cloudregion string `help:"filter cloudproviders by cloudregion"`
|
||||
|
||||
HostSchedtagId string `help:"filter by host schedtag"`
|
||||
}
|
||||
R(&CloudproviderListOptions{}, "cloud-provider-list", "List cloud providers", func(s *mcclient.ClientSession, args *CloudproviderListOptions) error {
|
||||
var params *jsonutils.JSONDict
|
||||
{
|
||||
var err error
|
||||
params, err = args.BaseListOptions.Params()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if args.Usable {
|
||||
params.Add(jsonutils.NewBool(true), "usable")
|
||||
}
|
||||
|
||||
if args.HasObjectStorage {
|
||||
params.Add(jsonutils.JSONTrue, "has_object_storage")
|
||||
} else if args.NoObjectStorage {
|
||||
params.Add(jsonutils.JSONFalse, "has_object_storage")
|
||||
}
|
||||
|
||||
if len(args.Capability) > 0 {
|
||||
params.Add(jsonutils.NewStringArray(args.Capability), "capability")
|
||||
}
|
||||
|
||||
if len(args.Cloudregion) > 0 {
|
||||
params.Add(jsonutils.NewString(args.Cloudregion), "cloudregion")
|
||||
}
|
||||
|
||||
if len(args.HostSchedtagId) > 0 {
|
||||
params.Add(jsonutils.NewString(args.HostSchedtagId), "host_schedtag_id")
|
||||
}
|
||||
}
|
||||
result, err := modules.Cloudproviders.List(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(result, modules.Cloudproviders.GetColumns(s))
|
||||
return nil
|
||||
})
|
||||
|
||||
type CloudproviderUpdateOptions struct {
|
||||
ID string `help:"ID or Name of cloud provider"`
|
||||
Name string `help:"New name to update"`
|
||||
AccessUrl string `help:"New access url"`
|
||||
Desc string `help:"Description"`
|
||||
}
|
||||
R(&CloudproviderUpdateOptions{}, "cloud-provider-update", "Update a cloud provider", func(s *mcclient.ClientSession, args *CloudproviderUpdateOptions) error {
|
||||
params := jsonutils.NewDict()
|
||||
if len(args.Name) > 0 {
|
||||
params.Add(jsonutils.NewString(args.Name), "name")
|
||||
}
|
||||
if len(args.AccessUrl) > 0 {
|
||||
params.Add(jsonutils.NewString(args.AccessUrl), "access_url")
|
||||
}
|
||||
if len(args.Desc) > 0 {
|
||||
params.Add(jsonutils.NewString(args.Desc), "description")
|
||||
}
|
||||
if params.Size() == 0 {
|
||||
return InvalidUpdateError()
|
||||
}
|
||||
result, err := modules.Cloudproviders.Update(s, args.ID, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
|
||||
type CloudproviderChangeProjectOptions struct {
|
||||
ID string `help:"ID or Name of cloud provider"`
|
||||
TENANT string `help:"ID or Name of tenant"`
|
||||
}
|
||||
R(&CloudproviderChangeProjectOptions{}, "cloud-provider-change-project", "Change project for provider", func(s *mcclient.ClientSession, args *CloudproviderChangeProjectOptions) error {
|
||||
result, err := modules.Cloudproviders.PerformAction(s, args.ID, "change-project", jsonutils.Marshal(map[string]string{"project": args.TENANT}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
|
||||
type CloudproviderShowOptions struct {
|
||||
ID string `help:"ID or Name of cloud provider"`
|
||||
}
|
||||
R(&CloudproviderShowOptions{}, "cloud-provider-show", "Get details of a cloud provider", func(s *mcclient.ClientSession, args *CloudproviderShowOptions) error {
|
||||
result, err := modules.Cloudproviders.Get(s, args.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&CloudproviderShowOptions{}, "cloud-provider-delete", "Delete a cloud provider", func(s *mcclient.ClientSession, args *CloudproviderShowOptions) error {
|
||||
result, err := modules.Cloudproviders.Delete(s, args.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&CloudproviderShowOptions{}, "cloud-provider-enable", "Enable cloud provider", func(s *mcclient.ClientSession, args *CloudproviderShowOptions) error {
|
||||
result, err := modules.Cloudproviders.PerformAction(s, args.ID, "enable", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&CloudproviderShowOptions{}, "cloud-provider-disable", "Disable cloud provider", func(s *mcclient.ClientSession, args *CloudproviderShowOptions) error {
|
||||
result, err := modules.Cloudproviders.PerformAction(s, args.ID, "disable", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&CloudproviderShowOptions{}, "cloud-provider-balance", "Get balance", func(s *mcclient.ClientSession, args *CloudproviderShowOptions) error {
|
||||
result, err := modules.Cloudproviders.GetSpecific(s, args.ID, "balance", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
|
||||
type CloudproviderUpdateCredentialOptions struct {
|
||||
ID string `help:"ID or Name of cloud provider"`
|
||||
ACCOUNT string `help:"new account"`
|
||||
SECRET string `help:"new secret"`
|
||||
}
|
||||
R(&CloudproviderUpdateCredentialOptions{}, "cloud-provider-update-credential", "Update credential of a cloud provider", func(s *mcclient.ClientSession, args *CloudproviderUpdateCredentialOptions) error {
|
||||
params := jsonutils.NewDict()
|
||||
params.Add(jsonutils.NewString(args.ACCOUNT), "account")
|
||||
params.Add(jsonutils.NewString(args.SECRET), "secret")
|
||||
|
||||
result, err := modules.Cloudproviders.PerformAction(s, args.ID, "update-credential", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
|
||||
type CloudproviderSyncOptions struct {
|
||||
ID string `help:"ID or Name of cloud provider"`
|
||||
Force bool `help:"Force sync no matter what"`
|
||||
FullSync bool `help:"Synchronize everything"`
|
||||
ProjectSync bool `help:"Auto sync project info"`
|
||||
Region []string `help:"region to sync"`
|
||||
Zone []string `help:"region to sync"`
|
||||
Host []string `help:"region to sync"`
|
||||
}
|
||||
R(&CloudproviderSyncOptions{}, "cloud-provider-sync", "Sync of a cloud provider account", func(s *mcclient.ClientSession, args *CloudproviderSyncOptions) error {
|
||||
params := jsonutils.NewDict()
|
||||
if args.Force {
|
||||
params.Add(jsonutils.JSONTrue, "force")
|
||||
}
|
||||
if args.FullSync {
|
||||
params.Add(jsonutils.JSONTrue, "full_sync")
|
||||
}
|
||||
if args.ProjectSync {
|
||||
params.Add(jsonutils.JSONTrue, "project_sync")
|
||||
}
|
||||
if len(args.Region) > 0 {
|
||||
params.Add(jsonutils.NewStringArray(args.Region), "region")
|
||||
}
|
||||
if len(args.Zone) > 0 {
|
||||
params.Add(jsonutils.NewStringArray(args.Zone), "zone")
|
||||
}
|
||||
if len(args.Host) > 0 {
|
||||
params.Add(jsonutils.NewStringArray(args.Host), "host")
|
||||
}
|
||||
result, err := modules.Cloudproviders.PerformAction(s, args.ID, "sync", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
|
||||
type CloudproviderClientRCOptions struct {
|
||||
ID string `help:"ID or name of cloud provider"`
|
||||
}
|
||||
R(&CloudproviderClientRCOptions{}, "cloud-provider-clirc", "Get client RC file of the cloud provider", func(s *mcclient.ClientSession, args *CloudproviderClientRCOptions) error {
|
||||
result, err := modules.Cloudproviders.GetSpecific(s, args.ID, "clirc", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.GetWithCustomShow("clirc", func(result jsonutils.JSONObject) {
|
||||
rc := make(map[string]string)
|
||||
err = result.Unmarshal(&rc)
|
||||
err := result.Unmarshal(&rc)
|
||||
if err != nil {
|
||||
return err
|
||||
log.Errorf("error: %v", err)
|
||||
return
|
||||
}
|
||||
for k, v := range rc {
|
||||
fmt.Printf(`export %s="%s"`+"\n", k, v)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
type CloudproviderStorageClassesOptions struct {
|
||||
ID string `help:"ID or Name of cloud provider" json:"-"`
|
||||
Cloudregion string `help:"cloud region name or Id"`
|
||||
}
|
||||
R(&CloudproviderStorageClassesOptions{}, "cloud-provider-storage-classes", "Get list of supported storage classes of a cloud provider", func(s *mcclient.ClientSession, args *CloudproviderStorageClassesOptions) error {
|
||||
params, err := options.StructToParams(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := modules.Cloudproviders.GetSpecific(s, args.ID, "storage-classes", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&CloudproviderShowOptions{}, "cloud-provider-change-owner-candidate-domains", "Show candiate domains of a cloud provider changing owner", func(s *mcclient.ClientSession, args *CloudproviderShowOptions) error {
|
||||
result, err := modules.Cloudproviders.GetSpecific(s, args.ID, "change-owner-candidate-domains", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
}, &options.BaseIdOptions{})
|
||||
cmd.Get("storage-classes", &compute.CloudproviderStorageClassesOptions{})
|
||||
cmd.Get("change-owner-candidate-domains", &options.BaseIdOptions{})
|
||||
cmd.Get("balance", &options.BaseIdOptions{})
|
||||
}
|
||||
|
||||
@@ -96,6 +96,8 @@ func init() {
|
||||
cmd.Get("sshable", new(options.ServerIdOptions))
|
||||
cmd.Get("make-sshable-cmd", new(options.ServerIdOptions))
|
||||
cmd.Get("change-owner-candidate-domains", new(options.ServerChangeOwnerCandidateDomainsOptions))
|
||||
cmd.Get("change-owner-candidate-domains", new(options.ServerChangeOwnerCandidateDomainsOptions))
|
||||
cmd.GetProperty(&options.StatusStatisticsOptions{})
|
||||
|
||||
type ServerTaskShowOptions struct {
|
||||
ID string `help:"ID or name of server" json:"-"`
|
||||
@@ -179,6 +181,19 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&options.ResourceMetadataOptions{}, "server-set-metadata", "Set raw metadata of a server", func(s *mcclient.ClientSession, opts *options.ResourceMetadataOptions) error {
|
||||
params, err := opts.Params()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := modules.Servers.PerformAction(s, opts.ID, "metadata", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(result)
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&options.ServerCreateFromInstanceSnapshot{}, "server-create-from-instance-snapshot", "server create from instance snapshot",
|
||||
func(s *mcclient.ClientSession, opts *options.ServerCreateFromInstanceSnapshot) error {
|
||||
params := &compute.ServerCreateInput{}
|
||||
|
||||
@@ -224,6 +224,28 @@ type IShowOpt interface {
|
||||
IIdOpt
|
||||
}
|
||||
|
||||
type IPropertyOpt interface {
|
||||
IOpt
|
||||
Property() string
|
||||
}
|
||||
|
||||
func (cmd ResourceCmd) GetProperty(args IPropertyOpt) {
|
||||
man := cmd.manager
|
||||
callback := func(s *mcclient.ClientSession, args IPropertyOpt) error {
|
||||
params, err := args.Params()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret, err := man.(modulebase.Manager).Get(s, args.Property(), params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(ret)
|
||||
return nil
|
||||
}
|
||||
cmd.RunWithDesc(args.Property(), fmt.Sprintf("Get property of a %s", man.GetKeyword()), args, callback)
|
||||
}
|
||||
|
||||
func (cmd ResourceCmd) Show(args IShowOpt) {
|
||||
man := cmd.manager
|
||||
callback := func(s *mcclient.ClientSession, args IShowOpt) error {
|
||||
@@ -421,7 +443,7 @@ type IGetOpt interface {
|
||||
IOpt
|
||||
}
|
||||
|
||||
func (cmd ResourceCmd) Get(specific string, args IGetOpt) {
|
||||
func (cmd ResourceCmd) GetWithCustomShow(specific string, show func(data jsonutils.JSONObject), args IGetOpt) {
|
||||
man := cmd.manager
|
||||
callback := func(s *mcclient.ClientSession, args IGetOpt) error {
|
||||
params, err := args.Params()
|
||||
@@ -432,12 +454,16 @@ func (cmd ResourceCmd) Get(specific string, args IGetOpt) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(ret)
|
||||
show(ret)
|
||||
return nil
|
||||
}
|
||||
cmd.RunWithDesc(specific, fmt.Sprintf("Get %s of a %s", specific, man.GetKeyword()), args, callback)
|
||||
}
|
||||
|
||||
func (cmd ResourceCmd) Get(specific string, args IGetOpt) {
|
||||
cmd.GetWithCustomShow(specific, printObject, args)
|
||||
}
|
||||
|
||||
type IUpdateOpt interface {
|
||||
IOpt
|
||||
IIdOpt
|
||||
|
||||
20
cmd/climc/shell/meter/bills_dimension.go
Normal file
20
cmd/climc/shell/meter/bills_dimension.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package meter
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/cmd/climc/shell"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/meter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd := shell.NewResourceCmd(modules.BillingDimensionManager)
|
||||
cmd.Create(&options.BillDimensionCreateOptions{})
|
||||
cmd.List(&options.BillDimensionListOptions{})
|
||||
cmd.Show(&options.BillDimensionShowOptions{})
|
||||
cmd.Delete(&options.BillDimensionDeleteOptions{})
|
||||
|
||||
cmdAnalysis := shell.NewResourceCmd(modules.BillingDimensionAnalysisManager)
|
||||
cmdAnalysis.List(&options.BillDimensionAnalysisListOptions{})
|
||||
cmdAnalysis.Show(&options.BillDimensionAnalysisShowOptions{})
|
||||
|
||||
}
|
||||
13
cmd/climc/shell/monitor/monitor_resource.go
Normal file
13
cmd/climc/shell/monitor/monitor_resource.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/cmd/climc/shell"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/monitor"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd := shell.NewResourceCmd(modules.MonitorResourceManager)
|
||||
cmd.Get("", new(options.MonitorResourceJointAlertOptions))
|
||||
|
||||
}
|
||||
@@ -15,12 +15,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/atexit"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
"yunion.io/x/onecloud/pkg/webconsole/service"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer atexit.Handle()
|
||||
|
||||
go procutils.WaitZombieLoop(context.TODO())
|
||||
|
||||
service.StartService()
|
||||
}
|
||||
|
||||
@@ -138,26 +138,17 @@ func (ai *SAnsiblePlaybookInstance) runPlaybook(ctx context.Context, userCred mc
|
||||
return errors.Wrap(err, "unable to update ansibleplaybookinstance")
|
||||
}
|
||||
|
||||
convertJO := func(o jsonutils.JSONObject) map[string]interface{} {
|
||||
if o == nil {
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
ret := make(map[string]interface{})
|
||||
o.Unmarshal(&ret)
|
||||
return ret
|
||||
}
|
||||
// merge configs
|
||||
params := make(map[string]interface{})
|
||||
for k, v := range convertJO(ar.DefaultParams) {
|
||||
params[k] = v
|
||||
}
|
||||
for k, v := range convertJO(ai.Params) {
|
||||
params[k] = v
|
||||
dp, params := ar.DefaultParams.(*jsonutils.JSONDict), ai.Params.(*jsonutils.JSONDict)
|
||||
for _, k := range dp.SortedKeys() {
|
||||
v, _ := dp.Get(k)
|
||||
params.Set(k, v)
|
||||
}
|
||||
ar.DefaultParams.(*jsonutils.JSONDict).SortedKeys()
|
||||
sess := ansiblev2.NewOfflineSession().
|
||||
Inventory(ai.Inventory).
|
||||
PrivateKey(privateKey).
|
||||
Configs(params).
|
||||
ConfigYaml(params.YAMLString()).
|
||||
PlaybookPath(ar.PlaybookPath).
|
||||
OutputWriter(&ansiblePlaybookOutputWriter{ai}).
|
||||
KeepTmpdir(options.Options.KeepTmpdir)
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
@@ -190,45 +191,62 @@ func (f *ResourceHandlers) listHandler(ctx context.Context, w http.ResponseWrite
|
||||
func (f *ResourceHandlers) doList(ctx context.Context, session *mcclient.ClientSession, module modulebase.IBaseManager, query jsonutils.JSONObject, w http.ResponseWriter, r *http.Request) {
|
||||
var exportKeys []string
|
||||
var exportTexts []string
|
||||
exportFormat, _ := query.GetString("export")
|
||||
if len(exportFormat) > 0 {
|
||||
exportKeyStr, _ := query.GetString("export_keys")
|
||||
exportTextStr, _ := query.GetString("export_texts")
|
||||
if len(exportKeyStr) > 0 {
|
||||
exportKeys = strings.Split(exportKeyStr, ",")
|
||||
export := struct {
|
||||
ExportFormat string `json:"export"`
|
||||
ExportKeys string `json:"export_keys"`
|
||||
ExportTexts string `json:"export_texts"`
|
||||
}{}
|
||||
query.Unmarshal(&export)
|
||||
if len(export.ExportFormat) > 0 {
|
||||
if len(export.ExportKeys) > 0 {
|
||||
exportKeys = strings.Split(export.ExportKeys, ",")
|
||||
}
|
||||
if len(exportTextStr) > 0 {
|
||||
exportTexts = strings.Split(exportTextStr, ",")
|
||||
if len(export.ExportTexts) > 0 {
|
||||
exportTexts = strings.Split(export.ExportTexts, ",")
|
||||
}
|
||||
if len(exportKeys) == 0 {
|
||||
httperrors.InvalidInputError(ctx, w, "missing export keys")
|
||||
return
|
||||
} else if len(exportKeys) != len(exportTexts) {
|
||||
if len(exportTexts) == 0 {
|
||||
exportTexts = exportKeys
|
||||
} else {
|
||||
httperrors.InvalidInputError(ctx, w, "inconsistent export keys and texts")
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(exportTexts) == 0 {
|
||||
exportTexts = exportKeys
|
||||
}
|
||||
if len(exportKeys) != len(exportTexts) {
|
||||
httperrors.InvalidInputError(ctx, w, "inconsistent export keys and texts")
|
||||
return
|
||||
}
|
||||
queryDict := query.(*jsonutils.JSONDict)
|
||||
queryDict.Remove("export")
|
||||
queryDict.Remove("export_texts")
|
||||
keys := []string{}
|
||||
for _, key := range exportKeys {
|
||||
if !strings.Contains(key, ".") {
|
||||
keys = append(keys, key)
|
||||
continue
|
||||
}
|
||||
key = strings.Split(key, ".")[0]
|
||||
if !utils.IsInStringArray(key, keys) {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
queryDict.Set("export_keys", jsonutils.NewString(strings.Join(keys, ",")))
|
||||
query = queryDict
|
||||
}
|
||||
|
||||
ret, e := module.List(session, query)
|
||||
if e != nil {
|
||||
httperrors.GeneralServerError(ctx, w, e)
|
||||
} else if len(exportFormat) > 0 {
|
||||
return
|
||||
}
|
||||
if len(export.ExportFormat) > 0 {
|
||||
w.Header().Set("Content-Description", "File Transfer")
|
||||
w.Header().Set("Content-Transfer-Encoding", "binary")
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"export-%s.xlsx\"", module.KeyString()))
|
||||
excelutils.Export(ret.Data, exportKeys, exportTexts, w)
|
||||
} else {
|
||||
appsrv.SendJSON(w, modulebase.ListResult2JSON(ret))
|
||||
return
|
||||
}
|
||||
appsrv.SendJSON(w, modulebase.ListResult2JSON(ret))
|
||||
}
|
||||
|
||||
// get single
|
||||
|
||||
@@ -137,6 +137,8 @@ type CloudproviderDetails struct {
|
||||
|
||||
// 子订阅品牌信息
|
||||
Brand string `json:"brand"`
|
||||
|
||||
ProjectMappingResourceInfo
|
||||
}
|
||||
|
||||
// 云订阅输入参数
|
||||
|
||||
@@ -153,6 +153,9 @@ type ProjectMappingDetails struct {
|
||||
|
||||
// 所绑定的云账号列表
|
||||
Accounts []SProjectMappingAccount `json:"accounts"`
|
||||
|
||||
// 所绑定的云订阅列表
|
||||
Managers []SProjectMappingAccount `json:"managers"`
|
||||
}
|
||||
|
||||
type ProjectMappingCreateInput struct {
|
||||
|
||||
@@ -19,13 +19,13 @@ import "yunion.io/x/onecloud/pkg/apis"
|
||||
type SnapshotCreateInput struct {
|
||||
apis.VirtualResourceCreateInput
|
||||
|
||||
// 磁盘名称或Id,建议使用Id
|
||||
// 磁盘Id
|
||||
// 目前仅VMware平台不支持创建快照,其余平台磁盘均支持创建快照
|
||||
// required: true
|
||||
Disk string `json:"disk"`
|
||||
// swagger:ignore
|
||||
DiskId string `json:"disk_id"`
|
||||
// swagger:ignore
|
||||
Disk string `json:"disk" yunion-deprecated-by:"disk_id"`
|
||||
// swagger:ignore
|
||||
StorageId string `json:"storage_id"`
|
||||
// swagger:ignore
|
||||
CreatedBy string `json:"created_by"`
|
||||
|
||||
@@ -12,6 +12,7 @@ const (
|
||||
METRIC_RES_TYPE_TENANT = "tenant"
|
||||
METRIC_RES_TYPE_DOMAIN = "domain"
|
||||
METRIC_RES_TYPE_STORAGE = "storage"
|
||||
METRIC_RES_TYPE_JENKINS = "jenkins"
|
||||
|
||||
METRIC_UNIT_PERCENT = "%"
|
||||
METRIC_UNIT_BPS = "bps"
|
||||
|
||||
38
pkg/apis/monitor/monitor_resource.go
Normal file
38
pkg/apis/monitor/monitor_resource.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
"yunion.io/x/onecloud/pkg/apis/compute"
|
||||
)
|
||||
|
||||
const (
|
||||
MONITOR_RESOURCE_ALERT_STATUS_INIT = "init"
|
||||
MONITOR_RESOURCE_ALERT_STATUS_ATTACH = "attach"
|
||||
MONITOR_RESOURCE_ALERT_STATUS_ALERTING = "alerting"
|
||||
)
|
||||
|
||||
type MonitorResourceCreateInput struct {
|
||||
apis.VirtualResourceCreateInput
|
||||
apis.EnabledBaseResourceCreateInput
|
||||
|
||||
ResId string `json:"res_id"`
|
||||
ResType string `json:"res_type"`
|
||||
AlertStatus string `json:"alert_status"`
|
||||
}
|
||||
|
||||
type MonitorResourceListInput struct {
|
||||
apis.VirtualResourceListInput
|
||||
apis.EnabledResourceBaseListInput
|
||||
compute.ManagedResourceListInput
|
||||
|
||||
ResId []string `json:"res_id"`
|
||||
ResType string `json:"res_type"`
|
||||
OnlyResId bool `json:"only_res_id"`
|
||||
}
|
||||
|
||||
type MonitorResourceDetails struct {
|
||||
apis.VirtualResourceDetails
|
||||
compute.CloudregionResourceInfo
|
||||
|
||||
AttachAlertCount int64 `json:"attach_alert_count"`
|
||||
}
|
||||
23
pkg/apis/monitor/monitor_resource_alert.go
Normal file
23
pkg/apis/monitor/monitor_resource_alert.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
)
|
||||
|
||||
type MonitorResourceJointListInput struct {
|
||||
MonitorResourceId string `json:"monitor_resource_id"`
|
||||
AlertId string `json:"alert_id"`
|
||||
}
|
||||
|
||||
type MonitorResourceJointCreateInput struct {
|
||||
apis.Meta
|
||||
MonitorResourceId string `json:"monitor_resource_id"`
|
||||
AlertId string `json:"alert_id"`
|
||||
|
||||
AlertRecordId string `width:"36" charset:"ascii" list:"user" update:"user"`
|
||||
AlertState string `width:"18" charset:"ascii" list:"user" update:"user"`
|
||||
TriggerTime time.Time `list:"user" update:"user" json:"trigger_time"`
|
||||
Data EvalMatch `json:"data"`
|
||||
}
|
||||
@@ -230,3 +230,11 @@ type OpsLogDetails struct {
|
||||
OwnerDomain string `json:"owner_domain"`
|
||||
OwnerProject string `json:"owner_tenant"`
|
||||
}
|
||||
|
||||
type StatusStatistic struct {
|
||||
// 资源总数
|
||||
TotalCount int64 `json:"total_count"`
|
||||
// 回收站数量
|
||||
// 需要指定pending_delete=all
|
||||
PendingDeletedCount int64 `json:"pending_deleted_count"`
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func (job *SStatusProbeJob) Do(ctx context.Context, now time.Time) error {
|
||||
if bStatus == api.BAREMETAL_READY || bStatus == api.BAREMETAL_RUNNING || bStatus == api.BAREMETAL_UNKNOWN {
|
||||
ps, err := job.baremetal.GetPowerStatus()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetPowerStatus")
|
||||
return errors.Wrap(err, "StatusProbeJob get power status")
|
||||
}
|
||||
job.lastTime = now
|
||||
pps := PowerStatusToBaremetalStatus(ps)
|
||||
|
||||
@@ -212,9 +212,13 @@ func handleServerStart(ctx *Context, bm *baremetal.SBaremetalInstance, _ baremet
|
||||
}
|
||||
|
||||
func handleServerStop(ctx *Context, bm *baremetal.SBaremetalInstance, _ baremetaltypes.IBaremetalServer) {
|
||||
if err := bm.StartServerStopTask(ctx.UserCred(), ctx.TaskId(), ctx.Data()); err != nil {
|
||||
ctx.ResponseError(httperrors.NewGeneralError(err))
|
||||
return
|
||||
if bm.HasBMC() {
|
||||
if err := bm.StartServerStopTask(ctx.UserCred(), ctx.TaskId(), ctx.Data()); err != nil {
|
||||
ctx.ResponseError(httperrors.NewGeneralError(err))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
bm.StartBaremetalMaintenanceTask(ctx.UserCred(), ctx.TaskId(), ctx.Data())
|
||||
}
|
||||
ctx.ResponseOk()
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/disktool"
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/ipmitool"
|
||||
raiddrivers "yunion.io/x/onecloud/pkg/baremetal/utils/raid/drivers"
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/uefi"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
||||
"yunion.io/x/onecloud/pkg/hostman/guestfs"
|
||||
@@ -797,7 +798,11 @@ func PowerStatusToServerStatus(bm *SBaremetalInstance, status string) string {
|
||||
if conf, _ := bm.GetSSHConfig(); conf == nil {
|
||||
return baremetalstatus.SERVER_RUNNING
|
||||
} else {
|
||||
return baremetalstatus.SERVER_ADMIN
|
||||
if !bm.HasBMC() && !bm.IsMaintenance() {
|
||||
return baremetalstatus.SERVER_READY
|
||||
} else {
|
||||
return baremetalstatus.SERVER_ADMIN
|
||||
}
|
||||
}
|
||||
case types.POWER_STATUS_OFF:
|
||||
return baremetalstatus.SERVER_READY
|
||||
@@ -927,13 +932,18 @@ func (b *SBaremetalInstance) NeedPXEBoot() bool {
|
||||
taskNeedPXEBoot = true
|
||||
}
|
||||
ret := false
|
||||
if taskNeedPXEBoot || (task == nil && len(serverId) == 0 && b.GetHostType() == "baremetal") {
|
||||
if taskNeedPXEBoot || (task == nil && len(serverId) == 0 && b.GetHostType() == "baremetal") || (task == nil && b.IsMaintenance()) {
|
||||
ret = true
|
||||
}
|
||||
log.Infof("Check task %s, server %s NeedPXEBoot: %v", taskName, serverId, ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) IsMaintenance() bool {
|
||||
isMt, _ := b.desc.Bool("is_maintenance")
|
||||
return isMt
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetHostType() string {
|
||||
hostType, _ := b.desc.GetString("host_type")
|
||||
return hostType
|
||||
@@ -990,6 +1000,13 @@ func (b *SBaremetalInstance) getDHCPConfig(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isPxe && IsUEFIPxeArch(arch) && !b.NeedPXEBoot() {
|
||||
// TODO: use chainloader boot UEFI firmware,
|
||||
// currently not response PXE request,
|
||||
// and let BIOS detect bootable device
|
||||
b.ClearSSHConfig()
|
||||
return nil, errors.Errorf("Baremetal %s not need UEFI PXE boot", b.GetName())
|
||||
}
|
||||
return GetNicDHCPConfig(nic, serverIP.String(), hostName, isPxe, arch)
|
||||
}
|
||||
|
||||
@@ -1290,6 +1307,19 @@ func (b *SBaremetalInstance) GetRawIPMIConfig() *types.SIPMIInfo {
|
||||
return &ipmiInfo
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetUEFIInfo() (*types.EFIBootMgrInfo, error) {
|
||||
key := "uefi_info"
|
||||
if !b.desc.Contains(key) {
|
||||
return nil, nil
|
||||
}
|
||||
info := new(types.EFIBootMgrInfo)
|
||||
if err := b.desc.Unmarshal(info, key); err != nil {
|
||||
|
||||
return nil, errors.Wrap(err, "Unmarshal json")
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetAccessIp() string {
|
||||
accessIp, _ := b.desc.GetString("access_ip")
|
||||
return accessIp
|
||||
@@ -1347,6 +1377,206 @@ func (b *SBaremetalInstance) SetExistingIPMIIPAddr(ipAddr string) {
|
||||
b.desc.Set("ipmi_info", info)
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) HasBMC() bool {
|
||||
conf := b.GetIPMIConfig()
|
||||
if conf == nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetHostSSHClient() (*ssh.Client, error) {
|
||||
conf, err := b.GetSSHConfig()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Get host ssh config")
|
||||
}
|
||||
if conf == nil {
|
||||
return nil, errors.Errorf("Host ssh config is empty")
|
||||
}
|
||||
sshCli, err := ssh.NewClient(conf.RemoteIP, 22, "root", conf.Password, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "New ssh client")
|
||||
}
|
||||
return sshCli, nil
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetServerSSHClient() (*ssh.Client, error) {
|
||||
s := b.GetServer()
|
||||
if s == nil {
|
||||
return nil, errors.Error("No server")
|
||||
}
|
||||
|
||||
privateKey, err := modules.Sshkeypairs.FetchPrivateKey(context.TODO(), auth.AdminCredential())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Get server %s login info", s.GetId())
|
||||
}
|
||||
nics := s.GetNics()
|
||||
var errs []error
|
||||
for _, nic := range nics {
|
||||
if nic.LinkUp && nic.Ip != "" {
|
||||
for _, user := range []string{"cloudroot", "root"} {
|
||||
sshCli, err := ssh.NewClient(nic.Ip, 22, user, "", privateKey)
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "New server %s ssh client %s@%s", s.GetName(), user, nic.Ip)
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
return sshCli, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) SSHReachable() (bool, error) {
|
||||
var errs []error
|
||||
if cli, err := b.GetHostSSHClient(); err != nil {
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
// host ssh reachable
|
||||
cli.Close()
|
||||
return true, nil
|
||||
}
|
||||
if cli, err := b.GetServerSSHClient(); err != nil {
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
// server ssh reachable
|
||||
cli.Close()
|
||||
return true, nil
|
||||
}
|
||||
return false, errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) sshRunWrapper(
|
||||
hostRun func(hostCli *ssh.Client) ([]string, error),
|
||||
serverRun func(serverCli *ssh.Client) ([]string, error),
|
||||
) ([]string, error) {
|
||||
hostCli, err := b.GetHostSSHClient()
|
||||
if err != nil {
|
||||
log.Warningf("Get host ssh client error: %v", err)
|
||||
} else {
|
||||
if hostCli != nil {
|
||||
return hostRun(hostCli)
|
||||
}
|
||||
}
|
||||
|
||||
serverCli, err := b.GetServerSSHClient()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Get baremetal %s server ssh client", b.GetName())
|
||||
}
|
||||
return serverRun(serverCli)
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) sshRun(hostCmd string, serverCmd string) ([]string, error) {
|
||||
return b.sshRunWrapper(
|
||||
func(hostCli *ssh.Client) ([]string, error) {
|
||||
return hostCli.RawRun(hostCmd)
|
||||
},
|
||||
func(serverCli *ssh.Client) ([]string, error) {
|
||||
return serverCli.RunWithTTY(serverCmd)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) adjustUEFIWrapper(cli *ssh.Client, f func() error) error {
|
||||
isUEFI, err := uefi.RemoteIsUEFIBoot(cli)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Check is uefi boot")
|
||||
}
|
||||
if !isUEFI {
|
||||
return nil
|
||||
}
|
||||
return f()
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) AdjustUEFICurrentBootOrder(hostCli *ssh.Client) error {
|
||||
return b.adjustUEFIWrapper(hostCli, func() error {
|
||||
mgr, err := uefi.NewEFIBootMgrFromRemote(hostCli, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "NewEFIBootMgrFromRemote")
|
||||
}
|
||||
if err := uefi.RemoteSetCurrentBootAtFirst(hostCli, mgr); err != nil {
|
||||
return errors.Wrap(err, "Set current boot order at first")
|
||||
}
|
||||
return b.SendUEFIInfo(mgr)
|
||||
})
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) SendUEFIInfo(mgr *uefi.BootMgr) error {
|
||||
info, err := mgr.ToEFIBootMgrInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
desc := b.desc
|
||||
uefiData := jsonutils.Marshal(info)
|
||||
desc.Add(uefiData, "uefi_info")
|
||||
if err := b.SaveDesc(desc); err != nil {
|
||||
return errors.Wrap(err, "Save uefi_info")
|
||||
}
|
||||
updateData := jsonutils.NewDict()
|
||||
updateData.Add(uefiData, "uefi_info")
|
||||
if _, err := modules.Hosts.Update(b.GetClientSession(), b.GetId(), updateData); err != nil {
|
||||
return errors.Wrap(err, "Update cloud uefi info")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) adjustServerUEFIBootOrder(srvCli *ssh.Client) error {
|
||||
return b.adjustUEFIWrapper(srvCli, func() error {
|
||||
info, err := b.GetUEFIInfo()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetUEFIInfo from local desc")
|
||||
}
|
||||
if info == nil {
|
||||
return uefi.RemoteTryToSetPXEBoot(srvCli)
|
||||
}
|
||||
if err := uefi.RemoteTryToSetPXEBoot(srvCli); err != nil {
|
||||
log.Warningf("RemoteTryToSetPXEBoot error: %v", err)
|
||||
}
|
||||
_, err = uefi.RemoteSetBootOrderByInfo(srvCli, info.GetPXEEntry())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) AdjustUEFIBootOrder() error {
|
||||
_, err := b.sshRunWrapper(
|
||||
func(hostCli *ssh.Client) ([]string, error) {
|
||||
return nil, b.AdjustUEFICurrentBootOrder(hostCli)
|
||||
},
|
||||
func(srvCli *ssh.Client) ([]string, error) {
|
||||
return nil, b.adjustServerUEFIBootOrder(srvCli)
|
||||
},
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) SSHReboot() error {
|
||||
if !b.HasBMC() {
|
||||
// try adjust uefi boot order before reboot
|
||||
if err := b.AdjustUEFIBootOrder(); err != nil {
|
||||
return errors.Wrap(err, "Adjust uefi boot order")
|
||||
}
|
||||
}
|
||||
if _, err := b.sshRun("/sbin/reboot", "sudo shutdown -r now && exit"); err != nil {
|
||||
if !ssh.IsExitMissingError(err) {
|
||||
return errors.Wrap(err, "Try reboot")
|
||||
}
|
||||
}
|
||||
b.ClearSSHConfig()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) SSHShutdown() error {
|
||||
if _, err := b.sshRun("/sbin/poweroff", "sudo shutdown -h now && exit"); err != nil {
|
||||
if ssh.IsExitMissingError(err) {
|
||||
return nil
|
||||
}
|
||||
return errors.Wrap(err, "Try poweroff")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetIPMITool() *ipmitool.LanPlusIPMI {
|
||||
conf := b.GetIPMIConfig()
|
||||
if conf == nil {
|
||||
@@ -1429,9 +1659,34 @@ func (b *SBaremetalInstance) DoDiskBoot() error {
|
||||
*/
|
||||
|
||||
func (b *SBaremetalInstance) GetPowerStatus() (string, error) {
|
||||
status, err := b.getPowerStatus()
|
||||
if err != nil {
|
||||
if errors.Cause(err) != types.ErrIPMIToolNull {
|
||||
return "", errors.Wrap(err, "GetPowerStatus")
|
||||
} else if b.HasBMC() {
|
||||
return "", errors.Wrap(err, "GetPowerStatus from ipmi")
|
||||
}
|
||||
}
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) getPowerStatus() (string, error) {
|
||||
ipmiCli := b.GetIPMITool()
|
||||
if ipmiCli == nil {
|
||||
return "", fmt.Errorf("Baremetal %s ipmitool is nil", b.GetId())
|
||||
if cli, err := b.GetHostSSHClient(); err == nil {
|
||||
cli.Close()
|
||||
return types.POWER_STATUS_ON, nil
|
||||
} else {
|
||||
log.Warningf("Use host %s ssh client get powerstatus: %v", b.GetName(), err)
|
||||
}
|
||||
if cli, err := b.GetServerSSHClient(); err == nil {
|
||||
cli.Close()
|
||||
b.ClearSSHConfig()
|
||||
return types.POWER_STATUS_ON, nil
|
||||
} else {
|
||||
log.Warningf("Use server %s ssh client get powerstatus: %v", b.GetServerName(), err)
|
||||
}
|
||||
return "", errors.Wrapf(types.ErrIPMIToolNull, "Baremetal %s", b.GetId())
|
||||
}
|
||||
return ipmitool.GetChassisPowerStatus(ipmiCli)
|
||||
}
|
||||
@@ -2111,6 +2366,15 @@ func (s *SBaremetalServer) GetDiskConfig() ([]*api.BaremetalDiskConfig, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(layouts) != 0 {
|
||||
firstDisk := layouts[0]
|
||||
// convert to normal order if first disk is Linux or PCIE driver
|
||||
driver := firstDisk.Conf.Driver
|
||||
if utils.IsInStringArray(driver, []string{baremetal.DISK_DRIVER_LINUX, baremetal.DISK_DRIVER_PCIE}) {
|
||||
return baremetal.GetLayoutDiskConfig(layouts), nil
|
||||
}
|
||||
}
|
||||
return baremetal.GetLayoutRaidConfig(layouts), nil
|
||||
}
|
||||
|
||||
@@ -2131,7 +2395,9 @@ func (s *SBaremetalServer) DoDiskConfig(term *ssh.Client) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("CalculateLayout: %v", err)
|
||||
}
|
||||
log.Errorf("===layouts: %s", jsonutils.Marshal(layouts).PrettyString())
|
||||
diskConfs := baremetal.GroupLayoutResultsByDriverAdapter(layouts)
|
||||
log.Errorf("===diskConfs: %s", jsonutils.Marshal(diskConfs).PrettyString())
|
||||
for _, dConf := range diskConfs {
|
||||
driver := dConf.Driver
|
||||
raidDrv := raiddrivers.GetDriver(driver, term)
|
||||
|
||||
@@ -28,6 +28,30 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/util/dhcp"
|
||||
)
|
||||
|
||||
const (
|
||||
// ref: https://datatracker.ietf.org/doc/html/rfc4578#section-2.1
|
||||
PXE_CLIENT_ARCH_INTEL_X86PC = iota
|
||||
PXE_CLIENT_ARCH_NEC_PC98
|
||||
PXE_CLIENT_ARCH_EFI_ITANIUM
|
||||
PXE_CLIENT_ARCH_DEC_ALPHA
|
||||
PXE_CLIENT_ARCH_ARC_X86
|
||||
PXE_CLIENT_ARCH_INTEL_LEAN_CLIENT
|
||||
PXE_CLIENT_ARCH_EFI_IA32
|
||||
PXE_CLIENT_ARCH_EFI_BC
|
||||
PXE_CLIENT_ARCH_EFI_XSCALE
|
||||
PXE_CLIENT_ARCH_EFI_X86_64
|
||||
)
|
||||
|
||||
func IsUEFIPxeArch(arch uint16) bool {
|
||||
switch arch {
|
||||
case PXE_CLIENT_ARCH_EFI_IA32:
|
||||
return true
|
||||
case PXE_CLIENT_ARCH_EFI_BC, PXE_CLIENT_ARCH_EFI_XSCALE, PXE_CLIENT_ARCH_EFI_X86_64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetNicDHCPConfig(
|
||||
n *types.SNic,
|
||||
serverIP string,
|
||||
@@ -71,9 +95,9 @@ func GetNicDHCPConfig(
|
||||
if isPxe {
|
||||
conf.BootServer = serverIP
|
||||
switch arch {
|
||||
case 7, 9:
|
||||
case PXE_CLIENT_ARCH_EFI_BC, PXE_CLIENT_ARCH_EFI_X86_64:
|
||||
conf.BootFile = "bootx64.efi"
|
||||
case 6:
|
||||
case PXE_CLIENT_ARCH_EFI_IA32:
|
||||
conf.BootFile = "bootia32.efi"
|
||||
default:
|
||||
//if o.Options.EnableTftpHttpDownload {
|
||||
|
||||
@@ -328,23 +328,23 @@ func (req *dhcpRequest) findBaremetalsOfAnyMac(session *mcclient.ClientSession,
|
||||
|
||||
// createOrUpdateBaremetal create or update baremetal by client MAC
|
||||
func (req *dhcpRequest) createOrUpdateBaremetal(session *mcclient.ClientSession) (jsonutils.JSONObject, error) {
|
||||
// first try UUID
|
||||
ret, err := req.findBaremetalsByUuid(session)
|
||||
// first try mac and is_baremetal=true
|
||||
ret, err := req.findBaremetalsOfAnyMac(session, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ret.Data) == 0 {
|
||||
// try mac and is_baremetal=true
|
||||
ret, err = req.findBaremetalsOfAnyMac(session, true)
|
||||
// try mac and host_type=baremetal
|
||||
ret, err = req.findBaremetalsOfAnyMac(session, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ret.Data) == 0 {
|
||||
// try mac and host_type=baremetal
|
||||
ret, err = req.findBaremetalsOfAnyMac(session, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(ret.Data) == 0 {
|
||||
// try UUID
|
||||
ret, err = req.findBaremetalsByUuid(session)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
switch len(ret.Data) {
|
||||
|
||||
@@ -342,6 +342,29 @@ func (self *SBaremetalTaskBase) EnsurePowerUp() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SBaremetalTaskBase) EnsureSSHReboot() error {
|
||||
if err := self.Baremetal.SSHReboot(); err != nil {
|
||||
return errors.Wrap(err, "Ensure ssh reboot")
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
canReach bool
|
||||
)
|
||||
maxTries := 20
|
||||
startTime := time.Now()
|
||||
for count := 0; count < maxTries; count++ {
|
||||
times := (count % 10) + 1
|
||||
log.Infof("Try %s ssh connection after reboot %d times, %s passed", self.Baremetal.GetName(), times, time.Now().Sub(startTime))
|
||||
canReach, err = self.Baremetal.SSHReachable()
|
||||
if canReach {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(10 * time.Second * time.Duration(times))
|
||||
}
|
||||
return errors.Wrapf(err, "Test %s ssh connection after reboot", self.Baremetal.GetName())
|
||||
}
|
||||
|
||||
func (self *SBaremetalTaskBase) NeedPXEBoot() bool {
|
||||
return false
|
||||
}
|
||||
@@ -400,27 +423,35 @@ func (self *SBaremetalPXEBootTaskBase) InitPXEBootTask(ctx context.Context, args
|
||||
self.PxeBoot = false
|
||||
}
|
||||
|
||||
// Do soft reboot
|
||||
if self.data != nil && jsonutils.QueryBoolean(self.data, "soft_boot", false) {
|
||||
self.startTime = time.Now()
|
||||
if err := self.Baremetal.DoPowerShutdown(true); err != nil {
|
||||
// ignore error
|
||||
log.Errorf("DoPowerShutdown error: %v", err)
|
||||
if !self.Baremetal.HasBMC() {
|
||||
// Try remote ssh reboot
|
||||
if err := self.Baremetal.SSHReboot(); err != nil {
|
||||
return errors.Wrap(err, "Try ssh reboot")
|
||||
}
|
||||
//self.CallNextStage(self, self.WaitForShutdown, nil)
|
||||
self.SetStage(self.WaitForShutdown)
|
||||
} else {
|
||||
// Do soft reboot
|
||||
if self.data != nil && jsonutils.QueryBoolean(self.data, "soft_boot", false) {
|
||||
self.startTime = time.Now()
|
||||
if err := self.Baremetal.DoPowerShutdown(true); err != nil {
|
||||
// ignore error
|
||||
log.Errorf("DoPowerShutdown error: %v", err)
|
||||
}
|
||||
//self.CallNextStage(self, self.WaitForShutdown, nil)
|
||||
self.SetStage(self.WaitForShutdown)
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// shutdown and power up to PXE mode
|
||||
if err := self.EnsurePowerShutdown(false); err != nil {
|
||||
return errors.Wrap(err, "EnsurePowerShutdown")
|
||||
}
|
||||
|
||||
if err := self.EnsurePowerUp(); err != nil {
|
||||
return errors.Wrap(err, "EnsurePowerUp to pxe")
|
||||
}
|
||||
}
|
||||
|
||||
// shutdown and power up to PXE mode
|
||||
if err := self.EnsurePowerShutdown(false); err != nil {
|
||||
return errors.Wrap(err, "EnsurePowerShutdown")
|
||||
}
|
||||
|
||||
if err := self.EnsurePowerUp(); err != nil {
|
||||
return errors.Wrap(err, "EnsurePowerUp to pxe")
|
||||
}
|
||||
// this stage will be called by baremetalInstance when pxe start notify
|
||||
self.SetSSHStage(self.IPXEBootTask().OnPXEBoot)
|
||||
return nil
|
||||
@@ -459,3 +490,7 @@ func (self *SBaremetalPXEBootTaskBase) OnStopComplete(ctx context.Context, args
|
||||
func (self *SBaremetalPXEBootTaskBase) GetName() string {
|
||||
return "BaremetalPXEBootTaskBase"
|
||||
}
|
||||
|
||||
func AdjustUEFIBootOrder(term *ssh.Client, bm IBaremetal) error {
|
||||
return bm.AdjustUEFICurrentBootOrder(term)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/uefi"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
)
|
||||
@@ -29,12 +31,15 @@ import (
|
||||
type IServerBaseDeployTask interface {
|
||||
IPXEBootTask
|
||||
|
||||
RemoveEFIOSEntry() bool
|
||||
DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error)
|
||||
PostDeploys(term *ssh.Client) error
|
||||
}
|
||||
|
||||
type SBaremetalServerBaseDeployTask struct {
|
||||
SBaremetalPXEBootTaskBase
|
||||
|
||||
needPXEBoot bool
|
||||
}
|
||||
|
||||
func newBaremetalServerBaseDeployTask(
|
||||
@@ -45,6 +50,7 @@ func newBaremetalServerBaseDeployTask(
|
||||
) SBaremetalServerBaseDeployTask {
|
||||
task := SBaremetalServerBaseDeployTask{
|
||||
SBaremetalPXEBootTaskBase: newBaremetalPXEBootTaskBase(userCred, baremetal, taskId, data),
|
||||
needPXEBoot: true,
|
||||
}
|
||||
// any inheritance must call:
|
||||
// task.SetStage(task.InitPXEBootTask)
|
||||
@@ -59,6 +65,10 @@ func (self *SBaremetalServerBaseDeployTask) GetName() string {
|
||||
return "BaremetalServerBaseDeployTask"
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerBaseDeployTask) NeedPXEBoot() bool {
|
||||
return self.needPXEBoot
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerBaseDeployTask) GetFinishAction() string {
|
||||
if self.data != nil {
|
||||
action, _ := self.data.GetString("on_finish")
|
||||
@@ -67,6 +77,10 @@ func (self *SBaremetalServerBaseDeployTask) GetFinishAction() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerBaseDeployTask) RemoveEFIOSEntry() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerBaseDeployTask) DoDeploys(_ *ssh.Client) (jsonutils.JSONObject, error) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -77,10 +91,22 @@ func (self *SBaremetalServerBaseDeployTask) PostDeploys(_ *ssh.Client) error {
|
||||
|
||||
func (self *SBaremetalServerBaseDeployTask) OnPXEBoot(ctx context.Context, term *ssh.Client, args interface{}) error {
|
||||
log.Infof("%s called on stage pxeboot, args: %v", self.GetName(), args)
|
||||
|
||||
if self.IServerBaseDeployTask().RemoveEFIOSEntry() {
|
||||
if err := uefi.RemoteTryRemoveOSBootEntry(term); err != nil {
|
||||
return errors.Wrap(err, "Remote uefi boot entry")
|
||||
}
|
||||
}
|
||||
|
||||
result, err := self.IServerBaseDeployTask().DoDeploys(term)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Do deploy")
|
||||
}
|
||||
|
||||
if err := AdjustUEFIBootOrder(term, self.Baremetal); err != nil {
|
||||
return errors.Wrap(err, "Adjust UEFI boot order")
|
||||
}
|
||||
|
||||
_, err = term.Run(
|
||||
"/bin/sync",
|
||||
"/sbin/sysctl -w vm.drop_caches=3",
|
||||
@@ -88,23 +114,42 @@ func (self *SBaremetalServerBaseDeployTask) OnPXEBoot(ctx context.Context, term
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Sync disk")
|
||||
}
|
||||
|
||||
if err := self.IServerBaseDeployTask().PostDeploys(term); err != nil {
|
||||
return errors.Wrap(err, "post deploy")
|
||||
}
|
||||
|
||||
onFinishAction := self.GetFinishAction()
|
||||
if utils.IsInStringArray(onFinishAction, []string{"restart", "shutdown"}) {
|
||||
err = self.EnsurePowerShutdown(false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Ensure power off")
|
||||
}
|
||||
if onFinishAction == "restart" {
|
||||
err = self.EnsurePowerUp()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Ensure power up")
|
||||
if self.Baremetal.HasBMC() {
|
||||
if err := self.EnsurePowerShutdown(false); err != nil {
|
||||
return errors.Wrap(err, "Ensure power off")
|
||||
}
|
||||
if onFinishAction == "restart" {
|
||||
if err := self.EnsurePowerUp(); err != nil {
|
||||
return errors.Wrap(err, "Ensure power up")
|
||||
}
|
||||
}
|
||||
self.Baremetal.AutoSyncAllStatus()
|
||||
} else {
|
||||
if onFinishAction == "shutdown" {
|
||||
log.Infof("None BMC baremetal can't shutdown when deploying")
|
||||
/*
|
||||
* if err := self.Baremetal.SSHShutdown(); err != nil {
|
||||
* return errors.Wrap(err, "Try ssh shutdown")
|
||||
* }
|
||||
*/
|
||||
} else {
|
||||
// do restart
|
||||
// hack: ssh reboot to disk
|
||||
self.needPXEBoot = false
|
||||
if err := self.EnsureSSHReboot(); err != nil {
|
||||
return errors.Wrap(err, "Try ssh reboot")
|
||||
}
|
||||
}
|
||||
}
|
||||
self.Baremetal.SyncAllStatus(types.POWER_STATUS_ON)
|
||||
}
|
||||
self.Baremetal.AutoSyncAllStatus()
|
||||
SetTaskComplete(self, result)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -137,22 +137,24 @@ func (task *sBaremetalPrepareTask) prepareBaremetalInfo(cli *ssh.Client) (*barem
|
||||
if ipmiInfo == nil {
|
||||
ipmiInfo = &types.SIPMIInfo{}
|
||||
}
|
||||
if !ipmiInfo.Present && ipmiEnable {
|
||||
if !ipmiInfo.Verified && !ipmiInfo.Present && ipmiEnable {
|
||||
ipmiInfo.Present = true
|
||||
ipmiInfo.Verified = false
|
||||
}
|
||||
|
||||
return &baremetalPrepareInfo{
|
||||
sysInfo,
|
||||
cpuInfo,
|
||||
dmiCPUInfo,
|
||||
memInfo,
|
||||
nicsInfo,
|
||||
diskInfo,
|
||||
storageDriver,
|
||||
ipmiInfo,
|
||||
isolatedDevicesInfo,
|
||||
}, nil
|
||||
prepareInfo := &baremetalPrepareInfo{
|
||||
sysInfo: sysInfo,
|
||||
cpuInfo: cpuInfo,
|
||||
dmiCpuInfo: dmiCPUInfo,
|
||||
memInfo: memInfo,
|
||||
nicsInfo: nicsInfo,
|
||||
diskInfo: diskInfo,
|
||||
storageDriver: storageDriver,
|
||||
ipmiInfo: ipmiInfo,
|
||||
isolatedDevicesInfo: isolatedDevicesInfo,
|
||||
}
|
||||
|
||||
return prepareInfo, nil
|
||||
}
|
||||
|
||||
func (task *sBaremetalPrepareTask) configIPMISetting(cli *ssh.Client, i *baremetalPrepareInfo) error {
|
||||
@@ -313,7 +315,7 @@ func (task *sBaremetalPrepareTask) DoPrepare(cli *ssh.Client) error {
|
||||
// set ipmi nic address and user password
|
||||
if err = task.configIPMISetting(cli, infos); err != nil {
|
||||
logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, err, task.userCred, false)
|
||||
return err
|
||||
return errors.Wrap(err, "Config IPMI setting")
|
||||
}
|
||||
|
||||
if err = task.updateBmInfo(cli, infos); err != nil {
|
||||
@@ -327,6 +329,11 @@ func (task *sBaremetalPrepareTask) DoPrepare(cli *ssh.Client) error {
|
||||
log.Errorf("SetNTP fail: %s", err)
|
||||
}
|
||||
|
||||
if err = AdjustUEFIBootOrder(cli, task.baremetal); err != nil {
|
||||
logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, err, task.userCred, false)
|
||||
return errors.Wrap(err, "Adjust UEFI boot order")
|
||||
}
|
||||
|
||||
logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, infos.sysInfo, task.userCred, true)
|
||||
|
||||
log.Infof("Prepare complete")
|
||||
@@ -358,7 +365,7 @@ func (task *sBaremetalPrepareTask) findAdminNic(cli *ssh.Client, nicsInfo []*typ
|
||||
|
||||
func (task *sBaremetalPrepareTask) updateBmInfo(cli *ssh.Client, i *baremetalPrepareInfo) error {
|
||||
adminNic := task.baremetal.GetAdminNic()
|
||||
if adminNic == nil {
|
||||
if adminNic == nil || (adminNic != nil && !adminNic.LinkUp) {
|
||||
adminIdx, adminNicDev, err := task.findAdminNic(cli, i.nicsInfo)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "task.findAdminNic")
|
||||
@@ -368,7 +375,7 @@ func (task *sBaremetalPrepareTask) updateBmInfo(cli *ssh.Client, i *baremetalPre
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "send Admin Nic Info")
|
||||
}
|
||||
adminNic = task.baremetal.GetAdminNic()
|
||||
adminNic = task.baremetal.GetNicByMac(adminNicDev.Mac)
|
||||
}
|
||||
// collect params
|
||||
updateInfo := make(map[string]interface{})
|
||||
|
||||
@@ -46,6 +46,10 @@ func (self *SBaremetalServerCreateTask) GetName() string {
|
||||
return "BaremetalServerCreateTask"
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerCreateTask) RemoveEFIOSEntry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerCreateTask) DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error) {
|
||||
// Build raid
|
||||
err := self.Baremetal.GetServer().DoDiskConfig(term)
|
||||
@@ -90,7 +94,10 @@ func doPoweroff(term *ssh.Client) error {
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerCreateTask) PostDeploys(term *ssh.Client) error {
|
||||
return doPoweroff(term)
|
||||
if self.Baremetal.HasBMC() {
|
||||
return doPoweroff(term)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerCreateTask) onError(term *ssh.Client, err error) error {
|
||||
|
||||
@@ -43,6 +43,10 @@ func (self *SBaremetalServerDeployTask) GetName() string {
|
||||
return "BaremetalServerDeployTask"
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerDeployTask) RemoveEFIOSEntry() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerDeployTask) DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error) {
|
||||
return self.Baremetal.GetServer().DoDeploy(term, self.data, false)
|
||||
}
|
||||
|
||||
@@ -44,6 +44,10 @@ func (self *SBaremetalServerDestroyTask) GetName() string {
|
||||
return "BaremetalServerDestroyTask"
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerDestroyTask) RemoveEFIOSEntry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerDestroyTask) DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error) {
|
||||
if err := self.Baremetal.GetServer().DoEraseDisk(term); err != nil {
|
||||
log.Errorf("Delete server do erase disk: %v", err)
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
baremetaltypes "yunion.io/x/onecloud/pkg/baremetal/types"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
)
|
||||
|
||||
type IBaremetal interface {
|
||||
@@ -68,6 +69,12 @@ type IBaremetal interface {
|
||||
SaveSSHConfig(remoteAddr string, key string) error
|
||||
ServerLoadDesc() error
|
||||
GetDHCPServerIP() (net.IP, error)
|
||||
|
||||
HasBMC() bool
|
||||
SSHReachable() (bool, error)
|
||||
SSHReboot() error
|
||||
SSHShutdown() error
|
||||
AdjustUEFICurrentBootOrder(cli *ssh.Client) error
|
||||
}
|
||||
|
||||
type IBmManager interface {
|
||||
|
||||
@@ -45,6 +45,10 @@ func (self *SBaremetalServerRebuildTask) GetName() string {
|
||||
return "BaremetalServerRebuildTask"
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerRebuildTask) RemoveEFIOSEntry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerRebuildTask) DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error) {
|
||||
parts, err := self.Baremetal.GetServer().DoRebuildRootDisk(term)
|
||||
if err != nil {
|
||||
@@ -65,5 +69,8 @@ func (self *SBaremetalServerRebuildTask) DoDeploys(term *ssh.Client) (jsonutils.
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerRebuildTask) PostDeploys(term *ssh.Client) error {
|
||||
return doPoweroff(term)
|
||||
if self.Baremetal.HasBMC() {
|
||||
return doPoweroff(term)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -48,3 +48,8 @@ func (self *SBaremetalReprepareTask) DoDeploys(term *ssh.Client) (jsonutils.JSON
|
||||
err := task.DoPrepare(term)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (self *SBaremetalReprepareTask) PostDeploys(term *ssh.Client) error {
|
||||
self.Baremetal.AutoSyncStatus()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -44,16 +44,22 @@ func NewBaremetalServerStartTask(
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerStartTask) DoBoot(ctx context.Context, args interface{}) error {
|
||||
conf := self.Baremetal.GetRawIPMIConfig()
|
||||
if !conf.CdromBoot {
|
||||
err := self.Baremetal.DoPXEBoot()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "DoPXEBoot")
|
||||
if self.Baremetal.HasBMC() {
|
||||
conf := self.Baremetal.GetRawIPMIConfig()
|
||||
if !conf.CdromBoot {
|
||||
err := self.Baremetal.DoPXEBoot()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "DoPXEBoot")
|
||||
}
|
||||
} else {
|
||||
err := self.Baremetal.DoRedfishPowerOn()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "DoRedfishPowerOn")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err := self.Baremetal.DoRedfishPowerOn()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "DoRedfishPowerOn")
|
||||
if err := self.Baremetal.SSHReboot(); err != nil {
|
||||
return errors.Wrap(err, "Try reboot")
|
||||
}
|
||||
}
|
||||
self.SetStage(self.WaitForStart)
|
||||
@@ -68,7 +74,7 @@ func (self *SBaremetalServerStartTask) GetName() string {
|
||||
func (self *SBaremetalServerStartTask) WaitForStart(ctx context.Context, args interface{}) error {
|
||||
status, err := self.Baremetal.GetPowerStatus()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetPowerStatus")
|
||||
return errors.Wrap(err, "Wait for start")
|
||||
}
|
||||
log.Infof("%s WaitForStart status=%s", self.GetName(), status)
|
||||
if status == types.POWER_STATUS_ON {
|
||||
|
||||
@@ -46,9 +46,16 @@ func NewBaremetalServerStopTask(
|
||||
}
|
||||
|
||||
func (task *SBaremetalServerStopTask) DoStop(ctx context.Context, args interface{}) error {
|
||||
task.SetStage(task.WaitForStop)
|
||||
if err := task.Baremetal.DoPowerShutdown(true); err != nil {
|
||||
log.Errorf("Do power shutdown error: %s", err)
|
||||
if task.Baremetal.HasBMC() {
|
||||
task.SetStage(task.WaitForStop)
|
||||
if err := task.Baremetal.DoPowerShutdown(true); err != nil {
|
||||
log.Errorf("Do power shutdown error: %s", err)
|
||||
}
|
||||
} else {
|
||||
if err := task.Baremetal.SSHShutdown(); err != nil {
|
||||
return errors.Wrap(err, "Try ssh shutdown")
|
||||
}
|
||||
task.SetStage(task.OnStopComplete)
|
||||
}
|
||||
task.startTime = time.Now()
|
||||
ExecuteTask(task, nil)
|
||||
|
||||
@@ -16,9 +16,9 @@ package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
baremetalstatus "yunion.io/x/onecloud/pkg/baremetal/status"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
@@ -45,14 +45,23 @@ func NewBaremetalUnmaintenanceTask(
|
||||
|
||||
func (task *SBaremetalUnmaintenanceTask) DoUnmaintenance(ctx context.Context, args interface{}) error {
|
||||
var err error
|
||||
if jsonutils.QueryBoolean(task.data, "guest_running", false) {
|
||||
err = task.EnsurePowerShutdown(false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("EnsurePowerShutdown hard: %v", err)
|
||||
}
|
||||
err = task.EnsurePowerUp()
|
||||
if err != nil {
|
||||
return fmt.Errorf("EnsurePowerUp disk: %v", err)
|
||||
hasBMC := task.Baremetal.HasBMC()
|
||||
if jsonutils.QueryBoolean(task.data, "guest_running", false) || !hasBMC {
|
||||
if hasBMC {
|
||||
err = task.EnsurePowerShutdown(false)
|
||||
if err != nil {
|
||||
return errors.Errorf("EnsurePowerShutdown hard: %v", err)
|
||||
}
|
||||
err = task.EnsurePowerUp()
|
||||
if err != nil {
|
||||
return errors.Errorf("EnsurePowerUp disk: %v", err)
|
||||
}
|
||||
} else {
|
||||
if task.Baremetal.GetServer() != nil {
|
||||
if err := task.EnsureSSHReboot(); err != nil {
|
||||
return errors.Wrap(err, "Do unmaintenance for none BMC server")
|
||||
}
|
||||
}
|
||||
}
|
||||
task.Baremetal.SyncStatus(baremetalstatus.RUNNING, "")
|
||||
SetTaskComplete(task, nil)
|
||||
@@ -61,7 +70,7 @@ func (task *SBaremetalUnmaintenanceTask) DoUnmaintenance(ctx context.Context, ar
|
||||
task.SetStage(task.WaitForStop)
|
||||
err = task.EnsurePowerShutdown(true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("EnsurePowerShutdown soft: %v", err)
|
||||
return errors.Errorf("EnsurePowerShutdown soft: %v", err)
|
||||
}
|
||||
ExecuteTask(task, nil)
|
||||
return nil
|
||||
|
||||
@@ -36,6 +36,7 @@ type IBaremetalServer interface {
|
||||
SyncPartitionSize(term *ssh.Client, parts []*disktool.Partition) ([]jsonutils.JSONObject, error)
|
||||
DoDeploy(term *ssh.Client, data jsonutils.JSONObject, isInit bool) (jsonutils.JSONObject, error)
|
||||
SaveDesc(desc jsonutils.JSONObject) error
|
||||
GetNics() []types.SServerNic
|
||||
GetNicByMac(mac net.HardwareAddr) *types.SNic
|
||||
|
||||
GetRootTemplateId() string
|
||||
|
||||
@@ -616,7 +616,24 @@ func (tool *PartitionTool) parseLsDisk(lines []string, driver string) {
|
||||
}
|
||||
minCnt := int(math.Min(float64(len(disks)), float64(len(tool.diskTable[driver]))))
|
||||
for i := 0; i < minCnt; i++ {
|
||||
tool.diskTable[driver][i].SetInfo(disks[i])
|
||||
driverDisk := tool.diskTable[driver][i]
|
||||
if utils.IsInStringArray(driver, []string{NONRAID_DRIVER, PCIE_DRIVER}) {
|
||||
// find size matched disk from disks
|
||||
size := driverDisk.sizeMB
|
||||
var remoteDisk *types.SDiskInfo
|
||||
for ri := range disks {
|
||||
if disks[ri].Size == size {
|
||||
remoteDisk = disks[ri]
|
||||
// remove matched disks
|
||||
disks = append(disks[:ri], disks[ri+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
driverDisk.SetInfo(remoteDisk)
|
||||
} else {
|
||||
// raid disk set by order
|
||||
driverDisk.SetInfo(disks[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1
pkg/baremetal/utils/uefi/doc.go
Normal file
1
pkg/baremetal/utils/uefi/doc.go
Normal file
@@ -0,0 +1 @@
|
||||
package uefi // import "yunion.io/x/onecloud/pkg/baremetal/utils/uefi"
|
||||
478
pkg/baremetal/utils/uefi/uefi.go
Normal file
478
pkg/baremetal/utils/uefi/uefi.go
Normal file
@@ -0,0 +1,478 @@
|
||||
// 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"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
"yunion.io/x/onecloud/pkg/util/regutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
)
|
||||
|
||||
const (
|
||||
// efibootmgr useage: https://github.com/rhboot/efibootmgr
|
||||
CMD_EFIBOOTMGR = "/usr/sbin/efibootmgr"
|
||||
SUDO_EFIBOOTMGR = "sudo efibootmgr"
|
||||
|
||||
MAC_KEYWORD = "MAC"
|
||||
)
|
||||
|
||||
type BootMgr struct {
|
||||
// bootCurrent - the boot entry used to start the currently running system.
|
||||
bootCurrent string
|
||||
|
||||
// bootOrder - the boot order as would appear in the boot manager.
|
||||
// The boot manager tries to boot the first active entry on this list.
|
||||
// If unsuccessful, it tries the next entry, and so on.
|
||||
bootOrder []string
|
||||
|
||||
// bootNext - the boot entry which is scheduled to be run on next boot.
|
||||
// This superceeds BootOrder for one boot only, and is deleted by the
|
||||
// boot manager after first use.
|
||||
// This allows you to change the next boot behavior without changing BootOrder.
|
||||
bootNext string
|
||||
|
||||
// timeout - the time in seconds between when the boot manager appears on the screen
|
||||
// until when it automatically chooses the startup value from BootNext or BootOrder.
|
||||
timeout int
|
||||
|
||||
// entries - the boot entry parsed in map
|
||||
entries map[string]*BootEntry
|
||||
}
|
||||
|
||||
type BootEntry struct {
|
||||
BootNum string
|
||||
Description string
|
||||
IsActive bool
|
||||
}
|
||||
|
||||
func getEFIBootMgrCmd(sudo bool) string {
|
||||
if sudo {
|
||||
return SUDO_EFIBOOTMGR
|
||||
}
|
||||
return CMD_EFIBOOTMGR
|
||||
}
|
||||
|
||||
func ParseEFIBootMGR(input string) (*BootMgr, error) {
|
||||
lines := strings.Split(input, "\n")
|
||||
|
||||
mgr := &BootMgr{
|
||||
bootOrder: []string{},
|
||||
timeout: -1,
|
||||
entries: make(map[string]*BootEntry),
|
||||
}
|
||||
|
||||
pf := func(ff func(string) bool) {
|
||||
for _, l := range lines {
|
||||
if ok := ff(l); ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parse BootCurrent
|
||||
pf(func(l string) bool {
|
||||
if current := parseEFIBootMGRBootCurrent(l); current != "" {
|
||||
mgr.bootCurrent = current
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// parse Timeout second
|
||||
pf(func(l string) bool {
|
||||
if timeout := parseEFIBootMGRTimeout(l); timeout != -1 {
|
||||
mgr.timeout = timeout
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// parse BootOrder
|
||||
pf(func(l string) bool {
|
||||
if order := parseEFIBootMGRBootOrder(l); len(order) != 0 {
|
||||
mgr.bootOrder = order
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// parse BootNext
|
||||
pf(func(l string) bool {
|
||||
if next := parseEFIBootMGRBootNext(l); next != "" {
|
||||
mgr.bootNext = next
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// parse entries
|
||||
pf(func(l string) bool {
|
||||
if entry := parseEFIBootMGREntry(l); entry != nil {
|
||||
mgr.entries[entry.BootNum] = entry
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// finally check
|
||||
if err := mgr.DataCheck(); err != nil {
|
||||
return nil, errors.Wrap(err, "Invalid efibootmgr parse")
|
||||
}
|
||||
|
||||
return mgr, nil
|
||||
}
|
||||
|
||||
func (m *BootMgr) DataCheck() error {
|
||||
if m.bootCurrent == "" {
|
||||
return errors.Error("BootCurrent is empty")
|
||||
}
|
||||
|
||||
if len(m.bootOrder) == 0 {
|
||||
return errors.Error("BootOrder length is 0")
|
||||
}
|
||||
|
||||
// check if BootOrder in entries
|
||||
for _, orderNum := range m.bootOrder {
|
||||
if _, ok := m.entries[orderNum]; !ok {
|
||||
return errors.Errorf("Not found BootOrder %s entry", orderNum)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseEFIBootMGRBootCurrent(line string) string {
|
||||
prefix := "BootCurrent: "
|
||||
if strings.HasPrefix(line, prefix) {
|
||||
return strings.Split(line, prefix)[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func parseEFIBootMGRBootOrder(line string) []string {
|
||||
prefix := "BootOrder: "
|
||||
if !strings.HasPrefix(line, prefix) {
|
||||
return nil
|
||||
}
|
||||
orderStr := strings.Split(line, prefix)[1]
|
||||
return strings.Split(orderStr, ",")
|
||||
}
|
||||
|
||||
func parseEFIBootMGRBootNext(line string) string {
|
||||
prefix := "BootNext: "
|
||||
if !strings.HasPrefix(line, prefix) {
|
||||
return ""
|
||||
}
|
||||
return strings.Split(line, prefix)[1]
|
||||
}
|
||||
|
||||
func parseEFIBootMGRTimeout(line string) int {
|
||||
timeoutRegexp := `^Timeout: (?P<seconds>[0-9]{1,}) seconds`
|
||||
matches := regutils2.SubGroupMatch(timeoutRegexp, line)
|
||||
if len(matches) == 0 {
|
||||
return -1
|
||||
}
|
||||
secondStr := matches["seconds"]
|
||||
second, err := strconv.Atoi(secondStr)
|
||||
if err != nil {
|
||||
log.Errorf("parse %s seconds error: %v", secondStr, err)
|
||||
return -1
|
||||
}
|
||||
return second
|
||||
}
|
||||
|
||||
func parseEFIBootMGREntry(line string) *BootEntry {
|
||||
entryRegexp := `^Boot(?P<num>[0-9a-zA-Z]{4})[^:]+?\s+(?P<description>.*)`
|
||||
matches := regutils2.SubGroupMatch(entryRegexp, line)
|
||||
if len(matches) == 0 {
|
||||
return nil
|
||||
}
|
||||
num, ok := matches["num"]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
desc, ok := matches["description"]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
isActive := false
|
||||
if strings.Contains(line, "* ") {
|
||||
isActive = true
|
||||
}
|
||||
return &BootEntry{
|
||||
BootNum: num,
|
||||
Description: desc,
|
||||
IsActive: isActive,
|
||||
}
|
||||
}
|
||||
|
||||
func NewEFIBootMgrFromRemote(cli *ssh.Client, sudo bool) (*BootMgr, error) {
|
||||
return newEFIBootMgrFromRemote(cli, sudo, true)
|
||||
}
|
||||
|
||||
func newEFIBootMgrFromRemote(cli *ssh.Client, sudo bool, verbose bool) (*BootMgr, error) {
|
||||
cmd := getEFIBootMgrCmd(sudo)
|
||||
if verbose {
|
||||
cmd = fmt.Sprintf("%s -v", cmd)
|
||||
}
|
||||
lines, err := cli.RawRun(cmd)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Execute command: %s", cmd)
|
||||
}
|
||||
return ParseEFIBootMGR(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetCommand(sudo bool) string {
|
||||
return getEFIBootMgrCmd(sudo)
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetBootCurrent() string {
|
||||
return m.bootCurrent
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetBootOrder() []string {
|
||||
return m.bootOrder
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetBootNext() string {
|
||||
return m.bootNext
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetTimeout() int {
|
||||
return m.timeout
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetBootEntry(num string) *BootEntry {
|
||||
return m.entries[num]
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetBootEntryByDesc(desc string) *BootEntry {
|
||||
for _, entry := range m.entries {
|
||||
if strings.Contains(entry.Description, desc) {
|
||||
return entry
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *BootMgr) FindBootOrderPos(num string) int {
|
||||
return stringArraryFindItemPos(m.bootOrder, num)
|
||||
}
|
||||
|
||||
func stringArraryFindItemPos(items []string, item string) int {
|
||||
for idx, elem := range items {
|
||||
if elem == item {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func stringArraryMove(items []string, item string, pos int) []string {
|
||||
origPos := stringArraryFindItemPos(items, item)
|
||||
if origPos == -1 {
|
||||
items = append(items, item)
|
||||
origPos = stringArraryFindItemPos(items, item)
|
||||
}
|
||||
|
||||
for i := origPos; i != pos; {
|
||||
if i < pos {
|
||||
// from left to right
|
||||
tmp := items[i]
|
||||
items[i] = items[i+1]
|
||||
items[i+1] = tmp
|
||||
i++
|
||||
} else if i > pos {
|
||||
// from right to left
|
||||
tmp := items[i]
|
||||
items[i] = items[i-1]
|
||||
items[i-1] = tmp
|
||||
i--
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func (m *BootMgr) MoveBootOrder(num string, pos int) *BootMgr {
|
||||
if entry := m.GetBootEntry(num); entry == nil {
|
||||
log.Warningf("Not found boot entry by %q", num)
|
||||
return m
|
||||
}
|
||||
m.bootOrder = stringArraryMove(m.bootOrder, num, pos)
|
||||
return m
|
||||
}
|
||||
|
||||
func getSetBootOrderArgs(bootOrder []string) string {
|
||||
return strings.Join(bootOrder, ",")
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetSetBootOrderArgs() string {
|
||||
return getSetBootOrderArgs(m.bootOrder)
|
||||
}
|
||||
|
||||
func RemoteIsUEFIBoot(cli *ssh.Client) (bool, error) {
|
||||
checkCmd := "test -d /sys/firmware/efi && echo is || echo not"
|
||||
lines, err := cli.Run(checkCmd)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "is") {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func convertToEFIBootMgrInfo(info *BootMgr) (*types.EFIBootMgrInfo, error) {
|
||||
data := &types.EFIBootMgrInfo{
|
||||
PxeBootNum: info.GetBootCurrent(),
|
||||
BootOrder: make([]*types.EFIBootEntry, len(info.GetBootOrder())),
|
||||
}
|
||||
for idx, orderNum := range info.GetBootOrder() {
|
||||
entry := info.GetBootEntry(orderNum)
|
||||
if entry == nil {
|
||||
return nil, errors.Errorf("Not found boot entry by %q", orderNum)
|
||||
}
|
||||
data.BootOrder[idx] = &types.EFIBootEntry{
|
||||
BootNum: entry.BootNum,
|
||||
Description: entry.Description,
|
||||
IsActive: entry.IsActive,
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (mgr *BootMgr) ToEFIBootMgrInfo() (*types.EFIBootMgrInfo, error) {
|
||||
return convertToEFIBootMgrInfo(mgr)
|
||||
}
|
||||
|
||||
func (mgr *BootMgr) sortEntryByKeyword(keyword string) *BootMgr {
|
||||
newOrder := []string{}
|
||||
oldOrder := []string{}
|
||||
for _, num := range mgr.bootOrder {
|
||||
entry := mgr.GetBootEntry(num)
|
||||
if strings.Contains(entry.Description, keyword) {
|
||||
newOrder = append(newOrder, num)
|
||||
} else {
|
||||
oldOrder = append(oldOrder, num)
|
||||
}
|
||||
}
|
||||
newOrder = append(newOrder, oldOrder...)
|
||||
mgr.bootOrder = newOrder
|
||||
return mgr
|
||||
}
|
||||
|
||||
func RemoteSetCurrentBootAtFirst(cli *ssh.Client, mgr *BootMgr) error {
|
||||
curPos := mgr.FindBootOrderPos(mgr.GetBootCurrent())
|
||||
if curPos == -1 {
|
||||
return errors.Errorf("Not found BootCurrent position %q", mgr.GetBootCurrent())
|
||||
}
|
||||
// move to first
|
||||
mgr.MoveBootOrder(mgr.GetBootCurrent(), 0)
|
||||
cmd := fmt.Sprintf("%s -o %s", mgr.GetCommand(false), mgr.GetSetBootOrderArgs())
|
||||
_, err := cli.Run(cmd)
|
||||
return err
|
||||
}
|
||||
|
||||
func RemoteSetBootOrder(cli *ssh.Client, order []string) error {
|
||||
cmd := fmt.Sprintf("%s -o %s", SUDO_EFIBOOTMGR, getSetBootOrderArgs(order))
|
||||
_, err := cli.RunWithTTY(cmd)
|
||||
return err
|
||||
}
|
||||
|
||||
func RemoteSetBootOrderByInfo(cli *ssh.Client, entry *types.EFIBootEntry) (*BootMgr, error) {
|
||||
mgr, err := newEFIBootMgrFromRemote(cli, true, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
curEntry := mgr.GetBootEntryByDesc(entry.Description)
|
||||
if curEntry == nil {
|
||||
return nil, errors.Wrapf(err, "Not found remote boot entry by %q", entry.Description)
|
||||
}
|
||||
|
||||
mgr = mgr.sortEntryByKeyword(curEntry.Description)
|
||||
return mgr, RemoteSetBootOrder(cli, mgr.GetBootOrder())
|
||||
}
|
||||
|
||||
func RemoteTryToSetPXEBoot(cli *ssh.Client) error {
|
||||
mgr, err := newEFIBootMgrFromRemote(cli, true, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mgr = mgr.sortEntryByKeyword(MAC_KEYWORD)
|
||||
return RemoteSetBootOrder(cli, mgr.GetBootOrder())
|
||||
}
|
||||
|
||||
func remoteISUEFIBootWrap(cli *ssh.Client, f func(*ssh.Client) error) error {
|
||||
isUEFI, err := RemoteIsUEFIBoot(cli)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Check is UEFI boot")
|
||||
}
|
||||
if !isUEFI {
|
||||
return nil
|
||||
}
|
||||
return f(cli)
|
||||
}
|
||||
|
||||
func RemoteTryRemoveOSBootEntry(hostCli *ssh.Client) error {
|
||||
return remoteISUEFIBootWrap(hostCli, remoteTryRemoveOSBootEntry)
|
||||
}
|
||||
|
||||
func remoteTryRemoveOSBootEntry(hostCli *ssh.Client) error {
|
||||
mgr, err := newEFIBootMgrFromRemote(hostCli, false, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: find other ways to decide whether entry is OS boot
|
||||
osKeywords := []string{
|
||||
"linux",
|
||||
"centos",
|
||||
"ubuntu",
|
||||
"windows",
|
||||
"grub",
|
||||
}
|
||||
|
||||
isOsEntry := func(desc string) bool {
|
||||
for _, key := range osKeywords {
|
||||
desc := strings.ToLower(desc)
|
||||
if strings.Contains(desc, key) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
for _, entry := range mgr.entries {
|
||||
if !isOsEntry(entry.Description) {
|
||||
continue
|
||||
}
|
||||
// delete entry and remove it from BootOrder
|
||||
cmd := fmt.Sprintf("%s -b %s -B", mgr.GetCommand(false), entry.BootNum)
|
||||
if _, err := hostCli.Run(cmd); err != nil {
|
||||
return errors.Wrapf(err, "remove boot entry: %s", entry.Description)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
316
pkg/baremetal/utils/uefi/uefi_test.go
Normal file
316
pkg/baremetal/utils/uefi/uefi_test.go
Normal file
@@ -0,0 +1,316 @@
|
||||
// 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 (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
TestPCOutput = `BootCurrent: 0007
|
||||
Timeout: 2 seconds
|
||||
BootOrder: 0003,0002,0001,0000,0004,0005,0007,0008,0009,000A
|
||||
Boot0000 Windows Boot Manager
|
||||
Boot0001 ubuntu
|
||||
Boot0002 ubuntu
|
||||
Boot0003 CentOS Linux
|
||||
Boot0004* Onboard NIC(IPV4)
|
||||
Boot0005 Onboard NIC(IPV6)
|
||||
Boot0007* PXE IPv4 Intel(R) Ethernet Server Adapter X520-2
|
||||
Boot0008 PXE IPv6 Intel(R) Ethernet Server Adapter X520-2
|
||||
Boot0009* PXE IPv4 Intel(R) Ethernet Server Adapter X520-2
|
||||
Boot000A PXE IPv6 Intel(R) Ethernet Server Adapter X520-2`
|
||||
|
||||
TestQemuOutput = `BootCurrent: 0005
|
||||
Timeout: 0 seconds
|
||||
BootOrder: 0005,0009,0008,0007,0002,0003,0004,0006,0001,0000
|
||||
Boot0000* UiApp
|
||||
Boot0001* UEFI QEMU DVD-ROM QM00003
|
||||
Boot0002* UEFI Floppy
|
||||
Boot0003* UEFI Floppy 2
|
||||
Boot0004* UEFI QEMU HARDDISK QM00001
|
||||
Boot0005* UEFI PXEv4 (MAC:525400123456)
|
||||
Boot0006* EFI Internal Shell
|
||||
Boot0007* CentOS
|
||||
Boot0008* CentOS Linux
|
||||
Boot0009* ubuntu`
|
||||
)
|
||||
|
||||
func Test_parseTimeout(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want int
|
||||
}{
|
||||
{
|
||||
input: "Timeout: 0 seconds",
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
input: "Timeout: 30 seconds",
|
||||
want: 30,
|
||||
},
|
||||
{
|
||||
input: "Timeout: seconds",
|
||||
want: -1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
if got := parseEFIBootMGRTimeout(tc.input); !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("parseEFIBootMGRTimeout() = %v, want %v", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseEFIBootMGRBootOrder(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
input: "BootOrder: 0005,0009,0008,0007,0002,0003,0004,0006,0001,0000",
|
||||
want: []string{"0005", "0009", "0008", "0007", "0002", "0003", "0004", "0006", "0001", "0000"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
if got := parseEFIBootMGRBootOrder(tc.input); !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("parseEFIBootMGRBootOrder() = %v, want %v", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseEFIBootMGRBootCurrent(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
line string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "BootCurrent: 0005",
|
||||
line: "BootCurrent: 0005",
|
||||
want: "0005",
|
||||
},
|
||||
{
|
||||
name: "BootCurrent: ",
|
||||
line: "BootCurrent: ",
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "BootCurrent:",
|
||||
line: "BootCurrent:",
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := parseEFIBootMGRBootCurrent(tt.line); got != tt.want {
|
||||
t.Errorf("parseEFIBootMGRBootCurrent() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseEFIBootMGREntry(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
line string
|
||||
want *BootEntry
|
||||
}{
|
||||
{
|
||||
name: "Boot0009* ubuntu",
|
||||
line: "Boot0009* ubuntu",
|
||||
want: &BootEntry{
|
||||
BootNum: "0009",
|
||||
Description: "ubuntu",
|
||||
IsActive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Boot0005* UEFI PXEv4 (MAC:525400123456)",
|
||||
line: "Boot0005* UEFI PXEv4 (MAC:525400123456)",
|
||||
want: &BootEntry{
|
||||
BootNum: "0005",
|
||||
Description: "UEFI PXEv4 (MAC:525400123456)",
|
||||
IsActive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Boot0003 CentOS Linux",
|
||||
line: "Boot0003 CentOS Linux",
|
||||
want: &BootEntry{
|
||||
BootNum: "0003",
|
||||
Description: "CentOS Linux",
|
||||
IsActive: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Timeout: 2 seconds",
|
||||
line: "Timeout: 2 seconds",
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := parseEFIBootMGREntry(tt.line); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("parseEFIBootMGREntry() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseEFIBootMGR(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want *BootMgr
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Parse PC output",
|
||||
input: TestPCOutput,
|
||||
want: &BootMgr{
|
||||
bootCurrent: "0007",
|
||||
timeout: 2,
|
||||
bootOrder: []string{"0003", "0002", "0001", "0000", "0004", "0005", "0007", "0008", "0009", "000A"},
|
||||
entries: map[string]*BootEntry{
|
||||
"0000": {
|
||||
BootNum: "0000",
|
||||
IsActive: false,
|
||||
Description: "Windows Boot Manager",
|
||||
},
|
||||
"0001": {
|
||||
BootNum: "0001",
|
||||
IsActive: false,
|
||||
Description: "ubuntu",
|
||||
},
|
||||
"0002": {
|
||||
BootNum: "0002",
|
||||
IsActive: false,
|
||||
Description: "ubuntu",
|
||||
},
|
||||
"0003": {
|
||||
BootNum: "0003",
|
||||
IsActive: false,
|
||||
Description: "CentOS Linux",
|
||||
},
|
||||
"0004": {
|
||||
BootNum: "0004",
|
||||
IsActive: true,
|
||||
Description: "Onboard NIC(IPV4)",
|
||||
},
|
||||
"0005": {
|
||||
BootNum: "0005",
|
||||
IsActive: false,
|
||||
Description: "Onboard NIC(IPV6)",
|
||||
},
|
||||
"0007": {
|
||||
BootNum: "0007",
|
||||
IsActive: true,
|
||||
Description: "PXE IPv4 Intel(R) Ethernet Server Adapter X520-2",
|
||||
},
|
||||
"0008": {
|
||||
BootNum: "0008",
|
||||
IsActive: false,
|
||||
Description: "PXE IPv6 Intel(R) Ethernet Server Adapter X520-2",
|
||||
},
|
||||
"0009": {
|
||||
BootNum: "0009",
|
||||
IsActive: true,
|
||||
Description: "PXE IPv4 Intel(R) Ethernet Server Adapter X520-2",
|
||||
},
|
||||
"000A": {
|
||||
BootNum: "000A",
|
||||
IsActive: false,
|
||||
Description: "PXE IPv6 Intel(R) Ethernet Server Adapter X520-2",
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
assert := assert.New(t)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseEFIBootMGR(tt.input)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ParseEFIBootMGR() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if equal := assert.Equal(tt.want, got); !equal {
|
||||
t.Errorf("ParseEFIBootMGR() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_stringArraryMove(t *testing.T) {
|
||||
type args struct {
|
||||
items []string
|
||||
item string
|
||||
pos int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "move left to right",
|
||||
args: args{
|
||||
items: []string{"1", "2", "3"},
|
||||
item: "3",
|
||||
pos: 0,
|
||||
},
|
||||
want: []string{"3", "1", "2"},
|
||||
},
|
||||
{
|
||||
name: "move right to left",
|
||||
args: args{
|
||||
items: []string{"1", "2", "3"},
|
||||
item: "1",
|
||||
pos: 2,
|
||||
},
|
||||
want: []string{"2", "3", "1"},
|
||||
},
|
||||
{
|
||||
name: "no move",
|
||||
args: args{
|
||||
items: []string{"1", "2", "3"},
|
||||
item: "2",
|
||||
pos: 1,
|
||||
},
|
||||
want: []string{"1", "2", "3"},
|
||||
},
|
||||
{
|
||||
name: "add item move",
|
||||
args: args{
|
||||
items: []string{"1", "2", "3"},
|
||||
item: "4",
|
||||
pos: 0,
|
||||
},
|
||||
want: []string{"4", "1", "2", "3"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := stringArraryMove(tt.args.items, tt.args.item, tt.args.pos); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("stringArraryMove() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -282,6 +282,9 @@ const (
|
||||
ACT_UPDATE_RULE = "update_config"
|
||||
ACT_UPDATE_TAGS = "update_tags"
|
||||
|
||||
ACT_UPDATE_MONITOR_RESOURCE_JOINT = "update_monitor_resource_joint"
|
||||
ACT_DETACH_MONITOR_RESOURCE_JOINT = "detach_monitor_resource_joint"
|
||||
|
||||
ACT_MERGE_NETWORK = "merge_network"
|
||||
ACT_MERGE_NETWORK_FAILED = "merge_network_failed"
|
||||
)
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
@@ -55,6 +56,39 @@ func (self *SStatusStandaloneResourceBase) GetIStatusStandaloneModel() IStatusSt
|
||||
return self.GetVirtualObject().(IStatusStandaloneModel)
|
||||
}
|
||||
|
||||
func (manager *SStatusStandaloneResourceBaseManager) AllowGetPropertyStatistics(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return IsAdminAllowGetSpec(userCred, manager, "statistics")
|
||||
}
|
||||
|
||||
func (manager *SStatusStandaloneResourceBaseManager) GetPropertyStatistics(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (map[string]apis.StatusStatistic, error) {
|
||||
sq := manager.Query().SubQuery()
|
||||
q := sq.Query(sq.Field("status"), sqlchemy.COUNT("total_count", sq.Field("id")))
|
||||
_, queryScope, err := FetchCheckQueryOwnerScope(ctx, userCred, query, manager, rbacutils.ActionList, true)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewGeneralError(err)
|
||||
}
|
||||
q = manager.FilterByOwner(q, userCred, queryScope)
|
||||
q = manager.FilterBySystemAttributes(q, userCred, query, queryScope)
|
||||
q = manager.FilterByHiddenSystemAttributes(q, userCred, query, queryScope)
|
||||
q = q.GroupBy(sq.Field("status"))
|
||||
|
||||
ret := []struct {
|
||||
Status string
|
||||
TotalCount int64
|
||||
}{}
|
||||
err = q.All(&ret)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "q.All")
|
||||
}
|
||||
result := map[string]apis.StatusStatistic{}
|
||||
for _, s := range ret {
|
||||
result[s.Status] = apis.StatusStatistic{
|
||||
TotalCount: s.TotalCount,
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 更新资源状态
|
||||
func (self *SStatusStandaloneResourceBase) PerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformStatusInput) (jsonutils.JSONObject, error) {
|
||||
err := StatusBasePerformStatus(self.GetIStatusStandaloneModel(), userCred, input)
|
||||
|
||||
@@ -87,6 +87,45 @@ func (manager *SVirtualResourceBaseManager) GetIVirtualModelManager() IVirtualMo
|
||||
return q
|
||||
}*/
|
||||
|
||||
func (manager *SVirtualResourceBaseManager) AllowGetPropertyStatistics(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return IsAdminAllowGetSpec(userCred, manager, "statistics")
|
||||
}
|
||||
|
||||
func (manager *SVirtualResourceBaseManager) GetPropertyStatistics(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (map[string]apis.StatusStatistic, error) {
|
||||
sq := manager.Query().SubQuery()
|
||||
q := sq.Query(sq.Field("status"), sqlchemy.COUNT("total_count", sq.Field("id")), sqlchemy.SUM("pending_deleted_count", sq.Field("pending_deleted")))
|
||||
_, queryScope, err := FetchCheckQueryOwnerScope(ctx, userCred, query, manager, rbacutils.ActionList, true)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewGeneralError(err)
|
||||
}
|
||||
|
||||
q = manager.FilterByOwner(q, userCred, queryScope)
|
||||
q = manager.FilterBySystemAttributes(q, userCred, query, queryScope)
|
||||
q = manager.FilterByHiddenSystemAttributes(q, userCred, query, queryScope)
|
||||
q = q.GroupBy(sq.Field("status"))
|
||||
ret := []struct {
|
||||
Status string
|
||||
TotalCount int64
|
||||
PendingDeletedCount int64
|
||||
}{}
|
||||
err = q.All(&ret)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "q.All")
|
||||
}
|
||||
type sStatistic struct {
|
||||
TotalCount int64
|
||||
PendingDeletedCount int64
|
||||
}
|
||||
result := map[string]apis.StatusStatistic{}
|
||||
for _, s := range ret {
|
||||
result[s.Status] = apis.StatusStatistic{
|
||||
TotalCount: s.TotalCount,
|
||||
PendingDeletedCount: s.PendingDeletedCount,
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (manager *SVirtualResourceBaseManager) FilterByHiddenSystemAttributes(q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject, scope rbacutils.TRbacScope) *sqlchemy.SQuery {
|
||||
q = manager.SStatusStandaloneResourceBaseManager.FilterByHiddenSystemAttributes(q, userCred, query, scope)
|
||||
|
||||
|
||||
@@ -17,6 +17,12 @@ package types
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
ErrIPMIToolNull = errors.Error("IPMI tool is null")
|
||||
)
|
||||
|
||||
type SSHConfig struct {
|
||||
|
||||
35
pkg/cloudcommon/types/uefi.go
Normal file
35
pkg/cloudcommon/types/uefi.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// 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 types
|
||||
|
||||
type EFIBootMgrInfo struct {
|
||||
PxeBootNum string `json:"pxe_boot_num"`
|
||||
BootOrder []*EFIBootEntry `json:"boot_order"`
|
||||
}
|
||||
|
||||
type EFIBootEntry struct {
|
||||
BootNum string `json:"boot_num"`
|
||||
Description string `json:"description"`
|
||||
IsActive bool `json:"is_active"`
|
||||
}
|
||||
|
||||
func (info *EFIBootMgrInfo) GetPXEEntry() *EFIBootEntry {
|
||||
for _, e := range info.BootOrder {
|
||||
if e.BootNum == info.PxeBootNum {
|
||||
return e
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1133,6 +1133,7 @@ func (self *SCloudaccount) RegisterSAMProvider() (*SSAMLProvider, error) {
|
||||
}()
|
||||
sp.EntityId = options.Options.ApiServer
|
||||
sp.CloudaccountId = self.Id
|
||||
sp.DomainId = self.DomainId
|
||||
sp.Status = api.SAML_PROVIDER_STATUS_CREATING
|
||||
metadata := SamlIdpInstance().GetMetadata(self.Id).String()
|
||||
sp.MetadataDocument = metadata
|
||||
@@ -1199,7 +1200,7 @@ func (self *SCloudaccount) StartSyncCloudrolesTask(ctx context.Context, userCred
|
||||
}
|
||||
|
||||
func (self *SCloudaccount) GetSAMLProviders() ([]SSAMLProvider, error) {
|
||||
q := SAMLProviderManager.Query().Equals("cloudaccount_id", self.Id)
|
||||
q := SAMLProviderManager.Query().Equals("cloudaccount_id", self.Id).Desc("external_id")
|
||||
samls := []SSAMLProvider{}
|
||||
err := db.FetchModelObjects(SAMLProviderManager, q, &samls)
|
||||
if err != nil {
|
||||
|
||||
@@ -58,7 +58,7 @@ func isDiskConfigStorageMatch(
|
||||
adapterIsEqual := (confAdapter == nil || *confAdapter == adapter) &&
|
||||
(confDriver == nil || *confDriver == driver)
|
||||
|
||||
log.V(10).Debugf("typeIsHybrid: %v, typeIsRotate: %v, typeIsSSD: %v, rangeIsNoneAndCountZero: %v, rangeIsNotNoneAndIndexInRange: %v, rangeIsNoneAndSmallThanCount: %v, adapterIsEqual: %v", typeIsHybrid, typeIsRotate, typeIsSSD, rangeIsNoneAndCountZero, rangeIsNotNoneAndIndexInRange, rangeIsNoneAndSmallThanCount, adapterIsEqual)
|
||||
log.V(10).Debugf("Try storage: %#v, typeIsHybrid: %v, typeIsRotate: %v, typeIsSSD: %v, rangeIsNoneAndCountZero: %v, rangeIsNotNoneAndIndexInRange: %v, rangeIsNoneAndSmallThanCount: %v, adapterIsEqual: %v", *storage, typeIsHybrid, typeIsRotate, typeIsSSD, rangeIsNoneAndCountZero, rangeIsNotNoneAndIndexInRange, rangeIsNoneAndSmallThanCount, adapterIsEqual)
|
||||
|
||||
if (typeIsHybrid || typeIsRotate || typeIsSSD) &&
|
||||
(rangeIsNoneAndCountZero || rangeIsNotNoneAndIndexInRange || rangeIsNoneAndSmallThanCount) &&
|
||||
@@ -109,7 +109,11 @@ func RetrieveStorages(diskConfig *api.BaremetalDiskConfig, storages []*Baremetal
|
||||
} else {
|
||||
rest = append(rest, storage)
|
||||
}
|
||||
idx++
|
||||
if confDriver == nil {
|
||||
idx++
|
||||
} else if *confDriver == storage.Driver {
|
||||
idx++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -298,10 +302,18 @@ func ExpandNoneConf(layouts []Layout) (ret []Layout) {
|
||||
}
|
||||
|
||||
func GetLayoutRaidConfig(layouts []Layout) []*api.BaremetalDiskConfig {
|
||||
return getLayoutConfig(layouts, true)
|
||||
}
|
||||
|
||||
func GetLayoutDiskConfig(layouts []Layout) []*api.BaremetalDiskConfig {
|
||||
return getLayoutConfig(layouts, false)
|
||||
}
|
||||
|
||||
func getLayoutConfig(layouts []Layout, onlyRaidDisk bool) []*api.BaremetalDiskConfig {
|
||||
var disk []*BaremetalStorage
|
||||
ret := make([]*api.BaremetalDiskConfig, 0)
|
||||
for _, layout := range layouts {
|
||||
if layout.Conf.Conf == DISK_CONF_NONE &&
|
||||
if onlyRaidDisk && layout.Conf.Conf == DISK_CONF_NONE &&
|
||||
sets.NewString(DISK_DRIVER_LINUX, DISK_DRIVER_PCIE).Has(layout.Disks[0].Driver) {
|
||||
continue
|
||||
}
|
||||
@@ -335,8 +347,8 @@ func CalculateLayout(confs []*api.BaremetalDiskConfig, storages []*BaremetalStor
|
||||
noneConf, _ := ParseDiskConfig(DISK_CONF_NONE)
|
||||
conf = &noneConf
|
||||
}
|
||||
selected, storage1 := RetrieveStorages(conf, storages)
|
||||
storages = storage1
|
||||
selected, restStorges := RetrieveStorages(conf, storages)
|
||||
storages = restStorges
|
||||
if len(selected) == 0 {
|
||||
err = fmt.Errorf("Not found matched storages by config: %#v", conf)
|
||||
return
|
||||
|
||||
@@ -19,6 +19,11 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
)
|
||||
|
||||
@@ -464,7 +469,9 @@ func TestCalculateLayout(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Unmarshal expectedLayoutJson err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(layout, expectedLayout) {
|
||||
|
||||
assert := assert.New(t)
|
||||
if !assert.Equal(expectedLayout, layout) {
|
||||
t.Errorf("CalculateLayout() = %v, want %v", layout, expectedLayout)
|
||||
}
|
||||
}
|
||||
@@ -475,7 +482,7 @@ func TestCheckDisksAllocable(t *testing.T) {
|
||||
"6:raid5:adapter2",
|
||||
"6:raid5:adapter2",
|
||||
)
|
||||
bitmainConfs, err := NewBaremetalDiskConfigs("raid10:(60g,)")
|
||||
bitmainConfs, err := NewBaremetalDiskConfigs("MarvelRaid:raid10:(60g,)")
|
||||
if err != nil {
|
||||
t.Fatalf("NewDiskConfigs err: %v", err)
|
||||
}
|
||||
@@ -1255,3 +1262,133 @@ func TestGetSplitSizes(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
pcieStorages = []*BaremetalStorage{
|
||||
{
|
||||
Driver: DISK_DRIVER_LINUX,
|
||||
Rotate: true,
|
||||
Size: 51200,
|
||||
Adapter: 0,
|
||||
Index: 0,
|
||||
},
|
||||
{
|
||||
Driver: DISK_DRIVER_PCIE,
|
||||
Rotate: false,
|
||||
Size: 61440,
|
||||
Adapter: 0,
|
||||
Index: 0,
|
||||
},
|
||||
}
|
||||
|
||||
pcie2Storages = []*BaremetalStorage{
|
||||
{
|
||||
Driver: DISK_DRIVER_LINUX,
|
||||
Dev: "sda",
|
||||
Rotate: true,
|
||||
Size: 3815447,
|
||||
Adapter: 0,
|
||||
Index: 0,
|
||||
},
|
||||
{
|
||||
Driver: DISK_DRIVER_PCIE,
|
||||
Dev: "nvme0n1",
|
||||
Rotate: false,
|
||||
Size: 1953514,
|
||||
Adapter: 0,
|
||||
Index: 0,
|
||||
},
|
||||
{
|
||||
Driver: DISK_DRIVER_PCIE,
|
||||
Dev: "nvme1n1",
|
||||
Rotate: false,
|
||||
Size: 122104,
|
||||
Adapter: 0,
|
||||
Index: 0,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestPCIEStoragesAllocable(t *testing.T) {
|
||||
adapter0 := 0
|
||||
confs := []*api.BaremetalDiskConfig{
|
||||
{
|
||||
Adapter: &adapter0,
|
||||
Conf: DISK_CONF_NONE,
|
||||
Count: 1,
|
||||
Driver: DISK_DRIVER_PCIE,
|
||||
Range: []int64{0},
|
||||
Type: DISK_TYPE_SSD,
|
||||
},
|
||||
{
|
||||
Adapter: &adapter0,
|
||||
Conf: DISK_CONF_NONE,
|
||||
Count: 1,
|
||||
Driver: DISK_DRIVER_LINUX,
|
||||
Range: []int64{0},
|
||||
Type: DISK_TYPE_ROTATE,
|
||||
},
|
||||
}
|
||||
|
||||
layouts, err := CalculateLayout(confs, pcieStorages)
|
||||
if err != nil {
|
||||
t.Errorf("CalculateLayout error: %v", err)
|
||||
return
|
||||
}
|
||||
// log.Errorf("layouts: %s", jsonutils.Marshal(layouts))
|
||||
disks := []*api.DiskConfig{
|
||||
{
|
||||
Backend: api.STORAGE_LOCAL,
|
||||
Driver: "scsi",
|
||||
SizeMb: 30720,
|
||||
},
|
||||
{
|
||||
Backend: api.STORAGE_LOCAL,
|
||||
Driver: "scsi",
|
||||
Fs: "ext4",
|
||||
Mountpoint: "/opt",
|
||||
SizeMb: -1,
|
||||
},
|
||||
{
|
||||
Backend: api.STORAGE_LOCAL,
|
||||
Driver: "scsi",
|
||||
Fs: "ext4",
|
||||
Mountpoint: "/data",
|
||||
SizeMb: -1,
|
||||
},
|
||||
}
|
||||
if ok := IsDisksAllocable(layouts, disks); !ok {
|
||||
t.Errorf("Disk not allocable")
|
||||
}
|
||||
}
|
||||
|
||||
func Test2PCIEStoragesAllocable(t *testing.T) {
|
||||
adapter0 := 0
|
||||
confs := []*api.BaremetalDiskConfig{
|
||||
{
|
||||
Adapter: &adapter0,
|
||||
Conf: DISK_CONF_NONE,
|
||||
Count: 0,
|
||||
Driver: DISK_DRIVER_PCIE,
|
||||
Range: []int64{1},
|
||||
Type: DISK_TYPE_SSD,
|
||||
},
|
||||
}
|
||||
|
||||
layouts, err := CalculateLayout(confs, pcie2Storages)
|
||||
if err != nil {
|
||||
t.Errorf("CalculateLayout error: %v", err)
|
||||
return
|
||||
}
|
||||
log.Errorf("layouts: %s", jsonutils.Marshal(layouts))
|
||||
disks := []*api.DiskConfig{
|
||||
{
|
||||
Backend: api.STORAGE_LOCAL,
|
||||
Driver: "scsi",
|
||||
SizeMb: -1,
|
||||
},
|
||||
}
|
||||
if ok := IsDisksAllocable(layouts, disks); !ok {
|
||||
t.Errorf("Disk not allocable")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ func (self *SBaremetalGuestDriver) GetNamedNetworkConfiguration(guest *models.SG
|
||||
}
|
||||
reuseAddr := false
|
||||
hn := host.GetAttach2Network(netConfig.Network)
|
||||
if hn != nil && netConfig.Address == "" && options.Options.BaremetalServerReuseHostIp {
|
||||
if hn != nil && options.Options.BaremetalServerReuseHostIp {
|
||||
// try to reuse host network IP address
|
||||
netConfig.Address = hn.IpAddr
|
||||
reuseAddr = true
|
||||
@@ -345,7 +345,7 @@ func (self *SBaremetalGuestDriver) RequestStopGuestForDelete(ctx context.Context
|
||||
return guest.StartGuestStopTask(ctx, task.GetUserCred(), true, false, task.GetTaskId())
|
||||
}
|
||||
if host != nil && !host.GetEnabled() && !purge {
|
||||
return fmt.Errorf("fail to contact baremetal")
|
||||
return errors.Errorf("fail to contact baremetal")
|
||||
}
|
||||
task.ScheduleRun(nil)
|
||||
return nil
|
||||
|
||||
@@ -300,6 +300,8 @@ func (manager *SCachedimageManager) GetImageById(ctx context.Context, userCred m
|
||||
oSTypeOk := options.Options.NoCheckOsTypeForCachedImage || len(cachedImage.GetOSType()) > 0
|
||||
if !refresh && cachedImage.GetStatus() == cloudprovider.IMAGE_STATUS_ACTIVE && oSTypeOk && !cachedImage.isRefreshSessionExpire() {
|
||||
return cachedImage.GetImage()
|
||||
} else if options.Options.ProhibitRefreshingCloudImage {
|
||||
return cachedImage.GetImage()
|
||||
} else if len(cachedImage.ExternalId) > 0 { // external image, request refresh
|
||||
return cachedImage.requestRefreshExternalImage(ctx, userCred)
|
||||
}
|
||||
@@ -323,6 +325,8 @@ func (manager *SCachedimageManager) getImageByName(ctx context.Context, userCred
|
||||
cachedImage := imgObj.(*SCachedimage)
|
||||
if !refresh && cachedImage.GetStatus() == cloudprovider.IMAGE_STATUS_ACTIVE && len(cachedImage.GetOSType()) > 0 && !cachedImage.isRefreshSessionExpire() {
|
||||
return cachedImage.GetImage()
|
||||
} else if options.Options.ProhibitRefreshingCloudImage {
|
||||
return cachedImage.GetImage()
|
||||
} else if len(cachedImage.ExternalId) > 0 { // external image, request refresh
|
||||
return cachedImage.requestRefreshExternalImage(ctx, userCred)
|
||||
}
|
||||
|
||||
@@ -2885,5 +2885,5 @@ func (self *SCloudaccount) PerformProjectMapping(ctx context.Context, userCred m
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, refreshMapping()
|
||||
return nil, refreshPmCaches()
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/proxy"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
@@ -52,6 +53,7 @@ type SCloudproviderManager struct {
|
||||
db.SEnabledStatusStandaloneResourceBaseManager
|
||||
db.SProjectizedResourceBaseManager
|
||||
|
||||
SProjectMappingResourceBaseManager
|
||||
SSyncableBaseResourceManager
|
||||
}
|
||||
|
||||
@@ -108,49 +110,67 @@ type SCloudprovider struct {
|
||||
|
||||
// 云账号的平台信息
|
||||
Provider string `width:"64" charset:"ascii" list:"domain" create:"domain_required"`
|
||||
|
||||
SProjectMappingResourceBase
|
||||
}
|
||||
|
||||
type providerMapping struct {
|
||||
Id string
|
||||
CloudaccountId string
|
||||
ProjectMappingId string
|
||||
type pmCache struct {
|
||||
Id string
|
||||
CloudaccountId string
|
||||
AccountProjectMappingId string
|
||||
ManagerProjectMappingId string
|
||||
}
|
||||
|
||||
var providerProjectMapping map[string]*providerMapping = map[string]*providerMapping{}
|
||||
func (self *pmCache) GetProjectMapping() (*SProjectMapping, error) {
|
||||
if len(self.ManagerProjectMappingId) > 0 {
|
||||
pm, err := GetRuleMapping(self.ManagerProjectMappingId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetRuleMapping(%s)", self.ManagerProjectMappingId)
|
||||
}
|
||||
if pm.Enabled.IsTrue() {
|
||||
return pm, nil
|
||||
}
|
||||
}
|
||||
if len(self.AccountProjectMappingId) > 0 {
|
||||
return GetRuleMapping(self.AccountProjectMappingId)
|
||||
}
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "empty project mapping id")
|
||||
}
|
||||
|
||||
func refreshMapping() error {
|
||||
q := CloudproviderManager.Query("cloudaccount_id", "id")
|
||||
var pmCaches map[string]*pmCache = map[string]*pmCache{}
|
||||
|
||||
func refreshPmCaches() error {
|
||||
q := CloudproviderManager.Query().SubQuery()
|
||||
providers := q.Query(q.Field("cloudaccount_id"), q.Field("id"), q.Field("project_mapping_id").Label("manager_project_mapping_id"))
|
||||
sq := CloudaccountManager.Query().SubQuery()
|
||||
q = q.LeftJoin(sq, sqlchemy.Equals(q.Field("cloudaccount_id"), sq.Field("id"))).AppendField(sq.Field("project_mapping_id"))
|
||||
pms := []providerMapping{}
|
||||
err := q.All(&pms)
|
||||
mq := providers.LeftJoin(sq, sqlchemy.Equals(q.Field("cloudaccount_id"), sq.Field("id"))).AppendField(sq.Field("project_mapping_id").Label("account_project_mapping_id"))
|
||||
caches := []pmCache{}
|
||||
err := mq.All(&caches)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "q.All")
|
||||
}
|
||||
for i := range pms {
|
||||
providerProjectMapping[pms[i].Id] = &pms[i]
|
||||
for i := range caches {
|
||||
pmCaches[caches[i].Id] = &caches[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *providerMapping) GetCloudaccount() (*SCloudaccount, error) {
|
||||
account, err := CloudaccountManager.FetchById(self.CloudaccountId)
|
||||
func (self *SCloudprovider) GetProjectMapping() (*SProjectMapping, error) {
|
||||
cache, err := func() (*pmCache, error) {
|
||||
mp, ok := pmCaches[self.Id]
|
||||
if ok {
|
||||
return mp, nil
|
||||
}
|
||||
err := refreshPmCaches()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "refreshPmCaches")
|
||||
}
|
||||
return pmCaches[self.Id], nil
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "FetchById(%s)", self.CloudaccountId)
|
||||
return nil, errors.Wrapf(err, "get project mapping cache")
|
||||
}
|
||||
return account.(*SCloudaccount), nil
|
||||
}
|
||||
|
||||
func GetProviderMapping(id string) (*providerMapping, error) {
|
||||
mp, ok := providerProjectMapping[id]
|
||||
if ok {
|
||||
return mp, nil
|
||||
}
|
||||
err := refreshMapping()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return providerProjectMapping[id], nil
|
||||
return cache.GetProjectMapping()
|
||||
}
|
||||
|
||||
func (self *SCloudprovider) ValidateDeleteCondition(ctx context.Context) error {
|
||||
@@ -976,6 +996,7 @@ func (manager *SCloudproviderManager) FetchCustomizeColumns(
|
||||
|
||||
stdRows := manager.SEnabledStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
projRows := manager.SProjectizedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
pmRows := manager.SProjectMappingResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
accountIds := make([]string, len(objs))
|
||||
for i := range rows {
|
||||
provider := objs[i].(*SCloudprovider)
|
||||
@@ -985,6 +1006,7 @@ func (manager *SCloudproviderManager) FetchCustomizeColumns(
|
||||
ProjectizedResourceInfo: projRows[i],
|
||||
SCloudproviderUsage: provider.getUsage(),
|
||||
SyncStatus2: provider.getSyncStatus2(),
|
||||
ProjectMappingResourceInfo: pmRows[i],
|
||||
}
|
||||
capabilities, _ := CloudproviderCapabilityManager.getCapabilities(provider.Id)
|
||||
if len(capabilities) > 0 {
|
||||
@@ -1193,6 +1215,10 @@ func (manager *SCloudproviderManager) ListItemFilter(
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.ListItemFilter")
|
||||
}
|
||||
q, err = manager.SProjectizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ProjectizedResourceListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "SProjectizedResourceBaseManager.ListItemFilter")
|
||||
}
|
||||
q, err = manager.SSyncableBaseResourceManager.ListItemFilter(ctx, q, userCred, query.SyncableBaseResourceListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SSyncableBaseResourceManager.ListItemFilter")
|
||||
@@ -1294,6 +1320,13 @@ func (manager *SCloudproviderManager) QueryDistinctExtraField(q *sqlchemy.SQuery
|
||||
return q, nil
|
||||
}
|
||||
|
||||
if field == "account" {
|
||||
accounts := CloudaccountManager.Query("name", "id").SubQuery()
|
||||
q.AppendField(accounts.Field("name", field)).Distinct()
|
||||
q = q.Join(accounts, sqlchemy.Equals(q.Field("cloudaccount_id"), accounts.Field("id")))
|
||||
return q, nil
|
||||
}
|
||||
|
||||
q, err = manager.SEnabledStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
|
||||
if err == nil {
|
||||
return q, nil
|
||||
@@ -1885,3 +1918,48 @@ func (self *SCloudprovider) SyncCallSyncCloudproviderInterVpcNetwork(ctx context
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (manager *SCloudproviderManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) {
|
||||
q, err := manager.SEnabledStatusStandaloneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SEnabledStatusStandaloneResourceBaseManager.ListItemExportKeys")
|
||||
}
|
||||
q, err = manager.SProjectizedResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "SProjectizedResourceBaseManager.ListItemExportKeys")
|
||||
}
|
||||
q, err = manager.SProjectMappingResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "SProjectMappingResourceBaseManager.ListItemExportKeys")
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (self *SCloudprovider) AllowPerformProjectMapping(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, self, "project-mapping")
|
||||
}
|
||||
|
||||
// 绑定同步策略
|
||||
func (self *SCloudprovider) PerformProjectMapping(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CloudaccountProjectMappingInput) (jsonutils.JSONObject, error) {
|
||||
if len(input.ProjectMappingId) > 0 {
|
||||
_, err := validators.ValidateModel(userCred, ProjectMappingManager, &input.ProjectMappingId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(self.ProjectMappingId) > 0 && self.ProjectMappingId != input.ProjectMappingId {
|
||||
return nil, httperrors.NewInputParameterError("cloudprovider %s has aleady bind project mapping %s", self.Name, self.ProjectMappingId)
|
||||
}
|
||||
}
|
||||
// no changes
|
||||
if self.ProjectMappingId == input.ProjectMappingId {
|
||||
return nil, nil
|
||||
}
|
||||
_, err := db.Update(self, func() error {
|
||||
self.ProjectMappingId = input.ProjectMappingId
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, refreshPmCaches()
|
||||
}
|
||||
|
||||
@@ -1400,20 +1400,21 @@ func (manager *SCloudproviderregionManager) initAllRecords() {
|
||||
|
||||
func SyncCloudProject(userCred mcclient.TokenCredential, model db.IVirtualModel, syncOwnerId mcclient.IIdentityProvider, extModel cloudprovider.IVirtualResource, managerId string) {
|
||||
newOwnerId, err := func() (mcclient.IIdentityProvider, error) {
|
||||
pm, err := GetProviderMapping(managerId)
|
||||
_manager, err := CloudproviderManager.FetchById(managerId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetProviderMapping")
|
||||
return nil, errors.Wrapf(err, "CloudproviderManager.FetchById(%s)", managerId)
|
||||
}
|
||||
if len(pm.ProjectMappingId) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
account, err := pm.GetCloudaccount()
|
||||
manager := _manager.(*SCloudprovider)
|
||||
rm, err := manager.GetProjectMapping()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetCloudaccount")
|
||||
if errors.Cause(err) == cloudprovider.ErrNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Wrapf(err, "GetProjectMapping")
|
||||
}
|
||||
rm, err := GetRuleMapping(pm.ProjectMappingId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetRuleMapping")
|
||||
account := manager.GetCloudaccount()
|
||||
if account == nil {
|
||||
return nil, fmt.Errorf("can not find manager %s account", manager.Name)
|
||||
}
|
||||
if rm != nil && rm.Enabled.Bool() {
|
||||
extTags, err := extModel.GetTags()
|
||||
|
||||
@@ -2152,6 +2152,19 @@ func (self *SGuest) findGuestnetworkByInfo(ipStr string, macStr string, index in
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SGuest) getReuseAddr(gn *SGuestnetwork) string {
|
||||
if self.GetHypervisor() != api.HYPERVISOR_BAREMETAL {
|
||||
return ""
|
||||
}
|
||||
hostNics := self.GetHost().GetNics()
|
||||
for _, hn := range hostNics {
|
||||
if hn.GetMac().String() == gn.MacAddr {
|
||||
return hn.IpAddr
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Change IPaddress of a guestnetwork
|
||||
// first detach the network, then attach a network with identity mac address but different IP configurations
|
||||
// TODO change IP address of a teaming NIC may fail!!
|
||||
@@ -2189,7 +2202,7 @@ func (self *SGuest) PerformChangeIpaddr(ctx context.Context, userCred mcclient.T
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = isValidNetworkInfo(userCred, conf)
|
||||
err = isValidNetworkInfo(userCred, conf, self.getReuseAddr(gn))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -2338,7 +2351,7 @@ func (self *SGuest) PerformAttachnetwork(ctx context.Context, userCred mcclient.
|
||||
}
|
||||
var inicCnt, enicCnt int
|
||||
for i := 0; i < count; i++ {
|
||||
err := isValidNetworkInfo(userCred, input.Nets[i])
|
||||
err := isValidNetworkInfo(userCred, input.Nets[i], "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1413,7 +1413,7 @@ func (manager *SGuestManager) validateCreateData(
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInputParameterError("parse network description error %s", err)
|
||||
}
|
||||
err = isValidNetworkInfo(userCred, netConfig)
|
||||
err = isValidNetworkInfo(userCred, netConfig, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -188,6 +188,9 @@ type SHost struct {
|
||||
|
||||
// IPv4地址,作为么有云vpc访问外网时的网关
|
||||
OvnMappedIpAddr string `width:"16" charset:"ascii" nullable:"true" list:"user"`
|
||||
|
||||
// UEFI详情
|
||||
UefiInfo jsonutils.JSONObject `nullable:"true" get:"domain" update:"domain" create:"domain_optional"`
|
||||
}
|
||||
|
||||
func (manager *SHostManager) GetContextManagers() [][]db.IModelManager {
|
||||
@@ -2445,7 +2448,7 @@ func (self *SHost) GetNetinterfacesWithIdAndCredential(netId string, userCred mc
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
if used == 0 && !reserved {
|
||||
if used == 0 && !reserved && !options.Options.BaremetalServerReuseHostIp {
|
||||
return nil, nil
|
||||
}
|
||||
matchNetIfs := make([]SNetInterface, 0)
|
||||
@@ -2863,16 +2866,11 @@ func (self *SHost) getMoreDetails(ctx context.Context, out api.HostDetails, show
|
||||
out.ServerIps = strings.Join(server.GetRealIPs(), ",")
|
||||
}
|
||||
}
|
||||
netifs := self.GetNetInterfaces()
|
||||
if netifs != nil && len(netifs) > 0 {
|
||||
nics := self.GetNics()
|
||||
if nics != nil && len(nics) > 0 {
|
||||
nicInfos := []jsonutils.JSONObject{}
|
||||
for i := 0; i < len(netifs); i += 1 {
|
||||
nicInfo := netifs[i].getBaremetalJsonDesc()
|
||||
if nicInfo == nil {
|
||||
log.Errorf("netif %s get baremetal desc failed", netifs[i].GetId())
|
||||
continue
|
||||
}
|
||||
nicInfos = append(nicInfos, nicInfo)
|
||||
for i := 0; i < len(nics); i += 1 {
|
||||
nicInfos = append(nicInfos, jsonutils.Marshal(nics[i]))
|
||||
}
|
||||
out.NicCount = len(nicInfos)
|
||||
out.NicInfo = nicInfos
|
||||
@@ -3527,6 +3525,10 @@ func (self *SHost) PostUpdate(ctx context.Context, userCred mcclient.TokenCreden
|
||||
log.Errorf("baremetal host %s update related server %s spec error: %v", self.GetName(), guest.GetName(), err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := self.startSyncConfig(ctx, userCred, "", true); err != nil {
|
||||
log.Errorf("start sync host %q config after updated", self.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SHost) UpdateDnsRecords(isAdd bool) {
|
||||
@@ -3932,6 +3934,25 @@ func (self *SHost) AllowPerformPrepare(ctx context.Context,
|
||||
return db.IsAdminAllowPerform(userCred, self, "prepare")
|
||||
}
|
||||
|
||||
func (self *SHost) HasBMC() bool {
|
||||
ipmiInfo, _ := self.GetIpmiInfo()
|
||||
if ipmiInfo.Username != "" && ipmiInfo.Password != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SHost) IsUEFIBoot() bool {
|
||||
info, _ := self.GetUEFIInfo()
|
||||
if info == nil {
|
||||
return false
|
||||
}
|
||||
if len(info.PxeBootNum) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SHost) isRedfishCapable() bool {
|
||||
ipmiInfo, _ := self.GetIpmiInfo()
|
||||
if ipmiInfo.Verified && ipmiInfo.RedfishApi {
|
||||
@@ -4274,6 +4295,7 @@ func (self *SHost) addNetif(ctx context.Context, userCred mcclient.TokenCredenti
|
||||
return httperrors.NewBadRequestError("%v", err)
|
||||
}
|
||||
}
|
||||
self.ClearSchedDescCache()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4586,6 +4608,7 @@ func (self *SHost) RemoveNetif(ctx context.Context, userCred mcclient.TokenCrede
|
||||
}
|
||||
}
|
||||
}
|
||||
self.ClearSchedDescCache()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5616,6 +5639,35 @@ func (host *SHost) GetIpmiInfo() (types.SIPMIInfo, error) {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (host *SHost) GetNics() []*types.SNic {
|
||||
netifs := host.GetNetInterfaces()
|
||||
nicInfos := []*types.SNic{}
|
||||
if netifs != nil && len(netifs) > 0 {
|
||||
for i := 0; i < len(netifs); i += 1 {
|
||||
desc := netifs[i].getBaremetalJsonDesc()
|
||||
if desc == nil {
|
||||
log.Errorf("netif %s get baremetal desc failed", netifs[i].GetId())
|
||||
continue
|
||||
}
|
||||
nicInfo := new(types.SNic)
|
||||
desc.Unmarshal(nicInfo)
|
||||
nicInfos = append(nicInfos, nicInfo)
|
||||
}
|
||||
}
|
||||
return nicInfos
|
||||
}
|
||||
|
||||
func (host *SHost) GetUEFIInfo() (*types.EFIBootMgrInfo, error) {
|
||||
if host.UefiInfo == nil {
|
||||
return nil, nil
|
||||
}
|
||||
info := new(types.EFIBootMgrInfo)
|
||||
if err := host.UefiInfo.Unmarshal(info); err != nil {
|
||||
return nil, errors.Wrap(err, "host.UefiInfo.Unmarshal")
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (self *SHost) AllowGetDetailsJnlp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowGetSpec(userCred, self, "jnlp")
|
||||
}
|
||||
@@ -5717,7 +5769,13 @@ func (self *SHost) PerformSyncConfig(ctx context.Context, userCred mcclient.Toke
|
||||
}
|
||||
|
||||
func (self *SHost) StartSyncConfig(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalSyncConfigTask", self, userCred, nil, parentTaskId, "", nil)
|
||||
return self.startSyncConfig(ctx, userCred, parentTaskId, false)
|
||||
}
|
||||
|
||||
func (self *SHost) startSyncConfig(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, noStatus bool) error {
|
||||
data := jsonutils.NewDict()
|
||||
data.Add(jsonutils.NewBool(noStatus), "not_sync_status")
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalSyncConfigTask", self, userCred, data, parentTaskId, "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -173,6 +173,59 @@ func (self *SNetwork) ValidateDeleteCondition(ctx context.Context) error {
|
||||
return self.SSharableVirtualResourceBase.ValidateDeleteCondition(ctx)
|
||||
}
|
||||
|
||||
func (nm *SNetworkManager) jointNetworkCount(manager db.IModelManager, netIds []string, filter func(*sqlchemy.SQuery) *sqlchemy.SQuery) *sqlchemy.SQuery {
|
||||
q := manager.Query("network_id").In("network_id", netIds)
|
||||
if filter != nil {
|
||||
q = filter(q)
|
||||
}
|
||||
q = q.AppendField(sqlchemy.COUNT("count"))
|
||||
return q
|
||||
}
|
||||
|
||||
func (nm *SNetworkManager) GetTotalNicCount(netIds []string) (map[string]int, error) {
|
||||
guestNetworkQ := nm.jointNetworkCount(GuestnetworkManager, netIds, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
|
||||
return q.Filter(sqlchemy.IsFalse(q.Field("virtual")))
|
||||
})
|
||||
|
||||
groupNetworkQ := nm.jointNetworkCount(GroupnetworkManager, netIds, nil)
|
||||
hostNetworkQ := nm.jointNetworkCount(HostnetworkManager, netIds, nil)
|
||||
lbNetworkQ := nm.jointNetworkCount(LoadbalancernetworkManager, netIds, nil)
|
||||
dbInstanceNetworkQ := nm.jointNetworkCount(DBInstanceNetworkManager, netIds, nil)
|
||||
eipNetworkQ := nm.jointNetworkCount(ElasticipManager, netIds, nil)
|
||||
natgatewayNetworkQ := nm.jointNetworkCount(NatGatewayManager, netIds, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
|
||||
return q.Filter(sqlchemy.IsNotEmpty(q.Field("ip_addr")))
|
||||
})
|
||||
|
||||
reserverIpsQ := nm.jointNetworkCount(ReservedipManager, netIds, filterExpiredReservedIps)
|
||||
|
||||
sq := NetworkinterfacenetworkManager.Query("networkinterface_id", "network_id").In("network_id", netIds).Distinct().SubQuery()
|
||||
networkInterfaceQ := NetworkInterfaceManager.Query()
|
||||
networkInterfaceQ = networkInterfaceQ.Join(sq, sqlchemy.Equals(networkInterfaceQ.Field("id"), sq.Field("networkinterface_id")))
|
||||
networkInterfaceQ.AppendField(sq.Field("network_id", "network_id"))
|
||||
networkInterfaceQ.AppendField(sqlchemy.COUNT("count"))
|
||||
networkInterfaceQ.GroupBy("network_id")
|
||||
|
||||
union, err := sqlchemy.UnionWithError(guestNetworkQ, groupNetworkQ, hostNetworkQ, lbNetworkQ, dbInstanceNetworkQ, eipNetworkQ, natgatewayNetworkQ, reserverIpsQ, networkInterfaceQ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
networkCounts := make([]struct {
|
||||
NetworkId string
|
||||
Count int
|
||||
}, 0)
|
||||
err = union.Query().All(&networkCounts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := map[string]int{}
|
||||
for _, nc := range networkCounts {
|
||||
if len(nc.NetworkId) > 0 {
|
||||
ret[nc.NetworkId] += nc.Count
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetTotalNicCount() (int, error) {
|
||||
count, err := self.GetAllocatedNicCount()
|
||||
if err != nil {
|
||||
@@ -1002,6 +1055,10 @@ func (self *SNetwork) GetFreeAddressCount() (int, error) {
|
||||
return self.getFreeAddressCount()
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetTotalAddressCount() int {
|
||||
return self.getIPRange().AddressCount()
|
||||
}
|
||||
|
||||
func (self *SNetwork) getFreeAddressCount() (int, error) {
|
||||
used, err := self.GetTotalNicCount()
|
||||
if err != nil {
|
||||
@@ -1010,7 +1067,7 @@ func (self *SNetwork) getFreeAddressCount() (int, error) {
|
||||
return self.getIPRange().AddressCount() - used, nil
|
||||
}
|
||||
|
||||
func isValidNetworkInfo(userCred mcclient.TokenCredential, netConfig *api.NetworkConfig) error {
|
||||
func isValidNetworkInfo(userCred mcclient.TokenCredential, netConfig *api.NetworkConfig, reuseAddr string) error {
|
||||
if len(netConfig.Network) > 0 {
|
||||
netObj, err := NetworkManager.FetchByIdOrName(userCred, netConfig.Network)
|
||||
if err != nil {
|
||||
@@ -1043,7 +1100,7 @@ func isValidNetworkInfo(userCred mcclient.TokenCredential, netConfig *api.Networ
|
||||
if err != nil {
|
||||
return httperrors.NewInternalServerError("isAddressUsed fail %s", err)
|
||||
}
|
||||
if used {
|
||||
if used && netConfig.Address != reuseAddr {
|
||||
return httperrors.NewInputParameterError("Address %s has been used", netConfig.Address)
|
||||
}
|
||||
}
|
||||
@@ -1051,10 +1108,18 @@ func isValidNetworkInfo(userCred mcclient.TokenCredential, netConfig *api.Networ
|
||||
if netConfig.BwLimit > api.MAX_BANDWIDTH {
|
||||
return httperrors.NewInputParameterError("Bandwidth limit cannot exceed %dMbps", api.MAX_BANDWIDTH)
|
||||
}
|
||||
if net.ServerType == api.NETWORK_TYPE_BAREMETAL {
|
||||
// not check baremetal network free address here
|
||||
// TODO: find better solution ?
|
||||
return nil
|
||||
}
|
||||
freeCnt, err := net.getFreeAddressCount()
|
||||
if err != nil {
|
||||
return httperrors.NewInternalServerError("getFreeAddressCount fail %s", err)
|
||||
}
|
||||
if reuseAddr != "" {
|
||||
freeCnt += 1
|
||||
}
|
||||
if freeCnt < 1 {
|
||||
return httperrors.NewInputParameterError("network %s(%s) has no free addresses", net.Name, net.Id)
|
||||
}
|
||||
|
||||
@@ -141,16 +141,22 @@ func (manager *SProjectMappingManager) FetchCustomizeColumns(
|
||||
mp := objs[i].(*SProjectMapping)
|
||||
mpIds[i] = mp.Id
|
||||
}
|
||||
accounts := []struct {
|
||||
type sBind struct {
|
||||
Id string
|
||||
Name string
|
||||
ProjectMappingId string
|
||||
}{}
|
||||
}
|
||||
accounts, managers := []sBind{}, []sBind{}
|
||||
q := CloudaccountManager.Query().In("project_mapping_id", mpIds)
|
||||
err := q.All(&accounts)
|
||||
if err != nil {
|
||||
return rows
|
||||
}
|
||||
q = CloudproviderManager.Query().In("project_mapping_id", mpIds)
|
||||
err = q.All(&managers)
|
||||
if err != nil {
|
||||
return rows
|
||||
}
|
||||
accountMapping := map[string][]api.SProjectMappingAccount{}
|
||||
for i := range accounts {
|
||||
_, ok := accountMapping[accounts[i].ProjectMappingId]
|
||||
@@ -163,9 +169,22 @@ func (manager *SProjectMappingManager) FetchCustomizeColumns(
|
||||
}
|
||||
accountMapping[accounts[i].ProjectMappingId] = append(accountMapping[accounts[i].ProjectMappingId], account)
|
||||
}
|
||||
managerMapping := map[string][]api.SProjectMappingAccount{}
|
||||
for i := range managers {
|
||||
_, ok := managerMapping[managers[i].ProjectMappingId]
|
||||
if !ok {
|
||||
managerMapping[managers[i].ProjectMappingId] = []api.SProjectMappingAccount{}
|
||||
}
|
||||
manager := api.SProjectMappingAccount{
|
||||
Id: managers[i].Id,
|
||||
Name: managers[i].Name,
|
||||
}
|
||||
managerMapping[managers[i].ProjectMappingId] = append(managerMapping[managers[i].ProjectMappingId], manager)
|
||||
}
|
||||
|
||||
for i := range rows {
|
||||
rows[i].Accounts, _ = accountMapping[mpIds[i]]
|
||||
rows[i].Managers, _ = managerMapping[mpIds[i]]
|
||||
}
|
||||
return rows
|
||||
}
|
||||
@@ -236,6 +255,16 @@ func (self *SProjectMapping) GetCloudaccounts() ([]SCloudaccount, error) {
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
func (self *SProjectMapping) GetCloudproviders() ([]SCloudprovider, error) {
|
||||
q := CloudproviderManager.Query().Equals("project_mapping_id", self.Id)
|
||||
ret := []SCloudprovider{}
|
||||
err := db.FetchModelObjects(CloudproviderManager, q, &ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SProjectMapping) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
delete(projectRuleMapping, self.Id)
|
||||
return self.SEnabledStatusInfrasResourceBase.Delete(ctx, userCred)
|
||||
@@ -249,6 +278,13 @@ func (self *SProjectMapping) ValidateDeleteCondition(ctx context.Context) error
|
||||
if len(accounts) > 0 {
|
||||
return httperrors.NewNotEmptyError("project mapping has associate %d accounts", len(accounts))
|
||||
}
|
||||
providers, err := self.GetCloudproviders()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetCloudproviders")
|
||||
}
|
||||
if len(providers) > 0 {
|
||||
return httperrors.NewNotEmptyError("project mapping has associate %d cloudproviders", len(providers))
|
||||
}
|
||||
return self.SEnabledStatusInfrasResourceBase.ValidateDeleteCondition(ctx)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
@@ -341,26 +342,19 @@ func (manager *SSnapshotManager) ValidateCreateData(
|
||||
ownerId mcclient.IIdentityProvider,
|
||||
query jsonutils.JSONObject,
|
||||
input api.SnapshotCreateInput,
|
||||
) (*jsonutils.JSONDict, error) {
|
||||
for _, disk := range []string{input.Disk, input.DiskId} {
|
||||
if len(disk) > 0 {
|
||||
input.Disk = disk
|
||||
break
|
||||
}
|
||||
) (api.SnapshotCreateInput, error) {
|
||||
if len(input.DiskId) == 0 {
|
||||
return input, httperrors.NewMissingParameterError("disk_id")
|
||||
}
|
||||
if len(input.Disk) == 0 {
|
||||
return nil, httperrors.NewMissingParameterError("disk")
|
||||
_disk, err := validators.ValidateModel(userCred, DiskManager, &input.DiskId)
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
|
||||
_disk, err := DiskManager.FetchByIdOrName(userCred, input.Disk)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, httperrors.NewResourceNotFoundError("failed to found disk %s", input.Disk)
|
||||
}
|
||||
return nil, httperrors.NewGeneralError(errors.Wrap(err, "DiskManager.FetchByIdOrName"))
|
||||
}
|
||||
disk := _disk.(*SDisk)
|
||||
input.DiskId = disk.Id
|
||||
if disk.Status != api.DISK_READY {
|
||||
return input, httperrors.NewInvalidStatusError("disk %s status is not %s", disk.Name, api.DISK_READY)
|
||||
}
|
||||
input.DiskType = disk.DiskType
|
||||
input.Size = disk.DiskSize
|
||||
input.OsArch = disk.OsArch
|
||||
@@ -372,38 +366,38 @@ func (manager *SSnapshotManager) ValidateCreateData(
|
||||
input.ManagerId = storage.ManagerId
|
||||
region := storage.GetRegion()
|
||||
if region == nil {
|
||||
return nil, httperrors.NewInputParameterError("failed to found region for disk's storage %s(%s)", storage.Name, storage.Id)
|
||||
return input, httperrors.NewInputParameterError("failed to found region for disk's storage %s(%s)", storage.Name, storage.Id)
|
||||
}
|
||||
input.CloudregionId = region.Id
|
||||
|
||||
driver, err := storage.GetRegionDriver()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "storage.GetRegionDriver")
|
||||
return input, errors.Wrap(err, "storage.GetRegionDriver")
|
||||
}
|
||||
input.OutOfChain = driver.SnapshotIsOutOfChain(disk)
|
||||
|
||||
err = driver.ValidateCreateSnapshotData(ctx, userCred, disk, storage, &input)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "driver.ValidateCreateSnapshotData")
|
||||
return input, errors.Wrap(err, "driver.ValidateCreateSnapshotData")
|
||||
}
|
||||
|
||||
input.VirtualResourceCreateInput, err = manager.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return input, err
|
||||
}
|
||||
|
||||
pendingUsage := &SRegionQuota{Snapshot: 1}
|
||||
keys, err := disk.GetQuotaKeys()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return input, err
|
||||
}
|
||||
pendingUsage.SetKeys(keys.(SComputeResourceKeys).SRegionalCloudResourceKeys)
|
||||
err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return input, err
|
||||
}
|
||||
|
||||
return input.JSON(input), nil
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (self *SSnapshot) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
|
||||
@@ -132,11 +132,11 @@ func (self *SStorage) ValidateUpdateData(ctx context.Context, userCred mcclient.
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
input.StorageConf = jsonutils.NewDict()
|
||||
if self.StorageConf != nil {
|
||||
input.StorageConf = self.StorageConf.(*jsonutils.JSONDict)
|
||||
} else {
|
||||
input.StorageConf = jsonutils.NewDict()
|
||||
input.StorageConf.Update(jsonutils.Marshal(self.StorageConf))
|
||||
}
|
||||
|
||||
driver := GetStorageDriver(self.StorageType)
|
||||
if driver != nil {
|
||||
return driver.ValidateUpdateData(ctx, userCred, input)
|
||||
|
||||
@@ -173,6 +173,8 @@ type ComputeOptions struct {
|
||||
|
||||
NoCheckOsTypeForCachedImage bool `help:"Don't check os type for cached image"`
|
||||
|
||||
ProhibitRefreshingCloudImage bool `help:"Prohibit refreshing cloud image"`
|
||||
|
||||
esxi.EsxiOptions
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ var (
|
||||
"natdentries",
|
||||
"policy_assignments",
|
||||
"proxysettings",
|
||||
"project_mappings",
|
||||
}
|
||||
computeUserResources = []string{
|
||||
"keypairs",
|
||||
|
||||
@@ -69,12 +69,13 @@ func (self *BaremetalMaintenanceTask) OnEnterMaintenantModeSucc(ctx context.Cont
|
||||
metadatas["__maint_guest_running"] = guestRunning
|
||||
}
|
||||
baremetal.SetAllMetadata(ctx, metadatas, self.UserCred)
|
||||
baremetal.StartSyncConfig(ctx, self.GetUserCred(), "")
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
func (self *BaremetalMaintenanceTask) OnEnterMaintenantModeSuccFailed(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) {
|
||||
self.SetStageFailed(ctx, body)
|
||||
baremetal.StartSyncstatus(ctx, self.UserCred, "")
|
||||
baremetal.StartSyncstatus(ctx, self.GetUserCred(), "")
|
||||
guest := baremetal.GetBaremetalServer()
|
||||
if guest != nil {
|
||||
guest.StartSyncstatus(ctx, self.UserCred, "")
|
||||
|
||||
@@ -57,6 +57,7 @@ func (self *BaremetalServerSyncStatusTask) OnInit(ctx context.Context, obj db.IS
|
||||
func (self *BaremetalServerSyncStatusTask) OnGuestStatusTaskComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
|
||||
var status string
|
||||
var hostStatus string
|
||||
host := guest.GetHost()
|
||||
if data.Contains("status") {
|
||||
statusStr, _ := data.GetString("status")
|
||||
switch statusStr {
|
||||
@@ -69,6 +70,9 @@ func (self *BaremetalServerSyncStatusTask) OnGuestStatusTaskComplete(ctx context
|
||||
case "admin":
|
||||
status = api.VM_ADMIN
|
||||
hostStatus = api.HOST_STATUS_RUNNING
|
||||
if !host.IsMaintenance && !host.HasBMC() {
|
||||
status = api.VM_READY
|
||||
}
|
||||
default:
|
||||
status = api.VM_INIT
|
||||
hostStatus = api.HOST_STATUS_UNKNOWN
|
||||
@@ -78,7 +82,6 @@ func (self *BaremetalServerSyncStatusTask) OnGuestStatusTaskComplete(ctx context
|
||||
hostStatus = api.HOST_STATUS_UNKNOWN
|
||||
}
|
||||
guest.SetStatus(self.UserCred, status, "BaremetalServerSyncStatusTask")
|
||||
host := guest.GetHost()
|
||||
host.SetStatus(self.UserCred, hostStatus, "BaremetalServerSyncStatusTask")
|
||||
|
||||
self.SetStageComplete(ctx, nil)
|
||||
|
||||
@@ -54,9 +54,14 @@ func (self *BaremetalSyncConfigTask) DoSyncConfig(ctx context.Context, baremetal
|
||||
}
|
||||
|
||||
func (self *BaremetalSyncConfigTask) OnSyncConfigComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) {
|
||||
logclient.AddActionLogWithStartable(self, baremetal, logclient.ACT_SYNC_CONF, "", self.UserCred, true)
|
||||
notSyncStatus, _ := self.Params.Bool("not_sync_status")
|
||||
if notSyncStatus {
|
||||
self.SetStageComplete(ctx, nil)
|
||||
return
|
||||
}
|
||||
self.SetStage("OnSyncstatusComplete", nil)
|
||||
baremetal.StartSyncstatus(ctx, self.UserCred, self.GetTaskId())
|
||||
logclient.AddActionLogWithStartable(self, baremetal, logclient.ACT_SYNC_CONF, "", self.UserCred, true)
|
||||
}
|
||||
|
||||
func (self *BaremetalSyncConfigTask) OnSyncstatusComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) {
|
||||
|
||||
@@ -55,6 +55,10 @@ func (self *BaremetalSyncStatusTask) OnSyncstatusComplete(ctx context.Context, b
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
func (self *BaremetalSyncStatusTask) OnSyncstatusCompleteFailed(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) {
|
||||
self.SetStageFailed(ctx, body)
|
||||
}
|
||||
|
||||
type BaremetalSyncAllGuestsStatusTask struct {
|
||||
SBaremetalBaseTask
|
||||
}
|
||||
@@ -105,6 +109,10 @@ func (self *BaremetalSyncAllGuestsStatusTask) OnGuestSyncStatusComplete(ctx cont
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
func (self *BaremetalSyncAllGuestsStatusTask) OnGuestSyncStatusCompleteFailed(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) {
|
||||
self.SetStageFailed(ctx, body)
|
||||
}
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(BaremetalSyncStatusTask{})
|
||||
taskman.RegisterTask(BaremetalSyncAllGuestsStatusTask{})
|
||||
|
||||
@@ -63,10 +63,11 @@ func (self *BaremetalUnmaintenanceTask) OnUnmaintenantComplete(ctx context.Conte
|
||||
"__maint_password": "None",
|
||||
"__maint_ip": "None",
|
||||
}
|
||||
baremetal.SetAllMetadata(ctx, metadatas, self.UserCred)
|
||||
baremetal.SetAllMetadata(ctx, metadatas, self.GetUserCred())
|
||||
self.SetStageComplete(ctx, nil)
|
||||
guest := baremetal.GetBaremetalServer()
|
||||
if guest != nil {
|
||||
guest.StartSyncstatus(ctx, self.UserCred, "")
|
||||
guest.StartSyncstatus(ctx, self.GetUserCred(), "")
|
||||
}
|
||||
baremetal.StartSyncConfig(ctx, self.GetUserCred(), "")
|
||||
}
|
||||
|
||||
@@ -364,15 +364,38 @@ func GetArgs(ctx context.Context, serverId, proxyEndpointId string, others inter
|
||||
if len(influxdbUrl) == 0 {
|
||||
return nil, errors.Wrap(ErrCannotReachInfluxbd, "please create usable Proxy Endpoint for server and try again")
|
||||
}
|
||||
vmId := info.serverDetails.Id
|
||||
tenantId := info.serverDetails.ProjectId
|
||||
domainId := info.serverDetails.DomainId
|
||||
tags := map[string]string{
|
||||
"host": info.serverDetails.Host,
|
||||
"host_id": info.serverDetails.HostId,
|
||||
"vm_id": info.serverDetails.Id,
|
||||
"vm_ip": info.serverDetails.IPs,
|
||||
"vm_name": info.serverDetails.Name,
|
||||
"zone": info.serverDetails.Zone,
|
||||
"zone_id": info.serverDetails.ZoneId,
|
||||
"zone_ext_id": info.serverDetails.ZoneExtId,
|
||||
"os_type": info.serverDetails.OsType,
|
||||
"status": info.serverDetails.Status,
|
||||
"cloudregion": info.serverDetails.Cloudregion,
|
||||
"cloudregion_id": info.serverDetails.CloudregionId,
|
||||
"region_ext_id": info.serverDetails.RegionExtId,
|
||||
"tenant": info.serverDetails.Tenant,
|
||||
"tenant_id": info.serverDetails.TenantId,
|
||||
"brand": info.serverDetails.Brand,
|
||||
"scaling_group_id": info.serverDetails.ScalingGroupId,
|
||||
"domain_id": info.serverDetails.DomainId,
|
||||
"project_domain": info.serverDetails.ProjectDomain,
|
||||
}
|
||||
telegrafTags := make([]map[string]string, 0, len(tags))
|
||||
for name, value := range tags {
|
||||
telegrafTags = append(telegrafTags, map[string]string{
|
||||
"tag_name": name,
|
||||
"tag_value": value,
|
||||
})
|
||||
}
|
||||
ret := map[string]interface{}{
|
||||
"influxdb_url": influxdbUrl,
|
||||
"influxdb_name": "telegraf",
|
||||
"onecloud_vm_id": vmId,
|
||||
"onecloud_tenant_id": tenantId,
|
||||
"onecloud_domain_id": domainId,
|
||||
"influxdb_url": influxdbUrl,
|
||||
"influxdb_name": "telegraf",
|
||||
"telegraf_global_tags": telegrafTags,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -263,6 +263,8 @@ func ResizePartitionFs(fpath, fs string, raiseError bool) (error, bool) {
|
||||
{"umount", tmpPoint},
|
||||
{"sleep", "2"},
|
||||
{"rm", "-fr", tmpPoint}}
|
||||
} else if fs == "ntfs" {
|
||||
cmds = [][]string{{"ntfsresize", "-c", fpath}, {"ntfsresize", "-P", "-f", fpath}}
|
||||
}
|
||||
|
||||
if len(cmds) > 0 {
|
||||
|
||||
@@ -207,6 +207,10 @@ func (d *SLibguestfsDriver) Zerofree() {
|
||||
}
|
||||
|
||||
func (d *SLibguestfsDriver) ResizePartition() error {
|
||||
if d.IsLVMPartition() {
|
||||
// do not try to resize LVM partition
|
||||
return nil
|
||||
}
|
||||
return fsutils.ResizeDiskFs(d.nbddev, 0)
|
||||
}
|
||||
|
||||
|
||||
@@ -241,6 +241,10 @@ func (d *NBDDriver) FormatPartition(fs, uuid string) error {
|
||||
}
|
||||
|
||||
func (d *NBDDriver) ResizePartition() error {
|
||||
if d.IsLVMPartition() {
|
||||
// do not resize LVM partition
|
||||
return nil
|
||||
}
|
||||
return fsutils.ResizeDiskFs(d.nbdDev, 0)
|
||||
}
|
||||
|
||||
|
||||
@@ -536,6 +536,14 @@ func (d *sDebianLikeRootFs) PrepareFsForTemplate(rootFs IDiskPartition) error {
|
||||
return errors.Wrap(err, "file put content /etc/network/interface")
|
||||
}
|
||||
}
|
||||
|
||||
// clean /etc/netplan/*
|
||||
netplanDir := "/etc/netplan/"
|
||||
if rootFs.Exists(netplanDir, false) {
|
||||
for _, f := range rootFs.ListDir(netplanDir, false) {
|
||||
rootFs.Remove(netplanDir+f, false)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -577,6 +585,16 @@ func getNicTeamingConfigCmds(slaves []*types.SServerNic) string {
|
||||
return cmds.String()
|
||||
}
|
||||
|
||||
func (d *sDebianLikeRootFs) deployNetplanConfigFile(rootFs IDiskPartition, nics []*types.SServerNic) error {
|
||||
netplanDir := "/etc/netplan/"
|
||||
dirExists := rootFs.Exists(netplanDir, false)
|
||||
if !dirExists {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics []*types.SServerNic) error {
|
||||
if err := d.sLinuxRootFs.DeployNetworkingScripts(rootFs, nics); err != nil {
|
||||
return err
|
||||
@@ -587,7 +605,19 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics
|
||||
cmds.WriteString("iface lo inet loopback\n\n")
|
||||
|
||||
// ToServerNics(nics)
|
||||
allNics, _ := convertNicConfigs(nics)
|
||||
allNics, bondNics := convertNicConfigs(nics)
|
||||
|
||||
netplanDir := "/etc/netplan"
|
||||
if rootFs.Exists(netplanDir, false) {
|
||||
for _, f := range rootFs.ListDir(netplanDir, false) {
|
||||
rootFs.Remove(netplanDir+f, false)
|
||||
}
|
||||
netplanConfig := NewNetplanConfig(allNics, bondNics)
|
||||
if err := rootFs.FilePutContents(path.Join(netplanDir, "config.yaml"), netplanConfig.YAMLString(), false, false); err != nil {
|
||||
return errors.Wrap(err, "Put netplan config")
|
||||
}
|
||||
}
|
||||
|
||||
mainNic, err := getMainNic(allNics)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -596,6 +626,10 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics
|
||||
if mainNic != nil {
|
||||
mainIp = mainNic.Ip
|
||||
}
|
||||
|
||||
var systemdResolveConfig strings.Builder
|
||||
dnss := []string{}
|
||||
domains := []string{}
|
||||
for i := range allNics {
|
||||
nicDesc := allNics[i]
|
||||
cmds.WriteString(fmt.Sprintf("auto %s\n", nicDesc.Name))
|
||||
@@ -628,8 +662,10 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics
|
||||
dnslist := netutils2.GetNicDns(nicDesc)
|
||||
if len(dnslist) > 0 {
|
||||
cmds.WriteString(fmt.Sprintf(" dns-nameservers %s\n", strings.Join(dnslist, " ")))
|
||||
dnss = append(dnss, dnslist...)
|
||||
if len(nicDesc.Domain) > 0 {
|
||||
cmds.WriteString(fmt.Sprintf(" dns-search %s\n", nicDesc.Domain))
|
||||
domains = append(domains, nicDesc.Domain)
|
||||
}
|
||||
}
|
||||
if len(nicDesc.TeamingSlaves) > 0 {
|
||||
@@ -644,6 +680,19 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics
|
||||
cmds.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
if len(dnss) != 0 {
|
||||
systemdResolveConfig.WriteString("[Resolve]\n")
|
||||
systemdResolveConfig.WriteString(fmt.Sprintf("DNS=%s\n", strings.Join(dnss, " ")))
|
||||
if len(domains) != 0 {
|
||||
systemdResolveConfig.WriteString(fmt.Sprintf("Domains=%s\n", strings.Join(domains, " ")))
|
||||
}
|
||||
systemdResolveFn := "/etc/systemd/resolved.conf"
|
||||
content := systemdResolveConfig.String()
|
||||
if err := rootFs.FilePutContents(systemdResolveFn, content, false, false); err != nil {
|
||||
log.Warningf("Put %s to %s error: %v", content, systemdResolveFn, err)
|
||||
}
|
||||
}
|
||||
log.Debugf("%s", cmds.String())
|
||||
return rootFs.FilePutContents(fn, cmds.String(), false, false)
|
||||
}
|
||||
|
||||
125
pkg/hostman/guestfs/fsdriver/netplan.go
Normal file
125
pkg/hostman/guestfs/fsdriver/netplan.go
Normal file
@@ -0,0 +1,125 @@
|
||||
// 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 fsdriver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
"yunion.io/x/onecloud/pkg/util/netplan"
|
||||
"yunion.io/x/onecloud/pkg/util/netutils2"
|
||||
)
|
||||
|
||||
func NewNetplanConfig(allNics []*types.SServerNic, bondNics []*types.SServerNic) *netplan.Configuration {
|
||||
network := newNetplanNetwork(allNics, bondNics)
|
||||
return netplan.NewConfiguration(network)
|
||||
}
|
||||
|
||||
func newNetplanNetwork(allNics []*types.SServerNic, bondNics []*types.SServerNic) *netplan.Network {
|
||||
network := netplan.NewNetwork()
|
||||
|
||||
for _, nic := range allNics {
|
||||
nicConf := getNetplanEthernetConfig(nic, false)
|
||||
|
||||
if nicConf == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
network.AddEthernet(nic.Name, nicConf)
|
||||
}
|
||||
|
||||
for _, bondNic := range bondNics {
|
||||
if len(bondNic.TeamingSlaves) < 2 {
|
||||
log.Warningf("BondNic %s slaves nic %#v less than 2", bondNic.Name, bondNic.TeamingSlaves)
|
||||
continue
|
||||
}
|
||||
|
||||
var defaultMtu = 1442
|
||||
|
||||
interfaces := make([]string, len(bondNic.TeamingSlaves))
|
||||
for i, sn := range bondNic.TeamingSlaves {
|
||||
interfaces[i] = sn.Name
|
||||
|
||||
nicConf := &netplan.EthernetConfig{
|
||||
DHCP4: false,
|
||||
MacAddress: sn.Mac,
|
||||
Match: netplan.NewEthernetConfigMatchMac(sn.Mac),
|
||||
}
|
||||
|
||||
if sn.Mtu > 0 {
|
||||
nicConf.Mtu = sn.Mtu
|
||||
} else {
|
||||
nicConf.Mtu = defaultMtu
|
||||
}
|
||||
|
||||
network.AddEthernet(sn.Name, nicConf)
|
||||
}
|
||||
|
||||
primaryNic := bondNic.TeamingSlaves[0]
|
||||
netConf := getNetplanEthernetConfig(primaryNic, true)
|
||||
netConf.MacAddress = primaryNic.Mac
|
||||
|
||||
if netConf.Mtu == 0 {
|
||||
netConf.Mtu = defaultMtu
|
||||
}
|
||||
|
||||
// TODO: implement kinds of bond mode config
|
||||
// bondConf := netplan.NewBondMode4(netConf, interfaces)
|
||||
bondConf := netplan.NewBondMode1(netConf, interfaces)
|
||||
|
||||
network.AddBond(bondNic.Name, bondConf)
|
||||
}
|
||||
|
||||
return network
|
||||
}
|
||||
|
||||
func getNetplanEthernetConfig(nic *types.SServerNic, isBond bool) *netplan.EthernetConfig {
|
||||
var nicConf *netplan.EthernetConfig
|
||||
|
||||
if !isBond && (nic.TeamingMaster != nil || nic.TeamingSlaves != nil) {
|
||||
return nil
|
||||
} else if nic.Virtual {
|
||||
addr := fmt.Sprintf("%s/32", netutils2.PSEUDO_VIP)
|
||||
nicConf = netplan.NewStaticEthernetConfig(addr, "", nil, nil, nil)
|
||||
} else if nic.Manual {
|
||||
addr := fmt.Sprintf("%s/%d", nic.Ip, nic.Masklen)
|
||||
gateway := nic.Gateway
|
||||
var routes []*netplan.Route
|
||||
|
||||
for _, route := range nic.Routes {
|
||||
routes = append(routes, &netplan.Route{
|
||||
To: route[0],
|
||||
Via: route[1],
|
||||
})
|
||||
}
|
||||
|
||||
nicConf = netplan.NewStaticEthernetConfig(
|
||||
addr, gateway,
|
||||
[]string{nic.Domain},
|
||||
netutils2.GetNicDns(nic),
|
||||
routes,
|
||||
)
|
||||
if nic.Mtu > 0 {
|
||||
nicConf.Mtu = nic.Mtu
|
||||
}
|
||||
} else {
|
||||
// dhcp
|
||||
nicConf = netplan.NewDHCP4EthernetConfig()
|
||||
}
|
||||
|
||||
return nicConf
|
||||
}
|
||||
@@ -149,6 +149,14 @@ func (w *SWindowsRootFs) IsWindows10() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *SWindowsRootFs) IsOldWindows() bool {
|
||||
info := w.GetReleaseInfo(nil)
|
||||
if info != nil && strings.HasPrefix(info.Version, "5.") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *SWindowsRootFs) GetOs() string {
|
||||
return "Windows"
|
||||
}
|
||||
@@ -332,6 +340,12 @@ func (w *SWindowsRootFs) CommitChanges(part IDiskPartition) error {
|
||||
tool := winutils.NewWinRegTool(confPath)
|
||||
tool.CheckPath()
|
||||
tool.EnableRdp()
|
||||
|
||||
if w.IsOldWindows() {
|
||||
// windows prior to windows 2003 should not try to commit changes
|
||||
return nil
|
||||
}
|
||||
|
||||
tool.InstallGpeditStartScript(WIN_BOOT_SCRIPT_PATH)
|
||||
|
||||
bootDir := path.Dir(BOOT_SCRIPT_PATH)
|
||||
|
||||
@@ -593,7 +593,7 @@ func (s *SGuestResumeTask) taskFailed(reason string) {
|
||||
if s.ctx != nil && len(appctx.AppContextTaskId(s.ctx)) > 0 {
|
||||
hostutils.TaskFailed(s.ctx, reason)
|
||||
} else {
|
||||
s.SyncStatus()
|
||||
s.SyncStatus(reason)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,12 +632,12 @@ func (s *SGuestResumeTask) onStartRunning() {
|
||||
if len(disksIdx) > 0 {
|
||||
s.startStreamDisks(disksIdx)
|
||||
} else if options.HostOptions.AutoMergeBackingTemplate {
|
||||
s.SyncStatus()
|
||||
s.SyncStatus("")
|
||||
timeutils2.AddTimeout(
|
||||
time.Second*time.Duration(options.HostOptions.AutoMergeDelaySeconds),
|
||||
func() { s.startStreamDisks(nil) })
|
||||
} else {
|
||||
s.SyncStatus()
|
||||
s.SyncStatus("")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,7 +663,7 @@ func (s *SGuestResumeTask) startStreamDisks(disksIdx []int) {
|
||||
func (s *SGuestResumeTask) onStreamComplete(disksIdx []int) {
|
||||
if len(disksIdx) == 0 {
|
||||
// if disks idx length == 0 indicate merge backing template
|
||||
s.SyncStatus()
|
||||
s.SyncStatus("")
|
||||
} else {
|
||||
s.streamDisksComplete(context.Background())
|
||||
}
|
||||
@@ -747,7 +747,7 @@ func (s *SGuestStreamDisksTask) onBlockDrivesSucc(res *jsonutils.JSONArray) {
|
||||
s.taskComplete()
|
||||
} else {
|
||||
s.startDoBlockStream()
|
||||
s.SyncStatus()
|
||||
s.SyncStatus("")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,7 +783,7 @@ func (s *SGuestStreamDisksTask) checkStreamJobs(jobs int) {
|
||||
}
|
||||
|
||||
func (s *SGuestStreamDisksTask) taskComplete() {
|
||||
s.SyncStatus()
|
||||
s.SyncStatus("")
|
||||
|
||||
// XXX: region disk post-migrate not implement
|
||||
|
||||
|
||||
@@ -279,14 +279,13 @@ func (s *SKVMGuestInstance) asyncScriptStart(ctx context.Context, params interfa
|
||||
s.syncMeta = s.CleanImportMetadata()
|
||||
s.StartMonitor(ctx)
|
||||
return nil, nil
|
||||
} else {
|
||||
log.Infof("Async start server %s failed: %s!!!", s.GetName(), err)
|
||||
if ctx != nil && len(appctx.AppContextTaskId(ctx)) >= 0 {
|
||||
hostutils.TaskFailed(ctx, fmt.Sprintf("Async start server failed: %s", err))
|
||||
}
|
||||
s.SyncStatus()
|
||||
return nil, err
|
||||
}
|
||||
log.Infof("Async start server %s failed: %s!!!", s.GetName(), err)
|
||||
if ctx != nil && len(appctx.AppContextTaskId(ctx)) >= 0 {
|
||||
hostutils.TaskFailed(ctx, fmt.Sprintf("Async start server failed: %s", err))
|
||||
}
|
||||
s.SyncStatus("")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) saveScripts(data *jsonutils.JSONDict) error {
|
||||
@@ -335,7 +334,7 @@ func (s *SKVMGuestInstance) ImportServer(pendingDelete bool) {
|
||||
}
|
||||
log.Infof("%s is %s, pending_delete=%t", s.GetName(), action, pendingDelete)
|
||||
if !s.IsSlave() {
|
||||
s.SyncStatus()
|
||||
s.SyncStatus("")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -374,8 +373,8 @@ func (s *SKVMGuestInstance) IsMonitorAlive() bool {
|
||||
// }
|
||||
|
||||
func (s *SKVMGuestInstance) onImportGuestMonitorDisConnect(err error) {
|
||||
log.Infof("Import Guest %s monitor disconnect", s.Id)
|
||||
s.SyncStatus()
|
||||
log.Infof("Import Guest %s monitor disconnect reason: %v", s.Id, err)
|
||||
s.SyncStatus(fmt.Sprintf("import guest monitor disconnect %v", err))
|
||||
|
||||
// clean import pid file
|
||||
if s.GetPid() == -2 {
|
||||
@@ -407,7 +406,7 @@ func (s *SKVMGuestInstance) onImportGuestMonitorConnected(ctx context.Context) {
|
||||
meta.Set("hot_remove_nic", jsonutils.NewString("disable"))
|
||||
meta.Set("__qemu_version", jsonutils.NewString(s.GetQemuVersionStr()))
|
||||
s.SyncMetadata(meta)
|
||||
s.SyncStatus()
|
||||
s.SyncStatus("")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -470,7 +469,7 @@ func (s *SKVMGuestInstance) onReceiveQMPEvent(event *monitor.Event) {
|
||||
if stype == "mirror" {
|
||||
mirrorStatus := s.MirrorJobStatus()
|
||||
if mirrorStatus.IsSucc() {
|
||||
_, err := hostutils.UpdateServerStatus(context.Background(), s.GetId(), compute.VM_RUNNING)
|
||||
_, err := hostutils.UpdateServerStatus(context.Background(), s.GetId(), compute.VM_RUNNING, "BLOCK_JOB_READY")
|
||||
if err != nil {
|
||||
log.Errorf("onReceiveQMPEvent update server status error: %s", err)
|
||||
}
|
||||
@@ -549,11 +548,11 @@ func (s *SKVMGuestInstance) onGetQemuVersion(ctx context.Context, version string
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) onMonitorDisConnect(err error) {
|
||||
log.Infof("Guest %s on Monitor Disconnect", s.Id)
|
||||
log.Errorf("Guest %s on Monitor Disconnect reason: %v", s.Id, err)
|
||||
s.CleanStartupTask()
|
||||
s.scriptStop()
|
||||
if !jsonutils.QueryBoolean(s.Desc, "is_slave", false) {
|
||||
s.SyncStatus()
|
||||
s.SyncStatus(fmt.Sprintf("monitor disconnect %v", err))
|
||||
}
|
||||
s.clearCgroup(0)
|
||||
s.Monitor = nil
|
||||
@@ -569,7 +568,7 @@ func (s *SKVMGuestInstance) startDiskBackupMirror(ctx context.Context) {
|
||||
status = compute.VM_BLOCK_STREAM_FAIL
|
||||
s.SyncMirrorJobFailed("mirror job missing")
|
||||
}
|
||||
hostutils.UpdateServerStatus(context.Background(), s.GetId(), status)
|
||||
hostutils.UpdateServerStatus(context.Background(), s.GetId(), status, "")
|
||||
} else {
|
||||
metadata, _ := s.Desc.Get("metadata")
|
||||
if metadata == nil || !metadata.Contains("backup_nbd_server_uri") {
|
||||
@@ -748,7 +747,7 @@ func (s *SKVMGuestInstance) DoResumeTask(ctx context.Context) {
|
||||
s.startupTask.Start()
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) SyncStatus() {
|
||||
func (s *SKVMGuestInstance) SyncStatus(reason string) {
|
||||
if s.IsRunning() {
|
||||
s.Monitor.GetBlockJobCounts(s.CheckBlockOrRunning)
|
||||
return
|
||||
@@ -758,7 +757,7 @@ func (s *SKVMGuestInstance) SyncStatus() {
|
||||
status = "suspend"
|
||||
}
|
||||
|
||||
hostutils.UpdateServerStatus(context.Background(), s.Id, status)
|
||||
hostutils.UpdateServerStatus(context.Background(), s.Id, status, reason)
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) CheckBlockOrRunning(jobs int) {
|
||||
@@ -776,7 +775,7 @@ func (s *SKVMGuestInstance) CheckBlockOrRunning(jobs int) {
|
||||
status = compute.VM_BLOCK_STREAM
|
||||
}
|
||||
}
|
||||
_, err := hostutils.UpdateServerStatus(context.Background(), s.Id, status)
|
||||
_, err := hostutils.UpdateServerStatus(context.Background(), s.Id, status, "")
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
|
||||
@@ -89,6 +89,22 @@ func (s *SKVMGuestInstance) getOsDistribution() string {
|
||||
return osDis
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) getOsVersion() string {
|
||||
osVer, _ := s.Desc.GetString("metadata", "os_version")
|
||||
return osVer
|
||||
}
|
||||
|
||||
// is windows prioer to windows server 2003
|
||||
func (s *SKVMGuestInstance) isOldWindows() bool {
|
||||
if s.getOsname() == OS_NAME_WINDOWS {
|
||||
ver := s.getOsVersion()
|
||||
if len(ver) > 1 && ver[0:2] == "5." {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) getMachine() string {
|
||||
machine, err := s.Desc.GetString("machine")
|
||||
if err != nil {
|
||||
@@ -527,13 +543,15 @@ function nic_mtu() {
|
||||
|
||||
cmd += " -device virtio-serial"
|
||||
cmd += " -usb"
|
||||
if !utils.IsInStringArray(s.getOsDistribution(), []string{OS_NAME_OPENWRT, OS_NAME_CIRROS}) && !s.disableUsbKbd() {
|
||||
if !utils.IsInStringArray(s.getOsDistribution(), []string{OS_NAME_OPENWRT, OS_NAME_CIRROS}) && !s.isOldWindows() && !s.disableUsbKbd() {
|
||||
cmd += " -device usb-kbd"
|
||||
}
|
||||
// # if osname == self.OS_NAME_ANDROID:
|
||||
// # cmd += " -device usb-mouse"
|
||||
// # else:
|
||||
cmd += " -device usb-tablet"
|
||||
if !s.isOldWindows() {
|
||||
cmd += " -device usb-tablet"
|
||||
}
|
||||
|
||||
if s.IsVdiSpice() {
|
||||
cmd += " -device qxl-vga,id=video0,ram_size=141557760,vram_size=141557760"
|
||||
|
||||
@@ -136,9 +136,12 @@ func RemoteStoragecacheCacheImage(ctx context.Context, storagecacheId, imageId,
|
||||
storagecacheId, imageId, query, params)
|
||||
}
|
||||
|
||||
func UpdateServerStatus(ctx context.Context, sid, status string) (jsonutils.JSONObject, error) {
|
||||
func UpdateServerStatus(ctx context.Context, sid, status, reason string) (jsonutils.JSONObject, error) {
|
||||
var stats = jsonutils.NewDict()
|
||||
stats.Set("status", jsonutils.NewString(status))
|
||||
if len(reason) > 0 {
|
||||
stats.Set("reason", jsonutils.NewString(reason))
|
||||
}
|
||||
return modules.Servers.PerformAction(GetComputeSession(ctx), sid, "status", stats)
|
||||
}
|
||||
|
||||
|
||||
@@ -347,7 +347,7 @@ func StartSyncStorageSizeTask(interval time.Duration) {
|
||||
manager := GetManager()
|
||||
for i := 0; i < len(manager.Storages); i++ {
|
||||
iS := manager.Storages[i]
|
||||
if iS.StorageType() == api.STORAGE_LOCAL {
|
||||
if iS.StorageType() == api.STORAGE_LOCAL || iS.StorageType() == api.STORAGE_RBD {
|
||||
err := iS.SyncStorageSize()
|
||||
if err != nil {
|
||||
log.Errorf("sync storage %s size failed: %s", iS.GetStorageName(), err)
|
||||
|
||||
@@ -547,35 +547,77 @@ type SMonCommand struct {
|
||||
Format string
|
||||
}
|
||||
|
||||
func (s *SRbdStorage) getCapacity() (uint64, error) {
|
||||
_sizeKb, err := s.withCluster(func(conn *rados.Conn) (interface{}, error) {
|
||||
stats, err := conn.GetClusterStats()
|
||||
type sCephCapacity struct {
|
||||
CapacitySizeByte int64
|
||||
UsedCapacitySizeByte int64
|
||||
}
|
||||
|
||||
type sCephDfResult struct {
|
||||
Stats struct {
|
||||
TotalBytes int64 `json:"total_bytes"`
|
||||
TotalAvailBytes int64 `json:"total_avail_bytes"`
|
||||
TotalUsedBytes int64 `json:"total_used_bytes"`
|
||||
TotalUsedRawBytes int64 `json:"total_used_raw_bytes"`
|
||||
TotalUsedRawRatio float64 `json:"total_used_raw_ratio"`
|
||||
NumOsds int `json:"num_osds"`
|
||||
NumPerPoolOsds int `json:"num_per_pool_osds"`
|
||||
}
|
||||
Pools []struct {
|
||||
Name string `json:"name"`
|
||||
ID int `json:"id"`
|
||||
Stats struct {
|
||||
Stored int `json:"stored"`
|
||||
Objects int `json:"objects"`
|
||||
KbUsed int `json:"kb_used"`
|
||||
BytesUsed int64 `json:"bytes_used"`
|
||||
PercentUsed int `json:"percent_used"`
|
||||
MaxAvail int64 `json:"max_avail"`
|
||||
} `json:"stats"`
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SRbdStorage) getCapacity() (*sCephCapacity, error) {
|
||||
ret := &sCephCapacity{}
|
||||
_, err := s.withCluster(func(conn *rados.Conn) (interface{}, error) {
|
||||
poolName, _ := s.StorageConf.GetString("pool")
|
||||
cmd := SMonCommand{Prefix: "df", Format: "json"}
|
||||
buffer, _, err := conn.MonCommand([]byte(jsonutils.Marshal(cmd).String()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrapf(err, "MonCommand")
|
||||
}
|
||||
clusterSizeKb := stats.Kb
|
||||
pool, _ := s.StorageConf.GetString("pool")
|
||||
cmd := SMonCommand{Prefix: "osd pool get-quota", Pool: pool, Format: "json"}
|
||||
bufer, _, err := conn.MonCommand([]byte(jsonutils.Marshal(cmd).String()))
|
||||
result, err := jsonutils.Parse(buffer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrapf(err, string(buffer))
|
||||
}
|
||||
result, err := jsonutils.Parse(bufer)
|
||||
if err != nil {
|
||||
log.Errorf("parse %s json err: %v", string(bufer), err)
|
||||
return nil, err
|
||||
df := &sCephDfResult{}
|
||||
result.Unmarshal(df)
|
||||
ret.CapacitySizeByte = df.Stats.TotalAvailBytes
|
||||
ret.UsedCapacitySizeByte = df.Stats.TotalUsedBytes
|
||||
for _, pool := range df.Pools {
|
||||
if pool.Name == poolName {
|
||||
ret.UsedCapacitySizeByte = pool.Stats.BytesUsed
|
||||
}
|
||||
}
|
||||
maxBytes, _ := result.Int("quota_max_bytes")
|
||||
if maxBytes == 0 || uint64(maxBytes) > clusterSizeKb*1024 {
|
||||
return clusterSizeKb, nil
|
||||
}
|
||||
return uint64(maxBytes) / 1024, nil
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "getCapacity")
|
||||
return ret, errors.Wrap(err, "getCapacity")
|
||||
}
|
||||
sizeKb := _sizeKb.(uint64)
|
||||
return sizeKb / 1024, nil
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *SRbdStorage) SyncStorageSize() error {
|
||||
content := jsonutils.NewDict()
|
||||
capacity, err := s.getCapacity()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "getCapacity")
|
||||
}
|
||||
content.Set("capacity", jsonutils.NewInt(int64(capacity.CapacitySizeByte/1024/1024)))
|
||||
content.Set("actual_capacity_used", jsonutils.NewInt(int64(capacity.UsedCapacitySizeByte/1024/1024)))
|
||||
_, err = modules.Storages.Put(
|
||||
hostutils.GetComputeSession(context.Background()),
|
||||
s.StorageId, content)
|
||||
return errors.Wrapf(err, "storage update")
|
||||
}
|
||||
|
||||
func (s *SRbdStorage) SyncStorageInfo() (jsonutils.JSONObject, error) {
|
||||
@@ -586,10 +628,11 @@ func (s *SRbdStorage) SyncStorageInfo() (jsonutils.JSONObject, error) {
|
||||
return modules.Storages.PerformAction(hostutils.GetComputeSession(context.Background()), s.StorageId, "offline", nil)
|
||||
}
|
||||
content = map[string]interface{}{
|
||||
"name": s.StorageName,
|
||||
"capacity": capacity,
|
||||
"status": api.STORAGE_ONLINE,
|
||||
"zone": s.GetZoneName(),
|
||||
"name": s.StorageName,
|
||||
"capacity": capacity.CapacitySizeByte / 1024 / 1024,
|
||||
"actual_capacity_used": capacity.UsedCapacitySizeByte / 1024 / 1024,
|
||||
"status": api.STORAGE_ONLINE,
|
||||
"zone": s.GetZoneName(),
|
||||
}
|
||||
return modules.Storages.Put(hostutils.GetComputeSession(context.Background()), s.StorageId, jsonutils.Marshal(content))
|
||||
}
|
||||
|
||||
29
pkg/mcclient/modules/mod_billing_exchange_rates.go
Normal file
29
pkg/mcclient/modules/mod_billing_exchange_rates.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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 modules
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
|
||||
var (
|
||||
BillingExchangeRates modulebase.ResourceManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
BillingExchangeRates = NewMeterManager("billing_exchange_rate", "billing_exchange_rates",
|
||||
[]string{"src", "dest", "rate", "date"},
|
||||
[]string{},
|
||||
)
|
||||
register(&BillingExchangeRates)
|
||||
}
|
||||
49
pkg/mcclient/modules/mod_bills_dimension.go
Normal file
49
pkg/mcclient/modules/mod_bills_dimension.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package modules
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
|
||||
type SBillsDimensionManager struct {
|
||||
*modulebase.ResourceManager
|
||||
}
|
||||
|
||||
var (
|
||||
BillingDimensionManager *SBillsDimensionManager
|
||||
BillingDimensionAnalysisManager *SBillsDimensionManager
|
||||
BillingDimensionJointManager *SBillsDimensionManager
|
||||
)
|
||||
|
||||
func NewBillingDimensionManager() *SBillsDimensionManager {
|
||||
man := NewMeterManager("billsdimension", "billsdimensions",
|
||||
[]string{"id", "name", "dimension_type", "dimension_items"},
|
||||
[]string{})
|
||||
return &SBillsDimensionManager{
|
||||
ResourceManager: &man,
|
||||
}
|
||||
}
|
||||
|
||||
func NewBillingDimensionAnalysisManager() *SBillsDimensionManager {
|
||||
man := NewMeterManager("billsdimensionsanalysis", "billsdimensionsanalysis",
|
||||
[]string{"id", "name", "usage_type", "resource_type", "brand", "description"},
|
||||
[]string{})
|
||||
return &SBillsDimensionManager{
|
||||
ResourceManager: &man,
|
||||
}
|
||||
}
|
||||
|
||||
func NewBillingDimensionJointManager() *SBillsDimensionManager {
|
||||
man := NewMeterManager("dimensionjoint", "dimensionjoints",
|
||||
[]string{"id", "name", "data"},
|
||||
[]string{})
|
||||
return &SBillsDimensionManager{
|
||||
ResourceManager: &man,
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
BillingDimensionManager = NewBillingDimensionManager()
|
||||
BillingDimensionAnalysisManager = NewBillingDimensionAnalysisManager()
|
||||
BillingDimensionJointManager = NewBillingDimensionJointManager()
|
||||
register(BillingDimensionManager)
|
||||
register(BillingDimensionAnalysisManager)
|
||||
register(BillingDimensionJointManager)
|
||||
}
|
||||
26
pkg/mcclient/modules/mod_monitor_resource.go
Normal file
26
pkg/mcclient/modules/mod_monitor_resource.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package modules
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
|
||||
var (
|
||||
MonitorResourceManager *SMonitorResourceManager
|
||||
)
|
||||
|
||||
type SMonitorResourceManager struct {
|
||||
*modulebase.ResourceManager
|
||||
}
|
||||
|
||||
func init() {
|
||||
MonitorResourceManager = NewMonitorResourceManager()
|
||||
|
||||
register(MonitorResourceManager)
|
||||
}
|
||||
|
||||
func NewMonitorResourceManager() *SMonitorResourceManager {
|
||||
man := NewMonitorV2Manager("monitorresource", "monitorresources",
|
||||
[]string{"id", "name", "res_type", "res_id", "alert_state"},
|
||||
[]string{})
|
||||
return &SMonitorResourceManager{
|
||||
ResourceManager: &man,
|
||||
}
|
||||
}
|
||||
@@ -418,3 +418,29 @@ func (o ChangeOwnerOptions) Params() (jsonutils.JSONObject, error) {
|
||||
}
|
||||
return jsonutils.Marshal(map[string]string{"project_domain": o.ProjectDomain}), nil
|
||||
}
|
||||
|
||||
type StatusStatisticsOptions struct {
|
||||
PendingDelete *bool `help:"Show only pending deleted resources"`
|
||||
PendingDeleteAll *bool `help:"Show also pending-deleted resources" json:"-"`
|
||||
DeleteAll *bool `help:"Show also deleted resources" json:"-"`
|
||||
ShowEmulated *bool `help:"Show all resources including the emulated resources"`
|
||||
Scope string `help:"resource scope" choices:"system|domain|project|user"`
|
||||
}
|
||||
|
||||
func (o StatusStatisticsOptions) Params() (jsonutils.JSONObject, error) {
|
||||
params, err := ListStructToParams(o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if BoolV(o.DeleteAll) {
|
||||
params.Set("delete", jsonutils.NewString("all"))
|
||||
}
|
||||
if BoolV(o.PendingDeleteAll) {
|
||||
params.Set("pending_delete", jsonutils.NewString("all"))
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func (o StatusStatisticsOptions) Property() string {
|
||||
return "statistics"
|
||||
}
|
||||
|
||||
145
pkg/mcclient/options/compute/cloudproviders.go
Normal file
145
pkg/mcclient/options/compute/cloudproviders.go
Normal file
@@ -0,0 +1,145 @@
|
||||
// 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/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type CloudproviderListOptions struct {
|
||||
options.BaseListOptions
|
||||
|
||||
Usable bool `help:"Vpc & Network usable"`
|
||||
|
||||
HasObjectStorage bool `help:"filter cloudproviders that has object storage"`
|
||||
NoObjectStorage bool `help:"filter cloudproviders that has no object storage"`
|
||||
Capability []string `help:"capability filter" choices:"project|compute|network|loadbalancer|objectstore|rds|cache|event"`
|
||||
Cloudregion string `help:"filter cloudproviders by cloudregion"`
|
||||
|
||||
HostSchedtagId string `help:"filter by host schedtag"`
|
||||
}
|
||||
|
||||
func (opts *CloudproviderListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
params, err := opts.BaseListOptions.Params()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.Usable {
|
||||
params.Add(jsonutils.NewBool(true), "usable")
|
||||
}
|
||||
|
||||
if opts.HasObjectStorage {
|
||||
params.Add(jsonutils.JSONTrue, "has_object_storage")
|
||||
} else if opts.NoObjectStorage {
|
||||
params.Add(jsonutils.JSONFalse, "has_object_storage")
|
||||
}
|
||||
|
||||
if len(opts.Capability) > 0 {
|
||||
params.Add(jsonutils.NewStringArray(opts.Capability), "capability")
|
||||
}
|
||||
|
||||
if len(opts.Cloudregion) > 0 {
|
||||
params.Add(jsonutils.NewString(opts.Cloudregion), "cloudregion")
|
||||
}
|
||||
|
||||
if len(opts.HostSchedtagId) > 0 {
|
||||
params.Add(jsonutils.NewString(opts.HostSchedtagId), "host_schedtag_id")
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
type CloudproviderUpdateOptions struct {
|
||||
options.BaseIdOptions
|
||||
Name string `help:"New name to update"`
|
||||
AccessUrl string `help:"New access url"`
|
||||
Desc string `help:"Description"`
|
||||
}
|
||||
|
||||
func (opts *CloudproviderUpdateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
params := jsonutils.NewDict()
|
||||
if len(opts.Name) > 0 {
|
||||
params.Add(jsonutils.NewString(opts.Name), "name")
|
||||
}
|
||||
if len(opts.AccessUrl) > 0 {
|
||||
params.Add(jsonutils.NewString(opts.AccessUrl), "access_url")
|
||||
}
|
||||
if len(opts.Desc) > 0 {
|
||||
params.Add(jsonutils.NewString(opts.Desc), "description")
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
type CloudproviderChangeProjectOptions struct {
|
||||
options.BaseIdOptions
|
||||
TENANT string `help:"ID or Name of tenant"`
|
||||
}
|
||||
|
||||
func (opts *CloudproviderChangeProjectOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(map[string]string{"project": opts.TENANT}), nil
|
||||
}
|
||||
|
||||
type CloudproviderSyncOptions struct {
|
||||
options.BaseIdOptions
|
||||
Force bool `help:"Force sync no matter what"`
|
||||
FullSync bool `help:"Synchronize everything"`
|
||||
ProjectSync bool `help:"Auto sync project info"`
|
||||
Region []string `help:"region to sync"`
|
||||
Zone []string `help:"region to sync"`
|
||||
Host []string `help:"region to sync"`
|
||||
}
|
||||
|
||||
func (opts *CloudproviderSyncOptions) Params() (jsonutils.JSONObject, error) {
|
||||
params := jsonutils.NewDict()
|
||||
if opts.Force {
|
||||
params.Add(jsonutils.JSONTrue, "force")
|
||||
}
|
||||
if opts.FullSync {
|
||||
params.Add(jsonutils.JSONTrue, "full_sync")
|
||||
}
|
||||
if opts.ProjectSync {
|
||||
params.Add(jsonutils.JSONTrue, "project_sync")
|
||||
}
|
||||
if len(opts.Region) > 0 {
|
||||
params.Add(jsonutils.NewStringArray(opts.Region), "region")
|
||||
}
|
||||
if len(opts.Zone) > 0 {
|
||||
params.Add(jsonutils.NewStringArray(opts.Zone), "zone")
|
||||
}
|
||||
if len(opts.Host) > 0 {
|
||||
params.Add(jsonutils.NewStringArray(opts.Host), "host")
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
type CloudproviderStorageClassesOptions struct {
|
||||
options.BaseIdOptions
|
||||
Cloudregion string `help:"cloud region name or Id"`
|
||||
}
|
||||
|
||||
func (opts *CloudproviderStorageClassesOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.StructToParams(opts)
|
||||
}
|
||||
|
||||
type ClouproviderProjectMappingOptions struct {
|
||||
options.BaseIdOptions
|
||||
ProjectMappingId string `json:"project_mapping_id" help:"project mapping id"`
|
||||
}
|
||||
|
||||
func (opts *ClouproviderProjectMappingOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(map[string]string{"project_mapping_id": opts.ProjectMappingId}), nil
|
||||
}
|
||||
29
pkg/mcclient/options/meter/billing_exchange_rates.go
Normal file
29
pkg/mcclient/options/meter/billing_exchange_rates.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package meter
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type BillingExchangeRateListOptions struct {
|
||||
options.BaseListOptions
|
||||
}
|
||||
|
||||
func (opt *BillingExchangeRateListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.StructToParams(opt)
|
||||
}
|
||||
|
||||
type BillingExchangeRateUpdateOptions struct {
|
||||
ID string `help:"ID of billing exchange rate" json:"-"`
|
||||
|
||||
Rate float64 `help:"exchange rate" json:"rate"`
|
||||
}
|
||||
|
||||
func (opt *BillingExchangeRateUpdateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.StructToParams(opt)
|
||||
}
|
||||
|
||||
func (opt *BillingExchangeRateUpdateOptions) GetId() string {
|
||||
return opt.ID
|
||||
}
|
||||
90
pkg/mcclient/options/meter/bills_dimension.go
Normal file
90
pkg/mcclient/options/meter/bills_dimension.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package meter
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type BillDimensionCreateOptions struct {
|
||||
apis.StatusStandaloneResourceCreateInput
|
||||
|
||||
DimensionAnalysisIds []string `json:"dimension_analysis_ids" help:"ids of DimensionAnalysis"`
|
||||
DimensionItemName string `json:"dimension_item_name" help:"name of dimension item" required:"true"`
|
||||
DimensionType string `json:"dimension_type" choices:"bill|resource_type"`
|
||||
}
|
||||
|
||||
func (o *BillDimensionCreateOptions) Params() (jsonutils.JSONObject, error) {
|
||||
data := jsonutils.NewDict()
|
||||
data.Add(jsonutils.NewString(o.Name), "name")
|
||||
data.Add(jsonutils.NewString(o.DimensionType), "dimension_type")
|
||||
|
||||
dimensionItem := jsonutils.NewDict()
|
||||
dimensionItem.Add(jsonutils.NewString(o.DimensionItemName))
|
||||
dimensionItem.Add(jsonutils.NewStringArray(o.DimensionAnalysisIds), "dimension_analysis_ids")
|
||||
|
||||
data.Add(jsonutils.NewArray(dimensionItem), "dimension_items")
|
||||
return data, nil
|
||||
|
||||
}
|
||||
|
||||
type BillDimensionListOptions struct {
|
||||
options.BaseListOptions
|
||||
apis.EnabledResourceBaseListInput
|
||||
|
||||
DimensionType string `json:"dimension_type"`
|
||||
}
|
||||
|
||||
func (o *BillDimensionListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.ListStructToParams(o)
|
||||
}
|
||||
|
||||
type BillDimensionShowOptions struct {
|
||||
ID string `help:"ID of bill dimension " json:"-"`
|
||||
}
|
||||
|
||||
func (o *BillDimensionShowOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.StructToParams(o)
|
||||
}
|
||||
|
||||
func (o *BillDimensionShowOptions) GetId() string {
|
||||
return o.ID
|
||||
}
|
||||
|
||||
type BillDimensionDeleteOptions struct {
|
||||
ID string `json:"-"`
|
||||
}
|
||||
|
||||
func (o *BillDimensionDeleteOptions) GetId() string {
|
||||
return o.ID
|
||||
}
|
||||
|
||||
func (o *BillDimensionDeleteOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.StructToParams(o)
|
||||
}
|
||||
|
||||
type BillDimensionAnalysisListOptions struct {
|
||||
options.BaseListOptions
|
||||
apis.EnabledResourceBaseListInput
|
||||
|
||||
UsageType string `json:"usage_type"`
|
||||
ResourceType string `json:"resource_type"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
func (o *BillDimensionAnalysisListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.ListStructToParams(o)
|
||||
}
|
||||
|
||||
type BillDimensionAnalysisShowOptions struct {
|
||||
ID string `help:"ID of bill dimension " json:"-"`
|
||||
}
|
||||
|
||||
func (o *BillDimensionAnalysisShowOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.StructToParams(o)
|
||||
}
|
||||
|
||||
func (o *BillDimensionAnalysisShowOptions) GetId() string {
|
||||
return o.ID
|
||||
}
|
||||
18
pkg/mcclient/options/monitor/monitor_resource.go
Normal file
18
pkg/mcclient/options/monitor/monitor_resource.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type MonitorResourceJointAlertOptions struct {
|
||||
}
|
||||
|
||||
func (o *MonitorResourceJointAlertOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.ListStructToParams(o)
|
||||
}
|
||||
|
||||
func (o *MonitorResourceJointAlertOptions) GetId() string {
|
||||
return "alert"
|
||||
}
|
||||
@@ -109,9 +109,9 @@ func (c FormatCond) String() string {
|
||||
func (c *QueryCondition) filterTags(tags map[string]string, details monitor.CommonAlertMetricDetails) map[string]string {
|
||||
ret := make(map[string]string)
|
||||
for key, val := range tags {
|
||||
if strings.HasSuffix(key, "_id") {
|
||||
continue
|
||||
}
|
||||
//if strings.HasSuffix(key, "_id") {
|
||||
// continue
|
||||
//}
|
||||
if len(val) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -211,6 +211,9 @@ func (e *AlertEngine) processJob(attemptID int, attemptChan chan int, cancelChan
|
||||
attemptChan <- (attemptID + 1)
|
||||
return
|
||||
}
|
||||
log.Errorf("gt AlertingMaxAttempts, error: %v", evalContext.Error)
|
||||
close(attemptChan)
|
||||
return
|
||||
}
|
||||
|
||||
// create new context with timeout for notifications
|
||||
|
||||
@@ -145,10 +145,14 @@ func (n *notificationService) getNeededNotifiers(nIds []string, evalCtx *EvalCon
|
||||
}
|
||||
}
|
||||
if shouldNotify || evalCtx.Rule.State == monitor.AlertStateAlerting {
|
||||
n.createAlertRecordWhenNotify(evalCtx, shouldNotify)
|
||||
go func() {
|
||||
n.createAlertRecordWhenNotify(evalCtx, shouldNotify)
|
||||
}()
|
||||
}
|
||||
if !shouldNotify && evalCtx.shouldUpdateAlertState() && evalCtx.NoDataFound {
|
||||
n.detachAlertResourceWhenNodata(evalCtx)
|
||||
go func() {
|
||||
n.detachAlertResourceWhenNodata(evalCtx)
|
||||
}()
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -178,7 +182,6 @@ func (n *notificationService) createAlertRecordWhenNotify(evalCtx *EvalContext,
|
||||
recordCreateInput.ResType = recordCreateInput.AlertRule.ResType
|
||||
createData := recordCreateInput.JSON(recordCreateInput)
|
||||
alert, _ := models.CommonAlertManager.GetAlert(evalCtx.Rule.Id)
|
||||
log.Errorf("alert:%v", jsonutils.Marshal(alert))
|
||||
record, err := db.DoCreate(models.AlertRecordManager, evalCtx.Ctx, evalCtx.UserCred, jsonutils.NewDict(),
|
||||
createData, evalCtx.UserCred)
|
||||
if err != nil {
|
||||
|
||||
@@ -271,4 +271,21 @@ func init() {
|
||||
newMetricFieldCreateInput("usage_active", "Storage utilization rate", monitor.METRIC_UNIT_PERCENT, 1),
|
||||
newMetricFieldCreateInput("free", "Free storage", monitor.METRIC_UNIT_MB, 2),
|
||||
})
|
||||
|
||||
//jenkins
|
||||
RegistryMetricCreateInput("jenkins_node", "jenkins node",
|
||||
monitor.METRIC_RES_TYPE_JENKINS, monitor.METRIC_DATABASE_TELE, 1, []monitor.MetricFieldCreateInput{
|
||||
newMetricFieldCreateInput("disk_available", "disk_available", monitor.METRIC_UNIT_BYTE, 1),
|
||||
newMetricFieldCreateInput("temp_available", "temp_available", monitor.METRIC_UNIT_BYTE, 2),
|
||||
newMetricFieldCreateInput("memory_available", "memory_available", monitor.METRIC_UNIT_BYTE, 3),
|
||||
newMetricFieldCreateInput("memory_total", "memory_total", monitor.METRIC_UNIT_BYTE, 4),
|
||||
newMetricFieldCreateInput("swap_available", "swap_available", monitor.METRIC_UNIT_BYTE, 5),
|
||||
newMetricFieldCreateInput("swap_total", "swap_total", monitor.METRIC_UNIT_BYTE, 6),
|
||||
})
|
||||
RegistryMetricCreateInput("jenkins_job", "jenkins job",
|
||||
monitor.METRIC_RES_TYPE_JENKINS, monitor.METRIC_DATABASE_TELE, 2, []monitor.MetricFieldCreateInput{
|
||||
newMetricFieldCreateInput("duration", "duration", monitor.METRIC_UNIT_MS, 1),
|
||||
newMetricFieldCreateInput("number", "number", monitor.METRIC_UNIT_COUNT, 2),
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -138,6 +138,7 @@ type SAlert struct {
|
||||
LastStateChange time.Time `list:"user"`
|
||||
StateChanges int `default:"0" nullable:"false" list:"user"`
|
||||
CustomizeConfig jsonutils.JSONObject `list:"user" create:"optional" update:"user"`
|
||||
ResType string `width:"32" list:"user" update:"user"`
|
||||
}
|
||||
|
||||
func (alert *SAlert) IsEnable() bool {
|
||||
|
||||
@@ -3,6 +3,7 @@ package models
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
@@ -213,6 +214,9 @@ func (record *SAlertRecord) GetMoreDetails(out monitor.AlertRecordDetails) (moni
|
||||
if err != nil {
|
||||
return out, errors.Wrap(err, "record Unmarshal evalMatchs error")
|
||||
}
|
||||
for i, _ := range evalMatchs {
|
||||
evalMatchs[i] = record.filterTags(evalMatchs[i])
|
||||
}
|
||||
out.ResNum = int64(len(evalMatchs))
|
||||
}
|
||||
commonAlert, _ := CommonAlertManager.GetAlert(record.AlertId)
|
||||
@@ -220,6 +224,15 @@ func (record *SAlertRecord) GetMoreDetails(out monitor.AlertRecordDetails) (moni
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (record *SAlertRecord) filterTags(match monitor.EvalMatch) monitor.EvalMatch {
|
||||
for key, _ := range match.Tags {
|
||||
if strings.HasSuffix(key, "_id") {
|
||||
delete(match.Tags, key)
|
||||
}
|
||||
}
|
||||
return match
|
||||
}
|
||||
|
||||
func (man *SAlertRecordManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, _ jsonutils.JSONObject, data monitor.AlertRecordCreateInput) (monitor.AlertRecordCreateInput, error) {
|
||||
return data, nil
|
||||
}
|
||||
@@ -292,11 +305,15 @@ getNewMatchTag:
|
||||
|
||||
func (record *SAlertRecord) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
record.SStatusStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
|
||||
err := MonitorResourceManager.UpdateMonitorResourceAttachJoint(ctx, userCred, record)
|
||||
if err != nil {
|
||||
log.Errorf("UpdateMonitorResourceAttachJoint error: %v", err)
|
||||
}
|
||||
if err := GetAlertResourceManager().ReconcileFromRecord(ctx, userCred, ownerId, record); err != nil {
|
||||
log.Errorf("Reconcile from alert record error: %v", err)
|
||||
return
|
||||
}
|
||||
err := GetAlertResourceManager().NotifyAlertResourceCount(ctx)
|
||||
err = GetAlertResourceManager().NotifyAlertResourceCount(ctx)
|
||||
if err != nil {
|
||||
log.Errorf("NotifyAlertResourceCount error: %v", err)
|
||||
return
|
||||
|
||||
@@ -54,6 +54,7 @@ func init() {
|
||||
SAlertManager: *NewAlertManager(SCommonAlert{}, "commonalert", "commonalerts"),
|
||||
}
|
||||
CommonAlertManager.SetVirtualObject(CommonAlertManager)
|
||||
//registry.RegisterService(CommonAlertManager)
|
||||
}
|
||||
|
||||
type ISubscriptionManager interface {
|
||||
@@ -86,6 +87,41 @@ func (man *SCommonAlertManager) NamespaceScope() rbacutils.TRbacScope {
|
||||
return rbacutils.ScopeSystem
|
||||
}
|
||||
|
||||
func (manager *SCommonAlertManager) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (man *SCommonAlertManager) Run(ctx context.Context) error {
|
||||
alerts, err := man.GetAlerts(monitor.CommonAlertListInput{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
errs := make([]error, 0)
|
||||
for _, alert := range alerts {
|
||||
err := alert.UpdateMonitorResourceJoint(ctx, auth.AdminCredential())
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
}
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (man *SCommonAlertManager) UpdateAlertsResType(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
alerts, err := man.GetAlerts(monitor.CommonAlertListInput{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
errs := make([]error, 0)
|
||||
for _, alert := range alerts {
|
||||
err := alert.UpdateResType()
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (man *SCommonAlertManager) ValidateCreateData(
|
||||
ctx context.Context, userCred mcclient.TokenCredential,
|
||||
ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject,
|
||||
@@ -336,6 +372,7 @@ func (alert *SCommonAlert) PostCreate(ctx context.Context,
|
||||
log.Errorln(errors.Wrap(err, "Alert PerformSetScope"))
|
||||
}
|
||||
CommonAlertManager.SetSubscriptionAlert(alert)
|
||||
alert.StartUpdateMonitorAlertJointTask(ctx, userCred)
|
||||
}
|
||||
|
||||
func (man *SCommonAlertManager) ListItemFilter(
|
||||
@@ -347,15 +384,19 @@ func (man *SCommonAlertManager) ListItemFilter(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q.Filter(sqlchemy.IsNull(q.Field("used_by")))
|
||||
|
||||
if len(query.Level) > 0 {
|
||||
q.Equals("level", query.Level)
|
||||
}
|
||||
man.FieldListFilter(q, query)
|
||||
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (man *SCommonAlertManager) FieldListFilter(q *sqlchemy.SQuery, input monitor.CommonAlertListInput) {
|
||||
q.Filter(sqlchemy.IsNull(q.Field("used_by")))
|
||||
|
||||
if len(input.Level) > 0 {
|
||||
q.Equals("level", input.Level)
|
||||
}
|
||||
}
|
||||
|
||||
func (manager *SCommonAlertManager) GetExportExtraKeys(ctx context.Context, keys stringutils2.SSortedStrings, rowMap map[string]string) *jsonutils.JSONDict {
|
||||
res := manager.SResourceBaseManager.GetExportExtraKeys(ctx, keys, rowMap)
|
||||
if keys.Contains("tenant") {
|
||||
@@ -482,6 +523,17 @@ func (man *SCommonAlertManager) GetAlert(id string) (*SCommonAlert, error) {
|
||||
return obj.(*SCommonAlert), nil
|
||||
}
|
||||
|
||||
func (manager *SCommonAlertManager) GetAlerts(input monitor.CommonAlertListInput) ([]SCommonAlert, error) {
|
||||
alerts := make([]SCommonAlert, 0)
|
||||
query := manager.Query()
|
||||
manager.FieldListFilter(query, input)
|
||||
err := db.FetchModelObjects(manager, query, &alerts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SCommonAlertManagerGetAlerts err")
|
||||
}
|
||||
return alerts, nil
|
||||
}
|
||||
|
||||
func (man *SCommonAlertManager) FetchCustomizeColumns(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
@@ -918,6 +970,7 @@ func (alert *SCommonAlert) PostUpdate(
|
||||
alert.setMetaName(ctx, userCred, updateInput.MetaName)
|
||||
}
|
||||
CommonAlertManager.SetSubscriptionAlert(alert)
|
||||
alert.StartUpdateMonitorAlertJointTask(ctx, userCred)
|
||||
}
|
||||
|
||||
func (alert *SCommonAlert) UpdateNotification(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
@@ -957,6 +1010,7 @@ func (alert *SCommonAlert) CustomizeDelete(
|
||||
return errors.Wrap(err, "customizeDeleteNotis")
|
||||
}
|
||||
alert.StartDeleteTask(ctx, userCred)
|
||||
alert.StartDetachMonitorAlertJointTask(ctx, userCred)
|
||||
return alert.SAlert.CustomizeDelete(ctx, userCred, query, data)
|
||||
}
|
||||
|
||||
@@ -1181,6 +1235,113 @@ func (manager *SCommonAlertManager) DetachAlertResourceByAlertId(ctx context.Con
|
||||
return
|
||||
}
|
||||
|
||||
func (alert *SCommonAlert) StartUpdateMonitorAlertJointTask(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "UpdateMonitorResourceJointTask", alert, userCred, jsonutils.NewDict(), "", "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
task.ScheduleRun(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (alert *SCommonAlert) UpdateMonitorResourceJoint(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
setting, _ := alert.GetSettings()
|
||||
inputQuery := monitor.MetricInputQuery{
|
||||
MetricQuery: make([]*monitor.AlertQuery, 0),
|
||||
}
|
||||
var resType string
|
||||
for _, con := range setting.Conditions {
|
||||
measurement, _ := MetricMeasurementManager.GetCache().Get(con.Query.Model.Measurement)
|
||||
if measurement == nil {
|
||||
continue
|
||||
}
|
||||
if !utils.IsInStringArray(measurement.ResType, []string{monitor.METRIC_RES_TYPE_HOST,
|
||||
monitor.METRIC_RES_TYPE_GUEST}) {
|
||||
continue
|
||||
}
|
||||
resType = measurement.ResType
|
||||
inputQuery.MetricQuery = append(inputQuery.MetricQuery, &con.Query)
|
||||
}
|
||||
if len(inputQuery.MetricQuery) == 0 {
|
||||
return nil
|
||||
}
|
||||
metrics, err := doQuery(inputQuery)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "doQuery err")
|
||||
}
|
||||
resourceIds := make([]string, 0)
|
||||
for _, serie := range metrics.Series {
|
||||
resourceKeyId := monitor.MEASUREMENT_TAG_ID[resType]
|
||||
resourceId := serie.Tags[resourceKeyId]
|
||||
if len(resourceId) == 0 {
|
||||
continue
|
||||
}
|
||||
resourceIds = append(resourceIds, resourceId)
|
||||
}
|
||||
joints, _ := MonitorResourceAlertManager.GetJoinsByListInput(monitor.MonitorResourceJointListInput{AlertId: alert.GetId()})
|
||||
jointLoop:
|
||||
for _, joint := range joints {
|
||||
for i, resId := range resourceIds {
|
||||
if resId == joint.MonitorResourceId {
|
||||
resourceIds = append(resourceIds[0:i], resourceIds[i+1:]...)
|
||||
continue jointLoop
|
||||
}
|
||||
}
|
||||
joint.Detach(ctx, userCred)
|
||||
}
|
||||
if len(resourceIds) == 0 {
|
||||
return nil
|
||||
}
|
||||
monitorResources, _ := MonitorResourceManager.GetMonitorResources(monitor.MonitorResourceListInput{ResId: resourceIds})
|
||||
errs := make([]error, 0)
|
||||
for _, monRes := range monitorResources {
|
||||
err := monRes.AttachAlert(ctx, userCred, alert.GetId())
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
monRes.UpdateAlertState()
|
||||
}
|
||||
|
||||
if len(errs) != 0 {
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (alert *SCommonAlert) StartDetachMonitorAlertJointTask(ctx context.Context,
|
||||
userCred mcclient.TokenCredential) error {
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "DetachMonitorResourceJointTask", alert, userCred, jsonutils.NewDict(), "", "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
task.ScheduleRun(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (alert *SCommonAlert) DetachMonitorResourceJoint(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
err := MonitorResourceAlertManager.DetachJoint(ctx, userCred, monitor.MonitorResourceJointListInput{AlertId: alert.GetId()})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SCommonAlert DetachJoint err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (alert *SCommonAlert) UpdateResType() error {
|
||||
setting, _ := alert.GetSettings()
|
||||
measurement, _ := MetricMeasurementManager.GetCache().Get(setting.Conditions[0].Query.Model.Measurement)
|
||||
if measurement == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := db.Update(alert, func() error {
|
||||
alert.ResType = measurement.ResType
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "alert:%s UpdateResType err", alert.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SCompanyInfo struct {
|
||||
Copyright string `json:"copyright"`
|
||||
Name string `json:"name"`
|
||||
|
||||
@@ -409,6 +409,11 @@ func (man *SMetricMeasurementManager) Run(ctx context.Context) error {
|
||||
return errors.Wrap(err, "init metric json error")
|
||||
}
|
||||
log.Infoln("========metric_measurement_field init finish==========")
|
||||
err = CommonAlertManager.UpdateAlertsResType(ctx, auth.AdminCredential())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "CommonAlertManager UpdateAlertsResType err")
|
||||
}
|
||||
log.Infoln("========UpdateAlertsResType Finish==========")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
342
pkg/monitor/models/monitor_resource.go
Normal file
342
pkg/monitor/models/monitor_resource.go
Normal file
@@ -0,0 +1,342 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
"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/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
var (
|
||||
MonitorResourceManager *SMonitorResourceManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
MonitorResourceManager = &SMonitorResourceManager{
|
||||
SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
|
||||
&SMonitorResource{},
|
||||
"monitor_resource_tbl",
|
||||
"monitorresource",
|
||||
"monitorresources",
|
||||
),
|
||||
}
|
||||
MonitorResourceManager.SetVirtualObject(MonitorResourceManager)
|
||||
RegistryResourceSync(NewGuestResourceSync())
|
||||
RegistryResourceSync(NewHostResourceSync())
|
||||
}
|
||||
|
||||
type SMonitorResourceManager struct {
|
||||
db.SVirtualResourceBaseManager
|
||||
db.SEnabledResourceBaseManager
|
||||
}
|
||||
|
||||
type SMonitorResource struct {
|
||||
db.SVirtualResourceBase
|
||||
db.SEnabledResourceBase
|
||||
|
||||
AlertState string `width:"36" charset:"ascii" list:"user" default:"init" update:"user" json:"alert_state"`
|
||||
ResId string `width:"256" charset:"ascii" index:"true" list:"user" update:"user" json:"res_id"`
|
||||
ResType string `width:"36" charset:"ascii" list:"user" update:"user" json:"res_type"`
|
||||
}
|
||||
|
||||
func (manager *SMonitorResourceManager) SyncResources(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
isStart bool) {
|
||||
for _, sync := range GetResourceSyncMap() {
|
||||
err := sync.SyncResources(ctx, userCred, jsonutils.NewDict())
|
||||
if err != nil {
|
||||
log.Errorf("resType:%s SyncResources err:%v", sync.SyncType(), err)
|
||||
}
|
||||
}
|
||||
err := CommonAlertManager.Run(ctx)
|
||||
if err != nil {
|
||||
log.Errorf("CommonAlertManager UpdateMonitorResourceJoint err:%v", err)
|
||||
return
|
||||
}
|
||||
log.Infoln("====SMonitorResourceManager SyncResources End====")
|
||||
}
|
||||
|
||||
func (manager *SMonitorResourceManager) GetMonitorResources(input monitor.MonitorResourceListInput) ([]SMonitorResource, error) {
|
||||
monitorResources := make([]SMonitorResource, 0)
|
||||
query := manager.Query()
|
||||
if input.OnlyResId {
|
||||
query = query.AppendField(query.Field("id"), query.Field("res_id"))
|
||||
}
|
||||
query = manager.FieldListFilter(query, input)
|
||||
err := db.FetchModelObjects(manager, query, &monitorResources)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SMonitorResourceManager FetchModelObjects err")
|
||||
}
|
||||
return monitorResources, nil
|
||||
}
|
||||
|
||||
func (manager *SMonitorResourceManager) GetMonitorResourceById(id string) (*SMonitorResource, error) {
|
||||
iModel, err := db.FetchById(manager, id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, fmt.Sprintf("GetMonitorResourceById:%s err", id))
|
||||
}
|
||||
return iModel.(*SMonitorResource), nil
|
||||
}
|
||||
|
||||
func (manager *SMonitorResourceManager) ListItemFilter(
|
||||
ctx context.Context, q *sqlchemy.SQuery,
|
||||
userCred mcclient.TokenCredential,
|
||||
query monitor.MonitorResourceListInput,
|
||||
) (*sqlchemy.SQuery, error) {
|
||||
var err error
|
||||
q, err = manager.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
|
||||
}
|
||||
q, err = manager.SEnabledResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledResourceBaseListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SEnabledResourceBaseManager.ListItemFilter")
|
||||
}
|
||||
|
||||
q = manager.FieldListFilter(q, query)
|
||||
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (manager *SMonitorResourceManager) FieldListFilter(q *sqlchemy.SQuery, query monitor.MonitorResourceListInput) *sqlchemy.SQuery {
|
||||
if len(query.ResType) != 0 {
|
||||
q.Equals("res_type", query.ResType)
|
||||
}
|
||||
if len(query.ResId) != 0 {
|
||||
q.In("res_id", query.ResId)
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func (man *SMonitorResourceManager) OrderByExtraFields(
|
||||
ctx context.Context,
|
||||
q *sqlchemy.SQuery,
|
||||
userCred mcclient.TokenCredential,
|
||||
input monitor.SuggestSysAlertListInput,
|
||||
) (*sqlchemy.SQuery, error) {
|
||||
var err error
|
||||
q, err = man.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.VirtualResourceListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.OrderByExtraFields")
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (man *SMonitorResourceManager) HasName() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (man *SMonitorResourceManager) ValidateCreateData(
|
||||
ctx context.Context, userCred mcclient.TokenCredential,
|
||||
ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject,
|
||||
data monitor.MonitorResourceCreateInput) (monitor.MonitorResourceCreateInput, error) {
|
||||
//rule 查询到资源信息后没有将资源id,进行转换
|
||||
if len(data.ResId) == 0 {
|
||||
return data, httperrors.NewInputParameterError("not found res_id %q", data.ResId)
|
||||
}
|
||||
if len(data.ResType) == 0 {
|
||||
return data, httperrors.NewInputParameterError("not found res_type %q", data.ResType)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (self *SMonitorResource) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (man *SMonitorResourceManager) FetchCustomizeColumns(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
objs []interface{},
|
||||
fields stringutils2.SSortedStrings,
|
||||
isList bool,
|
||||
) []monitor.MonitorResourceDetails {
|
||||
rows := make([]monitor.MonitorResourceDetails, len(objs))
|
||||
virtRows := man.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
for i := range rows {
|
||||
rows[i] = monitor.MonitorResourceDetails{
|
||||
VirtualResourceDetails: virtRows[i],
|
||||
}
|
||||
rows[i] = objs[i].(*SMonitorResource).getMoreDetails(rows[i])
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
func (self *SMonitorResource) AttachAlert(ctx context.Context, userCred mcclient.TokenCredential, alertId string) error {
|
||||
iModel, _ := db.NewModelObject(MonitorResourceAlertManager)
|
||||
input := monitor.MonitorResourceJointCreateInput{
|
||||
MonitorResourceId: self.ResId,
|
||||
AlertId: alertId,
|
||||
AlertState: monitor.MONITOR_RESOURCE_ALERT_STATUS_ATTACH,
|
||||
}
|
||||
data := input.JSON(&input)
|
||||
err := data.Unmarshal(iModel)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "MonitorResourceJointCreateInput unmarshal to joint err")
|
||||
}
|
||||
if err := MonitorResourceAlertManager.TableSpec().Insert(ctx, iModel); err != nil {
|
||||
return errors.Wrap(err, "insert MonitorResourceJoint model err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SMonitorResource) UpdateAlertState() error {
|
||||
joints, _ := MonitorResourceAlertManager.GetJoinsByListInput(monitor.MonitorResourceJointListInput{MonitorResourceId: self.ResId})
|
||||
jointState := monitor.MONITOR_RESOURCE_ALERT_STATUS_ATTACH
|
||||
if len(joints) == 0 {
|
||||
jointState = monitor.MONITOR_RESOURCE_ALERT_STATUS_INIT
|
||||
}
|
||||
for _, joint := range joints {
|
||||
if joint.AlertState == monitor.MONITOR_RESOURCE_ALERT_STATUS_ALERTING && time.Now().Sub(joint.
|
||||
TriggerTime) < time.Minute*30 {
|
||||
jointState = monitor.MONITOR_RESOURCE_ALERT_STATUS_ALERTING
|
||||
}
|
||||
}
|
||||
_, err := db.Update(self, func() error {
|
||||
self.AlertState = jointState
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "SMonitorResource:%s UpdateAlertState err", self.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SMonitorResource) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
err := self.DetachJoint(ctx, userCred)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return self.SVirtualResourceBase.Delete(ctx, userCred)
|
||||
}
|
||||
|
||||
func (self *SMonitorResource) DetachJoint(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
err := MonitorResourceAlertManager.DetachJoint(ctx, userCred,
|
||||
monitor.MonitorResourceJointListInput{MonitorResourceId: self.ResId})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SMonitorResource DetachJoint err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SMonitorResource) getMoreDetails(out monitor.MonitorResourceDetails) monitor.MonitorResourceDetails {
|
||||
joints, err := MonitorResourceAlertManager.GetJoinsByListInput(monitor.
|
||||
MonitorResourceJointListInput{MonitorResourceId: self.ResId})
|
||||
if err != nil {
|
||||
log.Errorf("getMoreDetails err:%v", err)
|
||||
}
|
||||
out.AttachAlertCount = int64(len(joints))
|
||||
return out
|
||||
}
|
||||
|
||||
func (manager *SMonitorResourceManager) AllowGetPropertyAlert(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type AlertStatusCount struct {
|
||||
CountId int64
|
||||
AlertState string
|
||||
}
|
||||
|
||||
func (manager *SMonitorResourceManager) GetPropertyAlert(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
scope, _ := data.GetString("scope")
|
||||
if len(scope) == 0 {
|
||||
scope = "system"
|
||||
}
|
||||
result := jsonutils.NewDict()
|
||||
for resType, _ := range GetResourceSyncMap() {
|
||||
query := manager.Query("alert_state")
|
||||
owner, _ := manager.FetchOwnerId(ctx, data)
|
||||
if owner == nil {
|
||||
owner = userCred
|
||||
}
|
||||
manager.FilterByOwner(query, owner, rbacutils.TRbacScope(scope))
|
||||
query.AppendField(sqlchemy.COUNT("count_id", query.Field("id")))
|
||||
input := monitor.MonitorResourceListInput{ResType: resType}
|
||||
query = manager.FieldListFilter(query, input)
|
||||
query.GroupBy(query.Field("alert_state"))
|
||||
log.Errorf("query:%s", query.String())
|
||||
rows, err := query.Rows()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getMonitorResourceAlert query err")
|
||||
}
|
||||
total := int64(0)
|
||||
resTypeDict := jsonutils.NewDict()
|
||||
for rows.Next() {
|
||||
row := new(AlertStatusCount)
|
||||
err := query.Row2Struct(rows, row)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "MonitorResource Row2Struct err")
|
||||
}
|
||||
resTypeDict.Add(jsonutils.NewInt(row.CountId), row.AlertState)
|
||||
total += row.CountId
|
||||
}
|
||||
resTypeDict.Add(jsonutils.NewInt(total), "total")
|
||||
result.Add(resTypeDict, resType)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (manager *SMonitorResourceManager) UpdateMonitorResourceAttachJoint(ctx context.Context,
|
||||
userCred mcclient.TokenCredential, alertRecord *SAlertRecord) error {
|
||||
if !utils.IsInStringArray(alertRecord.ResType, []string{monitor.METRIC_RES_TYPE_HOST,
|
||||
monitor.METRIC_RES_TYPE_GUEST}) {
|
||||
return nil
|
||||
}
|
||||
|
||||
matches, _ := alertRecord.GetEvalData()
|
||||
errs := make([]error, 0)
|
||||
for _, matche := range matches {
|
||||
resId := matche.Tags[monitor.MEASUREMENT_TAG_ID[alertRecord.ResType]]
|
||||
if len(resId) == 0 {
|
||||
continue
|
||||
}
|
||||
monitorResources, err := manager.GetMonitorResources(monitor.MonitorResourceListInput{ResType: alertRecord.ResType, ResId: []string{resId}})
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrapf(err, "SMonitorResourceManager GetMonitorResources by resId:%s err", resId))
|
||||
continue
|
||||
}
|
||||
for _, res := range monitorResources {
|
||||
err := res.UpdateAttachJoint(alertRecord, matche)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (self *SMonitorResource) UpdateAttachJoint(alertRecord *SAlertRecord, match monitor.EvalMatch) error {
|
||||
joints, err := MonitorResourceAlertManager.GetJoinsByListInput(monitor.MonitorResourceJointListInput{MonitorResourceId: self.
|
||||
ResId, AlertId: alertRecord.AlertId})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "SMonitorResource:%s UpdateAttachJoint err", self.Name)
|
||||
}
|
||||
errs := make([]error, 0)
|
||||
for _, joint := range joints {
|
||||
err := joint.UpdateAlertRecordData(alertRecord, &match)
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrapf(err, "joint %s:%s %s:%s UpdateAlertRecordData err",
|
||||
MonitorResourceAlertManager.GetMasterFieldName(), self.ResId,
|
||||
MonitorResourceAlertManager.GetSlaveFieldName(), alertRecord.AlertId))
|
||||
}
|
||||
}
|
||||
self.UpdateAlertState()
|
||||
return errors.NewAggregate(errs)
|
||||
|
||||
}
|
||||
123
pkg/monitor/models/monitor_resource_alert.go
Normal file
123
pkg/monitor/models/monitor_resource_alert.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
var (
|
||||
MonitorResourceAlertManager *SMonitorResourceAlertManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
MonitorResourceAlertManager = &SMonitorResourceAlertManager{
|
||||
SJointResourceBaseManager: db.NewJointResourceBaseManager(
|
||||
SMonitorResourceAlert{},
|
||||
"monitor_resource_alert_tbl",
|
||||
"monitorresourcealert",
|
||||
"monitorresourcealerts",
|
||||
MonitorResourceManager, CommonAlertManager),
|
||||
}
|
||||
MonitorResourceAlertManager.SetVirtualObject(MonitorResourceAlertManager)
|
||||
}
|
||||
|
||||
type SMonitorResourceAlertManager struct {
|
||||
db.SJointResourceBaseManager
|
||||
}
|
||||
|
||||
type SMonitorResourceAlert struct {
|
||||
db.SJointResourceBase
|
||||
|
||||
MonitorResourceId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required" index:"true" json:"monitor_resource_id"`
|
||||
AlertId string `width:"36" charset:"ascii" list:"user" create:"required" index:"true"`
|
||||
AlertRecordId string `width:"36" charset:"ascii" list:"user" update:"user"`
|
||||
AlertState string `width:"18" charset:"ascii" default:"init" list:"user" update:"user"`
|
||||
TriggerTime time.Time `list:"user" update:"user" json:"trigger_time"`
|
||||
Data jsonutils.JSONObject `list:"user" update:"user"`
|
||||
}
|
||||
|
||||
func (manager *SMonitorResourceAlertManager) GetMasterFieldName() string {
|
||||
return "monitor_resource_id"
|
||||
}
|
||||
|
||||
func (manager *SMonitorResourceAlertManager) GetSlaveFieldName() string {
|
||||
return "alert_id"
|
||||
}
|
||||
|
||||
func (manager *SMonitorResourceAlertManager) DetachJoint(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
input monitor.MonitorResourceJointListInput) error {
|
||||
joints, err := manager.GetJoinsByListInput(input)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "SMonitorResourceAlertManager DetachJoint when GetJoinsByListInput err,input:%v", input)
|
||||
}
|
||||
errs := make([]error, 0)
|
||||
for _, joint := range joints {
|
||||
err := joint.Delete(ctx, userCred)
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrapf(err, "joint %s:%s ,%s:%s", manager.GetMasterFieldName(),
|
||||
joint.MonitorResourceId, manager.GetSlaveFieldName(), joint.AlertId))
|
||||
continue
|
||||
}
|
||||
resources, err := MonitorResourceManager.GetMonitorResources(monitor.MonitorResourceListInput{ResId: []string{joint.
|
||||
MonitorResourceId}})
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
for _, res := range resources {
|
||||
err := res.UpdateAlertState()
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (obj *SMonitorResourceAlert) Detach(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
return db.DetachJoint(ctx, userCred, obj)
|
||||
}
|
||||
|
||||
func (obj *SMonitorResourceAlert) GetAlert() (*SCommonAlert, error) {
|
||||
sObj, err := CommonAlertManager.FetchById(obj.AlertId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sObj.(*SCommonAlert), nil
|
||||
}
|
||||
|
||||
func (manager *SMonitorResourceAlertManager) GetJoinsByListInput(input monitor.MonitorResourceJointListInput) ([]SMonitorResourceAlert, error) {
|
||||
joints := make([]SMonitorResourceAlert, 0)
|
||||
query := manager.Query()
|
||||
|
||||
if len(input.MonitorResourceId) != 0 {
|
||||
query.Equals(manager.GetMasterFieldName(), input.MonitorResourceId)
|
||||
}
|
||||
if len(input.AlertId) != 0 {
|
||||
query.Equals(manager.GetSlaveFieldName(), input.AlertId)
|
||||
}
|
||||
err := db.FetchModelObjects(manager, query, &joints)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "FetchModelObjects by GetJoinsByMasterId:%s err", input)
|
||||
}
|
||||
return joints, nil
|
||||
}
|
||||
|
||||
func (obj *SMonitorResourceAlert) UpdateAlertRecordData(record *SAlertRecord, match *monitor.EvalMatch) error {
|
||||
if _, err := db.Update(obj, func() error {
|
||||
obj.AlertRecordId = record.GetId()
|
||||
obj.AlertState = record.State
|
||||
obj.TriggerTime = record.CreatedAt
|
||||
obj.Data = jsonutils.Marshal(match)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
242
pkg/monitor/models/monitor_resource_sync.go
Normal file
242
pkg/monitor/models/monitor_resource_sync.go
Normal file
@@ -0,0 +1,242 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostinfo/hostconsts"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
mc_mds "yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
)
|
||||
|
||||
var (
|
||||
resourceSyncMap map[string]IResourceSync
|
||||
guestResourceSync IResourceSync
|
||||
hostResourceSync IResourceSync
|
||||
)
|
||||
|
||||
func RegistryResourceSync(sync IResourceSync) error {
|
||||
if resourceSyncMap == nil {
|
||||
resourceSyncMap = make(map[string]IResourceSync)
|
||||
}
|
||||
if _, ok := resourceSyncMap[sync.SyncType()]; ok {
|
||||
return errors.Errorf(fmt.Sprintf("syncType:%s has registered", sync.SyncType()))
|
||||
}
|
||||
resourceSyncMap[sync.SyncType()] = sync
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetResourceSyncByType(syncType string) IResourceSync {
|
||||
if resourceSyncMap == nil {
|
||||
resourceSyncMap = make(map[string]IResourceSync)
|
||||
}
|
||||
return resourceSyncMap[syncType]
|
||||
}
|
||||
|
||||
func GetResourceSyncMap() map[string]IResourceSync {
|
||||
if resourceSyncMap == nil {
|
||||
resourceSyncMap = make(map[string]IResourceSync)
|
||||
}
|
||||
return resourceSyncMap
|
||||
}
|
||||
|
||||
type SyncObject struct {
|
||||
sync IResourceSync
|
||||
}
|
||||
|
||||
type IResourceSync interface {
|
||||
SyncResources(ctx context.Context, userCred mcclient.TokenCredential, param jsonutils.JSONObject) error
|
||||
SyncType() string
|
||||
}
|
||||
|
||||
type GuestResourceSync struct {
|
||||
SyncObject
|
||||
}
|
||||
|
||||
func NewGuestResourceSync() IResourceSync {
|
||||
if guestResourceSync == nil {
|
||||
sync := new(GuestResourceSync)
|
||||
obj := newSyncObj(sync)
|
||||
sync.SyncObject = obj
|
||||
guestResourceSync = sync
|
||||
}
|
||||
|
||||
return guestResourceSync
|
||||
}
|
||||
|
||||
func (g *GuestResourceSync) SyncType() string {
|
||||
return monitor.METRIC_RES_TYPE_GUEST
|
||||
}
|
||||
|
||||
type HostResourceSync struct {
|
||||
SyncObject
|
||||
}
|
||||
|
||||
func (self *HostResourceSync) SyncType() string {
|
||||
return monitor.METRIC_RES_TYPE_HOST
|
||||
}
|
||||
|
||||
func NewHostResourceSync() IResourceSync {
|
||||
if hostResourceSync == nil {
|
||||
sync := new(HostResourceSync)
|
||||
obj := newSyncObj(sync)
|
||||
sync.SyncObject = obj
|
||||
hostResourceSync = sync
|
||||
}
|
||||
return hostResourceSync
|
||||
}
|
||||
|
||||
func newSyncObj(sync IResourceSync) SyncObject {
|
||||
return SyncObject{sync: sync}
|
||||
}
|
||||
|
||||
func (self *SyncObject) SyncResources(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
param jsonutils.JSONObject) error {
|
||||
log.Errorf("start sync %s", self.sync.SyncType())
|
||||
resources, err := GetOnecloudResources(self.sync.SyncType())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, fmt.Sprintf("syncType:%s GetOnecloudResources err", self.sync.SyncType()))
|
||||
}
|
||||
input := monitor.MonitorResourceListInput{
|
||||
OnlyResId: true,
|
||||
ResType: self.sync.SyncType(),
|
||||
}
|
||||
monResources, err := MonitorResourceManager.GetMonitorResources(input)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetMonitorResources err")
|
||||
}
|
||||
errs := make([]error, 0)
|
||||
monLoop:
|
||||
for i, _ := range monResources {
|
||||
for index, res := range resources {
|
||||
resId, _ := res.GetString("id")
|
||||
if resId == monResources[i].ResId {
|
||||
resource, err := MonitorResourceManager.GetMonitorResourceById(monResources[i].GetId())
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue monLoop
|
||||
}
|
||||
_, err = db.Update(resource, func() error {
|
||||
res.Unmarshal(resource)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrapf(err, "monitorResource:%s Update err", resource.Name))
|
||||
continue monLoop
|
||||
}
|
||||
if index == len(resources)-1 {
|
||||
resources = resources[0:index]
|
||||
} else {
|
||||
resources = append(resources[0:index], resources[index+1:]...)
|
||||
}
|
||||
|
||||
index--
|
||||
continue monLoop
|
||||
}
|
||||
}
|
||||
resource, _ := MonitorResourceManager.GetMonitorResourceById(monResources[i].GetId())
|
||||
err := resource.RealDelete(ctx, userCred)
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrapf(err, "delete monitorResource:%s err", resource.GetId()))
|
||||
}
|
||||
}
|
||||
for _, res := range resources {
|
||||
createData := self.newMonitorResourceCreateInput(res)
|
||||
_, err = db.DoCreate(MonitorResourceManager, ctx, userCred, nil, createData,
|
||||
userCred)
|
||||
if err != nil {
|
||||
name, _ := createData.GetString("name")
|
||||
errs = append(errs, errors.Wrapf(err, "monitorResource:%s resType:%s DoCreate err", name, self.sync.SyncType()))
|
||||
}
|
||||
}
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (self *SyncObject) newMonitorResourceCreateInput(input jsonutils.JSONObject) jsonutils.JSONObject {
|
||||
monitorResource := jsonutils.DeepCopy(input).(*jsonutils.JSONDict)
|
||||
id, _ := monitorResource.GetString("id")
|
||||
monitorResource.Add(jsonutils.NewString(id), "res_id")
|
||||
monitorResource.Remove("id")
|
||||
monitorResource.Add(jsonutils.NewString(self.sync.SyncType()), "res_type")
|
||||
|
||||
return monitorResource
|
||||
}
|
||||
|
||||
func GetOnecloudResources(resTyep string) ([]jsonutils.JSONObject, error) {
|
||||
var err error
|
||||
allResources := make([]jsonutils.JSONObject, 0)
|
||||
|
||||
query := jsonutils.NewDict()
|
||||
query.Add(jsonutils.NewStringArray([]string{"running", "ready"}), "status")
|
||||
query.Add(jsonutils.NewString("true"), "admin")
|
||||
switch resTyep {
|
||||
case monitor.METRIC_RES_TYPE_HOST:
|
||||
//query.Set("host-type", jsonutils.NewString(hostconsts.TELEGRAF_TAG_KEY_HYPERVISOR))
|
||||
allResources, err = ListAllResources(&mc_mds.Hosts, query)
|
||||
case monitor.METRIC_RES_TYPE_GUEST:
|
||||
allResources, err = ListAllResources(&mc_mds.Servers, query)
|
||||
case monitor.METRIC_RES_TYPE_RDS:
|
||||
allResources, err = ListAllResources(&mc_mds.DBInstance, query)
|
||||
case monitor.METRIC_RES_TYPE_REDIS:
|
||||
allResources, err = ListAllResources(&mc_mds.ElasticCache, query)
|
||||
case monitor.METRIC_RES_TYPE_OSS:
|
||||
allResources, err = ListAllResources(&mc_mds.Buckets, query)
|
||||
case monitor.METRIC_RES_TYPE_CLOUDACCOUNT:
|
||||
query.Remove("status")
|
||||
query.Add(jsonutils.NewBool(true), "enabled")
|
||||
allResources, err = ListAllResources(&mc_mds.Cloudaccounts, query)
|
||||
case monitor.METRIC_RES_TYPE_TENANT:
|
||||
allResources, err = ListAllResources(&mc_mds.Projects, query)
|
||||
case monitor.METRIC_RES_TYPE_DOMAIN:
|
||||
allResources, err = ListAllResources(&mc_mds.Domains, query)
|
||||
case monitor.METRIC_RES_TYPE_STORAGE:
|
||||
query.Remove("status")
|
||||
allResources, err = ListAllResources(&mc_mds.Storages, query)
|
||||
default:
|
||||
query := jsonutils.NewDict()
|
||||
query.Set("brand", jsonutils.NewString(hostconsts.TELEGRAF_TAG_ONECLOUD_BRAND))
|
||||
query.Set("host-type", jsonutils.NewString(hostconsts.TELEGRAF_TAG_KEY_HYPERVISOR))
|
||||
allResources, err = ListAllResources(&mc_mds.Hosts, query)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "NoDataQueryCondition Host list error")
|
||||
}
|
||||
return allResources, nil
|
||||
}
|
||||
|
||||
func ListAllResources(manager modulebase.Manager, params *jsonutils.JSONDict) ([]jsonutils.JSONObject, error) {
|
||||
if params == nil {
|
||||
params = jsonutils.NewDict()
|
||||
}
|
||||
params.Add(jsonutils.NewString("system"), "scope")
|
||||
params.Add(jsonutils.NewInt(0), "limit")
|
||||
params.Add(jsonutils.NewBool(true), "details")
|
||||
var count int
|
||||
session := auth.GetAdminSession(context.Background(), "", "")
|
||||
objs := make([]jsonutils.JSONObject, 0)
|
||||
for {
|
||||
params.Set("offset", jsonutils.NewInt(int64(count)))
|
||||
result, err := manager.List(session, params)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "list %s resources with params %s", manager.KeyString(), params.String())
|
||||
}
|
||||
for _, data := range result.Data {
|
||||
objs = append(objs, data)
|
||||
}
|
||||
total := result.Total
|
||||
count = count + len(result.Data)
|
||||
if count >= total {
|
||||
break
|
||||
}
|
||||
}
|
||||
return objs, nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user