Compare commits

...

59 Commits

Author SHA1 Message Date
wanyaoqi
eed954c67f fix(region): guestdisk list add auto reset column (#22347) 2025-03-26 15:07:24 +08:00
wanyaoqi
353a212672 fix(monitor): vm_net add bytes and packages measurements (#22343) 2025-03-26 15:06:20 +08:00
Jian Qiu
a23fdd9c3a fix: update vendor 20250325 (#22339)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-03-25 23:53:56 +08:00
Zexi Li
4f30a56981 fix(scheduler,monitor): tls 3des (#22336) 2025-03-25 21:15:24 +08:00
Zexi Li
67f228c520 fix(monitor): wrong index of details (#22333) 2025-03-25 15:31:31 +08:00
Zexi Li
61a166b873 fix(monitor): wrong TriggerTime (#22330) 2025-03-25 11:42:47 +08:00
Zexi Li
821b6e31f5 optmized(monitor): query alertrecord by res_id (#22321) 2025-03-25 10:03:52 +08:00
屈轩
ffd9b638bd fix(region): vendor update (#22327) 2025-03-24 19:49:51 +08:00
Jian Qiu
b02f9b8dee fix: fail to delete sub images of composed guest image (#22282)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-03-19 22:27:47 +08:00
屈轩
afa9b2bd4b fix(region): add post delete hook (#22303) 2025-03-19 10:13:59 +08:00
Zexi Li
2a1b5eda54 optimized(keystone): local sql auth by password (#22306) 2025-03-19 10:11:05 +08:00
屈轩
498363de17 fix(monitor): optimized alert list (#22299) 2025-03-18 19:52:43 +08:00
屈轩
60c987846f fix(region): vendor update (#22292) 2025-03-18 16:15:53 +08:00
wanyaoqi
f03fc0f434 fix(host): virtio nic add fdb record on has sriov nic (#22284) 2025-03-18 15:10:26 +08:00
Zexi Li
1fe13965a4 fix(webconsole): disable 3des cipher suites (#22295) 2025-03-18 13:12:26 +08:00
Zexi Li
5d53daefff feat(monitor): support listing monitor_resource by alert_state (#22288) 2025-03-18 11:52:31 +08:00
wanyaoqi
367462b1d1 fix(host): hotplug mem adaptation for guests no memdev (#22257) 2025-03-16 00:04:45 +08:00
屈轩
dc22730323 fix(region): vendor update (#22278) 2025-03-16 00:03:39 +08:00
屈轩
94442d23d7 fix(region): vendor update (#22275) 2025-03-14 09:58:23 +08:00
屈轩
c736464391 fix(climc): service config bool value (#22271) 2025-03-13 14:00:47 +08:00
屈轩
1fc55a1e43 fix(region): show dump info when worker stuck (#22252) 2025-03-12 16:53:22 +08:00
Zexi Li
dbf0be4364 Merge pull request #22260 from zexi/automated-cherry-pick-of-#22259-upstream-release-3.11.10
Automated cherry pick of #22259: fix(webconsole,monitor): pprof handlers of gorilla mux
2025-03-11 12:27:34 +08:00
Zexi Li
436d467793 fix(webconsole,monitor): pprof handlers of gorilla mux 2025-03-11 11:56:37 +08:00
wanyaoqi
02b47c7cb0 feat(glance,region,host): auto cache glance images to ceph storages (#22249) 2025-03-08 11:04:55 +08:00
Jian Qiu
862120661e fix: auto truncate sql string (#22244)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-03-08 10:46:12 +08:00
wanyaoqi
58a4d2b9bc fix(host): change storage clone disk add encrypt info (#22230) 2025-03-08 09:00:05 +08:00
wanyaoqi
462d944947 fix(host-deployer): set xfs default ftype 1 (#22234) 2025-03-06 11:16:29 +08:00
Zexi Li
c2766a24f3 fix(host): not purge isolated device when detaching it (#22223) 2025-03-05 19:33:14 +08:00
Jian Qiu
74bc8d20cf fix: glance always init s3 client (#22134)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-03-03 20:15:40 +08:00
Jian Qiu
a7e11e26e5 fix: add summary-stats options (#22220)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-03-03 10:10:48 +08:00
Jian Qiu
202170cf1d fix: disk copy to storage inherit classmeta (#22214)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-03-03 10:03:38 +08:00
wanyaoqi
73a9d2cfa0 Automated cherry pick of #22206: Fix/deploy guest add cpu vendor (#22207)
* fix(host-deployer): deploy guest add cpu vendor

* fix(host): host generate passwd use strong ruler
2025-02-28 14:12:35 +08:00
wanyaoqi
1321bb5675 fix(host): shared lvm activte with auto activation option (#22190) 2025-02-27 16:05:19 +08:00
wanyaoqi
17eaed67ea feat(region,host): add guest disk auto reset on shutdown support (#22198) 2025-02-27 16:03:23 +08:00
屈轩
6ffaacb26c fix(esxi-agent): vendor update (#22204) 2025-02-27 14:49:35 +08:00
屈轩
ae3deea643 fix(region): fast sync image (#22186) 2025-02-26 15:18:38 +08:00
屈轩
51d22691f2 fix(region): fix typo (#22183) 2025-02-25 17:12:04 +08:00
屈轩
9876b0b26a fix(region): allow create disk when vm running (#22172) 2025-02-25 12:02:23 +08:00
Zexi Li
6b0c3a2cd5 fix(monitor,webconsole): enable app profiling (#22177) 2025-02-24 19:51:43 +08:00
Jian Qiu
cbb791cc0b fix: remove default values from cmtbound columns (#22166)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-02-24 17:32:58 +08:00
Jian Qiu
c0cff6e33c fix: disable server TLS 1.0, 1.x and 1.2 SHA1 (#22167)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-02-24 17:29:49 +08:00
屈轩
a195d99ade fix(region): 允许不同账号dns zone重名 (#22162) 2025-02-24 11:11:12 +08:00
wanyaoqi
d36bda5226 fix(region): lvm disk path add dev prefix (#22151) 2025-02-21 10:24:16 +08:00
wanyaoqi
2d76337894 fix(host): check has vga use desc dev_type instead of host dev object (#22148) 2025-02-21 10:22:09 +08:00
wanyaoqi
5df0a085a8 fix(host): add check default mem dev exist on hotplug (#22142) 2025-02-21 10:21:28 +08:00
Zexi Li
15da787731 fix(climc): show ip column of action-show command (#22154) 2025-02-21 10:15:23 +08:00
Zexi Li
4bfbba43d1 fix(monitor): always skip_check_series for metric-measurement api (#22146) 2025-02-19 19:57:39 +08:00
Zexi Li
e5fd6dafa7 fix(scheduler): 尽早发现子网ip不足引起的调度失败 (#22132) 2025-02-19 17:03:12 +08:00
屈轩
fe04233e8f fix(climc): support create vm with tags (#22126) 2025-02-19 16:49:59 +08:00
Jian Qiu
5c050d70b1 fix: monitor query close response http client (#22111)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
2025-02-18 18:31:30 +08:00
Zexi Li
90b3e1c1bd fix(monitor): split tags and cloud_tags (#22121) 2025-02-18 18:29:29 +08:00
屈轩
ac3ed49d9a fix(region): avoid panic (#22119) 2025-02-18 15:53:08 +08:00
wanyaoqi
5b718bfb63 Automated cherry pick of #22108: Automated cherry pick of #22085: fix(host): update host-base to v0.7.0 (#22109)
* fix(host): update host-base to v0.7.0

* fix(cronjob): init timezone, timer set location
2025-02-15 00:02:53 +08:00
屈轩
51dc7ada1c fix(region): support change vm billing type (#22104) 2025-02-14 11:33:51 +08:00
屈轩
1e476f9d48 fix(region): skip sync server status time range (#22099) 2025-02-13 15:18:08 +08:00
Zexi Li
39d8acd535 fix(lbagent): telegraf configuration panic (#22096) 2025-02-13 12:29:50 +08:00
屈轩
a02d717ee9 fix(region): 跳过esxi存储找不到的关联宿主机 (#22084) 2025-02-12 12:05:00 +08:00
Zexi Li
074fd501bb fix(monitor): not split resource when notifying (#22079) 2025-02-10 19:50:17 +08:00
wanyaoqi
c7818df1b5 fix(host,host-image): use nbd export images (#22074) 2025-02-10 14:16:25 +08:00
223 changed files with 3370 additions and 1314 deletions

View File

@@ -47,11 +47,6 @@ PKGS := go list ./...
CGO_CFLAGS_ENV = $(shell go env CGO_CFLAGS)
CGO_LDFLAGS_ENV = $(shell go env CGO_LDFLAGS)
ifdef LIBQEMUIO_PATH
X_CGO_CFLAGS := ${CGO_CFLAGS_ENV} -I${LIBQEMUIO_PATH}/src -I${LIBQEMUIO_PATH}/src/include
X_CGO_LDFLAGS := ${CGO_LDFLAGS_ENV} -laio -lqemuio -lpthread -lgnutls -lnettle -L ${LIBQEMUIO_PATH}/src
endif
export GOOS ?= linux
export GO111MODULE:=on
export CGO_CFLAGS = ${X_CGO_CFLAGS}
@@ -63,7 +58,7 @@ ifeq ($(UNAME), Linux)
XARGS_FLAGS = --no-run-if-empty
endif
cmdTargets:=$(filter-out cmd/host-image,$(wildcard cmd/*))
cmdTargets:=$(wildcard cmd/*)
rpmTargets:=$(foreach b,$(patsubst cmd/%,%,$(cmdTargets)),$(if $(shell [ -f "$(CURDIR)/build/$(b)/vars" ] && echo 1),rpm/$(b)))
debTargets:=$(foreach b,$(patsubst cmd/%,%,$(cmdTargets)),$(if $(shell [ -f "$(CURDIR)/build/$(b)/vars" ] && echo 1),deb/$(b)))
@@ -282,7 +277,7 @@ dep:
@echo "$$depDeprecated"
@$(MAKE) mod
RELEASE_BRANCH:=release/3.11
RELEASE_BRANCH:=release/3.11.10
GOPROXY ?= direct
mod:

View File

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

View File

@@ -1,9 +1,6 @@
FROM registry.cn-beijing.aliyuncs.com/yunionio/onecloud-base:v0.3.5-1
RUN mkdir -p /opt/yunion/bin
ENV TZ UTC
ADD ./_output/bin/host-image /opt/yunion/bin/host-image
ADD ./_output/bin/.host-image.bin /opt/yunion/bin/.host-image.bin
ADD ./_output/bin/bundles/host-image /opt/yunion/bin/bundles/host-image
RUN mkdir -p /opt/yunion/bin
ADD ./_output/alpine-build/bin/host-image /opt/yunion/bin/host-image

View File

@@ -7,11 +7,11 @@ COPY . /root/go/src/yunion.io/x/onecloud
WORKDIR /root/go/src/yunion.io/x/onecloud
RUN make cmd/host
FROM registry.cn-beijing.aliyuncs.com/yunionio/host-base:v0.4.0
FROM registry.cn-beijing.aliyuncs.com/yunionio/host-base:v0.7.0
MAINTAINER "Yaoqi Wan wanyaoqi@yunionyun.com"
ENV TZ Asia/Shanghai
ENV TZ UTC
RUN mkdir -p /opt/yunion/bin
COPY --from=build /root/go/src/yunion.io/x/onecloud/_output/bin/host /opt/yunion/bin/host

View File

@@ -68,6 +68,7 @@
border-bottom: 1px solid #d7d7d7;
}
</style>
{{$match_tags_str := .match_tags_str}}
<body>
<h3 class="title">报警提醒</h3>
<table border="0" cellspacing="0" cellpadding="0" class="table">
@@ -110,6 +111,9 @@
<td>平台</td>
<td>指标</td>
<td>触发值</td>
{{if gt (len $match_tags_str) 1}}
<td>标签</td>
{{end}}
</tr>
</thead>
<tbody>
@@ -127,6 +131,9 @@
</td>
<td>{{ $Matche.metric }}</td>
<td>{{ $Matche.value_str }}</td>
{{if gt (len $match_tags_str) 1}}
<td>{{ index $match_tags_str $i }}</td>
{{end}}
</tr>
{{end}}
</tbody>

View File

@@ -68,6 +68,7 @@
border-bottom: 1px solid #d7d7d7;
}
</style>
{{$match_tags_str := .match_tags_str}}
<body>
<h3 class="title">Alert</h3>
<table border="0" cellspacing="0" cellpadding="0" class="table">
@@ -110,6 +111,9 @@
<td>Brand</td>
<td>Metric</td>
<td>Trigger value</td>
{{if gt (len $match_tags_str) 1}}
<td>Tags</td>
{{end}}
</tr>
</thead>
<tbody>
@@ -127,6 +131,9 @@
</td>
<td>{{ $Matche.metric }}</td>
<td>{{ $Matche.value_str }}</td>
{{if gt (len $match_tags_str) 1}}
<td>{{ index $match_tags_str $i }}</td>
{{end}}
</tr>
{{end}}
</tbody>

View File

@@ -138,6 +138,7 @@ func init() {
AutoSnapshot string `help:"enable/disable auto snapshot of disk" choices:"enable|disable"`
DiskType string `help:"Disk type" choices:"data|volume|sys"`
IsSsd *bool `help:"mark disk as ssd" negative:"no-is-ssd"`
AutoReset *bool `help:"Enable auto reset disk after geust shutdown"`
}
R(&DiskUpdateOptions{}, "disk-update", "Update property of a virtual disk", func(s *mcclient.ClientSession, args *DiskUpdateOptions) error {
params := jsonutils.NewDict()
@@ -171,6 +172,10 @@ func init() {
params.Add(jsonutils.JSONFalse, "is_ssd")
}
}
if args.AutoReset != nil {
params.Add(jsonutils.NewBool(*args.AutoReset), "auto_reset")
}
if params.Size() == 0 {
return InvalidUpdateError()
}

View File

@@ -26,4 +26,5 @@ func init() {
cmd.Update(&compute.DeviceUpdateOptions{})
cmd.Show(&compute.DeviceShowOptions{})
cmd.BatchDelete(&compute.DeviceDeleteOptions{})
cmd.BatchPerform("purge", &compute.DevicePurgeOptions{})
}

View File

@@ -51,6 +51,7 @@ func init() {
cmd.BatchPut(new(options.ServerUpdateOptions))
cmd.GetMetadata(new(options.ServerIdOptions))
cmd.Perform("clone", new(options.ServerCloneOptions))
cmd.Perform("change-billing-type", new(options.ServerChangeBillingTypeOptions))
cmd.BatchPerform("start", new(options.ServerStartOptions))
cmd.BatchPerform("syncstatus", new(options.ServerIdsOptions))
cmd.BatchPerform("sync", new(options.ServerIdsOptions))

View File

@@ -211,7 +211,13 @@ func init() {
}
key := strings.TrimSpace(c[:pos])
value := strings.TrimSpace(c[pos+1:])
config.Add(jsonutils.NewString(value), "config", "default", key)
var v jsonutils.JSONObject
if value == "true" || value == "false" {
v = jsonutils.NewBool(value == "true")
} else {
v = jsonutils.NewString(value)
}
config.Add(v, "config", "default", key)
}
nconf, err := modules.ServicesV3.PerformAction(s, args.SERVICE, "config", config)
if err != nil {

View File

@@ -47,7 +47,7 @@ type BaseActionListOptions struct {
type ActionListOptions struct {
BaseActionListOptions
Service []string `help:"service name`
Service []string `help:"service name"`
Id string `help:"" metavar:"OBJ_ID"`
Type []string `help:"Type of relevant object" metavar:"OBJ_TYPE"`
}

6
go.mod
View File

@@ -87,14 +87,14 @@ require (
k8s.io/client-go v0.19.3
k8s.io/cluster-bootstrap v0.19.3
moul.io/http2curl/v2 v2.3.0
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250113015525-b483c92621d7
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250325104241-0718ac214f29
yunion.io/x/executor v0.0.0-20241205080005-48f5b1212256
yunion.io/x/jsonutils v1.0.1-0.20240930100528-1671a2d0d22f
yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91
yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900
yunion.io/x/pkg v1.10.4-0.20250123070256-9247ce856f07
yunion.io/x/pkg v1.10.4-0.20250324235849-27051af0af08
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1
yunion.io/x/sqlchemy v1.1.3-0.20240926163039-d41512b264e1
yunion.io/x/sqlchemy v1.1.3-0.20250307104108-2f8e0af883e9
yunion.io/x/structarg v0.0.0-20231017124457-df4d5009457c
)

12
go.sum
View File

@@ -1274,8 +1274,8 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250113015525-b483c92621d7 h1:f3j4AHOANU1rDyHmXpPdmVklpL1NFODhcYXDXtt5gOE=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250113015525-b483c92621d7/go.mod h1:KQ/jWx7bZlmjCE711KEWuvHW/dzpdr/UTlBjjutkj0Y=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250325104241-0718ac214f29 h1:9cR9HujwH+N7hlQNwi+NYmXozYo5qLJgzchx9VymcTQ=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20250325104241-0718ac214f29/go.mod h1:FXxAEbdNfWXX9gjME3K2nJhkydHY5EKEUZb+RLEzVwQ=
yunion.io/x/executor v0.0.0-20241205080005-48f5b1212256 h1:kLKQ6zbgPDQflRwoHFAjxNChcbhXIFgsUVLkJwiXu/8=
yunion.io/x/executor v0.0.0-20241205080005-48f5b1212256/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws=
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634=
@@ -1289,11 +1289,11 @@ yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900 h1:Hu/4ERvoWaN6aiFs4h4/yvVB
yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900/go.mod h1:0vLkNEhlmA64HViPBAnSTUMrx5QP1CLsxXmxDKQ80tc=
yunion.io/x/pkg v0.0.0-20190620104149-945c25821dbf/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/pkg v0.0.0-20190628082551-f4033ba2ea30/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/pkg v1.10.4-0.20250123070256-9247ce856f07 h1:XZsL8+YpgVBHMcRwdpZ4cOfSZCc+KsgUCTdV3KeK8FI=
yunion.io/x/pkg v1.10.4-0.20250123070256-9247ce856f07/go.mod h1:0Bwxqd9MA3ACi119/l02FprY/o9gHahmYC2bsSbnVpM=
yunion.io/x/pkg v1.10.4-0.20250324235849-27051af0af08 h1:GBX/Bb72xWWp++tClxj7PjuhZnRhzgvmLIbD3RyEUZc=
yunion.io/x/pkg v1.10.4-0.20250324235849-27051af0af08/go.mod h1:0Bwxqd9MA3ACi119/l02FprY/o9gHahmYC2bsSbnVpM=
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1 h1:1KJ3YYinydPHpDEQRXdr/T8SYcKZ5Er+m489H+PnaQ4=
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1/go.mod h1:0iFKpOs1y4lbCxeOmq3Xx/0AcQoewVPwj62eRluioEo=
yunion.io/x/sqlchemy v1.1.3-0.20240926163039-d41512b264e1 h1:HWPqY1I5JSmM6Sks6FyK9hnq/MjL7FDghM6M8DXHob0=
yunion.io/x/sqlchemy v1.1.3-0.20240926163039-d41512b264e1/go.mod h1:vCIZpqhZ5Jzaq3tFyrti/vv8BijQKtkzSgNT/uH4H5A=
yunion.io/x/sqlchemy v1.1.3-0.20250307104108-2f8e0af883e9 h1:KU8BikBBUJlSkA1Ib01htqaDdzt20K+ezNkTZD8Rxjk=
yunion.io/x/sqlchemy v1.1.3-0.20250307104108-2f8e0af883e9/go.mod h1:vCIZpqhZ5Jzaq3tFyrti/vv8BijQKtkzSgNT/uH4H5A=
yunion.io/x/structarg v0.0.0-20231017124457-df4d5009457c h1:QuLab2kSRECZRxo4Lo2KcYn6XjQFDGaZ1+x0pYDVVwQ=
yunion.io/x/structarg v0.0.0-20231017124457-df4d5009457c/go.mod h1:EP6NSv2C0zzqBDTKumv8hPWLb3XvgMZDHQRfyuOrQng=

View File

@@ -58,7 +58,7 @@ func StartService() {
// log.Infof("Modules: %s", jsonutils.Marshal(jmods).PrettyString())
if !options.Options.DisableReporting {
cron := cronman.InitCronJobManager(true, 1)
cron := cronman.InitCronJobManager(true, 1, opts.TimeZone)
rand.Seed(time.Now().Unix())
cron.AddJobEveryFewDays("AutoReport", 1, rand.Intn(23), rand.Intn(59), 0, report.Report, true)
go cron.Start()

View File

@@ -172,6 +172,10 @@ type DiskConfig struct {
// requried: false
Fs string `json:"fs"`
// 关机后自动重置磁盘
// required: false
AutoReset bool `json:"auto_reset"`
// 磁盘存储格式
// enum: qcow2, raw, docker, iso, vmdk, vmdkflatver1, vmdkflatver2, vmdkflat, vmdksparse, vmdksparsever1, vmdksparsever2, vmdksepsparse vhd
// requried: false

View File

@@ -338,7 +338,7 @@ type CloudaccountDetail struct {
SyncIntervalSeconds int `json:"sync_interval_seconds"`
// 同步状态
SyncStatus2 string `json:"sync_stauts2"`
SyncStatus2 string `json:"sync_status2"`
// 云账号环境类型
// public: 公有云
@@ -565,7 +565,7 @@ type SyncRangeInput struct {
// 按资源类型同步,可输入多个
// enmu: project, compute, network, eip, loadbalancer, objectstore, rds, cache, event, cloudid, dnszone, public_ip, intervpcnetwork, saml_auth, quota, nat, nas, waf, mongodb, es, kafka, app, cdn, container, ipv6_gateway, tablestore, modelarts, vpcpeer, misc
Resources []string `json:"resources" choices:"project|compute|network|eip|loadbalancer|objectstore|rds|cache|event|cloudid|dnszone|public_ip|intervpcnetwork|saml_auth|quota|nat|nas|waf|mongodb|es|kafka|app|cdn|container|ipv6_gateway|tablestore|modelarts|vpcpeer|misc"`
Resources []string `json:"resources" choices:"project|compute|network|eip|loadbalancer|objectstore|rds|cache|event|cloudid|dnszone|public_ip|intervpcnetwork|saml_auth|quota|nat|nas|waf|mongodb|es|kafka|app|cdn|container|ipv6_gateway|tablestore|modelarts|vpcpeer|misc|image"`
}
type iRes interface {

View File

@@ -261,6 +261,8 @@ type DiskUpdateInput struct {
// 磁盘类型
DiskType string `json:"disk_type"`
// 关机自动重置
AutoReset *bool `json:"auto_reset"`
}
type DiskSaveInput struct {

View File

@@ -178,6 +178,10 @@ const (
VM_QGA_SET_NETWORK = "qga_set_network"
VM_QGA_SET_NETWORK_FAILED = "qga_set_network_failed"
// 更改计费模式
VM_CHANGE_BILLING_TYPE = "change_billing_type"
VM_CHANGE_BILLING_TYPE_FAILED = "change_billing_type_failed"
SHUTDOWN_STOP = "stop"
SHUTDOWN_TERMINATE = "terminate"

View File

@@ -34,6 +34,8 @@ type GuestDiskDetails struct {
// 磁盘类型
// example: data
DiskType string `json:"disk_type"`
// 关机自动重置
AutoReset bool `json:"auto_reset"`
// 介质类型
// example: ssd
MediumType string `json:"medium_type"`
@@ -91,6 +93,7 @@ type GuestdiskJsonDesc struct {
Dev string `json:"dev"`
IsSSD bool `json:"is_ssd"`
NumQueues uint8 `json:"num_queues"`
AutoReset bool `json:"auto_reset"`
// esxi
ImageInfo struct {

View File

@@ -359,6 +359,7 @@ type GuestDiskInfo struct {
Driver string `json:"driver"`
CacheMode string `json:"cache_mode"`
AioMode string `json:"aio_mode"`
AutoReset bool `json:"auto_reset"`
MediumType string `json:"medium_type"`
StorageType string `json:"storage_type"`
Iops int `json:"iops"`
@@ -624,6 +625,7 @@ type ServerStopInput struct {
TimeoutSecs int `json:"timeout_secs"`
// 是否关机停止计费, 若平台不支持停止计费,此参数无作用
// 若包年包月机器关机设置此参数,则先转换计费模式到按量计费,再关机不收费
// 目前仅阿里云,腾讯云此参数生效
StopCharging bool `json:"stop_charging"`
}
@@ -978,6 +980,13 @@ type ServerChangeDiskStorageInternalInput struct {
CloneDiskCount int `json:"disk_count"`
}
type ServerCopyDiskToStorageInput struct {
KeepOriginDisk bool `json:"keep_origin_disk"`
GuestRunning bool `json:"guest_running"`
CompletedDiskCount int `json:"completed_disk_count"`
CloneDiskCount int `json:"disk_count"`
}
type ServerSetExtraOptionInput struct {
Key string `json:"key"`
Value string `json:"value"`
@@ -1247,6 +1256,8 @@ type GuestPerformStartInput struct {
// 指定启动虚拟机的Qemu版本可选值2.12.1, 4.2.0
// 仅适用于KVM虚拟机
QemuVersion string `json:"qemu_version"`
// 按量机器自动转换为包年包月
AutoPrepaid bool `json:"auto_prepaid"`
}
type ServerSetOSInfoInput struct {
@@ -1332,6 +1343,10 @@ func (conf ServerChangeConfigSettings) MemChanged() bool {
return conf.VmemSize != conf.Old.VmemSize
}
func (conf ServerChangeConfigSettings) InstanceTypeChanged() bool {
return len(conf.InstanceType) > 0 && conf.InstanceType != conf.Old.InstanceType
}
func (conf ServerChangeConfigSettings) AddedMem() int {
addMem := conf.VmemSize - conf.Old.VmemSize
if addMem < 0 {
@@ -1350,3 +1365,10 @@ func (conf ServerChangeConfigSettings) AddedDisk() int {
}
return size
}
type ServerChangeBillingTypeInput struct {
// 仅在虚拟机开机或关机状态下调用
// enmu: [postpaid, prepaid]
// required: true
BillingType string `json:"billing_type"`
}

View File

@@ -23,6 +23,10 @@ import (
"yunion.io/x/onecloud/pkg/apis"
)
const (
ErrMsgIsolatedDeviceUsedByServer = "Isolated device used by server"
)
type IsolateDeviceDetails struct {
apis.StandaloneResourceDetails
HostResourceInfo

View File

@@ -80,6 +80,9 @@ type StorageCreateInput struct {
// enable ceph messenger v2
EnableMessengerV2 *bool `json:"enable_messenger_v2"`
// rbd storage auto cache glance images
AutoCacheImages *bool `json:"auto_cache_images"`
// swagger:ignore
MonHost string
@@ -246,6 +249,8 @@ type StorageUpdateInput struct {
// enable ceph messenger v2
EnableMessengerV2 *bool `json:"enable_messenger_v2"`
// rbd storage auto cache glance images
AutoCacheImages *bool `json:"auto_cache_images"`
RbdTimeoutInput
@@ -259,6 +264,18 @@ type StorageUpdateInput struct {
MasterHost string
}
type RbdStorageConf struct {
RadosMonOpTimeout int `json:"rados_mon_op_timeout"`
RadosOsdOpTimeout int `json:"rados_osd_op_timeout"`
ClientMountTimeout int `json:"client_mount_timeout"`
MonHost string `json:"mon_host"`
Pool string `json:"pool"`
Key string `json:"key"`
EnableMessengerV2 bool `json:"enable_messenger_v2"`
AutoCacheImages bool `json:"auto_cache_images"`
}
type StorageSetCmtBoundInput struct {
Cmtbound *float32
}

View File

@@ -181,6 +181,7 @@ var (
SHARED_FILE_STORAGE = []string{STORAGE_NFS, STORAGE_GPFS}
FIEL_STORAGE = []string{STORAGE_LOCAL, STORAGE_NFS, STORAGE_GPFS, STORAGE_LVM, STORAGE_CLVM, STORAGE_SLVM}
LVM_STORAGE = []string{STORAGE_LVM, STORAGE_CLVM, STORAGE_SLVM}
// supported shared storage types
SHARED_STORAGE = []string{STORAGE_NFS, STORAGE_GPFS, STORAGE_RBD, STORAGE_CLVM, STORAGE_SLVM}
@@ -272,4 +273,7 @@ type StorageListInput struct {
// filter storages of baremetal host
IsBaremetal *bool `json:"is_baremetal"`
// filter by storage type
StorageType string `json:"storage_type"`
}

View File

@@ -41,6 +41,8 @@ const (
IMAGE_STATUS_DELETED = "deleted"
IMAGE_STATUS_PENDING_DELETE = "pending_delete"
IMAGE_STATUS_UNKNOWN = "unkown"
IMAGE_STATUS_SYNC_CLASS_METADATA_FAILEd = "sync_class_metadata_failed"
ImageTypeTemplate = TImageType("image")

View File

@@ -168,6 +168,8 @@ type ModelBaseListInput struct {
ExportKeys string `json:"export_keys" help:"Export field keys"`
// 返回结果携带delete_fail_reason和update_fail_reason字段
ShowFailReason *bool `json:"show_fail_reason"`
// 是否返回状态统计信息默认为False
SummaryStats *bool `json:"summary_stats"`
}
func (o ModelBaseListInput) GetExportKeys() string {

View File

@@ -45,7 +45,7 @@ type AlertRecordListInput struct {
type AlertRecordDetails struct {
SAlertRecord
apis.StatusStandaloneResourceDetails
apis.StandaloneAnonResourceDetails
apis.ScopedResourceBaseInfo
ResNum int64 `json:"res_num"`

View File

@@ -39,9 +39,10 @@ type MonitorResourceListInput struct {
apis.EnabledResourceBaseListInput
compute.ManagedResourceListInput
ResId []string `json:"res_id"`
ResType string `json:"res_type"`
OnlyResId bool `json:"only_res_id"`
ResId []string `json:"res_id"`
ResType string `json:"res_type"`
OnlyResId bool `json:"only_res_id"`
AlertStates []string `json:"alert_states"`
ResName string `json:"res_name"`
}

View File

@@ -19,10 +19,12 @@ type NotificationTemplateCreateInput struct {
}
type NotificationTemplateConfig struct {
Title string `json:"title"`
Name string `json:"name"`
ResourceName string `json:"resource_name"`
Matches []*EvalMatch `json:"matches"`
Title string `json:"title"`
Name string `json:"name"`
ResourceName string `json:"resource_name"`
Matches []*EvalMatch `json:"matches"`
MatchTags []map[string]string `json:"match_tags"`
MatchTagsStr []string `json:"match_tags_str"`
// PrevAlertState AlertStateType `json:"prev_alert_state"`
// State AlertStateType `json:"state"`
NoDataFound bool `json:"no_data"`

View File

@@ -128,11 +128,12 @@ type TimeSeriesSlice []*TimeSeries
type TimeSeries struct {
// RawName is used to frontend displaying the curve name
RawName string `json:"raw_name"`
Columns []string `json:"columns"`
Name string `json:"name"`
Points TimeSeriesPoints `json:"points"`
Tags map[string]string `json:"tags,omitempty"`
RawName string `json:"raw_name"`
Columns []string `json:"columns"`
Name string `json:"name"`
Points TimeSeriesPoints `json:"points"`
Tags map[string]string `json:"tags,omitempty"`
CloudTags map[string]string `json:"cloud_tags,omitempty"`
}
type TimePoint []interface{}

View File

@@ -29,11 +29,11 @@ type SAlert struct {
apis.SStatusStandaloneResourceBase
SMonitorScopedResource
// Frequency is evaluate period
Frequency int64 `json:"frequency"`
Frequency int64 `json:"frequency"`
Settings *AlertSetting `json:"settings"`
Level string `json:"level"`
Message string `json:"message"`
UsedBy string `json:"used_by"`
Level string `json:"level"`
Message string `json:"message"`
UsedBy string `json:"used_by"`
// Silenced bool
ExecutionError string `json:"execution_error"`
// If an alert rule has a configured `For` and the query violates the configured threshold

View File

@@ -78,6 +78,8 @@ type Application struct {
isTLS bool
enableProfiling bool
allowTLS1x bool
}
const (
@@ -139,6 +141,12 @@ func (app *Application) SetDefaultTimeout(to time.Duration) *Application {
return app
}
func (app *Application) AllowTLS1x() *Application {
log.Infof("Allow TLS1.0&1.1")
app.allowTLS1x = true
return app
}
func SplitPath(path string) []string {
ret := make([]string, 0)
for _, seg := range strings.Split(path, "/") {
@@ -361,6 +369,7 @@ func (app *Application) defaultHandle(w http.ResponseWriter, r *http.Request, ri
w.Header().Set("Server", "Yunion AppServer/Go/2018.4")
w.Header().Set("X-Frame-Options", "SAMEORIGIN")
w.Header().Set("X-XSS-Protection", "1; mode=block")
w.Header().Set("X-Content-Type-Options", "nosniff")
if app.isTLS {
w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
}
@@ -469,6 +478,10 @@ func timeoutHandle(h http.Handler) http.HandlerFunc {
}
func (app *Application) initServer(addr string) *http.Server {
return InitHTTPServer(app, addr)
}
func InitHTTPServer(app *Application, addr string) *http.Server {
/* db := AppContextDB(app.context)
if db != nil {
db.SetMaxIdleConns(app.connMax + 1)
@@ -478,7 +491,18 @@ func (app *Application) initServer(addr string) *http.Server {
cipherSuites := []uint16{}
for _, suite := range tls.CipherSuites() {
cipherSuites = append(cipherSuites, suite.ID)
if !strings.HasSuffix(suite.Name, "_SHA") {
cipherSuites = append(cipherSuites, suite.ID)
}
}
minTLSVer := uint16(tls.VersionTLS12)
if app.allowTLS1x {
minTLSVer = tls.VersionTLS10
}
tlsConf := &tls.Config{
CipherSuites: cipherSuites,
MinVersion: minTLSVer,
}
s := &http.Server{
@@ -493,9 +517,7 @@ func (app *Application) initServer(addr string) *http.Server {
// issue like: https://github.com/megaease/easegress/issues/481
ErrorLog: olog.New(io.Discard, "", olog.LstdFlags),
TLSConfig: &tls.Config{
CipherSuites: cipherSuites,
},
TLSConfig: tlsConf,
}
return s
}
@@ -603,22 +625,6 @@ func (app *Application) listenAndServeInternal(s *http.Server, certFile, keyFile
}
}
func isJsonContentType(r *http.Request) bool {
contType := strings.ToLower(r.Header.Get("Content-Type"))
if strings.HasPrefix(contType, "application/json") {
return true
}
return false
}
func isFormContentType(r *http.Request) bool {
contType := strings.ToLower(r.Header.Get("Content-Type"))
if strings.HasPrefix(contType, "application/json") {
return true
}
return false
}
type TContentType string
const (

View File

@@ -20,6 +20,8 @@ import (
"net/http"
"net/http/pprof"
"github.com/gorilla/mux"
"yunion.io/x/pkg/util/version"
)
@@ -85,3 +87,26 @@ func profSymbol(_ context.Context, w http.ResponseWriter, r *http.Request) {
func profTrace(_ context.Context, w http.ResponseWriter, r *http.Request) {
pprof.Trace(w, r)
}
func AddMiscHandlersToMuxRouter(app *Application, root *mux.Router, enableProfiling bool) {
adapterF := func(appHandleFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
appHandleFunc(app.GetContext(), w, r)
}
}
root.HandleFunc("/version", adapterF(VersionHandler))
root.HandleFunc("/stats", adapterF(StatisticHandler))
root.HandleFunc("/ping", adapterF(PingHandler))
root.HandleFunc("/worker_stats", adapterF(WorkerStatsHandler))
if enableProfiling {
pp := "/debug/pprof"
ppPath := func(sufix string) string {
return fmt.Sprintf("%s/%s", pp, sufix)
}
root.HandleFunc(ppPath(""), pprof.Index)
root.HandleFunc(ppPath("cmdline"), pprof.Cmdline)
root.HandleFunc(ppPath("profile"), pprof.Profile)
root.HandleFunc(ppPath("symbol"), pprof.Symbol)
root.HandleFunc(ppPath("trace"), pprof.Trace)
}
}

View File

@@ -292,15 +292,14 @@ func (wm *SWorkerManager) scheduleWithLock() {
log.Debugf("no enough worker, add new worker %s", worker)
}
go worker.run()
} else if queueSize > 10 {
log.Warningf("[%s] BUSY activeWork %d detachedWork %d max %d queue: %d", wm, wm.ActiveWorkerCount(), wm.DetachedWorkerCount(), wm.workerCount, wm.queue.Size())
} else if queueSize > 50 {
w := wm.activeWorker.list.Front()
for w != nil {
if w != nil {
worker := w.Value.(*SWorker)
log.Warningf("work [%s]%s stucking for a while", worker.task.start, worker.task.task.Dump())
w = w.Next()
}
} else if queueSize > 10 {
log.Warningf("[%s] BUSY activeWork %d detachedWork %d max %d queue: %d", wm, wm.ActiveWorkerCount(), wm.DetachedWorkerCount(), wm.workerCount, wm.queue.Size())
}
}

View File

@@ -78,7 +78,7 @@ func (s *BaremetalService) StartService() {
s.startAgent(app)
cron := cronman.InitCronJobManager(false, o.Options.CronJobWorkerCount)
cron := cronman.InitCronJobManager(false, o.Options.CronJobWorkerCount, o.Options.TimeZone)
cron.AddJobAtIntervals("BaremetalCronJobs", 10*time.Second, baremetal.DoCronJobs)
cron.Start()
defer cron.Stop()

View File

@@ -40,6 +40,9 @@ func InitApp(options *common_options.BaseOptions, dbAccess bool) *appsrv.Applica
if options.EnableAppProfiling {
app.EnableProfiling()
}
if options.AllowTLS1x {
app.AllowTLS1x()
}
return app
}

View File

@@ -124,6 +124,8 @@ func ParseDiskConfig(diskStr string, idx int) (*compute.DiskConfig, error) {
diskConfig.Mountpoint = p
} else if p == "autoextend" {
diskConfig.SizeMb = -1
} else if p == "autoreset" {
diskConfig.AutoReset = true
} else if utils.IsInStringArray(p, compute.STORAGE_TYPES) {
diskConfig.Backend = p
} else if len(p) > 0 {

View File

@@ -140,15 +140,22 @@ type SCronJobManager struct {
running bool
workers *appsrv.SWorkerManager
dataLock *sync.Mutex
timezone *time.Location
}
func InitCronJobManager(isDbWorker bool, workerCount int) *SCronJobManager {
func InitCronJobManager(isDbWorker bool, workerCount int, timezone string) *SCronJobManager {
if manager == nil {
tz, err := time.LoadLocation(timezone)
if err != nil {
log.Errorf("InitCronJobManager failed")
tz = time.UTC
}
manager = &SCronJobManager{
jobs: make([]*SCronJob, 0),
workers: appsrv.NewWorkerManager("CronJobWorkers", workerCount, 1024, isDbWorker),
dataLock: new(sync.Mutex),
add: make(chan struct{}),
timezone: tz,
}
}
return manager
@@ -311,7 +318,7 @@ func (self *SCronJobManager) AddJobEveryFewHour(name string, hour, min, sec int,
}
func (self *SCronJobManager) addJob(newJob *SCronJob) {
now := time.Now()
now := time.Now().In(self.timezone)
newJob.Next = newJob.Timer.Next(now)
if newJob.StartRun {
newJob.runJob(true, now)
@@ -375,7 +382,7 @@ func (self *SCronJobManager) Stop() {
}
func (self *SCronJobManager) init() {
now := time.Now()
now := time.Now().In(self.timezone)
self.next(now)
heap.Init(&self.jobs)
for i := 0; i < len(self.jobs); i += 1 {
@@ -388,7 +395,7 @@ func (self *SCronJobManager) init() {
func (self *SCronJobManager) run(ctx context.Context) {
var timer *time.Timer
var now = time.Now()
var now = time.Now().In(self.timezone)
for {
self.dataLock.Lock()
if len(self.jobs) == 0 || self.jobs[0].Next.IsZero() {

View File

@@ -23,7 +23,7 @@ import (
)
func TestSCronJobManager_AddRemoveJobs(t *testing.T) {
manager := InitCronJobManager(false, 4)
manager := InitCronJobManager(false, 4, "")
testFunc := func(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {}
manager.AddJobAtIntervals("Test1", time.Second*100, testFunc)

View File

@@ -1980,6 +1980,9 @@ func deleteItem(manager IModelManager, model IModel, ctx context.Context, userCr
// 删除后钩子
model.PostDelete(ctx, userCred)
if err := model.GetModelManager().GetExtraHook().AfterPostDelete(ctx, userCred, model, query); err != nil {
logclient.AddActionLogWithContext(ctx, model, logclient.ACT_POST_DELETE_HOOK, err, userCred, false)
}
// 避免设置删除状态没有正常返回
jsonutils.Update(details, model)

View File

@@ -152,6 +152,7 @@ type IModelManager interface {
type IModelManagerExtraHook interface {
AfterPostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, model IModel, query jsonutils.JSONObject, data jsonutils.JSONObject) error
AfterPostDelete(ctx context.Context, userCred mcclient.TokenCredential, model IModel, query jsonutils.JSONObject) error
}
type IModel interface {

View File

@@ -876,3 +876,7 @@ func NewEmptyExtraHook() *SEmptyExtraHook {
func (e SEmptyExtraHook) AfterPostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, model IModel, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
return nil
}
func (e SEmptyExtraHook) AfterPostDelete(ctx context.Context, userCred mcclient.TokenCredential, model IModel, query jsonutils.JSONObject) error {
return nil
}

View File

@@ -65,7 +65,7 @@ func NewNameValidator(ctx context.Context, manager IModelManager, ownerId mcclie
}
if strings.ContainsAny(name, forbiddenNameChars) {
return errors.Wrapf(errors.ErrInvalidFormat, "name should not contains any of %s", forbiddenNameChars)
return errors.Wrapf(errors.ErrInvalidFormat, "name should not contains any of %q", forbiddenNameChars)
}
uniq, err := isNameUnique(ctx, manager, ownerId, name, uniqValues)
@@ -113,7 +113,7 @@ func alterNameValidator(ctx context.Context, model IModel, name string) error {
}
if strings.ContainsAny(name, forbiddenNameChars) {
return errors.Wrapf(errors.ErrInvalidFormat, "name should not contains any of %s", forbiddenNameChars)
return errors.Wrapf(errors.ErrInvalidFormat, "name should not contains any of %q", forbiddenNameChars)
}
uniq, err := isAlterNameUnique(ctx, model, name)

View File

@@ -265,7 +265,10 @@ func genMsgViaLang(ctx context.Context, p sNotifyParams) ([]npk.SNotifyMessage,
topic = p.event
}
msg.Topic = topic
body, _ := getContent(langSuffix, p.event, "content", p.channel, p.data)
body, err := getContent(langSuffix, p.event, "content", p.channel, p.data)
if err != nil {
log.Errorf("get content error: %s", err)
}
if len(body) == 0 {
body, _ = p.data.GetString()
}

View File

@@ -119,6 +119,7 @@ type BaseOptions struct {
PlatformNames map[string]string `help:"identity name of this platform by language"`
EnableAppProfiling bool `help:"enable profiling API" default:"false"`
AllowTLS1x bool `help:"allow obsolete insecure TLS V1.0&1.1" default:"false" json:"allow_tls1x"`
EnableChangeOwnerAutoRename bool `help:"Allows renaming when changing names" default:"false"`
EnableDefaultPolicy bool `help:"Enable defualt policies" default:"true"`

View File

@@ -55,7 +55,7 @@ func StartService() {
defer cloudcommon.CloseDB()
if !opts.IsSlaveNode {
cron := cronman.InitCronJobManager(true, options.Options.CronJobWorkerCount)
cron := cronman.InitCronJobManager(true, options.Options.CronJobWorkerCount, options.Options.TimeZone)
cron.AddJobAtIntervalsWithStartRun("SyncCloudprovider", time.Duration(opts.CloudproviderSyncIntervalMinutes)*time.Minute, models.CloudproviderManager.SyncCloudproviders, true)
cron.AddJobAtIntervalsWithStartRun("CloudeventSyncTask", time.Duration(opts.CloudeventSyncIntervalHours)*time.Hour, models.CloudproviderManager.SyncCloudeventTask, true)

View File

@@ -85,7 +85,7 @@ func StartService() {
}
if !opts.IsSlaveNode {
cron := cronman.InitCronJobManager(true, options.Options.CronJobWorkerCount)
cron := cronman.InitCronJobManager(true, options.Options.CronJobWorkerCount, options.Options.TimeZone)
cron.AddJobAtIntervalsWithStartRun("SyncCloudaccountResources", time.Duration(opts.CloudIdResourceSyncIntervalHours)*time.Hour, models.CloudaccountManager.SyncCloudaccountResources, true)
cron.AddJobAtIntervalsWithStartRun("SyncCloudproviderResources", time.Duration(opts.CloudIdResourceSyncIntervalHours)*time.Hour, models.CloudproviderManager.SyncCloudproviderResources, true)

View File

@@ -987,7 +987,7 @@ func (res *SResources) CollectMetrics(ctx context.Context, userCred mcclient.Tok
log.Errorf("Get influxdb %s service url: %v", options.Options.SessionEndpointType, err)
return
}
if err := influxdb.SendMetrics(urls, "meter_db", metrics, true); err != nil {
if err := influxdb.SendMetrics(urls, "meter_db", metrics, false); err != nil {
log.Errorf("SendMetrics to meter_db: %v", err)
return
}

View File

@@ -48,7 +48,7 @@ func StartService() {
res := resources.NewResources()
if !opts.IsSlaveNode {
cron := cronman.InitCronJobManager(true, options.Options.CronJobWorkerCount)
cron := cronman.InitCronJobManager(true, options.Options.CronJobWorkerCount, options.Options.TimeZone)
cron.AddJobAtIntervalsWithStartRun("InitResources", time.Duration(opts.ResourcesSyncInterval)*time.Minute, res.Init, true)
cron.AddJobAtIntervals("IncrementResources", time.Duration(opts.ResourcesSyncInterval)*time.Minute, res.IncrementSync)
cron.AddJobAtIntervals("DecrementResources", time.Duration(opts.ResourcesSyncInterval)*time.Minute, res.DecrementSync)

View File

@@ -103,8 +103,8 @@ func (self *SAliyunGuestDriver) IsAllowSaveImageOnRunning() bool {
return true
}
func (self *SAliyunGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
func (self *SAliyunGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}
func (self *SAliyunGuestDriver) GetDeployStatus() ([]string, error) {

View File

@@ -179,7 +179,7 @@ func (self *SAwsGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SAwsGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SAwsGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}

View File

@@ -102,7 +102,7 @@ func (self *SAzureGuestDriver) IsRebuildRootSupportChangeUEFI() bool {
return false
}
func (self *SAzureGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SAzureGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}

View File

@@ -123,7 +123,7 @@ func (self *SBaremetalGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_ADMIN}, nil
}
func (self *SBaremetalGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SBaremetalGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return nil, httperrors.NewUnsupportOperationError("Cannot change config for baremtal")
}

View File

@@ -125,6 +125,10 @@ func (drv *SBaseGuestDriver) RequestDetachDisk(ctx context.Context, guest *model
return nil
}
func (drv *SBaseGuestDriver) RequestChangeBillingType(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
return errors.Wrapf(cloudprovider.ErrNotImplemented, "RequestChangeBillingType")
}
func (drv *SBaseGuestDriver) RequestAttachDisk(ctx context.Context, guest *models.SGuest, disk *models.SDisk, task taskman.ITask) error {
task.ScheduleRun(nil)
return nil
@@ -170,8 +174,8 @@ func (drv *SBaseGuestDriver) IsRebuildRootSupportChangeUEFI() bool {
return true
}
func (drv *SBaseGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
return []string{}, fmt.Errorf("This Guest driver dose not implement GetChangeConfigStatus")
func (drv *SBaseGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{}, fmt.Errorf("This Guest driver dose not implement GetChangeInstanceTypeStatus")
}
func (drv *SBaseGuestDriver) ValidateDetachDisk(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, disk *models.SDisk) error {

View File

@@ -80,7 +80,7 @@ func (self *SCloudpodsGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SCloudpodsGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SCloudpodsGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}

View File

@@ -94,7 +94,7 @@ func (self *SCtyunGuestDriver) GetAttachDiskStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SCtyunGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SCtyunGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}

View File

@@ -88,7 +88,7 @@ func (self *SEcloudGuestDriver) GetAttachDiskStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SEcloudGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SEcloudGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}

View File

@@ -209,7 +209,7 @@ func (self *SESXiGuestDriver) GetAttachDiskStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SESXiGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SESXiGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}

View File

@@ -129,7 +129,7 @@ func (self *SGoogleGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}
func (self *SGoogleGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SGoogleGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}

View File

@@ -98,7 +98,7 @@ func (self *SH3CGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SH3CGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SH3CGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}

View File

@@ -98,7 +98,7 @@ func (self *SHCSGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SHCSGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SHCSGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}

View File

@@ -98,7 +98,7 @@ func (self *SHCSOPGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SHCSOPGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SHCSOPGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}

View File

@@ -89,7 +89,7 @@ func (self *SHuaweiGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SHuaweiGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SHuaweiGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}

View File

@@ -93,7 +93,7 @@ func (self *SHCSOGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SHCSOGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SHCSOGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}

View File

@@ -119,7 +119,7 @@ func (self *SInCloudSphereGuestDriver) GetAttachDiskStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}
func (self *SInCloudSphereGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SInCloudSphereGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}

View File

@@ -443,9 +443,11 @@ func (self *SKVMGuestDriver) RequestChangeVmConfig(ctx context.Context, guest *m
body := jsonutils.NewDict()
if vcpuCount > int64(guest.VcpuCount) {
body.Set("add_cpu", jsonutils.NewInt(addCpu))
body.Set("total_cpu", jsonutils.NewInt(int64(guest.VcpuCount)))
}
if vmemSize > int64(guest.VmemSize) {
body.Set("add_mem", jsonutils.NewInt(addMem))
body.Set("total_mem", jsonutils.NewInt(int64(guest.VmemSize)))
}
if taskParams.Contains("cpu_numa_pin") {
cpuNumaPin, _ := taskParams.Get("cpu_numa_pin")
@@ -606,7 +608,7 @@ func (self *SKVMGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}
func (self *SKVMGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SKVMGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}

View File

@@ -475,6 +475,14 @@ func (drv *SManagedVirtualizedGuestDriver) RequestStartOnHost(ctx context.Contex
result := jsonutils.NewDict()
if ivm.GetStatus() != api.VM_RUNNING {
if guest.BillingType == billing_api.BILLING_TYPE_POSTPAID && jsonutils.QueryBoolean(task.GetParams(), "auto_prepaid", false) {
err = ivm.ChangeBillingType(billing_api.BILLING_TYPE_PREPAID)
if err != nil && errors.Cause(err) != cloudprovider.ErrNotImplemented {
logclient.AddSimpleActionLog(guest, logclient.ACT_VM_CHANGE_BILLING_TYPE, errors.Wrapf(err, billing_api.BILLING_TYPE_PREPAID), userCred, false)
}
}
err := ivm.StartVM(ctx)
if err != nil {
return errors.Wrapf(err, "StartVM")
@@ -936,6 +944,15 @@ func (drv *SManagedVirtualizedGuestDriver) RequestStopOnHost(ctx context.Context
if ivm.GetStatus() != api.VM_READY {
opts := &cloudprovider.ServerStopOptions{}
task.GetParams().Unmarshal(opts)
// 包年包月实例关机不收费,先转按量付费再关机
if opts.StopCharging && guest.BillingType == billing_api.BILLING_TYPE_PREPAID {
err = ivm.ChangeBillingType(billing_api.BILLING_TYPE_POSTPAID)
if err != nil && errors.Cause(err) != cloudprovider.ErrNotImplemented {
logclient.AddSimpleActionLog(guest, logclient.ACT_VM_CHANGE_BILLING_TYPE, errors.Wrapf(err, billing_api.BILLING_TYPE_POSTPAID), task.GetUserCred(), false)
}
}
err = ivm.StopVM(ctx, opts)
if err != nil {
return nil, errors.Wrapf(err, "ivm.StopVM")
@@ -952,6 +969,51 @@ func (drv *SManagedVirtualizedGuestDriver) RequestStopOnHost(ctx context.Context
return nil
}
func (drv *SManagedVirtualizedGuestDriver) RequestChangeBillingType(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
ivm, err := guest.GetIVM(ctx)
if err != nil {
return nil, errors.Wrapf(err, "guest.GetIVM")
}
billType := ""
switch guest.BillingType {
case billing_api.BILLING_TYPE_POSTPAID:
billType = billing_api.BILLING_TYPE_PREPAID
case billing_api.BILLING_TYPE_PREPAID:
billType = billing_api.BILLING_TYPE_POSTPAID
}
err = ivm.ChangeBillingType(billType)
if err != nil {
return nil, errors.Wrapf(err, "ChangeBillingType")
}
err = cloudprovider.Wait(time.Second*5, time.Minute*3, func() (bool, error) {
err = ivm.Refresh()
if err != nil {
return false, err
}
if ivm.GetBillingType() != billType {
return false, nil
}
return true, nil
})
if err != nil {
return nil, errors.Wrapf(err, "Wait vm billing type changed")
}
_, err = db.Update(guest, func() error {
guest.BillingType = ivm.GetBillingType()
guest.Status = ivm.GetStatus()
guest.ExpiredAt = time.Time{}
if guest.BillingType == billing_api.BILLING_TYPE_PREPAID {
guest.AutoRenew = ivm.IsAutoRenew()
guest.ExpiredAt = ivm.GetExpiredAt()
}
return nil
})
return nil, err
})
return nil
}
func (drv *SManagedVirtualizedGuestDriver) RequestSyncstatusOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, userCred mcclient.TokenCredential, task taskman.ITask) error {
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
ihost, err := host.GetIHost(ctx)

View File

@@ -131,7 +131,7 @@ func (self *SNutanixGuestDriver) GetAttachDiskStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}
func (self *SNutanixGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SNutanixGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}

View File

@@ -132,7 +132,7 @@ func (self *SOpenStackGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING, api.VM_REBUILD_ROOT_FAIL}, nil
}
func (self *SOpenStackGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SOpenStackGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}

View File

@@ -136,7 +136,7 @@ func (self *SProxmoxGuestDriver) GetAttachDiskStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}
func (self *SProxmoxGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SProxmoxGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}

View File

@@ -95,7 +95,7 @@ func (self *SQcloudGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SQcloudGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SQcloudGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}

View File

@@ -77,7 +77,7 @@ func (self *SUCloudGuestDriver) GetAttachDiskStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SUCloudGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SUCloudGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}

View File

@@ -83,7 +83,7 @@ func (self *SVolcengineGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}
func (self *SVolcengineGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SVolcengineGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}

View File

@@ -101,7 +101,7 @@ func (self *SZStackGuestDriver) GetRebuildRootStatus() ([]string, error) {
return []string{api.VM_READY}, nil
}
func (self *SZStackGuestDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
func (self *SZStackGuestDriver) GetChangeInstanceTypeStatus() ([]string, error) {
return []string{api.VM_READY, api.VM_RUNNING}, nil
}

View File

@@ -28,6 +28,7 @@ import (
"yunion.io/x/onecloud/pkg/apis"
api "yunion.io/x/onecloud/pkg/apis/billing"
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
notifyapi "yunion.io/x/onecloud/pkg/apis/notify"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
@@ -247,7 +248,21 @@ func fetchExpiredModels(manager db.IModelManager, advanceDay int) ([]IBillingMod
upLimit := time.Now().AddDate(0, 0, advanceDay+1)
downLimit := time.Now().AddDate(0, 0, advanceDay)
v := reflect.MakeSlice(reflect.SliceOf(manager.TableSpec().DataType()), 0, 0)
q := manager.Query().LE("expired_at", upLimit).GE("expired_at", downLimit)
q := manager.Query()
q = q.Filter(
sqlchemy.OR(
sqlchemy.AND(
sqlchemy.Equals(q.Field("billing_type"), billing_api.BILLING_TYPE_POSTPAID),
sqlchemy.LE(q.Field("expired_at"), upLimit),
sqlchemy.GE(q.Field("expired_at"), downLimit),
),
// 跳过自动续费实例
sqlchemy.AND(
sqlchemy.Equals(q.Field("billing_type"), billing_api.BILLING_TYPE_PREPAID),
sqlchemy.LE(q.Field("expired_at"), upLimit),
sqlchemy.GE(q.Field("expired_at"), downLimit),
sqlchemy.IsFalse(q.Field("auto_renew")),
)))
vp := reflect.New(v.Type())
vp.Elem().Set(v)

View File

@@ -866,8 +866,10 @@ func syncZoneStorages(
newCacheIds = append(newCacheIds, cachePair)
}
}
if !remoteStorages[i].DisableSync() {
syncStorageDisks(ctx, userCred, syncResults, provider, driver, &localStorages[i], remoteStorages[i], syncRange)
if syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_COMPUTE) {
if !remoteStorages[i].DisableSync() {
syncStorageDisks(ctx, userCred, syncResults, provider, driver, &localStorages[i], remoteStorages[i], syncRange)
}
}
}()
}
@@ -974,9 +976,11 @@ func syncZoneHosts(
}
newCachePairs = syncHostStorages(ctx, userCred, syncResults, provider, &localHosts[i], remoteHosts[i], storageCachePairs, syncRange.Xor)
syncHostNics(ctx, userCred, syncResults, provider, &localHosts[i], remoteHosts[i])
// syncHostWires(ctx, userCred, syncResults, provider, &localHosts[i], remoteHosts[i])
syncHostVMs(ctx, userCred, syncResults, provider, driver, &localHosts[i], remoteHosts[i], syncRange)
if syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_COMPUTE) {
syncHostNics(ctx, userCred, syncResults, provider, &localHosts[i], remoteHosts[i])
syncHostVMs(ctx, userCred, syncResults, provider, driver, &localHosts[i], remoteHosts[i], syncRange)
}
}()
}
return newCachePairs
@@ -1596,6 +1600,9 @@ func syncWafIPSets(
return remoteRegion.GetICloudWafIPSets()
}()
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotImplemented || errors.Cause(err) == cloudprovider.ErrNotSupported {
return nil
}
msg := fmt.Sprintf("GetICloudWafIPSets for region %s failed %s", remoteRegion.GetName(), err)
log.Errorf(msg)
return err
@@ -1630,6 +1637,9 @@ func syncWafRegexSets(
return remoteRegion.GetICloudWafRegexSets()
}()
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotImplemented || errors.Cause(err) == cloudprovider.ErrNotSupported {
return nil
}
msg := fmt.Sprintf("GetICloudWafRegexSets for region %s failed %s", remoteRegion.GetName(), err)
log.Errorf(msg)
return err
@@ -1950,6 +1960,9 @@ func syncWafRules(ctx context.Context, userCred mcclient.TokenCredential, syncRe
return remoteWafs.GetRules()
}()
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotImplemented || errors.Cause(err) == cloudprovider.ErrNotSupported {
return nil
}
msg := fmt.Sprintf("GetRules for waf instance %s failed %s", localWaf.Name, err)
log.Errorf(msg)
return err
@@ -2162,12 +2175,13 @@ func syncPublicCloudProviderInfo(
if cloudprovider.IsSupportCompute(driver) {
if syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_NETWORK) ||
syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_NAT) ||
syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_IMAGE) ||
syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_EIP) {
// 需要先同步vpc避免私有云eip找不到network
if !(driver.GetFactory().IsPublicCloud() && !syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_NETWORK)) && syncRange.IsNotSkipSyncResource(VpcManager) {
syncRegionVPCs(ctx, userCred, syncResults, provider, localRegion, remoteRegion, syncRange)
}
if syncRange.IsNotSkipSyncResource(ElasticipManager) {
if syncRange.IsNotSkipSyncResource(ElasticipManager) && syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_EIP) {
syncRegionEips(ctx, userCred, syncResults, provider, localRegion, remoteRegion, syncRange)
}
@@ -2177,7 +2191,7 @@ func syncPublicCloudProviderInfo(
}
if syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_COMPUTE) {
if syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_COMPUTE) || syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_IMAGE) {
for j := 0; j < len(localZones); j += 1 {
@@ -2311,7 +2325,7 @@ func syncPublicCloudProviderInfo(
}
}
if cloudprovider.IsSupportCompute(driver) && syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_COMPUTE) {
if cloudprovider.IsSupportCompute(driver) && (syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_COMPUTE) || syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_IMAGE)) {
log.Debugf("storageCachePairs count %d", len(storageCachePairs))
for i := range storageCachePairs {
// always sync private cloud cached images
@@ -2397,8 +2411,10 @@ func syncOnPremiseCloudProviderStorage(ctx context.Context, userCred mcclient.To
storageCachePairs = append(storageCachePairs, cachePair)
}
}
if !remoteStorages[i].DisableSync() {
syncStorageDisks(ctx, userCred, syncResults, provider, driver, &localStorages[i], remoteStorages[i], syncRange)
if cloudprovider.IsSupportCompute(driver) && syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_COMPUTE) {
if !remoteStorages[i].DisableSync() {
syncStorageDisks(ctx, userCred, syncResults, provider, driver, &localStorages[i], remoteStorages[i], syncRange)
}
}
}()
}
@@ -2431,63 +2447,72 @@ func syncOnPremiseCloudProviderInfo(
}
var storageCachePairs []sStoragecacheSyncPair
if cloudprovider.IsSupportCompute(driver) && syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_COMPUTE) {
remoteVpcs, err := iregion.GetIVpcs()
if err != nil {
msg := fmt.Sprintf("GetIVpcs for provider %s failed %s", provider.GetName(), err)
log.Errorf(msg)
return err
}
if cloudprovider.IsSupportCompute(driver) &&
(syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_COMPUTE) ||
syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_IMAGE) ||
syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_NETWORK)) {
zone, err := getZoneForOnPremiseCloudRegion(ctx, userCred, iregion)
if err != nil {
msg := fmt.Sprintf("Can't get zone for Premise cloud region %s error: %v", iregion.GetName(), err)
log.Errorf(msg)
return errors.Wrap(err, "getZoneForOnPremiseCloudRegion")
}
{
// sync wires
localVpc := VpcManager.FetchDefaultVpc()
syncVpcWires(ctx, userCred, syncResults, provider, localVpc, remoteVpcs[0], zone, syncRange)
if syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_COMPUTE) || syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_NETWORK) {
remoteVpcs, err := iregion.GetIVpcs()
if err != nil {
msg := fmt.Sprintf("GetIVpcs for provider %s failed %s", provider.GetName(), err)
log.Errorf(msg)
return err
}
{
// sync wires
localVpc := VpcManager.FetchDefaultVpc()
syncVpcWires(ctx, userCred, syncResults, provider, localVpc, remoteVpcs[0], zone, syncRange)
}
}
storageCachePairs = syncOnPremiseCloudProviderStorage(ctx, userCred, syncResults, provider, iregion, driver, zone, syncRange)
ihosts, err := func() ([]cloudprovider.ICloudHost, error) {
defer syncResults.AddRequestCost(HostManager)()
return iregion.GetIHosts()
}()
if err != nil {
msg := fmt.Sprintf("GetIHosts for provider %s failed %s", provider.GetName(), err)
log.Errorf(msg)
return err
}
localHosts, remoteHosts, result := func() ([]SHost, []cloudprovider.ICloudHost, compare.SyncResult) {
defer syncResults.AddSqlCost(HostManager)()
return HostManager.SyncHosts(ctx, userCred, provider, zone, nil, ihosts, syncRange.Xor)
}()
syncResults.Add(HostManager, result)
msg := result.Result()
notes := fmt.Sprintf("SyncHosts for provider %s result: %s", provider.Name, msg)
log.Infof(notes)
provider.SyncError(result, notes, userCred)
for i := 0; i < len(localHosts); i += 1 {
if len(syncRange.Host) > 0 && !utils.IsInStringArray(localHosts[i].Id, syncRange.Host) {
continue
if syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_COMPUTE) {
ihosts, err := func() ([]cloudprovider.ICloudHost, error) {
defer syncResults.AddRequestCost(HostManager)()
return iregion.GetIHosts()
}()
if err != nil {
msg := fmt.Sprintf("GetIHosts for provider %s failed %s", provider.GetName(), err)
log.Errorf(msg)
return err
}
newCachePairs := syncHostStorages(ctx, userCred, syncResults, provider, &localHosts[i], remoteHosts[i], storageCachePairs, syncRange.Xor)
if len(newCachePairs) > 0 {
storageCachePairs = append(storageCachePairs, newCachePairs...)
localHosts, remoteHosts, result := func() ([]SHost, []cloudprovider.ICloudHost, compare.SyncResult) {
defer syncResults.AddSqlCost(HostManager)()
return HostManager.SyncHosts(ctx, userCred, provider, zone, nil, ihosts, syncRange.Xor)
}()
syncResults.Add(HostManager, result)
msg := result.Result()
notes := fmt.Sprintf("SyncHosts for provider %s result: %s", provider.Name, msg)
log.Infof(notes)
provider.SyncError(result, notes, userCred)
for i := 0; i < len(localHosts); i += 1 {
if len(syncRange.Host) > 0 && !utils.IsInStringArray(localHosts[i].Id, syncRange.Host) {
continue
}
newCachePairs := syncHostStorages(ctx, userCred, syncResults, provider, &localHosts[i], remoteHosts[i], storageCachePairs, syncRange.Xor)
if len(newCachePairs) > 0 {
storageCachePairs = append(storageCachePairs, newCachePairs...)
}
syncHostNics(ctx, userCred, syncResults, provider, &localHosts[i], remoteHosts[i])
syncHostVMs(ctx, userCred, syncResults, provider, driver, &localHosts[i], remoteHosts[i], syncRange)
}
syncHostNics(ctx, userCred, syncResults, provider, &localHosts[i], remoteHosts[i])
// syncOnPremiseHostWires(ctx, userCred, syncResults, provider, &localHosts[i], remoteHosts[i])
syncHostVMs(ctx, userCred, syncResults, provider, driver, &localHosts[i], remoteHosts[i], syncRange)
}
}
if cloudprovider.IsSupportCompute(driver) && syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_COMPUTE) {
if cloudprovider.IsSupportCompute(driver) && (syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_COMPUTE) || syncRange.NeedSyncResource(cloudprovider.CLOUD_CAPABILITY_IMAGE)) {
log.Debugf("storageCachePairs count %d", len(storageCachePairs))
for i := range storageCachePairs {
// alway sync on-premise cached images
@@ -2509,34 +2534,6 @@ func syncOnPremiseCloudProviderInfo(
return nil
}
/*func syncOnPremiseHostWires(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, localHost *SHost, remoteHost cloudprovider.ICloudHost) {
log.Infof("start to sync OnPremeseHostWires")
if provider.Provider != api.CLOUD_PROVIDER_VMWARE {
return
}
func() {
defer func() {
if syncResults != nil {
syncResults.AddSqlCost(NetInterfaceManager)()
}
}()
result := localHost.SyncEsxiHostWires(ctx, userCred, remoteHost)
if syncResults != nil {
syncResults.Add(NetInterfaceManager, result)
}
msg := result.Result()
notes := fmt.Sprintf("SyncEsxiHostWires for host %s result: %s", localHost.Name, msg)
if result.IsError() {
log.Errorf(notes)
return
} else {
log.Infof(notes)
}
db.OpsLog.LogEvent(provider, db.ACT_SYNC_HOST_COMPLETE, msg, userCred)
}()
}*/
func syncHostNics(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, localHost *SHost, remoteHost cloudprovider.ICloudHost) {
defer func() {
if syncResults != nil {

View File

@@ -130,6 +130,8 @@ type SDisk struct {
Preallocation string `width:"12" default:"off" charset:"ascii" nullable:"true" list:"user" update:"admin" json:"preallocation"`
// # is persistent
Nonpersistent bool `default:"false" list:"user" json:"nonpersistent"`
// auto reset disk after guest shutdown
AutoReset bool `default:"false" list:"user" update:"user" json:"auto_reset"`
// 是否标记为SSD磁盘
IsSsd bool `nullable:"false" default:"false" list:"user" update:"user" create:"optional"`
@@ -430,6 +432,12 @@ func (self *SDisk) ValidateUpdateData(ctx context.Context, userCred mcclient.Tok
}
}
if input.AutoReset != nil && *input.AutoReset != self.AutoReset {
if guest := self.GetGuest(); guest != nil && guest.Status != api.VM_READY {
return input, httperrors.NewBadRequestError("Can't set disk auto_reset on guest status %s", guest.Status)
}
}
storage, _ := self.GetStorage()
if storage == nil {
return input, httperrors.NewNotFoundError("failed to find storage for disk %s", self.Name)
@@ -1384,15 +1392,22 @@ func (self *SDisk) GetCloudprovider() *SCloudprovider {
}
func (self *SDisk) GetPathAtHost(host *SHost) string {
usedStorageId := self.StorageId
hostStorage := host.GetHoststorageOfId(self.StorageId)
if hostStorage != nil {
return path.Join(hostStorage.MountPoint, self.Id)
} else if len(self.BackupStorageId) > 0 {
if hostStorage == nil {
hostStorage = host.GetHoststorageOfId(self.BackupStorageId)
if hostStorage != nil {
return path.Join(hostStorage.MountPoint, self.Id)
usedStorageId = self.BackupStorageId
}
}
if hostStorage != nil {
storage := StorageManager.FetchStorageById(usedStorageId)
hostpath := path.Join(hostStorage.MountPoint, self.Id)
if storage != nil && utils.IsInStringArray(storage.StorageType, api.LVM_STORAGE) {
hostpath = path.Join("/dev", hostpath)
}
return hostpath
}
return ""
}

View File

@@ -74,6 +74,25 @@ type SDnsZone struct {
ProductType string `width:"32" charset:"ascii" nullable:"false" list:"domain" create:"domain_optional"`
}
func (self *SDnsZone) GetUniqValues() jsonutils.JSONObject {
return jsonutils.Marshal(map[string]string{"manager_id": self.ManagerId})
}
func (manager *SDnsZoneManager) FetchUniqValues(ctx context.Context, data jsonutils.JSONObject) jsonutils.JSONObject {
managerId, _ := data.GetString("manager_id")
return jsonutils.Marshal(map[string]string{"manager_id": managerId})
}
func (manager *SDnsZoneManager) FilterByUniqValues(q *sqlchemy.SQuery, values jsonutils.JSONObject) *sqlchemy.SQuery {
managerId, _ := values.GetString("manager_id")
if len(managerId) > 0 {
q = q.Equals("manager_id", managerId)
} else {
q = q.IsNullOrEmpty("manager_id")
}
return q
}
// 创建
func (manager *SDnsZoneManager) ValidateCreateData(
ctx context.Context,

View File

@@ -29,6 +29,7 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/gotypes"
"yunion.io/x/pkg/tristate"
"yunion.io/x/pkg/util/billing"
"yunion.io/x/pkg/util/httputils"
@@ -1665,6 +1666,9 @@ func (self *SGuest) PerformRebuildRoot(
}
diskCat := self.CategorizeDisks()
if gotypes.IsNil(diskCat.Root) {
return nil, httperrors.NewInputParameterError("no root disk is found")
}
if img.MinDiskMB == 0 || img.Status != imageapi.IMAGE_STATUS_ACTIVE {
return nil, httperrors.NewInputParameterError("invlid image")
}
@@ -2965,15 +2969,7 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T
return nil, httperrors.NewBadRequestError("Guest have backup not allow to change config")
}
changeStatus, err := self.GetDriver().GetChangeConfigStatus(self)
if err != nil {
return nil, httperrors.NewInputParameterError("%v", err)
}
if !utils.IsInStringArray(self.Status, changeStatus) {
return nil, httperrors.NewInvalidStatusError("Cannot change config in %s for %s, requires %s", self.Status, self.GetHypervisor(), changeStatus)
}
_, err = self.GetHost()
_, err := self.GetHost()
if err != nil {
return nil, httperrors.NewInvalidStatusError("no valid host")
}
@@ -2983,6 +2979,16 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T
return nil, errors.Wrap(err, "ValidateGuestChangeConfigInput")
}
if confs.CpuChanged() || confs.MemChanged() || confs.InstanceTypeChanged() {
changeStatus, err := self.GetDriver().GetChangeInstanceTypeStatus()
if err != nil {
return nil, httperrors.NewInputParameterError("%v", err)
}
if !utils.IsInStringArray(self.Status, changeStatus) {
return nil, httperrors.NewInvalidStatusError("Cannot change config in %s for %s, requires %s", self.Status, self.GetHypervisor(), changeStatus)
}
}
if self.PowerStates == api.VM_POWER_STATES_ON && (confs.CpuChanged() || confs.MemChanged()) {
confs, err = self.GetDriver().ValidateGuestHotChangeConfigInput(ctx, self, confs)
if err != nil {
@@ -2990,6 +2996,16 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T
}
}
if len(confs.Create) > 0 {
attachStatus, err := self.GetDriver().GetAttachDiskStatus()
if err != nil {
return nil, httperrors.NewInputParameterError("%v", err)
}
if !utils.IsInStringArray(self.Status, attachStatus) {
return nil, httperrors.NewInvalidStatusError("Cannot attach disk in %s for %s, requires %s", self.Status, self.GetHypervisor(), attachStatus)
}
}
log.Debugf("%s", jsonutils.Marshal(confs).String())
pendingUsage := &SQuota{}
@@ -5793,7 +5809,21 @@ func (self *SGuest) PerformChangeDiskStorage(ctx context.Context, userCred mccli
return nil, err
}
// create a disk on target storage from source disk
{
copyInput := api.ServerCopyDiskToStorageInput{
KeepOriginDisk: input.KeepOriginDisk,
GuestRunning: self.Status == api.VM_RUNNING,
}
err := self.CopyDiskToStorage(ctx, userCred, srcDisk, storage, copyInput, "")
if err != nil {
return nil, errors.Wrap(err, "CopyDiskToStorage")
}
}
return nil, nil
}
func (guest *SGuest) CopyDiskToStorage(ctx context.Context, userCred mcclient.TokenCredential, srcDisk *SDisk, storage *SStorage, input api.ServerCopyDiskToStorageInput, parentTaskId string) error {
diskConf := &api.DiskConfig{
Index: -1,
ImageId: srcDisk.TemplateId,
@@ -5802,19 +5832,39 @@ func (self *SGuest) PerformChangeDiskStorage(ctx context.Context, userCred mccli
DiskType: srcDisk.DiskType,
}
targetDisk, err := self.CreateDiskOnStorage(ctx, userCred, storage, diskConf, nil, true, true)
targetDisk, err := guest.CreateDiskOnStorage(ctx, userCred, storage, diskConf, nil, true, true)
if err != nil {
return nil, errors.Wrapf(err, "Create target disk on storage %s", storage.GetName())
return errors.Wrapf(err, "Create target disk on storage %s", storage.GetName())
}
{
err := db.InheritFromTo(ctx, userCred, srcDisk, targetDisk)
if err != nil {
return errors.Wrapf(err, "Inherit class metadata from src %s to target %s", srcDisk.GetName(), targetDisk.GetName())
}
}
internalInput := &api.ServerChangeDiskStorageInternalInput{
ServerChangeDiskStorageInput: *input,
StorageId: srcDisk.StorageId,
TargetDiskId: targetDisk.GetId(),
GuestRunning: self.Status == api.VM_RUNNING,
ServerChangeDiskStorageInput: api.ServerChangeDiskStorageInput{
DiskId: srcDisk.Id,
TargetStorageId: storage.Id,
KeepOriginDisk: input.KeepOriginDisk,
},
StorageId: srcDisk.StorageId,
TargetDiskId: targetDisk.GetId(),
GuestRunning: input.GuestRunning,
CloneDiskCount: input.CloneDiskCount,
CompletedDiskCount: input.CompletedDiskCount,
}
return nil, self.StartChangeDiskStorageTask(ctx, userCred, internalInput, "")
{
err := guest.StartChangeDiskStorageTask(ctx, userCred, internalInput, parentTaskId)
if err != nil {
return errors.Wrap(err, "StartChangeDiskStorageTask")
}
}
return nil
}
func (self *SGuest) StartChangeDiskStorageTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerChangeDiskStorageInternalInput, parentTaskId string) error {
@@ -6238,3 +6288,29 @@ func (g *SGuest) PerformSetRootDiskMatcher(ctx context.Context, userCred mcclien
}
return nil, nil
}
func (g *SGuest) PerformChangeBillingType(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ServerChangeBillingTypeInput) (jsonutils.JSONObject, error) {
if !utils.IsInStringArray(g.Status, []string{api.VM_RUNNING, api.VM_READY}) {
return nil, httperrors.NewServerStatusError("Cannot change guest billing type in status %s", g.Status)
}
if len(input.BillingType) == 0 {
return nil, httperrors.NewMissingParameterError("billing_type")
}
if !utils.IsInStringArray(input.BillingType, []string{billing_api.BILLING_TYPE_POSTPAID, billing_api.BILLING_TYPE_PREPAID}) {
return nil, httperrors.NewInputParameterError("invalid billing_type %s", input.BillingType)
}
if g.BillingType == input.BillingType {
return nil, nil
}
return nil, g.StartChangeBillingTypeTask(ctx, userCred, "")
}
func (self *SGuest) StartChangeBillingTypeTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
self.SetStatus(ctx, userCred, api.VM_CHANGE_BILLING_TYPE, "")
kwargs := jsonutils.NewDict()
task, err := taskman.TaskManager.NewTask(ctx, "GuestChangeBillingTypeTask", self, userCred, kwargs, parentTaskId, "", nil)
if err != nil {
return err
}
return task.ScheduleRun(nil)
}

View File

@@ -379,6 +379,7 @@ func fetchGuestDisksInfo(guestIds []string) map[string][]api.GuestDiskInfo {
guestdisks.Field("driver"),
guestdisks.Field("cache_mode"),
guestdisks.Field("aio_mode"),
disks.Field("auto_reset"),
storages.Field("medium_type"),
storages.Field("storage_type"),
guestdisks.Field("iops"),

View File

@@ -147,6 +147,7 @@ func (manager *SGuestdiskManager) FetchCustomizeColumns(
rows[i].Status = disk.Status
rows[i].DiskSize = disk.DiskSize
rows[i].DiskType = disk.DiskType
rows[i].AutoReset = disk.AutoReset
storage, _ := disk.GetStorage()
if storage != nil {
rows[i].StorageType = storage.StorageType
@@ -228,6 +229,7 @@ func (self *SGuestdisk) GetDiskJsonDescAtHost(ctx context.Context, host *SHost,
desc.Index = self.Index
bootIndex := self.BootIndex
desc.BootIndex = &bootIndex
desc.AutoReset = disk.AutoReset
if len(disk.SnapshotId) > 0 {
needMerge := disk.GetMetadata(ctx, "merge_snapshot", nil)

View File

@@ -112,6 +112,7 @@ type IGuestDriver interface {
RequestStartOnHost(ctx context.Context, guest *SGuest, host *SHost, userCred mcclient.TokenCredential, task taskman.ITask) error
RequestStopOnHost(ctx context.Context, guest *SGuest, host *SHost, task taskman.ITask, syncStatus bool) error
RequestChangeBillingType(ctx context.Context, guest *SGuest, task taskman.ITask) error
StartDeleteGuestTask(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, params *jsonutils.JSONDict, parentTaskId string) error
@@ -140,7 +141,7 @@ type IGuestDriver interface {
GetAttachDiskStatus() ([]string, error)
GetRebuildRootStatus() ([]string, error)
IsAllowSaveImageOnRunning() bool
GetChangeConfigStatus(guest *SGuest) ([]string, error)
GetChangeInstanceTypeStatus() ([]string, error)
GetDeployStatus() ([]string, error)
ValidateResizeDisk(guest *SGuest, disk *SDisk, storage *SStorage) error
CanKeepDetachDisk() bool

View File

@@ -137,7 +137,7 @@ type SHost struct {
// 预留CPU大小
CpuReserved int `nullable:"true" default:"0" list:"domain" update:"domain" create:"domain_optional"`
// CPU超分比
CpuCmtbound float32 `nullable:"true" default:"8" list:"domain" create:"domain_optional"`
CpuCmtbound float32 `nullable:"true" list:"domain" create:"domain_optional"`
// CPUMicrocode
CpuMicrocode string `width:"64" charset:"ascii" nullable:"true" get:"domain" update:"domain" create:"domain_optional"`
// CPU架构
@@ -148,7 +148,7 @@ type SHost struct {
// 预留内存大小
MemReserved int `nullable:"true" default:"0" list:"domain" update:"domain" create:"domain_optional"`
// 内存超分比
MemCmtbound float32 `nullable:"true" default:"1" list:"domain" create:"domain_optional"`
MemCmtbound float32 `nullable:"true" list:"domain" create:"domain_optional"`
// 页大小
PageSizeKB int `nullable:"false" default:"4" list:"domain" update:"domain" create:"domain_optional"`
EnableNumaAllocate bool `nullable:"true" default:"false" list:"domain" update:"domain" create:"domain_optional"`

View File

@@ -948,7 +948,7 @@ func (self *SIsolatedDevice) PerformPurge(ctx context.Context, userCred mcclient
func (self *SIsolatedDevice) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
if len(self.GuestId) > 0 {
if !jsonutils.QueryBoolean(data, "purge", false) {
return httperrors.NewBadRequestError("Isolated device used by server: %s", self.GuestId)
return httperrors.NewBadRequestError("%s: %s", api.ErrMsgIsolatedDeviceUsedByServer, self.GuestId)
}
iGuest, err := GuestManager.FetchById(self.GuestId)
if err != nil {

View File

@@ -25,6 +25,7 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/gotypes"
"yunion.io/x/pkg/tristate"
"yunion.io/x/pkg/util/compare"
"yunion.io/x/pkg/util/rbacscope"
@@ -88,7 +89,7 @@ type SStorage struct {
// example: ssd
MediumType string `width:"32" charset:"ascii" nullable:"false" list:"user" update:"domain" create:"domain_required"`
// 超售比
Cmtbound float32 `nullable:"true" default:"1" list:"domain"`
Cmtbound float32 `nullable:"true" list:"domain"`
// 存储配置信息
StorageConf jsonutils.JSONObject `nullable:"true" get:"domain" list:"domain" update:"domain"`
@@ -117,11 +118,14 @@ func (self *SStorage) ValidateUpdateData(ctx context.Context, userCred mcclient.
if err != nil {
return input, err
}
input.StorageConf = jsonutils.NewDict()
if gotypes.IsNil(input.StorageConf) {
input.StorageConf = jsonutils.NewDict()
}
if self.StorageConf != nil {
confs, _ := self.StorageConf.GetMap()
for k, v := range confs {
if !input.StorageConf.Contains(k) {
if input.StorageConf.Contains(k) {
continue
}
input.StorageConf.Set(k, v)
@@ -734,9 +738,9 @@ func (self *SStorage) GetUsedCapacity(isReady tristate.TriState) int64 {
}
}
func (self *SStorage) GetOvercommitBound() float32 {
if self.Cmtbound > 0 {
return self.Cmtbound
func (storage *SStorage) GetOvercommitBound() float32 {
if storage.Cmtbound > 0 {
return storage.Cmtbound
} else {
return options.Options.DefaultStorageOvercommitBound
}
@@ -1484,6 +1488,7 @@ func (self *SStorage) createDisk(ctx context.Context, name string, diskConfig *a
disk.Iops = diskConfig.Iops
disk.Throughput = diskConfig.Throughput
disk.Preallocation = diskConfig.Preallocation
disk.AutoReset = diskConfig.AutoReset
if self.MediumType == api.DISK_TYPE_SSD {
disk.IsSsd = true
@@ -1712,6 +1717,10 @@ func (manager *SStorageManager) ListItemFilter(
q = q.Filter(sqlchemy.In(q.Field("storage_type"), api.STORAGE_LOCAL_TYPES))
}
if len(query.StorageType) > 0 {
q = q.Equals("storage_type", query.StorageType)
}
if len(query.SchedtagId) > 0 {
schedTag, err := SchedtagManager.FetchByIdOrName(ctx, nil, query.SchedtagId)
if err != nil {

View File

@@ -106,8 +106,7 @@ type ComputeOptions struct {
RetentionDaysLimit int `default:"49" help:"Days of snapshot retention, default 49 days"`
TimePointsLimit int `default:"1" help:"time point of every days, default 1 point"`
ServerStatusSyncIntervalMinutes int `default:"5" help:"Interval to sync server status, defualt is 5 minutes"`
CloudAccountBatchSyncSize int `default:"10" help:"How many cloud account syncing in a batch"`
CloudAccountBatchSyncSize int `default:"10" help:"How many cloud account syncing in a batch"`
ServerSkuSyncIntervalMinutes int `default:"60" help:"Interval to sync public cloud server skus, defualt is 1 hour"`
SkuBatchSync int `default:"5" help:"How many skus can be sync in a batch"`
@@ -223,6 +222,15 @@ type ComputeOptions struct {
esxi.EsxiOptions
NetworkAlwaysManualConfig bool `help:"always manually configure network settings" default:"false"`
ComputeEEOptions
}
type ComputeEEOptions struct {
// 快速同步资源状态时间周期
ServerStatusSyncIntervalMinutes int `default:"5" help:"Interval to sync server status, defualt is 5 minutes"`
// 跳过新增资源同步时间范围
SkipServerStatusSyncTimeRange string `help:"Skip server status sync time range example: 08:00-18:00"`
}
type SCapabilityOptions struct {

View File

@@ -146,7 +146,7 @@ func StartServiceWithJobs(jobs func(cron *cronman.SCronJobManager)) {
cronFunc := func() {
cachesync.StartTenantCacheSync(opts.TenantCacheExpireSeconds)
cron := cronman.InitCronJobManager(true, options.Options.CronJobWorkerCount)
cron := cronman.InitCronJobManager(true, options.Options.CronJobWorkerCount, options.Options.TimeZone)
cron.AddJobAtIntervals("CleanPendingDeleteServers", time.Duration(opts.PendingDeleteCheckSeconds)*time.Second, models.GuestManager.CleanPendingDeleteServers)
cron.AddJobAtIntervals("CleanPendingDeleteDisks", time.Duration(opts.PendingDeleteCheckSeconds)*time.Second, models.DiskManager.CleanPendingDeleteDisks)
if opts.PrepaidExpireCheck {

View File

@@ -91,6 +91,10 @@ func (self *SRbdStorageDriver) ValidateCreateData(ctx context.Context, userCred
if input.EnableMessengerV2 != nil {
enableMessengerV2 = *input.EnableMessengerV2
}
autoCacheImages := false
if input.AutoCacheImages != nil {
autoCacheImages = *input.AutoCacheImages
}
input.StorageConf.Update(
jsonutils.Marshal(map[string]interface{}{
"mon_host": input.MonHost,
@@ -100,6 +104,7 @@ func (self *SRbdStorageDriver) ValidateCreateData(ctx context.Context, userCred
"rados_osd_op_timeout": input.RadosOsdOpTimeout,
"client_mount_timeout": input.ClientMountTimeout,
"enable_messenger_v2": enableMessengerV2,
"auto_cache_images": autoCacheImages,
}))
return nil
}
@@ -120,6 +125,11 @@ func (self *SRbdStorageDriver) ValidateUpdateData(ctx context.Context, userCred
input.UpdateStorageConf = true
}
if input.AutoCacheImages != nil {
input.StorageConf.Set("auto_cache_images", jsonutils.NewBool(*input.AutoCacheImages))
input.UpdateStorageConf = true
}
if len(input.RbdKey) > 0 {
input.StorageConf.Set("key", jsonutils.NewString(strings.Trim(input.RbdKey, " ")))
input.UpdateStorageConf = true

View File

@@ -0,0 +1,56 @@
// 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 tasks
import (
"context"
"yunion.io/x/jsonutils"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/util/logclient"
)
type GuestChangeBillingTypeTask struct {
SGuestBaseTask
}
func init() {
taskman.RegisterTask(GuestChangeBillingTypeTask{})
}
func (self *GuestChangeBillingTypeTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
guest := obj.(*models.SGuest)
self.SetStage("OnGuestChangeBillingTypeTaskComplete", nil)
drv := guest.GetDriver()
err := drv.RequestChangeBillingType(ctx, guest, self)
if err != nil {
self.OnGuestChangeBillingTypeTaskCompleteFailed(ctx, guest, jsonutils.NewString(err.Error()))
}
}
func (self *GuestChangeBillingTypeTask) OnGuestChangeBillingTypeTaskComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_VM_CHANGE_BILLING_TYPE, guest.BillingType, self.UserCred, false)
self.SetStageComplete(ctx, nil)
}
func (self *GuestChangeBillingTypeTask) OnGuestChangeBillingTypeTaskCompleteFailed(ctx context.Context, guest *models.SGuest, reason jsonutils.JSONObject) {
guest.SetStatus(ctx, self.GetUserCred(), api.VM_CHANGE_BILLING_TYPE_FAILED, "")
logclient.AddActionLogWithStartable(self, guest, logclient.ACT_VM_CHANGE_BILLING_TYPE, reason.String(), self.UserCred, false)
self.SetStageFailed(ctx, reason)
}

View File

@@ -381,36 +381,17 @@ func (t *GuestChangeDisksStorageTask) CreateTargetDisk(ctx context.Context, gues
return
}
// create a disk on target storage from source disk
diskConf := &api.DiskConfig{
Index: -1,
ImageId: srcDisk.TemplateId,
SizeMb: srcDisk.DiskSize,
Fs: srcDisk.FsFormat,
DiskType: srcDisk.DiskType,
}
targetDisk, err := guest.CreateDiskOnStorage(ctx, t.UserCred, storage, diskConf, nil, true, true)
if err != nil {
t.TaskFailed(ctx, guest, jsonutils.NewString(fmt.Sprintf("Create target disk on storage %s: %s", storage.GetName(), err)))
return
}
internalInput := &api.ServerChangeDiskStorageInternalInput{
ServerChangeDiskStorageInput: api.ServerChangeDiskStorageInput{
DiskId: srcDisk.Id,
TargetStorageId: storage.Id,
KeepOriginDisk: input.KeepOriginDisk,
},
StorageId: srcDisk.StorageId,
TargetDiskId: targetDisk.GetId(),
GuestRunning: input.GuestRunning,
CloneDiskCount: input.DiskCount,
CompletedDiskCount: input.DiskCount - len(input.Disks) - 1,
}
if err := guest.StartChangeDiskStorageTask(ctx, t.UserCred, internalInput, t.Id); err != nil {
t.TaskFailed(ctx, guest, jsonutils.NewString(err.Error()))
return
{
copyInput := api.ServerCopyDiskToStorageInput{
KeepOriginDisk: input.KeepOriginDisk,
GuestRunning: input.GuestRunning,
CloneDiskCount: input.DiskCount,
CompletedDiskCount: input.DiskCount - len(input.Disks) - 1,
}
err := guest.CopyDiskToStorage(ctx, t.UserCred, srcDisk, storage, copyInput, t.GetTaskId())
if err != nil {
t.TaskFailed(ctx, guest, jsonutils.NewString(fmt.Sprintf("CopyDiskToStorage fail: %s", err)))
return
}
}
}

View File

@@ -69,7 +69,7 @@ func (self *SecurityGroupRuleUpdateTask) OnInit(ctx context.Context, obj db.ISta
}
if len(rule.ExternalId) == 0 {
self.taskFailed(ctx, secgroup, errors.Wrapf(err, "empty external id"))
self.taskFailed(ctx, secgroup, errors.Errorf("empty external id"))
return
}

View File

@@ -23,6 +23,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/cronman"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/devtool/options"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules/ansible"
@@ -111,7 +112,7 @@ func AddOneCronjob(item *SCronjob, s *mcclient.ClientSession) error {
}
func InitializeCronjobs(ctx context.Context) error {
DevToolCronManager = cronman.InitCronJobManager(true, 8)
DevToolCronManager = cronman.InitCronJobManager(true, 8, options.Options.TimeZone)
DevToolCronManager.Start()
Session := auth.GetAdminSession(ctx, "")

View File

@@ -113,7 +113,7 @@ func (ea *SEsxiAgent) Start() error {
ea.AgentStorage = storageman.NewAgentStorage(&storageman.SStorageManager{LocalStorageImagecacheManager: ea.CacheManager},
ea, options.Options.AgentTempPath)
cronManager := cronman.InitCronJobManager(false, options.Options.CronJobWorkerCount)
cronManager := cronman.InitCronJobManager(false, options.Options.CronJobWorkerCount, options.Options.TimeZone)
err = cronManager.AddJobEveryFewDays(
"CleanRecycleDiskFiles", 1, 3, 0, 0, storageman.CleanRecycleDiskfiles, false)
if err != nil {

View File

@@ -20,43 +20,36 @@ import (
"net/http"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/pierrec/lz4/v4"
execlient "yunion.io/x/executor/client"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/appctx"
"yunion.io/x/onecloud/pkg/apis"
"yunion.io/x/onecloud/pkg/appsrv"
app_common "yunion.io/x/onecloud/pkg/cloudcommon/app"
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
common_options "yunion.io/x/onecloud/pkg/cloudcommon/options"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/util/procutils"
"yunion.io/x/onecloud/pkg/util/qemuimg"
"yunion.io/x/onecloud/pkg/util/seclib2"
)
type SHostImageOptions struct {
common_options.CommonOptions
LocalImagePath []string `help:"Local Image Paths"`
LVMVolumeGroups []string `help:"LVM Volume Groups(vgs)"`
SnapshotDirSuffix string `help:"Snapshot dir name equal diskId concat snapshot dir suffix" default:"_snap"`
CommonConfigFile string `help:"common config file for container"`
StreamChunkSize int `help:"Download stream chunk size KB" default:"4096"`
Lz4ChecksumOff bool `help:"Turn off lz4 checksum option"`
common_options.HostCommonOptions
LocalImagePath []string `help:"Local Image Paths"`
LVMVolumeGroups []string `help:"LVM Volume Groups(vgs)"`
SnapshotDirSuffix string `help:"Snapshot dir name equal diskId concat snapshot dir suffix" default:"_snap"`
HostImageNbdPidDir string `help:"Host-image nbd pid files dir " default:"/var/run/onecloud/host-image"`
CommonConfigFile string `help:"common config file for container"`
}
var (
HostImageOptions SHostImageOptions
streamingWorkerMan *appsrv.SWorkerManager
HostImageOptions SHostImageOptions
nbdExportManager *SNbdExportManager
)
func init() {
streamingWorkerMan = appsrv.NewWorkerManager("streaming_worker", 20, 1024, false)
}
func StartService() {
consts.SetServiceType("host-image")
common_options.ParseOptions(&HostImageOptions, os.Args, "host.conf", "host-image")
@@ -68,6 +61,19 @@ func StartService() {
HostImageOptions.CommonOptions = *commonCfg
HostImageOptions.BaseOptions.BaseOptions = baseOpt
}
log.Infof("exec socket path: %s", HostImageOptions.ExecutorSocketPath)
if HostImageOptions.EnableRemoteExecutor {
execlient.Init(HostImageOptions.ExecutorSocketPath)
execlient.SetTimeoutSeconds(HostImageOptions.ExecutorConnectTimeoutSeconds)
procutils.SetRemoteExecutor()
}
nbdExportManager = NewNbdExportManager()
output, err := procutils.NewCommand("mkdir", "-p", HostImageOptions.HostImageNbdPidDir).Output()
if err != nil {
log.Fatalf("failed to create path %s: %s %s", HostImageOptions.HostImageNbdPidDir, output, err)
}
HostImageOptions.EnableSsl = false
HostImageOptions.Port += 40000
app_common.InitAuth(&HostImageOptions.CommonOptions, func() {
@@ -79,27 +85,23 @@ func StartService() {
}
func initHandlers(app *appsrv.Application, prefix string) {
app.AddHandler("GET", fmt.Sprintf("%s/disks/<sid>", prefix), getImage).
SetProcessNoTimeout().SetWorkerManager(streamingWorkerMan)
app.AddHandler("GET", fmt.Sprintf("%s/snapshots/<diskId>/<sid>", prefix), getImage).
SetProcessNoTimeout().SetWorkerManager(streamingWorkerMan)
app.AddHandler("POST", fmt.Sprintf("%s/disks/<sid>/nbd-export", prefix), auth.Authenticate(imageNbdExport))
app.AddHandler("POST", fmt.Sprintf("%s/snapshots/<diskId>/<sid>/nbd-export", prefix), auth.Authenticate(imageNbdExport))
app.AddHandler("HEAD", fmt.Sprintf("%s/disks/<sid>", prefix), getImageMeta)
app.AddHandler("HEAD", fmt.Sprintf("%s/snapshots/<diskId>/<sid>", prefix), getImageMeta)
app.AddHandler("POST", fmt.Sprintf("%s/disks/<sid>", prefix), closeImage)
app.AddHandler("POST", fmt.Sprintf("%s/snapshots/<diskId>/<sid>", prefix), closeImage)
app.AddHandler("POST", fmt.Sprintf("%s/disks/<sid>/nbd-close", prefix), auth.Authenticate(imageNbdClose))
app.AddHandler("POST", fmt.Sprintf("%s/snapshots/<diskId>/<sid>/nbd-close", prefix), auth.Authenticate(imageNbdClose))
}
func getDiskPath(diskId string) string {
for _, imagePath := range HostImageOptions.LocalImagePath {
diskPath := path.Join(imagePath, diskId)
if _, err := os.Stat(diskPath); !os.IsNotExist(err) {
if _, err := procutils.RemoteStat(diskPath); err == nil {
return diskPath
}
}
for _, vg := range HostImageOptions.LVMVolumeGroups {
diskPath := path.Join("/dev", vg, diskId)
if _, err := os.Stat(diskPath); !os.IsNotExist(err) {
if _, err := procutils.RemoteStat(diskPath); err == nil {
return diskPath
}
}
@@ -110,244 +112,79 @@ func getSnapshotPath(diskId, snapshotId string) string {
for _, imagePath := range HostImageOptions.LocalImagePath {
diskPath := path.Join(imagePath, "snapshots",
diskId+HostImageOptions.SnapshotDirSuffix, snapshotId)
if _, err := os.Stat(diskPath); !os.IsNotExist(err) {
if _, err := procutils.RemoteStat(diskPath); err == nil {
return diskPath
}
}
for _, vg := range HostImageOptions.LVMVolumeGroups {
diskPath := path.Join("/dev", vg, "snap_"+snapshotId)
if _, err := os.Stat(diskPath); !os.IsNotExist(err) {
if _, err := procutils.RemoteStat(diskPath); err == nil {
return diskPath
}
}
return ""
}
func inputCheck(ctx context.Context) (string, error) {
var params = appctx.AppContextParams(ctx)
func inputCheck(ctx context.Context, w http.ResponseWriter, r *http.Request) (string, string, error) {
params, _, body := appsrv.FetchEnv(ctx, w, r)
var sid = params["<sid>"]
var imagePath string
var remoteDiskId string
remoteDiskId, _ = body.GetString("disk_id")
if remoteDiskId == "" {
return "", "", httperrors.NewMissingParameterError("disk_id")
}
if diskId, ok := params["<diskId>"]; ok {
imagePath = getSnapshotPath(diskId, sid)
} else {
imagePath = getDiskPath(sid)
}
if len(imagePath) == 0 {
return "", httperrors.NewNotFoundError("Disk not found")
return "", "", httperrors.NewNotFoundError("Disk not found")
}
return imagePath, nil
return imagePath, remoteDiskId, nil
}
func parseRange(reqRange string) (int64, int64, error) {
if !strings.HasPrefix(reqRange, "bytes=") {
return 0, 0, httperrors.NewInputParameterError("Invalid range header")
}
reqRange = reqRange[len("bytes="):]
ranges := strings.Split(reqRange, "-")
if len(ranges) != 2 {
return 0, 0, httperrors.NewInputParameterError("Invalid range header")
}
startPos, err := strconv.ParseInt(ranges[0], 10, 0)
if err != nil {
return 0, 0, httperrors.NewInputParameterError("Invalid range header")
}
endPos, err := strconv.ParseInt(ranges[1], 10, 0)
if err != nil {
return 0, 0, httperrors.NewInputParameterError("Invalid range header")
}
return startPos, endPos, nil
}
func closeImage(ctx context.Context, w http.ResponseWriter, r *http.Request) {
imagePath, err := inputCheck(ctx)
func imageNbdExport(ctx context.Context, w http.ResponseWriter, r *http.Request) {
imagePath, targetDiskId, err := inputCheck(ctx, w, r)
if err != nil {
httperrors.GeneralServerError(ctx, w, err)
return
}
var f IImage
if r.Header.Get("X-Read-File") == "true" {
f = &SFile{}
} else {
f = &SQcow2Image{}
imageInfo := qemuimg.SImageInfo{
Path: imagePath,
}
err = f.Load(imagePath, true, false)
encryptKey := r.Header.Get("X-Encrypt-Key")
if len(encryptKey) > 0 {
imageInfo.Password = encryptKey
imageInfo.EncryptAlg = seclib2.TSymEncAlg(r.Header.Get("X-Encrypt-Alg"))
}
nbdPort, err := nbdExportManager.QemuNbdStartExport(imageInfo, targetDiskId)
if err != nil {
httperrors.GeneralServerError(ctx, w, err)
return
}
f.Close()
w.WriteHeader(http.StatusOK)
log.Infof("Image %s request nbd export with port %d", targetDiskId, nbdPort)
ret := jsonutils.NewDict()
ret.Set("nbd_port", jsonutils.NewInt(int64(nbdPort)))
appsrv.SendJSON(w, ret)
}
func getImage(ctx context.Context, w http.ResponseWriter, r *http.Request) {
imagePath, err := inputCheck(ctx)
func imageNbdClose(ctx context.Context, w http.ResponseWriter, r *http.Request) {
_, targetDiskId, err := inputCheck(ctx, w, r)
if err != nil {
httperrors.GeneralServerError(ctx, w, err)
return
}
var (
f IImage
startPos, endPos int64
rateLimit int64 = -1
encryptInfo *apis.SEncryptInfo
)
if r.Header.Get("X-Read-File") == "true" {
f = &SFile{}
} else {
f = &SQcow2Image{}
}
err = f.Load(imagePath, true, true)
if err != nil {
encryptKey := r.Header.Get("X-Encrypt-Key")
if len(encryptKey) > 0 {
encryptInfo = new(apis.SEncryptInfo)
encryptInfo.Key = encryptKey
encryptInfo.Alg = seclib2.TSymEncAlg(r.Header.Get("X-Encrypt-Alg"))
}
if err = f.Open(imagePath, true, encryptInfo); err != nil {
log.Errorf("Open image error: %s", err)
httperrors.GeneralServerError(ctx, w, err)
return
}
}
defer f.Close()
endPos = f.Length() - 1
reqRange := r.Header.Get("Range")
if len(reqRange) > 0 {
startPos, endPos, err = parseRange(reqRange)
if err != nil {
log.Errorf("Parse range error: %s", err)
httperrors.GeneralServerError(ctx, w, err)
return
}
}
strRateLimit := r.Header.Get("X-Rate-Limit-Mbps")
if len(strRateLimit) > 0 {
rateLimit, err = strconv.ParseInt(strRateLimit, 10, 0)
if err != nil {
log.Errorf("Parse ratelimit error: %s", err)
httperrors.InvalidInputError(ctx, w, "Invaild rate limit header")
return
}
}
streamHeader(w, f, startPos, endPos)
startStream(w, f, startPos, endPos, rateLimit)
}
func streamHeader(w http.ResponseWriter, f IImage, startPos, endPos int64) {
var statusCode = http.StatusOK
w.Header().Set("Content-Type", "application/octet-stream")
if startPos > 0 || endPos < f.Length()-1 {
statusCode = http.StatusPartialContent
w.Header().Set("Content-Range",
fmt.Sprintf("bytes %d-%d/%d", startPos, endPos, f.Length()))
}
w.WriteHeader(statusCode)
}
func startStream(w http.ResponseWriter, f IImage, startPos, endPos, rateLimit int64) {
var CHUNK_SIZE int64 = int64(HostImageOptions.StreamChunkSize * 1024)
var readSize int64 = CHUNK_SIZE
var sendBytes int64
var lz4Writer = lz4.NewWriter(w)
var startTime = time.Now()
opts := []lz4.Option{
lz4.BlockSizeOption(lz4.Block4Mb),
lz4.ConcurrencyOption(-1),
lz4.CompressionLevelOption(lz4.Fast),
}
if HostImageOptions.Lz4ChecksumOff {
log.Infof("Turn off lz4 checksum")
opts = append(opts,
lz4.BlockChecksumOption(false),
lz4.ChecksumOption(false),
)
}
if err := lz4Writer.Apply(opts...); err != nil {
log.Errorf("lz4Writer.Apply options error: %v", err)
goto fail
}
for startPos < endPos {
if endPos-startPos < CHUNK_SIZE {
readSize = endPos - startPos + 1
}
buf, err := f.Read(startPos, readSize)
if err != nil {
log.Errorf("Read image error: %s", err)
goto fail
}
startPos += readSize
wSize, err := lz4Writer.Write(buf)
if err != nil {
log.Errorf("lz4Write error: %s", err)
goto fail
}
sendBytes += int64(wSize)
if rateLimit > 0 {
tmDelta := time.Now().Sub(startTime)
tms := tmDelta.Seconds()
vtmDelta := float64(sendBytes*8) / float64(1024.0*1024.0*rateLimit)
if vtmDelta > tms {
time.Sleep(time.Duration(vtmDelta - tms))
}
}
}
fail:
if err := lz4Writer.Close(); err != nil {
log.Errorf("lz4 Close error: %s", err)
}
}
func getImageMeta(ctx context.Context, w http.ResponseWriter, r *http.Request) {
imagePath, err := inputCheck(ctx)
err = nbdExportManager.QemuNbdCloseExport(targetDiskId)
if err != nil {
httperrors.GeneralServerError(ctx, w, err)
return
}
var (
f IImage
encryptInfo *apis.SEncryptInfo
)
if r.Header.Get("X-Read-File") == "true" {
f = &SFile{}
} else {
f = &SQcow2Image{}
}
log.Infof("open image %s", imagePath)
err = f.Load(imagePath, true, true)
if err != nil {
encryptKey := r.Header.Get("X-Encrypt-Key")
if len(encryptKey) > 0 {
encryptInfo = new(apis.SEncryptInfo)
encryptInfo.Key = encryptKey
encryptInfo.Alg = seclib2.TSymEncAlg(r.Header.Get("X-Encrypt-Alg"))
}
if err = f.Open(imagePath, true, encryptInfo); err != nil {
log.Errorf("Open image error: %s", err)
httperrors.GeneralServerError(ctx, w, err)
return
}
}
defer f.Close()
w.Header().Set("Content-Length", fmt.Sprintf("%d", f.Length()))
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Accept-Ranges", "bytes")
w.WriteHeader(200)
log.Infof("Image %s request nbd close export with port", targetDiskId)
appsrv.SendStruct(w, map[string]string{"result": "ok"})
}

View File

@@ -1,266 +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 hostimage
/*
#cgo pkg-config: glib-2.0 zlib
#include "libqemuio.h"
#include "qemu/osdep.h"
*/
import "C"
import (
"bufio"
"fmt"
"io"
"os"
"path/filepath"
"sync"
"sync/atomic"
"unsafe"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/apis"
"yunion.io/x/onecloud/pkg/util/qemuimg"
)
var qemuBlkCache sync.Map
type QemuioBlkDev struct {
imagePath string
readonly bool
encrypted bool
refCount int32
blk *C.struct_QemuioBlk
}
func init() {
C.qemuio_init()
}
func (qb *QemuioBlkDev) ReadQcow2(offset int64, count int64) ([]byte, int64) {
if qb.blk == nil || offset < 0 || count < 0 {
return nil, -1
}
var b = make([]byte, count)
var total = C.int64_t(0)
ret := C.read_qcow2(qb.blk, unsafe.Pointer(&b[0]), C.int64_t(offset), C.int64_t(count), &total)
if ret < 0 {
return nil, int64(ret)
} else {
return b, int64(total)
}
}
func OpenQcow2(disk *qemuimg.SImageInfo, readonly bool) *QemuioBlkDev {
key := fmt.Sprintf("%s_%v", disk.Path, readonly)
if blk, ok := qemuBlkCache.Load(key); ok {
qb := blk.(*QemuioBlkDev)
atomic.AddInt32(&qb.refCount, 1)
return qb
}
qb := &QemuioBlkDev{
imagePath: disk.Path,
readonly: readonly,
}
diskPath := C.CString(disk.Path)
imageOpts := C.CString(disk.ImageOptions())
// sec options
var secretOpts *C.char
secOpt := disk.SecretOptions()
if len(secOpt) > 0 {
secretOpts = C.CString(secOpt)
qb.encrypted = true
}
// qemu io open image
blk := C.open_qcow2(diskPath, imageOpts, secretOpts, C.bool(readonly))
C.free(unsafe.Pointer(diskPath))
C.free(unsafe.Pointer(imageOpts))
if secretOpts != nil {
C.free(unsafe.Pointer(secretOpts))
}
if blk == nil {
// failed open qemu image
return nil
}
qb.blk = blk
qb.refCount = 1
qemuBlkCache.Store(key, qb)
return qb
}
func LoadQcow2(imagePath string, readonly bool) *QemuioBlkDev {
key := fmt.Sprintf("%s_%v", imagePath, readonly)
if blk, ok := qemuBlkCache.Load(key); ok {
return blk.(*QemuioBlkDev)
}
return nil
}
func (qb *QemuioBlkDev) Qcow2GetLength() int64 {
if qb.blk == nil {
return -1
} else {
return int64(C.qcow2_get_length(qb.blk))
}
}
func (qb *QemuioBlkDev) CloseQcow2() {
if qb.blk != nil && atomic.AddInt32(&qb.refCount, -1) == 0 {
var secId *C.char
if qb.encrypted {
secId = C.CString(filepath.Base(qb.imagePath))
}
C.close_qcow2(qb.blk, secId)
qemuBlkCache.Delete(fmt.Sprintf("%s_%v", qb.imagePath, qb.readonly))
if secId != nil {
C.free(unsafe.Pointer(secId))
}
}
}
type IImage interface {
// Open image file and its backing file (if have)
Open(imagePath string, readonly bool, encryptInfo *apis.SEncryptInfo) error
// load opend qcow2 img form qemu blk cache
Load(imagePath string, readonly, reference bool) error
// Close may not really close image file handle, just reudce ref count
Close()
// If return number < 0 indicate read failed
Read(offset, count int64) ([]byte, error)
// Get image file length, not file actual length, it's image virtual size
Length() int64
}
type SQcow2Image struct {
fd *QemuioBlkDev
}
func (img *SQcow2Image) newQemuImage(imagePath string, encryptInfo *apis.SEncryptInfo) *qemuimg.SImageInfo {
info := &qemuimg.SImageInfo{
Path: imagePath,
}
if encryptInfo != nil {
info.SetSecId(filepath.Base(imagePath))
info.Password = encryptInfo.Key
info.EncryptAlg = encryptInfo.Alg
}
return info
}
func (img *SQcow2Image) Open(imagePath string, readonly bool, encryptInfo *apis.SEncryptInfo) error {
disk := img.newQemuImage(imagePath, encryptInfo)
fd := OpenQcow2(disk, readonly)
if fd == nil {
return fmt.Errorf("open image %s failed", imagePath)
} else {
img.fd = fd
return nil
}
}
func (img *SQcow2Image) Load(imagePath string, readonly, reference bool) error {
fd := LoadQcow2(imagePath, readonly)
if fd == nil {
return fmt.Errorf("image %s readonly: %v not found", imagePath, readonly)
} else {
atomic.AddInt32(&fd.refCount, 1)
img.fd = fd
return nil
}
}
func (img *SQcow2Image) Read(offset, count int64) ([]byte, error) {
buf, ret := img.fd.ReadQcow2(offset, count)
if ret >= 0 {
return buf[:ret], nil
} else {
return nil, errors.Errorf("failed read data %d", ret)
}
}
func (img *SQcow2Image) Close() {
img.fd.CloseQcow2()
}
func (img *SQcow2Image) Length() int64 {
return img.fd.Qcow2GetLength()
}
type SFile struct {
fd *os.File
}
func (f *SFile) Open(imagePath string, readonly bool, encryptInfo *apis.SEncryptInfo) error {
var mode = os.O_RDWR
if readonly {
mode = os.O_RDONLY
}
fd, err := os.OpenFile(imagePath, mode, 0644)
if err != nil {
return err
} else {
f.fd = fd
return nil
}
}
func (f *SFile) Load(imagePath string, readonly, reference bool) error {
return fmt.Errorf("File don't support load")
}
func (f *SFile) Read(offset, count int64) ([]byte, error) {
if _, err := f.fd.Seek(offset, io.SeekStart); err != nil {
log.Errorf("seek file %s", err)
return nil, errors.Wrap(err, "seek")
}
buf := make([]byte, count)
r := bufio.NewReader(f.fd)
n, err := r.Read(buf)
if err != nil {
return nil, errors.Wrap(err, "read")
}
return buf[:n], nil
}
func (f *SFile) Close() {
f.fd.Close()
}
func (f *SFile) Length() int64 {
stat, e := f.fd.Stat()
if e != nil {
return -1
}
return stat.Size()
}

162
pkg/hostimage/nbd.go Normal file
View File

@@ -0,0 +1,162 @@
// 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 hostimage
import (
"fmt"
"os"
"path"
"strconv"
"strings"
"sync"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/version"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/netutils2"
"yunion.io/x/onecloud/pkg/util/procutils"
"yunion.io/x/onecloud/pkg/util/qemuimg"
"yunion.io/x/onecloud/pkg/util/qemutils"
)
var EXPORT_NBD_BASE_PORT = 7777
var LAST_USED_NBD_SERVER_PORT = 0
type SNbdExportManager struct {
portsLock *sync.Mutex
}
func NewNbdExportManager() *SNbdExportManager {
return &SNbdExportManager{
portsLock: new(sync.Mutex),
}
}
func (m *SNbdExportManager) GetFreePortByBase(basePort int) int {
var port = 1
for {
if netutils2.IsTcpPortUsed("0.0.0.0", basePort+port) {
port += 1
} else {
break
}
}
return port + basePort
}
func (m *SNbdExportManager) GetNBDServerFreePort() int {
basePort := EXPORT_NBD_BASE_PORT + LAST_USED_NBD_SERVER_PORT
var port = 1
for {
if netutils2.IsTcpPortUsed("0.0.0.0", basePort+port) {
port += 1
} else {
break
}
}
LAST_USED_NBD_SERVER_PORT = port
if LAST_USED_NBD_SERVER_PORT > 1000 {
LAST_USED_NBD_SERVER_PORT = 0
}
return port + basePort
}
func (m *SNbdExportManager) getQemuNbdVersion() (string, error) {
output, err := procutils.NewRemoteCommandAsFarAsPossible(qemutils.GetQemuNbd(), "--version").Output()
if err != nil {
log.Errorf("qemu-nbd version failed %s %s", output, err.Error())
return "", errors.Wrapf(err, "qemu-nbd version failed %s", output)
}
lines := strings.Split(strings.TrimSpace(string(output)), "\n")
if len(lines) > 0 {
parts := strings.Split(lines[0], " ")
return parts[1], nil
}
return "", errors.Error("empty version output")
}
func (m *SNbdExportManager) QemuNbdStartExport(imageInfo qemuimg.SImageInfo, diskId string) (int, error) {
m.portsLock.Lock()
defer m.portsLock.Unlock()
nbdPort := m.GetNBDServerFreePort()
pidFilePath := path.Join(HostImageOptions.HostImageNbdPidDir, fmt.Sprintf("nbd_%s.pid", diskId))
nbdVer, err := m.getQemuNbdVersion()
if err != nil {
return -1, errors.Wrap(err, "getQemuNbdVersion")
}
var cmd []string
if imageInfo.Encrypted() {
cmd = []string{
qemutils.GetQemuNbd(),
"--read-only", "--persistent", "-x", diskId, "-p", strconv.Itoa(nbdPort),
"--object", imageInfo.SecretOptions(),
"--image-opts", imageInfo.ImageOptions(),
}
} else {
cmd = []string{
qemutils.GetQemuNbd(),
"--read-only", "--persistent", "-x", diskId, "-p", strconv.Itoa(nbdPort),
imageInfo.Path,
}
}
cmd = append(cmd, "--pid-file", pidFilePath)
if version.GE(nbdVer, "4.0.0") {
cmd = append(cmd, "--fork")
}
cmdStr := strings.Join(cmd, " ")
err = procutils.NewRemoteCommandAsFarAsPossible("sh", "-c", cmdStr).Run()
if err != nil {
log.Errorf("qemu-nbd connect failed %s %s", err.Error())
return -1, errors.Wrapf(err, "qemu-nbd connect failed")
}
return nbdPort, nil
}
func (m *SNbdExportManager) QemuNbdCloseExport(diskId string) error {
pidFilePath := path.Join(HostImageOptions.HostImageNbdPidDir, fmt.Sprintf("nbd_%s.pid", diskId))
if !m.nbdProcessExist(diskId) {
if fileutils2.Exists(pidFilePath) {
if err := os.Remove(pidFilePath); err != nil {
log.Errorf("failed remove nbd pid file %s", pidFilePath)
}
}
return nil
}
if fileutils2.Exists(pidFilePath) {
pid, err := fileutils2.FileGetIntContent(pidFilePath)
if err != nil {
return errors.Wrapf(err, "failed get pid of qemu-nbd process %s", pidFilePath)
}
out, err := procutils.NewRemoteCommandAsFarAsPossible("kill", "-9", strconv.Itoa(pid)).Output()
if err != nil {
log.Errorf("failed kill nbd export process %s %s", err, out)
return errors.Wrapf(err, "kill nbd export failed: %s", out)
}
if err := os.Remove(pidFilePath); err != nil {
log.Errorf("failed remove nbd pid file %s", pidFilePath)
}
}
return nil
}
func (m *SNbdExportManager) nbdProcessExist(diskId string) bool {
return procutils.NewRemoteCommandAsFarAsPossible("sh", "-c",
fmt.Sprintf("ps -ef | grep [q]emu-nbd | grep %s", diskId)).Run() == nil
}

View File

@@ -417,7 +417,7 @@ func FormatPartition(path, fs, uuid string) error {
// #case fs == "ntfs":
// # cmd = []string{"/sbin/mkfs.ntfs"}
case fs == "xfs":
cmd = []string{"mkfs.xfs", "-f", "-m", "crc=0", "-i", "projid32bit=0", "-n", "ftype=0"}
cmd = []string{"mkfs.xfs", "-f", "-m", "crc=0", "-i", "projid32bit=0", "-n", "ftype=1"}
cmdUuid = []string{"xfs_admin", "-U", uuid}
}

View File

@@ -620,7 +620,15 @@ func (d *QemuBaseDriver) startCmds(
if sysutils.IsKvmSupport() {
cmd += __("-enable-kvm")
cmd += __("-cpu host")
isCPUIntel := sysutils.IsProcessorIntel()
isCPUAMD := sysutils.IsProcessorAmd()
if isCPUIntel {
cmd += __("-cpu host,vendor=GenuineIntel")
} else if isCPUAMD {
cmd += __("-cpu host,vendor=AuthenticAMD")
} else {
cmd += __("-cpu host")
}
} else {
cmd += __("-cpu max")
}

View File

@@ -397,6 +397,13 @@ func NewUsbController(masterbus string, port int) *UsbController {
return uc
}
func (m *SGuestMem) GuestNumaNodeCount() int {
if m.Mem == nil {
return 0
}
return len(m.Mem.Mems) + 1
}
func NewMemDesc(objType, id string, nodeId *uint16, cpus *string) *SMemDesc {
md := &SMemDesc{
Object: NewObject(objType, id),

View File

@@ -140,7 +140,7 @@ func (m *SGuestManager) GuestCreateFromCloudpods(
iDisk := storage.CreateDisk(diskId)
diskUrl := fmt.Sprintf("http://%s:48885/disks/%s",
createConfig.CloudpodsAccessInfo.HostIp, createConfig.CloudpodsAccessInfo.OriginDisksId[i])
if err = iDisk.CreateFromImageFuse(ctx, diskUrl, 0, nil); err != nil {
if err = iDisk.CreateFromRemoteHostImage(ctx, diskUrl, 0, nil); err != nil {
log.Errorf("failed create disk %s from fuse %s", diskUrl, err)
break
}

View File

@@ -589,6 +589,14 @@ func guestHotplugCpuMem(ctx context.Context, userCred mcclient.TokenCredential,
AddCpuCount: addCpuCount,
AddMemSize: addMemSize,
}
totalMemSize, err := body.Int("total_mem")
if err == nil {
input.TotalMemSize = &totalMemSize
}
totalCpuCount, err := body.Int("total_cpu")
if err == nil {
input.TotalCpuCount = &totalCpuCount
}
if body.Contains("cpu_numa_pin") {
cpuNumaPin := make([]schedapi.SCpuNumaPin, 0)

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