mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-20 16:13:56 +08:00
Compare commits
243 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1760d7b86 | ||
|
|
c456586860 | ||
|
|
71e759a032 | ||
|
|
96ce64d5f0 | ||
|
|
d31802c87b | ||
|
|
d3964bf0d1 | ||
|
|
57dad6281a | ||
|
|
b74130903e | ||
|
|
44046f2861 | ||
|
|
95f7600b67 | ||
|
|
749b819a62 | ||
|
|
f7f7c7be81 | ||
|
|
6a221dbc1e | ||
|
|
002a2af272 | ||
|
|
1757d3d6a9 | ||
|
|
be5a6a93cc | ||
|
|
bbbe78dbef | ||
|
|
a851e01330 | ||
|
|
29d06d52dd | ||
|
|
1a6d4d1035 | ||
|
|
90d649aef9 | ||
|
|
23b99a6232 | ||
|
|
16c38fe6be | ||
|
|
b43f8b3fac | ||
|
|
8e5b74a3f6 | ||
|
|
b0cd3bf999 | ||
|
|
4a15284684 | ||
|
|
d78068d43a | ||
|
|
80dce5eafb | ||
|
|
950696ddb9 | ||
|
|
e7674116e1 | ||
|
|
3fae388db5 | ||
|
|
8f3b8c7ff1 | ||
|
|
a4b1d3952d | ||
|
|
a5ef16243d | ||
|
|
b0823a09bb | ||
|
|
492920ac7c | ||
|
|
f96ac33175 | ||
|
|
1bc16d6f2e | ||
|
|
0ba8cb7fbd | ||
|
|
93bb4022f2 | ||
|
|
bbfb9db90a | ||
|
|
66c81ee548 | ||
|
|
8f3cb9cdbb | ||
|
|
1ac71d0016 | ||
|
|
e8238a2cf5 | ||
|
|
22349276d9 | ||
|
|
d6a9b5dac2 | ||
|
|
0e3aa96f77 | ||
|
|
5306add70a | ||
|
|
f367f1abef | ||
|
|
93dc36134d | ||
|
|
f706d59ac5 | ||
|
|
7438bcab23 | ||
|
|
e038eebeb7 | ||
|
|
bd7d84aebd | ||
|
|
5f6f554464 | ||
|
|
d99d93d046 | ||
|
|
4a856ac688 | ||
|
|
7da0a67af5 | ||
|
|
37c14d756d | ||
|
|
6f1417d268 | ||
|
|
b3105a05d3 | ||
|
|
93ac3260a1 | ||
|
|
3d39c20c91 | ||
|
|
8a0eeb7d67 | ||
|
|
163be04dbb | ||
|
|
06db7339e4 | ||
|
|
e5b3f4673d | ||
|
|
3a6e910580 | ||
|
|
0317fad04e | ||
|
|
af11737e79 | ||
|
|
15bd6bacd6 | ||
|
|
28ba60f630 | ||
|
|
8799a0bcf9 | ||
|
|
6706c39f85 | ||
|
|
8ce183eaf5 | ||
|
|
a5bec974f1 | ||
|
|
ac15981b5d | ||
|
|
3c447a32f6 | ||
|
|
3a8809e060 | ||
|
|
0964c6ad82 | ||
|
|
5d0fd14cb3 | ||
|
|
cd123e007d | ||
|
|
91736710ba | ||
|
|
c2589e7352 | ||
|
|
d454b0a792 | ||
|
|
2a1a640f6b | ||
|
|
9dee4c1b90 | ||
|
|
0fd6481e4d | ||
|
|
d799a71fdb | ||
|
|
436f1e5cb9 | ||
|
|
a92a0e2d05 | ||
|
|
0cafa996d7 | ||
|
|
2a10b72f98 | ||
|
|
31c2b6270a | ||
|
|
a6397d18c2 | ||
|
|
203bf44639 | ||
|
|
d7a4e52b15 | ||
|
|
7946dcef3f | ||
|
|
f77bcd3bbf | ||
|
|
4841fbb843 | ||
|
|
9b8758e191 | ||
|
|
62f5355b11 | ||
|
|
d774581a23 | ||
|
|
062301f54a | ||
|
|
16d13941ce | ||
|
|
a10b1945f6 | ||
|
|
3aadaf15b7 | ||
|
|
7a9b491d13 | ||
|
|
ecbe76e9ff | ||
|
|
41bdfffd57 | ||
|
|
a9c44ed304 | ||
|
|
7a2c2b8047 | ||
|
|
a35a6f3c11 | ||
|
|
5a7876ae47 | ||
|
|
31de731cbd | ||
|
|
8b2680ed56 | ||
|
|
39c35359aa | ||
|
|
a764a64696 | ||
|
|
f05a56aadd | ||
|
|
6cd69d3587 | ||
|
|
2a1bbfd574 | ||
|
|
301006e213 | ||
|
|
950a2c29fb | ||
|
|
558872db27 | ||
|
|
6386391bf4 | ||
|
|
d298c75448 | ||
|
|
28cb75f41b | ||
|
|
560a68d379 | ||
|
|
11f8e4f71f | ||
|
|
9e597db90a | ||
|
|
9c9cb17d32 | ||
|
|
cc96c01c00 | ||
|
|
cb22077722 | ||
|
|
8e3c173373 | ||
|
|
45e092bd2d | ||
|
|
4b2d67de42 | ||
|
|
6677c30a5d | ||
|
|
64593fc882 | ||
|
|
0d7e1a2517 | ||
|
|
6fc835abb9 | ||
|
|
44a9cc5adb | ||
|
|
ff2b7eb345 | ||
|
|
7b5d822373 | ||
|
|
f5a45fc16e | ||
|
|
a583bbc74b | ||
|
|
ece82cce42 | ||
|
|
8042b9430c | ||
|
|
d1cbaead09 | ||
|
|
1768d1e7db | ||
|
|
ff34e93862 | ||
|
|
a891d1d876 | ||
|
|
c7d6bb7811 | ||
|
|
2076a75113 | ||
|
|
5d0d6d960b | ||
|
|
cf89e8a23a | ||
|
|
45994b59d5 | ||
|
|
d2d2934e80 | ||
|
|
0ec0c8e703 | ||
|
|
3347d6c1e1 | ||
|
|
dce9d5e4a1 | ||
|
|
ef9b64990e | ||
|
|
f19fe2178c | ||
|
|
2265c9422f | ||
|
|
cf3717557a | ||
|
|
0bbdcb9278 | ||
|
|
d72081770e | ||
|
|
0f874d8f07 | ||
|
|
dd5dd15a76 | ||
|
|
cb36572c1c | ||
|
|
2a210407a5 | ||
|
|
82c59154c5 | ||
|
|
041ee6f904 | ||
|
|
4317fac6da | ||
|
|
a022f99851 | ||
|
|
407251975e | ||
|
|
b7fd879e44 | ||
|
|
0a7ccbe958 | ||
|
|
7efb85ba04 | ||
|
|
84e8e4e6c8 | ||
|
|
3fe15d2675 | ||
|
|
d574a823ba | ||
|
|
a2c8ad13ab | ||
|
|
de8f57f625 | ||
|
|
658159b18e | ||
|
|
67cc7b8747 | ||
|
|
ce694ff638 | ||
|
|
98702c2470 | ||
|
|
9ac5265c07 | ||
|
|
896bec8f43 | ||
|
|
a8426482bf | ||
|
|
d7316b26aa | ||
|
|
d16affc9a6 | ||
|
|
2259fe1195 | ||
|
|
09e37de117 | ||
|
|
715db28e3e | ||
|
|
152ea54f36 | ||
|
|
865b433bb0 | ||
|
|
f04a37d0a3 | ||
|
|
7edcd151e3 | ||
|
|
df90d99bc5 | ||
|
|
552ce3b2ea | ||
|
|
91c852676a | ||
|
|
8528d79817 | ||
|
|
d2aacafa4c | ||
|
|
8714503087 | ||
|
|
d0d3d8e333 | ||
|
|
dee1aa26dc | ||
|
|
c973bf1632 | ||
|
|
1aa82ae950 | ||
|
|
488e7e56a6 | ||
|
|
6a2ebdc123 | ||
|
|
4105284718 | ||
|
|
ac5f29bc0c | ||
|
|
58d52d947c | ||
|
|
c5ef564d35 | ||
|
|
1cacf1068f | ||
|
|
63b0d0c071 | ||
|
|
39a45229af | ||
|
|
e82cf184f1 | ||
|
|
afa16944c0 | ||
|
|
8f953e41da | ||
|
|
6e0180656d | ||
|
|
718bf0d537 | ||
|
|
9fbd4f0599 | ||
|
|
078b1d635a | ||
|
|
b8e0599e3f | ||
|
|
27bd564d2d | ||
|
|
5b79ba5607 | ||
|
|
c9d20d13f1 | ||
|
|
779d4a9acf | ||
|
|
1bc4b3cb90 | ||
|
|
49887e540a | ||
|
|
7ce0d7b44b | ||
|
|
e8d737ff6c | ||
|
|
fd954efcf2 | ||
|
|
e9415ae49e | ||
|
|
5b0afbdc6b | ||
|
|
ec393a6e3c | ||
|
|
adccad3487 | ||
|
|
76f794d1a2 | ||
|
|
bba2cf23fd |
@@ -38,7 +38,7 @@ jobs:
|
||||
command: |
|
||||
targets="$(circleci tests glob "cmd/*" | grep -v cmd/host-image | circleci tests split)"
|
||||
echo $targets | tr ' ' '\n'
|
||||
make -j3 $targets
|
||||
make -j2 $targets
|
||||
ls -lh _output/bin/
|
||||
- save_cache:
|
||||
key: onecloud-build-cache-{{ checksum "go.mod" }}-{{ checksum "Makefile" }}
|
||||
|
||||
1
Makefile
1
Makefile
@@ -314,6 +314,7 @@ VERSION ?= $(shell git describe --exact-match 2> /dev/null || \
|
||||
git describe --match=$(git rev-parse --short=8 HEAD) --always --dirty --abbrev=8)
|
||||
|
||||
image:
|
||||
mkdir -p $(ROOT_DIR)/_output
|
||||
DEBUG=$(DEBUG) ARCH=$(ARCH) TAG=$(VERSION) REGISTRY=$(REGISTRY) $(ROOT_DIR)/scripts/docker_push.sh $(filter-out $@,$(MAKECMDGOALS))
|
||||
|
||||
.PHONY: image
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM registry.cn-beijing.aliyuncs.com/yunionio/host-deployer-base:v0.4
|
||||
FROM registry.cn-beijing.aliyuncs.com/yunionio/host-deployer-base:v0.5
|
||||
|
||||
MAINTAINER "Yaoqi Wan wanyaoqi@yunionyun.com"
|
||||
|
||||
|
||||
3
build/docker/Dockerfile.telegraf-raid
Normal file
3
build/docker/Dockerfile.telegraf-raid
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM registry.cn-beijing.aliyuncs.com/yunionio/yunion-rootfs:v1.0
|
||||
|
||||
ADD ./_output/bin/telegraf-raid-plugin /opt/yunion/bin/telegraf-raid-plugin
|
||||
@@ -1,6 +1,7 @@
|
||||
## {{.title}}
|
||||
- 时间: {{.start_time}}
|
||||
- 级别: {{.level}}
|
||||
- 详情地址: {{.web_url}}/commonalerts
|
||||
|
||||
{{ range .matches}}
|
||||
- 指标: {{.metric}}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
## {{.title}}
|
||||
- Time: {{.start_time}}
|
||||
- Level: {{.level}}
|
||||
- WebUrl: {{.web_url}}/commonalerts
|
||||
|
||||
{{ range .matches}}
|
||||
- Metric: {{.metric}}
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
Help bool `help:"Show help"`
|
||||
CloudEnv string `help:"Cloud environment" default:"$ALIYUN_CLOUD_ENV" choices:"InternationalCloud|FinanceCloud" metavar:"ALIYUN_CLOUD_ENV"`
|
||||
AccessKey string `help:"Access key" default:"$ALIYUN_ACCESS_KEY" metavar:"ALIYUN_ACCESS_KEY"`
|
||||
Secret string `help:"Secret" default:"$ALIYUN_SECRET" metavar:"ALIYUN_SECRET"`
|
||||
RegionId string `help:"RegionId" default:"$ALIYUN_REGION" metavar:"ALIYUN_REGION"`
|
||||
@@ -86,7 +87,9 @@ func newClient(options *BaseOptions) (*aliyun.SRegion, error) {
|
||||
|
||||
cli, err := aliyun.NewAliyunClient(
|
||||
aliyun.NewAliyunClientConfig(
|
||||
options.AccessKey, options.Secret,
|
||||
options.CloudEnv,
|
||||
options.AccessKey,
|
||||
options.Secret,
|
||||
).Debug(options.Debug),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -27,6 +27,9 @@ func init() {
|
||||
|
||||
Region string `help:"show images cached at cloud region"`
|
||||
Zone string `help:"show images cached at zone"`
|
||||
|
||||
HostSchedtagId string `help:"filter cached image with host schedtag"`
|
||||
Valid *bool `help:"valid cachedimage"`
|
||||
}
|
||||
R(&CachedImageListOptions{}, "cached-image-list", "List cached images", func(s *mcclient.ClientSession, args *CachedImageListOptions) error {
|
||||
params, err := options.ListStructToParams(args)
|
||||
|
||||
@@ -259,10 +259,10 @@ func init() {
|
||||
params.Add(jsonutils.NewString(args.Desc), "description")
|
||||
}
|
||||
if args.CpuCommitBound > 0.0 {
|
||||
params.Add(jsonutils.NewFloat(args.CpuCommitBound), "cpu_cmtbound")
|
||||
params.Add(jsonutils.NewFloat64(args.CpuCommitBound), "cpu_cmtbound")
|
||||
}
|
||||
if args.MemoryCommitBound > 0.0 {
|
||||
params.Add(jsonutils.NewFloat(args.MemoryCommitBound), "mem_cmtbound")
|
||||
params.Add(jsonutils.NewFloat64(args.MemoryCommitBound), "mem_cmtbound")
|
||||
}
|
||||
if len(args.MemoryReserved) > 0 {
|
||||
params.Add(jsonutils.NewString(args.MemoryReserved), "mem_reserved")
|
||||
|
||||
@@ -83,6 +83,7 @@ func init() {
|
||||
cmd.Perform("publicip-to-eip", new(options.ServerPublicipToEip))
|
||||
cmd.Perform("set-auto-renew", new(options.ServerSetAutoRenew))
|
||||
cmd.Perform("save-template", new(options.ServerSaveImageOptions))
|
||||
cmd.Perform("remote-update", new(options.ServerRemoteUpdateOptions))
|
||||
|
||||
cmd.Get("vnc", new(options.ServerIdOptions))
|
||||
cmd.Get("desc", new(options.ServerIdOptions))
|
||||
|
||||
@@ -37,6 +37,7 @@ func init() {
|
||||
Schedtag string `help:"filter storage by schedtag"`
|
||||
|
||||
HostSchedtagId string `help:"filter storage by host schedtag"`
|
||||
ImageId string `help:"filter storage by image"`
|
||||
}
|
||||
R(&StorageListOptions{}, "storage-list", "List storages", func(s *mcclient.ClientSession, opts *StorageListOptions) error {
|
||||
params, err := options.ListStructToParams(opts)
|
||||
|
||||
@@ -284,10 +284,7 @@ type IPerformOpt interface {
|
||||
func (cmd ResourceCmd) Perform(action string, args IPerformOpt) {
|
||||
man := cmd.manager
|
||||
callback := func(s *mcclient.ClientSession, args IPerformOpt) error {
|
||||
params, err := args.Params()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params := jsonutils.Marshal(args) // .Params()
|
||||
ret, err := man.(modulebase.Manager).PerformAction(s, args.GetId(), action, params)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -488,7 +485,7 @@ func (cmd JointCmd) List(args IJointListOpt) {
|
||||
if len(args.GetMasterOpt()) > 0 {
|
||||
result, err = man.ListDescendent(s, args.GetMasterOpt(), params)
|
||||
} else if len(args.GetSlaveOpt()) > 0 {
|
||||
result, err = man.ListDescendent2(s, args.GetMasterOpt(), params)
|
||||
result, err = man.ListDescendent2(s, args.GetSlaveOpt(), params)
|
||||
} else {
|
||||
result, err = man.List(s, params)
|
||||
}
|
||||
|
||||
@@ -37,11 +37,13 @@ func init() {
|
||||
RoleDomain string `help:"Domain for role"`
|
||||
Limit int64 `help:"maximal returned number of rows"`
|
||||
Offset int64 `help:"offset index of returned results"`
|
||||
Users []string `help:"fitler by users id or name"`
|
||||
Groups []string `help:"fitler by users id or name"`
|
||||
Roles []string `help:"fitler by users id or name"`
|
||||
Projects []string `help:"fitler by users id or name"`
|
||||
Domains []string `help:"fitler by users id or name"`
|
||||
Users []string `help:"fitler by user id or name"`
|
||||
Groups []string `help:"fitler by group id or name"`
|
||||
Roles []string `help:"fitler by role id or name"`
|
||||
Projects []string `help:"fitler by project id or name"`
|
||||
Domains []string `help:"fitler by domain id or name"`
|
||||
|
||||
ProjectDomains []string `help:"filter by project's domain id or name"`
|
||||
}
|
||||
R(&RoleAssignmentsOptions{}, "role-assignments", "List all role assignments", func(s *mcclient.ClientSession, args *RoleAssignmentsOptions) error {
|
||||
query := jsonutils.NewDict()
|
||||
@@ -105,6 +107,9 @@ func init() {
|
||||
if len(args.Domains) > 0 {
|
||||
query.Add(jsonutils.NewStringArray(args.Domains), "domains")
|
||||
}
|
||||
if len(args.ProjectDomains) > 0 {
|
||||
query.Add(jsonutils.NewStringArray(args.ProjectDomains), "project_domains")
|
||||
}
|
||||
if args.Limit > 0 {
|
||||
query.Add(jsonutils.NewInt(args.Limit), "limit")
|
||||
}
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -25,4 +28,36 @@ func init() {
|
||||
printObject(s.ToJson())
|
||||
return nil
|
||||
})
|
||||
type FreezeResrouceOptions struct {
|
||||
ID string `help:"ID of resource"`
|
||||
Module string `help:"Resource module type, eg: servers, disks, networks"`
|
||||
}
|
||||
R(&FreezeResrouceOptions{}, "freeze-resource", "Freeze resource operation update and perform action except for unfreeze", func(s *mcclient.ClientSession, args *FreezeResrouceOptions) error {
|
||||
mod, err := modulebase.GetModule(s, args.Module)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed get module")
|
||||
}
|
||||
obj, err := mod.PerformAction(s, args.ID, "freeze", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(obj)
|
||||
return nil
|
||||
})
|
||||
type UnfreezeResrouceOptions struct {
|
||||
ID string `help:"ID of resource"`
|
||||
Module string `help:"Resource module type, eg: servers, disks, networks"`
|
||||
}
|
||||
R(&UnfreezeResrouceOptions{}, "unfreeze-resource", "Unfreeze resource", func(s *mcclient.ClientSession, args *UnfreezeResrouceOptions) error {
|
||||
mod, err := modulebase.GetModule(s, args.Module)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed get module")
|
||||
}
|
||||
obj, err := mod.PerformAction(s, args.ID, "unfreeze", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(obj)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
31
cmd/climc/shell/monitor/alertresource.go
Normal file
31
cmd/climc/shell/monitor/alertresource.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/cmd/climc/shell"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/monitor"
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/monitor"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd := shell.NewResourceCmd(monitor.AlertResources)
|
||||
cmd.List(new(options.AlertResourceListOptions))
|
||||
cmd.Show(new(options.AlertResourceShowOptions))
|
||||
cmd.BatchDelete(new(options.AlertResourceDeleteOptions))
|
||||
|
||||
jCmd := shell.NewJointCmd(monitor.AlertResourceAlert)
|
||||
jCmd.List(new(options.AlertResourceAlertListOptions))
|
||||
}
|
||||
@@ -12,5 +12,6 @@ func init() {
|
||||
cmd.Show(new(options.CommonAlertShowOptions))
|
||||
cmd.Perform("enable", &options.CommonAlertShowOptions{})
|
||||
cmd.Perform("disable", &options.CommonAlertShowOptions{})
|
||||
cmd.Delete(new(options.CommonAlertDeleteOptions))
|
||||
cmd.BatchDelete(new(options.CommonAlertDeleteOptions))
|
||||
cmd.Perform("config", &options.CommonAlertUpdateOptions{})
|
||||
}
|
||||
|
||||
@@ -15,70 +15,19 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/monitor"
|
||||
"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() {
|
||||
aN := cmdN("suggestsysrule")
|
||||
R(&options.SuggestRuleListOptions{}, aN("list"), "List all suggestsysrules",
|
||||
func(s *mcclient.ClientSession, args *options.SuggestRuleListOptions) error {
|
||||
params, err := args.Params()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret, err := monitor.SuggestSysRuleManager.List(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printList(ret, monitor.SuggestSysRuleManager.GetColumns(s))
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&options.SuggestRuleCreateOptions{}, aN("create"), "Create suggestsys rule",
|
||||
func(s *mcclient.ClientSession, args *options.SuggestRuleCreateOptions) error {
|
||||
params, err := args.Params()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret, err := monitor.SuggestSysRuleManager.Create(s, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(ret)
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&options.SuggestRuleShowOptions{}, aN("show"), "Show details of a alert rule",
|
||||
func(s *mcclient.ClientSession, args *options.SuggestRuleShowOptions) error {
|
||||
ret, err := monitor.SuggestSysRuleManager.Get(s, args.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(ret)
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&options.SuggestRuleUpdateOptions{}, aN("update"), "Update a alert rule",
|
||||
func(s *mcclient.ClientSession, args *options.SuggestRuleUpdateOptions) error {
|
||||
params, err := args.Params()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ret, err := monitor.SuggestSysRuleManager.Update(s, args.ID, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printObject(ret)
|
||||
return nil
|
||||
})
|
||||
|
||||
R(&options.SuggestRuleDeleteOptions{}, aN("delete"), "Delete alerts",
|
||||
func(s *mcclient.ClientSession, args *options.SuggestRuleDeleteOptions) error {
|
||||
ret := monitor.SuggestSysRuleManager.BatchDelete(s, args.ID, nil)
|
||||
printBatchResults(ret, monitor.SuggestSysRuleManager.GetColumns(s))
|
||||
return nil
|
||||
})
|
||||
cmd := shell.NewResourceCmd(modules.SuggestSysRuleManager)
|
||||
cmd.List(new(options.SuggestRuleListOptions))
|
||||
cmd.Show(new(options.SuggestRuleShowOptions))
|
||||
cmd.Perform("enable", new(options.SuggestRuleShowOptions))
|
||||
cmd.Perform("disable", new(options.SuggestRuleShowOptions))
|
||||
cmd.BatchDelete(new(options.CommonAlertDeleteOptions))
|
||||
cmd.Perform("config", new(options.SuggestRuleConfigOptions))
|
||||
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func main() {
|
||||
|
||||
err = fs.Serve(c, *fetcherFs)
|
||||
if err != nil {
|
||||
log.Errorln("serve failed %s", err)
|
||||
log.Errorf("serve failed %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ type BaseOptions struct {
|
||||
Host string `help:"SSH Host IP" default:"$RAID_HOST" metavar:"RAID_HOST"`
|
||||
Username string `help:"Username, usually root" default:"$RAID_USERNAME" metavar:"RAID_USERNAME"`
|
||||
Password string `help:"Password" default:"$RAID_PASSWORD" metavar:"RAID_PASSWORD"`
|
||||
Driver string `help:"Password" default:"$RAID_DRIVER" metavar:"RAID_DRIVER" choices:"MegaRaid|HPSARaid|Mpt2SAS|MarvelRaid"`
|
||||
Driver string `help:"Raid dirver" default:"$RAID_DRIVER" metavar:"RAID_DRIVER" choices:"MegaRaid|HPSARaid|Mpt2SAS|MarvelRaid"`
|
||||
LocalHost bool `help:"Run raidcli in localhost"`
|
||||
SUBCOMMAND string `help:"s3cli subcommand" subcommand:"true"`
|
||||
}
|
||||
|
||||
@@ -83,43 +84,48 @@ func showErrorAndExit(e error) {
|
||||
}
|
||||
|
||||
func newClient() (raid.IRaidDriver, error) {
|
||||
if len(options.Host) == 0 {
|
||||
return nil, fmt.Errorf("Missing host")
|
||||
}
|
||||
|
||||
if len(options.Username) == 0 {
|
||||
return nil, fmt.Errorf("Missing username")
|
||||
}
|
||||
|
||||
if len(options.Password) == 0 {
|
||||
return nil, fmt.Errorf("Missing password")
|
||||
if options.Debug {
|
||||
raid.Debug = true
|
||||
}
|
||||
|
||||
if len(options.Driver) == 0 {
|
||||
return nil, fmt.Errorf("Missing driver")
|
||||
}
|
||||
|
||||
if options.Debug {
|
||||
raid.Debug = true
|
||||
var drv raid.IRaidDriver
|
||||
if !options.LocalHost {
|
||||
if len(options.Host) == 0 {
|
||||
return nil, fmt.Errorf("Missing host")
|
||||
}
|
||||
|
||||
if len(options.Username) == 0 {
|
||||
return nil, fmt.Errorf("Missing username")
|
||||
}
|
||||
|
||||
if len(options.Password) == 0 {
|
||||
return nil, fmt.Errorf("Missing password")
|
||||
}
|
||||
|
||||
sshClient, err := ssh.NewClient(
|
||||
options.Host,
|
||||
22,
|
||||
options.Username,
|
||||
options.Password,
|
||||
"",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ssh client init fail: %s", err)
|
||||
}
|
||||
|
||||
drv = drivers.GetDriver(options.Driver, sshClient)
|
||||
if drv == nil {
|
||||
return nil, fmt.Errorf("not supported driver %s", options.Driver)
|
||||
}
|
||||
} else {
|
||||
drv = drivers.GetLocalDriver(options.Driver)
|
||||
}
|
||||
|
||||
sshClient, err := ssh.NewClient(
|
||||
options.Host,
|
||||
22,
|
||||
options.Username,
|
||||
options.Password,
|
||||
"",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ssh client init fail: %s", err)
|
||||
}
|
||||
|
||||
drv := drivers.GetDriver(options.Driver, sshClient)
|
||||
if drv == nil {
|
||||
return nil, fmt.Errorf("not supported driver %s", options.Driver)
|
||||
}
|
||||
|
||||
err = drv.ParsePhyDevs()
|
||||
err := drv.ParsePhyDevs()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse phyical devices error %s", err)
|
||||
}
|
||||
|
||||
193
cmd/telegraf-raid-plugin/main.go
Normal file
193
cmd/telegraf-raid-plugin/main.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/detect_storages"
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/raid/drivers"
|
||||
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
)
|
||||
|
||||
const (
|
||||
INTERVAL_SECOND = 300
|
||||
TelegrafServer = "http://localhost:8087/write"
|
||||
)
|
||||
|
||||
// Failed, Offline, Degraded, Rebuilding, Out of Sync (OSY)
|
||||
|
||||
func main() {
|
||||
// creates the in-cluster config
|
||||
config, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
// creates the clientset
|
||||
clientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
nodeName := os.Getenv("NODENAME")
|
||||
if len(nodeName) == 0 {
|
||||
log.Fatalf("Missing env nodename")
|
||||
}
|
||||
|
||||
node, err := clientset.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
var masterAddress string
|
||||
if length := len(node.Status.Conditions); length > 0 {
|
||||
if node.Status.Conditions[length-1].Type == v1.NodeReady &&
|
||||
node.Status.Conditions[length-1].Status == v1.ConditionTrue {
|
||||
for _, addr := range node.Status.Addresses {
|
||||
if addr.Type == v1.NodeInternalIP {
|
||||
masterAddress = addr.Address
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("Start Colloct Raid Info And Send To Telegraf ...")
|
||||
c := NewRaidInfoCollector(nodeName, masterAddress, INTERVAL_SECOND)
|
||||
c.Start()
|
||||
}
|
||||
|
||||
type RaidInfoCollector struct {
|
||||
waitingReportData []string
|
||||
LastCollectTime time.Time
|
||||
ReportInterval int // seconds
|
||||
Hostname string
|
||||
HostIp string
|
||||
}
|
||||
|
||||
func NewRaidInfoCollector(hostname, hostIp string, interval int) *RaidInfoCollector {
|
||||
return &RaidInfoCollector{
|
||||
waitingReportData: make([]string, 0),
|
||||
ReportInterval: interval,
|
||||
Hostname: hostname,
|
||||
HostIp: hostIp,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RaidInfoCollector) runMain() {
|
||||
timeBegin := time.Now()
|
||||
elapse := timeBegin.Sub(c.LastCollectTime)
|
||||
if elapse < time.Second*time.Duration(c.ReportInterval) {
|
||||
return
|
||||
} else {
|
||||
c.LastCollectTime = timeBegin
|
||||
}
|
||||
c.runMonitor()
|
||||
}
|
||||
|
||||
func (c *RaidInfoCollector) runMonitor() {
|
||||
reportData := c.collectReportData()
|
||||
if len(reportData) > 0 {
|
||||
c.reportRaidInfoToTelegraf(reportData)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RaidInfoCollector) collectReportData() string {
|
||||
if len(c.waitingReportData) > 60 {
|
||||
c.waitingReportData = c.waitingReportData[1:]
|
||||
}
|
||||
return c.CollectReportData()
|
||||
}
|
||||
|
||||
func (c *RaidInfoCollector) CollectReportData() string {
|
||||
raidDiskInfo := make([]*baremetal.BaremetalStorage, 0)
|
||||
// raidDrivers := []string{}
|
||||
for _, drv := range drivers.GetDrivers(drivers.NewExecutor()) {
|
||||
if err := drv.ParsePhyDevs(); err != nil {
|
||||
log.Warningf("Raid driver %s ParsePhyDevs failed: %s", drv.GetName(), err)
|
||||
continue
|
||||
}
|
||||
raidDiskInfo = append(raidDiskInfo, detect_storages.GetRaidDevices(drv)...)
|
||||
// raidDrivers = append(raidDrivers, drv.GetName())
|
||||
}
|
||||
if len(raidDiskInfo) > 0 {
|
||||
ret := c.toTelegrafReportData(raidDiskInfo)
|
||||
return ret
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *RaidInfoCollector) Start() {
|
||||
for {
|
||||
c.runMain()
|
||||
time.Sleep(time.Second * 1)
|
||||
}
|
||||
}
|
||||
|
||||
const MEASUREMENT = "host_raid"
|
||||
|
||||
func (c *RaidInfoCollector) toTelegrafReportData(raidDiskInfo []*baremetal.BaremetalStorage) string {
|
||||
tag := fmt.Sprintf("%s=%s,%s=%s", "hostname", c.Hostname, "host_ip", c.HostIp)
|
||||
ret := []string{}
|
||||
for i := 0; i < len(raidDiskInfo); i++ {
|
||||
statArr := []string{}
|
||||
raidDiskInfo[i].Status = strings.ToLower(raidDiskInfo[i].Status)
|
||||
jStat := jsonutils.Marshal(raidDiskInfo[i])
|
||||
jMap, _ := jStat.GetMap()
|
||||
for k, v := range jMap {
|
||||
statArr = append(statArr, fmt.Sprintf("%s=%s", k, v.String()))
|
||||
}
|
||||
stat := strings.Join(statArr, ",")
|
||||
diskTag := fmt.Sprintf(
|
||||
"%s,%s=%s,%s=%d,%s=%d", tag, "driver", raidDiskInfo[i].Driver,
|
||||
"adapter", raidDiskInfo[i].Adapter, "slot", raidDiskInfo[i].Slot,
|
||||
)
|
||||
line := fmt.Sprintf("%s,%s %s", MEASUREMENT, diskTag, stat)
|
||||
ret = append(ret, line)
|
||||
}
|
||||
return strings.Join(ret, "\n")
|
||||
}
|
||||
|
||||
func (c *RaidInfoCollector) reportRaidInfoToTelegraf(data string) {
|
||||
body := strings.NewReader(data)
|
||||
res, err := httputils.Request(
|
||||
httputils.GetDefaultClient(), context.Background(), "POST", TelegrafServer, nil, body, false)
|
||||
if err != nil {
|
||||
log.Errorf("Upload guest metric failed: %s", err)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 204 {
|
||||
log.Errorf("upload guest metric failed %d", res.StatusCode)
|
||||
timestamp := time.Now().UnixNano()
|
||||
for _, line := range strings.Split(data, "\n") {
|
||||
c.waitingReportData = append(c.waitingReportData,
|
||||
fmt.Sprintf("%s %d", line, timestamp))
|
||||
}
|
||||
} else {
|
||||
if len(c.waitingReportData) > 0 {
|
||||
oldDatas := strings.Join(c.waitingReportData, "\n")
|
||||
body = strings.NewReader(oldDatas)
|
||||
res, err = httputils.Request(
|
||||
httputils.GetDefaultClient(), context.Background(), "POST", TelegrafServer, nil, body, false)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
if res.StatusCode == 204 {
|
||||
c.waitingReportData = c.waitingReportData[len(c.waitingReportData):]
|
||||
} else {
|
||||
log.Errorf("upload guest metric failed code: %d", res.StatusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
4
go.mod
4
go.mod
@@ -141,12 +141,12 @@ require (
|
||||
k8s.io/cluster-bootstrap v0.17.3
|
||||
k8s.io/kubernetes v1.16.0
|
||||
yunion.io/x/executor v0.0.0-20200227030256-a18417815e74
|
||||
yunion.io/x/jsonutils v0.0.0-20200814075449-927b118adbd8
|
||||
yunion.io/x/jsonutils v0.0.0-20201014025429-7ce189228db6
|
||||
yunion.io/x/log v0.0.0-20200313080802-57a4ce5966b3
|
||||
yunion.io/x/ovsdb v0.0.0-20200526071744-27bf0940cbc7
|
||||
yunion.io/x/pkg v0.0.0-20200814072949-4f1b541857d6
|
||||
yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e
|
||||
yunion.io/x/sqlchemy v0.0.0-20200909073353-606d1fe76db5
|
||||
yunion.io/x/sqlchemy v0.0.0-20201014101037-8fe75542e6d8
|
||||
yunion.io/x/structarg v0.0.0-20200720093445-9f850fa222ce
|
||||
)
|
||||
|
||||
|
||||
8
go.sum
8
go.sum
@@ -1124,8 +1124,8 @@ vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj
|
||||
yunion.io/x/executor v0.0.0-20200227030256-a18417815e74 h1:A15C6VdVRWvmQ9pAJHrUs9yan5qKlYH7uaRxHg1kRbk=
|
||||
yunion.io/x/executor v0.0.0-20200227030256-a18417815e74/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws=
|
||||
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634=
|
||||
yunion.io/x/jsonutils v0.0.0-20200814075449-927b118adbd8 h1:KQVw5EmohJCqBDMdZ3fAcOQNNMIYgVuNCNWNwg4EM3A=
|
||||
yunion.io/x/jsonutils v0.0.0-20200814075449-927b118adbd8/go.mod h1:p0nyMqGA/apTxxyLIU/o1k4V7Vujl2O6ey30L594sYE=
|
||||
yunion.io/x/jsonutils v0.0.0-20201014025429-7ce189228db6 h1:RPpnTSCZwa9VQuWAf7S2dRjnZlYTIM0NH/j5vvUpk9g=
|
||||
yunion.io/x/jsonutils v0.0.0-20201014025429-7ce189228db6/go.mod h1:p0nyMqGA/apTxxyLIU/o1k4V7Vujl2O6ey30L594sYE=
|
||||
yunion.io/x/log v0.0.0-20190514041436-04ce53b17c6b/go.mod h1:+gauLs73omeJAPlsXcevLsJLKixV+sR/E7WSYTSx1fE=
|
||||
yunion.io/x/log v0.0.0-20190629062853-9f6483a7103d h1:59zrDL7Ft+hDukguJRmLr/Gdu/9V75x+yX99ovZwfaA=
|
||||
yunion.io/x/log v0.0.0-20190629062853-9f6483a7103d/go.mod h1:LC6f/4FozL0iaAbnFt2eDX9jlsyo3WiOUPm03d7+U4U=
|
||||
@@ -1139,7 +1139,7 @@ yunion.io/x/pkg v0.0.0-20200814072949-4f1b541857d6 h1:UarEDTBGkgcgc+nc+PZ75uo9M9
|
||||
yunion.io/x/pkg v0.0.0-20200814072949-4f1b541857d6/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
|
||||
yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e h1:v+EzIadodSwkdZ/7bremd7J8J50Cise/HCylsOJngmo=
|
||||
yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e/go.mod h1:0iFKpOs1y4lbCxeOmq3Xx/0AcQoewVPwj62eRluioEo=
|
||||
yunion.io/x/sqlchemy v0.0.0-20200909073353-606d1fe76db5 h1:Z1BoBr1LUEqQKJ5k+xTczj22OSiOvMxEN5BJrOQq7+g=
|
||||
yunion.io/x/sqlchemy v0.0.0-20200909073353-606d1fe76db5/go.mod h1:FTdwPdGhMgh4E+UFXc9klI1Ok34fMuybTT+jLhOaIjI=
|
||||
yunion.io/x/sqlchemy v0.0.0-20201014101037-8fe75542e6d8 h1:AkPrqO37Yi3aICys9qDjTlSdVVGh2pWQ8ET+dSJmhoI=
|
||||
yunion.io/x/sqlchemy v0.0.0-20201014101037-8fe75542e6d8/go.mod h1:FTdwPdGhMgh4E+UFXc9klI1Ok34fMuybTT+jLhOaIjI=
|
||||
yunion.io/x/structarg v0.0.0-20200720093445-9f850fa222ce h1:kU8xE7O5uZ1GSJVMZHoJ+jrNL7csUQHYGyAPW9QfNpE=
|
||||
yunion.io/x/structarg v0.0.0-20200720093445-9f850fa222ce/go.mod h1:EP6NSv2C0zzqBDTKumv8hPWLb3XvgMZDHQRfyuOrQng=
|
||||
|
||||
@@ -447,6 +447,7 @@ type ServerCreateInput struct {
|
||||
|
||||
// 弹性公网IP带宽
|
||||
// 指定此参数后会创建新的弹性公网IP并绑定到新建的虚拟机
|
||||
// 此参数优先级低于public_ip
|
||||
// 私有云不支持此参数
|
||||
EipBw int `json:"eip_bw,omitzero"`
|
||||
// 弹性公网IP计费类型
|
||||
@@ -459,6 +460,25 @@ type ServerCreateInput struct {
|
||||
// required: false
|
||||
Eip string `json:"eip,omitempty"`
|
||||
|
||||
// 公网IP带宽(单位MB)
|
||||
// 若指定此参数则忽略eip相关参数
|
||||
// 私有云不支持此参数
|
||||
//
|
||||
//
|
||||
// |平台 |支持范围 |
|
||||
// |---- |------- |
|
||||
// |腾讯云 |按量计费1-100, 包年包月1-200 |
|
||||
PublicIpBw int `json:"public_ip_bw,omitzero"`
|
||||
// 公网IP计费类型
|
||||
// 默认按流量计费
|
||||
//
|
||||
//
|
||||
// |类别 |说明 |
|
||||
// |---- |------- |
|
||||
// |traffic |按流量计费|
|
||||
// |bandwidth |按带宽计费|
|
||||
PublicIpChargeType string `json:"public_ip_charge_type,omitempty"`
|
||||
|
||||
// 使用主机快照创建虚拟机, 主机快照不会重置密码及秘钥信息
|
||||
// 使用主机快照创建的虚拟机将沿用之前的密码秘钥及安全组信息
|
||||
// required: false
|
||||
|
||||
@@ -367,7 +367,8 @@ type CAGuestNet struct {
|
||||
|
||||
type CAIPNet struct {
|
||||
// description: IP
|
||||
IP string `json:"ip"`
|
||||
IP string `json:"ip"`
|
||||
VlanID int32 `json:"vlan_id"`
|
||||
// description: 合适的已有网络
|
||||
SuitableNetwork string `json:"suitable_network,allowempty"`
|
||||
}
|
||||
@@ -377,6 +378,7 @@ type CASimpleNetConf struct {
|
||||
GuestIpEnd string `json:"guest_ip_end"`
|
||||
GuestIpMask int8 `json:"guest_ip_mask"`
|
||||
GuestGateway string `json:"guest_gateway"`
|
||||
VlanID int32 `json:"vlan_id"`
|
||||
}
|
||||
|
||||
type CANetConf struct {
|
||||
|
||||
@@ -71,6 +71,8 @@ const (
|
||||
CLOUD_ACCESS_ENV_AZURE_CHINA = CLOUD_PROVIDER_AZURE
|
||||
CLOUD_ACCESS_ENV_HUAWEI_GLOBAL = CLOUD_PROVIDER_HUAWEI + "-int"
|
||||
CLOUD_ACCESS_ENV_HUAWEI_CHINA = CLOUD_PROVIDER_HUAWEI
|
||||
CLOUD_ACCESS_ENV_ALIYUN_GLOBAL = CLOUD_PROVIDER_ALIYUN
|
||||
CLOUD_ACCESS_ENV_ALIYUN_FINANCE = CLOUD_PROVIDER_ALIYUN + "-fin"
|
||||
CLOUD_ACCESS_ENV_CTYUN_CHINA = CLOUD_PROVIDER_CTYUN
|
||||
)
|
||||
|
||||
|
||||
@@ -98,6 +98,9 @@ type ServerListInput struct {
|
||||
SrcMacCheck *bool `json:"src_mac_check"`
|
||||
|
||||
InstanceType []string `json:"instance_type"`
|
||||
|
||||
// 是否调度到宿主机上
|
||||
WithHost *bool `json:"with_host"`
|
||||
}
|
||||
|
||||
func (input *ServerListInput) AfterUnmarshal() {
|
||||
@@ -409,3 +412,8 @@ type GuestAddSecgroupInput struct {
|
||||
// | 其他 | 5 |
|
||||
SecgroupIds []string `json:"secgroup_ids"`
|
||||
}
|
||||
|
||||
type ServerRemoteUpdateInput struct {
|
||||
// 是否覆盖替换所有标签
|
||||
ReplaceTags *bool `json:"replace_tags" help:"replace all remote tags"`
|
||||
}
|
||||
|
||||
@@ -58,6 +58,12 @@ type CachedimageListInput struct {
|
||||
// 镜像类型,可能值为: system(公有云公共镜像), customized(自定义镜像)
|
||||
// example: system
|
||||
ImageType []string `json:"image_type"`
|
||||
|
||||
// filter by host schedtag
|
||||
HostSchedtagId string `json:"host_schedtag_id"`
|
||||
|
||||
// valid cachedimage
|
||||
Valid *bool `json:"valid"`
|
||||
}
|
||||
|
||||
type ExternalProjectListInput struct {
|
||||
|
||||
@@ -187,6 +187,12 @@ type NetworkCreateInput struct {
|
||||
|
||||
// 是否加入自动分配地址池
|
||||
IsAutoAlloc *bool `json:"is_auto_alloc"`
|
||||
|
||||
// VlanId
|
||||
VlanId *int `json:"vlan_id"`
|
||||
|
||||
// deprecated
|
||||
Vlan *int `json:"vlan" yunion-deprecated-by:"vlan_id"`
|
||||
}
|
||||
|
||||
type NetworkDetails struct {
|
||||
|
||||
@@ -21,10 +21,10 @@ type SnapshotPolicyDetails struct {
|
||||
|
||||
SSnapshotPolicy
|
||||
|
||||
RetentionDays int `json:"retention_days"`
|
||||
RepeatWeekdays []int `json:"repeat_weekdays"`
|
||||
TimePoints []int `json:"time_points"`
|
||||
IsActivated *bool `json:"is_activated,omitempty"`
|
||||
RetentionDays int `json:"retention_days"`
|
||||
RepeatWeekdaysDisplay []int `json:"repeat_weekdays_display"`
|
||||
TimePointsDisplay []int `json:"time_points_display"`
|
||||
IsActivated *bool `json:"is_activated,omitempty"`
|
||||
|
||||
BindingDiskCount int `json:"binding_disk_count"`
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ const (
|
||||
STORAGE_GOOGLE_LOCAL_SSD = "local-ssd" //本地SSD暂存盘 (最多8个)
|
||||
STORAGE_GOOGLE_PD_STANDARD = "pd-standard" //标准永久性磁盘
|
||||
STORAGE_GOOGLE_PD_SSD = "pd-ssd" //SSD永久性磁盘
|
||||
STORAGE_GOOGLE_PD_BALANCED = "pd-balanced" //平衡永久性磁盘
|
||||
|
||||
// ctyun storage type
|
||||
STORAGE_CTYUN_SSD = "SSD" // 超高IO云硬盘
|
||||
@@ -186,4 +187,7 @@ type StorageListInput struct {
|
||||
|
||||
// filter by host schedtag
|
||||
HostSchedtagId string `json:"host_schedtag_id"`
|
||||
|
||||
// filter by cachedimage
|
||||
ImageId string `json:"image_id"`
|
||||
}
|
||||
|
||||
@@ -916,7 +916,8 @@ type SGuestTemplate struct {
|
||||
// 计费方式
|
||||
BillingType string `json:"billing_type"`
|
||||
// 其他配置信息
|
||||
Content interface{} `json:"content"`
|
||||
Content interface{} `json:"content"`
|
||||
LastCheckTime time.Time `json:"last_check_time"`
|
||||
}
|
||||
|
||||
// SGuestTemplateResourceBase is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SGuestTemplateResourceBase.
|
||||
@@ -925,20 +926,6 @@ type SGuestTemplateResourceBase struct {
|
||||
GuestTemplateId string `json:"guest_template_id"`
|
||||
}
|
||||
|
||||
// SGuestcdrom is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SGuestcdrom.
|
||||
type SGuestcdrom struct {
|
||||
Id string `json:"id"`
|
||||
// = Column(VARCHAR(36, charset='ascii'), primary_key=True)
|
||||
ImageId string `json:"image_id"`
|
||||
// Column(VARCHAR(36, charset='ascii'), nullable=True)
|
||||
Name string `json:"name"`
|
||||
// Column(VARCHAR(64, charset='ascii'), nullable=True)
|
||||
Path string `json:"path"`
|
||||
// Column(VARCHAR(256, charset='ascii'), nullable=True)
|
||||
Size int64 `json:"size"`
|
||||
UpdateVersion int `json:"update_version"`
|
||||
}
|
||||
|
||||
// SGuestdisk is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SGuestdisk.
|
||||
type SGuestdisk struct {
|
||||
SGuestJointsBase
|
||||
@@ -1058,10 +1045,11 @@ type SHost struct {
|
||||
OvnVersion string `json:"ovn_version"`
|
||||
IsBaremetal bool `json:"is_baremetal"`
|
||||
// 是否处于维护状态
|
||||
IsMaintenance bool `json:"is_maintenance"`
|
||||
EnableHealthCheck bool `json:"enable_health_check"`
|
||||
ResourceType string `json:"resource_type"`
|
||||
RealExternalId string `json:"real_external_id"`
|
||||
IsMaintenance bool `json:"is_maintenance"`
|
||||
LastPingAt time.Time `json:"last_ping_at"`
|
||||
EnableHealthCheck bool `json:"enable_health_check"`
|
||||
ResourceType string `json:"resource_type"`
|
||||
RealExternalId string `json:"real_external_id"`
|
||||
// 是否为导入的宿主机
|
||||
IsImport bool `json:"is_import"`
|
||||
// 是否允许PXE启动
|
||||
@@ -1317,9 +1305,12 @@ type SLoadbalancerAgentDeployment struct {
|
||||
|
||||
// SLoadbalancerAgentParams is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SLoadbalancerAgentParams.
|
||||
type SLoadbalancerAgentParams struct {
|
||||
KeepalivedConfTmpl string `json:"keepalived_conf_tmpl"`
|
||||
HaproxyConfTmpl string `json:"haproxy_conf_tmpl"`
|
||||
TelegrafConfTmpl string `json:"telegraf_conf_tmpl"`
|
||||
KeepalivedConfTmpl string `json:"keepalived_conf_tmpl"`
|
||||
HaproxyConfTmpl string `json:"haproxy_conf_tmpl"`
|
||||
TelegrafConfTmpl string `json:"telegraf_conf_tmpl"`
|
||||
Vrrp SLoadbalancerAgentParamsVrrp `json:"vrrp"`
|
||||
Haproxy SLoadbalancerAgentParamsHaproxy `json:"haproxy"`
|
||||
Telegraf SLoadbalancerAgentParamsTelegraf `json:"telegraf"`
|
||||
}
|
||||
|
||||
// SLoadbalancerAgentParamsHaproxy is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SLoadbalancerAgentParamsHaproxy.
|
||||
@@ -1561,16 +1552,6 @@ type SManagedResourceBase struct {
|
||||
ManagerId string `json:"manager_id"`
|
||||
}
|
||||
|
||||
// SNatDEntry is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SNatDEntry.
|
||||
type SNatDEntry struct {
|
||||
SNatEntry
|
||||
ExternalIP string `json:"external_ip"`
|
||||
ExternalPort int `json:"external_port"`
|
||||
InternalIP string `json:"internal_ip"`
|
||||
InternalPort int `json:"internal_port"`
|
||||
IpProtocol string `json:"ip_protocol"`
|
||||
}
|
||||
|
||||
// SNatEntry is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SNatEntry.
|
||||
type SNatEntry struct {
|
||||
apis.SStatusInfrasResourceBase
|
||||
@@ -1601,25 +1582,6 @@ type SNatgatewayResourceBase struct {
|
||||
NatgatewayId string `json:"natgateway_id"`
|
||||
}
|
||||
|
||||
// SNetInterface is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SNetInterface.
|
||||
type SNetInterface struct {
|
||||
Mac string `json:"mac"`
|
||||
// Column(VARCHAR(36, charset='ascii'), primary_key=True)
|
||||
BaremetalId string `json:"baremetal_id"`
|
||||
// Column(VARCHAR(36, charset='ascii'), nullable=True)
|
||||
WireId string `json:"wire_id"`
|
||||
// Column(VARCHAR(36, charset='ascii'), nullable=True)
|
||||
Rate int `json:"rate"`
|
||||
// Column(Integer, nullable=True) # Mbps
|
||||
NicType string `json:"nic_type"`
|
||||
// Column(VARCHAR(36, charset='ascii'), nullable=True)
|
||||
Index byte `json:"index"`
|
||||
// Column(TINYINT, nullable=True)
|
||||
LinkUp bool `json:"link_up"`
|
||||
// Column(Boolean, nullable=True)
|
||||
Mtu int16 `json:"mtu"`
|
||||
}
|
||||
|
||||
// SNetwork is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SNetwork.
|
||||
type SNetwork struct {
|
||||
apis.SSharableVirtualResourceBase
|
||||
@@ -1825,6 +1787,8 @@ type SScalingAlarm struct {
|
||||
Value float64 `json:"value"`
|
||||
// Real-time cumulate number
|
||||
RealCumulate int `json:"real_cumulate"`
|
||||
// Last trigger time
|
||||
LastTriggerTime time.Time `json:"last_trigger_time"`
|
||||
}
|
||||
|
||||
// SScalingGroup is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SScalingGroup.
|
||||
@@ -1855,6 +1819,10 @@ type SScalingGroup struct {
|
||||
HealthCheckGov int `json:"health_check_gov"`
|
||||
LoadbalancerBackendPort int `json:"loadbalancer_backend_port"`
|
||||
LoadbalancerBackendWeight int `json:"loadbalancer_backend_weight"`
|
||||
// Time to allow scale
|
||||
AllowScaleTime time.Time `json:"allow_scale_time"`
|
||||
// NextCheckTime descripe the next time to check instance's health
|
||||
NextCheckTime time.Time `json:"next_check_time"`
|
||||
}
|
||||
|
||||
// SScalingGroupGuest is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SScalingGroupGuest.
|
||||
@@ -2196,7 +2164,13 @@ type STimer struct {
|
||||
WeekDays byte `json:"week_days"`
|
||||
// 0-31 0 is unlimited
|
||||
MonthDays uint32 `json:"month_days"`
|
||||
IsExpired bool `json:"is_expired"`
|
||||
// StartTime represent the start time of this timer
|
||||
StartTime time.Time `json:"start_time"`
|
||||
// EndTime represent deadline of this timer
|
||||
EndTime time.Time `json:"end_time"`
|
||||
// NextTime represent the time timer should bell
|
||||
NextTime time.Time `json:"next_time"`
|
||||
IsExpired bool `json:"is_expired"`
|
||||
}
|
||||
|
||||
// SVCenter is an autogenerated struct via yunion.io/x/onecloud/pkg/compute/models.SVCenter.
|
||||
|
||||
@@ -87,6 +87,8 @@ type RoleAssignmentsInput struct {
|
||||
Projects []string `json:"projects"`
|
||||
Domains []string `json:"domains"`
|
||||
|
||||
ProjectDomains []string `json:"project_domains"`
|
||||
|
||||
IncludeNames *bool `json:"include_names"`
|
||||
Effective *bool `json:"effective"`
|
||||
IncludeSubtree *bool `json:"include_subtree"`
|
||||
|
||||
@@ -241,6 +241,12 @@ type PerformChangeProjectOwnerInput struct {
|
||||
ProjectizedResourceInput
|
||||
}
|
||||
|
||||
type PerformFreezeInput struct {
|
||||
}
|
||||
|
||||
type PerformUnfreezeInput struct {
|
||||
}
|
||||
|
||||
type PerformChangeDomainOwnerInput struct {
|
||||
DomainizedResourceInput
|
||||
}
|
||||
@@ -326,6 +332,9 @@ type GetMetadataInput struct {
|
||||
// | 外部标签 | key以ext:为前缀,为从其他平台同步过来的标签 |
|
||||
//
|
||||
Field []string `json:"field"`
|
||||
|
||||
// 按标签前缀过滤
|
||||
Prefix string `json:"prefix"`
|
||||
}
|
||||
|
||||
// 获取资源标签(元数据)输出
|
||||
|
||||
@@ -195,8 +195,19 @@ type MetadataResourceListInput struct {
|
||||
// 通过标签过滤
|
||||
OrderByTag string `json:"order_by_tag"`
|
||||
|
||||
// 返回资源的标签不包含特定的用户标签
|
||||
WithoutUserMeta bool `json:"without_user_meta"`
|
||||
// deprecated
|
||||
// 返回资源的标签不包含用户标签
|
||||
WithoutUserMeta *bool `json:"without_user_meta"`
|
||||
|
||||
// 返回包含用户标签的资源
|
||||
WithUserMeta *bool `json:"with_user_meta"`
|
||||
|
||||
// 返回包含外部标签的资源
|
||||
WithCloudMeta *bool `json:"with_cloud_meta"`
|
||||
|
||||
// 返回包含任意标签的资源
|
||||
WithAnyMeta *bool `json:"with_any_meta"`
|
||||
|
||||
// 返回列表数据中包含资源的标签数据(Metadata)
|
||||
WithMeta *bool `json:"with_meta"`
|
||||
}
|
||||
|
||||
@@ -39,6 +39,6 @@ type AlertRecordRule struct {
|
||||
// 比较运算符, 比如: >, <, >=, <=
|
||||
Comparator string `json:"comparator"`
|
||||
// 报警阀值
|
||||
Threshold float64 `json:"threshold"`
|
||||
Period string `json:"period"`
|
||||
Threshold string `json:"threshold"`
|
||||
Period string `json:"period"`
|
||||
}
|
||||
|
||||
77
pkg/apis/monitor/alertresource.go
Normal file
77
pkg/apis/monitor/alertresource.go
Normal file
@@ -0,0 +1,77 @@
|
||||
// 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 monitor
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
)
|
||||
|
||||
type AlertResourceType string
|
||||
|
||||
const (
|
||||
// AlertResourceTypeNode means onecloud system infrastructure controller or host node
|
||||
AlertResourceTypeNode AlertResourceType = "node"
|
||||
// AlertResourceTypeCloudaccount means cloudaccount resource
|
||||
AlertResourceTypeCloudaccount AlertResourceType = "cloudaccount"
|
||||
// AlertResourceTypeVM means virtual machine guest resource
|
||||
// AlertResourceTypeVM AlertResourceType = "vm"
|
||||
)
|
||||
|
||||
type AlertResourceCreateInput struct {
|
||||
apis.StandaloneResourceCreateInput
|
||||
|
||||
Type AlertResourceType `json:"type"`
|
||||
}
|
||||
|
||||
type AlertResourceListInput struct {
|
||||
apis.StandaloneResourceListInput
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type AlertResourceAttachInput struct {
|
||||
apis.Meta
|
||||
|
||||
AlertResourceId string `json:"alert_resource_id"`
|
||||
AlertId string `json:"alert_id"`
|
||||
AlertRecordId string `json:"alert_record_id"`
|
||||
Data EvalMatch `json:"data"`
|
||||
}
|
||||
|
||||
type AlertResourceDetails struct {
|
||||
apis.StandaloneResourceDetails
|
||||
Count int `json:"count"`
|
||||
Tags map[string]string `json:"tags"`
|
||||
}
|
||||
|
||||
type AlertResourceAlertListInput struct {
|
||||
apis.JointResourceBaseListInput
|
||||
AlertResourceId string `json:"alert_resource_id"`
|
||||
AlertId string `json:"alert_id"`
|
||||
}
|
||||
|
||||
type AlertResourceJointBaseDetails struct {
|
||||
apis.JointResourceBaseDetails
|
||||
|
||||
AlertResource string `json:"alert_resource"`
|
||||
Type AlertResourceType `json:"type"`
|
||||
}
|
||||
|
||||
type AlertResourceAlertDetails struct {
|
||||
AlertResourceJointBaseDetails
|
||||
Alert string `json:"alert"`
|
||||
AlertType string `json:"alert_type"`
|
||||
Level string `json:"level"`
|
||||
CommonAlertMetricDetails []*CommonAlertMetricDetails `json:"common_alert_metric_details"`
|
||||
}
|
||||
@@ -86,6 +86,8 @@ type CommonAlertUpdateInput struct {
|
||||
Channel []string `json:"channel"`
|
||||
// 通知接受者
|
||||
Recipients []string `json:"recipients"`
|
||||
// systemalert policy may need update through operator
|
||||
ForceUpdate bool `json:"force_update"`
|
||||
}
|
||||
|
||||
type CommonAlertDetails struct {
|
||||
@@ -105,13 +107,14 @@ type CommonAlertMetricDetails struct {
|
||||
Comparator string `json:"comparator"`
|
||||
Threshold float64 `json:"threshold"`
|
||||
// metric points'value的运算方式
|
||||
Reduce string `json:"reduce"`
|
||||
DB string `json:"db"`
|
||||
Measurement string `json:"measurement"`
|
||||
MeasurementDisplayName string `json:"measurement_display_name"`
|
||||
ResType string `json:"res_type"`
|
||||
Field string `json:"field"`
|
||||
Groupby string `json:"groupby"`
|
||||
Reduce string `json:"reduce"`
|
||||
DB string `json:"db"`
|
||||
Measurement string `json:"measurement"`
|
||||
MeasurementDisplayName string `json:"measurement_display_name"`
|
||||
ResType string `json:"res_type"`
|
||||
Field string `json:"field"`
|
||||
Groupby string `json:"groupby"`
|
||||
Filters []MetricQueryTag `json:"filters"`
|
||||
FieldDescription MetricFieldDetail
|
||||
FieldOpt string `json:"field_opt"`
|
||||
}
|
||||
|
||||
@@ -41,8 +41,11 @@ type MetricFunc struct {
|
||||
}
|
||||
|
||||
type MetricInputQuery struct {
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Scope string `json:"scope"`
|
||||
//default group by
|
||||
Unit bool `json:"unit"`
|
||||
Interval string `json:"interval"`
|
||||
DomainId string `json:"domain_id"`
|
||||
ProjectId string `json:"project_id"`
|
||||
|
||||
@@ -70,10 +70,11 @@ type SAlertRecord struct {
|
||||
apis.SEnabledResourceBase
|
||||
apis.SStatusStandaloneResourceBase
|
||||
apis.SScopedResourceBase
|
||||
AlertId string `json:"alert_id"`
|
||||
Level string `json:"level"`
|
||||
State string `json:"state"`
|
||||
EvalData interface{} `json:"eval_data"`
|
||||
AlertId string `json:"alert_id"`
|
||||
Level string `json:"level"`
|
||||
State string `json:"state"`
|
||||
EvalData interface{} `json:"eval_data"`
|
||||
AlertRule interface{} `json:"alert_rule"`
|
||||
}
|
||||
|
||||
// SAlertnotification is an autogenerated struct via yunion.io/x/onecloud/pkg/monitor/models.SAlertnotification.
|
||||
|
||||
@@ -29,6 +29,7 @@ const (
|
||||
FEISHU_ROBOT = "feishu-robot"
|
||||
DINGTALK_ROBOT = "dingtalk-robot"
|
||||
WORKWX_ROBOT = "workwx-robot"
|
||||
WEBHOOK = "webhook"
|
||||
|
||||
ROBOT = "robot"
|
||||
|
||||
@@ -60,4 +61,7 @@ const (
|
||||
TEMPLATE_TYPE_TITLE = "title"
|
||||
TEMPLATE_TYPE_CONTENT = "content"
|
||||
TEMPLATE_TYPE_REMOTE = "remote"
|
||||
|
||||
CTYPE_ROBOT_YES = "yes"
|
||||
CTYPE_ROBOT_ONLY = "only"
|
||||
)
|
||||
|
||||
@@ -157,7 +157,7 @@ type ProjectizedResourceInfo struct {
|
||||
|
||||
// 资源归属项目的ID(向后兼容别名)
|
||||
// Deprecated
|
||||
TenantId string `json:"project_id" yunion-deprecated-by:"tenant"`
|
||||
TenantId string `json:"project_id" yunion-deprecated-by:"tenant_id"`
|
||||
|
||||
// 资源归属项目的名称(向后兼容别名)
|
||||
// Deprecated
|
||||
|
||||
@@ -228,4 +228,6 @@ type SVirtualResourceBase struct {
|
||||
PendingDeletedAt time.Time `json:"pending_deleted_at"`
|
||||
// 资源是否处于回收站中
|
||||
PendingDeleted bool `json:"pending_deleted"`
|
||||
// 资源是否被冻结
|
||||
Freezed bool `json:"freezed"`
|
||||
}
|
||||
|
||||
@@ -28,11 +28,11 @@ func doStatsHandler(stats *jsonutils.JSONArray, method, path string, hi, total *
|
||||
s.Add(jsonutils.NewString(path), "path")
|
||||
s.Add(jsonutils.NewString(hi.GetName(nil)), "name")
|
||||
s.Add(jsonutils.NewInt(hi.counter2XX.hit), "hit.2XX")
|
||||
s.Add(jsonutils.NewFloat(hi.counter2XX.duration), "duration.2XX")
|
||||
s.Add(jsonutils.NewFloat64(hi.counter2XX.duration), "duration.2XX")
|
||||
s.Add(jsonutils.NewInt(hi.counter4XX.hit), "hit.4XX")
|
||||
s.Add(jsonutils.NewFloat(hi.counter4XX.duration), "duration.4XX")
|
||||
s.Add(jsonutils.NewFloat64(hi.counter4XX.duration), "duration.4XX")
|
||||
s.Add(jsonutils.NewInt(hi.counter5XX.hit), "hit.5XX")
|
||||
s.Add(jsonutils.NewFloat(hi.counter5XX.duration), "duration.5XX")
|
||||
s.Add(jsonutils.NewFloat64(hi.counter5XX.duration), "duration.5XX")
|
||||
total.counter2XX.hit += hi.counter2XX.hit
|
||||
total.counter2XX.duration += hi.counter2XX.duration
|
||||
total.counter4XX.hit += hi.counter4XX.hit
|
||||
@@ -56,10 +56,10 @@ func StatisticHandler(ctx context.Context, w http.ResponseWriter, r *http.Reques
|
||||
result := jsonutils.NewDict()
|
||||
result.Add(stats, "paths")
|
||||
result.Add(jsonutils.NewInt(total.counter2XX.hit), "hit.2XX")
|
||||
result.Add(jsonutils.NewFloat(total.counter2XX.duration), "duration.2XX")
|
||||
result.Add(jsonutils.NewFloat64(total.counter2XX.duration), "duration.2XX")
|
||||
result.Add(jsonutils.NewInt(total.counter4XX.hit), "hit.4XX")
|
||||
result.Add(jsonutils.NewFloat(total.counter4XX.duration), "duration.4XX")
|
||||
result.Add(jsonutils.NewFloat64(total.counter4XX.duration), "duration.4XX")
|
||||
result.Add(jsonutils.NewInt(total.counter5XX.hit), "hit.5XX")
|
||||
result.Add(jsonutils.NewFloat(total.counter5XX.duration), "duration.5XX")
|
||||
result.Add(jsonutils.NewFloat64(total.counter5XX.duration), "duration.5XX")
|
||||
fmt.Fprintf(w, result.String())
|
||||
}
|
||||
|
||||
@@ -269,6 +269,9 @@ func (req *dhcpRequest) findNetworkConf(session *mcclient.ClientSession, filterU
|
||||
params.Add(jsonutils.NewString(
|
||||
fmt.Sprintf("guest_dhcp.endswith(',%s')", req.RelayAddr)),
|
||||
"filter.3")
|
||||
params.Add(jsonutils.NewString(
|
||||
fmt.Sprintf("guest_dhcp.equals(%s)", req.RelayAddr)),
|
||||
"filter.4")
|
||||
params.Add(jsonutils.JSONTrue, "filter_any")
|
||||
}
|
||||
params.Add(jsonutils.JSONTrue, "is_classic")
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/raid/drivers"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
"yunion.io/x/onecloud/pkg/util/sysutils"
|
||||
)
|
||||
|
||||
@@ -48,7 +47,7 @@ func GetRaidLogicVolumes(drv raid.IRaidDriver) ([]*raid.RaidLogicalVolume, error
|
||||
return lvs, nil
|
||||
}
|
||||
|
||||
func DetectStorageInfo(term *ssh.Client, wait bool) ([]*baremetal.BaremetalStorage, []*baremetal.BaremetalStorage, []*baremetal.BaremetalStorage, error) {
|
||||
func DetectStorageInfo(term raid.IExecTerm, wait bool) ([]*baremetal.BaremetalStorage, []*baremetal.BaremetalStorage, []*baremetal.BaremetalStorage, error) {
|
||||
raidDiskInfo := make([]*baremetal.BaremetalStorage, 0)
|
||||
lvDiskInfo := make([]*raid.RaidLogicalVolume, 0)
|
||||
|
||||
|
||||
@@ -29,10 +29,9 @@ import (
|
||||
_ "yunion.io/x/onecloud/pkg/baremetal/utils/raid/mvcli"
|
||||
_ "yunion.io/x/onecloud/pkg/baremetal/utils/raid/sas2iru"
|
||||
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
)
|
||||
|
||||
func GetDriver(name string, term *ssh.Client) raid.IRaidDriver {
|
||||
func GetDriver(name string, term raid.IExecTerm) raid.IRaidDriver {
|
||||
factory := raid.RaidDrivers[name]
|
||||
if factory == nil {
|
||||
return nil
|
||||
@@ -40,7 +39,15 @@ func GetDriver(name string, term *ssh.Client) raid.IRaidDriver {
|
||||
return factory(term)
|
||||
}
|
||||
|
||||
func GetDriverWithInit(name string, term *ssh.Client) (raid.IRaidDriver, error) {
|
||||
func GetLocalDriver(name string) raid.IRaidDriver {
|
||||
factory := raid.RaidDrivers[name]
|
||||
if factory == nil {
|
||||
return nil
|
||||
}
|
||||
return factory(NewExecutor())
|
||||
}
|
||||
|
||||
func GetDriverWithInit(name string, term raid.IExecTerm) (raid.IRaidDriver, error) {
|
||||
drv := GetDriver(name, term)
|
||||
if drv == nil {
|
||||
return nil, errors.Errorf("Not found raid driver %q", name)
|
||||
@@ -48,7 +55,7 @@ func GetDriverWithInit(name string, term *ssh.Client) (raid.IRaidDriver, error)
|
||||
return drv, drv.ParsePhyDevs()
|
||||
}
|
||||
|
||||
func GetDriverByKernelModule(module string, term *ssh.Client) (raid.IRaidDriver, error) {
|
||||
func GetDriverByKernelModule(module string, term raid.IExecTerm) (raid.IRaidDriver, error) {
|
||||
name := ""
|
||||
switch module {
|
||||
case raid.MODULE_MEGARAID:
|
||||
@@ -64,7 +71,7 @@ func GetDriverByKernelModule(module string, term *ssh.Client) (raid.IRaidDriver,
|
||||
return GetDriverWithInit(name, term)
|
||||
}
|
||||
|
||||
func GetDrivers(term *ssh.Client) []raid.IRaidDriver {
|
||||
func GetDrivers(term raid.IExecTerm) []raid.IRaidDriver {
|
||||
ret := []raid.IRaidDriver{}
|
||||
for _, factory := range raid.RaidDrivers {
|
||||
ret = append(ret, factory(term))
|
||||
|
||||
56
pkg/baremetal/utils/raid/drivers/executor.go
Normal file
56
pkg/baremetal/utils/raid/drivers/executor.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package drivers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
)
|
||||
|
||||
type Executor struct{}
|
||||
|
||||
func (e *Executor) Run(cmds ...string) ([]string, error) {
|
||||
return e.run(true, cmds, nil)
|
||||
}
|
||||
|
||||
func (e *Executor) RunWithInput(input io.Reader, cmds ...string) ([]string, error) {
|
||||
return e.run(true, cmds, input)
|
||||
}
|
||||
|
||||
func (e *Executor) run(parseOutput bool, cmds []string, input io.Reader) ([]string, error) {
|
||||
ret := []string{}
|
||||
for _, cmd := range cmds {
|
||||
log.Debugf("Run command: %s", cmd)
|
||||
proc := exec.Command("sh", "-c", cmd)
|
||||
var stdOut bytes.Buffer
|
||||
var stdErr bytes.Buffer
|
||||
proc.Stdout = &stdOut
|
||||
proc.Stderr = &stdErr
|
||||
proc.Stdin = input
|
||||
if err := proc.Run(); err != nil {
|
||||
var outputErr error
|
||||
errMsg := stdErr.String()
|
||||
if len(stdOut.String()) != 0 {
|
||||
errMsg = fmt.Sprintf("%s %s", errMsg, stdOut.String())
|
||||
}
|
||||
outputErr = errors.Error(errMsg)
|
||||
err = errors.Wrapf(outputErr, "%q error: %v, cmd error", cmd, err)
|
||||
return nil, err
|
||||
}
|
||||
if parseOutput {
|
||||
ret = append(ret, ssh.ParseOutput(stdOut.Bytes())...)
|
||||
} else {
|
||||
ret = append(ret, stdOut.String())
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func NewExecutor() *Executor {
|
||||
return new(Executor)
|
||||
}
|
||||
@@ -31,7 +31,6 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/raid"
|
||||
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
||||
"yunion.io/x/onecloud/pkg/util/regutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
)
|
||||
|
||||
type HPSARaidPhyDev struct {
|
||||
@@ -347,11 +346,11 @@ func (adapter *HPSARaidAdaptor) RemoveLogicVolumes() error {
|
||||
}
|
||||
|
||||
type HPSARaid struct {
|
||||
term *ssh.Client
|
||||
term raid.IExecTerm
|
||||
adapters []*HPSARaidAdaptor
|
||||
}
|
||||
|
||||
func NewHPSARaid(term *ssh.Client) raid.IRaidDriver {
|
||||
func NewHPSARaid(term raid.IExecTerm) raid.IRaidDriver {
|
||||
return &HPSARaid{
|
||||
term: term,
|
||||
adapters: make([]*HPSARaidAdaptor, 0),
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package raid
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
||||
)
|
||||
@@ -41,3 +43,8 @@ type IRaidAdapter interface {
|
||||
BuildRaid10(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig) error
|
||||
BuildNoneRaid(devs []*baremetal.BaremetalStorage) error
|
||||
}
|
||||
|
||||
type IExecTerm interface {
|
||||
Run(cmds ...string) ([]string, error)
|
||||
RunWithInput(input io.Reader, cmds ...string) ([]string, error)
|
||||
}
|
||||
|
||||
@@ -27,10 +27,10 @@ import (
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/raid"
|
||||
raiddrivers "yunion.io/x/onecloud/pkg/baremetal/utils/raid"
|
||||
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
||||
"yunion.io/x/onecloud/pkg/util/regutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -320,7 +320,7 @@ func (adapter *MegaRaidAdaptor) GetIndex() int {
|
||||
return adapter.index
|
||||
}
|
||||
|
||||
func (adapter *MegaRaidAdaptor) getTerm() *ssh.Client {
|
||||
func (adapter *MegaRaidAdaptor) getTerm() raid.IExecTerm {
|
||||
return adapter.raid.term
|
||||
}
|
||||
|
||||
@@ -376,7 +376,7 @@ func (adapter *MegaRaidAdaptor) parseLogicVolumes(lines []string) ([]*raiddriver
|
||||
return lvs, nil
|
||||
}
|
||||
|
||||
func getLogicVolumeDeviceById(hostNum, scsiId int, term *ssh.Client) (string, error) {
|
||||
func getLogicVolumeDeviceById(hostNum, scsiId int, term raid.IExecTerm) (string, error) {
|
||||
items, err := raiddrivers.SGMap(term)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -892,13 +892,13 @@ func (adapter *MegaRaidAdaptor) clearJBODDisks() {
|
||||
}
|
||||
|
||||
type MegaRaid struct {
|
||||
term *ssh.Client
|
||||
term raid.IExecTerm
|
||||
adapters []*MegaRaidAdaptor
|
||||
PhyDevsCnt int
|
||||
Capacity int64
|
||||
}
|
||||
|
||||
func NewMegaRaid(term *ssh.Client) raiddrivers.IRaidDriver {
|
||||
func NewMegaRaid(term raid.IExecTerm) raiddrivers.IRaidDriver {
|
||||
return &MegaRaid{
|
||||
term: term,
|
||||
adapters: make([]*MegaRaidAdaptor, 0),
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/raid"
|
||||
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
)
|
||||
|
||||
type MarvelRaidPhyDev struct {
|
||||
@@ -243,11 +242,11 @@ func (adapter *MarvelRaidAdaptor) BuildNoneRaid(devs []*baremetal.BaremetalStora
|
||||
}
|
||||
|
||||
type MarvelRaid struct {
|
||||
term *ssh.Client
|
||||
term raid.IExecTerm
|
||||
adapters []*MarvelRaidAdaptor
|
||||
}
|
||||
|
||||
func NewMarvelRaid(term *ssh.Client) raid.IRaidDriver {
|
||||
func NewMarvelRaid(term raid.IExecTerm) raid.IRaidDriver {
|
||||
return &MarvelRaid{
|
||||
term: term,
|
||||
adapters: make([]*MarvelRaidAdaptor, 0),
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
"yunion.io/x/onecloud/pkg/util/sysutils"
|
||||
)
|
||||
|
||||
@@ -45,7 +44,7 @@ const (
|
||||
UnknownLogicalVolumeIndex = MaxInt
|
||||
)
|
||||
|
||||
type RaidDriverFactory func(term *ssh.Client) IRaidDriver
|
||||
type RaidDriverFactory func(term IExecTerm) IRaidDriver
|
||||
|
||||
type sRaidDrivers map[string]RaidDriverFactory
|
||||
|
||||
@@ -106,7 +105,7 @@ func (dev *RaidBasePhyDev) ToBaremetalStorage(index int) *baremetal.BaremetalSto
|
||||
}
|
||||
}
|
||||
|
||||
func GetModules(term *ssh.Client) []string {
|
||||
func GetModules(term IExecTerm) []string {
|
||||
ret := []string{}
|
||||
lines, err := term.Run("/sbin/lsmod")
|
||||
if err != nil {
|
||||
@@ -142,7 +141,7 @@ type RaidLogicalVolume struct {
|
||||
BlockDev string
|
||||
}
|
||||
|
||||
func SGMap(term *ssh.Client) ([]compute.SGMapItem, error) {
|
||||
func SGMap(term IExecTerm) ([]compute.SGMapItem, error) {
|
||||
lines, err := term.Run("/usr/bin/sg_map -x")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "run sg_map")
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/raid"
|
||||
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
)
|
||||
|
||||
type Mpt2SASRaidPhyDev struct {
|
||||
@@ -307,12 +306,12 @@ func (adapter *Mpt2SASRaidAdaptor) RemoveLogicVolumes() error {
|
||||
}
|
||||
|
||||
type Mpt2SASRaid struct {
|
||||
term *ssh.Client
|
||||
term raid.IExecTerm
|
||||
utility string
|
||||
adapters []*Mpt2SASRaidAdaptor
|
||||
}
|
||||
|
||||
func NewMpt2SASRaid(term *ssh.Client) raid.IRaidDriver {
|
||||
func NewMpt2SASRaid(term raid.IExecTerm) raid.IRaidDriver {
|
||||
return &Mpt2SASRaid{
|
||||
term: term,
|
||||
adapters: make([]*Mpt2SASRaidAdaptor, 0),
|
||||
|
||||
@@ -30,6 +30,7 @@ type SEndpointChangeManager struct {
|
||||
func newEndpointChangeManager() *SEndpointChangeManager {
|
||||
man := &SEndpointChangeManager{}
|
||||
man.InitSync(man)
|
||||
man.FirstSync()
|
||||
return man
|
||||
}
|
||||
|
||||
|
||||
@@ -301,6 +301,7 @@ func FetchCustomizeColumns(
|
||||
retVal := make([]*jsonutils.JSONDict, ret[0].Len())
|
||||
for i := 0; i < ret[0].Len(); i += 1 {
|
||||
jsonDict := ValueToJSONDict(ret[0].Index(i))
|
||||
// NOTE: don't use obj update jsonDict as retval
|
||||
jsonDict.Update(jsonutils.Marshal(objs[i]).(*jsonutils.JSONDict))
|
||||
retVal[i] = jsonDict
|
||||
}
|
||||
|
||||
@@ -928,6 +928,12 @@ func (dispatcher *DBModelDispatcher) Get(ctx context.Context, idStr string, quer
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if data != nil {
|
||||
if dataDict, ok := data.(*jsonutils.JSONDict); ok {
|
||||
i18nDict := dispatcher.modelManager.GetI18N(ctx, idStr, data)
|
||||
if i18nDict != nil {
|
||||
dataDict.Set("_i18n", i18nDict)
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@@ -1368,7 +1374,7 @@ func (dispatcher *DBModelDispatcher) BatchCreate(ctx context.Context, query json
|
||||
ret := make([]sCreateResult, len(multiData))
|
||||
for i := range multiData {
|
||||
var model IModel
|
||||
model, err = batchCreateDoCreateItem(manager, ctx, userCred, ownerId, query, multiData[i], i)
|
||||
model, err = batchCreateDoCreateItem(manager, ctx, userCred, ownerId, query, multiData[i], i+1)
|
||||
if err == nil {
|
||||
ret[i] = sCreateResult{model: model, err: nil}
|
||||
} else {
|
||||
|
||||
@@ -107,12 +107,7 @@ func (manager *SDomainizedResourceBaseManager) ListItemFilter(
|
||||
) (*sqlchemy.SQuery, error) {
|
||||
if len(query.ProjectDomainIds) > 0 {
|
||||
// make sure ids are not utf8 string
|
||||
idList := make([]string, 0)
|
||||
for _, pid := range query.ProjectDomainIds {
|
||||
if !stringutils2.IsUtf8(pid) {
|
||||
idList = append(idList, pid)
|
||||
}
|
||||
}
|
||||
idList := stringutils2.RemoveUtf8Strings(query.ProjectDomainIds)
|
||||
tenants := TenantCacheManager.GetDomainQuery().SubQuery()
|
||||
subq := tenants.Query(tenants.Field("id")).Filter(sqlchemy.OR(
|
||||
sqlchemy.In(tenants.Field("id"), idList),
|
||||
|
||||
@@ -127,6 +127,8 @@ type IModelManager interface {
|
||||
QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error)
|
||||
|
||||
GetPagingConfig() *SPagingConfig
|
||||
|
||||
GetI18N(ctx context.Context, idstr string, resObj jsonutils.JSONObject) *jsonutils.JSONDict
|
||||
}
|
||||
|
||||
type IModel interface {
|
||||
@@ -246,6 +248,10 @@ type IMetadataBaseModelManager interface {
|
||||
GetMetadataHiddenKeys() []string
|
||||
}
|
||||
|
||||
type IMetadataBaseModel interface {
|
||||
OnMetadataUpdated(ctx context.Context, userCred mcclient.TokenCredential)
|
||||
}
|
||||
|
||||
type IStandaloneModelManager interface {
|
||||
IResourceModelManager
|
||||
|
||||
@@ -282,6 +288,8 @@ type IStandaloneModel interface {
|
||||
GetAllMetadata(userCred mcclient.TokenCredential) (map[string]string, error)
|
||||
|
||||
IsShared() bool
|
||||
|
||||
IMetadataBaseModel
|
||||
}
|
||||
|
||||
type IDomainLevelModelManager interface {
|
||||
|
||||
@@ -26,7 +26,8 @@ func TestEctdLockManager(t *testing.T) {
|
||||
shared := newSharedObject()
|
||||
for i := 0; i < 4; i++ {
|
||||
lockman, err := NewEtcdLockManager(&SEtcdLockManagerConfig{
|
||||
Endpoints: []string{"localhost:2379"},
|
||||
LockPrefix: "test-etcd-lock-manager",
|
||||
Endpoints: []string{"localhost:2379"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Skipf("new etcd lockman: %v", err)
|
||||
|
||||
@@ -492,6 +492,7 @@ func (manager *SMetadataManager) SetValues(ctx context.Context, obj IModel, stor
|
||||
}
|
||||
|
||||
newRecord := SMetadata{}
|
||||
newRecord.SetModelManager(manager, &newRecord)
|
||||
|
||||
newRecord.ObjId = obj.GetId()
|
||||
newRecord.ObjType = obj.GetModelManager().Keyword()
|
||||
@@ -585,13 +586,16 @@ func (manager *SMetadataManager) SetAll(ctx context.Context, obj IModel, store m
|
||||
return nil
|
||||
}
|
||||
|
||||
func (manager *SMetadataManager) GetAll(obj IModel, keys []string, userCred mcclient.TokenCredential) (map[string]string, error) {
|
||||
func (manager *SMetadataManager) GetAll(obj IModel, keys []string, keyPrefix string, userCred mcclient.TokenCredential) (map[string]string, error) {
|
||||
idStr := GetObjectIdstr(obj)
|
||||
records := make([]SMetadata, 0)
|
||||
q := manager.Query().Equals("id", idStr)
|
||||
if keys != nil && len(keys) > 0 {
|
||||
q = q.In("key", keys)
|
||||
}
|
||||
if len(keyPrefix) > 0 {
|
||||
q = q.Startswith("key", keyPrefix)
|
||||
}
|
||||
err := FetchModelObjects(manager, q, &records)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -70,10 +70,34 @@ func (meta *SMetadataResourceBaseModelManager) ListItemFilter(
|
||||
q = q.Filter(sqlchemy.In(q.Field("id"), sq))
|
||||
}
|
||||
|
||||
if input.WithoutUserMeta {
|
||||
if input.WithoutUserMeta != nil || input.WithUserMeta != nil {
|
||||
metadatas := Metadata.Query().Equals("obj_type", manager.Keyword()).SubQuery()
|
||||
sq := metadatas.Query(metadatas.Field("obj_id")).Startswith("key", USER_TAG_PREFIX).Distinct().SubQuery()
|
||||
q.Filter(sqlchemy.NotIn(q.Field("id"), sq))
|
||||
if (input.WithoutUserMeta != nil && *input.WithoutUserMeta) || (input.WithUserMeta != nil && !*input.WithUserMeta) {
|
||||
q = q.Filter(sqlchemy.NotIn(q.Field("id"), sq))
|
||||
} else {
|
||||
q = q.Filter(sqlchemy.In(q.Field("id"), sq))
|
||||
}
|
||||
}
|
||||
|
||||
if input.WithCloudMeta != nil {
|
||||
metadatas := Metadata.Query().Equals("obj_type", manager.Keyword()).SubQuery()
|
||||
sq := metadatas.Query(metadatas.Field("obj_id")).Startswith("key", CLOUD_TAG_PREFIX).Distinct().SubQuery()
|
||||
if *input.WithCloudMeta {
|
||||
q = q.Filter(sqlchemy.In(q.Field("id"), sq))
|
||||
} else {
|
||||
q = q.Filter(sqlchemy.NotIn(q.Field("id"), sq))
|
||||
}
|
||||
}
|
||||
|
||||
if input.WithAnyMeta != nil {
|
||||
metadatas := Metadata.Query().Equals("obj_type", manager.Keyword()).SubQuery()
|
||||
sq := metadatas.Query(metadatas.Field("obj_id")).Distinct().SubQuery()
|
||||
if *input.WithAnyMeta {
|
||||
q = q.Filter(sqlchemy.In(q.Field("id"), sq))
|
||||
} else {
|
||||
q = q.Filter(sqlchemy.NotIn(q.Field("id"), sq))
|
||||
}
|
||||
}
|
||||
|
||||
return q
|
||||
|
||||
@@ -359,14 +359,15 @@ func (manager *SModelBaseManager) GetPropertyDistinctField(ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
var res = jsonutils.NewDict()
|
||||
q := im.Query()
|
||||
q, err = ListItemQueryFilters(im, ctx, q, userCred, query, policy.PolicyActionList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var backupQuery = *q
|
||||
|
||||
var (
|
||||
backupQuery = *q
|
||||
res = jsonutils.NewDict()
|
||||
)
|
||||
// query field
|
||||
for i := 0; i < len(fields); i++ {
|
||||
var nq = backupQuery
|
||||
@@ -424,6 +425,10 @@ func (manager *SModelBaseManager) OnCreateFailed(ctx context.Context, userCred m
|
||||
return nil
|
||||
}
|
||||
|
||||
func (manager *SModelBaseManager) GetI18N(ctx context.Context, idstr string, resObj jsonutils.JSONObject) *jsonutils.JSONDict {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (model *SModelBase) GetId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -91,6 +91,13 @@ func GenerateName(manager IModelManager, ownerId mcclient.IIdentityProvider, hin
|
||||
return GenerateName2(manager, ownerId, hint, nil, 1)
|
||||
}
|
||||
|
||||
func GenerateAlterName(model IModel, hint string) (string, error) {
|
||||
if hint == model.GetName() {
|
||||
return hint, nil
|
||||
}
|
||||
return GenerateName2(nil, nil, hint, model, 1)
|
||||
}
|
||||
|
||||
func GenerateName2(manager IModelManager, ownerId mcclient.IIdentityProvider, hint string, model IModel, baseIndex int) (string, error) {
|
||||
_, pattern, patternLen := stringutils.ParseNamePattern(hint)
|
||||
var name string
|
||||
|
||||
@@ -45,31 +45,29 @@ type SOpsLogManager struct {
|
||||
type SOpsLog struct {
|
||||
SModelBase
|
||||
|
||||
Id int64 `primary:"true" auto_increment:"true" list:"user"` // = Column(BigInteger, primary_key=True)
|
||||
ObjType string `width:"40" charset:"ascii" nullable:"false" list:"user" create:"required"` // = Column(VARCHAR(40, charset='ascii'), nullable=False)
|
||||
ObjId string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required" index:"true"` // = Column(VARCHAR(ID_LENGTH, charset='ascii'), nullable=False)
|
||||
ObjName string `width:"128" charset:"utf8" nullable:"false" list:"user" create:"required"` //= Column(VARCHAR(128, charset='utf8'), nullable=False)
|
||||
Action string `width:"32" charset:"utf8" nullable:"false" list:"user" create:"required"` //= Column(VARCHAR(32, charset='ascii'), nullable=False)
|
||||
Id int64 `primary:"true" auto_increment:"true" list:"user"`
|
||||
ObjType string `width:"40" charset:"ascii" nullable:"false" list:"user" create:"required"`
|
||||
ObjId string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"required" index:"true"`
|
||||
ObjName string `width:"128" charset:"utf8" nullable:"false" list:"user" create:"required"`
|
||||
Action string `width:"32" charset:"utf8" nullable:"false" list:"user" create:"required"`
|
||||
Notes string `charset:"utf8" list:"user" create:"required"`
|
||||
|
||||
ProjectId string `name:"tenant_id" width:"128" charset:"ascii" list:"user" create:"required" index:"true"` // = Column(VARCHAR(ID_LENGTH, charset='ascii'))
|
||||
Project string `name:"tenant" width:"128" charset:"utf8" list:"user" create:"required"` // tenant = Column(VARCHAR(128, charset='utf8'))
|
||||
ProjectId string `name:"tenant_id" width:"128" charset:"ascii" list:"user" create:"optional" index:"true"`
|
||||
Project string `name:"tenant" width:"128" charset:"utf8" list:"user" create:"optional"`
|
||||
|
||||
ProjectDomainId string `name:"project_domain_id" default:"default" width:"128" charset:"ascii" list:"user" create:"required"`
|
||||
ProjectDomain string `name:"project_domain" default:"Default" width:"128" charset:"utf8" list:"user" create:"required"`
|
||||
ProjectDomainId string `name:"project_domain_id" default:"default" width:"128" charset:"ascii" list:"user" create:"optional"`
|
||||
ProjectDomain string `name:"project_domain" default:"Default" width:"128" charset:"utf8" list:"user" create:"optional"`
|
||||
|
||||
UserId string `width:"128" charset:"ascii" list:"user" create:"required"` // = Column(VARCHAR(ID_LENGTH, charset='ascii'))
|
||||
User string `width:"128" charset:"utf8" list:"user" create:"required"` // = Column(VARCHAR(128, charset='utf8'))
|
||||
UserId string `width:"128" charset:"ascii" list:"user" create:"required"`
|
||||
User string `width:"128" charset:"utf8" list:"user" create:"required"`
|
||||
DomainId string `width:"128" charset:"ascii" list:"user" create:"optional"`
|
||||
Domain string `width:"128" charset:"utf8" list:"user" create:"optional"`
|
||||
Roles string `width:"64" charset:"ascii" list:"user" create:"optional"` // = Column(VARCHAR(64, charset='ascii'))
|
||||
Roles string `width:"64" charset:"ascii" list:"user" create:"optional"`
|
||||
|
||||
// BillingType string `width:"64" charset:"ascii" default:"postpaid" list:"user" create:"user"` // billing_type = Column(VARCHAR(64, charset='ascii'), nullable=True)
|
||||
OpsTime time.Time `nullable:"false" list:"user"` // = Column(DateTime, nullable=False)
|
||||
OpsTime time.Time `nullable:"false" list:"user"`
|
||||
|
||||
OwnerDomainId string `name:"owner_domain_id" default:"default" width:"128" charset:"ascii" list:"user" create:"optional"`
|
||||
OwnerProjectId string `name:"owner_tenant_id" width:"128" charset:"ascii" list:"user" create:"optional"` // = Column(VARCHAR(ID_LENGTH, charset='ascii'))
|
||||
// owner_user_id = Column(VARCHAR(ID_LENGTH, charset='ascii'))
|
||||
OwnerProjectId string `name:"owner_tenant_id" width:"128" charset:"ascii" list:"user" create:"optional"`
|
||||
}
|
||||
|
||||
var OpsLog *SOpsLogManager
|
||||
@@ -434,6 +432,7 @@ func (manager *SOpsLogManager) ValidateCreateData(ctx context.Context,
|
||||
query jsonutils.JSONObject,
|
||||
data apis.OpsLogCreateInput,
|
||||
) (apis.OpsLogCreateInput, error) {
|
||||
data.User = ownerId.GetUserName()
|
||||
return data, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,9 @@ const (
|
||||
ACT_BACKUP_START = "backup_start"
|
||||
ACT_BACKUP_START_FAILED = "backup_start_fail"
|
||||
|
||||
ACT_FREEZE = "freeze"
|
||||
ACT_UNFREEZE = "unfreeze"
|
||||
|
||||
ACT_RESTARING = "restarting"
|
||||
ACT_RESTART_FAIL = "restart_fail"
|
||||
|
||||
|
||||
@@ -102,12 +102,7 @@ func (manager *SProjectizedResourceBaseManager) ListItemFilter(
|
||||
}
|
||||
if len(query.ProjectIds) > 0 {
|
||||
// make sure ids are not utf8 string
|
||||
idList := make([]string, 0)
|
||||
for _, pid := range query.ProjectIds {
|
||||
if !stringutils2.IsUtf8(pid) {
|
||||
idList = append(idList, pid)
|
||||
}
|
||||
}
|
||||
idList := stringutils2.RemoveUtf8Strings(query.ProjectIds)
|
||||
tenants := TenantCacheManager.GetTenantQuery().SubQuery()
|
||||
subq := tenants.Query(tenants.Field("id")).Filter(sqlchemy.OR(
|
||||
sqlchemy.In(tenants.Field("id"), idList),
|
||||
|
||||
@@ -33,6 +33,7 @@ var (
|
||||
func init() {
|
||||
quotaManagerTable = make(map[reflect.Type]IQuotaManager)
|
||||
|
||||
db.AddUsages = AddUsages
|
||||
db.CancelUsages = CancelUsages
|
||||
db.CancelPendingUsagesInContext = cancelPendingUsagesInContext
|
||||
db.InitPendingUsagesInContext = initPendingUsagesInContext
|
||||
|
||||
@@ -116,34 +116,8 @@ func (m *SScopedResourceBaseManager) FilterByOwner(q *sqlchemy.SQuery, userCred
|
||||
}
|
||||
switch scope {
|
||||
case rbacutils.ScopeDomain:
|
||||
/* q = q.Filter(sqlchemy.OR(
|
||||
// share to system
|
||||
sqlchemy.AND(
|
||||
sqlchemy.IsNullOrEmpty(q.Field("domain_id")),
|
||||
sqlchemy.IsNullOrEmpty(q.Field("tenant_id")),
|
||||
),
|
||||
// share to this domain or its sub-projects
|
||||
sqlchemy.Equals(q.Field("domain_id"), userCred.GetProjectDomainId()),
|
||||
)) */
|
||||
q = q.Equals("domain_id", userCred.GetProjectDomainId())
|
||||
case rbacutils.ScopeProject:
|
||||
/* q = q.Filter(sqlchemy.OR(
|
||||
// share to system
|
||||
sqlchemy.AND(
|
||||
sqlchemy.IsNullOrEmpty(q.Field("domain_id")),
|
||||
sqlchemy.IsNullOrEmpty(q.Field("tenant_id")),
|
||||
),
|
||||
// share to project's parent domain
|
||||
sqlchemy.AND(
|
||||
sqlchemy.Equals(q.Field("domain_id"), userCred.GetProjectDomainId()),
|
||||
sqlchemy.IsNullOrEmpty(q.Field("tenant_id")),
|
||||
),
|
||||
// share to this project
|
||||
sqlchemy.AND(
|
||||
sqlchemy.Equals(q.Field("domain_id"), userCred.GetProjectDomainId()),
|
||||
sqlchemy.Equals(q.Field("tenant_id"), userCred.GetProjectId()),
|
||||
),
|
||||
)) */
|
||||
q = q.Equals("tenant_id", userCred.GetProjectId())
|
||||
}
|
||||
return q
|
||||
|
||||
@@ -266,36 +266,97 @@ func (model *SStandaloneResourceBase) GetMetadataJson(key string, userCred mccli
|
||||
return Metadata.GetJsonValue(model, key, userCred)
|
||||
}
|
||||
|
||||
func isUserMetadata(key string) bool {
|
||||
return strings.HasPrefix(key, USER_TAG_PREFIX)
|
||||
}
|
||||
|
||||
func containsUserMetadata(dict map[string]interface{}) bool {
|
||||
for k := range dict {
|
||||
if isUserMetadata(k) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (model *SStandaloneResourceBase) SetMetadata(ctx context.Context, key string, value interface{}, userCred mcclient.TokenCredential) error {
|
||||
return Metadata.SetValue(ctx, model, key, value, userCred)
|
||||
err := Metadata.SetValue(ctx, model, key, value, userCred)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SetValue")
|
||||
}
|
||||
if isUserMetadata(key) {
|
||||
model.GetIStandaloneModel().OnMetadataUpdated(ctx, userCred)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (model *SStandaloneResourceBase) SetAllMetadata(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error {
|
||||
return Metadata.SetValuesWithLog(ctx, model, dictstore, userCred)
|
||||
err := Metadata.SetValuesWithLog(ctx, model, dictstore, userCred)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SetValuesWithLog")
|
||||
}
|
||||
if containsUserMetadata(dictstore) {
|
||||
model.GetIStandaloneModel().OnMetadataUpdated(ctx, userCred)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (model *SStandaloneResourceBase) SetUserMetadataValues(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error {
|
||||
return Metadata.SetValuesWithLog(ctx, model, dictstore, userCred)
|
||||
err := Metadata.SetValuesWithLog(ctx, model, dictstore, userCred)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SetValuesWithLog")
|
||||
}
|
||||
model.GetIStandaloneModel().OnMetadataUpdated(ctx, userCred)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (model *SStandaloneResourceBase) SetUserMetadataAll(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error {
|
||||
return Metadata.SetAll(ctx, model, dictstore, userCred, USER_TAG_PREFIX)
|
||||
err := Metadata.SetAll(ctx, model, dictstore, userCred, USER_TAG_PREFIX)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SetAll")
|
||||
}
|
||||
model.GetIStandaloneModel().OnMetadataUpdated(ctx, userCred)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (model *SStandaloneResourceBase) SetCloudMetadataAll(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error {
|
||||
return Metadata.SetAll(ctx, model, dictstore, userCred, CLOUD_TAG_PREFIX)
|
||||
err := Metadata.SetAll(ctx, model, dictstore, userCred, CLOUD_TAG_PREFIX)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SetAll")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (model *SStandaloneResourceBase) RemoveMetadata(ctx context.Context, key string, userCred mcclient.TokenCredential) error {
|
||||
return Metadata.SetValue(ctx, model, key, "", userCred)
|
||||
err := Metadata.SetValue(ctx, model, key, "", userCred)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SetValue")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (model *SStandaloneResourceBase) RemoveAllMetadata(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
return Metadata.RemoveAll(ctx, model, userCred)
|
||||
err := Metadata.RemoveAll(ctx, model, userCred)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "RemoveAll")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (model *SStandaloneResourceBase) GetAllMetadata(userCred mcclient.TokenCredential) (map[string]string, error) {
|
||||
return Metadata.GetAll(model, nil, userCred)
|
||||
return Metadata.GetAll(model, nil, "", userCred)
|
||||
}
|
||||
|
||||
func (model *SStandaloneResourceBase) GetAllUserMetadata() (map[string]string, error) {
|
||||
meta, err := Metadata.GetAll(model, nil, USER_TAG_PREFIX, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Metadata.GetAll")
|
||||
}
|
||||
ret := make(map[string]string)
|
||||
for k, v := range meta {
|
||||
ret[k[len(USER_TAG_PREFIX):]] = v
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (model *SStandaloneResourceBase) AllowGetDetailsMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
@@ -304,10 +365,18 @@ func (model *SStandaloneResourceBase) AllowGetDetailsMetadata(ctx context.Contex
|
||||
|
||||
// 获取资源标签(元数据)
|
||||
func (model *SStandaloneResourceBase) GetDetailsMetadata(ctx context.Context, userCred mcclient.TokenCredential, input apis.GetMetadataInput) (apis.GetMetadataOutput, error) {
|
||||
val, err := Metadata.GetAll(model, input.Field, userCred)
|
||||
val, err := Metadata.GetAll(model, input.Field, input.Prefix, userCred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Metadata.GetAll")
|
||||
}
|
||||
if len(input.Prefix) > 0 {
|
||||
// trim prefix from key
|
||||
ret := make(map[string]string)
|
||||
for k, v := range val {
|
||||
ret[k[len(input.Prefix):]] = v
|
||||
}
|
||||
val = ret
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
@@ -510,3 +579,7 @@ func (model *SStandaloneResourceBase) ValidateUpdateData(ctx context.Context, us
|
||||
func (model *SStandaloneResourceBase) IsShared() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (model *SStandaloneResourceBase) OnMetadataUpdated(ctx context.Context, userCred mcclient.TokenCredential) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ type IUsage interface {
|
||||
var (
|
||||
CancelUsages func(ctx context.Context, userCred mcclient.TokenCredential, usages []IUsage)
|
||||
|
||||
AddUsages func(ctx context.Context, userCred mcclient.TokenCredential, usages []IUsage)
|
||||
|
||||
CancelPendingUsagesInContext func(ctx context.Context, userCred mcclient.TokenCredential) error
|
||||
|
||||
InitPendingUsagesInContext func(ctx context.Context) context.Context
|
||||
|
||||
@@ -64,6 +64,8 @@ type SVirtualResourceBase struct {
|
||||
PendingDeletedAt time.Time `json:"pending_deleted_at" list:"user"`
|
||||
// 资源是否处于回收站中
|
||||
PendingDeleted bool `nullable:"false" default:"false" index:"true" get:"user" list:"user" json:"pending_deleted"`
|
||||
// 资源是否被冻结
|
||||
Freezed bool `nullable:"false" default:"false" get:"user" list:"user" json:"freezed"`
|
||||
}
|
||||
|
||||
func (model *SVirtualResourceBase) IsOwner(userCred mcclient.TokenCredential) bool {
|
||||
@@ -228,8 +230,21 @@ func (manager *SVirtualResourceBaseManager) FetchCustomizeColumns(
|
||||
return ret
|
||||
}
|
||||
|
||||
func (model *SVirtualResourceBase) PreCheckPerformAction(
|
||||
ctx context.Context, userCred mcclient.TokenCredential,
|
||||
action string, query jsonutils.JSONObject, data jsonutils.JSONObject,
|
||||
) error {
|
||||
if err := model.SStandaloneResourceBase.PreCheckPerformAction(ctx, userCred, action, query, data); err != nil {
|
||||
return err
|
||||
}
|
||||
if model.Freezed && action != "unfreeze" {
|
||||
return httperrors.NewBadRequestError("Virtual resource freezed, can't do %s", action)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (model *SVirtualResourceBase) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool {
|
||||
return model.IsOwner(userCred) || IsAdminAllowUpdate(userCred, model)
|
||||
return !model.Freezed && (model.IsOwner(userCred) || IsAdminAllowUpdate(userCred, model))
|
||||
}
|
||||
|
||||
func (model *SVirtualResourceBase) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
|
||||
@@ -253,6 +268,47 @@ func (model *SVirtualResourceBase) GetTenantCache(ctx context.Context) (*STenant
|
||||
return TenantCacheManager.FetchTenantById(ctx, model.ProjectId)
|
||||
}
|
||||
|
||||
func (model *SVirtualResourceBase) AllowPerformFreeze(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformFreezeInput) bool {
|
||||
return IsAdminAllowPerform(userCred, model, "freeze")
|
||||
}
|
||||
|
||||
// freezed update and perform action operation except for unfreeze
|
||||
func (model *SVirtualResourceBase) PerformFreeze(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformFreezeInput) (jsonutils.JSONObject, error) {
|
||||
if model.Freezed {
|
||||
return nil, httperrors.NewBadRequestError("virtual resource already freezed")
|
||||
}
|
||||
_, err := Update(model, func() error {
|
||||
model.Freezed = true
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
OpsLog.LogEvent(model, ACT_FREEZE, "perform freeze", userCred)
|
||||
logclient.AddActionLogWithContext(ctx, model, logclient.ACT_FREEZE, "perform freeze", userCred, true)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (model *SVirtualResourceBase) AllowPerformUnfreeze(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformUnfreezeInput) bool {
|
||||
return IsAdminAllowPerform(userCred, model, "unfreeze")
|
||||
}
|
||||
|
||||
func (model *SVirtualResourceBase) PerformUnfreeze(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformUnfreezeInput) (jsonutils.JSONObject, error) {
|
||||
if !model.Freezed {
|
||||
return nil, httperrors.NewBadRequestError("virtual resource not freezed")
|
||||
}
|
||||
_, err := Update(model, func() error {
|
||||
model.Freezed = false
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
OpsLog.LogEvent(model, ACT_UNFREEZE, "perform unfreeze", userCred)
|
||||
logclient.AddActionLogWithContext(ctx, model, logclient.ACT_UNFREEZE, "perform unfreeze", userCred, true)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (model *SVirtualResourceBase) AllowPerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeProjectOwnerInput) bool {
|
||||
return IsAdminAllowPerform(userCred, model, "change-owner")
|
||||
}
|
||||
@@ -393,11 +449,21 @@ func (model *SVirtualResourceBase) AllowPerformCancelDelete(ctx context.Context,
|
||||
func (model *SVirtualResourceBase) PerformCancelDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
if model.PendingDeleted && !model.Deleted {
|
||||
err := model.DoCancelPendingDelete(ctx, userCred)
|
||||
return nil, err
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "model.DoCancelPendingDelete")
|
||||
}
|
||||
model.RecoverUsages(ctx, userCred)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (model *SVirtualResourceBase) RecoverUsages(ctx context.Context, userCred mcclient.TokenCredential) {
|
||||
usages := model.GetIModel().GetUsages()
|
||||
if AddUsages != nil && len(usages) > 0 {
|
||||
AddUsages(ctx, userCred, usages)
|
||||
}
|
||||
}
|
||||
|
||||
func (model *SVirtualResourceBase) DoCancelPendingDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
err := model.CancelPendingDelete(ctx, userCred)
|
||||
if err == nil {
|
||||
@@ -416,7 +482,10 @@ func (model *SVirtualResourceBase) GetIVirtualModel() IVirtualModel {
|
||||
|
||||
func (model *SVirtualResourceBase) CancelPendingDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
if model.PendingDeleted && !model.Deleted {
|
||||
return model.MarkCancelPendingDelete(ctx, userCred)
|
||||
err := model.MarkCancelPendingDelete(ctx, userCred)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "MarkCancelPendingDelete")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -14,6 +14,13 @@
|
||||
|
||||
package notifyclient
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
)
|
||||
|
||||
const (
|
||||
SYSTEM_ERROR = "SYSTEM_ERROR"
|
||||
SYSTEM_WARNING = "SYSTEM_WARNING"
|
||||
@@ -28,3 +35,42 @@ const (
|
||||
|
||||
IMAGE_ACTIVED = "IMAGE_ACTIVED"
|
||||
)
|
||||
|
||||
type SAction string
|
||||
|
||||
var (
|
||||
Event SEvent
|
||||
|
||||
ActionCreate SAction = "create"
|
||||
ActionUpdate SAction = "update"
|
||||
ActionDelete SAction = "delete"
|
||||
ActionRebuildRoot SAction = "rebuild_root"
|
||||
ActionChangeConfig SAction = "change_config"
|
||||
)
|
||||
|
||||
type SEvent struct {
|
||||
resourceType string
|
||||
action SAction
|
||||
}
|
||||
|
||||
func (se SEvent) WithResourceType(manager db.IModelManager) SEvent {
|
||||
se.resourceType = manager.Keyword()
|
||||
return se
|
||||
}
|
||||
|
||||
func (se SEvent) WithAction(a SAction) SEvent {
|
||||
se.action = a
|
||||
return se
|
||||
}
|
||||
|
||||
func (se SEvent) ResourceType() string {
|
||||
return se.resourceType
|
||||
}
|
||||
|
||||
func (se SEvent) Action() string {
|
||||
return string(se.action)
|
||||
}
|
||||
|
||||
func (se SEvent) String() string {
|
||||
return strings.ToUpper(fmt.Sprintf("%s_%s", se.ResourceType(), se.Action()))
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -29,12 +30,15 @@ import (
|
||||
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/i18n"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
npk "yunion.io/x/onecloud/pkg/mcclient/modules/notify"
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -97,6 +101,9 @@ func getTemplate(ctx context.Context, topic string, contType string, channel npk
|
||||
}
|
||||
|
||||
func getContent(ctx context.Context, topic string, contType string, channel npk.TNotifyChannel, data jsonutils.JSONObject) (string, error) {
|
||||
if channel == npk.NotifyByWebhook {
|
||||
return "", nil
|
||||
}
|
||||
tmpl, err := getTemplate(ctx, topic, contType, channel)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -110,6 +117,23 @@ func getContent(ctx context.Context, topic string, contType string, channel npk.
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func NotifyWebhook(ctx context.Context, userCred mcclient.TokenCredential, obj db.IModel, action SAction) error {
|
||||
ret, err := db.FetchCustomizeColumns(obj.GetModelManager(), ctx, userCred, jsonutils.NewDict(), []interface{}{obj}, stringutils2.SSortedStrings{}, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(ret) == 0 {
|
||||
return fmt.Errorf("unable to get details for model %q", obj.GetId())
|
||||
}
|
||||
event := Event.WithAction(action).WithResourceType(obj.GetModelManager())
|
||||
msg := jsonutils.NewDict()
|
||||
msg.Set("resource_type", jsonutils.NewString(event.ResourceType()))
|
||||
msg.Set("action", jsonutils.NewString(event.Action()))
|
||||
msg.Set("resource_details", ret[0])
|
||||
RawNotifyWithCtx(ctx, []string{}, false, npk.NotifyByWebhook, npk.NotifyPriorityNormal, event.String(), msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NotifyWithCtx(ctx context.Context, recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) {
|
||||
notify(ctx, recipientId, isGroup, priority, event, data)
|
||||
}
|
||||
@@ -137,7 +161,16 @@ func RawNotify(recipientId []string, isGroup bool, channel npk.TNotifyChannel, p
|
||||
rawNotify(context.Background(), recipientId, isGroup, channel, priority, event, data)
|
||||
}
|
||||
|
||||
func rawNotify(ctx context.Context, recipientId []string, isGroup bool, channel npk.TNotifyChannel, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) {
|
||||
// IntelliNotify try to create receiver nonexistent if createReceiver is set to true
|
||||
func IntelliNotify(ctx context.Context, recipientId []string, isGroup bool, channel npk.TNotifyChannel, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject, createReceiver bool) {
|
||||
intelliNotify(ctx, recipientId, isGroup, channel, priority, event, data, createReceiver)
|
||||
}
|
||||
|
||||
const noSuchReceiver = `no such receiver whose uid is '(.*)'`
|
||||
|
||||
var noSuchReceiverRegexp = regexp.MustCompile(noSuchReceiver)
|
||||
|
||||
func intelliNotify(ctx context.Context, recipientId []string, isGroup bool, channel npk.TNotifyChannel, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject, createReceiver bool) {
|
||||
log.Infof("notify %s event %s priority %s", recipientId, event, priority)
|
||||
msg := npk.SNotifyMessage{}
|
||||
if isGroup {
|
||||
@@ -160,13 +193,46 @@ func rawNotify(ctx context.Context, recipientId []string, isGroup bool, channel
|
||||
// log.Debugf("send notification %s %s", topic, body)
|
||||
notifyClientWorkerMan.Run(func() {
|
||||
s := auth.GetAdminSession(context.Background(), consts.GetRegion(), "")
|
||||
err := npk.Notifications.Send(s, msg)
|
||||
if err != nil {
|
||||
log.Errorf("unable to send notification: %v", err)
|
||||
for {
|
||||
err := npk.Notifications.Send(s, msg)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if !createReceiver {
|
||||
log.Errorf("unable to send notification: %v", err)
|
||||
break
|
||||
}
|
||||
jerr, ok := err.(*httputils.JSONClientError)
|
||||
if !ok {
|
||||
log.Errorf("unable to send notification: %v", err)
|
||||
break
|
||||
}
|
||||
if jerr.Code > 500 {
|
||||
log.Errorf("unable to send notification: %v", err)
|
||||
break
|
||||
}
|
||||
match := noSuchReceiverRegexp.FindStringSubmatch(jerr.Details)
|
||||
if match == nil || len(match) <= 1 {
|
||||
log.Errorf("unable to send notification: %v", err)
|
||||
break
|
||||
}
|
||||
receiverId := match[1]
|
||||
createData := jsonutils.NewDict()
|
||||
createData.Set("uid", jsonutils.NewString(receiverId))
|
||||
_, err = modules.NotifyReceiver.Create(s, createData)
|
||||
if err != nil {
|
||||
log.Errorf("try to create receiver %q, but failed: %v", receiverId, err)
|
||||
break
|
||||
}
|
||||
log.Infof("create receiver %q successfully", receiverId)
|
||||
}
|
||||
}, nil, nil)
|
||||
}
|
||||
|
||||
func rawNotify(ctx context.Context, recipientId []string, isGroup bool, channel npk.TNotifyChannel, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) {
|
||||
intelliNotify(ctx, recipientId, isGroup, channel, priority, event, data, false)
|
||||
}
|
||||
|
||||
func NotifyNormal(recipientId []string, isGroup bool, event string, data jsonutils.JSONObject) {
|
||||
notifyNormal(context.Background(), recipientId, isGroup, event, data)
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ type SCloudaccountCredential struct {
|
||||
// 秘钥key (Aliyun, Aws, huawei, ucloud, ctyun, zstack, s3)
|
||||
AccessKeySecret string `json:"access_key_secret"`
|
||||
|
||||
// 环境 (Azure, Aws, huawei, ctyun)
|
||||
// 环境 (Azure, Aws, huawei, ctyun, aliyun)
|
||||
Environment string `json:"environment"`
|
||||
|
||||
// 目录ID (Azure)
|
||||
|
||||
@@ -51,6 +51,7 @@ const (
|
||||
CLOUD_CAPABILITY_EVENT = "event"
|
||||
CLOUD_CAPABILITY_CLOUDID = "cloudid"
|
||||
CLOUD_CAPABILITY_DNSZONE = "dnszone"
|
||||
CLOUD_CAPABILITY_PUBLIC_IP = "public_ip"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -14,6 +14,13 @@
|
||||
|
||||
package cloudprovider
|
||||
|
||||
type TElasticipChargeType string
|
||||
|
||||
var (
|
||||
ElasticipChargeTypeByTraffic = TElasticipChargeType("traffic")
|
||||
ElasticipChargeTypeByBandwidth = TElasticipChargeType("bandwidth")
|
||||
)
|
||||
|
||||
type SEip struct {
|
||||
Name string
|
||||
BandwidthMbps int
|
||||
|
||||
@@ -41,6 +41,11 @@ const (
|
||||
CLOUD_EC2 = "ec2"
|
||||
)
|
||||
|
||||
type SPublicIpInfo struct {
|
||||
PublicIpBw int
|
||||
PublicIpChargeType TElasticipChargeType
|
||||
}
|
||||
|
||||
type SManagedVMCreateConfig struct {
|
||||
Name string
|
||||
ExternalImageId string
|
||||
@@ -64,6 +69,10 @@ type SManagedVMCreateConfig struct {
|
||||
UserData string
|
||||
ProjectId string
|
||||
|
||||
SPublicIpInfo
|
||||
|
||||
Tags map[string]string
|
||||
|
||||
BillingCycle *billing.SBillingCycle
|
||||
}
|
||||
|
||||
|
||||
@@ -317,6 +317,8 @@ type ICloudVM interface {
|
||||
LiveMigrateVM(hostid string) error
|
||||
|
||||
GetError() error
|
||||
|
||||
SetMetadata(tags map[string]string, replace bool) error
|
||||
}
|
||||
|
||||
type ICloudNic interface {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
)
|
||||
|
||||
const DEFAULT_CLOUD_RULE_ID = "default_cloud_rule_id"
|
||||
const DEFAULT_LOCAL_RULE_ID = "default_local_rule_id"
|
||||
|
||||
type SecurityGroupFilterOptions struct {
|
||||
VpcId string
|
||||
@@ -44,6 +45,42 @@ type SecurityRule struct {
|
||||
ExternalId string
|
||||
}
|
||||
|
||||
type LocalSecurityRule struct {
|
||||
secrules.SecurityRule
|
||||
ExternalId string
|
||||
}
|
||||
|
||||
func (r LocalSecurityRule) String() string {
|
||||
return r.SecurityRule.String()
|
||||
}
|
||||
|
||||
type LocalSecurityRuleSet []LocalSecurityRule
|
||||
|
||||
func (srs LocalSecurityRuleSet) Len() int {
|
||||
return len(srs)
|
||||
}
|
||||
|
||||
func (srs LocalSecurityRuleSet) Swap(i, j int) {
|
||||
srs[i], srs[j] = srs[j], srs[i]
|
||||
}
|
||||
|
||||
func (srs LocalSecurityRuleSet) Less(i, j int) bool {
|
||||
if srs[i].Priority > srs[j].Priority {
|
||||
return true
|
||||
} else if srs[i].Priority == srs[j].Priority {
|
||||
return srs[i].String() < srs[j].String()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (srs LocalSecurityRuleSet) AllowList() secrules.SecurityRuleSet {
|
||||
rules := secrules.SecurityRuleSet{}
|
||||
for _, r := range srs {
|
||||
rules = append(rules, r.SecurityRule)
|
||||
}
|
||||
return rules.AllowList()
|
||||
}
|
||||
|
||||
type TPriorityOrder int
|
||||
|
||||
var (
|
||||
@@ -112,13 +149,15 @@ func CompareRules(
|
||||
defaultInRule, defaultOutRule SecurityRule,
|
||||
onlyAllowRules bool, debug bool,
|
||||
) (common, inAdds, outAdds, inDels, outDels []SecurityRule) {
|
||||
localInRules := secrules.SecurityRuleSet{}
|
||||
localOutRules := secrules.SecurityRuleSet{}
|
||||
localInRules := LocalSecurityRuleSet{}
|
||||
localOutRules := LocalSecurityRuleSet{}
|
||||
for i := range localRules {
|
||||
localRule := LocalSecurityRule{}
|
||||
localRule.SecurityRule = localRules[i]
|
||||
if localRules[i].Direction == secrules.DIR_IN {
|
||||
localInRules = append(localInRules, localRules[i])
|
||||
localInRules = append(localInRules, localRule)
|
||||
} else {
|
||||
localOutRules = append(localOutRules, localRules[i])
|
||||
localOutRules = append(localOutRules, localRule)
|
||||
}
|
||||
}
|
||||
inRules := SecurityRuleSet{}
|
||||
@@ -132,17 +171,22 @@ func CompareRules(
|
||||
}
|
||||
var inCommon, outCommon = inRules, outRules
|
||||
|
||||
defaultLocalInRule := *secrules.MustParseSecurityRule("in:deny any")
|
||||
defaultLocalOutRule := *secrules.MustParseSecurityRule("out:allow any")
|
||||
defaultLocalInRule := LocalSecurityRule{ExternalId: DEFAULT_LOCAL_RULE_ID}
|
||||
defaultLocalInRule.SecurityRule = *secrules.MustParseSecurityRule("in:deny any")
|
||||
defaultLocalOutRule := LocalSecurityRule{ExternalId: DEFAULT_LOCAL_RULE_ID}
|
||||
defaultLocalOutRule.SecurityRule = *secrules.MustParseSecurityRule("out:allow any")
|
||||
|
||||
inRules = AddDefaultRule(inRules, defaultInRule, defaultLocalInRule.String(), order, minPriority, maxPriority, onlyAllowRules)
|
||||
outRules = AddDefaultRule(outRules, defaultOutRule, defaultLocalOutRule.String(), order, minPriority, maxPriority, onlyAllowRules)
|
||||
|
||||
defaultInEquals, defaultOutEquals := true, true
|
||||
if defaultLocalInRule.String() != defaultInRule.String() {
|
||||
localInRules = append(localInRules, defaultLocalInRule)
|
||||
defaultInEquals = false
|
||||
}
|
||||
if defaultLocalOutRule.String() != defaultOutRule.String() {
|
||||
localOutRules = append(localOutRules, defaultLocalOutRule)
|
||||
defaultOutEquals = false
|
||||
}
|
||||
|
||||
sort.Sort(localInRules)
|
||||
@@ -150,9 +194,26 @@ func CompareRules(
|
||||
|
||||
localInAllowList := localInRules.AllowList()
|
||||
localOutAllowList := localOutRules.AllowList()
|
||||
_localInRules := LocalSecurityRuleSet{}
|
||||
for i := range localInAllowList {
|
||||
rule := LocalSecurityRule{}
|
||||
rule.SecurityRule = localInAllowList[i]
|
||||
_localInRules = append(_localInRules, rule)
|
||||
}
|
||||
_localOutRules := LocalSecurityRuleSet{}
|
||||
for i := range localOutAllowList {
|
||||
rule := LocalSecurityRule{}
|
||||
rule.SecurityRule = localOutAllowList[i]
|
||||
_localOutRules = append(_localOutRules, rule)
|
||||
}
|
||||
if onlyAllowRules {
|
||||
localInRules = localInAllowList
|
||||
localOutRules = localOutAllowList
|
||||
localOutRules, localInRules = _localOutRules, _localInRules
|
||||
}
|
||||
if len(_localInRules) < len(localInRules) {
|
||||
localInRules = _localInRules
|
||||
}
|
||||
if len(_localOutRules) < len(localOutRules) {
|
||||
localOutRules = _localOutRules
|
||||
}
|
||||
|
||||
SortSecurityRule(inRules, order, onlyAllowRules)
|
||||
@@ -199,7 +260,7 @@ func CompareRules(
|
||||
return init
|
||||
}
|
||||
|
||||
var compare = func(localRules secrules.SecurityRuleSet, remoteRules SecurityRuleSet) (common, add, del []SecurityRule) {
|
||||
var compare = func(localRules LocalSecurityRuleSet, remoteRules SecurityRuleSet) (common, add, del []SecurityRule) {
|
||||
i, j, inc, prePriority := 0, 0, 1, 0
|
||||
for i < len(localRules) || j < len(remoteRules) {
|
||||
if i < len(localRules) && j < len(remoteRules) {
|
||||
@@ -214,7 +275,11 @@ func CompareRules(
|
||||
if remoteRules[j].ExternalId == DEFAULT_CLOUD_RULE_ID {
|
||||
remoteRules[j].Priority = addPriority(remoteRules[j].Priority, order, 1, minPriority, maxPriority, onlyAllowRules)
|
||||
}
|
||||
common = append(common, remoteRules[j])
|
||||
if localRules[i].ExternalId != DEFAULT_LOCAL_RULE_ID ||
|
||||
(localRules[i].Direction == secrules.DIR_IN && !defaultInEquals) ||
|
||||
(localRules[i].Direction == secrules.DIR_OUT && !defaultOutEquals) {
|
||||
common = append(common, remoteRules[j])
|
||||
}
|
||||
i++
|
||||
j++
|
||||
} else if cmp < 0 {
|
||||
@@ -225,7 +290,11 @@ func CompareRules(
|
||||
} else {
|
||||
initPriority := getInitPriority(prePriority, minPriority, maxPriority)
|
||||
localRules[i].Priority = addPriority(initPriority, order, inc, minPriority, maxPriority, onlyAllowRules)
|
||||
add = append(add, SecurityRule{SecurityRule: localRules[i]})
|
||||
if localRules[i].ExternalId != DEFAULT_LOCAL_RULE_ID ||
|
||||
(localRules[i].Direction == secrules.DIR_IN && !defaultInEquals) ||
|
||||
(localRules[i].Direction == secrules.DIR_OUT && !defaultOutEquals) {
|
||||
add = append(add, SecurityRule{SecurityRule: localRules[i].SecurityRule})
|
||||
}
|
||||
i++
|
||||
inc++
|
||||
}
|
||||
@@ -241,7 +310,11 @@ func CompareRules(
|
||||
}
|
||||
initPriority = getInitPriority(initPriority, minPriority, maxPriority) // 若是初始添加规则,尽量以中间为节点,避免仅出现天地规则
|
||||
localRules[i].Priority = addPriority(initPriority, order, inc, minPriority, maxPriority, onlyAllowRules)
|
||||
add = append(add, SecurityRule{SecurityRule: localRules[i]})
|
||||
if localRules[i].ExternalId != DEFAULT_LOCAL_RULE_ID ||
|
||||
(localRules[i].Direction == secrules.DIR_IN && !defaultInEquals) ||
|
||||
(localRules[i].Direction == secrules.DIR_OUT && !defaultOutEquals) {
|
||||
add = append(add, SecurityRule{SecurityRule: localRules[i].SecurityRule})
|
||||
}
|
||||
i++
|
||||
inc++
|
||||
}
|
||||
@@ -250,17 +323,17 @@ func CompareRules(
|
||||
}
|
||||
|
||||
type rulePair struct {
|
||||
localRules []secrules.SecurityRule
|
||||
localRules LocalSecurityRuleSet
|
||||
remoteRules []SecurityRule
|
||||
protocol string
|
||||
}
|
||||
|
||||
var splitRules = func(localRules []secrules.SecurityRule, remoteRules []SecurityRule) []rulePair {
|
||||
var splitRules = func(localRules LocalSecurityRuleSet, remoteRules []SecurityRule) []rulePair {
|
||||
rules := map[string]rulePair{}
|
||||
for _, r := range localRules {
|
||||
pair, ok := rules[r.Protocol]
|
||||
if !ok {
|
||||
pair = rulePair{localRules: []secrules.SecurityRule{}, remoteRules: []SecurityRule{}, protocol: r.Protocol}
|
||||
pair = rulePair{localRules: LocalSecurityRuleSet{}, remoteRules: []SecurityRule{}, protocol: r.Protocol}
|
||||
}
|
||||
pair.localRules = append(pair.localRules, r)
|
||||
rules[r.Protocol] = pair
|
||||
@@ -269,7 +342,7 @@ func CompareRules(
|
||||
for _, r := range remoteRules {
|
||||
pair, ok := rules[r.Protocol]
|
||||
if !ok {
|
||||
pair = rulePair{localRules: []secrules.SecurityRule{}, remoteRules: []SecurityRule{}, protocol: r.Protocol}
|
||||
pair = rulePair{localRules: LocalSecurityRuleSet{}, remoteRules: []SecurityRule{}, protocol: r.Protocol}
|
||||
}
|
||||
pair.remoteRules = append(pair.remoteRules, r)
|
||||
rules[r.Protocol] = pair
|
||||
@@ -282,7 +355,7 @@ func CompareRules(
|
||||
return ret
|
||||
}
|
||||
|
||||
var compareRules = func(localRules []secrules.SecurityRule, remoteRules []SecurityRule) (common, add, dels []SecurityRule) {
|
||||
var compareRules = func(localRules LocalSecurityRuleSet, remoteRules []SecurityRule) (common, add, dels []SecurityRule) {
|
||||
pairs := splitRules(localRules, remoteRules)
|
||||
for _, r := range pairs {
|
||||
_common, _add, _dels := compare(r.localRules, r.remoteRules)
|
||||
|
||||
@@ -77,8 +77,8 @@ func (self *SAliyunGuestDriver) GetStorageTypes() []string {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SAliyunGuestDriver) ChooseHostStorage(host *models.SHost, backend string, storageIds []string) *models.SStorage {
|
||||
return self.chooseHostStorage(self, host, backend, storageIds)
|
||||
func (self *SAliyunGuestDriver) ChooseHostStorage(host *models.SHost, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
|
||||
return self.chooseHostStorage(self, host, diskConfig.Backend, storageIds), nil
|
||||
}
|
||||
|
||||
func (self *SAliyunGuestDriver) GetDetachDiskStatus() ([]string, error) {
|
||||
|
||||
@@ -131,8 +131,8 @@ func (self *SAwsGuestDriver) GetStorageTypes() []string {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SAwsGuestDriver) ChooseHostStorage(host *models.SHost, backend string, storageIds []string) *models.SStorage {
|
||||
return self.chooseHostStorage(self, host, backend, storageIds)
|
||||
func (self *SAwsGuestDriver) ChooseHostStorage(host *models.SHost, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
|
||||
return self.chooseHostStorage(self, host, diskConfig.Backend, storageIds), nil
|
||||
}
|
||||
|
||||
func (self *SAwsGuestDriver) GetDetachDiskStatus() ([]string, error) {
|
||||
|
||||
@@ -79,8 +79,8 @@ func (self *SAzureGuestDriver) GetStorageTypes() []string {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SAzureGuestDriver) ChooseHostStorage(host *models.SHost, backend string, storageIds []string) *models.SStorage {
|
||||
return self.chooseHostStorage(self, host, backend, storageIds)
|
||||
func (self *SAzureGuestDriver) ChooseHostStorage(host *models.SHost, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
|
||||
return self.chooseHostStorage(self, host, diskConfig.Backend, storageIds), nil
|
||||
}
|
||||
|
||||
func (self *SAzureGuestDriver) GetMaxSecurityGroupCount() int {
|
||||
|
||||
@@ -249,15 +249,15 @@ func (self *SBaremetalGuestDriver) GetStorageTypes() []string {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SBaremetalGuestDriver) ChooseHostStorage(host *models.SHost, backend string, storageIds []string) *models.SStorage {
|
||||
func (self *SBaremetalGuestDriver) ChooseHostStorage(host *models.SHost, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
|
||||
if len(storageIds) != 0 {
|
||||
return models.StorageManager.FetchStorageById(storageIds[0])
|
||||
return models.StorageManager.FetchStorageById(storageIds[0]), nil
|
||||
}
|
||||
bs := host.GetBaremetalstorage()
|
||||
if bs == nil {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
return bs.GetStorage()
|
||||
return bs.GetStorage(), nil
|
||||
}
|
||||
|
||||
func (self *SBaremetalGuestDriver) RequestGuestCreateAllDisks(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
@@ -404,8 +404,8 @@ func (self *SBaremetalGuestDriver) ValidateCreateDataOnHost(ctx context.Context,
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (self *SBaremetalGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject {
|
||||
return guest.GetJsonDescAtBaremetal(ctx, host)
|
||||
func (self *SBaremetalGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
|
||||
return guest.GetJsonDescAtBaremetal(ctx, host), nil
|
||||
}
|
||||
|
||||
func (self *SBaremetalGuestDriver) GetGuestVncInfo(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) (*jsonutils.JSONDict, error) {
|
||||
|
||||
@@ -270,6 +270,10 @@ func (self *SBaseGuestDriver) IsSupportEip() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SBaseGuestDriver) IsSupportPublicIp() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SBaseGuestDriver) RequestAssociateEip(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, eip *models.SElasticip, task taskman.ITask) error {
|
||||
return fmt.Errorf("SBaseGuestDriver: Not Implement RequestAssociateEip")
|
||||
}
|
||||
@@ -389,3 +393,8 @@ func (self *SBaseGuestDriver) RequestMigrate(ctx context.Context, guest *models.
|
||||
func (self *SBaseGuestDriver) RequestLiveMigrate(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, task taskman.ITask) error {
|
||||
return fmt.Errorf("Not Implement RequestLiveMigrate")
|
||||
}
|
||||
|
||||
func (self *SBaseGuestDriver) RequestRemoteUpdate(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential, replaceTags bool) error {
|
||||
// nil ops
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -148,8 +148,8 @@ func (self *SContainerDriver) RequestUndeployGuestOnHost(ctx context.Context, gu
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SContainerDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject {
|
||||
return guest.GetJsonDescAtHypervisor(ctx, host)
|
||||
func (self *SContainerDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
|
||||
return guest.GetJsonDescAtHypervisor(ctx, host), nil
|
||||
}
|
||||
|
||||
func (self *SContainerDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
|
||||
|
||||
@@ -79,6 +79,46 @@ func (self *SESXiGuestDriver) GetDefaultSysDiskBackend() string {
|
||||
return api.STORAGE_LOCAL
|
||||
}
|
||||
|
||||
func (self *SESXiGuestDriver) ChooseHostStorage(host *models.SHost, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
|
||||
if !options.Options.LockStorageFromCachedimage || len(diskConfig.ImageId) == 0 {
|
||||
return self.SVirtualizedGuestDriver.ChooseHostStorage(host, diskConfig, storageIds)
|
||||
}
|
||||
var (
|
||||
image *cloudprovider.SImage
|
||||
err error
|
||||
)
|
||||
obj, err := models.CachedimageManager.FetchById(diskConfig.ImageId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to fetch cachedimage %s", diskConfig.ImageId)
|
||||
}
|
||||
cachedimage := obj.(*models.SCachedimage)
|
||||
if len(cachedimage.ExternalId) > 0 || cachedimage.ImageType != cloudprovider.CachedImageTypeSystem {
|
||||
return self.SVirtualizedGuestDriver.ChooseHostStorage(host, diskConfig, storageIds)
|
||||
}
|
||||
storages, err := cachedimage.GetStorages()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to GetStorages of cachedimage %s", diskConfig.ImageId)
|
||||
}
|
||||
if len(storages) == 0 {
|
||||
log.Warningf("there no storage associated with cachedimage %q", image.Id)
|
||||
return self.SVirtualizedGuestDriver.ChooseHostStorage(host, diskConfig, storageIds)
|
||||
}
|
||||
if len(storages) > 1 {
|
||||
log.Warningf("there are multiple storageCache associated with caheimage %q", image.Id)
|
||||
}
|
||||
wantStorageIds := make([]string, len(storages))
|
||||
for i := range wantStorageIds {
|
||||
wantStorageIds[i] = storages[i].GetId()
|
||||
}
|
||||
for i := range wantStorageIds {
|
||||
if utils.IsInStringArray(wantStorageIds[i], storageIds) {
|
||||
log.Infof("use storage %q in where cachedimage %q", wantStorageIds[i], image.Id)
|
||||
return &storages[i], nil
|
||||
}
|
||||
}
|
||||
return self.SVirtualizedGuestDriver.ChooseHostStorage(host, diskConfig, storageIds)
|
||||
}
|
||||
|
||||
func (self *SESXiGuestDriver) GetMinimalSysDiskSizeGb() int {
|
||||
return options.Options.DefaultDiskSizeMB / 1024
|
||||
}
|
||||
@@ -153,49 +193,64 @@ type SEsxiImageInfo struct {
|
||||
StorageCacheHostIp string
|
||||
}
|
||||
|
||||
func (self *SESXiGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject {
|
||||
func (self *SESXiGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
|
||||
desc := guest.GetJsonDescAtHypervisor(ctx, host)
|
||||
// add image_info
|
||||
disks, _ := desc.GetArray("disks")
|
||||
if len(disks) == 0 {
|
||||
return desc
|
||||
return desc, nil
|
||||
}
|
||||
templateId, _ := disks[0].GetString("template_id")
|
||||
if len(templateId) == 0 {
|
||||
return desc
|
||||
return desc, nil
|
||||
}
|
||||
model, err := models.CachedimageManager.FetchById(templateId)
|
||||
if err != nil {
|
||||
log.Errorf("fail to Fetch cachedimage by '%s' in SESXiGuestDriver.GetJsonDescAtHost: %s", templateId, err)
|
||||
return desc
|
||||
return desc, errors.Wrapf(err, "CachedimageManager.FetchById(%s)", templateId)
|
||||
}
|
||||
img := model.(*models.SCachedimage)
|
||||
if img.ImageType != cloudprovider.CachedImageTypeSystem {
|
||||
return desc
|
||||
return desc, nil
|
||||
}
|
||||
sciSubQ := models.StoragecachedimageManager.Query("storagecache_id").Equals("cachedimage_id", templateId).Equals("status", api.CACHED_IMAGE_STATUS_ACTIVE).SubQuery()
|
||||
scQ := models.StoragecacheManager.Query().In("id", sciSubQ)
|
||||
storageCaches := make([]models.SStoragecache, 0, 1)
|
||||
err = db.FetchModelObjects(models.StoragecacheManager, scQ, &storageCaches)
|
||||
if err != nil {
|
||||
log.Errorf("fail to fetch storageCache associated with cacheimage '%s'", templateId)
|
||||
return desc
|
||||
return desc, errors.Wrapf(err, "fetch storageCache associated with cacheimage %s", templateId)
|
||||
}
|
||||
if len(storageCaches) == 0 {
|
||||
log.Errorf("no such storage cache associated with cacheimage '%s'", templateId)
|
||||
return desc
|
||||
return desc, errors.Errorf("no such storage cache associated with cacheimage %s", templateId)
|
||||
}
|
||||
if len(storageCaches) > 1 {
|
||||
log.Errorf("there are multiple storageCache associated with caheimage '%s' ??!!", templateId)
|
||||
log.Warningf("there are multiple storageCache associated with caheimage '%s' ??!!", templateId)
|
||||
}
|
||||
|
||||
var hostIp string
|
||||
storageCacheHost, err := storageCaches[0].GetHost()
|
||||
if err != nil {
|
||||
log.Errorf("fail to GetHost of storageCache %s", storageCaches[0].Id)
|
||||
hostIp = storageCaches[0].ExternalId
|
||||
var storageCacheHost *models.SHost
|
||||
// select storagecacheHost
|
||||
for i := range storageCaches {
|
||||
hosts, err := storageCaches[i].GetHosts()
|
||||
if err != nil {
|
||||
return desc, errors.Wrap(err, "storageCaches.GetHosts")
|
||||
}
|
||||
for i := range hosts {
|
||||
if host.GetId() == hosts[i].GetId() {
|
||||
storageCacheHost = &hosts[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
hostIp = storageCacheHost.AccessIp
|
||||
|
||||
if storageCacheHost == nil {
|
||||
storageCacheHost, err = storageCaches[0].GetHost()
|
||||
if err != nil {
|
||||
return desc, errors.Wrapf(err, "unable to GetHost of storageCache %s", storageCaches[0].Id)
|
||||
}
|
||||
if storageCacheHost == nil {
|
||||
return desc, fmt.Errorf("unable to GetHost of storageCache %s: result is nil", storageCaches[0].Id)
|
||||
}
|
||||
}
|
||||
|
||||
hostIp := storageCacheHost.AccessIp
|
||||
imageInfo := SEsxiImageInfo{
|
||||
ImageType: img.ImageType,
|
||||
ImageExternalId: img.ExternalId,
|
||||
@@ -203,7 +258,7 @@ func (self *SESXiGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mc
|
||||
}
|
||||
dict := disks[0].(*jsonutils.JSONDict)
|
||||
dict.Add(jsonutils.Marshal(imageInfo), "image_info")
|
||||
return desc
|
||||
return desc, nil
|
||||
}
|
||||
|
||||
func (self *SESXiGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
|
||||
|
||||
@@ -77,12 +77,13 @@ func (self *SGoogleGuestDriver) GetStorageTypes() []string {
|
||||
return []string{
|
||||
api.STORAGE_GOOGLE_PD_SSD,
|
||||
api.STORAGE_GOOGLE_PD_STANDARD,
|
||||
api.STORAGE_GOOGLE_PD_BALANCED,
|
||||
api.STORAGE_GOOGLE_LOCAL_SSD,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) ChooseHostStorage(host *models.SHost, backend string, storageIds []string) *models.SStorage {
|
||||
return self.chooseHostStorage(self, host, backend, storageIds)
|
||||
func (self *SGoogleGuestDriver) ChooseHostStorage(host *models.SHost, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
|
||||
return self.chooseHostStorage(self, host, diskConfig.Backend, storageIds), nil
|
||||
}
|
||||
|
||||
func (self *SGoogleGuestDriver) GetGuestInitialStateAfterCreate() string {
|
||||
@@ -113,7 +114,7 @@ func (self *SGoogleGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *m
|
||||
if !utils.IsInStringArray(guest.Status, []string{api.VM_READY, api.VM_RUNNING}) {
|
||||
return fmt.Errorf("Cannot resize disk when guest in status %s", guest.Status)
|
||||
}
|
||||
if !utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_PD_STANDARD}) {
|
||||
if !utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_PD_STANDARD, api.STORAGE_GOOGLE_PD_BALANCED}) {
|
||||
return fmt.Errorf("Cannot resize %s disk", storage.StorageType)
|
||||
}
|
||||
return nil
|
||||
@@ -132,13 +133,15 @@ func (self *SGoogleGuestDriver) ValidateCreateData(ctx context.Context, userCred
|
||||
minGB := -1
|
||||
maxGB := -1
|
||||
switch disk.Backend {
|
||||
case api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_PD_STANDARD:
|
||||
case api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_PD_STANDARD, api.STORAGE_GOOGLE_PD_BALANCED:
|
||||
minGB = 10
|
||||
maxGB = 65536
|
||||
case api.STORAGE_GOOGLE_LOCAL_SSD:
|
||||
minGB = 375
|
||||
maxGB = 375
|
||||
localDisk++
|
||||
default:
|
||||
return nil, httperrors.NewInputParameterError("Unknown google storage type %s", disk.Backend)
|
||||
}
|
||||
if i == 0 && disk.Backend == api.STORAGE_GOOGLE_LOCAL_SSD {
|
||||
return nil, httperrors.NewInputParameterError("System disk does not support %s disk", disk.Backend)
|
||||
|
||||
@@ -67,8 +67,8 @@ func (self *SHuaweiGuestDriver) GetStorageTypes() []string {
|
||||
return []string{api.STORAGE_HUAWEI_SATA, api.STORAGE_HUAWEI_SAS, api.STORAGE_HUAWEI_SSD}
|
||||
}
|
||||
|
||||
func (self *SHuaweiGuestDriver) ChooseHostStorage(host *models.SHost, backend string, storageIds []string) *models.SStorage {
|
||||
return self.chooseHostStorage(self, host, backend, storageIds)
|
||||
func (self *SHuaweiGuestDriver) ChooseHostStorage(host *models.SHost, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
|
||||
return self.chooseHostStorage(self, host, diskConfig.Backend, storageIds), nil
|
||||
}
|
||||
|
||||
func (self *SHuaweiGuestDriver) GetDetachDiskStatus() ([]string, error) {
|
||||
|
||||
@@ -234,8 +234,8 @@ func (self *SKVMGuestDriver) RequestUndeployGuestOnHost(ctx context.Context, gue
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SKVMGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject {
|
||||
return guest.GetJsonDescAtHypervisor(ctx, host)
|
||||
func (self *SKVMGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
|
||||
return guest.GetJsonDescAtHypervisor(ctx, host), nil
|
||||
}
|
||||
|
||||
func (self *SKVMGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
|
||||
@@ -267,7 +267,10 @@ func (self *SKVMGuestDriver) RequestStartOnHost(ctx context.Context, guest *mode
|
||||
header := self.getTaskRequestHeader(task)
|
||||
|
||||
config := jsonutils.NewDict()
|
||||
desc := guest.GetDriver().GetJsonDescAtHost(ctx, userCred, guest, host)
|
||||
desc, err := guest.GetDriver().GetJsonDescAtHost(ctx, userCred, guest, host, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetJsonDescAtHost")
|
||||
}
|
||||
config.Add(desc, "desc")
|
||||
params := task.GetParams()
|
||||
if params.Length() > 0 {
|
||||
@@ -447,7 +450,10 @@ func (self *SKVMGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *mode
|
||||
}
|
||||
|
||||
func (self *SKVMGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
|
||||
desc := guest.GetDriver().GetJsonDescAtHost(ctx, task.GetUserCred(), guest, host)
|
||||
desc, err := guest.GetDriver().GetJsonDescAtHost(ctx, task.GetUserCred(), guest, host, nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetJsonDescAtHost")
|
||||
}
|
||||
body := jsonutils.NewDict()
|
||||
body.Add(desc, "desc")
|
||||
if fw_only, _ := task.GetParams().Bool("fw_only"); fw_only {
|
||||
@@ -455,7 +461,7 @@ func (self *SKVMGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest
|
||||
}
|
||||
url := fmt.Sprintf("%s/servers/%s/sync", host.ManagerUri, guest.Id)
|
||||
header := self.getTaskRequestHeader(task)
|
||||
_, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
|
||||
_, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -496,13 +502,16 @@ func (self *SKVMGuestDriver) RequestRebuildRootDisk(ctx context.Context, guest *
|
||||
|
||||
func (self *SKVMGuestDriver) RequestSyncToBackup(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
host := guest.GetHost()
|
||||
desc := guest.GetDriver().GetJsonDescAtHost(ctx, task.GetUserCred(), guest, host)
|
||||
desc, err := guest.GetDriver().GetJsonDescAtHost(ctx, task.GetUserCred(), guest, host, nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetJsonDescAtHost")
|
||||
}
|
||||
body := jsonutils.NewDict()
|
||||
body.Add(desc, "desc")
|
||||
body.Set("backup_nbd_server_uri", jsonutils.NewString(guest.GetMetadata("backup_nbd_server_uri", task.GetUserCred())))
|
||||
url := fmt.Sprintf("%s/servers/%s/drive-mirror", host.ManagerUri, guest.Id)
|
||||
header := self.getTaskRequestHeader(task)
|
||||
_, _, err := httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
|
||||
_, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -54,12 +54,15 @@ func (d SManagedVirtualizedGuestDriver) DoScheduleMemoryFilter() bool { return f
|
||||
|
||||
func (d SManagedVirtualizedGuestDriver) DoScheduleStorageFilter() bool { return false }
|
||||
|
||||
func (self *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost) jsonutils.JSONObject {
|
||||
func (self *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
|
||||
config := cloudprovider.SManagedVMCreateConfig{}
|
||||
config.Name = guest.Name
|
||||
config.Cpu = int(guest.VcpuCount)
|
||||
config.MemoryMB = guest.VmemSize
|
||||
config.Description = guest.Description
|
||||
if params != nil {
|
||||
params.Unmarshal(&config.SPublicIpInfo)
|
||||
}
|
||||
|
||||
config.InstanceType = guest.InstanceType
|
||||
|
||||
@@ -117,7 +120,7 @@ func (self *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Contex
|
||||
if guest.BillingType == billing_api.BILLING_TYPE_PREPAID {
|
||||
bc, err := billing.ParseBillingCycle(guest.BillingCycle)
|
||||
if err != nil {
|
||||
log.Errorf("fail to parse billing cycle %s: %s", guest.BillingCycle, err)
|
||||
return nil, errors.Wrapf(err, "ParseBillingCycle(%s)", guest.BillingCycle)
|
||||
}
|
||||
if bc.IsValid() {
|
||||
bc.AutoRenew = guest.AutoRenew
|
||||
@@ -125,7 +128,7 @@ func (self *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Contex
|
||||
}
|
||||
}
|
||||
|
||||
return jsonutils.Marshal(&config)
|
||||
return jsonutils.Marshal(&config), nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizedGuestDriver) RequestGuestCreateAllDisks(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
|
||||
@@ -293,16 +296,18 @@ func (self *SManagedVirtualizedGuestDriver) RequestStartOnHost(ctx context.Conte
|
||||
func (self *SManagedVirtualizedGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
|
||||
config, err := guest.GetDeployConfigOnHost(ctx, task.GetUserCred(), host, task.GetParams())
|
||||
if err != nil {
|
||||
log.Errorf("GetDeployConfigOnHost error: %v", err)
|
||||
return err
|
||||
return errors.Wrapf(err, "GetDeployConfigOnHost")
|
||||
}
|
||||
log.Debugf("RequestDeployGuestOnHost: %s", config)
|
||||
|
||||
desc := cloudprovider.SManagedVMCreateConfig{}
|
||||
if err := desc.GetConfig(config); err != nil {
|
||||
return err
|
||||
err = desc.GetConfig(config)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "desc.GetConfig")
|
||||
}
|
||||
|
||||
desc.Tags, _ = guest.GetAllUserMetadata()
|
||||
|
||||
//创建并同步安全组规则
|
||||
{
|
||||
vpc, err := guest.GetVpc()
|
||||
@@ -447,10 +452,6 @@ func (self *SManagedVirtualizedGuestDriver) RemoteDeployGuestForCreate(ctx conte
|
||||
}
|
||||
|
||||
db.SetExternalId(guest, userCred, iVM.GetGlobalId())
|
||||
err = iVM.SetSecurityGroups(desc.ExternalSecgroupIds)
|
||||
if err != nil {
|
||||
log.Errorf("failed to set multi secgroup for instance %s error: %v", guest.Name, err)
|
||||
}
|
||||
|
||||
if hostId := iVM.GetIHostId(); len(hostId) > 0 {
|
||||
host, err := db.FetchByExternalIdAndManagerId(models.HostManager, hostId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
|
||||
@@ -482,8 +483,12 @@ func (self *SManagedVirtualizedGuestDriver) RemoteDeployGuestForCreate(ctx conte
|
||||
|
||||
iVM, err = ihost.GetIVMById(iVM.GetGlobalId())
|
||||
if err != nil {
|
||||
log.Errorf("cannot find vm %s", err)
|
||||
return nil, err
|
||||
return nil, errors.Wrapf(err, "GetIVMById(%s)", iVM.GetGlobalId())
|
||||
}
|
||||
|
||||
err = iVM.SetSecurityGroups(desc.ExternalSecgroupIds)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "SetSecurityGroups")
|
||||
}
|
||||
|
||||
ret, expect := 0, len(desc.DataDisks)+1
|
||||
@@ -1210,3 +1215,30 @@ func (self *SManagedVirtualizedGuestDriver) RequestSetAutoRenewInstance(ctx cont
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SManagedVirtualizedGuestDriver) RequestRemoteUpdate(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential, replaceTags bool) error {
|
||||
// nil ops
|
||||
iVM, err := guest.GetIVM()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "guest.GetIVM")
|
||||
}
|
||||
tags, err := guest.GetAllUserMetadata()
|
||||
if err != nil {
|
||||
log.Errorf("GetAllUserMetadata fail %s", err)
|
||||
} else {
|
||||
err := iVM.SetMetadata(tags, replaceTags)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "iVM.SetMetadata")
|
||||
}
|
||||
// sync back cloud metadata
|
||||
err = models.SyncVirtualResourceMetadata(ctx, userCred, guest, iVM)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "syncVirtualResourceMetadata")
|
||||
}
|
||||
}
|
||||
err = iVM.UpdateVM(ctx, guest.Name)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "iVM.UpdateVM")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -91,8 +91,8 @@ func (self *SOpenStackGuestDriver) GetStorageTypes() []string {
|
||||
return storages
|
||||
}
|
||||
|
||||
func (self *SOpenStackGuestDriver) ChooseHostStorage(host *models.SHost, backend string, storageIds []string) *models.SStorage {
|
||||
return self.chooseHostStorage(self, host, backend, storageIds)
|
||||
func (self *SOpenStackGuestDriver) ChooseHostStorage(host *models.SHost, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
|
||||
return self.chooseHostStorage(self, host, diskConfig.Backend, storageIds), nil
|
||||
}
|
||||
|
||||
func (self *SOpenStackGuestDriver) GetDetachDiskStatus() ([]string, error) {
|
||||
|
||||
@@ -75,8 +75,8 @@ func (self *SQcloudGuestDriver) GetStorageTypes() []string {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SQcloudGuestDriver) ChooseHostStorage(host *models.SHost, backend string, storageIds []string) *models.SStorage {
|
||||
return self.chooseHostStorage(self, host, backend, storageIds)
|
||||
func (self *SQcloudGuestDriver) ChooseHostStorage(host *models.SHost, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
|
||||
return self.chooseHostStorage(self, host, diskConfig.Backend, storageIds), nil
|
||||
}
|
||||
|
||||
func (self *SQcloudGuestDriver) GetDetachDiskStatus() ([]string, error) {
|
||||
@@ -259,6 +259,10 @@ func (self *SQcloudGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SQcloudGuestDriver) IsSupportPublicIp() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SQcloudGuestDriver) IsSupportPublicipToEip() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -174,11 +174,11 @@ func (self *SVirtualizedGuestDriver) GetStorageTypes() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SVirtualizedGuestDriver) ChooseHostStorage(host *models.SHost, backend string, storageIds []string) *models.SStorage {
|
||||
func (self *SVirtualizedGuestDriver) ChooseHostStorage(host *models.SHost, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
|
||||
if len(storageIds) == 0 {
|
||||
return host.GetLeastUsedStorage(backend)
|
||||
return host.GetLeastUsedStorage(diskConfig.Backend), nil
|
||||
}
|
||||
return models.StorageManager.FetchStorageById(storageIds[0])
|
||||
return models.StorageManager.FetchStorageById(storageIds[0]), nil
|
||||
}
|
||||
|
||||
func (self *SVirtualizedGuestDriver) RequestGuestCreateInsertIso(ctx context.Context, imageId string, guest *models.SGuest, task taskman.ITask) error {
|
||||
|
||||
@@ -85,8 +85,8 @@ func (self *SZStackGuestDriver) GetMaxSecurityGroupCount() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (self *SZStackGuestDriver) ChooseHostStorage(host *models.SHost, backend string, storageIds []string) *models.SStorage {
|
||||
return self.chooseHostStorage(self, host, backend, storageIds)
|
||||
func (self *SZStackGuestDriver) ChooseHostStorage(host *models.SHost, diskConfig *api.DiskConfig, storageIds []string) (*models.SStorage, error) {
|
||||
return self.chooseHostStorage(self, host, diskConfig.Backend, storageIds), nil
|
||||
}
|
||||
|
||||
func (self *SZStackGuestDriver) GetDetachDiskStatus() ([]string, error) {
|
||||
|
||||
@@ -48,7 +48,7 @@ func (self *SGoogleHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb
|
||||
minGB := 10
|
||||
maxGB := -1
|
||||
switch storage.StorageType {
|
||||
case api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_PD_STANDARD:
|
||||
case api.STORAGE_GOOGLE_PD_SSD, api.STORAGE_GOOGLE_PD_STANDARD, api.STORAGE_GOOGLE_PD_BALANCED:
|
||||
maxGB = 65536
|
||||
default:
|
||||
return fmt.Errorf("Not support resize %s disk", storage.StorageType)
|
||||
|
||||
@@ -716,11 +716,7 @@ func (bucket *SBucket) GetDetailsObjects(
|
||||
}
|
||||
iBucket, err := bucket.GetIBucket()
|
||||
if err != nil {
|
||||
if errors.Cause(err) == httperrors.ErrInvalidStatus {
|
||||
return output, httperrors.NewInvalidStatusError("%s", err)
|
||||
} else {
|
||||
return output, httperrors.NewInternalServerError("fail to find external bucket: %s", err)
|
||||
}
|
||||
return output, errors.Wrap(err, "GetIBucket")
|
||||
}
|
||||
prefix := input.Prefix
|
||||
isRecursive := false
|
||||
@@ -794,11 +790,7 @@ func (bucket *SBucket) PerformTempUrl(
|
||||
|
||||
iBucket, err := bucket.GetIBucket()
|
||||
if err != nil {
|
||||
if errors.Cause(err) == httperrors.ErrInvalidStatus {
|
||||
return output, httperrors.NewInvalidStatusError("%s", err)
|
||||
} else {
|
||||
return output, httperrors.NewInternalServerError("fail to find external bucket: %s", err)
|
||||
}
|
||||
return output, errors.Wrap(err, "GetIBucket")
|
||||
}
|
||||
tmpUrl, err := iBucket.GetTempUrl(method, key, time.Duration(expire)*time.Second)
|
||||
if err != nil {
|
||||
@@ -842,11 +834,7 @@ func (bucket *SBucket) PerformMakedir(
|
||||
|
||||
iBucket, err := bucket.GetIBucket()
|
||||
if err != nil {
|
||||
if errors.Cause(err) == httperrors.ErrInvalidStatus {
|
||||
return nil, httperrors.NewInvalidStatusError("%s", err)
|
||||
} else {
|
||||
return nil, httperrors.NewInternalServerError("fail to find external bucket: %s", err)
|
||||
}
|
||||
return nil, errors.Wrap(err, "GetIBucket")
|
||||
}
|
||||
|
||||
_, err = cloudprovider.GetIObject(iBucket, key+"/")
|
||||
@@ -913,11 +901,7 @@ func (bucket *SBucket) PerformDelete(
|
||||
|
||||
iBucket, err := bucket.GetIBucket()
|
||||
if err != nil {
|
||||
if errors.Cause(err) == httperrors.ErrInvalidStatus {
|
||||
return nil, httperrors.NewInvalidStatusError("%s", err)
|
||||
} else {
|
||||
return nil, httperrors.NewInternalServerError("fail to find external bucket: %s", err)
|
||||
}
|
||||
return nil, errors.Wrap(err, "GetIBucket")
|
||||
}
|
||||
ok := jsonutils.NewDict()
|
||||
results := modulebase.BatchDo(keyStrs, func(key string) (jsonutils.JSONObject, error) {
|
||||
@@ -977,11 +961,7 @@ func (bucket *SBucket) PerformUpload(
|
||||
|
||||
iBucket, err := bucket.GetIBucket()
|
||||
if err != nil {
|
||||
if errors.Cause(err) == httperrors.ErrInvalidStatus {
|
||||
return nil, httperrors.NewInvalidStatusError("%s", err)
|
||||
} else {
|
||||
return nil, httperrors.NewInternalServerError("fail to find external bucket: %s", err)
|
||||
}
|
||||
return nil, errors.Wrap(err, "GetIBucket")
|
||||
}
|
||||
|
||||
meta := cloudprovider.FetchMetaFromHttpHeader(cloudprovider.META_HEADER_PREFIX, appParams.Request.Header)
|
||||
@@ -1185,11 +1165,7 @@ func (bucket *SBucket) PerformSync(
|
||||
|
||||
iBucket, err := bucket.GetIBucket()
|
||||
if err != nil {
|
||||
if errors.Cause(err) == httperrors.ErrInvalidStatus {
|
||||
return nil, httperrors.NewInvalidStatusError("%s", err)
|
||||
} else {
|
||||
return nil, httperrors.NewInternalServerError("fail to find external bucket: %s", err)
|
||||
}
|
||||
return nil, errors.Wrap(err, "GetIBucket")
|
||||
}
|
||||
|
||||
err = bucket.syncWithCloudBucket(ctx, userCred, iBucket, nil, statsOnly)
|
||||
@@ -1233,11 +1209,7 @@ func (bucket *SBucket) GetDetailsAcl(
|
||||
}
|
||||
iBucket, err := bucket.GetIBucket()
|
||||
if err != nil {
|
||||
if errors.Cause(err) == httperrors.ErrInvalidStatus {
|
||||
return output, httperrors.NewInvalidStatusError("%s", err)
|
||||
} else {
|
||||
return output, httperrors.NewInternalServerError("fail to find external bucket: %s", err)
|
||||
}
|
||||
return output, errors.Wrap(err, "GetIBucket")
|
||||
}
|
||||
objKey := input.Key
|
||||
var acl cloudprovider.TBucketACLType
|
||||
@@ -1321,11 +1293,7 @@ func (bucket *SBucket) PerformLimit(
|
||||
|
||||
iBucket, err := bucket.GetIBucket()
|
||||
if err != nil {
|
||||
if errors.Cause(err) == httperrors.ErrInvalidStatus {
|
||||
return nil, httperrors.NewInvalidStatusError("%s", err)
|
||||
} else {
|
||||
return nil, httperrors.NewInternalServerError("fail to find external bucket: %s", err)
|
||||
}
|
||||
return nil, errors.Wrap(err, "GetIBucket")
|
||||
}
|
||||
|
||||
err = iBucket.SetLimit(limit)
|
||||
@@ -1438,11 +1406,7 @@ func (bucket *SBucket) processObjectsActionInput(input api.BucketObjectsActionIn
|
||||
}
|
||||
iBucket, err := bucket.GetIBucket()
|
||||
if err != nil {
|
||||
if errors.Cause(err) == httperrors.ErrInvalidStatus {
|
||||
return nil, nil, httperrors.NewInvalidStatusError("%s", err)
|
||||
} else {
|
||||
return nil, nil, httperrors.NewInternalServerError("fail to find external bucket: %s", err)
|
||||
}
|
||||
return nil, nil, errors.Wrap(err, "GetIBucket")
|
||||
}
|
||||
objects := make([]cloudprovider.ICloudObject, 0)
|
||||
for _, key := range input.Key {
|
||||
|
||||
@@ -272,6 +272,18 @@ func (manager *SCachedimageManager) cacheGlanceImageInfo(ctx context.Context, us
|
||||
}
|
||||
}
|
||||
|
||||
func (image *SCachedimage) GetStorages() ([]SStorage, error) {
|
||||
sq := StorageManager.Query()
|
||||
storagecacheimageSubq := StoragecachedimageManager.Query("storagecache_id").Equals("cachedimage_id", image.GetId()).SubQuery()
|
||||
sq.Join(storagecacheimageSubq, sqlchemy.Equals(sq.Field("storagecache_id"), storagecacheimageSubq.Field("storagecache_id")))
|
||||
storages := make([]SStorage, 0, 1)
|
||||
err := db.FetchModelObjects(StorageManager, sq, &storages)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "FetchModelObjects")
|
||||
}
|
||||
return storages, nil
|
||||
}
|
||||
|
||||
func (manager *SCachedimageManager) GetImageById(ctx context.Context, userCred mcclient.TokenCredential, imageId string, refresh bool) (*cloudprovider.SImage, error) {
|
||||
imgObj, _ := manager.FetchById(imageId)
|
||||
if imgObj != nil {
|
||||
@@ -472,7 +484,11 @@ func (self *SCachedimage) canDeleteLastCache() bool {
|
||||
|
||||
func (self *SCachedimage) syncWithCloudImage(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, image cloudprovider.ICloudImage, managerId string) error {
|
||||
diff, err := db.UpdateWithLock(ctx, self, func() error {
|
||||
self.Name = image.GetName()
|
||||
newName, err := db.GenerateAlterName(self, image.GetName())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GenerateAlterName")
|
||||
}
|
||||
self.Name = newName
|
||||
self.Size = image.GetSizeByte()
|
||||
self.ExternalId = image.GetGlobalId()
|
||||
self.ImageType = image.GetImageType()
|
||||
@@ -668,20 +684,6 @@ func (manager *SCachedimageManager) ListItemFilter(
|
||||
return nil, errors.Wrapf(err, "SSharableBaseResourceManager.ListItemFilter")
|
||||
}
|
||||
|
||||
q, err = managedResourceFilterByAccount(q, query.ManagedResourceListInput, "id", func() *sqlchemy.SQuery {
|
||||
cachedImages := CachedimageManager.Query().SubQuery()
|
||||
storagecachedImages := StoragecachedimageManager.Query().SubQuery()
|
||||
storageCaches := StoragecacheManager.Query().SubQuery()
|
||||
|
||||
subq := cachedImages.Query(cachedImages.Field("id"))
|
||||
subq = subq.Join(storagecachedImages, sqlchemy.Equals(cachedImages.Field("id"), storagecachedImages.Field("cachedimage_id")))
|
||||
subq = subq.Join(storageCaches, sqlchemy.Equals(storagecachedImages.Field("storagecache_id"), storageCaches.Field("id")))
|
||||
return subq
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "managedResourceFilterByAccount")
|
||||
}
|
||||
|
||||
q, err = manager.SSharableVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.SharableVirtualResourceListInput)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ListItemFilter")
|
||||
@@ -692,40 +694,74 @@ func (manager *SCachedimageManager) ListItemFilter(
|
||||
return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
|
||||
}
|
||||
|
||||
q, err = managedResourceFilterByRegion(q, query.RegionalFilterListInput, "id", func() *sqlchemy.SQuery {
|
||||
{
|
||||
var idFilter bool
|
||||
storagecachedImages := StoragecachedimageManager.Query().SubQuery()
|
||||
storageCaches := StoragecacheManager.Query().SubQuery()
|
||||
storages := StorageManager.Query().SubQuery()
|
||||
var storages *sqlchemy.SSubQuery
|
||||
|
||||
if query.Valid == nil {
|
||||
storages = StorageManager.Query().SubQuery()
|
||||
} else if *query.Valid {
|
||||
idFilter = true
|
||||
storages = StorageManager.Query().In("status", []string{api.STORAGE_ENABLED, api.STORAGE_ONLINE}).IsTrue("enabled").SubQuery()
|
||||
} else {
|
||||
idFilter = true
|
||||
stroage := StorageManager.Query()
|
||||
storages = stroage.Filter(sqlchemy.OR(sqlchemy.NotIn(stroage.Field("status"), []string{}), sqlchemy.IsFalse(stroage.Field("enabled")))).SubQuery()
|
||||
}
|
||||
zones := ZoneManager.Query().SubQuery()
|
||||
|
||||
subq := storagecachedImages.Query(storagecachedImages.Field("cachedimage_id"))
|
||||
subq = subq.Join(storageCaches, sqlchemy.Equals(storagecachedImages.Field("storagecache_id"), storageCaches.Field("id")))
|
||||
subq = subq.Join(storages, sqlchemy.Equals(storageCaches.Field("id"), storages.Field("storagecache_id")))
|
||||
subq = subq.Join(zones, sqlchemy.Equals(storages.Field("zone_id"), zones.Field("id")))
|
||||
subq = subq.Filter(sqlchemy.Equals(storagecachedImages.Field("status"), api.CACHED_IMAGE_STATUS_ACTIVE))
|
||||
return subq
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "managedResourceFilterByRegion")
|
||||
}
|
||||
|
||||
q, err = managedResourceFilterByZone(q, query.ZonalFilterListInput, "id", func() *sqlchemy.SQuery {
|
||||
storagecachedImages := StoragecachedimageManager.Query().SubQuery()
|
||||
storageCaches := StoragecacheManager.Query().SubQuery()
|
||||
storages := StorageManager.Query().SubQuery()
|
||||
|
||||
subq := storagecachedImages.Query(storagecachedImages.Field("cachedimage_id"))
|
||||
subq = subq.Join(storageCaches, sqlchemy.Equals(storagecachedImages.Field("storagecache_id"), storageCaches.Field("id")))
|
||||
subq = subq.Join(storages, sqlchemy.Equals(storageCaches.Field("id"), storages.Field("storagecache_id")))
|
||||
if len(query.HostSchedtagId) > 0 {
|
||||
idFilter = true
|
||||
schedTagObj, err := SchedtagManager.FetchByIdOrName(userCred, query.HostSchedtagId)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
return nil, errors.Wrapf(httperrors.ErrResourceNotFound, "%s %s", SchedtagManager.Keyword(), query.HostSchedtagId)
|
||||
} else {
|
||||
return nil, errors.Wrap(err, "SchedtagManager.FetchByIdOrName")
|
||||
}
|
||||
}
|
||||
hoststorages := HoststorageManager.Query("host_id", "storage_id").SubQuery()
|
||||
hostschedtags := HostschedtagManager.Query().Equals("schedtag_id", schedTagObj.GetId()).SubQuery()
|
||||
subq = subq.Join(hoststorages, sqlchemy.Equals(hoststorages.Field("storage_id"), storages.Field("id")))
|
||||
subq = subq.Join(hostschedtags, sqlchemy.Equals(hostschedtags.Field("host_id"), hoststorages.Field("host_id")))
|
||||
}
|
||||
subq = subq.Filter(sqlchemy.Equals(storagecachedImages.Field("status"), api.CACHED_IMAGE_STATUS_ACTIVE))
|
||||
return subq
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "managedResourceFilterByZone")
|
||||
|
||||
subq = subq.Snapshot()
|
||||
|
||||
subq, err = managedResourceFilterByAccount(subq, query.ManagedResourceListInput, "", nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "managedResourceFilterByAccount")
|
||||
}
|
||||
|
||||
subq, err = managedResourceFilterByRegion(subq, query.RegionalFilterListInput, "", nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "_managedResourceFilterByRegion")
|
||||
}
|
||||
|
||||
subq, err = managedResourceFilterByZone(subq, query.ZonalFilterListInput, "", nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "_managedResourceFilterByZone")
|
||||
}
|
||||
|
||||
if subq.IsAltered() {
|
||||
idFilter = true
|
||||
}
|
||||
|
||||
if idFilter {
|
||||
q = q.In("id", subq)
|
||||
}
|
||||
}
|
||||
|
||||
if len(query.ImageType) > 0 {
|
||||
q = q.In("image_type", query.ImageType)
|
||||
q = q.Equals("image_type", query.ImageType)
|
||||
}
|
||||
|
||||
return q, nil
|
||||
|
||||
@@ -44,6 +44,7 @@ type SCapabilities struct {
|
||||
DisabledComputeEngineBrands []string `json:",allowempty"`
|
||||
CloudIdBrands []string `json:",allowempty"`
|
||||
DisabledCloudIdBrands []string `json:",allowempty"`
|
||||
PublicIpBrands []string `json:",allowempty"`
|
||||
NetworkManageBrands []string `json:",allowempty"`
|
||||
DisabledNetworkManageBrands []string `json:",allowempty"`
|
||||
ObjectStorageBrands []string `json:",allowempty"`
|
||||
@@ -262,6 +263,7 @@ func getBrands(region *SCloudregion, zone *SZone, domainId string, capa *SCapabi
|
||||
capa.NetworkManageBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_NETWORK)
|
||||
capa.ObjectStorageBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE)
|
||||
capa.CloudIdBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_CLOUDID)
|
||||
capa.PublicIpBrands, _ = CloudaccountManager.getBrandsOfCapability(region, zone, domainId, tristate.True, cloudprovider.CLOUD_CAPABILITY_PUBLIC_IP)
|
||||
|
||||
if utils.IsInStringArray(api.HYPERVISOR_KVM, capa.Hypervisors) || utils.IsInStringArray(api.HYPERVISOR_BAREMETAL, capa.Hypervisors) {
|
||||
capa.Brands = append(capa.Brands, api.ONECLOUD_BRAND_ONECLOUD)
|
||||
|
||||
@@ -364,7 +364,7 @@ func (scm *SCloudaccountManager) PerformPrepareNets(ctx context.Context, userCre
|
||||
if ownerId == nil {
|
||||
ownerId = userCred
|
||||
}
|
||||
input.CloudaccountCreateInput, err = scm.ValidateCreateData(ctx, userCred, ownerId, query, input.CloudaccountCreateInput)
|
||||
input.CloudaccountCreateInput, err = scm.validateCreateData(ctx, userCred, ownerId, query, input.CloudaccountCreateInput)
|
||||
if err != nil {
|
||||
return output, err
|
||||
}
|
||||
@@ -539,13 +539,14 @@ func (scm *SCloudaccountManager) PerformPrepareNets(ctx context.Context, userCre
|
||||
FakeID int
|
||||
IP netutils.IPV4Addr
|
||||
Name string
|
||||
VlanID int32
|
||||
}
|
||||
vms := make([]vm, 0, len(simpleVms))
|
||||
var nip netutils.IPV4Addr
|
||||
guestMap := make(map[int]*api.CAGuestNet)
|
||||
for i := range simpleVms {
|
||||
id := i
|
||||
if len(simpleVms[i].IPs) == 0 {
|
||||
if len(simpleVms[i].IPVlans) == 0 {
|
||||
if _, ok := guestMap[id]; !ok {
|
||||
guestMap[id] = &api.CAGuestNet{
|
||||
Name: simpleVms[i].Name,
|
||||
@@ -553,8 +554,8 @@ func (scm *SCloudaccountManager) PerformPrepareNets(ctx context.Context, userCre
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, ip := range simpleVms[i].IPs {
|
||||
nip, err = netutils.NewIPV4Addr(ip)
|
||||
for _, ipVlan := range simpleVms[i].IPVlans {
|
||||
nip, err = netutils.NewIPV4Addr(ipVlan.IP)
|
||||
if err != nil {
|
||||
return output, err
|
||||
}
|
||||
@@ -562,6 +563,7 @@ func (scm *SCloudaccountManager) PerformPrepareNets(ctx context.Context, userCre
|
||||
FakeID: i,
|
||||
IP: nip,
|
||||
Name: simpleVms[i].Name,
|
||||
VlanID: ipVlan.VlanID,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -584,17 +586,18 @@ func (scm *SCloudaccountManager) PerformPrepareNets(ctx context.Context, userCre
|
||||
return excludeNets[i].GuestIpStart < excludeNets[j].GuestIpStart
|
||||
})
|
||||
|
||||
svNets := make([]netutils.IPV4AddrRange, 0, 5)
|
||||
svNets := make([]api.CASimpleNetConf, 0, 5)
|
||||
var vmi, neti int
|
||||
|
||||
lastEndIp := netutils.IPV4Addr(0)
|
||||
Loop:
|
||||
for neti = 0; neti < len(excludeNets); {
|
||||
if vmi >= len(vms) {
|
||||
break
|
||||
}
|
||||
startIp, _ := netutils.NewIPV4Addr(excludeNets[neti].GuestIpStart)
|
||||
endIp, _ := netutils.NewIPV4Addr(excludeNets[neti].GuestIpEnd)
|
||||
switch {
|
||||
case vms[vmi].IP > endIp:
|
||||
lastEndIp = endIp
|
||||
neti++
|
||||
case vms[vmi].IP >= startIp:
|
||||
for vms[vmi].IP <= endIp {
|
||||
@@ -608,29 +611,21 @@ Loop:
|
||||
guestMap[id].IPNets = append(guestMap[id].IPNets, api.CAIPNet{
|
||||
IP: vms[vmi].IP.String(),
|
||||
SuitableNetwork: excludeNets[neti].Id,
|
||||
VlanID: vms[vmi].VlanID,
|
||||
})
|
||||
vmi += 1
|
||||
if vmi == len(vms) {
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
lastEndIp = endIp
|
||||
neti++
|
||||
default:
|
||||
for vms[vmi].IP < startIp {
|
||||
suggestStartIp := vms[vmi].IP.NetAddr(24) + 1
|
||||
if suggestStartIp <= lastEndIp {
|
||||
suggestStartIp = lastEndIp + 1
|
||||
}
|
||||
if suggestStartIp >= startIp {
|
||||
break
|
||||
}
|
||||
suggestEndIp := suggestStartIp.NetAddr(24) + 255
|
||||
if suggestEndIp >= startIp {
|
||||
suggestEndIp = startIp - 1
|
||||
}
|
||||
svNets = append(svNets, netutils.NewIPV4AddrRange(suggestStartIp, suggestEndIp))
|
||||
for vmi < len(vms) && vms[vmi].IP <= suggestEndIp {
|
||||
suggestStartIp := vms[vmi].IP
|
||||
suggestEndIp := vms[vmi].IP
|
||||
endLimitIp := suggestStartIp.NetAddr(24) + 255
|
||||
vlanId := vms[vmi].VlanID
|
||||
for {
|
||||
id := vms[vmi].FakeID
|
||||
if _, ok := guestMap[id]; !ok {
|
||||
guestMap[id] = &api.CAGuestNet{
|
||||
@@ -639,10 +634,34 @@ Loop:
|
||||
}
|
||||
}
|
||||
guestMap[id].IPNets = append(guestMap[id].IPNets, api.CAIPNet{
|
||||
IP: vms[vmi].IP.String(),
|
||||
IP: vms[vmi].IP.String(),
|
||||
VlanID: vms[vmi].VlanID,
|
||||
})
|
||||
vmi++
|
||||
if vmi == len(vms) {
|
||||
break
|
||||
}
|
||||
if suggestEndIp == endLimitIp {
|
||||
break
|
||||
}
|
||||
if vms[vmi].IP > suggestEndIp+1 {
|
||||
break
|
||||
}
|
||||
if vms[vmi].IP >= startIp {
|
||||
break
|
||||
}
|
||||
if vms[vmi].VlanID != vlanId {
|
||||
break
|
||||
}
|
||||
suggestEndIp = vms[vmi].IP
|
||||
}
|
||||
svNets = append(svNets, api.CASimpleNetConf{
|
||||
GuestIpStart: suggestStartIp.String(),
|
||||
GuestIpEnd: suggestEndIp.String(),
|
||||
VlanID: vlanId,
|
||||
GuestIpMask: 24,
|
||||
GuestGateway: (suggestStartIp.NetAddr(24) + netutils.IPV4Addr(options.Options.DefaultNetworkGatewayAddressEsxi)).String(),
|
||||
})
|
||||
if vmi == len(vms) {
|
||||
break Loop
|
||||
}
|
||||
@@ -651,13 +670,11 @@ Loop:
|
||||
}
|
||||
|
||||
for vmi < len(vms) {
|
||||
suggestStartIp := vms[vmi].IP.NetAddr(24) + 1
|
||||
if suggestStartIp <= lastEndIp {
|
||||
suggestStartIp = lastEndIp + 1
|
||||
}
|
||||
suggestEndIp := suggestStartIp.NetAddr(24) + 255
|
||||
svNets = append(svNets, netutils.NewIPV4AddrRange(suggestStartIp, suggestEndIp))
|
||||
for vmi < len(vms) && vms[vmi].IP <= suggestEndIp {
|
||||
suggestStartIp := vms[vmi].IP
|
||||
endLimitIp := suggestStartIp.NetAddr(24) + 255
|
||||
suggestEndIp := suggestStartIp
|
||||
vlanId := vms[vmi].VlanID
|
||||
for {
|
||||
id := vms[vmi].FakeID
|
||||
if _, ok := guestMap[id]; !ok {
|
||||
guestMap[id] = &api.CAGuestNet{
|
||||
@@ -669,7 +686,27 @@ Loop:
|
||||
IP: vms[vmi].IP.String(),
|
||||
})
|
||||
vmi++
|
||||
if vmi == len(vms) {
|
||||
break
|
||||
}
|
||||
if suggestEndIp == endLimitIp {
|
||||
break
|
||||
}
|
||||
if vms[vmi].IP > suggestEndIp+1 {
|
||||
break
|
||||
}
|
||||
if vms[vmi].VlanID != vlanId {
|
||||
break
|
||||
}
|
||||
suggestEndIp = vms[vmi].IP
|
||||
}
|
||||
svNets = append(svNets, api.CASimpleNetConf{
|
||||
GuestIpStart: suggestStartIp.String(),
|
||||
GuestIpEnd: suggestEndIp.String(),
|
||||
VlanID: vlanId,
|
||||
GuestIpMask: 24,
|
||||
GuestGateway: (suggestStartIp.NetAddr(24) + netutils.IPV4Addr(options.Options.DefaultNetworkGatewayAddressEsxi)).String(),
|
||||
})
|
||||
}
|
||||
|
||||
for _, guest := range guestMap {
|
||||
@@ -679,12 +716,7 @@ Loop:
|
||||
if len(svNets) > 0 {
|
||||
confs := make([]api.CANetConf, len(svNets))
|
||||
for i := range confs {
|
||||
confs[i].CASimpleNetConf = api.CASimpleNetConf{
|
||||
GuestIpStart: svNets[i].StartIp().String(),
|
||||
GuestIpEnd: svNets[i].EndIp().String(),
|
||||
GuestIpMask: 24,
|
||||
GuestGateway: (svNets[i].StartIp().NetAddr(24) + 1).String(),
|
||||
}
|
||||
confs[i].CASimpleNetConf = svNets[i]
|
||||
confs[i].Name = fmt.Sprintf("%s-guest-network-%d", input.Name, i+1)
|
||||
}
|
||||
output.GuestSuggestedNetworks = confs
|
||||
@@ -758,7 +790,7 @@ func (manager *SCloudaccountManager) suggestHostNetworks(ips []netutils.IPV4Addr
|
||||
lastnetAddr = netAddr
|
||||
}
|
||||
|
||||
gatewayIP := consequent[0].NetAddr(mask) + 1
|
||||
gatewayIP := consequent[0].NetAddr(mask) + netutils.IPV4Addr(options.Options.DefaultNetworkGatewayAddressEsxi)
|
||||
ret = append(ret, api.CASimpleNetConf{
|
||||
GuestIpStart: consequent[0].String(),
|
||||
GuestIpEnd: consequent[len(consequent)-1].String(),
|
||||
@@ -767,7 +799,7 @@ func (manager *SCloudaccountManager) suggestHostNetworks(ips []netutils.IPV4Addr
|
||||
})
|
||||
consequent = []netutils.IPV4Addr{ip}
|
||||
}
|
||||
gatewayIp := consequent[0].NetAddr(mask) + 1
|
||||
gatewayIp := consequent[0].NetAddr(mask) + netutils.IPV4Addr(options.Options.DefaultNetworkGatewayAddressEsxi)
|
||||
ret = append(ret, api.CASimpleNetConf{
|
||||
GuestIpStart: consequent[0].String(),
|
||||
GuestIpEnd: consequent[len(consequent)-1].String(),
|
||||
@@ -888,11 +920,42 @@ func (manager *SCloudaccountManager) ValidateCreateData(
|
||||
ownerId mcclient.IIdentityProvider,
|
||||
query jsonutils.JSONObject,
|
||||
input api.CloudaccountCreateInput,
|
||||
) (api.CloudaccountCreateInput, error) {
|
||||
input, err := manager.validateCreateData(ctx, userCred, ownerId, query, input)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "validateCreateData")
|
||||
}
|
||||
|
||||
input.EnabledStatusInfrasResourceBaseCreateInput, err = manager.SEnabledStatusInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusInfrasResourceBaseCreateInput)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ValidateCreateData")
|
||||
}
|
||||
|
||||
quota := &SDomainQuota{
|
||||
SBaseDomainQuotaKeys: quotas.SBaseDomainQuotaKeys{
|
||||
DomainId: ownerId.GetProjectDomainId(),
|
||||
},
|
||||
Cloudaccount: 1,
|
||||
}
|
||||
err = quotas.CheckSetPendingQuota(ctx, userCred, quota)
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "CheckSetPendingQuota")
|
||||
}
|
||||
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (manager *SCloudaccountManager) validateCreateData(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
ownerId mcclient.IIdentityProvider,
|
||||
query jsonutils.JSONObject,
|
||||
input api.CloudaccountCreateInput,
|
||||
) (api.CloudaccountCreateInput, error) {
|
||||
// check domainId
|
||||
err := db.ValidateCreateDomainId(ownerId.GetProjectDomainId())
|
||||
if err != nil {
|
||||
return input, err
|
||||
return input, errors.Wrap(err, "db.ValidateCreateDomainId")
|
||||
}
|
||||
|
||||
if !cloudprovider.IsSupported(input.Provider) {
|
||||
@@ -1003,22 +1066,6 @@ func (manager *SCloudaccountManager) ValidateCreateData(
|
||||
input.SyncIntervalSeconds = options.Options.MinimalSyncIntervalSeconds
|
||||
}
|
||||
|
||||
input.EnabledStatusInfrasResourceBaseCreateInput, err = manager.SEnabledStatusInfrasResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.EnabledStatusInfrasResourceBaseCreateInput)
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
|
||||
quota := &SDomainQuota{
|
||||
SBaseDomainQuotaKeys: quotas.SBaseDomainQuotaKeys{
|
||||
DomainId: ownerId.GetProjectDomainId(),
|
||||
},
|
||||
Cloudaccount: 1,
|
||||
}
|
||||
err = quotas.CheckSetPendingQuota(ctx, userCred, quota)
|
||||
if err != nil {
|
||||
return input, errors.Wrapf(err, "CheckSetPendingQuota")
|
||||
}
|
||||
|
||||
return input, nil
|
||||
}
|
||||
|
||||
@@ -1050,9 +1097,6 @@ func (self *SCloudaccount) CustomizeCreate(ctx context.Context, userCred mcclien
|
||||
}
|
||||
|
||||
func (self *SCloudaccount) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
self.SEnabledStatusInfrasResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
|
||||
self.savePassword(self.Secret)
|
||||
|
||||
quota := &SDomainQuota{
|
||||
SBaseDomainQuotaKeys: quotas.SBaseDomainQuotaKeys{
|
||||
DomainId: ownerId.GetProjectDomainId(),
|
||||
@@ -1063,6 +1107,10 @@ func (self *SCloudaccount) PostCreate(ctx context.Context, userCred mcclient.Tok
|
||||
if err != nil {
|
||||
log.Errorf("CancelPendingUsage fail %s", err)
|
||||
}
|
||||
|
||||
self.SEnabledStatusInfrasResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
|
||||
self.savePassword(self.Secret)
|
||||
|
||||
if self.Enabled.IsTrue() {
|
||||
self.StartSyncCloudProviderInfoTask(ctx, userCred, nil, "")
|
||||
}
|
||||
@@ -1922,7 +1970,7 @@ func (self *SCloudaccount) GetDetailsBalance(ctx context.Context, userCred mccli
|
||||
return nil, httperrors.NewGeneralError(err)
|
||||
}
|
||||
ret := jsonutils.NewDict()
|
||||
ret.Add(jsonutils.NewFloat(balance), "balance")
|
||||
ret.Add(jsonutils.NewFloat64(balance), "balance")
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
)
|
||||
|
||||
func TestSCloudaccount_suggestHostNetworks(t *testing.T) {
|
||||
cases := []struct {
|
||||
in []string
|
||||
want []api.CASimpleNetConf
|
||||
}{
|
||||
{
|
||||
[]string{
|
||||
"10.168.13.234",
|
||||
"10.168.13.235",
|
||||
"10.168.13.233",
|
||||
"10.168.13.222",
|
||||
},
|
||||
[]api.CASimpleNetConf{
|
||||
{
|
||||
GuestIpStart: "10.168.13.222",
|
||||
GuestIpEnd: "10.168.13.222",
|
||||
GuestIpMask: 24,
|
||||
GuestGateway: "10.168.13.1",
|
||||
},
|
||||
{
|
||||
GuestIpStart: "10.168.13.233",
|
||||
GuestIpEnd: "10.168.13.235",
|
||||
GuestIpMask: 24,
|
||||
GuestGateway: "10.168.13.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]string{
|
||||
"10.168.12.254",
|
||||
"10.168.43.1",
|
||||
},
|
||||
[]api.CASimpleNetConf{
|
||||
{
|
||||
GuestIpStart: "10.168.12.254",
|
||||
GuestIpEnd: "10.168.12.254",
|
||||
GuestIpMask: 24,
|
||||
GuestGateway: "10.168.12.1",
|
||||
},
|
||||
{
|
||||
GuestIpStart: "10.168.43.1",
|
||||
GuestIpEnd: "10.168.43.1",
|
||||
GuestIpMask: 24,
|
||||
GuestGateway: "10.168.43.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]string{
|
||||
"10.155.50.103",
|
||||
"10.155.50.101",
|
||||
"10.155.50.102",
|
||||
},
|
||||
[]api.CASimpleNetConf{
|
||||
{
|
||||
GuestIpStart: "10.155.50.101",
|
||||
GuestIpEnd: "10.155.50.103",
|
||||
GuestIpMask: 24,
|
||||
GuestGateway: "10.155.50.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
ins := make([]netutils.IPV4Addr, len(c.in))
|
||||
for i := range ins {
|
||||
ins[i], _ = netutils.NewIPV4Addr(c.in[i])
|
||||
}
|
||||
out := CloudaccountManager.suggestHostNetworks(ins)
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
if out[i].GuestIpStart == out[j].GuestIpStart {
|
||||
return out[i].GuestIpEnd < out[j].GuestIpEnd
|
||||
}
|
||||
return out[i].GuestIpStart < out[j].GuestIpStart
|
||||
})
|
||||
if !reflect.DeepEqual(out, c.want) {
|
||||
t.Fatalf("want: %#v\nreal: %#v", c.want, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSCloudaccount_suggestVMNetwors(t *testing.T) {
|
||||
cases := []struct {
|
||||
in1 []string
|
||||
in2 []struct {
|
||||
startIp string
|
||||
endIp string
|
||||
}
|
||||
want []api.CASimpleNetConf
|
||||
}{
|
||||
{
|
||||
in1: []string{
|
||||
"10.168.222.23",
|
||||
"10.168.222.26",
|
||||
"10.168.222.145",
|
||||
"10.168.222.234",
|
||||
},
|
||||
in2: []struct {
|
||||
startIp string
|
||||
endIp string
|
||||
}{
|
||||
{"10.168.222.45", "10.168.222.120"},
|
||||
{"10.168.222.200", "10.168.222.230"},
|
||||
},
|
||||
want: []api.CASimpleNetConf{
|
||||
{
|
||||
GuestIpStart: "10.168.222.1",
|
||||
GuestIpEnd: "10.168.222.44",
|
||||
GuestIpMask: 24,
|
||||
GuestGateway: "10.168.222.1",
|
||||
},
|
||||
{
|
||||
GuestIpStart: "10.168.222.121",
|
||||
GuestIpEnd: "10.168.222.199",
|
||||
GuestIpMask: 24,
|
||||
GuestGateway: "10.168.222.1",
|
||||
},
|
||||
{
|
||||
GuestIpStart: "10.168.222.231",
|
||||
GuestIpEnd: "10.168.222.254",
|
||||
GuestIpMask: 24,
|
||||
GuestGateway: "10.168.222.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
in1: []string{
|
||||
"10.168.222.23",
|
||||
"10.168.224.178",
|
||||
},
|
||||
in2: []struct {
|
||||
startIp string
|
||||
endIp string
|
||||
}{
|
||||
{"10.168.222.100", "10.168.224.100"},
|
||||
},
|
||||
want: []api.CASimpleNetConf{
|
||||
{
|
||||
GuestIpStart: "10.168.222.1",
|
||||
GuestIpEnd: "10.168.222.99",
|
||||
GuestIpMask: 24,
|
||||
GuestGateway: "10.168.222.1",
|
||||
},
|
||||
{
|
||||
GuestIpStart: "10.168.224.101",
|
||||
GuestIpEnd: "10.168.224.254",
|
||||
GuestIpMask: 24,
|
||||
GuestGateway: "10.168.224.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
ins1 := make([]netutils.IPV4Addr, len(c.in1))
|
||||
ins2 := make([]netutils.IPV4AddrRange, len(c.in2))
|
||||
for i := range ins1 {
|
||||
ins1[i], _ = netutils.NewIPV4Addr(c.in1[i])
|
||||
}
|
||||
for i := range ins2 {
|
||||
ip1, _ := netutils.NewIPV4Addr(c.in2[i].startIp)
|
||||
ip2, _ := netutils.NewIPV4Addr(c.in2[i].endIp)
|
||||
ins2[i] = netutils.NewIPV4AddrRange(ip1, ip2)
|
||||
}
|
||||
out := CloudaccountManager.suggestVMNetwors(ins1, ins2)
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
if out[i].GuestIpStart == out[j].GuestIpStart {
|
||||
return out[i].GuestIpEnd < out[j].GuestIpEnd
|
||||
}
|
||||
return out[i].GuestIpStart < out[j].GuestIpStart
|
||||
})
|
||||
if !reflect.DeepEqual(out, c.want) {
|
||||
t.Fatalf("want: %#v\nreal: %#v", c.want, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user